diff --git a/PublicDomain b/PublicDomain
new file mode 100644
--- /dev/null
+++ b/PublicDomain
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+import Distribution.Simple
+
+main = defaultMain
diff --git a/regex-do.cabal b/regex-do.cabal
new file mode 100644
--- /dev/null
+++ b/regex-do.cabal
@@ -0,0 +1,80 @@
+name:                regex-do
+version:             1.0
+synopsis:            PCRE regex funs
+description:         Convenience functions to search, replace, format String | ByteString with PCRE regex
+author:              Imants Cekusins
+maintainer:          Imants Cekusins
+category:            Regex, Search, String
+license:             PublicDomain
+license-file:        PublicDomain
+cabal-version:       >=1.10
+build-type:          Simple
+homepage:            https://github.com/ciez/regex-do
+
+library
+  exposed-modules:
+          Regexdo.Trim
+          Regexdo.Pcre.Match
+          Regexdo.Pcre.Option
+          Regexdo.Pcre.Replace
+          Regexdo.Pcre.Result
+          Regexdo.Search
+          Regexdo.Format
+          Regexdo.TypeDo
+          Regexdo.TypeRegex
+          Regexdo.Convert
+
+  ghc-options:  -fwarn-unused-imports
+    
+  build-depends:  base >=4.8 && <4.9,
+              regex-base >= 0.93.2,
+              regex-pcre >= 0.94.4,
+              stringsearch >= 0.3.6.6,
+              bytestring >= 0.10.6.0,
+              array >= 0.5.1.0,
+              text
+
+
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  default-extensions:   FlexibleInstances
+                        MultiParamTypeClasses
+                        BangPatterns
+                        InstanceSigs
+                        OverloadedStrings
+                        FlexibleContexts
+                        ConstraintKinds
+                       
+
+test-suite spec
+  default-language:Haskell2010
+  type: exitcode-stdio-1.0
+  ghc-options:  -fwarn-unused-imports
+  hs-source-dirs: test, src
+  default-extensions:   FlexibleInstances
+                        MultiParamTypeClasses
+                        BangPatterns
+                        InstanceSigs
+                        OverloadedStrings
+                        FlexibleContexts
+                        ConstraintKinds
+
+
+  main-is: Main.hs
+  other-modules:
+        TestRegex.Format
+        TestRegex.Pcre
+        TestRegex.Replace
+        TestRegex.StringSearch
+        TestRegex.TestTrim
+
+  build-depends:  base >= 4.8,
+                  hspec >= 2.1.7,
+                  QuickCheck >= 2.8.1,
+                  regex-base >= 0.93.2,
+                  regex-pcre >= 0.94.4,
+                  stringsearch >= 0.3.6.6,
+                  bytestring >= 0.10.6.0,
+                  array >= 0.5.1.0,
+                  text,
+                  regex-do
diff --git a/src/Regexdo/Convert.hs b/src/Regexdo/Convert.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Convert.hs
@@ -0,0 +1,13 @@
+module Regexdo.Convert where
+
+import qualified Data.Text.Encoding as E
+import qualified Data.Text as T
+import Data.ByteString as B
+
+-- | both Ascii and Utf8
+toByteString::String -> ByteString
+toByteString = E.encodeUtf8 . T.pack
+
+-- | both Ascii and Utf8
+toString::ByteString -> String
+toString = T.unpack . E.decodeUtf8
diff --git a/src/Regexdo/Format.hs b/src/Regexdo/Format.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Format.hs
@@ -0,0 +1,72 @@
+module Regexdo.Format
+    (pad,
+    Format(..)) where
+
+import Prelude as P
+import Regexdo.TypeDo
+import Regexdo.Search as S (replace)
+import Regexdo.Convert
+
+
+class Format a where
+   format::String -> a -> String
+
+
+instance Format [String] where
+   format = foldr_idx foldFn_idx
+-- ^
+-- === index based
+-- >>> format "на первое {0}, на второе {0}" ["перловка"]
+--
+-- "на первое перловка, на второе перловка"
+--
+-- >>> format "Polly {0} a {1}" ["got","cracker"]
+--
+-- "Polly got a cracker"
+--
+
+foldFn_idx::String -> (Int, String) -> String
+foldFn_idx v (i,body1) = replaceOne body1 (show i) v
+
+
+instance Format [(String,String)] where
+   format = P.foldr foldFn_map
+-- ^
+-- === key based
+-- key may be {any string}
+--
+-- >>> format "овчинка {a} не {b}" [("a","выделки"),("b","стоит")]
+--
+-- "овчинка выделки не стоит"
+--
+
+foldFn_map:: (String, String) -> String -> String
+foldFn_map (k,v) body1 = replaceOne body1 k v
+
+replaceOne::String -> String -> String -> String
+replaceOne body k v = toString bs1
+   where pat1 = Needle $ toByteString $ "{" ++ k ++ "}"
+         repl1 = Replacement $ toByteString v
+         bs1 = S.replace pat1 repl1 $ Haystack $ toByteString body
+
+-- | pad String with Char to total length of Int
+--
+-- >>> pad '-' 5 "abc"
+-- "--abc"
+--
+pad::Char -> Int -> String -> String
+pad c0 tot0 txt0 =
+    [const c0 p1 | p1 <- [1..(tot0 - (P.length txt0))]] ++ txt0
+
+
+--  fold with index
+type CustomerFn a b = (a -> (Int,b) -> b)
+
+foldr_idx :: CustomerFn a b -> b -> [a] -> b
+foldr_idx fn init1 list = b1
+   where i0 = P.length list - 1
+         (-1,b1) = P.foldr (foldFn fn) (i0,init1) list
+
+foldFn :: CustomerFn a b -> a -> (Int,b) -> (Int,b)
+foldFn fn val t@(i,_)= (i-1,b1)
+   where b1 = fn val t
diff --git a/src/Regexdo/Pcre/Match.hs b/src/Regexdo/Pcre/Match.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Pcre/Match.hs
@@ -0,0 +1,76 @@
+module Regexdo.Pcre.Match where
+
+import qualified Text.Regex.Base.RegexLike as R hiding (makeRegex)
+import qualified Text.Regex.Base.RegexLike as R (makeRegex)
+import Regexdo.TypeDo
+import Regexdo.Pcre.Option as O
+import Regexdo.TypeRegex
+import Data.ByteString
+
+{- |
+    see "Regexdo.Pcre.Result"
+    for funs converting 'MatchArray' to something useful
+
+    'match' returns the first occurence - if any
+
+    -}
+
+class Match_ctr n h =>
+            Match_cl n h where
+
+   match::Needle n -> Haystack h -> Maybe MatchArray
+   match r0 (Haystack b0) = R.matchOnce (r_ r0) b0
+
+   matchTest::Needle n -> Haystack h -> Bool
+   matchTest r0 (Haystack b0) = R.matchTest (r_ r0) b0
+
+   matchAll::Needle n -> Haystack h -> [MatchArray]
+   matchAll r0 (Haystack b0) = R.matchAll (r_ r0) b0
+
+
+-- | tweak Regex with options
+makeRegexOpts::Match_opt n =>
+    [O.Comp] -> [O.Exec] -> Needle n -> Regex
+makeRegexOpts comp0 exec0 (Needle pat0) = rx1
+   where c1 = O.comp comp0
+         e1 = O.exec exec0
+         rx1 = R.makeRegexOpts c1 e1 pat0
+
+
+{- |
+    this instance accepts regex 'String'
+
+    >>> matchTest (Needle "^ab") (Haystack "abc")
+
+    True
+-}
+
+instance Match_cl String String
+-- | this instance accepts regex 'String'
+instance Match_cl String ByteString
+-- | this instance accepts regex 'ByteString'
+instance Match_cl ByteString ByteString
+-- | this instance accepts regex 'ByteString'
+instance Match_cl ByteString String
+-- | this instance accepts 'Regex' made with 'makeRegexOpts'
+instance Match_cl Regex String
+-- | this instance accepts 'Regex' made with 'makeRegexOpts'
+instance Match_cl Regex ByteString
+
+
+class Needle_ r where
+   r_::Needle r -> Regex
+
+instance Needle_ ByteString where
+   r_ (Needle r0) = R.makeRegex r0
+
+instance Needle_ String where
+   r_ (Needle r0) = R.makeRegex r0
+
+instance Needle_ Regex where
+   r_ (Needle r0) = r0
+
+
+-- | _ctr: constraint
+type Match_ctr n h = (R.Extract h, Needle_ n, R.RegexLike Regex h)
+type Match_opt n = R.RegexMaker Regex CompOption ExecOption n
diff --git a/src/Regexdo/Pcre/Option.hs b/src/Regexdo/Pcre/Option.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Pcre/Option.hs
@@ -0,0 +1,57 @@
+module Regexdo.Pcre.Option (
+      Comp(..),
+      Exec(..),
+      comp,
+      exec
+   ) where
+
+import Data.Bits
+import qualified Text.Regex.PCRE.ByteString as B
+
+
+data Comp = Blank       -- ^ 'B.compBlank'
+            | Anchored  -- ^ 'B.compAnchored'
+            | Caseless  -- ^ 'B.compCaseless'
+            | Dotall    -- ^ 'B.compDotAll'
+            | Multiline -- ^ 'B.compMultiline'
+            | Utf8      -- ^ 'B.compUTF8'
+            | Ungreedy  -- ^ 'B.compUngreedy'
+   deriving Enum
+
+
+data Exec = BlankE  -- ^ 'B.execBlank'
+            | NotEmpty  -- ^ 'B.execNotEmpty'
+            | Partial   -- ^ 'B.execPartial'
+   deriving Enum
+
+
+compOpt::Comp -> B.CompOption
+compOpt o = case o of
+   Blank -> B.compBlank
+   Anchored -> B.compAnchored
+   Caseless -> B.compCaseless
+   Dotall -> B.compDotAll
+   Multiline -> B.compMultiline
+   Utf8 -> B.compUTF8
+   Ungreedy -> B.compUngreedy
+
+
+comp::[Comp] -> B.CompOption
+comp [] = B.compBlank
+comp l = foldl (.&.) o0 l1
+   where l1 = compOpt <$> l
+         o0 = compOpt $ head l
+
+
+execOpt::Exec -> B.ExecOption
+execOpt o =   case o of
+   BlankE -> B.execBlank
+   NotEmpty -> B.execNotEmpty
+   Partial -> B.execPartial
+
+
+exec::[Exec] -> B.ExecOption
+exec [] = B.execBlank
+exec l = foldl (.&.) o0 l1
+   where l1 = execOpt <$> l
+         o0 = execOpt $ head l
diff --git a/src/Regexdo/Pcre/Replace.hs b/src/Regexdo/Pcre/Replace.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Pcre/Replace.hs
@@ -0,0 +1,195 @@
+module Regexdo.Pcre.Replace(
+   ReplaceCase(..),
+   Replace_cl(..),
+   replaceGroup,
+   replaceMatch
+   )  where
+
+import qualified Data.ByteString as B
+import qualified Text.Regex.Base.RegexLike as R
+
+import Regexdo.TypeDo
+import Regexdo.TypeRegex
+import Regexdo.Pcre.Match
+import Regexdo.Pcre.Result
+import qualified Regexdo.Pcre.Option as O
+import Regexdo.Convert
+
+data ReplaceCase = Once | All | Utf8 | Multiline deriving Eq
+
+type Mm a = (Match_cl Regex a, Match_opt a)
+type Rm a = (Replace_cl' a, Match_cl Regex a)
+
+
+addOpt::Match_opt a =>
+    Needle a -> [O.Comp] -> Needle Regex
+addOpt pat1 opt1 = Needle rx1
+    where rx1 = makeRegexOpts opt1 [] pat1
+
+
+
+ronce::Rm a =>
+    (Needle Regex, Replacement a) -> Haystack a -> a
+ronce (pat1, Replacement repl1) body1@(Haystack bs_str1) =
+      let pl2 = do
+               let m1 = match pat1 body1
+               poslen m1
+      in case pl2 of
+         Nothing -> bs_str1
+         Just pl_arr -> firstGroup pl_arr (repl1,bs_str1)
+
+
+rall::Rm a =>
+    (Needle Regex, Replacement a) -> Haystack a -> a
+rall (pat1, Replacement repl1) body1@(Haystack bs_str1) =
+      let pl_arr_arr1 = do
+               let m1 = matchAll pat1 body1
+               poslen m1
+          folderFn pl_arr acc1 = firstGroup pl_arr (repl1,acc1)
+      in foldr folderFn bs_str1 pl_arr_arr1
+
+
+
+firstGroup::Replace_cl' a =>
+    [PosLen] -> (a,a) -> a
+firstGroup (pl1:_) r1@(repl1,bs_str1) = replaceMatch pl1 r1
+
+
+-- | use in your custom 'GroupReplacer' passed to 'replaceGroup'
+--
+-- see example replacer above
+--
+replaceMatch::Replace_cl' a =>
+    (R.MatchOffset, R.MatchLength) ->
+        (a, a)  -- ^ (new val, acc passed to 'GroupReplacer')
+        -> a
+replaceMatch pl1 (repl1, bs_str1) =
+     concat' [prefix1,repl1,suffix1]
+     where   prefix1 = prefix pl1 bs_str1
+             suffix1 = suffix pl1 bs_str1
+
+
+{- | == dynamic group replace
+   custom replacer fn returns replacement value
+
+   >>> replacer::GroupReplacer String
+       replacer marr1 acc1 = case val1 of
+                              "101" -> fn1 "[A]"
+                              "3" -> fn1 "[Be]"
+       where ol1 = marr1 ! 3 :: (MatchOffset, MatchLength)
+             val1 = extract ol1 acc1
+             fn1 str1 = replaceMatch ol1 (str1,acc1)
+
+    see 'extract'
+
+    below test compares 'Once' vs 'All' options
+
+    >>> groupReplace::IO()
+        groupReplace =  hspec $ do
+            describe "Pcre.Replace group" $ do
+                it "Once" $ do
+                   runFn1 [Once] `shouldBe` "a=[A] b=3 12"
+                it "All" $ do
+                   runFn1 [All] `shouldBe` "a=[A] b=[Be] 12"
+                where runFn1 opts1 =
+                         let   rx1 = Needle "(\\w)(=)(\\d{1,3})"
+                               body1 = Haystack "a=101 b=3 12"
+                         in replaceGroup opts1 (rx1,replacer) body1
+-}
+replaceGroup::Mm a =>
+    [ReplaceCase]->(Needle a,GroupReplacer a) -> Haystack a -> a
+replaceGroup cases (pat1,repl1) = fn1 (pat2,repl1)
+    where pat2 = addOpt pat1 cOpt
+          cOpt = comp cases
+          fn1 = if elem All cases
+                 then rallGroup
+                 else ronceGroup
+
+
+ronceGroup::Match_cl Regex a =>
+    (Needle Regex, GroupReplacer a) -> Haystack a -> a
+ronceGroup (pat1, repl1) body1@(Haystack bs_str1) =
+     let m1 = match pat1 body1
+     in case m1 of
+            Nothing -> bs_str1
+            Just marr1 -> repl1 marr1 bs_str1
+
+
+rallGroup::Match_cl Regex a =>
+    (Needle Regex, GroupReplacer a) -> Haystack a -> a
+rallGroup (pat1, repl1) body1@(Haystack bs_str1) =
+    let marrList1 = matchAll pat1 body1
+    in foldr repl1 bs_str1 marrList1
+
+
+
+class Replace_cl a where
+   replace::[ReplaceCase] -> (Needle a, Replacement a) -> Haystack a -> a
+
+
+class Replace_cl' a where
+   prefix::PosLen -> a -> a
+   suffix::PosLen -> a -> a
+   concat'::[a] -> a
+
+
+{- |
+    >>> replace [Once,Utf8] (Needle "поп", Replacement  "крестьянин") (Haystack "у попа была собака")
+
+    "у крестьянина была собака"
+
+    >>> replace [Once,Utf8] (Needle "^a\\s", Replacement "A") (Haystack "a bc хол.гор.")
+
+    "Abc хол.гор."
+
+ -}
+
+instance Replace_cl String where
+   replace::[ReplaceCase] -> (Needle String, Replacement String) -> Haystack String -> String
+   replace cases0 (pat1,repl1) hay0 = if isUtf8 cases0 then
+        let res1 = fn1 (pat2 $ toByteString <$> pat1, toByteString <$> repl1) $ toByteString <$> hay0
+        in toString res1
+        else fn2 (pat2 pat1,repl1) hay0
+        where pat2 pat1 = addOpt pat1 cOpt
+              cOpt = comp cases0
+              fn1 = if elem All cases0 then rall else ronce
+              fn2 = if elem All cases0 then rall else ronce
+
+
+instance Replace_cl' String where
+   prefix pl1 = take $ fst pl1
+   suffix pl1 = drop (pos1 + len1)
+      where pos1 = fst pl1
+            len1 = snd pl1
+   concat' = concat
+
+
+
+instance Replace_cl B.ByteString where
+   replace::[ReplaceCase] -> (Needle B.ByteString, Replacement B.ByteString) ->
+        Haystack B.ByteString -> B.ByteString
+   replace cases0 (pat1,repl1) hay0 = fn1 (pat2,repl1) hay0
+    where pat2 = addOpt pat1 cOpt
+          cOpt = comp cases0
+          fn1 = if elem All cases0
+                then rall
+                else ronce
+
+
+instance Replace_cl' B.ByteString where
+   prefix pl1 = B.take $ fst pl1
+   suffix pl1 = B.drop (pos1 + len1)
+      where pos1 = fst pl1
+            len1 = snd pl1
+   concat' = B.concat
+
+
+comp::[ReplaceCase]-> [O.Comp]
+comp = map mapFn . filter filterFn
+   where filterFn o1 = o1 `elem` [Utf8,Multiline]
+         mapFn Utf8 = O.Utf8
+         mapFn Multiline = O.Multiline
+
+
+isUtf8::[ReplaceCase] -> Bool
+isUtf8 case0 = Utf8 `elem` case0
diff --git a/src/Regexdo/Pcre/Result.hs b/src/Regexdo/Pcre/Result.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Pcre/Result.hs
@@ -0,0 +1,27 @@
+module Regexdo.Pcre.Result
+    (poslen,
+    value,
+    oneMatchArray,
+    R.extract   -- | 'extract' is reexport from "Text.Regex.Base.RegexLike"
+    ) where
+
+import qualified Data.Array as A(elems)
+import Text.Regex.Base.RegexLike as R
+import Regexdo.TypeDo
+
+
+poslen::Functor f =>
+    f MatchArray -> f [PosLen]
+poslen = (A.elems <$>)
+
+
+-- | all matches
+value::(Functor f, R.Extract a) =>
+    Haystack a -> f MatchArray -> f [a]
+value hay0 results0 = oneMatchArray hay0 <$> results0
+
+
+-- | extracts all values in a MatchArray (matched group)
+oneMatchArray::R.Extract a =>
+    Haystack a -> MatchArray -> [a]
+oneMatchArray (Haystack hay1) arr1 = [R.extract tuple1 hay1 |  tuple1 <- A.elems arr1]
diff --git a/src/Regexdo/Search.hs b/src/Regexdo/Search.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Search.hs
@@ -0,0 +1,108 @@
+{- | from stringsearch package
+
+    this module uses newtypes for args plus function names are tweaked
+
+    regex is treated as ordinary String. Do not use regex.
+    -}
+module Regexdo.Search
+    (break,
+    breakFront,
+    breakEnd,
+    replace,
+    split,
+    splitEnd,
+    splitFront) where
+import qualified Data.ByteString.Search as S
+
+import Regexdo.TypeDo
+import Data.ByteString as B hiding (break, breakEnd, split)
+import qualified Data.ByteString.Lazy as L
+import Prelude hiding (break)
+
+-- ordinary:   a b
+-- front:      a \nb
+-- end:        a\n b
+
+{- | lose needle
+
+    >>> break (Needle ":") (Haystack "0123:oid:90")
+
+    ("0123", "oid:90")
+
+    >>> break (Needle "\n") (Haystack "a\nbc\nde")
+
+    ("a", "bc\\nde")     -}
+break::Needle ByteString -> Haystack ByteString -> (ByteString, ByteString)
+break (Needle pat0) (Haystack b0) =  (h1,t2)
+  where (h1,t1) = S.breakOn pat1 b0
+        len1 = B.length pat1
+        t2 = B.drop len1 t1
+        !pat1 = checkPattern pat0
+
+
+{- | (front, needle + end)
+
+    >>> breakFront (Needle "\n") (Haystack "a\nbc\nde")
+
+    ("a", "\\nbc\\nde")   -}
+breakFront::Needle ByteString -> Haystack ByteString -> (ByteString, ByteString)
+breakFront (Needle pat0)
+  (Haystack b0) = S.breakOn pat1 b0
+     where !pat1 = checkPattern pat0
+
+{- | (front + needle, end)
+
+    >>> breakEnd (Needle "\n") (Haystack "a\nbc\nde")
+
+    ("a\\n", "bc\\nde")     -}
+breakEnd::Needle ByteString -> Haystack ByteString -> (ByteString, ByteString)
+breakEnd (Needle pat0)
+  (Haystack b0) = S.breakAfter pat1 b0
+     where !pat1 = checkPattern pat0
+
+
+{- | >>> replace (Needle "\n") (Replacement ",") (Haystack "a\nbc\nde")
+
+    "a,bc,de"       -}
+replace::Needle ByteString -> Replacement ByteString -> Haystack ByteString -> ByteString
+replace (Needle pat0)
+  (Replacement replacement)
+  (Haystack b0) = B.concat . L.toChunks $ l
+  where l = S.replace pat1 replacement b0
+        !pat1 = checkPattern pat0
+
+{- | >>> split (Needle "\n") (Haystack "a\nbc\nde")
+
+    \["a", "bc", "de"]
+
+    >>> split (Needle " ") (Haystack "a bc de")
+
+    \["a", "bc", "de"]
+
+    >>> split (Needle "\\s") (Haystack "abc de fghi ")
+
+    \["abc de fghi "]        -}
+split::Needle ByteString -> Haystack ByteString -> [ByteString]
+split (Needle pat0)
+  (Haystack b0) = S.split pat1 b0
+     where !pat1 = checkPattern pat0
+
+{- | >>> splitEnd (Needle "\n") (Haystack "a\nbc\nde")
+
+   \["a\\n", "bc\\n", "de"]      -}
+splitEnd::Needle ByteString -> Haystack ByteString -> [ByteString]
+splitEnd (Needle pat0)
+  (Haystack b0) = S.splitKeepEnd pat1 b0
+     where !pat1 = checkPattern pat0
+
+{- | >>> splitFront (Needle "\n") (Haystack "a\nbc\nde")
+
+    \["a", "\\nbc", "\\nde"]     -}
+splitFront::Needle ByteString -> Haystack ByteString -> [ByteString]
+splitFront (Needle pat0)
+  (Haystack b0) = S.splitKeepFront pat1 b0
+     where !pat1 = checkPattern pat0
+
+checkPattern::ByteString -> ByteString
+checkPattern bs0 = if bs0 == B.empty then error "empty pattern"
+      else bs0
diff --git a/src/Regexdo/Trim.hs b/src/Regexdo/Trim.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/Trim.hs
@@ -0,0 +1,24 @@
+module Regexdo.Trim where
+
+import Regexdo.Pcre.Replace(replace,ReplaceCase(All))
+import Regexdo.TypeDo
+import Data.Char(isSpace)
+import qualified Data.ByteString as B
+import Regexdo.Convert
+
+
+{- | removes leading and trailing spaces and tabs   -}
+
+class Trim a where
+    trim::a -> a
+
+
+instance Trim B.ByteString where
+    trim bs1 = replace [All] (rx1,repl) $ Haystack bs1
+       where repl = Replacement B.empty
+             rx1 = rxFn "(^[\\s\\t]+)|([\\s\\t]+$)"
+             rxFn = Needle . toByteString
+
+instance  Trim String where
+    trim = f . f
+       where f = reverse . dropWhile isSpace
diff --git a/src/Regexdo/TypeDo.hs b/src/Regexdo/TypeDo.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/TypeDo.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE DeriveFunctor #-}
+module Regexdo.TypeDo where
+
+import Text.Regex.Base.RegexLike
+
+-- pcre
+type GroupReplacer acc = (MatchArray -> acc -> acc) -- MatchArray -> acc -> acc
+
+
+-- stringsearch, Pcre.Replace
+data Needle n = Needle n deriving (Functor)          -- Bs, String, RegexPcre
+data Haystack h = Haystack h deriving (Functor)                -- Bs, String
+data Replacement r = Replacement r deriving (Functor)     --    Bs, String
+
+type PosLen = (MatchOffset, MatchLength)
diff --git a/src/Regexdo/TypeRegex.hs b/src/Regexdo/TypeRegex.hs
new file mode 100644
--- /dev/null
+++ b/src/Regexdo/TypeRegex.hs
@@ -0,0 +1,15 @@
+-- | reexport common types from "Text.Regex.PCRE"
+module Regexdo.TypeRegex (
+    W.Regex(..),
+    R.MatchArray(..),
+    W.CompOption(..),
+    W.ExecOption()
+    )   where
+
+import Text.Regex.PCRE.ByteString as B (Regex)
+import Text.Regex.PCRE.String as S (Regex)
+import Text.Regex.Base.RegexLike as R (MatchArray)
+import Text.Regex.PCRE.Wrap as W
+
+type RegexB = B.Regex
+type RegexS = S.Regex
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,17 @@
+module Main where
+
+import qualified TestRegex.Format as F
+import qualified TestRegex.Pcre as P
+import qualified TestRegex.Replace as R
+import qualified TestRegex.StringSearch as S
+import qualified TestRegex.TestTrim as T
+
+
+
+main::IO()
+main = do
+            F.main
+            P.main
+            R.main
+            S.main
+            T.main
diff --git a/test/TestRegex/Format.hs b/test/TestRegex/Format.hs
new file mode 100644
--- /dev/null
+++ b/test/TestRegex/Format.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE NoOverloadedStrings #-}
+module TestRegex.Format where
+
+import Test.Hspec
+import Regexdo.Format
+
+
+main::IO()
+main = hspec $ do
+       describe "Habase.Bin.Format" $ do
+          it "list arg 0,0" $ do
+            format "на первое {0}, на второе {0}" ["перловка"] `shouldBe` "на первое перловка, на второе перловка"
+          it "list arg 0,1" $ do
+            format "Polly {0} a {1}" ["gets","cracker"] `shouldBe` "Polly gets a cracker"
+          it "map arg" $ do
+            format "овчинка {a} не {b}" [("a","выделки"),("b","стоит")] `shouldBe` "овчинка выделки не стоит"
+          it "pad" $ do
+            pad '-' 5 "abc" `shouldBe` "--abc"
diff --git a/test/TestRegex/Pcre.hs b/test/TestRegex/Pcre.hs
new file mode 100644
--- /dev/null
+++ b/test/TestRegex/Pcre.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE FlexibleContexts, NoOverloadedStrings #-}
+module TestRegex.Pcre where
+
+import Test.Hspec
+import Regexdo.TypeDo
+import qualified Regexdo.Pcre.Match as M
+import qualified Regexdo.Pcre.Result as R
+import Regexdo.Convert
+
+
+main::IO()
+main = do
+   test " ?String " Needle id M.match
+   test " [String] " Needle id M.matchAll
+   test " ?Bs " (Needle . b) (b <$>) M.match
+   test " [Bs] " (Needle . b) (b <$>) M.matchAll
+   hspec $ do
+    describe " matchTest " $ do
+     it " matchTest " $ do
+        M.matchTest (Needle "^ша") (Haystack "шапка") `shouldBe` True
+        M.matchTest (Needle "^cd") (Haystack "abcde") `shouldBe` False
+        M.matchTest (Needle "^ab") (Haystack "abc") `shouldBe` True
+
+n = "d1"
+h = Haystack "abcd1efg d1hij"
+b = toByteString
+
+
+test testCase patternCtor bodyCtor matchFn =   hspec $ do
+   describe testCase $ do
+         it " val " $ do
+            R.value h res `shouldSatisfy` check
+         it " tup " $
+            R.poslen res  `shouldSatisfy` check
+         it " pl " $
+            R.poslen res  `shouldSatisfy` check
+   where   res = matchFn pat $ bodyCtor h
+           pat = patternCtor n
+
+
+
+class Functor f =>  Pred f x where
+   check::f x -> Bool
+
+instance Pred Maybe [String] where
+   check Nothing = False
+   check (Just _) = True
+
+instance Pred [] [String] where
+   check [] = False
+   check (h:t) = True
+
+instance Pred Maybe [PosLen] where
+   check Nothing = False
+   check (Just _) = True
+
+instance Pred [] [PosLen] where
+   check [] = False
+   check (h:t) = True
diff --git a/test/TestRegex/Replace.hs b/test/TestRegex/Replace.hs
new file mode 100644
--- /dev/null
+++ b/test/TestRegex/Replace.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE BangPatterns, NoOverloadedStrings #-}
+module TestRegex.Replace where
+
+import Test.Hspec
+import Data.Array((!))
+import Regexdo.TypeDo
+
+import Regexdo.Pcre.Replace
+import Regexdo.Convert
+import Data.ByteString as B
+import Text.Regex.Base.RegexLike
+
+
+main::IO()
+main = do
+   onceUtf8
+   latinOnceAll
+   groupReplace
+   doc
+
+
+doc::IO()
+doc = hspec $ do
+       describe "Pcre.Replace doc" $ do
+          it "replace" $ do
+            replace [Once,Utf8] (Needle "поп", Replacement  "крестьянин") (Haystack "у попа была собака") `shouldBe` "у крестьянина была собака"
+
+
+
+onceUtf8::IO()
+onceUtf8 = hspec $ do
+       describe "Pcre.Replace Once Utf8" $ do
+          it "^a\\s" $ do
+            replace [Once,Utf8] (Needle "^a\\s", Replacement "A") (Haystack "a bc хол.гор.") `shouldBe` "Abc хол.гор."
+          it "^b\\s" $ do
+            replace [Once,Utf8] (Needle "^b\\s", Replacement "A") (Haystack "a bc хол.гор.") `shouldBe` "a bc хол.гор."
+
+
+latinOnceAll::IO()
+latinOnceAll =  hspec $ do
+         describe "Pcre.Replace" $ do
+            it "Once" $ do
+               runFn [Once] `shouldBe` toByteString "a=R1 b=11 12"
+            it "All" $ do
+               runFn [All] `shouldBe` toByteString "a=R1 b=R1 12"
+            where runFn opts =
+                     let   rx1 = pattern "(?<==)(\\d{2})"
+                           body = Haystack $ toByteString haystack
+                           haystack = "a=10 b=11 12"
+                           repl1  = replacement "R1"
+                     in replace opts (rx1,repl1) body
+
+
+pattern::String -> Needle ByteString
+pattern = Needle . toByteString
+
+
+replacement::String -> Replacement ByteString
+replacement = Replacement . toByteString
+
+
+groupReplace::IO()
+groupReplace =  hspec $ do
+         describe "Pcre.Replace group" $ do
+            it "Once" $ do
+               runFn1 [Once] `shouldBe` "a=[A] b=3 12"
+            it "All" $ do
+               runFn1 [All] `shouldBe` "a=[A] b=[Be] 12"
+            where runFn1 opts1 =
+                     let   rx1 = Needle "(\\w)(=)(\\d{1,3})"
+                           body1 = Haystack "a=101 b=3 12"
+                     in replaceGroup opts1 (rx1,replacer) body1
+
+
+replacer::GroupReplacer String
+replacer marr1 acc1 = case val1 of
+                          "101" -> fn1 "[A]"
+                          "3" -> fn1 "[Be]"
+   where ol1 = marr1 ! 3 :: (MatchOffset, MatchLength)
+         val1 = extract ol1 acc1
+--         !val2 = trace (show val1) val1
+         fn1 str1 = replaceMatch ol1 (str1,acc1)
diff --git a/test/TestRegex/StringSearch.hs b/test/TestRegex/StringSearch.hs
new file mode 100644
--- /dev/null
+++ b/test/TestRegex/StringSearch.hs
@@ -0,0 +1,74 @@
+module TestRegex.StringSearch where
+
+import Prelude hiding(break)
+import Test.Hspec
+import Control.Exception (evaluate)
+import Regexdo.TypeDo
+import Regexdo.Search
+import qualified Data.ByteString as B
+import Regexdo.Convert
+
+
+main::IO()
+main = hspec $ do
+       describe "Habase.Regex.StringSearch.Search" $ do
+          it "break :" $ do
+            break (Needle ":") (Haystack "0123:oid:90") `shouldBe` ("0123", "oid:90")
+          it "break" $ do
+            break (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ("a", "bc\nde")
+          it "break front" $ do
+            breakFront (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ("a", "\nbc\nde")
+          it "break end" $ do
+            breakEnd (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ("a\n", "bc\nde")
+          it "replace" $ do
+            replace (Needle "\n") (Replacement ",") (Haystack "a\nbc\nde") `shouldBe` "a,bc,de"
+          it "split" $ do
+            split (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ["a", "bc", "de"]
+          it "split_sp" $ do
+            split (Needle " ") (Haystack "a bc de") `shouldBe` ["a", "bc", "de"]
+          it "split end" $ do
+            splitEnd (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ["a\n", "bc\n", "de"]
+          it "split front" $ do
+            splitFront (Needle "\n") (Haystack "a\nbc\nde") `shouldBe` ["a", "\nbc", "\nde"]
+          it "split regex" $ do
+            split (Needle "\\s") (Haystack "abc de fghi ") `shouldBe` ["abc de fghi "]
+
+
+       describe "StringSearch.Search zerolength" $ do
+          it "break" $ do
+            evaluate (errFn break) `shouldThrow` anyException
+          it "break front" $ do
+            evaluate (errFn breakFront) `shouldThrow` anyException
+          it "break end" $ do
+            evaluate (errFn breakEnd) `shouldThrow` anyException
+          it "replace" $ do
+            evaluate (replace (Needle B.empty) with body) `shouldThrow` anyException
+          it "split" $ do
+            evaluate (errFn split) `shouldThrow` anyException
+          it "split end" $ do
+            evaluate (errFn splitEnd) `shouldThrow` anyException
+          it "split front" $ do
+            evaluate (errFn splitFront) `shouldThrow` anyException
+
+       describe "StringSearch.Search break delim not found" $ do
+            it "delim not found" $ do
+                break_nf `shouldBe` ( b "a\nbc\nde", B.empty)
+
+       where   pat = Needle "\n"
+               pat_sp = Needle " "
+               body = Haystack "a\nbc\nde"
+               body_sp = Haystack "a bc de"
+               with = Replacement ","
+               break1 = break pat body
+               breakFront1 = breakFront pat body
+               breakEnd1 = breakEnd pat body
+               replace1 = replace pat with body
+               split1 = split pat body
+               split_sp1 = split pat_sp body_sp
+               splitFront1 = splitFront pat body
+               splitEnd1 = splitEnd pat body
+               errFn fn1 = fn1 (Needle B.empty) body
+               b = toByteString
+               --   break delim not found
+               pat_nf = Needle ":"
+               break_nf = break pat_nf body
diff --git a/test/TestRegex/TestTrim.hs b/test/TestRegex/TestTrim.hs
new file mode 100644
--- /dev/null
+++ b/test/TestRegex/TestTrim.hs
@@ -0,0 +1,18 @@
+module TestRegex.TestTrim where
+
+import Test.Hspec
+import Debug.Trace
+import qualified Data.Text as T (strip, pack, unpack)
+import Regexdo.Trim
+import Regexdo.Convert
+
+
+main::IO()
+main = hspec $ do
+          describe "Habase.Bin.Bistro" $ do
+              it "trim" $ trace (show trimmed1) trimmed1 `shouldBe` (toByteString trimmed)
+              it "trim 2" $
+                trim sTrim `shouldBe` "aiy  \n  pdsodfg987"
+    where sTrim = "    aiy  \n  pdsodfg987   "
+          trimmed1 = trim $ toByteString sTrim
+          trimmed = (T.unpack . T.strip . T.pack) sTrim
