diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,131 @@
+
+
+
+
+			 The "Artistic License"
+
+				Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+	"Package" refers to the collection of files distributed by the
+	Copyright Holder, and derivatives of that collection of files
+	created through textual modification.
+
+	"Standard Version" refers to such a Package if it has not been
+	modified, or has been modified in accordance with the wishes
+	of the Copyright Holder as specified below.
+
+	"Copyright Holder" is whoever is named in the copyright or
+	copyrights for the package.
+
+	"You" is you, if you're thinking about copying or distributing
+	this Package.
+
+	"Reasonable copying fee" is whatever you can justify on the
+	basis of media cost, duplication charges, time of people involved,
+	and so on.  (You will not be required to justify it to the
+	Copyright Holder, but only to the computing community at large
+	as a market that must bear the fee.)
+
+	"Freely Available" means that no fee is charged for the item
+	itself, though there may be fees involved in handling the item.
+	It also means that recipients of the item may redistribute it
+	under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder.  A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+    a) place your modifications in the Public Domain or otherwise make them
+    Freely Available, such as by posting said modifications to Usenet or
+    an equivalent medium, or placing the modifications on a major archive
+    site such as uunet.uu.net, or by allowing the Copyright Holder to include
+    your modifications in the Standard Version of the Package.
+
+    b) use the modified Package only within your corporation or organization.
+
+    c) rename any non-standard executables so the names do not conflict
+    with standard executables, which must also be provided, and provide
+    a separate manual page for each non-standard executable that clearly
+    documents how it differs from the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+    a) distribute a Standard Version of the executables and library files,
+    together with instructions (in the manual page or equivalent) on where
+    to get the Standard Version.
+
+    b) accompany the distribution with the machine-readable source of
+    the Package with your modifications.
+
+    c) give non-standard executables non-standard names, and clearly
+    document the differences in manual pages (or equivalent), together
+    with instructions on where to get the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package.  You may charge any fee you choose for support of this
+Package.  You may not charge a fee for this Package itself.  However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.  You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whoever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.  If such scripts or library files are aggregated with this
+Package via the so-called "undump" or "unexec" methods of producing a
+binary executable image, then distribution of such an image shall
+neither be construed as a distribution of this Package nor shall it
+fall under the restrictions of Paragraphs 3 and 4, provided that you do
+not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other
+languages) supplied by you and linked into this Package in order to
+emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution.  Such use shall not be
+construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+				The End
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,281 @@
+{-# LANGUAGE ImplicitParams, NamedFieldPuns #-}
+import Data.SBV
+import Data.Set (toList)
+import Data.List (sort, nub)
+import Data.Bits
+import Data.Monoid
+import System.IO
+import Control.Monad (foldM)
+import Control.Monad.State
+import Control.Monad.Trans (MonadIO, liftIO)
+import qualified Data.Char
+import Text.Regex.TDFA.Pattern
+import Text.Regex.TDFA.ReadRegex (parseRegex)
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IntSet
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as IntMap
+import System.Environment
+
+type Len = Int
+type Str = [SWord8]
+type Offset = SWord8
+type Flips = SWord64
+type Captures = SWord64
+
+maxHits = 65535
+minLength = 0
+maxLength = 255
+maxRepeat = 3 -- 7 and 15 are also good
+
+lengths p = IntSet.toList . fst $ runState (possibleLengths $ parse p) mempty
+
+defaultRegex = "a(b|c)d{2,3}e*"
+parse r = case parseRegex r of
+    Right (pattern, _) -> pattern
+    Left x -> error $ show x
+
+type GroupLens = IntMap IntSet
+
+possibleLengths :: Pattern -> State GroupLens IntSet
+possibleLengths pat = case pat of
+    PGroup (Just idx) p -> do
+        lenP <- possibleLengths p
+        modify $ IntMap.insert idx lenP
+        return lenP
+    PGroup _ p -> possibleLengths p
+    PCarat{} -> zero
+    PDollar{} -> zero
+    PQuest p -> fmap (`mappend` IntSet.singleton 0) $ possibleLengths p
+    PDot{} -> one
+    POr ps -> fmap mconcat $ mapM possibleLengths ps
+    PChar{} -> one
+    PConcat [] -> return mempty
+    PConcat ps -> fmap (foldl1 sumSets) (mapM possibleLengths ps)
+        where
+        sumSets s1 s2 = IntSet.unions [ IntSet.map (+elm) s2 | elm <- IntSet.elems s1 ]
+    PAny {} -> one
+    PAnyNot {} -> one
+    PEscape {getPatternChar = ch}
+        | ch `elem` "ntrfaedwsWSD" -> one
+        | ch `elem` "b" -> zero
+        | Data.Char.isDigit ch -> gets (IntMap.findWithDefault (error $ "No such capture: " ++ [ch]) (charToDigit ch))
+        | Data.Char.isAlpha ch -> error $ "Unsupported escape: " ++ [ch]
+        | otherwise -> one
+    PBound low (Just high) p -> manyTimes p low high
+    PBound low _ p -> manyTimes p low (low+maxRepeat)
+    PPlus p -> manyTimes p 1 (maxRepeat+1)
+    PStar _ p -> manyTimes p 0 maxRepeat
+    _ -> error $ show pat
+    where
+    one = return $ IntSet.singleton 1
+    zero = return $ IntSet.singleton 0
+    manyTimes p low high = do
+        lenP <- possibleLengths p
+        return $ IntSet.unions [ IntSet.map (*i) lenP | i <- [low..high] ]
+
+charToDigit ch = Data.Char.ord ch - Data.Char.ord '0'
+
+exactMatch :: (?pats :: [Pattern]) => Len -> Symbolic SBool
+exactMatch len = do
+    str <- mkFreeVars len
+    initialBits <- free "bits"
+    let ?str = str
+    let initialStatus = Status
+            { ok = true
+            , pos = toEnum len
+            , bits = initialBits
+            , captureAt = minBound
+            , captureLen = minBound
+            }
+        runPat s pat = let ?pat = pat in
+            ite (ok s &&& pos s .== toEnum len)
+                (match s{ pos = 0, captureAt = minBound, captureLen = minBound })
+                s{ ok = false, pos = maxBound, bits = maxBound }
+    let finalStatus = foldl runPat initialStatus ?pats
+    return $
+        (bits finalStatus .== 0 &&& pos finalStatus .== toEnum len &&& ok finalStatus)
+
+data Status = Status
+    { ok :: SBool
+    , pos :: Offset
+    , bits :: Flips
+    , captureAt :: Captures
+    , captureLen :: Captures
+    }
+
+type Idx = Word8
+
+instance Mergeable Status where
+  symbolicMerge t s1 s2 = Status
+    { ok = symbolicMerge t (ok s1) (ok s2)
+    , pos = symbolicMerge t (pos s1) (pos s2)
+    , bits = symbolicMerge t (bits s1) (bits s2)
+    , captureAt = symbolicMerge t (captureAt s1) (captureAt s2)
+    , captureLen = symbolicMerge t (captureLen s1) (captureLen s2)
+    }
+
+choice :: (?str :: Str, ?pat :: Pattern) => Flips -> [Flips -> Status] -> Status
+choice _ [] = error "X"
+choice bits [a] = a bits
+choice bits [a, b] = ite (lsb bits) (a bits') (b bits')
+    where
+    bits' = bits `shiftR` 1
+choice bits xs = ite (lsb bits)
+    (choice bits' $ take half xs)
+    (choice bits' $ drop half xs)
+    where
+    half = length xs `div` 2
+    bits' = bits `shiftR` 1
+
+writeCapture :: Captures -> Int -> SWord8 -> Captures
+writeCapture cap idx val = foldl writeBit cap ([0..7] `zip` blastLE val)
+    where
+    writeBit c (i, bit) = setBitTo c (idx * 8 + i) bit
+
+readCapture cap idx = fromBitsLE [ bitValue cap (idx * 8 + i) | i <- [ 0..7 ] ]
+    
+match :: (?str :: Str, ?pat :: Pattern) => Status -> Status
+match s@Status{ ok, pos, bits, captureAt, captureLen } = ite (isFailedMatch ||| isOutOfBounds) __FAIL__ $ case ?pat of
+    PGroup (Just idx) p -> let s'@Status{ pos = pos' } = next p in s'
+        { captureAt = writeCapture captureAt idx pos
+        , captureLen = writeCapture captureLen idx (pos' - pos)
+        }
+    PGroup _ p -> next p
+    PCarat{} -> ite (isBegin ||| (charAt (pos-1) .== ord '\n')) s __FAIL__
+    PDollar{} -> ite (isEnd ||| (charAt (pos+1) .== ord '\n')) s __FAIL__
+    PQuest p -> choice bits [\b -> let ?pat = p in match s{ bits = b }, const s]
+    PDot{} -> cond isDot
+    POr [p] -> next p
+    POr ps -> choice bits $ map (\p -> \b -> let ?pat = p in match s{ bits = b }) ps
+    PChar{ getPatternChar = ch } -> cond (ord ch .== cur)
+    PConcat [p] -> next p
+    PConcat ps -> step ps s
+        where
+        step [] s' = s'
+        step (p:ps) s' = 
+            let s''@Status{ ok } = let ?pat = p in match s'
+                res = step ps s''
+             in ite ok res __FAIL__
+    PAny {getPatternSet = pset} -> case pset of
+        PatternSet (Just cset) _ _ _ -> oneOf $ toList cset
+        _ -> error "TODO"
+    PAnyNot {getPatternSet = pset} -> case pset of
+        PatternSet (Just cset) _ _ _ -> noneOf $ toList cset
+        _ -> error "TODO"
+    PEscape {getPatternChar = ch} -> case ch of
+        'n' -> condChar '\n'
+        't' -> condChar '\t'
+        'r' -> condChar '\r'
+        'f' -> condChar '\f'
+        'a' -> condChar '\a'
+        'e' -> condChar '\ESC'
+        'd' -> cond isDigit
+        'w' -> cond (isWordCharAt pos)
+        's' -> cond isWhiteSpace
+        'W' -> cond (isDot &&& bnot (isWordCharAt pos))
+        'S' -> cond (isDot &&& bnot isWhiteSpace)
+        'D' -> cond (isDot &&& bnot isDigit)
+        'b' -> ite isWordBoundary s __FAIL__
+        _ | Data.Char.isDigit ch -> 
+            let from = readCapture captureAt num
+                len = readCapture captureLen num
+                num = charToDigit ch
+             in ite (matchCapture (from :: SWord8) len 0) s{ pos = pos+len } __FAIL__
+          | Data.Char.isAlpha ch -> error $ "Unsupported escape: " ++ [ch]
+          | otherwise  -> cond (ord ch .== cur)
+    PBound low (Just high) p -> let s'@Status{ ok = ok' } = (let ?pat = PConcat (replicate low p) in match s) in
+        ite ok' (let ?pat = p in manyTimes s' $ high - low) s'
+    PBound low _ p -> let ?pat = (PBound low (Just $ low+maxRepeat) p) in match s
+    PPlus p ->
+        let s'@Status{ ok = ok, pos = pos'} = next p
+            res = let ?pat = PStar True p in match s'
+         in ite ok res s'
+    PStar _ p -> next $ PBound 0 Nothing p
+    _ -> error $ show ?pat
+    where
+    next p = let ?pat = p in match s
+    isDot = (cur .>= ord ' ' &&& cur .<= ord '~')
+    isOutOfBounds = pos .> toEnum (length ?str)
+    isFailedMatch = bnot ok
+    manyTimes s n
+        | n <= 0 = s
+        | otherwise = let s'@Status{ ok = ok' } = match s in
+            ite ok' (choice bits [\b -> s{ bits = b }, \b -> manyTimes s'{ bits = b } (n-1)]) s
+    cur = charAt pos
+    charAt = select ?str 0
+    condChar ch = cond (ord ch .== cur)
+    cond b = ite b s{ pos = pos+1 } __FAIL__
+    oneOf cs = cond $ bOr [ ord ch .== cur | ch <- cs ]
+    noneOf cs = cond $ bAnd ((cur .>= ord ' ') : (cur .<= ord '~') : [ ord ch ./= cur | ch <- cs ])
+    ord = toEnum . Data.Char.ord
+    matchCapture :: SWord8 -> SWord8 -> SWord8 -> SBool
+    matchCapture from len off = (len .<= off) |||
+        (charAt (pos+off) .== charAt (from+off) &&& matchCapture from len (off+1))
+    __FAIL__ = s{ ok = false, pos = maxBound, bits = maxBound }
+    isEnd = (pos .== toEnum (length ?str))
+    isBegin = (pos .== 0)
+    isWhiteSpace = cur .== 32 ||| (9 .<= cur &&& 13 .>= cur &&& 11 ./= cur)
+    isDigit = (ord '0' .<= cur &&& ord '9' .>= cur)
+    isWordCharAt at = let char = charAt at in
+        (char .>= ord 'A' &&& char .<= ord 'Z')
+            |||
+        (char .>= ord 'a' &&& char .<= ord 'z')
+            |||
+        (char .== ord '_')
+    isWordBoundary = case length ?str of
+        0 -> false
+        _ -> (isEnd &&& isWordCharAt (pos-1)) |||
+             (isBegin &&& isWordCharAt pos) |||
+             (isWordCharAt (pos-1) <+> isWordCharAt pos)
+
+main = do
+    hSetBuffering stdout NoBuffering
+    args <- getArgs
+    case args of
+        [] -> do
+            prog <- getProgName
+            if prog == "<interactive>" then run defaultRegex else do
+                fail $ "Usage: " ++ prog ++ " regex [regex...]"
+        rx -> runMany rx
+
+runMany regexes = do
+    let ?pats = map parse regexes
+    let lens = IntSet.toAscList $ foldl1 IntSet.intersection (map lenOf ?pats)
+    tryWith lens 0
+    where
+    lenOf p = fst $ runState (possibleLengths p) mempty
+
+run :: String -> IO ()
+run regex = runMany [regex]
+
+tryWith :: (?pats :: [Pattern]) => [Int] -> Int -> IO ()
+tryWith [] acc = return ()
+tryWith (len:lens) acc = if len > maxLength then return () else do
+    AllSatResult allRes <- allSat $ exactMatch len
+    showResult allRes acc
+    where
+    showResult [] a = tryWith lens a
+    showResult (r:rs) a = do
+        disp' $ getModel r
+        if (a+1 >= maxHits) then return () else showResult rs (a+1)
+
+disp' :: ([Word8], Word64) -> IO ()
+disp' (str, choices) = do
+    print $ map chr str
+    {-
+    putStr (show choices)
+    putStr "\t["
+    putStr $ map chr str
+    putStr "]\n"
+    -}
+    where
+    chr :: Word8 -> Char
+    chr = Data.Char.chr . fromEnum
+
+disp :: [Word8] -> IO ()
+disp str = do
+    putStrLn $ map chr str
+    where
+    chr :: Word8 -> Char
+    chr = Data.Char.chr . fromEnum
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,6 @@
+#!/usr/bin/env runghc
+> import Distribution.Simple
+> import System.Cmd (rawSystem)
+> 
+> main :: IO ()
+> main = defaultMainWithHooks simpleUserHooks
diff --git a/regex-genex.cabal b/regex-genex.cabal
new file mode 100644
--- /dev/null
+++ b/regex-genex.cabal
@@ -0,0 +1,26 @@
+Name            : regex-genex
+Version         : 0.1.20110523
+license         : OtherLicense
+license-file    : LICENSE
+cabal-version   : >= 1.6
+copyright       : 2011 Audrey Tang
+maintainer      : Audrey Tang <audreyt@audreyt.org>
+category        : Text, Regex
+stability       : experimental
+build-type      : Simple
+homepage        : https://github.com/audreyt/regex-genex
+synopsis        : From a regex, generate all possible strings it can match
+description     : From a regex, generate all possible strings it can match
+author          : Audrey Tang <audreyt@audreyt.org>
+Tested-With:    GHC==7.0.2
+
+executable genex
+    main-is:            Main.hs
+    hs-source-dirs:     .
+
+    build-depends:
+        base >= 3 && < 5, haskell98, mtl, containers, sbv, regex-tdfa
+
+source-repository head
+  type:     git
+  location: http://github.com/audreyt/regex-genex
