diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,5 +1,16 @@
 -*-change-log-*-
 
+2.0.1   Jul 2018
+        * External configuration of disambiguation tiers
+
+2.0.0   Jul 2018
+        * Support Morfeusz DAGs
+        * Use the Morfeusz format
+        * Drop dependency on Maca
+
+0.7.5	Aug 2016
+	* Start using `stack` to avoid dependency problems
+
 0.7.4	Nov 2014
 	* Ignore non-printable characters for compatibility with Maca
 
diff --git a/concraft-pl.cabal b/concraft-pl.cabal
--- a/concraft-pl.cabal
+++ b/concraft-pl.cabal
@@ -1,12 +1,12 @@
 name:               concraft-pl
-version:            0.7.4
+version:            2.0.1
 synopsis:           Morphological tagger for Polish
 description:
     A morphological tagger for Polish based on the concraft library.
 license:            BSD3
 license-file:       LICENSE
-cabal-version:      >= 1.6
-copyright:          Copyright (c) 2013 Jakub Waszczuk
+cabal-version:      >= 1.10
+copyright:          Copyright (c) 2012-2018 Jakub Waszczuk, IPI PAN
 author:             Jakub Waszczuk
 maintainer:         waszczuk.kuba@gmail.com
 stability:          experimental
@@ -19,24 +19,34 @@
 data-files: config/nkjp-tagset.cfg
 
 library
+    default-language:
+        Haskell2010
     hs-source-dirs: src
-
     build-depends:
         base >= 4 && < 5
-      , concraft                >= 0.9      && < 0.10
+      , concraft                >= 0.11     && < 0.12
+      , pedestrian-dag          >= 0.2      && < 0.3
+      , crf-chain1-constrained  >= 0.4      && < 0.5
+      , crf-chain2-tiers        >= 0.3      && < 0.4
       , tagset-positional       >= 0.3      && < 0.4
-      , sgd                     >= 0.3.3    && < 0.4
+      , sgd                     >= 0.4      && < 0.5
       , containers              >= 0.4      && < 0.6
       , bytestring              >= 0.9      && < 0.11
       , text                    >= 0.11     && < 1.3
-      , aeson                   >= 0.6      && < 0.9
-      , binary                  >= 0.5      && < 0.8
-      , process                 >= 1.1      && < 1.3
+      , aeson                   >= 0.6      && < 1.3
+      , binary                  >= 0.5      && < 0.9
+      , process                 >= 1.1      && < 1.7
       , mtl                     >= 2.0      && < 2.3
-      , transformers            >= 0.2      && < 0.5
+      , transformers            >= 0.2      && < 0.6
       , network                 >= 2.3      && < 2.7
       , lazy-io                 >= 0.1      && < 0.2
       , split                   >= 0.2      && < 0.3
+      , scotty                  >= 0.11     && < 0.12
+      , http-types              >= 0.9      && < 0.13
+      , wreq                    >= 0.5      && < 0.6
+      , lens                    >= 4.15     && < 4.17
+      , dhall                   >= 1.11     && < 1.12
+      , vector                  >= 0.11     && < 0.13
 
     exposed-modules:
         NLP.Concraft.Polish
@@ -45,6 +55,16 @@
       , NLP.Concraft.Polish.Request
       , NLP.Concraft.Polish.Server
 
+      -- , NLP.Concraft.Polish.DAG
+      -- , NLP.Concraft.Polish.DAG2
+      , NLP.Concraft.Polish.DAGSeg
+      , NLP.Concraft.Polish.DAG.Morphosyntax
+      , NLP.Concraft.Polish.DAG.Format.Base
+      , NLP.Concraft.Polish.DAG.Server
+
+      , NLP.Concraft.Polish.DAG.Config
+      , NLP.Concraft.Polish.DAG.Config.Disamb
+
     other-modules:
         NLP.Concraft.Polish.Format.Plain
 
@@ -55,8 +75,22 @@
     location: https://github.com/kawu/concraft-pl.git
 
 executable concraft-pl
+    default-language:
+        Haskell2010
     build-depends:
-        cmdargs                 >= 0.10     && < 0.11
-    hs-source-dirs: src, tools
+        concraft-pl
+      , concraft
+      , tagset-positional
+      , text
+      , bytestring
+      , sgd
+      , pedestrian-dag
+      , crf-chain1-constrained
+      , containers
+      , base                    >= 4        && < 5
+      , cmdargs                 >= 0.10     && < 0.11
+      , dhall                   >= 1.11     && < 1.12
+      , filepath                >= 1.3      && < 1.5
+    hs-source-dirs: tools
     main-is: concraft-pl.hs
     ghc-options: -Wall -O2 -threaded -rtsopts
diff --git a/src/NLP/Concraft/Polish.hs b/src/NLP/Concraft/Polish.hs
--- a/src/NLP/Concraft/Polish.hs
+++ b/src/NLP/Concraft/Polish.hs
@@ -29,6 +29,7 @@
 ) where
 
 
+import           Prelude hiding (Word)
 import           Control.Applicative ((<$>))
 import qualified Data.Text.Lazy as L
 import qualified Data.Set as S
@@ -79,8 +80,8 @@
 tiersDefault =
     [tier1, tier2]
   where
-    tier1 = D.Tier True $ S.fromList ["cas", "per"]
-    tier2 = D.Tier False $ S.fromList
+    tier1 = D.Tier True False $ S.fromList ["cas", "per"]
+    tier2 = D.Tier False False $ S.fromList
         [ "nmb", "gnd", "deg", "asp" , "ngt", "acm"
         , "acn", "ppr", "agg", "vlc", "dot" ]
 
diff --git a/src/NLP/Concraft/Polish/DAG/Config.hs b/src/NLP/Concraft/Polish/DAG/Config.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAG/Config.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+
+module NLP.Concraft.Polish.DAG.Config
+  ( Config(..)
+  ) where
+
+
+import           Dhall
+import qualified Data.Aeson as JSON
+
+import qualified NLP.Concraft.Polish.DAG.Config.Disamb as D
+
+
+-- | Tagging configuration (everything apart tagset).
+data Config = Config
+  { disambCfg :: D.DisambCfg
+  -- , guessCfg :: G.GuessCfg
+  } deriving (Generic, Show)
+
+
+instance Interpret Config
+
+instance JSON.FromJSON Config
+instance JSON.ToJSON Config where
+  toEncoding = JSON.genericToEncoding JSON.defaultOptions
diff --git a/src/NLP/Concraft/Polish/DAG/Config/Disamb.hs b/src/NLP/Concraft/Polish/DAG/Config/Disamb.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAG/Config/Disamb.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE DeriveGeneric #-}
+
+
+module NLP.Concraft.Polish.DAG.Config.Disamb
+  ( DisambCfg (..)
+  , TierCfg (..)
+  , Set(..)
+  ) where
+
+
+import qualified Data.Set as S
+import qualified Data.Vector as V
+import qualified Data.Text as T
+
+import           Dhall
+import qualified Data.Aeson as JSON
+
+
+-- | Disamb module configuration.
+data DisambCfg = DisambCfg
+  { tiersCfg :: [TierCfg]
+  } deriving (Generic, Show, Eq, Ord)
+
+
+-- | Disamb tiers configuration.
+data TierCfg = TierCfg
+  { withPos :: Bool
+  , withEos :: Bool
+  , withAtts :: Set T.Text
+  } deriving (Generic, Show, Eq, Ord)
+
+
+instance Interpret DisambCfg
+
+instance JSON.FromJSON DisambCfg
+instance JSON.ToJSON DisambCfg where
+  toEncoding = JSON.genericToEncoding JSON.defaultOptions
+
+
+instance Interpret TierCfg
+
+instance JSON.FromJSON TierCfg
+instance JSON.ToJSON TierCfg where
+  toEncoding = JSON.genericToEncoding JSON.defaultOptions
+
+
+------------------------------
+-- Set
+------------------------------
+
+
+newtype Set a = Set {unSet :: S.Set a}
+  deriving (Generic, Show, Eq, Ord)
+
+instance (Ord a, Interpret a) => Interpret (Set a) where
+    autoWith = fmap
+      (fmap $ Set . S.fromList . V.toList)
+      autoWith
+
+instance (Ord a, JSON.FromJSON a) => JSON.FromJSON (Set a)
+instance JSON.ToJSON a => JSON.ToJSON (Set a) where
+  toEncoding = JSON.genericToEncoding JSON.defaultOptions
diff --git a/src/NLP/Concraft/Polish/DAG/Format/Base.hs b/src/NLP/Concraft/Polish/DAG/Format/Base.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAG/Format/Base.hs
@@ -0,0 +1,304 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+
+module NLP.Concraft.Polish.DAG.Format.Base
+(
+-- * Printing
+  ShowCfg (..)
+, ProbType (..)
+, showSent
+, showData
+
+-- * Parsing
+, parseData
+, parseSent
+) where
+
+
+import           Prelude hiding (Word)
+import           Data.Monoid (mconcat, mappend)
+import qualified Data.Map as M
+import           Data.List (intersperse, groupBy)
+import           Data.Maybe (listToMaybe)
+import           Data.String (IsString)
+import           Data.Data (Data)
+import           Data.Typeable (Typeable)
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as L
+import qualified Data.Text.Lazy.Builder as L
+import           Text.Printf (printf)
+import           Text.Read (readMaybe)
+
+import qualified Data.DAG as DAG
+-- import qualified Data.CRF.Chain1.Constrained.DAG.Dataset.Internal as DAG
+
+import qualified NLP.Concraft.DAG.Morphosyntax as X
+-- import qualified NLP.Concraft.Polish.DAG2 as C
+-- import           NLP.Concraft.Polish.DAG2 (AnnoSent(..))
+import qualified NLP.Concraft.Polish.DAGSeg as C
+import           NLP.Concraft.Polish.DAGSeg (AnnoSent(..))
+import qualified NLP.Concraft.Polish.Morphosyntax as I
+
+import           NLP.Concraft.Polish.DAG.Morphosyntax hiding (tag, Tag)
+import qualified NLP.Concraft.Polish.DAG.Morphosyntax as PolX
+
+
+-----------------------------
+-- Base
+-----------------------------
+
+
+type Tag = PolX.Interp PolX.Tag
+
+
+-----------------------------
+-- Showing
+-----------------------------
+
+
+-- | Printing configuration.
+data ShowCfg = ShowCfg
+--   { suppressProbs :: Bool
+--     -- ^ Do not show any probabilities
+  { probType  :: ProbType
+    -- ^ Which type of probabilities to show (unless suppressed)
+  , numericDisamb :: Bool
+    -- ^ Print disamb markers as numerical values instead of probability values
+  }
+
+
+-- | Type of probabilities.
+data ProbType
+  = Marginals
+    -- ^ Marginals of the disambiguation model
+  | MaxProbs
+    -- ^ Max probabilities of the disambiguation model
+  | GuessedMarginals
+    -- ^ Marginals of the guessing model
+  deriving (Show, Eq, Ord, Enum, Typeable, Data)
+-- Above, deriving Typeable and Data so that it can be easily parsed
+-- for the command-line tool.
+
+
+-- mkProbType :: ProbType -> Disamb.ProbType
+-- mkProbType Marginals = Disamb.Marginals
+-- mkProbType MaxProbs = Disamb.MaxProbs
+
+
+-- | Show entire data.
+showData :: ShowCfg -> [[AnnoSent]] -> L.Text
+showData cfg
+  = flip L.append "\n"
+  . L.toLazyText
+  . mconcat
+  . intersperse "\n"
+  . map (buildSents cfg)
+
+-- | Show the given sentence.
+showSent :: ShowCfg -> [AnnoSent] -> L.Text
+showSent cfg = L.toLazyText . buildSents cfg
+
+buildSents :: ShowCfg -> [AnnoSent] -> L.Builder
+buildSents cfg =
+  finalize . map (buildSent cfg)
+  where
+    -- finalize = (`mappend` "\n") . mconcat . intersperse "\n"
+    finalize = mconcat
+
+buildSent :: ShowCfg -> AnnoSent -> L.Builder
+buildSent showCfg AnnoSent{..} = finalize $ do
+  let dag = guessSent
+  edgeID <- DAG.dagEdges dag
+  let tailNode = DAG.begsWith edgeID dag
+      headNode = DAG.endsWith edgeID dag
+      X.Seg{..} = DAG.edgeLabel edgeID dag
+  interp <- map Just (M.toList (X.unWMap tags)) ++
+            if known word then [] else [Nothing]
+  return $ case interp of
+    Just (interp@Interp{..}, weight) -> buildInterp
+      showCfg tailNode headNode word interp
+      (case probType showCfg of
+          Marginals ->
+            tagWeightIn edgeID interp marginals
+          MaxProbs ->
+            tagWeightIn edgeID interp maxProbs
+          GuessedMarginals ->
+            weight)
+      (tagLabelIn False edgeID interp disambs)
+    -- below, the case when the word is unknown
+    Nothing ->
+      let interp = Interp
+            { base = "none"
+            , tag = ign
+            , commonness = Nothing
+            , qualifier = Nothing
+            , metaInfo = Nothing
+            , eos = False }
+      in  buildInterp showCfg tailNode headNode word interp 0 False
+  where
+    finalize = (`mappend` "\n") . mconcat . intersperse "\n"
+    tagWeightIn = tagLabelIn 0
+    tagLabelIn def i x anno
+      = maybe def (tagLabel def x) (DAG.maybeEdgeLabel i anno)
+    tagLabel def x = maybe def id . M.lookup x
+
+
+buildInterp
+  :: ShowCfg
+  -> DAG.NodeID  -- ^ Tail node
+  -> DAG.NodeID  -- ^ Head node
+  -> Word        -- ^ Word
+  -> Interp PolX.Tag
+  -> Double
+  -> Bool
+  -> L.Builder
+buildInterp ShowCfg{..} tailNode headNode word Interp{..} weight disamb =
+  mconcat $ intersperse "\t" $
+  [ buildNode tailNode
+  , buildNode headNode
+  , L.fromText $ orth word
+  , L.fromText $ if known word then base else orth word
+  , L.fromText tag
+  , buildMayText commonness
+  , buildMayText qualifier
+  , if numericDisamb
+    then buildDisamb disamb
+    else buildWeight weight
+  , buildMayText metaInfo
+  , if eos then "eos" else ""
+  ] ++
+  if numericDisamb then [] else [buildDisamb disamb]
+  where
+    buildNode (DAG.NodeID i) = L.fromString (show i)
+    buildWeight = L.fromString . printf "%.4f"
+    buildDisamb True  = if numericDisamb then "1.0000" else "disamb"
+    buildDisamb False = if numericDisamb then "0.0000" else ""
+    -- buildDmb = between "\t" "\n" . L.fromString . printf "%.3f"
+    -- between x y z = x <> z <> y
+    buildMayText Nothing = ""
+    buildMayText (Just x) = L.fromText x
+
+
+-----------------------------
+-- Parsing
+-----------------------------
+
+
+-- | Parse the text in the DAG format.
+parseData :: L.Text -> [Sent Tag]
+parseData =
+  map parseSent . filter realSent . L.splitOn "\n\n"
+  where
+    realSent = not . L.null
+
+
+-- | Parse sentence in the DAG format.
+parseSent :: L.Text -> Sent Tag
+parseSent = fromRows . parseRows
+
+
+data Row = Row
+  { tailNode   :: Int
+  , headNode   :: Int
+  , orthForm   :: T.Text
+  , baseForm   :: T.Text
+  , theTag     :: PolX.Tag
+  , commonness :: Maybe T.Text
+  , qualifier  :: Maybe T.Text
+  , tagProb    :: Double
+  , metaInfo   :: Maybe T.Text
+  , eos        :: Bool
+  }
+
+
+fromRows :: [Row] -> Sent Tag
+fromRows =
+  -- DAG.fromList' I.None . zip (repeat I.None) . getEdges
+  DAG.mapN (const I.None) . DAG.fromEdgesUnsafe . getEdges
+  where
+    getEdges = map mkEdge . groupBy theSameEdge
+    theSameEdge r1 r2
+      =  tailNode r1 == tailNode r2
+      && headNode r1 == headNode r2
+    mkEdge [] = error "Format.Base.fromRows: empty list"
+    mkEdge rows@(row0:_) = DAG.Edge
+      { DAG.tailNode = DAG.NodeID $ tailNode row0
+      , DAG.headNode = DAG.NodeID $ headNode row0
+      , DAG.edLabel = edge }
+      where
+        edge = X.Seg
+          { word = newWord
+          , tags = newTags }
+        newWord = Word
+          { orth = orthForm row0
+          , known = not $ ign `elem` map theTag rows }
+        newTags = X.mkWMap
+          [ (interp, tagProb)
+          | Row{..} <- rows
+          , not $ theTag == ign
+          , let interp = Interp
+                  { base = baseForm
+                  , tag = theTag
+                  , commonness = commonness
+                  , qualifier = qualifier
+                  , metaInfo = metaInfo
+                  , eos = eos }
+          ]
+
+
+parseRows :: L.Text -> [Row]
+parseRows = map parseRow . L.splitOn "\n"
+
+
+parseRow :: L.Text -> Row
+parseRow =
+  doit . L.splitOn "\t"
+  where
+    doit (tlNode : hdNode : otForm : bsForm : tag :
+          comm : qual : prob : meta : eos : _) = Row
+      { tailNode = readTyp "tail node" $ L.unpack tlNode
+      , headNode = readTyp "head node" $ L.unpack hdNode
+      , orthForm = L.toStrict otForm
+      , baseForm = L.toStrict bsForm
+      , theTag   = L.toStrict tag
+      , commonness = nullIfEmpty comm
+      , qualifier = nullIfEmpty qual
+      , tagProb  = readTyp "probability value" $ L.unpack prob
+      , metaInfo = nullIfEmpty meta
+      , eos = case eos of
+          "eos" -> True
+          _ -> False
+      }
+    doit _ = error "parseRow: unexpected number of row cells"
+    nullIfEmpty x = case x of
+      "" -> Nothing
+      _  -> Just (L.toStrict x)
+
+
+-----------
+-- Utils
+-----------
+
+
+-- -- | An infix synonym for 'mappend'.
+-- (<>) :: Monoid m => m -> m -> m
+-- (<>) = mappend
+-- {-# INLINE (<>) #-}
+
+
+readTyp :: (Read a) => String -> String -> a
+readTyp typ x =
+  case readMaybe x of
+    Just x -> x
+    Nothing -> error $
+      "unable to parse \"" ++ x ++ "\" to a " ++ typ
+--       "Unable to parse <" ++ typ ++ ">" ++
+--       " (string=" ++ x ++ ")"
+
+
+-- | Tag which indicates unknown words.
+ign :: IsString a => a
+ign = "ign"
+{-# INLINE ign #-}
diff --git a/src/NLP/Concraft/Polish/DAG/Morphosyntax.hs b/src/NLP/Concraft/Polish/DAG/Morphosyntax.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAG/Morphosyntax.hs
@@ -0,0 +1,363 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE LambdaCase #-}
+
+
+-- | DAG-aware morphosyntax data layer in Polish.
+
+
+module NLP.Concraft.Polish.DAG.Morphosyntax
+(
+-- * Tag
+  Tag
+
+-- * Edge
+-- , Edge (..)
+, Word (..)
+, Interp (..)
+, voidInterp
+, Space (..)
+-- , select
+-- , select'
+, selectWMap
+, selectAnno
+
+-- * Sentence
+, Sent
+, SentO (..)
+, restore
+, restore'
+, withOrig
+
+-- * Conversion
+, packSent
+, packSentO
+-- , packSeg
+
+-- -- ** From simple sentence
+-- , fromList
+) where
+
+
+import           Prelude hiding (Word)
+import           Control.Applicative ((<$>), (<*>))
+-- import           Control.Arrow (first)
+import           Control.Monad (guard)
+import           Data.Binary (Binary, put, get, putWord8, getWord8)
+import           Data.Aeson
+import qualified Data.Aeson as Aeson
+import qualified Data.Set as S
+import qualified Data.Map.Strict as M
+import           Data.Maybe (catMaybes)
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as L
+import qualified Data.Tagset.Positional as P
+
+import           Data.DAG (DAG)
+import qualified Data.DAG as DAG
+-- import           Data.CRF.Chain1.Constrained.DAG.Dataset.Internal (DAG)
+-- import qualified Data.CRF.Chain1.Constrained.DAG.Dataset.Internal as DAG
+
+-- import qualified NLP.Concraft.DAG2 as C
+import qualified NLP.Concraft.DAG.Morphosyntax as X
+import qualified NLP.Concraft.DAG.Segmentation as Seg
+import qualified NLP.Concraft.Polish.Morphosyntax as R
+import           NLP.Concraft.Polish.Morphosyntax (Space(..))
+
+
+--------------------------------
+-- Basics
+--------------------------------
+
+
+-- | A textual representation of a morphosyntactic tag.
+type Tag = T.Text
+
+
+--------------------------------
+-- Interp
+--------------------------------
+
+
+-- | A morphosyntactic interpretation.
+data Interp t = Interp
+    { base  :: T.Text
+      -- ^ The base form (lemma)
+    , tag   :: t
+      -- ^ The (morphosyntactic) tag
+    , commonness :: Maybe T.Text
+    , qualifier  :: Maybe T.Text
+    , metaInfo   :: Maybe T.Text
+    , eos        :: Bool
+      -- ^ The remaining four are ignored for the moment, but we plan to rely on
+      -- them later on.
+    } deriving (Show, Eq, Ord)
+
+
+-- | An almost empty interpretation, with only the `tag` actually specified.
+voidInterp :: t -> Interp t
+voidInterp x = Interp
+  { base = "none"
+  , tag = x
+  , commonness = Nothing
+  , qualifier = Nothing
+  , metaInfo = Nothing
+  , eos = False
+  }
+
+
+instance (Ord t, Binary t) => Binary (Interp t) where
+    put Interp{..} = do
+      put base
+      put tag
+      put commonness
+      put qualifier
+      put metaInfo
+      put eos
+    get = Interp <$> get <*> get <*> get <*> get <*> get <*> get
+
+
+--------------------------------
+-- Edge
+--------------------------------
+
+
+-- -- | An edge consists of a word and a set of morphosyntactic interpretations.
+-- data Edge t = Edge
+--     { word      :: Word
+--     -- | Interpretations of the word, each interpretation annotated
+--     -- with a /disamb/ Boolean value (if 'True', the interpretation
+--     -- is correct within the context).
+--     , interps   :: X.WMap (Interp t) }
+--     deriving (Show, Eq, Ord)
+--
+-- instance (Ord t, Binary t) => Binary (Edge t) where
+--     put Edge{..} = put word >> put interps
+--     get = Edge <$> get <*> get
+--
+-- instance X.Word (Edge t) where
+--     orth = X.orth . word
+--     oov = X.oov . word
+
+
+--------------------------------
+-- Word
+--------------------------------
+
+
+-- | A word.
+data Word = Word
+    { orth      :: T.Text
+    -- , space     :: Space
+    , known     :: Bool }
+    deriving (Show, Eq, Ord)
+
+instance X.Word Word where
+    orth = orth
+    oov = not.known
+
+instance Binary Word where
+    -- put Word{..} = put orth >> put space >> put known
+    put Word{..} = put orth >> put known
+    -- get = Word <$> get <*> get <*> get
+    get = Word <$> get <*> get
+
+instance ToJSON Word where
+    toJSON Word{..} = object
+        [ "orth"  .= orth
+        , "known" .= known ]
+
+instance FromJSON Word where
+    parseJSON (Object v) = Word
+        <$> v .: "orth"
+        <*> v .: "known"
+    parseJSON _ = error "parseJSON [Word]"
+
+
+---------------------------------------------------------------------------------
+-- Selection
+--
+-- (Honestly, I don't remember what is this one about...)
+--
+-- Update: maybe related to the fact that base forms have to be handled somehow?
+---------------------------------------------------------------------------------
+
+
+-- -- | Select one chosen interpretation.
+-- select :: Ord a => a -> Edge a -> Edge a
+-- select = select' []
+--
+--
+-- -- | Select multiple interpretations and one chosen interpretation.
+-- select' :: Ord a => [a] -> a -> Edge a -> Edge a
+-- select' ys x = selectWMap . X.mkWMap $ (x, 1) : map (,0) ys
+
+
+-- | Select interpretations.
+selectAnno :: Ord a => M.Map (Interp a) Double -> X.Seg Word (Interp a) -> X.Seg Word (Interp a)
+selectAnno = selectWMap . X.fromMap
+
+
+-- | Select interpretations.
+selectWMap :: Ord a => X.WMap (Interp a) -> X.Seg Word (Interp a) -> X.Seg Word (Interp a)
+selectWMap wMap seg = seg {X.tags = wMap}
+--     seg { X.tags = newTags }
+--   where
+--     wSet = S.fromList . map tag . M.keys . X.unWMap . X.tags $ seg
+--     newTags = X.mkWMap $
+--         -- [ case M.lookup (tag interp) (X.unWMap wMap) of
+--         [ case M.lookup interp (X.unWMap wMap) of
+--             Just x  -> (interp, x)
+--             Nothing -> (interp, 0)
+--         | interp <- (M.keys . X.unWMap) (X.tags seg) ]
+--             ++ catMaybes
+--         [ if interp `S.member` wSet
+--             then Nothing
+--             else Just (interp, x)
+--         | (interp, x) <- M.toList (X.unWMap wMap)
+-- --         | let lemma = orth $ X.word seg   -- Default base form
+-- --         , (tag, x) <- M.toList (X.unWMap wMap)
+-- --         , let interp = Interp
+-- --                 { base = lemma
+-- --                 , tag = tag
+-- --                 , commonness = Nothing
+-- --                 , qualifier = Nothing
+-- --                 , metaInfo = Nothing
+-- --                 , eos = False }
+--         ]
+
+
+--------------------------------
+-- Sentence
+--------------------------------
+
+
+-- | A sentence.
+-- type Sent t = DAG Space (Edge t)
+type Sent t = DAG Space (X.Seg Word t)
+
+
+-- | A sentence with its original textual representation.
+data SentO t = SentO
+    { sent  :: Sent t
+    , orig  :: L.Text }
+
+
+-- | Restore textual representation of a sentence. The function is not very
+-- accurate, it could be improved if we enriched the representation of spaces.
+restore :: Sent t -> L.Text
+restore =
+    let edgeStr = orth . X.word
+        spaceStr None    = ""
+        spaceStr Space   = " "
+        spaceStr NewLine = "\n"
+    in  L.fromChunks . map (either spaceStr edgeStr) . pickPath
+
+
+-- | Use `restore` to translate `Sent` to a `SentO`.
+withOrig :: Sent t -> SentO t
+withOrig s = SentO
+    { sent = s
+    , orig = restore s }
+
+
+-- | A version of `restore` which places a space on each node.
+restore' :: Sent t -> L.Text
+restore' =
+    let edgeStr = orth . X.word
+        spaceStr = const " "
+    in  L.fromChunks . map (either spaceStr edgeStr) . tail . pickPath
+
+
+--------------------------------
+-- Utils
+--------------------------------
+
+
+-- | Pick any path from the given DAG. The result is a list
+-- of interleaved node and edge labels.
+pickPath :: (X.Word b) => DAG a b -> [Either a b]
+pickPath dag =
+  fstNodeVal path : concatMap getVals path
+  where
+    -- Take the value on the first node on the path
+    fstNodeVal = \case
+      edgeID : _ ->
+        Left $ DAG.nodeLabel (DAG.begsWith edgeID dag) dag
+      [] -> error "Morphosyntax.pickPath: empty path"
+    -- Select a shortest path in the DAG; thanks to edges being topologically
+    -- sorted, this should give us the list of edge IDs in an appropriate order.
+    path = S.toList $ Seg.findPath Seg.Min dag
+    -- Get the labels of the nodes and labels
+    getVals edgeID =
+      let
+        nodeID = DAG.endsWith edgeID dag
+      in
+        [ Right $ DAG.edgeLabel edgeID dag
+        , Left  $ DAG.nodeLabel nodeID dag ]
+
+
+---------------------------
+-- Conversion
+---------------------------
+
+
+-- -- | Convert a segment to a segment from the core library.
+-- packSeg :: Ord a => X.Seg Word a -> X.Seg Word a
+-- packSeg = id
+-- -- packSeg Edge{..}
+-- --     = X.Seg word
+-- --     $ X.mkWMap
+-- --     $ map (first tag)
+-- --     $ M.toList
+-- --     $ X.unWMap interps
+
+
+-- | Convert a sentence to a sentence from the core library.
+packSent :: Ord a => Sent a -> X.Sent Word a
+packSent
+  = DAG.mapN (const ())
+  -- . fmap packSeg
+
+
+-- | Convert a sentence to a sentence from the core library.
+-- packSentO :: P.Tagset -> SentO Tag -> X.SentO Word P.Tag
+packSentO :: Ord a => SentO a -> X.SentO Word a
+packSentO s = X.SentO
+    { segs = packSent (sent s)
+    , orig = orig s }
+
+
+---------------------------
+-- From simple sentence
+---------------------------
+
+
+-- fromWord :: R.Word -> (Space, Word)
+-- fromWord R.Word{..} = (space, Word {orth=orth, known=known})
+--
+--
+-- fromSeg :: (Ord t) => R.Seg t -> (Space, Edge t)
+-- fromSeg R.Seg{..} =
+--   (space, edge)
+--   where
+--     edge = Edge {word = newWord, interps = updateInterps interps}
+--     (space, newWord) = fromWord word
+--     -- updateInterps = X.mkWMap . map (first fromInterp) . X.unWMap
+--     updateInterps = X.mapWMap fromInterp
+--
+--
+-- fromInterp :: R.Interp t -> Interp t
+-- fromInterp R.Interp{..} = Interp
+--   { base = base
+--   , tag = tag
+--   , commonness = Nothing
+--   , qualifier = Nothing
+--   , metaInfo = Nothing
+--   , eos = False }
+--
+--
+-- -- | Create a DAG-based sentence from a list-based sentence.
+-- fromList :: Ord t => R.Sent t -> Sent t
+-- fromList = DAG.fromList' None . map fromSeg
diff --git a/src/NLP/Concraft/Polish/DAG/Server.hs b/src/NLP/Concraft/Polish/DAG/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAG/Server.hs
@@ -0,0 +1,128 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+
+
+-- | Concraft (DAG) web server.
+
+
+module NLP.Concraft.Polish.DAG.Server
+  (
+    -- * Types
+    ServerCfg(..)
+  , ClientCfg(..)
+  , Request(..)
+  , Answer(..)
+
+    -- * Server
+  , runServer
+
+    -- * Client
+  , sendRequest
+  ) where
+
+
+import           Control.Monad.IO.Class (liftIO)
+
+import           GHC.Generics
+
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as L
+import           Network.HTTP.Types (badRequest400)
+import qualified Web.Scotty as W
+import qualified Data.Aeson as A
+import           Data.Aeson ((.=))
+import qualified Network.Wreq as Wreq
+import           Control.Lens ((^?))
+
+import qualified NLP.Concraft.Polish.DAGSeg as Pol
+import qualified NLP.Concraft.Polish.DAG.Format.Base as DB
+
+
+---------------------------------------
+-- Types
+---------------------------------------
+
+
+type Concraft = Pol.Concraft Pol.Tag
+
+
+data Request = Request
+  { dag :: T.Text
+  } deriving (Generic)
+
+instance A.FromJSON Request
+instance A.ToJSON Request
+
+
+data Answer = Answer
+  { dag :: T.Text
+  } deriving (Generic)
+
+instance A.ToJSON Answer
+instance A.FromJSON Answer
+
+
+data ServerCfg = ServerCfg
+  { concraft :: Concraft
+  , annoCfg :: Pol.AnnoConf
+  , showCfg :: DB.ShowCfg
+  }
+
+
+---------------------------------------
+-- Server
+---------------------------------------
+
+
+serverApp :: ServerCfg -> W.ScottyM ()
+serverApp env = do
+  W.post "/parse" $ parse env
+  W.post "/parse" parseFailure
+
+
+parse :: ServerCfg -> W.ActionM ()
+parse ServerCfg{..} =  flip W.rescue (const W.next) $ do
+  Request{..} <- W.jsonData
+  let inp = DB.parseData (L.fromStrict dag)
+      out = Pol.annoAll annoCfg concraft <$> inp
+      dagStr = DB.showData showCfg out
+      ans = Answer { dag = L.toStrict dagStr }
+  -- W.json $ A.object [ "dag" .= dagStr ]
+  W.json $ A.toJSON ans
+
+
+parseFailure :: W.ActionM ()
+parseFailure = do
+  W.json $ A.object
+    [ "error" .= ("Invalid request" :: T.Text) ]
+  W.status badRequest400
+
+
+runServer
+  :: ServerCfg
+  -> Int -- ^ Port
+  -> IO ()
+runServer env port = W.scotty port (serverApp env)
+
+
+---------------------------------------
+-- Client
+---------------------------------------
+
+
+data ClientCfg = ClientCfg
+  { serverAddr :: String
+  }
+
+
+sendRequest
+  :: ClientCfg
+  -> Request
+  -> IO (Maybe Answer)
+sendRequest ClientCfg{..} req = do
+  let json = A.toJSON req
+  r <- Wreq.post serverAddr json
+  let result = A.decode =<< r ^? Wreq.responseBody
+  return result
diff --git a/src/NLP/Concraft/Polish/DAGSeg.hs b/src/NLP/Concraft/Polish/DAGSeg.hs
new file mode 100644
--- /dev/null
+++ b/src/NLP/Concraft/Polish/DAGSeg.hs
@@ -0,0 +1,612 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+
+-- | DAG-based model for morphosyntactic tagging.
+
+
+module NLP.Concraft.Polish.DAGSeg
+(
+
+-- * Types
+  Tag (..)
+-- ** Simplification
+, simplify4gsr
+, simplify4dmb
+
+-- ** Model
+, C.Concraft
+, C.saveModel
+, C.loadModel
+
+-- * Tagging
+, guess
+-- , disamb
+-- , disamb'
+-- , tag
+-- , tag'
+-- ** High level
+, AnnoSent (..)
+, AnnoConf (..)
+, annoAll
+
+-- * Training
+, TrainConf (..)
+-- , DisambTiersCfg (..)
+, train
+
+-- * Pruning
+-- , C.prune
+) where
+
+
+import           Prelude hiding (Word)
+import           Control.Applicative ((<$>))
+import           Control.Arrow (first)
+import           Control.Monad (guard)
+import           Data.Maybe (listToMaybe)
+import qualified Data.Text.Lazy as L
+import qualified Data.Set as S
+import qualified Data.Map.Strict as M
+import           Data.Data (Data)
+import           Data.Typeable (Typeable)
+
+import qualified Data.Tagset.Positional as P
+import qualified Numeric.SGD.Momentum as SGD
+
+import qualified Data.DAG as DAG
+
+import qualified NLP.Concraft.DAG.Morphosyntax as X
+import qualified NLP.Concraft.DAG.Morphosyntax.Ambiguous as XA
+import qualified NLP.Concraft.DAG.Schema as S
+import           NLP.Concraft.DAG.Schema (SchemaConf(..), entry, entryWith)
+import qualified NLP.Concraft.DAG.Guess as G
+import qualified NLP.Concraft.DAG.DisambSeg as D
+import qualified NLP.Concraft.DAG.Segmentation as Seg
+import qualified NLP.Concraft.DAGSeg as C
+
+import           NLP.Concraft.Polish.DAG.Morphosyntax hiding (tag, Tag)
+import qualified NLP.Concraft.Polish.DAG.Morphosyntax as PolX
+import qualified NLP.Concraft.Polish.DAG.Config as Cfg
+import qualified NLP.Concraft.Polish.DAG.Config.Disamb as Cfg
+
+-- import Debug.Trace (trace)
+
+
+-------------------------------------------------
+-- Default configuration
+-------------------------------------------------
+
+
+-- | Default configuration for the guessing observation schema.
+guessSchemaDefault :: SchemaConf
+guessSchemaDefault = S.nullConf
+    { lowPrefixesC  = entryWith [1, 2]      [0]
+    , lowSuffixesC  = entryWith [1, 2]      [0]
+    , knownC        = entry                 [0]
+    , begPackedC    = entry                 [0]
+    }
+
+
+-- | Default configuration for the segmentation observation schema.
+segmentSchemaDefault :: SchemaConf
+segmentSchemaDefault = S.nullConf
+    { lowPrefixesC  = entryWith [1, 2]      [-1, 0, 1]
+    , begPackedC    = entry                 [-1, 0, 1] }
+    -- NOTE: The current configuration works quite well for segmentation.
+    -- Adding orthographic forms was not a good idea, at least not on a small
+    -- training dataset.
+
+
+-- -- | Default configuration for the guessing observation schema.
+-- disambSchemaDefault :: SchemaConf
+-- disambSchemaDefault = S.nullConf
+--     { lowPrefixesC  = entryWith [1, 2]      [-1, 0, 1]
+--     , begPackedC    = entry                 [-1, 0, 1] }
+
+
+-- -- | Default configuration for the guessing observation schema.
+-- disambSchemaDefault :: SchemaConf
+-- disambSchemaDefault = S.nullConf
+--     { orthC         = entry                   [-2, -1, 0, 1, 2]
+--     -- { lowOrthC      = entry                   [-2, -1, 0, 1, 2]
+--     , lowPrefixesC  = entryWith [1, 2, 3]     [-2, -1, 0, 1, 2]
+--     , lowSuffixesC  = entryWith [1, 2, 3]     [-2, -1, 0, 1, 2]
+--     , begPackedC    = entry                   [-2, -1, 0, 1, 2] }
+
+
+-- | Default configuration for the disambiguation observation schema.
+disambSchemaDefault :: SchemaConf
+disambSchemaDefault = S.nullConf
+    { lowOrthC      = entry                         [-2, -1, 0, 1]
+    , lowPrefixesC  = oov $ entryWith [1, 2, 3]     [0]
+    , lowSuffixesC  = oov $ entryWith [1, 2, 3]     [0]
+    , begPackedC    = oov $ entry                   [0] }
+  where
+    oov (Just body) = Just $ body { S.oovOnly = True }
+    oov Nothing     = Nothing
+
+
+-- | Default tiered tagging configuration for the segmentation model.
+tiersSegment :: [D.Tier]
+tiersSegment =
+    [tier]
+  where
+    tier = D.Tier
+      { D.withPos = True
+      , D.withEos = True
+      , D.withAtts = S.fromList []
+      }
+
+
+-------------------------------------------------
+-- Tagging Tiers
+-------------------------------------------------
+
+
+-- -- | Configuration of disambiguation tiers.
+-- data DisambTiersCfg
+--   = TiersDefault
+--   | TiersGndCasSeparately
+--   deriving (Data, Typeable, Show, Eq, Ord)
+--
+--
+-- -- | Tiered tagging configuration for the disambiguation model.
+-- tiersDisamb :: DisambTiersCfg -> [D.Tier]
+-- tiersDisamb cfg = case cfg of
+--   TiersDefault -> tiersDisambDefault
+--   TiersGndCasSeparately -> tiersDisambGndCasSeparately
+--
+--
+-- -- | Default tiered tagging configuration for the disambiguation model.
+-- tiersDisambDefault :: [D.Tier]
+-- tiersDisambDefault =
+--     [tier1, tier2]
+--   where
+--     tier1 = D.Tier True False $ S.fromList
+--       ["cas", "per"]
+--     tier2 = D.Tier False False $ S.fromList
+--       [ "nmb", "gnd", "deg", "asp" , "ngt", "acm"
+--       , "acn", "ppr", "agg", "vlc", "dot"
+--       , "sbg", "col"
+--       ]
+--
+-- -- | Separate tier with gender and case values.
+-- tiersDisambGndCasSeparately :: [D.Tier]
+-- tiersDisambGndCasSeparately =
+--     [tier1, tier2, tier3]
+--   where
+--     tier1 = D.Tier True False $ S.fromList
+--       [ "per" ]
+--     tier2 = D.Tier False False $ S.fromList
+--       [ "nmb", "deg", "asp" , "ngt", "acm"
+--       , "acn", "ppr", "agg", "vlc", "dot"
+--       , "sbg", "col"
+--       ]
+--     tier3 = D.Tier False False $ S.fromList
+--       [ "cas", "gnd"
+--       ]
+
+
+tiersDisamb :: Cfg.Config -> [D.Tier]
+tiersDisamb Cfg.Config{..} = do
+  Cfg.TierCfg{..} <- Cfg.tiersCfg disambCfg
+  return $ D.Tier
+    { D.withPos = withPos
+    , D.withEos = withEos
+    , D.withAtts = Cfg.unSet withAtts
+    }
+
+
+-------------------------------------------------
+-- Tagging
+-------------------------------------------------
+
+
+-- type Tag = PolX.Tag
+type Tag = PolX.Interp PolX.Tag
+
+
+-- | Tag the sentence with guessing marginal probabilities.
+guess :: C.Concraft Tag -> Sent Tag -> Sent Tag
+guess = tagWith (C.guessMarginals . C.guesser)
+
+
+-- | Tag the sentence with disambiguation marginal probabilities.
+disamb :: C.Concraft Tag -> Sent Tag -> Sent Tag
+disamb = tagWith (C.disambMarginals . C.disamb)
+
+
+-- | Tag the sentence with disambiguation probabilities.
+disamb' :: C.Concraft Tag -> D.ProbType -> Sent Tag -> Sent Tag
+disamb' crf typ = tagWith (C.disambProbs typ . C.disamb) crf
+
+
+-- | Perform guessing -> trimming -> disambiguation.
+tag
+  :: Int
+  -- ^ Trimming parameter
+  -> C.Concraft Tag
+  -> Sent Tag
+  -> Sent Tag
+tag = tagWith . C.tag
+
+
+-- -- | Perform guessing -> trimming -> disambiguation.
+-- tag'
+--   :: Int
+--   -- ^ Trimming parameter
+--   -> D.ProbType
+--   -> C.Concraft
+--   -> Sent Tag
+--   -> Sent Tag
+-- tag' k probTyp = tagWith (C.tag' k probTyp)
+
+
+-- | Tag with the help of a lower-level annotation function.
+tagWith
+  -- :: (C.Concraft Tag -> X.Sent Word P.Tag -> C.Anno P.Tag Double)
+  :: (C.Concraft Tag -> X.Sent Word Tag -> C.Anno Tag Double)
+  -> C.Concraft Tag -> Sent Tag -> Sent Tag
+tagWith annoFun concraft sent
+  = fmap select
+  $ DAG.zipE sent annoSent
+  where
+    select (edge, anno) = selectAnno anno edge
+    annoSent = annoWith annoFun concraft sent
+
+
+-- | Annotate with the help of a lower-level annotation function.
+annoWith
+  :: (C.Concraft Tag -> X.Sent Word Tag -> C.Anno Tag a)
+  -> C.Concraft Tag -> Sent Tag -> C.Anno Tag a
+annoWith anno concraft =
+  anno concraft . packSent
+
+
+-- | Tranform each tag into two tags, one with EOS(end-of-sentence)=True, one
+-- with EOS=False. The idea behind this transformation is that, at some point,
+-- we may want to tag with an EOS-aware model, with no EOS-related information
+-- coming from the previous tagging stages.
+--
+-- NOTE: this procedure does not apply to OOV words. The motivation: it seems
+-- highly unlikely that any OOV word can mark a sentence end.
+--
+-- NOTE: this procedure does not apply to segmentation-ambiguous words either.
+-- Allowing the tagger to cut on such words might cause losing DAG edges.
+addEosMarkers
+  :: DAG.DAG a Bool
+  -> DAG.EdgeID
+  -> X.Seg Word Tag
+  -> X.Seg Word Tag
+addEosMarkers ambiDag edgeID seg
+  | X.oov seg = seg
+  | DAG.edgeLabel edgeID ambiDag = seg
+  | otherwise = seg {X.tags = newTags (X.tags seg)}
+  where
+    newTags tagMap = X.mkWMap $ concat
+      [ multiply interp p
+      | (interp, p) <- M.toList (X.unWMap tagMap) ]
+    multiply interp p
+      | eos interp == True =
+          [ (interp {eos=True}, p)
+          , (interp {eos=False}, 0) ]
+      | otherwise =
+          [ (interp {eos=True}, 0)
+          , (interp {eos=False}, p) ]
+
+
+-- | Decide if the word should be marked as eos or not.
+resolveEOS
+  :: Double
+     -- ^ 0.5 means that the probability of the tag being EOS is twice as high
+     -- as that of not being EOS. 1.0 means that EOS will be marked only if its
+     -- 100% probable.
+  -> X.Seg Word Tag
+  -> X.Seg Word Tag
+resolveEOS minProp seg
+  | isEos     = seg {X.tags = markEos}
+  | otherwise = seg {X.tags = markNoEos}
+  where
+    tagMap = X.tags seg
+    -- Determine the weights of the most probable EOS-marked and non-marked tags
+    withEosW = maxWeightWith ((==True) . PolX.eos) tagMap
+    withNoEosW = maxWeightWith ((==False) . PolX.eos) tagMap
+    -- Should the segment be marked as EOS?
+    isEos = case (withEosW, withNoEosW) of
+      (Just eosW, Just noEosW) ->
+        eosW / (eosW + noEosW) >= minProp
+      (Just _, Nothing) -> True
+      _ -> False
+    -- Mark the segment as EOS or not
+    markEos = X.mkWMap
+      [ (interp {PolX.eos=True}, p)
+      | (interp, p) <- M.toList (X.unWMap tagMap) ]
+    markNoEos = X.mkWMap
+      [ (interp {PolX.eos=False}, p)
+      | (interp, p) <- M.toList (X.unWMap tagMap) ]
+
+
+-- | Determine the weight of the most probable interpretation which satisfies
+-- the given predicate.
+maxWeightWith :: (Tag -> Bool) -> X.WMap Tag -> Maybe Double
+maxWeightWith pred tagMap = mayMaximum
+  [ p
+  | (interp, p) <- M.toList (X.unWMap tagMap)
+  , pred interp ]
+  where
+    mayMaximum [] = Nothing
+    mayMaximum xs = Just $ maximum xs
+
+
+-- | Try to segment the sentence based on the EOS markers.
+-- segment :: X.Sent Word Tag -> [X.Sent Word Tag]
+segment :: DAG.DAG a (X.Seg Word Tag) -> [DAG.DAG a (X.Seg Word Tag)]
+segment sent =
+
+  -- (\xs -> trace ("splits: " ++ show (length xs)) xs) $
+  -- go (trace ("splitPoints: " ++ show splitPoints) splitPoints) sent
+  go splitPoints sent
+
+  where
+
+    splitPoints = (S.toList . S.fromList)
+      [ DAG.endsWith edgeID sent
+      | edgeID <- DAG.dagEdges sent
+      , interp <- M.keys . X.unWMap . X.tags . DAG.edgeLabel edgeID $ sent
+      , PolX.eos interp ]
+
+    go (splitPoint:rest) dag =
+      case split splitPoint dag of
+        Just (left, right) -> left : go rest right
+        Nothing -> go rest dag
+    go [] dag = [dag]
+
+    split point dag = do
+      (x, y) <- DAG.splitTmp point dag
+      let empty = null . DAG.dagEdges
+      guard . not . empty $ x
+      guard . not . empty $ y
+      return (x, y)
+
+
+-------------------------------------------------
+-- High-level Tagging
+-------------------------------------------------
+
+
+-- -- | Configuration related to frequency-based path picking.
+-- data PickFreqConf = PickFreqConf
+--   { pickFreqMap :: Maybe (M.Map T.Text (Int, Int))
+--     -- ^ A map which assigns (chosen, not chosen) counts to the invidiaul
+--     -- orthographic forms.
+--   , smoothingParam :: Double
+--     -- ^ A naive smoothing related parameter, which should be adddd to each
+--     -- count in `pickFreqMap`.
+--   }
+
+
+-- | Annotation config.
+data AnnoConf = AnnoConf
+  { trimParam :: Int
+    -- ^ How many morphosyntactic tags should be kept for OOV words
+  , pickPath :: Maybe Seg.PathTyp
+    -- ^ Which path picking method should be used. The function takes the
+  }
+
+
+-- | Annotated sentence.
+data AnnoSent = AnnoSent
+  { guessSent :: Sent Tag
+  -- ^ The sentence after guessing and segmentation
+  -- (TODO: and annotated with marginal probabilities?)
+  , disambs   :: C.Anno Tag Bool
+  -- ^ Disambiguation markers
+  , marginals :: C.Anno Tag Double
+  -- ^ Marginal probabilities according to the disambiguation model
+  , maxProbs  :: C.Anno Tag Double
+  -- ^ Maximal probabilities according to the disambiguation model
+  }
+
+
+-- | Annotate all possibly interesting information.
+annoAll
+--   :: Int
+--   -- ^ Trimming parameter
+  :: AnnoConf
+  -> C.Concraft Tag
+  -> Sent Tag
+  -> [AnnoSent]
+-- annoAll k concraft sent0 =
+annoAll AnnoConf{..} concraft sent00 =
+
+  map annoOne _guessSent1
+
+  where
+
+    -- See whether the shortest path should be computed first
+    sent0 =
+      case pickPath of
+        Just typ -> Seg.pickPath typ sent00
+        Nothing -> sent00
+
+    -- We add EOS markers only after guessing, because the possible tags are not
+    -- yet determined for the OOV words.
+    ambiDag = XA.identifyAmbiguousSegments sent0
+    _guessSent0 = DAG.mapE (addEosMarkers ambiDag) $
+      tagWith (C.guess trimParam . C.guesser) concraft sent0
+    -- Resolve EOS tags based on the segmentation model
+    _guessSent1 = segment . fmap (resolveEOS 0.5) $
+      tagWith (C.disambProbs D.MaxProbs . C.segmenter) concraft _guessSent0
+
+    annoOne _guessSent = AnnoSent
+      { guessSent = _guessSent
+      , disambs   = _disambs
+      , marginals = _marginals
+      , maxProbs  = _maxProbs
+      }
+      where
+        _marginals =
+          annoWith (C.disambProbs D.Marginals . C.disamb) concraft _guessSent
+        _maxProbs  =
+          annoWith (C.disambProbs D.MaxProbs . C.disamb) concraft _guessSent
+        _disambs   =
+          C.disambPath (optimal _maxProbs) _maxProbs
+          where optimal = maybe [] id . listToMaybe . C.findOptimalPaths
+
+
+-------------------------------------------------
+-- Training
+-------------------------------------------------
+
+
+-- | Training configuration.
+data TrainConf = TrainConf {
+    -- | Tagset.
+      tagset    :: P.Tagset
+    -- | SGD parameters.
+    , sgdArgs   :: SGD.SgdArgs
+    -- | Store SGD dataset on disk.
+    , onDisk    :: Bool
+    -- | Numer of guessed tags for each word.
+    , guessNum  :: Int
+    -- | `G.r0T` parameter.
+    , r0        :: G.R0T
+    -- | `G.zeroProbLabel` parameter
+    , zeroProbLabel :: Tag
+    -- | Extract only visible features for the guesser
+    , guessOnlyVisible :: Bool
+    -- | Global configuration
+    , globalConfig :: Cfg.Config
+    -- -- | Disambiguation tiers configuration
+    -- , disambTiersCfg :: DisambTiersCfg
+    }
+
+
+-- | Train concraft model.
+-- TODO: It should be possible to supply the two training procedures with
+-- different SGD arguments.
+train
+    :: TrainConf
+    -> IO [Sent Tag]      -- ^ Training data
+    -> IO [Sent Tag]      -- ^ Evaluation data
+    -> IO (C.Concraft Tag)
+train TrainConf{..} train0 eval0 = do
+  let trainR'IO = map packSent <$> train0
+      evalR'IO  = map packSent <$> eval0
+
+  putStr $ concat
+    [ "Batch size for the "
+    , "guessing and sentence segmentation models = "
+    ]
+  -- average number of sentences per paragraph
+  averageParSize <- average . map (fromIntegral . length . segment) <$> trainR'IO
+  let parBatchSize = ceiling $ fromIntegral (SGD.batchSize sgdArgs) / averageParSize
+  print parBatchSize
+
+  putStrLn "\n===== Train guessing model ====="
+  guesser <- G.train (guessConf parBatchSize) trainR'IO evalR'IO
+  let guess = C.guessSent guessNum guesser
+      prepSent dag =
+        let ambiDag = XA.identifyAmbiguousSegments dag
+        in  DAG.mapE (addEosMarkers ambiDag) (guess dag)
+      trainG'IO = map prepSent <$> trainR'IO
+      evalG'IO  = map prepSent <$> evalR'IO
+
+  putStrLn "\n===== Train sentence segmentation model ====="
+  segmenter <- D.train (segmentConf parBatchSize) trainG'IO evalG'IO
+  let prepSent = segment . fmap (resolveEOS 0.5)
+      trainS'IO = concatMap prepSent <$> trainG'IO
+      evalS'IO  = concatMap prepSent <$> evalG'IO
+
+  putStrLn "\n===== Train disambiguation model ====="
+  disamb <- D.train disambConf trainS'IO evalS'IO
+
+  return $ C.Concraft tagset guessNum guesser segmenter disamb
+
+  where
+
+    guessConf batchSize = G.TrainConf
+      guessSchemaDefault
+      (sgdArgs {SGD.batchSize = batchSize})
+      onDisk r0 zeroProbLabel
+      (simplify4gsr tagset) strip4gsr
+      guessOnlyVisible
+    strip4gsr interp = PolX.voidInterp (PolX.tag interp)
+
+    segmentConf batchSize = D.TrainConf
+      tiersSegment segmentSchemaDefault
+      (sgdArgs {SGD.batchSize = batchSize})
+      onDisk
+      (simplify4dmb tagset)
+
+    disambConf = D.TrainConf
+      -- (tiersDisamb disambTiersCfg)
+      (tiersDisamb globalConfig)
+      disambSchemaDefault sgdArgs onDisk
+      (simplify4dmb tagset)
+
+
+-- | Simplify the tag for the sake of the disambiguation model.
+simplify4dmb :: P.Tagset -> PolX.Interp PolX.Tag -> D.Tag
+simplify4dmb tagset PolX.Interp{..} = D.Tag
+  { D.posiTag = P.parseTag tagset tag
+  , D.hasEos = eos }
+
+
+-- | Simplify the tag for the sake of the guessing model.
+-- TODO: it is also used in the evaluation script, which assumes that
+-- `simplify4gsr` simplifies to a positional tag. The name of the function
+-- should reflect this, perhaps, or there should be two separate functions: one
+-- dedicated to guesser, one dedicated to evaluation (and other more generic
+-- things).
+simplify4gsr :: P.Tagset -> PolX.Interp PolX.Tag -> P.Tag
+simplify4gsr tagset PolX.Interp{..} = P.parseTag tagset tag
+
+
+-- -- | Train the `Concraft` model.
+-- -- No reanalysis of the input data will be performed.
+-- --
+-- -- The `FromJSON` and `ToJSON` instances are used to store processed
+-- -- input data in temporary files on a disk.
+-- train
+--     :: (X.Word w, Ord t)
+--     => P.Tagset             -- ^ A morphosyntactic tagset to which `P.Tag`s
+--                             --   of the training and evaluation input data
+--                             --   must correspond.
+--     -> Int                  -- ^ How many tags is the guessing model supposed
+--                             --   to produce for a given OOV word?  It will be
+--                             --   used (see `G.guessSent`) on both training and
+--                             --   evaluation input data prior to the training
+--                             --   of the disambiguation model.
+--     -> G.TrainConf t P.Tag  -- ^ Training configuration for the guessing model.
+--     -> D.TrainConf t        -- ^ Training configuration for the
+--                             --   disambiguation model.
+--     -> IO [Sent w t]    -- ^ Training dataset.  This IO action will be
+--                             --   executed a couple of times, so consider using
+--                             --   lazy IO if your dataset is big.
+--     -> IO [Sent w t]    -- ^ Evaluation dataset IO action.  Consider using
+--                             --   lazy IO if your dataset is big.
+--     -> IO (Concraft t)
+-- train tagset guessNum guessConf disambConf trainR'IO evalR'IO = do
+--   Temp.withTempDirectory "." ".guessed" $ \tmpDir -> do
+--   let temp = withTemp tagset tmpDir
+--
+--   putStrLn "\n===== Train guessing model ====="
+--   guesser <- G.train guessConf trainR'IO evalR'IO
+--   let guess = guessSent guessNum guesser
+--   trainG  <- map guess <$> trainR'IO
+--   evalG   <- map guess <$> evalR'IO
+--
+--   temp "train" trainG $ \trainG'IO -> do
+--   temp "eval"  evalG  $ \evalG'IO  -> do
+--
+--   putStrLn "\n===== Train disambiguation model ====="
+--   disamb <- D.train disambConf trainG'IO evalG'IO
+--   return $ Concraft tagset guessNum guesser disamb
+
+
+-- | Compute an average of the list.
+average :: [Double] -> Double
+average xs = sum xs / fromIntegral (length xs)
diff --git a/src/NLP/Concraft/Polish/Maca.hs b/src/NLP/Concraft/Polish/Maca.hs
--- a/src/NLP/Concraft/Polish/Maca.hs
+++ b/src/NLP/Concraft/Polish/Maca.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE FlexibleContexts #-}
 
 
 -- | The module provides interface for the Maca analysis tool.
diff --git a/src/NLP/Concraft/Polish/Morphosyntax.hs b/src/NLP/Concraft/Polish/Morphosyntax.hs
--- a/src/NLP/Concraft/Polish/Morphosyntax.hs
+++ b/src/NLP/Concraft/Polish/Morphosyntax.hs
@@ -7,11 +7,11 @@
 
 
 module NLP.Concraft.Polish.Morphosyntax
-( 
+(
 -- * Tag
   Tag
 
--- * Segment 
+-- * Segment
 , Seg (..)
 , Word (..)
 , Interp (..)
@@ -33,6 +33,7 @@
 ) where
 
 
+import           Prelude hiding (Word)
 import           Control.Applicative ((<$>), (<*>))
 import           Control.Arrow (first)
 import           Data.Maybe (catMaybes)
@@ -58,7 +59,7 @@
 
 
 -- | A segment consists of a word and a set of morphosyntactic interpretations.
-data Seg t = Seg 
+data Seg t = Seg
     { word      :: Word
     -- | Interpretations of the token, each interpretation annotated
     -- with a /disamb/ Boolean value (if 'True', the interpretation
@@ -70,8 +71,8 @@
 instance (Ord t, Binary t) => Binary (Seg t) where
     put Seg{..} = put word >> put interps
     get = Seg <$> get <*> get
-    
 
+
 -- | A word.
 data Word = Word
     { orth      :: T.Text
@@ -103,7 +104,7 @@
 instance Binary Word where
     put Word{..} = put orth >> put space >> put known
     get = Word <$> get <*> get <*> get
-    
+
 
 -- | A morphosyntactic interpretation.
 data Interp t = Interp
diff --git a/tools/concraft-pl.hs b/tools/concraft-pl.hs
--- a/tools/concraft-pl.hs
+++ b/tools/concraft-pl.hs
@@ -1,60 +1,73 @@
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DoAndIfThenElse #-}
+{-# LANGUAGE LambdaCase #-}
 
 
 import           Control.Applicative ((<$>))
-import           Control.Monad (unless)
+import           Control.Monad (unless, forM_)
+import           System.FilePath (isAbsolute, (</>))
 import           System.Console.CmdArgs
-import           System.IO (hFlush, stdout)
-import qualified Network as N
-import qualified Numeric.SGD as SGD
+-- import           System.IO (hFlush, stdout)
+import qualified Numeric.SGD.Momentum as SGD
+import           Data.String (fromString)
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
 import qualified Data.Text.Lazy as L
-import qualified Data.Text.Lazy.IO as L
+-- import qualified Data.Text.Lazy.IO as L
+import qualified Data.Text.Lazy.Encoding as L
+import qualified Data.ByteString.Lazy as BL
 import           Data.Tagset.Positional (parseTagset)
-import           GHC.Conc (numCapabilities)
+import qualified Data.Tagset.Positional as P
+import qualified Data.Map.Strict as M
+import qualified Data.Set as S
 
-import qualified NLP.Concraft.Morphosyntax.Accuracy as Acc
-import qualified NLP.Concraft.Guess as Guess
+import qualified Dhall as Dhall
 
-import qualified NLP.Concraft.Polish.Maca as Maca
-import qualified NLP.Concraft.Polish as C
-import qualified NLP.Concraft.Polish.Request as R
-import qualified NLP.Concraft.Polish.Server as S
-import qualified NLP.Concraft.Polish.Morphosyntax as X
-import qualified NLP.Concraft.Polish.Format.Plain as P
+import qualified Data.DAG as DAG
 
-import           Paths_concraft_pl (version, getDataFileName)
-import           Data.Version (showVersion)
+import qualified Data.CRF.Chain1.Constrained.DAG.Dataset.Codec as CRF.Codec
+import qualified Data.CRF.Chain1.Constrained.DAG.Train as CRF.Train
 
+import qualified NLP.Concraft.DAG.Guess as Guess
+import qualified NLP.Concraft.DAG.Schema as Schema
+-- import qualified NLP.Concraft.DAG.Disamb as Disamb
 
--- | Default port number.
-portDefault :: Int
-portDefault = 10089
 
+-- import qualified NLP.Concraft.DAG2 as C
+-- import qualified NLP.Concraft.Polish.DAG2 as P
+import qualified NLP.Concraft.DAGSeg as C
+import qualified NLP.Concraft.DAG.Morphosyntax as X
+import qualified NLP.Concraft.DAG.Morphosyntax.Accuracy as Acc
+import qualified NLP.Concraft.DAG.Segmentation as Seg
+import qualified NLP.Concraft.Polish.DAG.Morphosyntax as PX
+import qualified NLP.Concraft.Polish.DAGSeg as Pol
+import qualified NLP.Concraft.Polish.DAG.Format.Base as DB
+import qualified NLP.Concraft.Polish.DAG.Server as Server
 
+-- import qualified NLP.Concraft.Polish.Request as R
+
+import           Paths_concraft_pl (version, getDataFileName)
+import           Data.Version (showVersion)
+
+
 ---------------------------------------
 -- Command line options
 ---------------------------------------
 
 
--- | Data formats.
-data Format = Plain deriving (Data, Typeable, Show)
-
-
--- | A description of the Concraft-pl tool
+-- | A description of the Concraft-pl tool.
 concraftDesc :: String
 concraftDesc = "Concraft-pl " ++ showVersion version
 
 
 data Concraft
   = Train
-    { trainPath	    :: FilePath
+    { trainPath     :: FilePath
     , evalPath      :: Maybe FilePath
-    , format        :: Format
     , tagsetPath    :: Maybe FilePath
-    , noAna         :: Bool
-    -- , discardHidden :: Bool
     , iterNum       :: Double
     , batchSize     :: Int
     , regVar        :: Double
@@ -63,33 +76,66 @@
     , disk          :: Bool
     , outModel      :: FilePath
     , guessNum      :: Int
-    , r0            :: Guess.R0T }
+    , r0            :: Guess.R0T
+    , zeroProbLabel :: String
+    , visibleOnly   :: Bool
+    -- , disambTiers   :: Pol.DisambTiersCfg
+    , config        :: FilePath
+    }
   | Tag
     { inModel       :: FilePath
-    , noAna         :: Bool
-    , format        :: Format
-    , marginals     :: Bool }
-    -- , guessNum      :: Int }
+    -- , marginals     :: Bool
+    , inFile        :: Maybe FilePath
+    , outFile       :: Maybe FilePath
+    , probType      :: DB.ProbType
+    -- , suppressProbs :: Bool
+    , mayGuessNum   :: Maybe Int
+    , freqPath      :: Maybe FilePath
+    , freqSmoothing :: Double
+    , shortestPath  :: Bool
+    , longestPath   :: Bool
+    , numericDisamb :: Bool
+    }
   | Server
     { inModel       :: FilePath
-    , port          :: Int }
+    , probType      :: DB.ProbType
+    , mayGuessNum   :: Maybe Int
+    , numericDisamb :: Bool
+    , port          :: Int
+    }
   | Client
-    { noAna         :: Bool
-    , format        :: Format
-    , marginals     :: Bool
-    , host          :: String
-    , port          :: Int }
-  | Compare
-    { tagsetPath    :: Maybe FilePath
-    , refPath       :: FilePath
-    , otherPath     :: FilePath
-    , format        :: Format }
-  | Prune
-    { inModel       :: FilePath
-    , outModel      :: FilePath
-    , threshold     :: Double }
---   | ReAna
---     { format	    :: Format }
+    { serverAddr    :: String
+    , inFile        :: Maybe FilePath
+    , outFile       :: Maybe FilePath
+    , batchSize     :: Int
+    }
+  | Eval
+    { justTagsetPath :: FilePath
+    , goldPath       :: FilePath
+    , taggPath       :: FilePath
+    , onlyOov        :: Bool
+    , onlyAmb        :: Bool
+    , onlyEos        :: Bool
+    , expandTags     :: Bool
+    , ignoreTags     :: Bool
+    , heedEos        :: Bool
+    , weak           :: Bool
+    , discardProb0   :: Bool
+    , verbose        :: Bool
+    }
+  | Check
+    { justTagsetPath :: FilePath
+    , dagPath        :: FilePath
+    }
+  | Freqs
+    { dagPath        :: FilePath
+    -- , justTagsetPath :: FilePath
+    -- , outPath        :: FilePath
+    }
+  | Ambi
+    { dagPath        :: FilePath
+    , onlyChosen     :: Bool
+    }
   deriving (Data, Typeable, Show)
 
 
@@ -98,68 +144,113 @@
     { trainPath = def &= argPos 1 &= typ "TRAIN-FILE"
     , evalPath = def &= typFile &= help "Evaluation file"
     , tagsetPath = def &= typFile &= help "Tagset definition file"
-    , format = enum [Plain &= help "Plain format"]
-    , noAna = False &= help "Do not perform reanalysis"
+    , config = def &= typFile &= help "Global configuration file"
     -- , discardHidden = False &= help "Discard hidden features"
     , iterNum = 20 &= help "Number of SGD iterations"
-    , batchSize = 50 &= help "Batch size"
-    , regVar = 10.0 &= help "Regularization variance"
-    , gain0 = 1.0 &= help "Initial gain parameter"
-    , tau = 5.0 &= help "Initial tau parameter"
+    , batchSize = 50 &= help
+      "Batch size (the number of dataset elements taken in a single SGD update)"
+    , regVar = 10.0 &=
+      help "Regularization variance (the higher variance, the higher penalty for large params)"
+    , gain0 = 0.25 &= help
+      "Initial gain parameter (gain is used to scale the gradient before parameter update)"
+      -- The value `0.25` makes sense given that SGD momentum is used
+    , tau = 5.0 &= help
+      "Initial tau parameter (after how many passes over the full dataset the gain is halved)"
     , disk = False &= help "Store SGD dataset on disk"
     , outModel = def &= typFile &= help "Output Model file"
     , guessNum = 10 &= help "Number of guessed tags for each unknown word"
-    , r0 = Guess.OovChosen &= help "R0 construction method" }
+    , r0 = Guess.OovChosen &= help "R0 construction method"
+    , zeroProbLabel = "xxx" &= help "Zero probability label"
+    , visibleOnly = False &= help "Extract only visible features for the guesser"
+    -- , disambTiers = Pol.TiersDefault &= help "Dismabiguation tiers configuration"
+    }
 
 
 tagMode :: Concraft
 tagMode = Tag
     { inModel  = def &= argPos 0 &= typ "MODEL-FILE"
-    , noAna    = False &= help "Do not analyse input text"
-    , format   = enum [Plain &= help "Plain format"]
-    , marginals = False &= help "Tag with marginal probabilities" }
-    -- , guessNum = 10 &= help "Number of guessed tags for each unknown word" }
+    -- , noAna    = False &= help "Do not analyse input text"
+    -- , marginals = False &= help "Tag with marginal probabilities" }
+    , inFile  = def &= typFile &= help "Input file (stdin by default)"
+    , outFile = def &= typFile &= help "Output file (stdout by default)"
+    , probType = DB.Marginals &= help "Type of probabilities"
+    -- , suppressProbs = False &= help "Do not show probabilities"
+    , freqPath = def &= typFile &= help "File with chosen/not-chosen counts"
+    , freqSmoothing = 1.0 &= help
+      "Smoothing parameter for frequency-based path selection"
+    , shortestPath = False &= help
+      "Select shortest paths prior to parsing (can serve as a segmentation baseline)"
+    , longestPath = False &= help
+      "Select longest paths prior to parsing (mutually exclusive with shortestPath)"
+    , mayGuessNum = def &= help "Number of guessed tags for each unknown word"
+    , numericDisamb = False &= help
+      "Print disamb markers as numerical values in the probability column"
+    }
 
 
 serverMode :: Concraft
 serverMode = Server
-    { inModel = def &= argPos 0 &= typ "MODEL-FILE"
-    , port    = portDefault &= help "Port number" }
+    { inModel  = def &= argPos 0 &= typ "MODEL-FILE"
+    , probType = DB.Marginals &= help "Type of probabilities"
+    , mayGuessNum = def &= help "Number of guessed tags for each unknown word"
+    , numericDisamb = False &= help
+      "Print disamb markers as numerical values in the probability column"
+    , port = 3000 &= help "Server port"
+    }
 
 
 clientMode :: Concraft
 clientMode = Client
-    { noAna   = False &= help "Do not perform reanalysis"
-    , port    = portDefault &= help "Port number"
-    , host    = "localhost" &= help "Server host name"
-    , format  = enum [Plain &= help "Plain output format"]
-    , marginals = False &= help "Tag with marginal probabilities" }
+    { serverAddr = "http://localhost:3000/parse" &= help "Server address"
+    , inFile  = def &= typFile &= help "Input file (stdin by default)"
+    , outFile = def &= typFile &= help "Output file (stdout by default)"
+    , batchSize = 10 &= help "Sent graphs in batches of the given size"
+    }
 
 
-compareMode :: Concraft
-compareMode = Compare
-    { refPath   = def &= argPos 1 &= typ "REFERENCE-FILE"
-    , otherPath = def &= argPos 2 &= typ "OTHER-FILE"
-    , tagsetPath = def &= typFile &= help "Tagset definition file"
-    , format  = enum [Plain &= help "Plain format"] }
+evalMode :: Concraft
+evalMode = Eval
+    { justTagsetPath = def &= typ "TAGSET-FILE"  &= argPos 0
+    , goldPath = def &= typ "GOLD-FILE" &= argPos 1
+    , taggPath = def &= typ "TAGGED-FILE" &= argPos 2
+    , onlyOov  = False &= help "Only OOV edges"
+    , onlyAmb  = False &= help "Only segmentation-ambiguous edges"
+    , onlyEos = False &= help "Only EOS edges"
+    , expandTags = False &= help "Expand tags"
+    , ignoreTags = False &= help "Ignore tags (compute segmentation-level accurracy)"
+    , heedEos = False &= help "Pay attention to EOS markers (ignored by default)"
+    , weak = False &= help "Compute weak accuracy rather than strong"
+    , discardProb0 = False &= help "Discard sentences with near 0 probability"
+    , verbose = False &= help "Print information about compared elements"
+    }
 
 
-pruneMode :: Concraft
-pruneMode = Prune
-    { inModel   = def &= argPos 0 &= typ "INPUT-MODEL"
-    , outModel  = def &= argPos 1 &= typ "OUTPUT-MODEL"
-    , threshold = 0.05 &=
-        help "Remove disambiguation features below the threshold" }
+checkMode :: Concraft
+checkMode = Check
+    { justTagsetPath = def &= typ "TAGSET-FILE"  &= argPos 0
+    , dagPath= def &= typ "DAG-FILE" &= argPos 1
+    }
 
 
--- reAnaMode :: Concraft
--- reAnaMode = ReAna
---     { format    = enum [Plain &= help "Plain format"] }
+freqsMode :: Concraft
+freqsMode = Freqs
+    { dagPath = def &= typ "DAG-FILE" &= argPos 1
+    -- , justTagsetPath = def &= typ "TAGSET-FILE"  &= argPos 0
+    -- , outPath = def &= typ "FREQ-FILE" &= help "Output file to store counts"
+    }
 
 
+ambiMode :: Concraft
+ambiMode = Ambi
+    { dagPath = def &= typ "DAG-FILE" &= argPos 1
+    , onlyChosen = False &= help "Take only the chose tokens into account"
+    }
+
+
 argModes :: Mode (CmdArgs Concraft)
 argModes = cmdArgsMode $ modes
-    [trainMode, tagMode, serverMode, clientMode, compareMode, pruneMode]
+    [ trainMode, tagMode, serverMode, clientMode
+    , evalMode, checkMode, freqsMode, ambiMode ]
     &= summary concraftDesc
     &= program "concraft-pl"
 
@@ -181,155 +272,362 @@
         Nothing -> getDataFileName "config/nkjp-tagset.cfg"
         Just x  -> return x
     tagset <- parseTagset tagsetPath' <$> readFile tagsetPath'
-    let train0 = parseFileO  format trainPath
-    let eval0  = parseFileO' format evalPath
-    concraft <- C.train (trainConf tagset) train0 eval0
+
+    -- Dhall configuration
+    let configPath =
+          if isAbsolute config
+          then config
+          else "./" </> config
+    dhall <- Dhall.detailed
+      (Dhall.input Dhall.auto $ fromString configPath)
+
+    -- let zeroProbLab = P.parseTag taset zeroProbLabel
+    let zeroProbLab = PX.Interp
+          { PX.base = "none"
+          , PX.tag = T.pack zeroProbLabel
+          , PX.commonness = Nothing
+          , PX.qualifier = Nothing
+          , PX.metaInfo = Nothing
+          , PX.eos = False }
+        train0 = DB.parseData <$> readFileUtf8 trainPath
+        eval0  = case evalPath of
+          Nothing -> return []
+          Just ph -> DB.parseData <$> readFileUtf8 ph
+    -- putStrLn $ "\nRegularization variance: " ++ show regVar
+    concraft <- Pol.train (trainConf dhall tagset zeroProbLab) train0 eval0
     unless (null outModel) $ do
         putStrLn $ "\nSaving model in " ++ outModel ++ "..."
-        C.saveModel outModel concraft
+        Pol.saveModel outModel concraft
   where
     sgdArgs = SGD.SgdArgs
         { SGD.batchSize = batchSize
         , SGD.regVar = regVar
         , SGD.iterNum = iterNum
         , SGD.gain0 = gain0
-        , SGD.tau = tau }
-    trainConf tagset = C.TrainConf
-        { tagset    = tagset 
+        , SGD.tau = tau
+        }
+    trainConf dhall tagset zeroLab = Pol.TrainConf
+        { tagset    = tagset
         , sgdArgs   = sgdArgs
-        , reana     = not noAna
+        -- , reana     = not noAna
         , onDisk    = disk
         , guessNum  = guessNum
-        , r0        = r0 }
+        , r0        = r0
+        , zeroProbLabel = zeroLab
+        , guessOnlyVisible = visibleOnly
+        -- , disambTiersCfg = disambTiers
+        , globalConfig = dhall
+        }
 
 
 exec Tag{..} = do
-    cft <- C.loadModel inModel
-    pool <- Maca.newMacaPool numCapabilities
-    inp <- L.getContents
-    out <- R.long (R.short pool cft) $ rq $ if noAna
-        then R.Doc $ parseText format inp
-        else R.Long inp
-    L.putStr $ showData showCfg out
-  where
-    rq x = R.Request
-        { R.rqBody = x
-        , R.rqConf = rqConf }
-    rqConf = R.Config
-        { R.tagProbs = marginals }
-    showCfg = ShowCfg
-        { formatCfg = format
-        , showWsCfg = marginals }
+  -- crf <- Pol.loadModel P.parseTag inModel
+  crf <- Pol.loadModel Pol.simplify4gsr Pol.simplify4dmb inModel
+  -- inp <- DB.parseData <$> L.getContents
+  inp <- DB.parseData <$> case inFile of
+    Nothing -> getContentsUtf8
+    Just path -> readFileUtf8 path
+  pathSelection <-
+    case (shortestPath, longestPath, freqPath) of
+      (True, _, _) -> return $ Just Seg.Min
+      (_, True, _) -> return $ Just Seg.Max
+      (_, _, Just freqPath) -> do
+        freqMap <- loadFreqMap freqPath
+        let conf = Seg.FreqConf
+              { Seg.pickFreqMap = freqMap
+              , Seg.smoothingParam = freqSmoothing
+              }
+        return . Just $ Seg.Freq conf
+      _         -> return Nothing
+  let guessNum = case mayGuessNum of
+        Nothing -> C.guessNum crf
+        Just k  -> k
+      cfg = Pol.AnnoConf
+        { trimParam = guessNum
+        , pickPath = pathSelection
+        }
+      out = Pol.annoAll cfg crf <$> inp
+      showCfg = DB.ShowCfg
+        -- { suppressProbs = suppressProbs
+        { probType = probType
+        , numericDisamb = numericDisamb }
+  case outFile of
+    Nothing -> putStrUtf8 $ DB.showData showCfg out
+    Just path -> writeFileUtf8 path $ DB.showData showCfg out
 
 
 exec Server{..} = do
-    putStr "Loading model..." >> hFlush stdout
-    concraft <- C.loadModel inModel
-    putStrLn " done"
-    pool <- Maca.newMacaPool numCapabilities
-    let portNum = N.PortNumber $ fromIntegral port
-    putStrLn $ "Listening on port " ++ show port
-    S.runConcraftServer pool concraft portNum
+  crf <- Pol.loadModel Pol.simplify4gsr Pol.simplify4dmb inModel
+  let guessNum = case mayGuessNum of
+        Nothing -> C.guessNum crf
+        Just k  -> k
+      cfg = Pol.AnnoConf
+        { trimParam = guessNum
+        , pickPath = Nothing
+        }
+      showCfg = DB.ShowCfg
+        { probType = probType
+        , numericDisamb = numericDisamb
+        }
+      serverCfg = Server.ServerCfg
+        { concraft = crf
+        , annoCfg = cfg
+        , showCfg = showCfg
+        }
+  Server.runServer serverCfg port
 
 
 exec Client{..} = do
-    let portNum = N.PortNumber $ fromIntegral port
-    inp <- L.getContents
-    out <- R.long (S.submit host portNum) $ rq $ if noAna
-        then R.Doc $ parseText format inp
-        else R.Long inp
-    L.putStr $ showData showCfg out
-  where
-    rq x = R.Request
-        { R.rqBody = x
-        , R.rqConf = rqConf }
-    rqConf = R.Config
-        { R.tagProbs = marginals }
-    showCfg = ShowCfg
-        { formatCfg = format
-        , showWsCfg = marginals }
+  -- clear the file, if specified (stdout otherwise)
+  case outFile of
+    Nothing -> return ()
+    Just path -> writeFileUtf8 path ""
+  inpAll <- case inFile of
+    Nothing -> getContentsUtf8
+    Just path -> readFileUtf8 path
+  let inputs
+        = map (L.intercalate "\n\n")
+        . group batchSize
+        $ filter
+          (not . L.null)
+          (L.splitOn "\n\n" inpAll)
+  forM_ (map L.toStrict inputs) $ \inp -> do
+    let req = Server.Request {dag = inp}
+        cfg = Server.ClientCfg {serverAddr=serverAddr}
+    Server.sendRequest cfg req >>= \case
+      Nothing -> putStrLn "<< NO RESPONSE >>"
+      Just Server.Answer{..} -> case outFile of
+        Nothing -> putStrUtf8 (L.fromStrict dag)
+        Just path -> appendFileUtf8 path (L.fromStrict dag)
 
 
-exec Compare{..} = do
-    tagsetPath' <- case tagsetPath of
-        Nothing -> getDataFileName "config/nkjp-tagset.cfg" 
-        Just x  -> return x
-    tagset <- parseTagset tagsetPath' <$> readFile tagsetPath'
-    let convert = map (X.packSeg tagset) . concat
-    xs <- convert <$> parseFile format refPath
-    ys <- convert <$> parseFile format otherPath
-    let s = Acc.weakLB tagset xs ys
-    putStrLn $ "Number of segments in reference file: " ++ show (Acc.gold s)
-    putStrLn $ "Number of correct tags: " ++ show (Acc.good s)
-    putStrLn $ "Weak accuracy lower bound: " ++ show (Acc.accuracy s)
+exec Eval{..} = do
+  tagset <- parseTagset justTagsetPath <$> readFile justTagsetPath
+  let simplify = fmap $ \seg ->
+        let simplify4eval interp =
+              ( P.parseTag tagset $ PX.tag interp
+              , PX.eos interp && heedEos )
+            newTags = X.mapWMap simplify4eval (X.tags seg)
+        in  seg {X.tags = newTags}
+      process = PX.packSent . simplify
+      fromFile = fmap (map process . DB.parseData) . readFileUtf8
 
+  putStrLn $ concat
+    [ "Note that in this evaluation lemmas "
+    , "are *not* taken into account."
+    ]
 
-exec Prune{..} = do
-    cft <- C.loadModel inModel
-    C.saveModel outModel $ C.prune threshold cft
+  let cfg = Acc.AccCfg
+        { Acc.onlyOov = onlyOov
+        , Acc.onlyAmb = onlyAmb
+        , Acc.onlyMarkedWith =
+            if onlyEos
+            then S.singleton True
+            else S.empty
+        , Acc.accTagset = tagset
+        , Acc.expandTag = expandTags
+        , Acc.ignoreTag = ignoreTags
+        , Acc.weakAcc = weak
+        , Acc.discardProb0 = discardProb0
+        , Acc.verbose = verbose
+        }
+  stats <- Acc.collect cfg
+    <$> fromFile goldPath
+    <*> fromFile taggPath
+  putStr "Precision: " >> print (Acc.precision stats)
+  putStr "Recall: " >> print (Acc.recall stats)
+  putStr "Accuracy: " >> print (Acc.accuracy stats)
 
 
--- exec ReAna{..} = do
---     inp  <- parseText format <$> L.getContents
---     out  <- showData format <$> 
+exec Check{..} = do
+  tagset <- parseTagset justTagsetPath <$> readFile justTagsetPath
+  dags <- DB.parseData <$> readFileUtf8 dagPath
+  forM_ dags $ \dag -> do
+    if (not $ DAG.isOK dag) then do
+      putStrLn "Incorrectly structured graph:"
+      showDAG dag
+    else if (not $ DAG.isDAG dag) then do
+      putStrLn "Graph with cycles:"
+      showDAG dag
+    else case verifyProb tagset dag of
+      Nothing -> return ()
+      Just p -> do
+        putStr "Probability equal to "
+        putStr (show p)
+        putStrLn ":"
+        showDAG dag
+  where
+    showDAG dag =
+      forM_ (DAG.dagEdges dag) $ \edgeID -> do
+        let from = DAG.begsWith edgeID dag
+            to = DAG.endsWith edgeID dag
+            val = DAG.edgeLabel edgeID dag
+        putStr (show $ DAG.unNodeID from)
+        putStr ", "
+        putStr (show $ DAG.unNodeID to)
+        putStr " => "
+        T.putStrLn (PX.orth $ X.word val)
+    verifyProb tagset dag =
+      let schema = Schema.fromConf Schema.nullConf
+          rawData = Guess.schemed (Pol.simplify4gsr tagset) schema [PX.packSent dag]
+          [encDag] = CRF.Codec.encodeDataL (CRF.Codec.mkCodec rawData) rawData
+          p = CRF.Train.dagProb encDag
+          eps = 1e-9
+      in  if p >= 1 - eps && p <= 1 + eps
+          then Nothing
+          else Just p
 
 
----------------------------------------
--- Reading files
----------------------------------------
+exec Freqs{..} = do
+  -- tagset <- parseTagset justTagsetPath <$> readFile justTagsetPath
+  dags <- DB.parseData <$> readFileUtf8 dagPath
+  let freqMap = Seg.computeFreqs $ map PX.packSent dags
+  printFreqMap freqMap
 
 
-parseFileO' :: Format -> Maybe FilePath -> IO [X.SentO X.Tag]
-parseFileO' format path = case path of
-    Nothing -> return []
-    Just pt -> parseFileO format pt
+exec Ambi{..} = do
+  dags <- DB.parseData <$> readFileUtf8 dagPath
+  let stats = Seg.computeAmbiStats cfg $ map PX.packSent dags
+  print stats
+  where
+    cfg = Seg.AmbiCfg
+      { Seg.onlyChosen = onlyChosen
+      }
 
 
-parseFileO :: Format -> FilePath -> IO [X.SentO X.Tag]
-parseFileO format path = parseParaO format <$> L.readFile path
+---------------------------------------
+-- Frequency map
+---------------------------------------
 
 
-parseFile :: Format -> FilePath -> IO [X.Sent X.Tag]
-parseFile format path = parsePara format <$> L.readFile path
+printFreqMap
+  :: M.Map T.Text (Int, Int)
+  -> IO ()
+printFreqMap freqMap =
+  forM_ (M.toList freqMap) $ \(orth, (chosen, notChosen)) -> do
+    T.putStr $ T.replace "\t" " " orth
+    T.putStr "\t"
+    putStr $ show chosen
+    T.putStr "\t"
+    putStrLn $ show notChosen
 
 
+loadFreqMap
+  :: FilePath
+  -> IO (M.Map T.Text (Int, Int))
+loadFreqMap filePath = do
+  M.fromList . map readPair . T.lines <$> T.readFile filePath
+  where
+    readPair line =
+      case T.splitOn "\t" line of
+        [orth, chosen, notChosen] ->
+          (orth, (readInt chosen, readInt notChosen))
+        _ -> error $ "loadFreqMap: line incorrectly formatted: " ++ T.unpack line
+    readInt = read . T.unpack
+
+
 ---------------------------------------
--- Parsing text
+-- UTF8
 ---------------------------------------
 
 
--- parseTextO :: Format -> L.Text -> [[X.SentO X.Tag]]
--- parseTextO format = map (map X.withOrig) . parseText format
+readFileUtf8 :: FilePath -> IO L.Text
+readFileUtf8 path = L.decodeUtf8 <$> BL.readFile path
 
 
-parseParaO :: Format -> L.Text -> [X.SentO X.Tag]
-parseParaO format = map X.withOrig . parsePara format
+writeFileUtf8 :: FilePath -> L.Text -> IO ()
+writeFileUtf8 path = BL.writeFile path . L.encodeUtf8
 
 
----------------------------------------
--- Parsing (format dependent)
----------------------------------------
+appendFileUtf8 :: FilePath -> L.Text -> IO ()
+appendFileUtf8 path = BL.appendFile path . L.encodeUtf8
 
 
-parseText :: Format -> L.Text -> [[X.Sent X.Tag]]
-parseText Plain = P.parsePlain
+getContentsUtf8 :: IO L.Text
+getContentsUtf8 = L.decodeUtf8 <$> BL.getContents
 
 
-parsePara :: Format -> L.Text -> [X.Sent X.Tag]
-parsePara Plain = P.parsePara
+putStrUtf8 :: L.Text -> IO ()
+putStrUtf8 = BL.putStr . L.encodeUtf8
 
 
 ---------------------------------------
--- Showing (format dependent)
+-- Utils
 ---------------------------------------
 
 
-data ShowCfg = ShowCfg {
-    -- | The format used.
-      formatCfg :: Format
-    -- | Show weights?
-    , showWsCfg :: Bool }
+-- | Group the input list into the groups of X.
+group :: Int -> [a] -> [[a]]
+group n =
+  doit n []
+  where
+    doit k acc (x:xs)
+      | k > 0 =
+          doit (k-1) (x:acc) xs
+      | otherwise =
+          reverse acc : doit n [] (x:xs)
+    doit _ acc []
+      | null acc  = []
+      | otherwise = [reverse acc]
 
 
-showData :: ShowCfg -> [[X.Sent X.Tag]] -> L.Text
-showData ShowCfg{..} = P.showPlain (P.ShowCfg {P.showWsCfg = showWsCfg})
+-- ---------------------------------------
+-- -- Reading files
+-- ---------------------------------------
+--
+-- -- TODO: make everything work on DAG input/output.
+--
+-- parseFileO' :: Format -> Maybe FilePath -> IO [X.SentO X.Tag]
+-- parseFileO' format path = case path of
+--     Nothing -> return []
+--     Just pt -> parseFileO format pt
+--
+--
+-- parseFileO :: Format -> FilePath -> IO [X.SentO X.Tag]
+-- parseFileO format path = parseParaO format <$> L.readFile path
+--
+--
+-- parseFile :: Format -> FilePath -> IO [X.Sent X.Tag]
+-- parseFile format path = parsePara format <$> L.readFile path
+--
+--
+-- ---------------------------------------
+-- -- Parsing text
+-- ---------------------------------------
+--
+--
+-- -- parseTextO :: Format -> L.Text -> [[X.SentO X.Tag]]
+-- -- parseTextO format = map (map X.withOrig) . parseText format
+--
+--
+-- parseParaO :: Format -> L.Text -> [X.SentO X.Tag]
+-- parseParaO format = map X.withOrig . parsePara format
+--
+--
+-- ---------------------------------------
+-- -- Parsing (format dependent)
+-- ---------------------------------------
+--
+--
+-- parseText :: Format -> L.Text -> [[X.Sent X.Tag]]
+-- parseText Plain = P.parsePlain
+--
+--
+-- parsePara :: Format -> L.Text -> [X.Sent X.Tag]
+-- parsePara Plain = P.parsePara
+--
+--
+-- ---------------------------------------
+-- -- Showing (format dependent)
+-- ---------------------------------------
+--
+--
+-- data ShowCfg = ShowCfg {
+--     -- | The format used.
+--       formatCfg :: Format
+--     -- | Show weights?
+--     , showWsCfg :: Bool }
+--
+--
+-- showData :: ShowCfg -> [[X.Sent X.Tag]] -> L.Text
+-- showData ShowCfg{..} = P.showPlain (P.ShowCfg {P.showWsCfg = showWsCfg})
