diff --git a/Data/Boltzmann/Compiler.hs b/Data/Boltzmann/Compiler.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Compiler.hs
@@ -0,0 +1,27 @@
+{-|
+ Module      : Data.Boltzmann.Compiler
+ Description : Compiler configuration utilities.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General framework of system compilers.
+ -}
+module Data.Boltzmann.Compiler
+    ( Configuration(..)
+    ) where
+
+import Data.Boltzmann.System
+
+-- | Compiler configurations.
+class Configuration a where
+    config  :: PSystem Double -- ^ Parametrised system.
+            -> Maybe String   -- ^ Output file location.
+            -> String         -- ^ Module name.
+            -> String         -- ^ Compile note.
+            -> a              -- ^ Configuration.
+
+    compile :: a              -- ^ Configuration.
+            -> IO ()          -- ^ Compiler IO action.
diff --git a/Data/Boltzmann/Compiler/Haskell/Algebraic.hs b/Data/Boltzmann/Compiler/Haskell/Algebraic.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Compiler/Haskell/Algebraic.hs
@@ -0,0 +1,393 @@
+{-|
+ Module      : Data.Boltzmann.Compiler.Haskell.Algebraic
+ Description : Algebraic Boltzmann system compiler for ghc-7.10.3.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Algebraic system compiler using Haskell's built-in algebraic data types
+ to handle to given system types. The outcome sampler is a rejection-based
+ sampler implementing the anticipated-rejection sampling scheme.
+ -}
+module Data.Boltzmann.Compiler.Haskell.Algebraic
+    ( Conf(..)
+    , compile
+    , config
+    ) where
+
+import Prelude hiding (and)
+import Language.Haskell.Exts hiding (List)
+import qualified Language.Haskell.Exts as LHE
+
+import Language.Haskell.Exts.SrcLoc (noLoc)
+
+import Data.Boltzmann.System
+import Data.Boltzmann.Internal.Annotations
+
+import Data.Boltzmann.Compiler
+import Data.Boltzmann.Compiler.Haskell.Helpers
+
+-- | Default configuration type.
+data Conf = Conf { paramSys    :: PSystem Double   -- ^ Parametrised system.
+                 , outputFile  :: Maybe String     -- ^ Output file.
+                 , moduleName  :: String           -- ^ Module name.
+                 , compileNote :: String           -- ^ Header comment note.
+                 , withIO      :: Bool             -- ^ Generate IO actions?
+                 , withLists   :: Bool             -- ^ Generate all list samplers?
+                 , withShow    :: Bool             -- ^ Generate deriving Show?
+                 }
+
+instance Configuration Conf where
+
+    config sys file' module' compilerNote' =
+        let with = withBool (annotations $ system sys)
+         in Conf { paramSys    = sys
+                 , outputFile  = file'
+                 , moduleName  = module'
+                 , compileNote = compilerNote'
+                 , withIO      = "withIO"    `with` True
+                 , withShow    = "withShow"  `with` True
+                 , withLists   = "withLists" `with` False
+                 }
+
+    compile conf = let sys        = paramSys conf
+                       file'      = outputFile conf
+                       name'      = moduleName conf
+                       note       = compileNote conf
+                       withIO'    = withIO conf
+                       withLists' = withLists conf
+                       withShow'  = withShow conf
+                       module'    = compileModule sys name'
+                                        withIO' withLists' withShow'
+                   in case file' of
+                        Nothing -> do
+                            -- write to stdout
+                            putStr $ moduleHeader sys note
+                            putStrLn $ prettyPrint module'
+                        Just f -> do
+                            -- write to given file
+                            let header  = moduleHeader sys note
+                            let sampler = prettyPrint module'
+                            writeFile f $ header ++ sampler
+
+moduleHeader :: PSystem Double -> String -> String
+moduleHeader sys compilerNote =
+    unlines (["-- | Compiler: " ++ compilerNote,
+              "-- | Singularity: " ++ show (param sys),
+              "-- | System type: algebraic"] ++ systemNote sys)
+
+compileModule :: PSystem Double -> String -> Bool -> Bool -> Bool -> Module
+compileModule sys mod' withIO' withLists' withShow' =
+    Module noLoc (ModuleName mod') []
+        Nothing (Just exports) imports decls
+    where
+        exports = declareExports sys withIO' withLists'
+        imports = declareImports withIO'
+        decls = declareADTs withShow' sys ++
+                    declareGenerators sys ++
+                    declareListGenerators sys withLists' ++
+                    declareSamplers sys ++
+                    declareListSamplers sys withLists' ++
+                    declareSamplersIO sys withIO' ++
+                    declareListSamplersIO sys withIO' withLists'
+
+declareImports :: Bool -> [ImportDecl]
+declareImports withIO' =
+    [importFrom "Control.Monad" [importFunc "guard"],
+     importFrom "Control.Monad.Trans" [importFunc "lift"],
+     importFrom "Control.Monad.Trans.Maybe" [importType "MaybeT",
+                                             importFunc "runMaybeT"],
+
+     importFrom "Control.Monad.Random" ([importType "RandomGen",
+                                        importFunc "Rand",
+                                        importFunc "getRandomR"]
+                                        ++ importIO withIO')]
+
+importIO :: Bool -> [ImportSpec]
+importIO False = []
+importIO True  = [importFunc "evalRandIO"]
+
+-- Naming functions.
+genName :: ShowS
+genName = (++) "genRandom"
+
+listGenName :: ShowS
+listGenName t = genName t ++ "List"
+
+samplerName :: ShowS
+samplerName = (++) "sample"
+
+listSamplerName :: ShowS
+listSamplerName t = samplerName t ++ "List"
+
+samplerIOName :: ShowS
+samplerIOName t = samplerName t ++ "IO"
+
+listSamplerIOName  :: ShowS
+listSamplerIOName t = listSamplerName t ++ "IO"
+
+declareExports :: PSystem Double -> Bool -> Bool -> [ExportSpec]
+declareExports sys withIO' withLists' =
+    exportTypes sys ++
+    exportGenerators sys ++
+    exportListGenerators sys withLists' ++
+    exportSamplers sys ++
+    exportListSamplers sys withLists' ++
+    exportSamplersIO sys withIO' ++
+    exportListSamplersIO sys withIO' withLists'
+
+exportGenerators :: PSystem Double -> [ExportSpec]
+exportGenerators sys = map (exportFunc . genName) $ typeList sys
+
+exportListGenerators :: PSystem Double -> Bool -> [ExportSpec]
+exportListGenerators sys withLists' = map (exportFunc . listGenName) $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+exportSamplers :: PSystem Double -> [ExportSpec]
+exportSamplers sys = map (exportFunc . samplerName) $ typeList sys
+
+exportListSamplers :: PSystem Double -> Bool -> [ExportSpec]
+exportListSamplers sys withLists' = map (exportFunc . listSamplerName) $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+exportSamplersIO :: PSystem Double -> Bool -> [ExportSpec]
+exportSamplersIO _ False = []
+exportSamplersIO sys True = map (exportFunc . samplerIOName) $ typeList sys
+
+exportListSamplersIO :: PSystem Double -> Bool -> Bool -> [ExportSpec]
+exportListSamplersIO _ False _ = []
+exportListSamplersIO sys True withLists' = map (exportFunc . listSamplerIOName) $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+-- Utils.
+maybeT' :: Type
+maybeT' = typeCons "MaybeT"
+
+rand' :: Type
+rand' = typeCons "Rand"
+
+int' :: Type
+int' = typeCons "Int"
+
+g' :: Type
+g' = typeVar "g"
+
+randomGen' :: QName
+randomGen' = unname "RandomGen"
+
+return' :: Exp
+return' = varExp "return"
+
+nat :: [String]
+nat = map show ([0..] :: [Integer])
+
+variableStream :: [String]
+variableStream = map ('x' :) nat
+
+weightStream :: [String]
+weightStream = map ('w' :) nat
+
+-- Generators.
+maybeTType :: Type -> Type
+maybeTType = TyApp (TyApp maybeT' (TyApp rand' g'))
+
+generatorType :: Type -> Type
+generatorType type' = TyForall Nothing
+    [ClassA randomGen' [g']]
+    (TyFun int' (maybeTType $ TyTuple Boxed [type', int']))
+
+declRandomP :: [Decl]
+declRandomP = declTFun "randomP" type' [] body
+    where type' = TyForall Nothing [ClassA randomGen' [g']] (maybeTType $ typeVar "Double")
+          body = App (varExp "lift")
+                     (App (varExp "getRandomR")
+                          (Tuple Boxed [toLit 0, toLit 1]))
+
+randomP :: String -> Stmt
+randomP v = bind v $ varExp "randomP"
+
+guardian :: String -> Stmt
+guardian v = Qualifier $ App (varExp "guard")
+                             (varExp v `greater` toLit 0)
+
+declareGenerators :: PSystem Double -> [Decl]
+declareGenerators sys =
+    declRandomP ++
+        concatMap declGenerator (paramTypesW sys)
+
+declGenerator :: (String, [(Cons Double, Int)]) -> [Decl]
+declGenerator (t, g) = declTFun (genName t) type' ["ub"] body
+    where type' = generatorType $ typeCons t
+          body  = constrGenerator g
+
+constrGenerator :: [(Cons Double, Int)] -> Exp
+constrGenerator [(constr, w)] = rec True constr w
+constrGenerator cs = Do (initSteps ++ branching)
+    where branching = [Qualifier $ constrGenerator' cs]
+          initSteps = [guardian "ub",
+                       randomP "p"]
+
+constrGenerator' :: [(Cons Double, Int)] -> Exp
+constrGenerator' [(constr, w)] = rec False constr w
+constrGenerator' ((constr, w) : cs) =
+    If (lessF (varExp "p") $ weight constr)
+       (rec False constr w)
+       (constrGenerator' cs)
+constrGenerator' _ = error "I wasn't expecting the Spanish inquisition!"
+
+rec :: Bool -> Cons Double -> Int -> Exp
+rec withGuardian constr w =
+    case arguments (args constr) (toLit w) variableStream weightStream of
+      ([], _, _)          -> applyF return' [Tuple Boxed [cons, toLit w]]
+      (stmts, totalW, xs) ->
+          Do ([guardian "ub" | withGuardian] ++ stmts ++ [ret cons xs (toLit w `add` totalW)])
+
+    where cons = conExp $ func constr
+
+arguments :: [Arg] -> Exp -> [String] -> [String] -> ([Stmt], Exp, [Exp])
+arguments [] _ _ _ = ([], toLit 0, [])
+arguments (Type arg:args') ub xs ws = arguments' genName arg args' ub xs ws
+arguments (List arg:args') ub xs ws = arguments' listGenName arg args' ub xs ws
+
+arguments' :: (t -> String) -> t -> [Arg] -> Exp -> [String] -> [String] -> ([Stmt], Exp, [Exp])
+arguments' f arg args' ub (x:xs) (w:ws) = (stmt : stmts, argW', v : vs)
+    where stmt              = bindP x w $ applyF (varExp $ f arg) [varExp "ub" `sub` ub]
+          (stmts, argW, vs) = arguments args' ub' xs ws
+          argW'             = argW `add` varExp w
+          ub'               = ub `sub` varExp w
+          v                 = varExp x
+arguments' _ _ _ _ _ _ = error "I wasn't expecting the Spanish inquisition!"
+
+ret :: Exp -> [Exp] -> Exp -> Stmt
+ret f [] w = Qualifier $ applyF return' [Tuple Boxed [f, w]]
+ret f xs w = Qualifier $ applyF return' [Tuple Boxed [t, w]]
+    where t = applyF f xs
+
+-- List generators.
+listGeneratorType :: Type -> Type
+listGeneratorType type' = TyForall Nothing
+    [ClassA randomGen' [g']]
+        (TyFun int' (maybeTType $ TyTuple Boxed [TyList type', int']))
+
+declareListGenerators :: PSystem Double -> Bool -> [Decl]
+declareListGenerators sys withLists' = concatMap (declListGenerator sys) $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+declListGenerator :: PSystem Double -> String -> [Decl]
+declListGenerator sys t = declTFun (listGenName t) type' ["ub"] body
+    where type' = listGeneratorType (typeCons t)
+          body  = constrListGenerator sys t
+
+constrListGenerator :: PSystem Double -> String -> Exp
+constrListGenerator sys t = Do (initSteps ++ branching)
+    where branching = [Qualifier $ constrListGenerator' sys t]
+          initSteps = [guardian "ub",
+                       randomP "p"]
+
+constrListGenerator' :: PSystem Double -> String -> Exp
+constrListGenerator' sys t =
+    If (lessF (varExp "p") (typeWeight sys t))
+       (retHeadList t)
+       retNil
+
+retHeadList :: String -> Exp
+retHeadList t = Do
+    [bindP "x" "w" (applyF (varExp $ genName t) [varExp "ub"]),
+     bindP "xs" "ws" (applyF (varExp $ listGenName t) [varExp "ub" `sub` varExp "w"]),
+     ret (InfixApp (varExp "x") (symbol ":") (varExp "xs"))
+            [] (varExp "w" `add` varExp "ws")]
+
+retNil :: Exp
+retNil = applyF return' [Tuple Boxed [LHE.List [], toLit 0]]
+
+-- Samplers.
+samplerType :: Type -> Type
+samplerType type' = TyForall Nothing
+    [ClassA randomGen' [g']]
+    (TyFun int'
+           (TyFun int'
+                  (TyApp (TyApp rand' g') type')))
+
+declareSamplers :: PSystem Double -> [Decl]
+declareSamplers sys = concatMap declSampler $ typeList sys
+
+declSampler :: String -> [Decl]
+declSampler t = declTFun (samplerName t) type' ["lb","ub"] body
+    where type' = samplerType (typeCons t)
+          body  = constructSampler t
+
+constructSampler' :: (t -> String) -> (t -> String) -> t -> Exp
+constructSampler' gen sam t =
+    Do [bind "sample" (applyF (varExp "runMaybeT")
+            [applyF (varExp $ gen t) [varExp "ub"]]),
+            caseSample]
+    where caseSample = Qualifier $ Case (varExp "sample")
+                 [Alt noLoc (PApp (unname "Nothing") [])
+                        (UnGuardedRhs rec') Nothing,
+                        Alt noLoc (PApp (unname "Just")
+                 [PTuple Boxed [PVar $ Ident "x",
+                  PVar $ Ident "s"]])
+                  (UnGuardedRhs return'') Nothing]
+
+          rec' = applyF (varExp $ sam t) [varExp "lb", varExp "ub"]
+          return'' = If (lessEq (varExp "lb") (varExp "s") `and` lessEq (varExp "s") (varExp "ub"))
+                        (applyF (varExp "return") [varExp "x"])
+                        rec'
+
+constructSampler :: String -> Exp
+constructSampler = constructSampler' genName samplerName
+
+declareListSamplers :: PSystem Double -> Bool -> [Decl]
+declareListSamplers sys withLists' = concatMap declListSampler $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+declListSampler :: String -> [Decl]
+declListSampler t = declTFun (listSamplerName t) type' ["lb","ub"] body
+    where type' = samplerType (TyList $ typeCons t)
+          body  = constructListSampler t
+
+constructListSampler :: String -> Exp
+constructListSampler = constructSampler' listGenName listSamplerName
+
+-- IO Samplers.
+samplerIOType :: Type -> Type
+samplerIOType type' = TyForall Nothing
+    [] (TyFun int' (TyFun int' (TyApp (typeVar "IO") type')))
+
+declareSamplersIO :: PSystem Double -> Bool -> [Decl]
+declareSamplersIO _ False = []
+declareSamplersIO sys True = concatMap declSamplerIO $ typeList sys
+
+declSamplerIO :: String -> [Decl]
+declSamplerIO t = declTFun (samplerIOName t) type' ["lb","ub"] body
+    where type' = samplerIOType (typeCons t)
+          body  = constructSamplerIO t
+
+constructSamplerIO' :: (t -> String) -> t -> Exp
+constructSamplerIO' sam t = applyF (varExp "evalRandIO")
+                               [applyF (varExp $ sam t) [varExp "lb",
+                                                         varExp "ub"]]
+
+constructSamplerIO :: String -> Exp
+constructSamplerIO = constructSamplerIO' samplerName
+
+declareListSamplersIO :: PSystem Double -> Bool -> Bool -> [Decl]
+declareListSamplersIO _ False _ = []
+declareListSamplersIO sys True withLists' = concatMap declListSamplerIO $ types' sys
+    where types' = if withLists' then typeList
+                                 else seqTypes . system
+
+declListSamplerIO :: String -> [Decl]
+declListSamplerIO t = declTFun (listSamplerIOName t) type' ["lb","ub"] body
+    where type' = samplerIOType (TyList $ typeCons t)
+          body  = constructListSamplerIO t
+
+constructListSamplerIO :: String -> Exp
+constructListSamplerIO = constructSamplerIO' listSamplerName
diff --git a/Data/Boltzmann/Compiler/Haskell/Helpers.hs b/Data/Boltzmann/Compiler/Haskell/Helpers.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Compiler/Haskell/Helpers.hs
@@ -0,0 +1,141 @@
+{-|
+ Module      : Data.Boltzmann.Compiler.Haskell.Helpers
+ Description : Helper methods for ghc-7.10.3 compiler syntax.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General utilities used across system compilers.
+ -}
+module Data.Boltzmann.Compiler.Haskell.Helpers where
+
+import Language.Haskell.Exts hiding (List)
+import Language.Haskell.Exts.SrcLoc (noLoc)
+
+import Data.Boltzmann.System
+
+-- | Prepares a system/module note.
+systemNote :: PSystem Double -> [String]
+systemNote psys = ["-- | System size: " ++ show n,
+                   "-- | Constructors: " ++ show m]
+    where sys = system psys
+          m   = constructors sys
+          n   = size sys
+
+unname :: String -> QName
+unname = UnQual . Ident
+
+typeCons :: String -> Type
+typeCons = TyCon . unname
+
+typeVar :: String -> Type
+typeVar = TyVar . Ident
+
+varExp :: String -> Exp
+varExp = Var . unname
+
+conExp :: String -> Exp
+conExp = Con . unname
+
+toLit :: Int -> Exp
+toLit = Lit . Int . toInteger
+
+importType :: String -> ImportSpec
+importType = IThingAll . Ident
+
+importFunc :: String -> ImportSpec
+importFunc = IVar . Ident
+
+-- | Simple import declaration.
+importFrom :: String -> [ImportSpec] -> ImportDecl
+importFrom module' specs = ImportDecl { importLoc = noLoc
+                                      , importModule = ModuleName module'
+                                      , importQualified = False
+                                      , importSrc = False
+                                      , importSafe = False
+                                      , importPkg = Nothing
+                                      , importAs = Nothing
+                                      , importSpecs = Just (False, specs)
+                                      }
+
+exportType :: String -> ExportSpec
+exportType = EThingAll . unname
+
+exportTypes :: PSystem Double -> [ExportSpec]
+exportTypes sys = map exportType $ typeList sys
+
+exportFunc :: String -> ExportSpec
+exportFunc = EVar . unname
+
+-- | Simple function declaration.
+declTFun :: String -> Type -> [String] -> Exp -> [Decl]
+declTFun f type' args' body = [decl, FunBind [main]]
+    where decl   = TypeSig noLoc [Ident f] type'
+          args'' = map (PVar . Ident) args'
+          main   = Match noLoc (Ident f) args'' Nothing
+                         (UnGuardedRhs body) Nothing
+
+symbol :: String -> QOp
+symbol s = QVarOp $ UnQual (Symbol s)
+
+greater :: Exp -> Exp -> Exp
+greater x = InfixApp x (symbol ">")
+
+less :: Exp -> Exp -> Exp
+less x = InfixApp x (symbol "<")
+
+and :: Exp -> Exp -> Exp
+and x = InfixApp x (symbol "&&")
+
+lessEq :: Exp -> Exp -> Exp
+lessEq x = InfixApp x (symbol "<=")
+
+lessF :: Real a => Exp -> a -> Exp
+lessF v x = less v (Lit $ Frac (toRational x))
+
+bind :: String -> Exp -> Stmt
+bind v = Generator noLoc (PVar $ Ident v)
+
+bindP :: String -> String -> Exp -> Stmt
+bindP x y = Generator noLoc (PTuple Boxed [PVar (Ident x), PVar (Ident y)])
+
+sub :: Exp -> Exp -> Exp
+sub x (Lit (Int 0)) = x
+sub x y             = InfixApp x (symbol "-") y
+
+add :: Exp -> Exp -> Exp
+add x (Lit (Int 0)) = x
+add (Lit (Int 0)) x = x
+add x y             = InfixApp x (symbol "+") y
+
+applyF :: Exp -> [Exp] -> Exp
+applyF = foldl App
+
+dot :: Exp -> Exp -> Exp
+dot x = InfixApp x (symbol ".")
+
+declareADTs :: Real a => Bool -> PSystem a -> [Decl]
+declareADTs withShow sys = map (declADT withShow) $ paramTypes sys
+
+declADT :: Real a => Bool -> (String, [Cons a]) -> Decl
+declADT withShow (t,[con]) = DataDecl noLoc flag [] (Ident t) []
+                               [QualConDecl noLoc [] [] (declCon con)]
+                               [(unname "Show", []) | withShow]
+
+    -- generate a newtype or data type?
+   where flag = if length (args con) == 1 then NewType
+                                          else DataType
+
+declADT withShow (t,cons) = DataDecl noLoc DataType [] (Ident t) []
+                              (map (QualConDecl noLoc [] [] . declCon) cons)
+                              [(unname "Show", []) | withShow]
+
+declCon :: Real a => Cons a -> ConDecl
+declCon expr = ConDecl (Ident $ func expr) ags
+    where ags = map declArg (args expr)
+
+declArg :: Arg -> Type
+declArg (Type s) = typeVar s
+declArg (List s) = TyList $ typeVar s
diff --git a/Data/Boltzmann/Compiler/Haskell/Rational.hs b/Data/Boltzmann/Compiler/Haskell/Rational.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Compiler/Haskell/Rational.hs
@@ -0,0 +1,304 @@
+{-|
+ Module      : Data.Boltzmann.Compiler.Haskell.Rational
+ Description : Rational Boltzmann system compiler for ghc-7.10.3.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Rational system compiler using Haskell's built-in algebraic data types
+ to handle to given system types. The outcome sampler is a rejection-based
+ sampler implementing the interruptible sampling scheme for strongly connected
+ specifications.
+ -}
+module Data.Boltzmann.Compiler.Haskell.Rational
+    ( Conf(..)
+    , compile
+    , config
+    ) where
+
+import Prelude hiding (and)
+import Language.Haskell.Exts hiding (List)
+import Language.Haskell.Exts.SrcLoc (noLoc)
+
+import Data.Boltzmann.System
+import Data.Boltzmann.Internal.Annotations
+
+import Data.Boltzmann.Compiler
+import Data.Boltzmann.Compiler.Haskell.Helpers
+
+-- | Default configuration type.
+data Conf = Conf { paramSys    :: PSystem Double   -- ^ Parametrised system.
+                 , outputFile  :: Maybe String     -- ^ Output file.
+                 , moduleName  :: String           -- ^ Module name.
+                 , compileNote :: String           -- ^ Header comment note.
+                 , withIO      :: Bool             -- ^ Generate IO actions?
+                 , withShow    :: Bool             -- ^ Generate deriving Show?
+                 }
+
+instance Configuration Conf where
+
+    config sys file' module' compilerNote' =
+        let with = withBool (annotations $ system sys)
+         in Conf { paramSys    = sys
+                 , outputFile  = file'
+                 , moduleName  = module'
+                 , compileNote = compilerNote'
+                 , withIO      = "withIO"    `with` True
+                 , withShow    = "withShow"  `with` True
+                 }
+
+    compile conf = let sys        = paramSys conf
+                       file'      = outputFile conf
+                       name'      = moduleName conf
+                       note       = compileNote conf
+                       withIO'    = withIO conf
+                       withShow'  = withShow conf
+                       module'    = compileModule sys name'
+                                        withIO' withShow'
+                   in case file' of
+                        Nothing -> do
+                            -- write to stdout
+                            putStr $ moduleHeader sys note
+                            putStrLn $ prettyPrint module'
+                        Just f -> do
+                            -- write to given file
+                            let header  = moduleHeader sys note
+                            let sampler = prettyPrint module'
+                            writeFile f $ header ++ sampler
+
+moduleHeader :: PSystem Double -> String -> String
+moduleHeader sys compilerNote =
+    unlines (["-- | Compiler: " ++ compilerNote,
+              "-- | Singularity: " ++ show (param sys),
+              "-- | System type: rational"] ++ systemNote sys)
+
+compileModule :: PSystem Double -> String -> Bool -> Bool -> Module
+compileModule sys mod' withIO' withShow' =
+    Module noLoc (ModuleName mod') []
+        Nothing (Just exports) imports decls
+    where
+        exports = declareExports sys withIO'
+        imports = declareImports withIO'
+        decls = declareADTs withShow' sys ++
+                    declareGenerators sys ++
+                    declareSamplers sys ++
+                    declareSamplersIO sys withIO'
+
+declareImports :: Bool -> [ImportDecl]
+declareImports withIO' =
+    [importFrom "Control.Monad.Trans" [importFunc "lift"],
+     importFrom "Control.Monad.Trans.Maybe" [importType "MaybeT",
+                                             importFunc "runMaybeT"],
+
+     importFrom "Control.Monad.Random" ([importType "RandomGen",
+                                        importFunc "Rand",
+                                        importFunc "getRandomR"]
+                                        ++ importIO withIO')]
+
+importIO :: Bool -> [ImportSpec]
+importIO False = []
+importIO True  = [importFunc "evalRandIO"]
+
+-- Naming functions.
+genName :: ShowS
+genName = (++) "genRandom"
+
+listGenName :: ShowS
+listGenName t = genName t ++ "List"
+
+samplerName :: ShowS
+samplerName = (++) "sample"
+
+samplerIOName :: ShowS
+samplerIOName t = samplerName t ++ "IO"
+
+declareExports :: PSystem Double -> Bool -> [ExportSpec]
+declareExports sys withIO' =
+    exportTypes sys ++
+    exportGenerators sys ++
+    exportSamplers sys ++
+    exportSamplersIO sys withIO'
+
+exportGenerators :: PSystem Double -> [ExportSpec]
+exportGenerators sys = map (exportFunc . genName) $ typeList sys
+
+exportSamplers :: PSystem Double -> [ExportSpec]
+exportSamplers sys = map (exportFunc . samplerName) $ typeList sys
+
+exportSamplersIO :: PSystem Double -> Bool -> [ExportSpec]
+exportSamplersIO _ False = []
+exportSamplersIO sys True = map (exportFunc . samplerIOName) $ typeList sys
+
+-- Utils.
+maybeT' :: Type
+maybeT' = typeCons "MaybeT"
+
+rand' :: Type
+rand' = typeCons "Rand"
+
+int' :: Type
+int' = typeCons "Int"
+
+g' :: Type
+g' = typeVar "g"
+
+randomGen' :: QName
+randomGen' = unname "RandomGen"
+
+return' :: Exp
+return' = varExp "return"
+
+nat :: [String]
+nat = map show ([0..] :: [Integer])
+
+variableStream :: [String]
+variableStream = map ('x' :) nat
+
+weightStream :: [String]
+weightStream = map ('w' :) nat
+
+-- Generators.
+maybeTType :: Type -> Type
+maybeTType = TyApp (TyApp maybeT' (TyApp rand' g'))
+
+generatorType :: Type -> Type
+generatorType type' = TyForall Nothing
+    [ClassA randomGen' [g']]
+    (TyFun int' (maybeTType $ TyTuple Boxed [type', int']))
+
+declRandomP :: [Decl]
+declRandomP = declTFun "randomP" type' [] body
+    where type' = TyForall Nothing [ClassA randomGen' [g']] (maybeTType $ typeVar "Double")
+          body = App (varExp "lift")
+                     (App (varExp "getRandomR")
+                          (Tuple Boxed [toLit 0, toLit 1]))
+
+randomP :: String -> Stmt
+randomP v = bind v $ varExp "randomP"
+
+when :: String -> (Cons Double, Int) -> Exp -> Stmt
+when v (cons, w) exp' =
+    Qualifier $ If (varExp v `lessEq` toLit 0)
+                    (applyF return' [Tuple Boxed [conExp (func cons), toLit w]])
+                    exp'
+
+declareGenerators :: PSystem Double -> [Decl]
+declareGenerators sys =
+    declRandomP ++
+        concatMap declGenerator (paramTypesW sys)
+
+declGenerator :: (String, [(Cons Double, Int)]) -> [Decl]
+declGenerator (t, g) = declTFun (genName t) type' ["ub"] body
+    where type' = generatorType $ typeCons t
+          body  = constrGenerator g
+
+atoms :: [(Cons Double, Int)] -> [(Cons Double, Int)]
+atoms = filter (isAtomic . fst)
+
+constrGenerator :: [(Cons Double, Int)] -> Exp
+constrGenerator [(constr, w)] = rec constr w
+constrGenerator cs = Do initSteps
+    where branching = [Qualifier $ constrGenerator' cs]
+          terms     = atoms cs
+          mainBody  = randomP "p" : branching
+          initSteps = if length terms == 1 then [when "ub" (head terms) (Do mainBody)]
+                                           else mainBody
+
+constrGenerator' :: [(Cons Double, Int)] -> Exp
+constrGenerator' [(constr, w)] = rec constr w
+constrGenerator' ((constr, w) : cs) =
+    If (lessF (varExp "p") $ weight constr)
+       (rec constr w)
+       (constrGenerator' cs)
+constrGenerator' _ = error "I wasn't expecting the Spanish inquisition!"
+
+rec :: Cons Double -> Int -> Exp
+rec constr w =
+    case arguments (args constr) (toLit w) variableStream weightStream of
+      ([], _, _)          -> applyF return' [Tuple Boxed [conExp (func constr), toLit w]]
+      (stmts, totalW, xs) ->
+          let mainBody = stmts ++ [ret (conExp $ func constr) xs (toLit w `add` totalW)]
+              interrupt = if isAtomic constr then [when "ub" (constr,w) (Do mainBody)]
+                                             else mainBody
+            in Do interrupt
+
+
+arguments :: [Arg] -> Exp -> [String] -> [String] -> ([Stmt], Exp, [Exp])
+arguments [] _ _ _ = ([], toLit 0, [])
+arguments (Type arg:args') ub xs ws = arguments' genName arg args' ub xs ws
+arguments (List arg:args') ub xs ws = arguments' listGenName arg args' ub xs ws
+
+arguments' :: (t -> String) -> t -> [Arg] -> Exp -> [String] -> [String] -> ([Stmt], Exp, [Exp])
+arguments' f arg args' ub (x:xs) (w:ws) = (stmt : stmts, argW', v : vs)
+    where stmt              = bindP x w $ applyF (varExp $ f arg) [varExp "ub" `sub` ub]
+          (stmts, argW, vs) = arguments args' ub' xs ws
+          argW'             = argW `add` varExp w
+          ub'               = ub `sub` varExp w
+          v                 = varExp x
+arguments' _ _ _ _ _ _ = error "I wasn't expecting the Spanish inquisition!"
+
+ret :: Exp -> [Exp] -> Exp -> Stmt
+ret f [] w = Qualifier $ applyF return' [Tuple Boxed [f, w]]
+ret f xs w = Qualifier $ applyF return' [Tuple Boxed [t, w]]
+    where t = applyF f xs
+
+-- Samplers.
+samplerType :: Type -> Type
+samplerType type' = TyForall Nothing
+    [ClassA randomGen' [g']]
+    (TyFun int'
+           (TyFun int'
+                  (TyApp (TyApp rand' g') type')))
+
+declareSamplers :: PSystem Double -> [Decl]
+declareSamplers sys = concatMap declSampler $ typeList sys
+
+declSampler :: String -> [Decl]
+declSampler t = declTFun (samplerName t) type' ["lb","ub"] body
+    where type' = samplerType (typeCons t)
+          body  = constructSampler t
+
+constructSampler' :: (t -> String) -> (t -> String) -> t -> Exp
+constructSampler' gen sam t =
+    Do [bind "sample" (applyF (varExp "runMaybeT")
+            [applyF (varExp $ gen t) [varExp "lb"]]),
+            caseSample]
+    where caseSample = Qualifier $ Case (varExp "sample")
+                 [Alt noLoc (PApp (unname "Nothing") [])
+                        (UnGuardedRhs rec') Nothing,
+                        Alt noLoc (PApp (unname "Just")
+                 [PTuple Boxed [PVar $ Ident "x",
+                  PVar $ Ident "s"]])
+                  (UnGuardedRhs return'') Nothing]
+
+          rec' = applyF (varExp $ sam t) [varExp "lb", varExp "ub"]
+          return'' = If (lessEq (varExp "lb") (varExp "s") `and` lessEq (varExp "s") (varExp "ub"))
+                        (applyF (varExp "return") [varExp "x"])
+                        rec'
+
+constructSampler :: String -> Exp
+constructSampler = constructSampler' genName samplerName
+
+-- IO Samplers.
+samplerIOType :: Type -> Type
+samplerIOType type' = TyForall Nothing
+    [] (TyFun int' (TyFun int' (TyApp (typeVar "IO") type')))
+
+declareSamplersIO :: PSystem Double -> Bool -> [Decl]
+declareSamplersIO _ False = []
+declareSamplersIO sys True = concatMap declSamplerIO $ typeList sys
+
+declSamplerIO :: String -> [Decl]
+declSamplerIO t = declTFun (samplerIOName t) type' ["lb","ub"] body
+    where type' = samplerIOType (typeCons t)
+          body  = constructSamplerIO t
+
+constructSamplerIO' :: (t -> String) -> t -> Exp
+constructSamplerIO' sam t = applyF (varExp "evalRandIO")
+                               [applyF (varExp $ sam t) [varExp "lb",
+                                                         varExp "ub"]]
+
+constructSamplerIO :: String -> Exp
+constructSamplerIO = constructSamplerIO' samplerName
diff --git a/Data/Boltzmann/Internal/Annotations.hs b/Data/Boltzmann/Internal/Annotations.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Internal/Annotations.hs
@@ -0,0 +1,64 @@
+{-|
+ Module      : Data.Boltzmann.Internal.Annotations
+ Description : System annotation utilities.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General utilities meant for handing system annotations
+ guiding the tuning and compilation process.
+ -}
+module Data.Boltzmann.Internal.Annotations
+    ( withDefault
+    , withDouble
+    , withInt
+    , withBool
+    ) where
+
+import Data.Maybe (fromMaybe)
+import Data.Char (toLower)
+
+import Data.Map (Map)
+import qualified Data.Map as M
+
+import Text.Read (readMaybe)
+
+-- | Read a given key value of a map with a default fallback.
+withDefault :: Read a
+            => Map String String
+            -> String -> a -> a
+
+withDefault f x d = case x `M.lookup` f of
+                      Nothing -> d
+                      Just x' -> fromMaybe d (readMaybe x')
+
+-- | `withDefault` specialised to doubles.
+withDouble :: Map String String
+           -> String -> Int -> Int
+
+withDouble = withDefault
+
+-- | `withDefault` specialised to ints.
+withInt :: Map String String
+        -> String -> Int -> Int
+
+withInt = withDefault
+
+-- | `withDefault` specialised to bools.
+withBool :: Map String String
+         -> String -> Bool -> Bool
+
+withBool f x d = case x `M.lookup` f of
+                   Nothing -> d
+                   Just x' -> case map toLower x' of
+                                "yes"   -> True    -- support 'yes'
+                                "y"     -> True    -- .. and alternative 'y'
+                                "1"     -> True    -- .. and alternative '1'
+                                "true"  -> True
+                                "no"    -> False
+                                "n"     -> False
+                                "false" -> False
+                                "0"     -> False
+                                _       -> d       -- error, fall back to default
diff --git a/Data/Boltzmann/Internal/Parser.hs b/Data/Boltzmann/Internal/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/Internal/Parser.hs
@@ -0,0 +1,93 @@
+{-|
+ Module      : Data.Boltzmann.Internal.Parser
+ Description : Parser utilities for combinatorial systems.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Common parser utilities and helper functions.
+ -}
+module Data.Boltzmann.Internal.Parser
+    ( sc
+
+    , lexeme
+    , symbol
+    , parens
+    , brackets
+    , integer
+    , double
+
+    , sepBy2
+    , parseN
+
+    , parseFromFile
+    , printError
+    ) where
+
+import Control.Monad (void)
+
+import Text.Megaparsec
+import Text.Megaparsec.String
+import qualified Text.Megaparsec.Lexer as L
+
+-- | Cut-out block and line comments parser.
+sc :: Parser ()
+sc = L.space (void spaceChar) lineCmnt blockCmnt
+    where lineCmnt  = L.skipLineComment "--"
+          blockCmnt = L.skipBlockComment "{-" "-}"
+
+-- | Lexeme parser.
+-- | Lexeme parser.
+lexeme :: Parser a -> Parser a
+lexeme = L.lexeme sc
+
+-- | Symbol parser.
+symbol :: String -> Parser String
+symbol = L.symbol sc
+
+-- | Parenthesis parser.
+parens :: Parser a -> Parser a
+parens = between (symbol "(") (symbol ")")
+
+-- | Brackets parser.
+brackets :: Parser a -> Parser a
+brackets = between (symbol "[") (symbol "]")
+
+-- | Integer parser.
+integer :: Parser Int
+integer = lexeme $ do
+    n <- L.integer
+    return $ fromIntegral n
+
+-- | Double parser.
+double :: Parser Double
+double = lexeme L.float
+
+-- | Separate by two parser.
+sepBy2 :: Parser a -> Parser b -> Parser [a]
+sepBy2 p q = do
+    x <- p
+    void q
+    xs <- p `sepBy1` q
+    return (x : xs)
+
+-- | n-fold parser application.
+parseN :: Parser a -> Int -> Parser [a]
+parseN _ 0 = return []
+parseN p n = do
+    x <- p
+    xs <- parseN p (n-1)
+    return $ x : xs
+
+-- | Uses an input file for parsing.
+parseFromFile :: Parsec e String a
+              -> String
+              -> IO (Either (ParseError Char e) a)
+
+parseFromFile p file = runParser p file <$> readFile file
+
+-- | Prints the given parsing errors.
+printError :: ParseError Char Dec -> IO ()
+printError err = putStr $ parseErrorPretty err
diff --git a/Data/Boltzmann/System.hs b/Data/Boltzmann/System.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System.hs
@@ -0,0 +1,241 @@
+{-|
+ Module      : Data.Boltzmann.System
+ Description : System utilities for combinatorial specifications.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General utilities for combinatorial system of algebraic and rational systems.
+ -}
+module Data.Boltzmann.System
+    ( System(..)
+    , size
+    , constructors
+    , Cons(..)
+    , Arg(..)
+    , types
+
+    , PSystem(..)
+    , typeList
+    , paramTypes
+    , paramTypesW
+    , typeWeight
+    , seqTypes
+
+    , SystemType(..)
+    , systemType
+    , hasAtoms
+    , isAtomic
+
+    , evalT
+    , evalC
+    , evalA
+    , getIdx
+    , value
+    , eval
+    ) where
+
+import Data.Set (Set)
+import qualified Data.Set as S
+
+import Data.Map (Map)
+import qualified Data.Map.Strict as M
+
+import Numeric.LinearAlgebra hiding (size)
+
+import Data.Maybe (mapMaybe)
+
+import Data.List (nub)
+import Data.Graph
+
+-- | System of combinatorial structures.
+data System a = System { defs        :: Map String [Cons a]   -- ^ Type definitions.
+                       , annotations :: Map String String     -- ^ System annotations.
+                       } deriving (Show)
+
+-- | Size of a combinatorial system.
+size :: System a -> Int
+size = M.size . defs
+
+-- | Constructors of a combinatorial system.
+constructors :: System a -> Int
+constructors = length . concat . M.elems . defs
+
+-- | Type constructor.
+data Cons a = Cons { func      :: String        -- ^ Constructor name.
+                   , args      :: [Arg]         -- ^ Argument list.
+                   , weight    :: a             -- ^ Constructor weight.
+                   , frequency :: Maybe Double  -- ^ Marking parameter.
+                   } deriving (Eq,Show)
+
+-- | Type constructor arguments.
+data Arg = Type String                       -- ^ Regular type reference.
+         | List String                       -- ^ Type list reference.
+           deriving (Eq,Show)
+
+-- | The name of an argument.
+argName :: Arg -> String
+argName (Type s) = s
+argName (List s) = s
+
+-- | Type set of the given system.
+types :: System a -> Set String
+types = M.keysSet . defs
+
+-- | Parametrised system of combinatorial structures.
+data PSystem a = PSystem { system  :: System a      -- ^ System with probability weights.
+                         , values  :: Vector a      -- ^ Numerical values of corresponding types.
+                         , param   :: a             -- ^ Evaluation parameter.
+                         , weights :: System Int    -- ^ System with input weights.
+                         } deriving (Show)
+
+-- | Type list of the given parametrised system.
+typeList :: PSystem a -> [String]
+typeList = S.toList . M.keysSet . defs . system
+
+-- | List of types with corresponding constructors.
+paramTypes :: PSystem a -> [(String, [Cons a])]
+paramTypes = M.toList . defs . system
+
+-- | List of types with corresponding constructors and input weights.
+paramTypesW :: PSystem a -> [(String, [(Cons a, Int)])]
+paramTypesW sys = map (addW $ weights sys) xs
+    where xs = paramTypes sys
+
+addW :: System Int -> (String, [a]) -> (String, [(a, Int)])
+addW sys (s, cons) = (s, zip cons ws)
+    where ws = typeW sys s
+
+typeW :: System Int -> String -> [Int]
+typeW sys s = case s `M.lookup` defs sys of
+    Just cons -> map weight cons
+    Nothing -> []
+
+-- | Type weight of the given parametrised system.
+typeWeight :: PSystem Double -> String -> Double
+typeWeight sys t = vec ! idx
+    where m   = defs $ system sys
+          vec = values sys
+          idx = M.findIndex t m
+
+-- | List of sequence types.
+seqTypes :: System a -> [String]
+seqTypes = S.elems . S.fromList . concatMap seqTypesCons
+            . concat . M.elems . defs
+
+seqTypesCons :: Cons a -> [String]
+seqTypesCons = mapMaybe listN . args
+    where listN (List s) = Just s
+          listN _        = Nothing
+
+-- | Checks it the argument is a list.
+isListArg :: Arg -> Bool
+isListArg (List _) = True
+isListArg  _       = False
+
+-- | Type of a combinatorial system.
+--   Note: System other than rational or algebraic are not yet supported.
+data SystemType = Rational
+                | Algebraic
+                | Unsupported String   -- ^ error message
+
+instance Show SystemType where
+    show Rational        = "rational"
+    show Algebraic       = "algebraic"
+    show (Unsupported _) = "unsupported"
+
+-- | Determines the system type.
+systemType :: System a -> SystemType
+systemType sys
+  | not (isLinear sys)        = Algebraic
+  | not (isInterruptible sys) = Unsupported "Given rational system is not interruptible."
+  | otherwise =
+    let depGraph = dependencyGraph sys
+     in case scc depGraph of
+          [_] -> Rational
+          xs  -> Unsupported $ "Given rational system has "
+                    ++ show (length xs) ++ " strongly connected components."
+
+-- | Constructs a dependency graph for the given system.
+dependencyGraph :: System a -> Graph
+dependencyGraph sys = buildG (0,n+d-1) (edgs ++ edgs')
+    where idx s      = M.findIndex s (defs sys)
+          idxSeq s   = n + S.findIndex s seqsSet
+          edgs       = concatMap (edges' atomicT idx idxSeq) $ M.toList (defs sys)
+          edgs'      = concatMap (\t -> [(idxSeq t, idxSeq t),
+                                    (idxSeq t, idx t)]) seqs
+          atomicT    = atomicTypes sys
+          seqsSet    = S.fromAscList seqs
+          seqs       = seqTypes sys
+          d          = S.size seqsSet
+          n          = size sys
+
+edges' :: Set String -> (String -> Int) -> (String -> Int) -> (String, [Cons b]) -> [(Vertex, Vertex)]
+edges' atomicT idx idxSeq (t,cons) = concatMap edge' $ neighbours cons
+    where tidx           = idx t
+          neighbours     = nub . concatMap args
+          edge' (List s) = [(tidx, idxSeq s)]
+          edge' (Type s)
+            | s `S.member` atomicT = [(tidx, idx s), (idx s, tidx)] -- double edge
+            | otherwise = [(tidx, idx s)]
+
+-- | Checks whether the system is linear, i.e.
+--   each constructor references at most one type.
+isLinear :: System a -> Bool
+isLinear sys = all (all linear) (M.elems $ defs sys)
+    where atomicT     = atomicTypes sys
+          linear cons =  not (any isListArg $ args cons)
+            && length (compoundArgs atomicT $ args cons) <= 1
+
+-- | Determines whether each constructor n the system has at most one atom.
+--   Note: the system is assumed to contain some atoms (see hasAtoms).
+isInterruptible :: System a -> Bool
+isInterruptible sys = all interruptible' $ M.elems (defs sys)
+    where interruptible' cons = length (filter isAtomic cons) <= 1
+
+compoundArgs :: Set String -> [Arg] -> [Arg]
+compoundArgs atomicT = filter (\x -> argName x `S.notMember` atomicT)
+
+-- | Determines the set of "atomic" types.
+atomicTypes :: System a -> Set String
+atomicTypes sys = S.fromList $ map fst ts
+    where ts = filter isAtomic' $ M.toList (defs sys)
+          isAtomic' (_,cons) = all isAtomic cons
+
+isAtomic :: Cons a -> Bool
+isAtomic = null . args
+
+-- | Determines whether the system has atoms.
+hasAtoms :: System a -> Bool
+hasAtoms sys = any (any isAtomic) $ M.elems (defs sys)
+
+-- | Evaluates the type in the given coordinates.
+evalT :: System Int -> Double -> Vector Double -> [Cons Int] -> Double
+evalT sys z ys cons = sum $ map (evalC sys z ys) cons
+
+-- | Evaluates the constructor in the given coordinates.
+evalC :: System Int -> Double -> Vector Double -> Cons Int -> Double
+evalC sys z ys con = foldl (*) start $ map (evalA sys ys) (args con)
+    where w = weight con
+          start = if w > 0 then z ^^ w
+                           else 1
+
+-- | Evaluates the argument in the given coordinates.
+evalA :: System Int -> Vector Double -> Arg -> Double
+evalA sys ys (Type t) = ys ! getIdx sys t
+evalA sys ys (List t) = recip $ 1 - ys ! getIdx sys t
+
+getIdx :: System Int -> String -> Int
+getIdx sys x = x `M.findIndex` defs sys
+
+value :: String -> System b -> Vector Double -> Double
+value t sys vec = vec ! M.findIndex t (defs sys)
+
+-- | Evaluates the system at the given coordinates.
+eval :: System Int -> Vector Double -> Double -> Vector Double
+eval sys ys z = n |> map update [0..n]
+    where n = size sys
+          f k = snd $ M.elemAt k (defs sys)
+          update idx = evalT sys z ys $ f idx
diff --git a/Data/Boltzmann/System/Errors.hs b/Data/Boltzmann/System/Errors.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Errors.hs
@@ -0,0 +1,190 @@
+{-|
+ Module      : Data.Boltzmann.System.Errors
+ Description : Various error handling utilities.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Common error utilities for combinatorial systems.
+ -}
+module Data.Boltzmann.System.Errors
+    ( SystemError
+    , ErrorMonad
+    , errors
+    ) where
+
+import Control.Monad.Except
+
+import Data.Map (Map)
+import qualified Data.Map.Strict as M
+
+import qualified Data.Set as S
+
+import Data.MultiSet (MultiSet)
+import qualified Data.MultiSet as MultiSet
+
+import Data.Char (isUpper)
+import Text.Read (readMaybe)
+
+import Data.Boltzmann.System
+import Data.Boltzmann.System.Jacobian
+
+-- | Semantic system errors referring to invalid
+--   input data, for instance ill-founded systems.
+data SystemError = Inconsistent String                -- Type name
+                                String                -- Constructor name
+                                String                -- Argument name
+
+                 | InvalidCons  String                -- Type name
+                                String                -- Constructor name
+
+                 | ClashCons    [String]              -- Clashing constructors
+                 | Illfounded                         -- Ill-founded system
+                 | Infinite                           -- Infinite structures
+
+                 | Frequencies  [String]              -- Incorrect frequencies
+
+                 | InvalidPrecision                   -- Invalid precision
+                 | InvalidMaxIter                     -- Invalid maxiter
+                 | InvalidModule                      -- Invalid module
+
+                 | UnsupportedSystemType String       -- Invalid system type
+
+instance Show SystemError where
+    show (Inconsistent t con arg) = "[Error] Invalid argument type '"
+        ++ arg ++ "' in constructor " ++ con ++ " of type " ++ t ++ "."
+
+    show (InvalidCons t con) = "[Error] Invalid constructor '" ++ con
+        ++ "' in type " ++ t ++ ": '" ++ con ++ "' names a declared type."
+
+    show (ClashCons cons) = "[Error] Clashing constructor names: "
+        ++ foldl1 (\c c' -> "'" ++ c ++ "', " ++ "'" ++ c' ++ "'") cons
+        ++ "."
+
+    show Illfounded = "[Error] Ill-founded system."
+
+    show Infinite = "[Error] System defines no finite structures."
+
+    show (Frequencies ts) = "[Error] Incorrect frequencies (expected real in [0.0,1.0]): "
+        ++ foldl1 (\c c' -> "'" ++ c ++ "', " ++ "'" ++ c' ++ "'") ts
+        ++ "."
+
+    show InvalidPrecision = "[Error] Invalid precision annotation. "
+            ++ "Expected a positive floating point number."
+
+    show InvalidMaxIter = "[Error] Invalid maxiter annotation. "
+            ++ "Expected a positive integer."
+
+    show InvalidModule = "[Error] Invalid module annotation. "
+            ++ "Expected a name starting with an upper case letter."
+
+    show (UnsupportedSystemType s) = "[Error] Unsupported system type. " ++ s
+
+-- | Monadic error handling wrapper.
+type ErrorMonad = Either SystemError
+
+-- | Checks whether the given input system is correct, yielding its type.
+--   Otherwise, returns an appropriate SystemError.
+errors :: Bool -> System Int -> ErrorMonad SystemType
+errors useForce sys = do
+    void $ consistent sys
+    void $ validCons sys
+    void $ clashCons sys
+    void $ infinite sys
+    void $ incorrectFrequencies sys
+    void $ invalidAnnotations sys
+    unless useForce $ illfounded sys
+    invalidSystemType sys
+
+invalidSystemType :: System a -> ErrorMonad SystemType
+invalidSystemType sys =
+    case systemType sys of
+      (Unsupported s) -> throwError (UnsupportedSystemType s) `catchError` Left
+      sysT            -> return sysT
+
+infinite :: System a -> ErrorMonad ()
+infinite sys = unless (hasAtoms sys || not (null $ seqTypes sys)) $ throwError Infinite `catchError` Left
+
+consistent :: System a -> ErrorMonad ()
+consistent sys = mapM_ consistentType (M.toList $ defs sys) `catchError` Left
+    where ts = types sys
+          consistentType (t,cons) = mapM_ (consistentCons t) cons
+          consistentCons t con    = mapM_ (consistentArg t con) $ args con
+
+          consistentArg :: String -> Cons a -> Arg -> ErrorMonad ()
+          consistentArg t con (List s)
+            | s `S.member` ts = return ()
+            | otherwise = throwError $ Inconsistent t (func con) s
+          consistentArg t con (Type s)
+            | s `S.member` ts = return ()
+            | otherwise = throwError $ Inconsistent t (func con) s
+
+validCons :: System a -> ErrorMonad ()
+validCons sys = mapM_ validType (M.toList $ defs sys) `catchError` Left
+    where ts = types sys
+          validType (t,cons) = mapM_ (validCon t) cons
+
+          validCon :: String -> Cons a -> ErrorMonad ()
+          validCon t con
+            | null (args con) && func con `S.member` ts =
+                throwError $ InvalidCons t (func con)
+            | otherwise = return ()
+
+consNames :: System a -> MultiSet String
+consNames sys = MultiSet.unions (map insT $ M.elems (defs sys))
+    where insT = MultiSet.fromList . map func
+
+duplicates :: System a -> [String]
+duplicates sys = map fst $ filter gather $ MultiSet.toOccurList ms
+    where gather (_,n) = n /= 1
+          ms           = consNames sys
+
+clashCons :: System a -> ErrorMonad ()
+clashCons sys = let cs = duplicates sys in
+                    unless (null cs) $ throwError (ClashCons cs) `catchError` Left
+
+illfounded :: System Int -> ErrorMonad ()
+illfounded sys = unless (wellFounded sys) $ throwError Illfounded `catchError` Left
+
+incorrectFrequencies :: System Int -> ErrorMonad ()
+incorrectFrequencies sys = unless (null fs) $ throwError (Frequencies fs) `catchError` Left
+    where fs = incorrectFrequencies' sys
+
+incorrectFrequencies' :: System Int -> [String]
+incorrectFrequencies' sys = concatMap incF $ M.elems (defs sys)
+    where incF cons  = map func $ filter incF' cons
+          incF' cons = case frequency cons of
+                         Nothing -> False
+                         Just f  -> 0.0 > f || 1.0 < f
+
+-- | General, compiler-independent admissible annotations.
+invalidAnnotations :: System Int -> ErrorMonad ()
+invalidAnnotations sys = do
+    let ann = annotations sys
+    void $ precisionAnnotation ann
+    void $ maxiterAnnotation ann
+    moduleAnnotation ann
+
+precisionAnnotation :: Map String String -> ErrorMonad ()
+precisionAnnotation ann =
+    case "precision" `M.lookup` ann of
+      Nothing -> return ()
+      Just x -> case readMaybe x :: Maybe Double of
+                  Nothing -> throwError InvalidPrecision
+                  Just x' -> unless (x' > 0) $ throwError InvalidPrecision `catchError` Left
+
+maxiterAnnotation :: Map String String -> ErrorMonad ()
+maxiterAnnotation ann =
+    case "maxiter" `M.lookup` ann of
+      Nothing -> return ()
+      Just x -> case readMaybe x :: Maybe Int of
+                  Nothing -> throwError InvalidMaxIter
+                  Just x' -> unless (x' > 0) $ throwError InvalidMaxIter `catchError` Left
+
+moduleAnnotation :: Map String String -> ErrorMonad ()
+moduleAnnotation ann =
+    case "module" `M.lookup` ann of
+      Nothing -> return ()
+      Just x -> unless (isUpper $ head x) $ throwError InvalidModule `catchError` Left
diff --git a/Data/Boltzmann/System/Jacobian.hs b/Data/Boltzmann/System/Jacobian.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Jacobian.hs
@@ -0,0 +1,133 @@
+{-|
+ Module      : Data.Boltzmann.System.Jacobian
+ Description : Jacobian matrix utilities.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General utilities related to the Jacobian associated
+ with the combinatorial system. In particular, for
+ well-foundness checks.
+ -}
+module Data.Boltzmann.System.Jacobian
+    ( jacobian
+    , wellFounded
+    ) where
+
+import Data.Boltzmann.System
+
+import Data.MultiSet (MultiSet)
+import qualified Data.MultiSet as S
+
+import qualified Data.Map as M
+
+import Numeric.LinearAlgebra hiding (size)
+
+-- | Symbolic derivable expressions.
+data Derivable = Product Derivable Derivable
+               | Union Derivable Derivable
+               | Func String Int
+               | Seq String Int
+               | Z Int
+
+norm :: [Arg] -> (MultiSet String, MultiSet String)
+norm args' = norm' args' S.empty S.empty
+
+norm' :: [Arg] -> MultiSet String -> MultiSet String
+      -> (MultiSet String, MultiSet String)
+
+norm' (Type t : xs) ts ls = norm' xs (t `S.insert` ts) ls
+norm' (List t : xs) ts ls = norm' xs ts (t `S.insert` ls)
+norm' [] ts ls            = (ts, ls)
+
+toDerivableL :: [Cons Int] -> Derivable
+toDerivableL cons = foldl1 Union $ map toDerivable cons
+
+toDerivable :: Cons Int -> Derivable
+toDerivable con = Product z derivs'
+    where z       = Z $ weight con
+          derivs  = toDerivable' (norm $ args con)
+          derivs' = foldl Product (Z 0) derivs
+
+toDerivable' :: (MultiSet String, MultiSet String) -> [Derivable]
+toDerivable' (ts, ls) = ts' ++ ls'
+    where ts' = map (uncurry Func) $ S.toOccurList ts
+          ls' = map (uncurry Seq) $ S.toOccurList ls
+
+-- | Evaluates the derivable in the given coordinates.
+evalD :: System Int -> String -> Double
+      -> Vector Double -> Derivable -> Double
+
+evalD _ _ z _ (Z n) = z ^^ n
+evalD sys _ _ ys (Func t n) = evalA sys ys (Type t) ^^ n
+evalD sys _ _ ys (Seq t n) = evalA sys ys (List t) ^^ n
+
+evalD sys f z ys (Product x y) = xd * yd
+    where xd = evalD sys f z ys x
+          yd = evalD sys f z ys y
+
+evalD sys f z ys (Union x y) = xd + yd
+    where xd = evalD sys f z ys x
+          yd = evalD sys f z ys y
+
+-- | Computes the derivative of the given derivable
+--   expression at the numerical coordinates.
+deriv :: System Int -> String -> Double
+      -> Vector Double -> Derivable -> Double
+
+deriv _ _ _ _ (Z _) = 0
+deriv sys f _ ys (Func t n)
+  | f /= t = 0
+  | otherwise = let x = evalA sys ys (Type t) in
+                    fromIntegral n * x ^^ (n-1)
+
+deriv sys f _ ys (Seq t n)
+  | f /= t = 0
+  | otherwise = let x = evalA sys ys (List t) in
+                    fromIntegral n * x ^^ (n+1)
+
+deriv sys f z ys (Product x y) = x' * yd + xd * y'
+    where x' = deriv sys f z ys x
+          y' = deriv sys f z ys y
+          xd = evalD sys f z ys x
+          yd = evalD sys f z ys y
+
+deriv sys f z ys (Union x y) = x' + y'
+    where x' = deriv sys f z ys x
+          y' = deriv sys f z ys y
+
+jacobian' :: System Int -> Double -> Vector Double -> Int -> Int -> Double
+jacobian' sys z ys i j = deriv sys f z ys (toDerivableL cons)
+    where f    = fst (M.elemAt j sys')
+          cons = snd (M.elemAt i sys')
+          sys' = defs sys
+
+-- | Computes the Jacobian matrix of the
+--   system at given numberical coordinates.
+jacobian :: System Int -> Double -> Vector Double -> Matrix Double
+jacobian sys z ys = let n = size sys in
+                        (n><n) [jacobian' sys z ys i j | i <- [0..n-1],
+                                                         j <- [0..n-1]]
+
+square :: Matrix Double -> Matrix Double
+square m = m <> m
+
+-- | Fast matrix exponentiation.
+power :: Matrix Double -> Int -> Matrix Double
+power m 1 = m
+power m n
+  | odd n = m * square (power m $ n-1)
+  | otherwise = square (power m $ n `div` 2)
+
+-- | Determines whether the given system well-founded.
+--   Note: The system is assumed to define no empty structures,
+--   i.e. structures of size zero.
+wellFounded :: System Int -> Bool
+wellFounded sys = empty $ m `power` n
+    where m = jacobian sys 0.0 $ n |> [0.0..]
+          n = size sys
+
+empty :: Matrix Double -> Bool
+empty m = all (==0.0) $ concat (toLists m)
diff --git a/Data/Boltzmann/System/Oracle.hs b/Data/Boltzmann/System/Oracle.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Oracle.hs
@@ -0,0 +1,85 @@
+{-|
+ Module      : Data.Boltzmann.System.Oracle
+ Description : Numeric Newton oracle utilities.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Numerical Newton oracles meant for combinatorial systems
+ without additional tuning parameters. Note that, alternatively,
+ convex optimisation methods are also available.
+ -}
+module Data.Boltzmann.System.Oracle
+    ( singularity
+    , parametrise
+    ) where
+
+import qualified Data.Map as M
+
+import Numeric.LinearAlgebra hiding (size)
+
+import Data.Boltzmann.System
+import Data.Boltzmann.System.Jacobian
+
+computeProb :: System Int -> Double -> Vector Double -> System Double
+computeProb sys z ys = sys { defs = M.mapWithKey computeProb' (defs sys) }
+    where computeProb' t = computeExp (value t sys ys) 0.0
+          computeExp _ _ [] = []
+          computeExp tw w (e:es) = e { weight = x / tw } : computeExp tw x es
+              where w' = evalC sys z ys e
+                    x  = w' + w
+
+-- | Compute the numerical Boltzmann probabilities for the given system.
+parametrise :: System Int -> Double -> Double -> PSystem Double
+parametrise sys rho eps = parametrise' initState state sys rho eps
+    where initState = size sys |> [0..]
+          state     = newton sys initState rho
+
+parametrise' :: Vector Double -> Vector Double
+             -> System Int -> Double -> Double -> PSystem Double
+
+parametrise' state' state sys rho eps
+  | not $ halt eps state' state =
+      let newState = newton sys state rho
+       in parametrise' state newState sys rho eps
+  | otherwise = PSystem { system  = computeProb sys rho state
+                        , values  = state
+                        , param   = rho
+                        , weights = sys
+                        }
+
+-- | Newton iteration for combinatorial systems.
+newton :: System Int -> Vector Double -> Double -> Vector Double
+newton sys state rho = state + (inv (ide - m) #> (h - state))
+    where h         = eval sys state rho
+          m         = jacobian sys rho state
+          ide       = ident $ size sys
+
+-- | Finds a numerical approximation of the system's dominating singularity.
+singularity :: System Int -> Double -> Double
+singularity sys eps = singularity' 0 1.0
+    where singularity' lb ub
+            | abs (ub - lb) < eps = lb
+            | otherwise = if divergent sys eps z then singularity' lb z
+                                                 else singularity' z ub
+            where z = (ub + lb) / 2
+
+divergent :: System Int -> Double -> Double -> Bool
+divergent sys eps z = divergent' 0 state initState
+    where initState = size sys |> [0..]
+          state     = newton sys initState z
+
+          divergent' :: Int -> Vector Double -> Vector Double -> Bool
+          divergent' iter v v'
+            | negative v || iter >= 25 = True
+            | halt eps v v' = False
+            | otherwise = divergent' (iter+1) (newton sys v z) v
+
+negative :: Vector Double -> Bool
+negative v = any (< 0) $ toList v
+
+-- | Decide whether the system diverges or not.
+halt :: Double -> Vector Double -> Vector Double -> Bool
+halt eps v w = all (< eps) $ toList $ cmap abs (v - w)
diff --git a/Data/Boltzmann/System/Parser.hs b/Data/Boltzmann/System/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Parser.hs
@@ -0,0 +1,130 @@
+{-|
+ Module      : Data.Boltzmann.System.Parser
+ Description : Parser utilities for combinatorial systems.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Parser utilities meant to deal with input system specifications.
+ -}
+module Data.Boltzmann.System.Parser
+    ( parseSystem
+    ) where
+
+import Control.Monad (void)
+
+import Text.Megaparsec
+import Text.Megaparsec.String
+
+import qualified Data.Map.Strict as M
+
+import Data.Boltzmann.Internal.Parser
+import qualified Data.Boltzmann.System as S
+
+-- | Identifier producer.
+identifierP :: Parser Char
+            -> (Parser Char -> Parser String)
+            -> Parser String
+
+identifierP p f = lexeme $ (:) <$> p <*> f (alphaNumChar <|> char '_')
+
+identifier :: Parser String
+identifier = identifierP upperChar many
+
+toFreq :: Double -> Maybe Double
+toFreq x
+    | x < 0     = Nothing
+    | otherwise = Just x
+
+systemStmt :: Parser (S.System Int)
+systemStmt = sc *> systemStmt' <* eof
+    where systemStmt' = do
+            an <- many annotationStmt
+            ds <- some defsStmt
+            return S.System { S.defs        = M.fromList ds
+                            , S.annotations = M.fromList an
+                            }
+
+defsStmt :: Parser (String, [S.Cons Int])
+defsStmt = do
+    t <- identifier
+    void (symbol "=")
+    exprs <- try (abbrevdef t) <|> exprListStmt
+    return (t, exprs)
+
+abbrevdef :: String -> Parser [S.Cons Int]
+abbrevdef f = try (listdef f) <|> tupledef f
+
+listdef :: String -> Parser [S.Cons Int]
+listdef f = do
+    xs <- listStmt
+    m  <- option (-1.0) (brackets double)
+    void (symbol ".")
+    return [S.Cons { S.func      = f
+                   , S.args      = [xs]
+                   , S.weight    = 0
+                   , S.frequency = toFreq m
+                   }]
+
+tupledef :: String -> Parser [S.Cons Int]
+tupledef f = do
+    ids <- parens $ identifier `sepBy2` symbol ","
+    m  <- option (-1.0) (brackets double)
+    void (symbol ".")
+    return [S.Cons { S.func      = f
+                   , S.args      = map S.Type ids
+                   , S.weight    = 0
+                   , S.frequency = toFreq m
+                   }]
+
+exprListStmt :: Parser [S.Cons Int]
+exprListStmt = do
+    stms <- exprStmt `sepBy1` symbol "|"
+    void (symbol ".")
+    return stms
+
+exprStmt :: Parser (S.Cons Int)
+exprStmt = do
+    f  <- identifier
+    as <- many argStmt
+    w  <- option 1 (parens integer)
+    m  <- option (-1.0) (brackets double)
+    return S.Cons { S.func      = f
+                  , S.args      = as
+                  , S.weight    = w
+                  , S.frequency = toFreq m
+                  }
+
+argStmt :: Parser S.Arg
+argStmt = try listStmt <|> typeStmt
+
+listStmt :: Parser S.Arg
+listStmt = do
+    t <- brackets identifier
+    return $ S.List t
+
+typeStmt :: Parser S.Arg
+typeStmt = do
+    t <- identifier
+    return $ S.Type t
+
+annotationIdentifier :: Parser String
+annotationIdentifier = identifierP (char '@') some
+
+annotationValue :: Parser String
+annotationValue = lexeme $ some (alphaNumChar <|> punctuationChar)
+
+-- | System annotations.
+annotationStmt :: Parser (String, String)
+annotationStmt = do
+    lhs <- annotationIdentifier
+    rhs <- annotationValue
+    return (tail lhs, rhs)
+
+-- | Parses the given system specification.
+parseSystem :: String
+            -> IO (Either (ParseError Char Dec) (S.System Int))
+
+parseSystem = parseFromFile systemStmt
diff --git a/Data/Boltzmann/System/Tuner.hs b/Data/Boltzmann/System/Tuner.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Tuner.hs
@@ -0,0 +1,344 @@
+{-|
+ Module      : Data.Boltzmann.System.Tuner
+ Description : Interface utilities with the Paganini tuner.
+ Copyright   : (c) Maciej Bendkowski, 2017
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ General utilities managing the IO interface between Boltzmann Brain
+ and the Paganini tuner script.
+ -}
+module Data.Boltzmann.System.Tuner
+    ( PSolver(..)
+    , PArg(..)
+    , defaultArgs
+    , writeSpecification
+    , readPaganini
+    , runPaganini
+    ) where
+
+import Control.Monad
+import Control.Exception
+
+import System.IO
+import System.Exit
+import System.Process hiding (system)
+
+import Text.Megaparsec
+import Text.Megaparsec.String
+
+import Numeric.LinearAlgebra hiding (size,double)
+
+import Data.Map (Map)
+import qualified Data.Map.Strict as M
+
+import Data.MultiSet (MultiSet)
+import qualified Data.MultiSet as B
+import qualified Data.Set as S
+
+import Data.Maybe
+
+import Data.Boltzmann.System
+import Data.Boltzmann.Internal.Parser
+
+-- | Catch IO exceptions.
+try' :: IO a ->  IO (Either IOException a)
+try' =  Control.Exception.try
+
+-- | Paganini convex program solvers.
+data PSolver = SCS
+             | ECOS
+             | CVXOPT
+
+instance Show PSolver where
+    show SCS    = "SCS"
+    show ECOS   = "ECOS"
+    show CVXOPT = "CVXOPT"
+
+-- | Paganini arguments.
+data PArg = PArg { solver    :: PSolver
+                 , precision :: Double
+                 , maxiters  :: Int
+                 , sysType   :: SystemType
+                 } deriving (Show)
+
+toArgs :: PArg -> [String]
+toArgs arg = ["-s", show (solver arg)
+             ,"-p", show (precision arg)
+             ,"-m", show (maxiters arg)
+             ,"-t", show (sysType arg)]
+
+rationalArgs :: PArg
+rationalArgs = PArg { solver    = SCS
+                    , precision = 1.0e-20
+                    , maxiters  = 2500
+                    , sysType   = Rational
+                    }
+
+algebraicArgs :: PArg
+algebraicArgs = PArg { solver    = ECOS
+                     , precision = 1.0e-20
+                     , maxiters  = 20
+                     , sysType   = Algebraic
+                     }
+
+-- | Determines default Paganini arguments.
+--   Note: It is assumed that the given system is either
+--   rational or algebraic. Otherwise, and error is raised.
+defaultArgs :: System a -> PArg
+defaultArgs sys =
+    case systemType sys of
+      Rational  -> rationalArgs
+      Algebraic -> algebraicArgs
+      _         -> error "Unsupported"
+
+logger :: String -> IO ()
+logger s = hPutStrLn stderr ("[LOG] " ++ s)
+
+writeListLn :: Show a => Handle -> [a] -> IO ()
+writeListLn h xs = hPutStrLn h (showsList xs)
+
+printer :: Show a => (a -> String -> String) -> [a] -> String
+printer _ [] = ""
+printer f xs = foldl1 (\a b -> (a . (" " ++) . b))
+                        (map f xs) ""
+
+showsList :: Show a => [a]
+          -> String
+
+showsList = printer shows
+
+-- | Writes the system specification into the given
+--   file handle. In paricular, to Paganini's standard
+--   input handle.
+writeSpecification :: System Int -> Handle -> IO ()
+writeSpecification sys hout = do
+    logger "Writing specification"
+    let freqs   = frequencies sys
+    let seqs    = seqTypes sys
+    let spec    = toPSpec sys
+
+    -- # of equations and frequencies
+    writeListLn hout [numTypes spec + numSeqTypes spec
+                     ,length freqs]
+
+    -- vector of frequencies
+    writeListLn hout freqs
+
+    -- type specifications
+    let find' x = x `S.findIndex` M.keysSet (defs sys)
+    foldM_ (typeSpecification hout find' spec) 0 (M.elems $ defs sys)
+
+    -- sequence specifications
+    mapM_ (seqSpecification hout find' spec) seqs
+
+    logger "Finished writing specification"
+
+getArgs :: System Int -> Maybe PArg -> PArg
+getArgs sys = fromMaybe (defaultArgs sys)
+
+handleIOEx :: String -> IO a
+handleIOEx ex =  do
+    hPutStrLn stderr $ "[ERROR] " ++ ex
+    exitWith (ExitFailure 1)
+
+-- | Communicates with Paganini and collects the respective
+--   tuning vector for the given system. If communication is not possible,
+--   for instance due to the missing Paganini script, the current process
+--   is terminated with an error message on the standard error output.
+runPaganini :: System Int -> Maybe PArg
+            -> IO (Either (ParseError Char Dec)
+                    (PSystem Double))
+
+runPaganini sys arg = do
+
+    logger "Running paganini"
+    let arg' = getArgs sys arg
+    logger (printer (++) $ "Arguments: " : toArgs arg')
+
+    -- Execute the paganini tuning script.
+    pp <- try' $ createProcess (proc "paganini" (toArgs arg')){ std_out = CreatePipe
+                                                              , std_in  = CreatePipe }
+
+    case pp of
+        Left _ -> handleIOEx "Could not locate the paganini tuner. Is is available in the PATH?"
+        Right (Just hin, Just hout, _, _) -> do
+
+            -- write to paganini's stdout
+            writeSpecification sys hin
+
+            -- read output parameters
+            s <- hGetContents hout
+            let spec = toPSpec sys
+            let pag  = parse (paganiniStmt spec) "" s
+
+            case pag of
+              Left err -> return $ Left err
+              Right (rho, us, ts) -> do
+                  logger "Parsed paganini output"
+                  let ts'  = fromList ts
+                  let sys' = parametrise sys rho ts' us
+                  logger "Finished"
+                  return $ Right sys'
+
+        _ -> handleIOEx "Could not establish inter-process communication with paganini."
+
+-- | Parses the given input string as a Paganini tuning vector.
+readPaganini :: System Int -> String
+             -> IO (Either (ParseError Char Dec)
+                   (PSystem Double))
+
+readPaganini sys f = do
+    let spec = toPSpec sys
+    pag <- parsePaganini spec f
+    case pag of
+        Left err -> return $ Left err
+        Right (rho, us, ts) -> do
+            let ts'  = fromList ts
+            return (Right $ parametrise sys rho ts' us)
+
+frequencies :: System Int -> [Double]
+frequencies sys = concatMap (mapMaybe frequency)
+    ((M.elems . defs) sys)
+
+-- | Paganini helper specification.
+data PSpec = PSpec { numFreqs    :: Int
+                   , numTypes    :: Int
+                   , numSeqTypes :: Int
+                   }
+
+toPSpec :: System Int -> PSpec
+toPSpec sys = PSpec { numFreqs    = d
+                    , numTypes    = ts
+                    , numSeqTypes = ss
+                    }
+
+   where ts = size sys
+         d  = length $ frequencies sys
+         ss = length $ seqTypes sys
+
+typeSpecification :: Handle -> (String -> Int) -> PSpec
+                  -> Int -> [Cons Int] -> IO Int
+
+typeSpecification hout find' spec idx cons = do
+    let n = length cons
+    hPrint hout n -- # of constructors
+    foldM (consSpecification hout find' spec) idx cons
+
+consSpecification :: Handle -> (String -> Int) -> PSpec
+                  -> Int -> Cons Int -> IO Int
+
+consSpecification hout find' spec idx cons = do
+    let (vec, idx') = consVec find' spec idx cons
+    writeListLn hout vec -- constructor specification
+    return idx'
+
+indicator :: Int -> Int -> [Int]
+indicator n k = indicator' n k 1
+
+indicator' :: Int -> Int -> Int -> [Int]
+indicator' 0 _ _ = []
+indicator' n 0 x = x : replicate (n-1) 0
+indicator' n k x = 0 : indicator' (n-1) (k-1) x
+
+occurrences :: Cons a -> (MultiSet String, MultiSet String)
+occurrences cons = occurrences' (B.empty,B.empty) $ args cons
+
+occurrences' :: (MultiSet String, MultiSet String)
+             -> [Arg] -> (MultiSet String, MultiSet String)
+
+occurrences' (ts,sts) [] = (ts,sts)
+occurrences' (ts,sts) (Type s : xs) = occurrences' (s `B.insert` ts,sts) xs
+occurrences' (ts,sts) (List s : xs) = occurrences' (ts, s `B.insert` sts) xs
+
+consVec :: (String -> Int) -> PSpec -> Int -> Cons Int -> ([Int], Int)
+consVec find' spec idx cons =
+      let (tocc, socc) = occurrences cons
+          w            = fromIntegral $ weight cons
+          dv           = indicator' (numFreqs spec) idx (weight cons)
+          tv           = typeVec find' (numTypes spec) tocc
+          sv           = typeVec find' (numSeqTypes spec) socc
+
+      in case frequency cons of
+           Just _  -> (w : dv ++ tv ++ sv, idx + 1)
+           Nothing -> (w : replicate (numFreqs spec) 0 ++ tv ++ sv, idx)
+
+typeVec :: (String -> Int) -> Int -> MultiSet String -> [Int]
+typeVec find' size' m = typeVec' find' vec ls
+    where vec = M.fromList [(n,0) | n <- [0..size'-1]]
+          ls  = B.toOccurList m
+
+typeVec' :: (String -> Int) -> Map Int Int -> [(String,Int)] -> [Int]
+typeVec' _ vec [] = M.elems vec
+typeVec' find' vec ((t,n) : xs) = typeVec' find' vec' xs
+    where vec' = M.insert (find' t) n vec
+
+seqSpecification :: Handle -> (String -> Int) -> PSpec
+                 -> String -> IO ()
+
+seqSpecification hout find' spec st = do
+    let n = 1 + numTypes spec + numFreqs spec + numSeqTypes spec
+    let f = replicate (numFreqs spec) 0
+    let t = indicator (numTypes spec) (find' st)
+    let s = indicator (numSeqTypes spec) (find' st)
+    hPrint hout (2 :: Int) -- # of constructors
+    writeListLn hout $ replicate n (0 :: Int)
+    writeListLn hout $ 0 : f ++ t ++ s
+
+paganiniStmt :: PSpec -> Parser (Double, [Double], [Double])
+paganiniStmt spec = do
+    rho <- double
+    us  <- parseN double $ numFreqs spec
+    ts  <- parseN double $ numTypes spec
+    return (rho, us, ts)
+
+-- | Parses the given Paganini specification.
+parsePaganini :: PSpec -> String
+              -> IO (Either (ParseError Char Dec)
+                    (Double, [Double], [Double]))
+
+parsePaganini spec = parseFromFile (paganiniStmt spec)
+
+-- | Compute the numerical Boltzmann probabilities for the given system.
+parametrise :: System Int -> Double -> Vector Double -> [Double] -> PSystem Double
+parametrise sys rho ts us = PSystem { system  = computeProb sys rho ts us
+                                    , values  = ts
+                                    , param   = rho
+                                    , weights = sys
+                                    }
+
+evalExp :: System Int -> Double -> Vector Double
+        -> [Double] -> Cons Int -> (Double, [Double])
+
+evalExp sys rho ts us exp' =
+    let w     = weight exp'
+        xs    = args exp'
+        exp'' = (rho ^^ w) * product (map (evalA sys ts) xs)
+     in case frequency exp' of
+          Nothing -> (exp'', us)
+          Just _  -> (head us ^^ w * exp'', tail us)
+
+computeExp :: System Int -> Double -> Vector Double
+           -> [Double] -> Double -> Double -> [Cons Int]
+           -> ([Cons Double], [Double])
+
+computeExp _ _ _ us _ _ [] = ([], us)
+computeExp sys rho ts us tw w (e:es) = (e { weight = x / tw } : es', us'')
+    where (es', us'') = computeExp sys rho ts us' tw x es
+          (w', us') = evalExp sys rho ts us e
+          x = w + w'
+
+computeProb' :: System Int -> Double -> Vector Double
+             -> [Double] -> [(String, [Cons Int])]
+             -> [(String, [Cons Double])]
+
+computeProb' _ _ _ _ [] = []
+computeProb' sys rho ts us ((t,cons):tys) = (t,cons') : tys'
+    where (cons', us') = computeExp sys rho ts us (value t sys ts) 0.0 cons
+          tys' = computeProb' sys rho ts us' tys
+
+computeProb :: System Int -> Double -> Vector Double -> [Double] -> System Double
+computeProb sys rho ts us = sys { defs = M.fromList tys }
+    where tys = computeProb' sys rho ts us (M.toList $ defs sys)
diff --git a/Data/Boltzmann/System/Warnings.hs b/Data/Boltzmann/System/Warnings.hs
new file mode 100644
--- /dev/null
+++ b/Data/Boltzmann/System/Warnings.hs
@@ -0,0 +1,48 @@
+{-|
+ Module      : Data.Boltzmann.System.Warnings
+ Description : Various warning handling utilities.
+ Copyright   : (c) Maciej Bendkowski, 2018
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+
+ Warning utilities meant to deal with, skippable, well-foundness checks
+ or other redundant sanity checks of the considered combinatorial system.
+ -}
+module Data.Boltzmann.System.Warnings
+    ( SystemWarning
+    , WarningMonad
+    , warnings
+    ) where
+
+import Control.Monad.Except
+
+import qualified Data.Map.Strict as M
+
+import Data.Boltzmann.System
+
+-- | Semantic system warnings.
+data SystemWarning = NullCons String                -- Type name
+                              String                -- Constructor name
+
+instance Show SystemWarning where
+    show (NullCons t con) = "[Warning] Invalid constructor '" ++ con
+        ++ "' in type " ++ t ++ ": encountered a structure of size 0."
+
+-- | Monadic warning handling wrapper.
+type WarningMonad = Either SystemWarning
+
+-- | Checks whether the given input system admits no warnings.
+warnings :: System Int -> WarningMonad ()
+warnings = nullCons
+
+nullCons :: (Num a, Eq a) => System a -> WarningMonad ()
+nullCons sys = mapM_ nullType (M.toList $ defs sys) `catchError` Left
+    where nullType (t,cons) = mapM_ (nullCon t) cons
+
+          nullCon :: (Num a, Eq a) => String -> Cons a -> WarningMonad ()
+          nullCon t con
+            | null (args con) && weight con == 0 =
+                throwError $ NullCons t (func con)
+            | otherwise = return ()
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Maciej Bendkowski and Sergey Dovgal (c) 2017
+
+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.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+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/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,199 @@
+{-|
+ Module      : Main
+ Description : Boltzmann brain executable.
+ Copyright   : (c) Maciej Bendkowski, 2018
+
+ License     : BSD3
+ Maintainer  : maciej.bendkowski@tcs.uj.edu.pl
+ Stability   : experimental
+ -}
+module Main
+    ( main
+    ) where
+
+import System.IO
+import System.Exit
+import System.Console.GetOpt
+import System.Environment
+
+import Control.Monad (when)
+import Data.Either (isLeft)
+import Data.Maybe (fromMaybe)
+import Data.List (nub)
+
+import qualified Data.Map as M
+
+import Data.Boltzmann.System
+import Data.Boltzmann.System.Parser
+import Data.Boltzmann.System.Errors
+import Data.Boltzmann.System.Warnings
+import Data.Boltzmann.Internal.Parser
+
+import qualified Data.Boltzmann.System.Tuner as T
+
+import Data.Boltzmann.Compiler
+import qualified Data.Boltzmann.Compiler.Haskell.Algebraic as A
+import qualified Data.Boltzmann.Compiler.Haskell.Rational as R
+
+data Flag = OutputFile String
+          | InputPaganini String
+          | OutputPaganini
+          | Force
+          | Werror
+          | Version
+          | Help
+            deriving (Eq)
+
+options :: [OptDescr Flag]
+options = [Option "o" ["output"] (ReqArg OutputFile "FILE")
+            "Optional output file.",
+
+           Option "p" ["paganini-in"] (ReqArg InputPaganini "FILE")
+            "Paganini tuning vector for the given system.",
+
+           Option "s" ["paganini-out"] (NoArg OutputPaganini)
+            "Output a suitable Paganini specification for the given system.",
+
+           Option "f" ["force"] (NoArg Force)
+            "Whether to skip the well-foundedness check.",
+
+           Option "w" ["werror"] (NoArg Werror)
+            "Whether to treat warnings as errors.",
+
+           Option "v" ["version"] (NoArg Version)
+            "Prints the program version number.",
+
+           Option "h?" ["help"] (NoArg Help)
+            "Prints this help message."]
+
+usageHeader :: String
+usageHeader = "Usage: bb [OPTIONS...]"
+
+versionHeader :: String
+versionHeader = "Boltzmann Brain v1.3.1.3 (c) Maciej Bendkowski and Sergey Dovgal 2018"
+
+compilerTimestamp :: String
+compilerTimestamp = "Boltzmann Brain v1.3.1.3"
+
+getPrecision :: System a -> Double
+getPrecision sys =
+    case "precision" `M.lookup` annotations sys of
+      Just x  -> read x :: Double
+      Nothing -> 1.0e-9
+
+getMaxIter :: System a -> Maybe Int
+getMaxIter sys =
+    case "maxiter" `M.lookup` annotations sys of
+      Just x  -> return (read x :: Int)
+      Nothing -> Nothing
+
+getModuleName :: System a -> String
+getModuleName sys = fromMaybe "Sampler" ("module" `M.lookup` annotations sys)
+
+output :: [Flag] -> Maybe String
+output (OutputFile f : _) = Just f
+output (_:fs)             = output fs
+output []                 = Nothing
+
+toPaganini :: [Flag] -> Bool
+toPaganini flags = OutputPaganini `elem` flags
+
+useForce :: [Flag] -> Bool
+useForce flags = Force `elem` flags
+
+fromPaganini :: [Flag] -> Maybe String
+fromPaganini (InputPaganini s : _) = Just s
+fromPaganini (_:fs)                = fromPaganini fs
+fromPaganini []                    = Nothing
+
+parse :: [String] -> IO ([Flag], [String])
+parse argv = case getOpt Permute options argv of
+               (ops, nonops, [])
+                    | Help `elem` ops -> do
+                        putStr $ usageInfo usageHeader options
+                        exitSuccess
+                    | Version `elem` ops -> do
+                        putStrLn versionHeader
+                        exitSuccess
+                    | otherwise -> return (nub (concatMap mkset ops), fs)
+                        where
+                            fs = if null nonops then [] else nonops
+                            mkset x = [x]
+               (_, _, errs) -> do
+                    hPutStr stderr (concat errs ++ usageInfo usageHeader options)
+                    exitWith (ExitFailure 1)
+
+run :: [Flag]
+    -> String
+    -> IO ()
+
+run flags f = do
+    sys <- parseSystem f
+    case sys of
+      Left err   -> printError err
+      Right sys' -> do
+          let ws = warnings sys'
+          reportSystemWarnings ws
+          case errors (useForce flags) sys' of
+              Left err'  -> reportSystemError err'
+              Right sysT -> do
+                  when (exitWerror flags ws) $ exitWith (ExitFailure 1)
+                  if toPaganini flags then writeSpec sys' (output flags)
+                                      else runCompiler sys' sysT flags
+
+exitWerror :: [Flag] -> WarningMonad () -> Bool
+exitWerror flags ws = isLeft ws && Werror `elem` flags
+
+writeSpec :: System Int -> Maybe FilePath -> IO ()
+writeSpec sys' (Just f) = withFile f WriteMode (T.writeSpecification sys')
+writeSpec sys' Nothing  = T.writeSpecification sys' stdout
+
+confCompiler :: PSystem Double -> Maybe String -> SystemType -> IO ()
+confCompiler sys outputFile Rational = do
+    let conf = config sys outputFile
+            (getModuleName $ system sys)
+            compilerTimestamp :: R.Conf
+    R.compile conf
+
+confCompiler sys outputFile Algebraic = do
+    let conf = config sys outputFile
+            (getModuleName $ system sys)
+            compilerTimestamp :: A.Conf
+    A.compile conf
+
+confCompiler _ _ _ = error "I wasn't expecting the Spanish inquisition!"
+
+runCompiler :: System Int -> SystemType -> [Flag] -> IO ()
+runCompiler sys sysT flags =
+    case fromPaganini flags of
+      Nothing -> do
+          let arg = T.defaultArgs sys
+          pag <- T.runPaganini sys (Just $ arg { T.precision = getPrecision sys
+                                               , T.maxiters  = fromMaybe (T.maxiters arg)
+                                                                         (getMaxIter sys) })
+          case pag of
+            Left err   -> printError err
+            Right sys' -> confCompiler sys' (output flags) sysT
+      Just s  -> do
+          pag <- T.readPaganini sys s
+          case pag of
+            Left err   -> printError err
+            Right sys' -> confCompiler sys' (output flags) sysT
+
+reportSystemError :: SystemError -> IO ()
+reportSystemError err = do
+    hPrint stderr err
+    exitWith (ExitFailure 1)
+
+reportSystemWarnings :: WarningMonad () -> IO ()
+reportSystemWarnings (Left ws) = hPrint stderr ws
+reportSystemWarnings _         = return ()
+
+main :: IO ()
+main = do
+    (ops, fs) <- getArgs >>= parse
+    case fs of
+      []     -> do hPutStr stderr (usageInfo usageHeader options)
+                   exitWith (ExitFailure 1)
+      (f:_)  -> do run ops f
+                   exitSuccess
diff --git a/boltzmann-brain.cabal b/boltzmann-brain.cabal
new file mode 100644
--- /dev/null
+++ b/boltzmann-brain.cabal
@@ -0,0 +1,59 @@
+name:                boltzmann-brain
+version:             1.3.1.3
+synopsis:            Boltzmann sampler compiler for combinatorial systems.
+description:         Boltzmann Brain is a combinatorial system sampler compiler.
+                     Using an easy and intuitive specification input representing a
+                     combinatorial system, Boltzmann Brain constructs a working,
+                     self-contained module implementing a dedicated singular,
+                     rejection-based Boltzmann sampler with some additional
+                     control over the constructor frequencies in the generated structures.
+homepage:            https://github.com/maciej-bendkowski/boltzmann-brain
+license:             BSD3
+license-file:        LICENSE
+author:              Maciej Bendkowski
+maintainer:          maciej.bendkowski@tcs.uj.edu.pl
+copyright:           2018 Maciej Bendkowski
+category:            Math
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Data.Boltzmann.System
+                     , Data.Boltzmann.System.Oracle
+                     , Data.Boltzmann.System.Parser
+                     , Data.Boltzmann.System.Jacobian
+                     , Data.Boltzmann.System.Errors
+                     , Data.Boltzmann.System.Warnings
+                     , Data.Boltzmann.System.Tuner
+                     , Data.Boltzmann.Internal.Annotations
+                     , Data.Boltzmann.Internal.Parser
+                     , Data.Boltzmann.Compiler
+                     , Data.Boltzmann.Compiler.Haskell.Helpers
+                     , Data.Boltzmann.Compiler.Haskell.Algebraic
+                     , Data.Boltzmann.Compiler.Haskell.Rational
+  build-depends:       base >= 4.7 && < 5
+                     , containers >= 0.5.6
+                     , megaparsec >= 5.2.0
+                     , array >= 0.5.1
+                     , haskell-src-exts == 1.17.1
+                     , mtl >= 2.2.1
+                     , multiset >= 0.3.3
+                     , hmatrix >= 0.18.0.0
+                     , process >= 1.4.3.0
+  ghc-options:         -O2 -Wall
+  default-language:    Haskell2010
+
+executable bb
+  hs-source-dirs:      app
+  main-is:             Main.hs
+  ghc-options:         -O2 -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:       base
+                     , hmatrix >= 0.18.0.0
+                     , containers >= 0.5.6
+                     , boltzmann-brain
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/maciej-bendkowski/boltzmann-brain
