diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2012, IPI PAN
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NLP/Adict/Basic.hs b/NLP/Adict/Basic.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Basic.hs
@@ -0,0 +1,61 @@
+module NLP.Adict.Basic
+( search
+) where
+
+import Data.Ix (range)
+import qualified Data.Array as A
+import qualified Data.Vector as V
+
+import NLP.Adict.Core
+import NLP.Adict.Trie hiding (insert)
+
+-- | Find all words within a trie with restricted generalized edit distance
+-- lower or equall to k.
+search :: Cost a -> Double -> Word a -> TrieD a b -> [([a], b, Double)]
+search cost k x trie =
+    foundHere ++ foundLower
+  where
+    foundHere
+        | dist' m <= k = case valueIn trie of
+            Just y  -> [([], y, dist' m)]
+            Nothing -> []
+        | otherwise = []
+    foundLower
+        | minimum (A.elems distV) > k = []
+        | otherwise = concatMap searchLower $ anyChild trie
+    searchLower = search' cost k dist' x
+
+    dist' = (A.!) distV 
+    distV = A.array bounds [(i, dist i) | i <- range bounds]
+    bounds = (0, m)
+    m = V.length x
+
+    dist 0 = 0
+    dist i = dist' (i-1) + (delete cost) i (x#i)
+
+search' :: Cost a -> Double -> (Int -> Double)
+        -> Word a -> (a, TrieD a b) -> [([a], b, Double)]
+search' cost k distP x (c, trie) =
+    foundHere ++ map appendChar foundLower
+  where
+    foundHere
+        | dist' m <= k = case valueIn trie of
+            Just y  -> [([c], y, dist' m)]
+            Nothing -> []
+        | otherwise = []
+    foundLower
+        | minimum (A.elems distV) > k = []
+        | otherwise = concatMap searchLower $ anyChild trie
+    searchLower = search' cost k dist' x
+    appendChar (cs, y, w) = ((c:cs), y, w)
+
+    dist' = (A.!) distV 
+    distV = A.array bounds [(i, dist i) | i <- range bounds]
+    bounds  = (0, m)
+    m = V.length x
+
+    dist 0 = distP 0  + (insert cost) 0       c
+    dist i = minimum
+        [ distP (i-1) + (subst cost)  i (x#i) c
+        , dist' (i-1) + (delete cost) i (x#i) 
+        , distP i     + (insert cost) i       c ]
diff --git a/NLP/Adict/Brute.hs b/NLP/Adict/Brute.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Brute.hs
@@ -0,0 +1,20 @@
+module NLP.Adict.Brute
+( search
+) where
+
+import Data.Maybe (mapMaybe)
+
+import NLP.Adict.Core
+import NLP.Adict.Dist
+
+-- | Find all words within a list with restricted generalized edit distance
+-- from x lower or equall to k.
+search :: Cost a -> Double -> Word a -> [(Word a, b)] -> [(Word a, b, Double)]
+search cost k x =
+    mapMaybe check
+  where
+    check (y, v)
+        | dist <= k = Just (y, v, dist)
+        | otherwise = Nothing
+      where
+        dist = editDist cost x y
diff --git a/NLP/Adict/Core.hs b/NLP/Adict/Core.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Core.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module NLP.Adict.Core
+( Word
+, Pos
+, Weight
+, costDefault
+, Cost (..)
+, (#)
+) where
+
+import qualified Data.Vector as V
+
+(#) :: V.Vector a -> Int -> a
+x#i = x V.! (i-1)
+{-# INLINE (#) #-}
+
+-- | Word with 'a' character type.
+type Word a = V.Vector a
+
+-- | Position.
+type Pos = Int
+
+-- | Cost of edit operation.  It has to be non-negative!
+type Weight = Double
+
+-- | Cost represents a cost (or weight) of a symbol insertion, deletion or
+-- substitution.  It can depend on edit operation position and on symbol
+-- values.
+data Cost a = Cost
+    { insert :: Pos -> a -> Weight
+    , delete :: Pos -> a -> Weight
+    , subst  :: Pos -> a -> a -> Weight }
+
+-- | Simple cost function: all edit operations cost 1.
+costDefault :: Eq a => Cost a
+costDefault =
+    Cost _insert _delete _subst
+  where
+    _insert _ _ = 1
+    _delete _ _ = 1
+    _subst _ x y
+        | x == y    = 0
+        | otherwise = 1
+{-# INLINABLE costDefault #-}
+{-# SPECIALIZE costDefault :: Cost Char #-}
diff --git a/NLP/Adict/CostDiv.hs b/NLP/Adict/CostDiv.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/CostDiv.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module NLP.Adict.CostDiv
+( Group (..)
+, CostDiv (..)
+, mapWeight
+, costDefault
+
+, Sub
+, mkSub
+, unSub
+, SubMap
+, subOn
+, mkSubMap
+
+, toCost
+, toCostInf
+) where
+
+import qualified Data.Set as S
+import qualified Data.Map as M
+
+import NLP.Adict.Core (Pos, Cost(..), Weight)
+
+-- | TODO: Add Choice data contructor together with appropriate
+-- implementation: Choice Char Weight
+data Group a = Filter
+    { predic :: a -> Bool
+    , weight :: Weight }
+
+mapWeight :: (Weight -> Weight) -> Group a -> Group a
+mapWeight f g = g { weight = f (weight g) }
+           
+-- | Cost function with edit operations divided with respect to weight.
+-- Two operations with the same cost should be assigned to the same group.
+data CostDiv a = CostDiv
+    { insert ::        [Group a]
+    , delete :: a   -> Weight
+    , subst  :: a   -> [Group a]
+    , posMod :: Pos -> Weight }
+
+costDefault :: Eq a => CostDiv a
+costDefault =
+    CostDiv insert delete subst posMod
+  where
+    insert   = [Filter (const True) 1]
+    delete _ = 1
+    subst x  =
+        [ Filter eq 0
+        , Filter ot 1 ]
+      where
+        eq = (x==)
+        ot = not.eq
+    posMod = const 1
+{-# INLINABLE costDefault #-}
+{-# SPECIALIZE costDefault :: CostDiv Char #-}
+
+-- | Substition desription for some character x.
+type Sub a = M.Map Weight (S.Set a)
+
+mkSub :: Ord a => [(a, Weight)] -> Sub a
+mkSub xs = M.fromListWith S.union [(w, S.singleton x) | (x, w) <- xs]
+
+unSub :: Ord a => Sub a -> [Group a]
+unSub sub =
+    [ Filter (`S.member` charSet) weight
+    | (weight, charSet) <- M.toAscList sub ]
+
+-- | Susbtitution map for an alphabet.
+type SubMap a = M.Map a (Sub a)
+
+subOn :: Ord a => a -> SubMap a -> Sub a
+subOn x sm = case M.lookup x sm of
+    Just sd -> sd
+    Nothing -> M.empty
+
+mkSubMap :: Ord a => [(a, a, Weight)] -> SubMap a
+mkSubMap xs = fmap mkSub $
+    M.fromListWith (++)
+        [ (x, [(y, w)])
+        | (x, y, w) <- xs ]
+
+-- | Transform CostDiv to plain Cost function with default weight value.
+toCost :: Double -> CostDiv a -> Cost a
+toCost defa CostDiv{..} =
+    Cost ins del sub
+  where
+    del k x   = delete x                                * posMod k
+    ins k x   = mini [w | Filter f w <- insert,  f x]   * posMod k
+    sub k x y = mini [w | Filter f w <- subst x, f y]   * posMod k
+    mini []   = defa
+    mini xs   = minimum xs
+
+-- | Transform CostDiv to plain Cost function with default weight value
+-- set to +Infinity.
+toCostInf :: CostDiv a -> Cost a
+toCostInf =
+    let inf = 1 / 0
+    in  toCost inf
diff --git a/NLP/Adict/DAWG.hs b/NLP/Adict/DAWG.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/DAWG.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module NLP.Adict.DAWG
+( DAWGD
+, DAWG (..)
+, size
+, row
+, Row (..)
+, entry
+, charOn
+, valueIn
+, edges
+, edgeOn
+
+, serialize
+, deserialize
+) where
+
+import Control.Applicative ((<$>))
+import Data.Maybe (listToMaybe)
+import Data.Binary (Binary, get, put)
+import qualified Data.Vector as V
+
+import NLP.Adict.DAWG.Node
+
+type DAWGD a b = DAWG a (Maybe b)
+
+data DAWG a b = DAWG
+    { root  :: Int
+    , array :: V.Vector (Row a b) }
+
+size :: DAWG a b -> Int
+size = V.length . array
+{-# INLINE size #-}
+
+row :: DAWG a b -> Int -> Row a b
+row dag k = array dag V.! k
+{-# INLINE row #-}
+
+data Row a b = Row
+    { rowValue :: b
+    , rowEdges :: V.Vector (a, Int) }
+
+valueIn :: DAWG a b -> Int -> b
+valueIn dag k = rowValue (array dag V.! k)
+{-# INLINE valueIn #-}
+
+edges :: DAWG a b -> Int -> [(a, Int)]
+edges dag k = V.toList . rowEdges $ row dag k
+{-# INLINE edges #-}
+
+edgeOn :: Eq a => DAWG a b -> Int -> a -> Maybe Int
+edgeOn DAWG{..} k x =
+    let r = array V.! k
+    in  snd <$> V.find ((x==).fst) (rowEdges r)
+
+entry :: DAWG a (Maybe b) -> [Int] -> Maybe ([a], b)
+entry dag xs = do
+    x <- mapM (charOn dag) (zip (root dag:xs) xs)
+    r <- maybeLast xs >>= valueIn dag 
+    return (x, r)
+  where
+    maybeLast [] = Nothing
+    maybeLast ys = Just $ last ys
+
+charOn :: DAWG a b -> (Int, Int) -> Maybe a
+charOn dag (root, x) = listToMaybe
+    [c | (c, y) <- edges dag root, x == y]
+
+serialize :: Ord a => DAWG a b -> [Node a b]
+serialize = map unRow . V.toList . array
+
+-- | Assumptiom: root node is last in the serialization list.
+deserialize :: Ord a => [Node a b] -> DAWG a b
+deserialize xs =
+    let arr = V.fromList $ map mkRow xs
+    in  DAWG (V.length arr - 1) arr
+
+unRow :: Ord a => Row a b -> Node a b
+unRow Row{..} = mkNode rowValue (V.toList rowEdges)
+{-# INLINE unRow #-}
+
+mkRow :: Ord a => Node a b -> Row a b
+mkRow n = Row (nodeValue n) (V.fromList $ nodeEdges n)
+{-# INLINE mkRow #-}
+
+instance (Ord a, Binary a, Binary b) => Binary (DAWG a b) where
+    put = put . serialize
+    get = deserialize <$> get
+
+-- goDown :: DAWG a -> Int -> DAWG a
+-- goDown DAWG{..} k = DAWG k array
+-- 
+-- instance T.Trie DAWGArray where
+--     unTrie dag@DAWGArray{..} =
+--         let row = array V.! root
+--         in  ( valueIn row
+--             , [ (c, goDown dag k)
+--               | (c, k) <- U.toList (edgeVec row) ] )
+--     child x dag@DAWGArray{..} =
+--         let row = array V.! root
+--         in  goDown dag <$> snd <$> U.find ((x==).fst) (edgeVec row)
diff --git a/NLP/Adict/DAWG/Node.hs b/NLP/Adict/DAWG/Node.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/DAWG/Node.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module NLP.Adict.DAWG.Node
+( Node
+, mkNode
+, nodeValue
+, nodeEdges
+) where
+
+import Data.Binary (Binary)
+
+newtype Node a b = Node { unNode :: (b, [(a, Int)]) }
+    deriving (Show, Eq, Ord, Binary)
+
+mkNode :: b -> [(a, Int)] -> Node a b
+mkNode x xs = Node (x, xs)
+
+nodeValue :: Node a b -> b
+nodeValue = fst . unNode
+
+nodeEdges :: Node a b -> [(a, Int)]
+nodeEdges = snd . unNode
diff --git a/NLP/Adict/Dist.hs b/NLP/Adict/Dist.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Dist.hs
@@ -0,0 +1,29 @@
+module NLP.Adict.Dist
+( editDist
+) where
+
+import qualified Data.Array as A
+import qualified Data.Vector as V
+import Data.Ix (range)
+
+import NLP.Adict.Core
+
+-- | Restricted generalized edit distance between two words with
+-- given cost function.
+editDist :: Cost a -> Word a -> Word a -> Weight
+editDist cost x y =
+    dist' m n
+  where
+    dist' i j = distA A.! (i, j)
+    distA = A.array bounds [(k, uncurry dist k) | k <- range bounds]
+    bounds  = ((0, 0), (m, n))
+    m = V.length x
+    n = V.length y
+
+    dist 0 0 = 0
+    dist i 0 = dist' (i-1) 0 + (delete cost) i (x#i)
+    dist 0 j = dist' 0 (j-1) + (insert cost) 0       (y#j)
+    dist i j = minimum
+        [ dist' (i-1) (j-1)  + (subst cost)  i (x#i) (y#j)
+        , dist' (i-1) j      + (delete cost) i (x#i) 
+        , dist' i (j-1)      + (insert cost) i       (y#j) ]
diff --git a/NLP/Adict/Graph.hs b/NLP/Adict/Graph.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Graph.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module NLP.Adict.Graph
+( minPath
+, Edges
+, IsEnd
+) where
+
+import Data.Function (on)
+import qualified Data.PQueue.Min as P
+import qualified Data.Map as M
+
+-- | Adjacent list for a given node @n. We assume, that the list
+-- is given in an ascending order.
+type Edges n w = n -> [(w, n)]
+type Edge n w  = (n, w, n)
+
+-- | Is @n node an ending node?
+type IsEnd n = n -> Bool
+
+-- | Non-empty list of adjacent nodes given in ascending order.
+-- We use new data type to implement custom Eq and Ord instances.
+data Adj n w = Adj
+    { from :: n
+    , to   :: [(w, n)] }
+    deriving Show
+
+proxy :: Adj n w -> (w, n)
+proxy = head . to
+{-# INLINE proxy #-}
+
+folls :: Adj n w -> [(w, n)]
+folls = tail . to
+{-# INLINE folls #-}
+
+instance (Eq n, Eq w) => Eq (Adj n w) where
+    (==) = (==) `on` proxy
+
+instance (Ord n, Ord w) => Ord (Adj n w) where
+    compare = compare `on` proxy
+
+-- | Remove minimal edge (from, weight, to) from the queue.
+minView :: (Ord n, Ord w) => P.MinQueue (Adj n w)
+        -> Maybe (Edge n w, P.MinQueue (Adj n w))
+minView queue = do
+    (adj, queue') <- P.minView queue
+    let p       = from adj
+        (w, q)  = proxy adj
+        e       = (p, w, q)
+    return (e, push queue' p (folls adj))
+
+push :: (Ord n, Ord w) => P.MinQueue (Adj n w) -> n
+     -> [(w, n)] -> P.MinQueue (Adj n w)
+push queue _ [] = queue
+push queue p xs = P.insert (Adj p xs) queue
+
+-- | Find shortes path from a beginning node to any ending node.
+minPath :: (Show n, Show w, Ord n, Ord w, Num w, Fractional w)
+        => w -> Edges n w -> IsEnd n -> n -> Maybe ([n], w)
+minPath threshold edgesFrom isEnd beg =
+
+    shortest M.empty $ P.singleton (Adj beg [(0, beg)])
+
+  where
+
+    -- | @visited -- set of visited nodes.
+    --   @queue -- priority queue,
+    shortest visited queue = do
+        (edge, queue') <- minView queue
+        shortest' visited queue' edge
+
+    shortest' visited queue (p, w, q)
+        | isEnd q               = Just (reverse (trace visited' q), w)
+        | q `M.member` visited  = shortest visited  queue
+        | otherwise             = shortest visited' queue'
+      where
+        visited' = M.insert q p visited
+        queue' = push queue q $
+                takeWhile ((<= threshold) . fst)
+                [(w + u, s) | (u, s) <- edgesFrom q]
+
+    trace visited n
+        | m == n    = [n]
+        | otherwise = n : trace visited m
+      where
+        m = visited M.! n
diff --git a/NLP/Adict/Nearest.hs b/NLP/Adict/Nearest.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Nearest.hs
@@ -0,0 +1,82 @@
+module NLP.Adict.Nearest
+( search
+) where
+
+import Control.Applicative ((<$>))
+import Control.Monad (guard)
+import Data.Maybe (isJust, catMaybes)
+import Data.List (sortBy)
+import Data.Ord (comparing)
+import Data.Function (on)
+import qualified Data.Vector as V
+
+import NLP.Adict.Core (Pos, Weight, Word, (#))
+import NLP.Adict.CostDiv
+import NLP.Adict.DAWG
+import NLP.Adict.Graph
+
+type NodeID  = Int
+data Node a = Node
+    { nodeID   :: {-# UNPACK #-} !NodeID
+    , nodePos  :: {-# UNPACK #-} !Pos
+    , nodeChar :: !(Maybe a) }
+    deriving (Show)
+
+proxy :: Node a -> (NodeID, Pos)
+proxy n = (nodeID n, nodePos n)
+{-# INLINE proxy #-}
+
+instance Eq (Node a) where
+    (==) = (==) `on` proxy
+
+instance Ord (Node a) where
+    compare = compare `on` proxy
+
+data Which a
+    = Del Weight
+    | Ins (Group a)
+    | Sub (Group a)
+
+weightOf :: Which a -> Weight
+weightOf (Del w) = w
+weightOf (Ins g) = weight g
+weightOf (Sub g) = weight g
+{-# INLINE weightOf #-}
+
+-- | We can check, if CostDiv satisfies basic properties.  On the other
+-- hand, we do not do this for plain Cost function.
+search :: Show a => CostDiv a -> Double -> Word a -> DAWGD a b -> Maybe ([a], b, Double)
+search cost z x dag = do
+    (xs, w) <- minPath z edgesFrom isEnd (Node (root dag) 0 Nothing)
+    let form = catMaybes . map nodeChar $ xs
+    r <- valueIn dag $ nodeID $ last xs
+    return (form, r, w)
+  where
+    edgesFrom (Node n i _) =
+        concatMap follow $ sortBy (comparing weightOf) groups
+      where
+        j = i+1
+
+        groups = insGroups ++ delGroups ++ subGroups
+        insGroups = Ins . mapWeight (*posMod cost i) <$>
+            insert cost
+        delGroups = Del . (*posMod cost j) <$> do
+            guard (j <= V.length x)
+            return $ delete cost (x#j)
+        subGroups = Sub . mapWeight (*posMod cost j) <$> do
+            guard (j <= V.length x)
+            subst cost (x#j)
+
+        follow (Ins (Filter f w)) =
+            [ (w, Node m i (Just c))
+            | (c, m) <- edges dag n
+            , f c ]
+
+        follow (Del w) = [(w, Node n j Nothing)]
+
+        follow (Sub (Filter f w)) =
+            [ (w, Node m j (Just c))
+            | (c, m) <- edges dag n
+            , f c ]
+
+    isEnd (Node n k _) = k == V.length x && isJust (valueIn dag n)
diff --git a/NLP/Adict/Trie.hs b/NLP/Adict/Trie.hs
new file mode 100644
--- /dev/null
+++ b/NLP/Adict/Trie.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE BangPatterns #-}
+
+module NLP.Adict.Trie
+( TrieD
+, Trie (..)
+, unTrie
+, child
+, anyChild
+, mkTrie
+, setValue
+, substChild
+, insert
+
+, size
+, follow
+, lookup
+, fromLang
+, fromList
+, toList
+
+, serialize
+, deserialize
+, toDAWG
+) where
+
+import Prelude hiding (lookup)
+import Control.Applicative ((<$>), (<*>))
+import Control.Monad ((>=>))
+import Data.List (foldl')
+import Data.Binary (Binary, get, put)
+import qualified Data.Map as M
+
+import NLP.Adict.DAWG.Node
+
+type TrieD a b = Trie a (Maybe b)
+
+data Trie a b = Trie
+    { valueIn :: b
+    , edgeMap :: M.Map a (Trie a b) }
+    deriving (Show, Eq, Ord)
+
+instance Functor (Trie a) where
+    fmap f Trie{..} = Trie (f valueIn) (fmap (fmap f) edgeMap)
+
+instance (Ord a, Binary a, Binary b) => Binary (Trie a b) where
+    put Trie{..} = do
+        put valueIn
+        put edgeMap
+    get = Trie <$> get <*> get
+
+unTrie :: Trie a b -> (b, [(a, Trie a b)])
+unTrie t = (valueIn t, M.toList $ edgeMap t)
+{-# INLINE unTrie #-}
+
+child :: Ord a => a -> Trie a b -> Maybe (Trie a b)
+child x Trie{..} = x `M.lookup` edgeMap
+{-# INLINE child #-}
+
+anyChild :: Trie a b -> [(a, Trie a b)]
+anyChild = snd . unTrie
+{-# INLINE anyChild #-}
+
+mkTrie :: Ord a => b -> [(a, Trie a b)] -> Trie a b
+mkTrie !v !cs = Trie v (M.fromList cs)
+{-# INLINE mkTrie #-}
+
+empty :: Ord a => TrieD a b
+empty = mkTrie Nothing []
+{-# INLINE empty #-}
+
+setValue :: b -> Trie a b -> Trie a b
+setValue !x !t = t { valueIn = x }
+{-# INLINE setValue #-}
+
+substChild :: Ord a => a -> Trie a b -> Trie a b -> Trie a b
+substChild !x !trie !newChild =
+    let how _ = Just newChild
+        !edges = M.alter how x (edgeMap trie)
+    in trie { edgeMap = edges }
+{-# INLINABLE substChild #-}
+{-# SPECIALIZE substChild
+    :: Char
+    -> Trie Char b
+    -> Trie Char b
+    -> Trie Char b #-}
+
+insert :: Ord a => [a] -> b -> TrieD a b -> TrieD a b
+insert [] v t = setValue (Just v) t
+insert (x:xs) v t = substChild x t . insert xs v $
+    case child x t of
+        Just t' -> t'
+        Nothing -> empty
+{-# INLINABLE insert #-}
+{-# SPECIALIZE insert
+    :: String -> b
+    -> TrieD Char b
+    -> TrieD Char b #-}
+
+size :: Trie a b -> Int
+size t = 1 + sum (map (size.snd) (anyChild t))
+
+follow :: Ord a => [a] -> Trie a b -> Maybe (Trie a b)
+follow xs t = foldr (>=>) return (map child xs) t
+
+lookup :: Ord a => [a] -> TrieD a b -> Maybe b
+lookup xs t = follow xs t >>= valueIn
+
+fromList :: Ord a => [([a], b)] -> TrieD a b
+fromList xs =
+    let update t (x, v) = insert x v t
+    in  foldl' update empty xs
+
+toList :: TrieD a b -> [([a], b)]
+toList t = case valueIn t of
+    Just y -> ([], y) : lower
+    Nothing -> lower
+  where
+    lower = concatMap goDown $ anyChild t
+    goDown (x, t') = map (addChar x) $ toList t'
+    addChar x (xs, y) = (x:xs, y)
+
+fromLang :: Ord a => [[a]] -> TrieD a ()
+fromLang xs = fromList [(x, ()) | x <- xs]
+
+toDAWG :: (Ord a, Ord b) => Trie a b -> Trie a b
+toDAWG = deserialize . serialize
+
+serialize :: (Ord a, Ord b) => Trie a b -> [Node a b]
+serialize r =
+    [ mkNode (valueIn t)
+        [ (c, m M.! s)
+        | (c, s) <- anyChild t ]
+    | t <- M.elems m' ]
+  where
+    m  = collect r
+    m' = M.fromList [(y, x) | (x, y) <- M.toList m]
+
+-- | FIXME: Null node list case.
+deserialize :: (Ord a, Ord b) => [Node a b] -> Trie a b
+deserialize =
+    snd . M.findMax . foldl' update M.empty
+  where
+    update m n =
+        let t = mkTrie (nodeValue n) [(c, m M.! k) | (c, k) <- nodeEdges n]
+        in  M.insert (M.size m) t m
+
+-- | Collect unique tries and assign identifiers to them.
+collect :: (Ord a, Ord b) => Trie a b -> M.Map (Trie a b) Int
+collect t = collect' M.empty t
+
+collect' :: (Ord a, Ord b) => M.Map (Trie a b) Int
+         -> Trie a b -> M.Map (Trie a b) Int
+collect' m0 t = M.alter f t m
+  where
+    !m = foldl' collect' m0 (M.elems $ edgeMap t)
+    !k = M.size m
+    f Nothing  = Just k
+    f (Just x) = Just x
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#! /usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/adict.cabal b/adict.cabal
new file mode 100644
--- /dev/null
+++ b/adict.cabal
@@ -0,0 +1,57 @@
+name:               adict
+version:            0.1.0
+synopsis:           Approximate dictionary searching
+description:
+    Approximate dictionary searching library.
+license:            BSD3
+license-file:       LICENSE
+cabal-version:      >= 1.8
+copyright:          Copyright (c) 2012 IPI PAN
+author:             Jakub Waszczuk
+maintainer:         waszczuk.kuba@gmail.com
+stability:          experimental
+category:           Natural Language Processing
+homepage:           https://github.com/kawu/adict
+build-type:         Simple
+
+library
+    build-depends:
+        base >= 4 && < 5
+      , containers
+      , vector
+      , array
+      , pqueue
+      , binary
+
+    exposed-modules:
+        NLP.Adict.Core
+      , NLP.Adict.CostDiv
+      , NLP.Adict.Dist
+      , NLP.Adict.Brute
+      , NLP.Adict.Trie
+      , NLP.Adict.DAWG.Node
+      , NLP.Adict.DAWG
+      , NLP.Adict.Graph
+      , NLP.Adict.Basic
+      , NLP.Adict.Nearest
+
+    ghc-options: -Wall -O2
+
+test-suite tests
+    type: exitcode-stdio-1.0
+    hs-source-dirs: tests
+    main-is: Properties.hs
+    build-depends:
+        base >= 4 && < 5.0
+      , QuickCheck
+      , containers
+      , vector
+      , test-framework >= 0.3.3
+      , test-framework-quickcheck2 >= 0.2.9
+      , adict
+
+  ghc-options: -Wall
+
+source-repository head
+    type: git
+    location: git://github.com/kawu/adict.git
diff --git a/tests/Properties.hs b/tests/Properties.hs
new file mode 100644
--- /dev/null
+++ b/tests/Properties.hs
@@ -0,0 +1,183 @@
+{-# LANGUAGE RecordWildCards #-}
+
+-- | QuickCheck properties which should be satisfied by the Adict.
+
+import Control.Applicative ((<$>), (<*>), (<|>), pure)
+import Data.Maybe (fromJust)
+import qualified Data.Set as S
+import qualified Data.Map as M
+import qualified Data.Vector as V
+
+import Test.QuickCheck
+import Test.Framework (Test, defaultMain)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import NLP.Adict.Core
+import qualified NLP.Adict.CostDiv as C
+import qualified NLP.Adict.Brute as Br
+import qualified NLP.Adict.Basic as Ba
+import qualified NLP.Adict.Nearest as Nr
+import qualified NLP.Adict.Trie as Trie
+import qualified NLP.Adict.DAWG as DAWG
+
+-- | Check parameters.
+posRange :: (Int, Int)
+posRange  = (0, 4)	-- ^ Position of random edit op
+
+weightRange :: (Weight, Weight)
+weightRange  = (0, 10)	-- ^ Weight of edit op 
+
+posModRange :: (Double, Double)
+posModRange  = (0.1, 2)	-- ^ Position modifier
+
+descRange :: (Int, Int)
+descRange = (0, 25)	-- ^ Number of random edit ops of given type
+
+langRange :: (Int, Int)
+langRange = (0, 25)	-- ^ Size of language
+
+arbitraryPos :: Gen Pos
+arbitraryPos = choose posRange
+
+arbitraryPosMod :: Gen Double
+arbitraryPosMod = choose posModRange
+
+arbitraryWeight :: Gen Weight
+arbitraryWeight = choose weightRange
+
+arbitraryChar :: Gen Char
+arbitraryChar = elements ['a'..'z']
+
+arbitraryWord :: Gen String
+arbitraryWord = listOf arbitraryChar
+
+arbitraryLang :: (Int, Int) -> Gen [String]
+arbitraryLang r = nub <$> (flip vectorOf arbitraryWord =<< choose r)
+
+-- | Helper structure with Arbitrary instance (implementation below),
+-- which can be transformed to the Adict Cost function.
+data CostDesc = CostDesc
+    { insD :: M.Map (Pos, Char) Weight
+    , delD :: M.Map (Pos, Char) Weight
+    , subD :: M.Map (Pos, Char, Char) Weight }
+    deriving Show
+
+-- | Construct Cost function from a description structure.
+toCost :: CostDesc -> Cost Char
+toCost CostDesc{..} = Cost ins del sub
+  where
+    ins i x = fromJust $ (i, x) `M.lookup` insD <|> return 1
+    del i x = fromJust $ (i, x) `M.lookup` delD <|> return 1
+    sub i x y = fromJust $ (i, x, y) `M.lookup` subD  <|> return (sub' x y)
+    sub' x y
+        | x == y    = 0
+        | otherwise = 1
+
+instance Arbitrary CostDesc where
+    arbitrary = do
+        ins <- M.fromList <$> mkList insElem 
+        del <- M.fromList <$> mkList delElem
+        sub <- M.fromList <$> mkList subElem
+        return $ CostDesc ins del sub
+      where 
+	insElem  = (,)  <$> arbitraryPos <*> arbitraryChar
+	delElem  = (,)  <$> arbitraryPos <*> arbitraryChar
+	subElem  = (,,) <$> arbitraryPos <*> arbitraryChar <*> arbitraryChar
+        mkList m = do
+            k <- choose descRange
+            vectorOf k ((,) <$> m <*> arbitraryWeight)
+
+-- | Helper structure with Arbitrary instance,
+-- which can be transformed to the Adict Cost function.
+data CostDivDesc = CostDivDesc
+    { insDivD :: [(Char, Weight)]
+    , delDivD :: [(Char, Weight)]
+    , subDivD :: [(Char, Char, Weight)]
+    , posModD :: M.Map Pos Double }
+    deriving Show
+
+instance Arbitrary CostDivDesc where
+    arbitrary = do
+        ins <- mkList insElem 
+        del <- mkList delElem
+        sub <- mkList subElem
+        posMod <- M.fromList <$> mkList posElem
+        return $ CostDivDesc ins del sub posMod
+      where 
+	insElem  = (,)  <$> arbitraryChar <*> arbitraryWeight
+	delElem  = (,)  <$> arbitraryChar <*> arbitraryWeight
+	subElem  = (,,) <$> arbitraryChar <*> arbitraryChar <*> arbitraryWeight
+	posElem  = (,)  <$> arbitraryPos <*> arbitraryPosMod
+        mkList m = do
+            k <- choose descRange
+            vectorOf k m
+
+-- | Construct Cost function from a description structure.
+toCostDiv :: CostDivDesc -> C.CostDiv Char
+toCostDiv CostDivDesc{..} = C.CostDiv ins del sub posMod
+  where
+    delMap = M.fromList delDivD
+    subMap = C.mkSubMap subDivD
+    ins = C.unSub . C.mkSub $ insDivD
+    del x = fromJust $ x `M.lookup` delMap <|> pure 1
+    sub x
+        =  C.Filter (x==) 0
+        :  C.unSub (C.subOn x subMap)
+        ++ [C.Filter (const True) 1]
+    posMod k = fromJust $ k `M.lookup` posModD <|> pure 1
+
+-- | Custom language generation.
+newtype Lang = Lang [String] deriving Show
+getWords :: Lang -> [String]
+getWords (Lang xs) = xs
+instance Arbitrary Lang where
+    arbitrary = Lang <$> arbitraryLang langRange
+
+-- | QuickCheck property1: set of matching dictionary entries should
+-- be the same no matter which searching function is used.
+pBaseEqBrute :: CostDesc -> Positive Double -> String -> Lang -> Bool
+pBaseEqBrute costDesc kP xR lang =
+    let br = (nub . map unWord) (Br.search cost k x ys)
+        ba = nub (Ba.search cost k x trie)
+    in  br == ba
+  where
+    x = V.fromList xR
+    cost = toCost costDesc
+    k = getPositive kP
+    trie = Trie.fromLang (getWords lang)
+    ys = [(V.fromList y, ()) | y <- getWords lang]
+    unWord (word, v, w) = (V.toList word, v, w)
+
+pBaseEqNearest :: CostDivDesc -> Positive Double -> String -> Lang -> Bool
+pBaseEqNearest costDesc kP xR lang =
+    let ba = Ba.search cost k x trie
+        nr = Nr.search costDiv k x dawg
+    in  check ba nr
+  where
+    check [] (Just _) = False
+    check [] Nothing  = True
+    check ys (Just y) = y `elem`
+        ( let thd (_, _, c) = c
+              m = minimum . map thd $ ys
+          in  filter ((<=m) . thd) ys )
+    check _  _        = False
+
+    x = V.fromList xR
+    k = getPositive kP
+
+    costDiv = toCostDiv costDesc
+    cost = C.toCostInf costDiv
+
+    trie = Trie.fromLang (getWords lang)
+    dawg = DAWG.deserialize . Trie.serialize $ trie
+
+nub :: Ord a => [a] -> [a]
+nub = S.toList . S.fromList
+
+main :: IO ()
+main = defaultMain tests
+
+tests :: [Test]
+tests =
+    [ testProperty "brute force == basic" pBaseEqBrute
+    , testProperty "nearest == minimum basic" pBaseEqNearest ]
