diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-CABALOPTS :=
+CABALOPTS := --disable-documentation
 
 .PHONY:	all
 
diff --git a/puzzle.tex b/puzzle.tex
--- a/puzzle.tex
+++ b/puzzle.tex
@@ -6,10 +6,11 @@
 \usepackage{pict2e}
 \usepackage{color}
 
-\newenvironment{puzzle}{\noindent\mbox{}\vfill}{\newpage}
+\newenvironment{puzzle}{\noindent\mbox{}\vfill}{\vfill\newpage}
 
 \newcommand\puzzles{
 \fontsize{36}{42}\selectfont
+\linethickness{0.1em}
 \setlength{\unitlength}{2.1em}
 \input{mixed}
 %
diff --git a/src/Common.hs b/src/Common.hs
--- a/src/Common.hs
+++ b/src/Common.hs
@@ -1,21 +1,45 @@
+{-# LANGUAGE TypeFamilies #-}
 module Common where
 
 import qualified Data.Array.Comfort.Boxed as BoxedArray
 import qualified Data.Array.Comfort.Shape as Shape
+import qualified Data.Set as Set
 import Data.Array.Comfort.Boxed (Array)
+import Data.Map (Map)
+import Data.Set (Set)
 
+import qualified Combinatorics
 
 
+
 data Op = Add | Mul
    deriving (Eq, Ord, Enum, Show)
 
-sizeFromOps :: Array (Shape.LowerTriangular ShapeInt) op -> Int
-sizeFromOps = succ . Shape.size . Shape.triangularSize . BoxedArray.shape
+newtype ShapeOp = ShapeOp {shapeOpSize :: Int}
 
+instance Shape.C ShapeOp where
+   size (ShapeOp n) = max 0 $ n-1
+
+instance Shape.Indexed ShapeOp where
+   type Index ShapeOp = Int
+   indices sh = take (Shape.size sh) [0..]
+   unifiedOffset sh = Shape.unifiedOffset (Shape.ZeroBased (Shape.size sh))
+
+
+sizeFromOps :: Array (Shape.LowerTriangular ShapeOp) op -> Int
+sizeFromOps = shapeOpSize . Shape.triangularSize . BoxedArray.shape
+
+
 type ShapeInt = Shape.ZeroBased Int
 
 
 type SolutionCheck a =
-      BoxedArray.Array (Shape.LowerTriangular ShapeInt) Op ->
-      [((Int,Int),a)] ->
+      BoxedArray.Array (Shape.LowerTriangular ShapeOp) Op ->
+      Map (Int,Int) a ->
       Bool
+
+
+allCellSelections :: Int -> [Set (Int, Int)]
+allCellSelections n =
+   map Set.fromList $ Combinatorics.tuples n $ Shape.indices $
+   Shape.lowerTriangular $ Shape.ZeroBased n
diff --git a/src/LinearAlgebra.hs b/src/LinearAlgebra.hs
--- a/src/LinearAlgebra.hs
+++ b/src/LinearAlgebra.hs
@@ -1,5 +1,7 @@
 module LinearAlgebra where
 
+import Common (allCellSelections)
+
 import qualified Numeric.LAPACK.Matrix.Triangular as Triangular
 import qualified Numeric.LAPACK.Matrix.Layout as Layout
 import qualified Numeric.LAPACK.Matrix as Matrix
@@ -8,14 +10,14 @@
 import Numeric.LAPACK.Matrix (ShapeInt, (#*|))
 import Numeric.LAPACK.Format ((##))
 
-import qualified Combinatorics
-
 import qualified Data.Array.Comfort.Boxed as BoxedArray
 import qualified Data.Array.Comfort.Storable as Array
+import qualified Data.Array.Comfort.Bool as ComfortSet
 import qualified Data.Array.Comfort.Shape as Shape
-import qualified Data.IntSet as IntSet
-import qualified Data.Set as Set
+import qualified Data.Map as Map
 import Data.Array.Comfort.Storable (Array)
+import Data.Map (Map)
+import Data.Set (Set)
 
 import Data.Foldable (for_)
 import Data.Tuple.HT (mapSnd)
@@ -24,6 +26,7 @@
 
 {- $setup
 >>> import qualified Data.Array.Comfort.Storable as Array
+>>> import qualified Data.Map as Map
 >>> import Data.Tuple.HT (mapSnd)
 >>>
 >>> myRound :: Double -> Integer
@@ -71,45 +74,43 @@
    Matrix.fromRows (Shape.lowerTriangular shape) $
    map (pyramid . Vector.unit shape) $ take n [0..]
 
-addIndices :: [[Maybe a]] -> [((Int,Int), a)]
-addIndices puzzle = do
+addIndices :: [[Maybe a]] -> Map (Int,Int) a
+addIndices puzzle = Map.fromList $ do
    (i, xs) <- zip [0..] puzzle
    (j, Just x) <- zip [0..] xs
    return ((i,j),x)
 
 {- |
->>> mapSnd (map myRound . Array.toList) $ solve 3 [((0,0),8), ((2,0),1), ((2,2),3)]
+>>> mapSnd (map myRound . Array.toList) $ solve 3 $ Map.fromList [((0,0),8), ((2,0),1), ((2,2),3)]
 (3,[8,3,5,1,2,3])
 -}
 solve ::
-   Int -> [((Int,Int),Double)] ->
+   Int -> Map (Int,Int) Double ->
    (Int, Array (Shape.LowerTriangular ShapeInt) Double)
 solve n indexed =
    let fullBasis = Matrix.transpose $ basis (Matrix.shapeInt n)
-       selected =
-         Matrix.takeRowArray
-            (BoxedArray.vectorFromList (map fst indexed))
-            fullBasis
+       selected = Matrix.takeRowSet (Map.keysSet indexed) fullBasis
    in mapSnd ((fullBasis #*|) . Matrix.flattenColumn)
          (Singular.leastSquaresMinimumNormRCond 1e-5 selected $
-          Matrix.singleColumn Layout.ColumnMajor $
-          Vector.autoFromList (map snd indexed))
+          Matrix.singleColumn Layout.ColumnMajor $ Array.fromMap indexed)
 
-solvable :: Int -> [(Int,Int)] -> Bool
+solvable :: Int -> Set (Int,Int) -> Bool
 solvable n =
    let shape = Matrix.shapeInt n
        fullBasis = basis shape
    in \ixs ->
          (n==) $ length $ takeWhile (1e-5<) $ Vector.toList $
-         (#*| Vector.one shape) $ Singular.values $
-         Matrix.takeColumnArray (BoxedArray.vectorFromList ixs) fullBasis
+         (#*| Vector.one ixs) $ Singular.values $
+         Matrix.takeColumnSet ixs fullBasis
 
-solvables :: Int -> [[(Int,Int)]]
+{- |
+>>> map (length . solvables) [0..5]
+[1,1,3,17,149,1824]
+-}
+solvables :: Int -> [Set (Int,Int)]
 solvables n =
    let check = solvable n
-   in filter check $
-      Combinatorics.tuples n $ Shape.indices $
-      Shape.lowerTriangular $ Matrix.shapeInt n
+   in filter check $ allCellSelections n
 
 {-
 Check, whether a sum pyramid contains a sub-pyramid of size k
@@ -117,27 +118,12 @@
 If yes, then the pyramid has redundancies in a sub-pyramid
 and is not solvable.
 -}
-wellcrowded :: Int -> [(Int,Int)] -> Bool
+wellcrowded :: Int -> Set (Int,Int) -> Bool
 wellcrowded n ixs =
    let triShape = Shape.lowerTriangular $ Matrix.shapeInt n
-       set = Set.fromList ixs
-       countSubTriangle (i,j) k =
-         length $
-         filter (\(si,sj) -> Set.member (i+si,j+sj) set) $
-         Shape.indices $ Shape.lowerTriangular $ Matrix.shapeInt k
-   in and $ do
-         (i,j) <- Shape.indices triShape
-         k <- [0..n-i]
-         return $ countSubTriangle (i,j) k <= k
-
-wellcrowdedIntSet :: Int -> [(Int,Int)] -> Bool
-wellcrowdedIntSet n ixs =
-   let triShape = Shape.lowerTriangular $ Matrix.shapeInt n
-       set = IntSet.fromList $ map (Shape.offset triShape) ixs
+       set = ComfortSet.fromSet triShape ixs
        countSubTriangle (i,j) k =
-         length $
-         filter (\(si,sj) ->
-            flip IntSet.member set $ Shape.offset triShape (i+si,j+sj)) $
+         length $ filter (\(si,sj) -> ComfortSet.member (i+si,j+sj) set) $
          Shape.indices $ Shape.lowerTriangular $ Matrix.shapeInt k
    in and $ do
          (i,j) <- Shape.indices triShape
@@ -155,20 +141,18 @@
  . * .
 * . . *
 -}
-counterexamples :: Int -> [[(Int, Int)]]
+counterexamples :: Int -> [Set (Int, Int)]
 counterexamples n =
    let check = solvable n
-   in filter (\ixs -> check ixs /= wellcrowded n ixs) $
-      Combinatorics.tuples n $ Shape.indices $
-      Shape.lowerTriangular $ Matrix.shapeInt n
+   in filter (\ixs -> check ixs /= wellcrowded n ixs) $ allCellSelections n
 
 
-boolMatrix :: Int -> [(Int, Int)] -> Matrix.Lower ShapeInt Float
+boolMatrix :: Int -> Set (Int, Int) -> Matrix.Lower ShapeInt Float
 boolMatrix n ixs =
    Triangular.fromLowerRowMajor $
    Array.fromAssociations 0
       (Shape.lowerTriangular $ Matrix.shapeInt n)
-      (map (\ix -> (ix,1::Float)) ixs)
+      (Map.toList $ Map.fromSet (const (1::Float)) ixs)
 
 
 test :: IO ()
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -4,11 +4,9 @@
 import qualified UniqueLogic as Logic
 import Common
 
-import qualified Combinatorics
-
 import qualified Control.Monad.Trans.Class as MT
 import qualified Control.Monad.Trans.State as MS
-import Control.Monad (replicateM, join)
+import Control.Monad (replicateM, join, guard)
 import Control.Applicative (pure, (<*>), (<|>))
 
 import qualified System.Random as Random
@@ -18,6 +16,7 @@
 import qualified Data.Array.Comfort.Boxed as BoxedArray
 import qualified Data.Array.Comfort.Shape as Shape
 import qualified Data.List.HT as ListHT
+import qualified Data.Map as Map
 import qualified Data.Set as Set
 import Data.Array.Comfort.Boxed (Array, (!))
 import Data.Foldable (for_)
@@ -47,7 +46,7 @@
    Allowed ->
    Array ShapeInt Integer ->
    MS.State g
-      (Array (Shape.LowerTriangular ShapeInt) Op,
+      (Array (Shape.LowerTriangular ShapeOp) Op,
        Array (Shape.LowerTriangular ShapeInt) Integer)
 pyramid allowed base = do
    let nextRow xs =
@@ -71,10 +70,9 @@
                else go ys0
    let xs0 = BoxedArray.toList base
    let shape@(Shape.ZeroBased n) = BoxedArray.shape base
-   let shape1 = Shape.ZeroBased (n-1)
    (ops,xs) <- go xs0
    return
-      (BoxedArray.fromList (Shape.lowerTriangular shape1) $
+      (BoxedArray.fromList (Shape.lowerTriangular (ShapeOp n)) $
          concat $ reverse ops,
        BoxedArray.fromList (Shape.lowerTriangular shape) $
          concat $ reverse (xs0:xs))
@@ -84,7 +82,7 @@
    SolutionCheck Integer ->
    Allowed -> Int -> Integer ->
    MS.State g
-      (Array (Shape.LowerTriangular ShapeInt) Op,
+      (Array (Shape.LowerTriangular ShapeOp) Op,
        Array (Shape.LowerTriangular ShapeInt) (Integer,Bool))
 construct check allowed n maxV = do
    let shape = Shape.ZeroBased n
@@ -95,7 +93,7 @@
          selected <-
             MS.evalStateT (replicateM n pick) $
             Set.fromList $ Shape.indices triShape
-         let puzzle = map (\ij -> (ij, pyr!ij)) selected
+         let puzzle = Map.fromSet (pyr!) $ Set.fromList selected
          if check ops puzzle
             then return selected
             else go
@@ -107,35 +105,39 @@
 
 latexFromPuzzle ::
    String ->
-   Either Int (Array (Shape.LowerTriangular ShapeInt) Op) ->
+   Either Int (Array (Shape.LowerTriangular ShapeOp) Op) ->
    Array (Shape.LowerTriangular ShapeInt) (Integer,Bool) ->
    [String]
-latexFromPuzzle hidden mops xs =
+latexFromPuzzle hiddenMacro mops xs =
    let n = either id sizeFromOps mops in
    printf "\\begin{picture}(%d,%d)" (2*n) n :
-   map (\(i,j) -> printf "\\put(%d,%d){\\framebox(2,1){}}" (n-1-i + 2*j) (n-i))
-      (Shape.indices $ Shape.lowerTriangular $ Shape.ZeroBased n) ++
-   (BoxedArray.toAssociations xs >>= \((i,j),(x,display)) ->
-      if null hidden && not display
-         then []
-         else
-            let cell :: String
-                cell =
-                   if display
-                      then printf "%d" x
-                      else printf "\\%s{%d}" hidden x
-            in [printf "\\put(%d,%d){\\makebox(2,1)[c]{%s}}"
-                  (n-1-i + 2*j) (n-i) cell]) ++
+   (take n [0..] >>= \i ->
+      printf "\\put(%d,%d){\\line(0,-1){1}\\line(1,0){%d}\\line(0,-1){1}}"
+         (n-1-i) (n-i) (2*i+2) :
+      flip map (take (n-i-1) $ iterate (2+) (2+i)) (\j ->
+         printf "\\put(%d,%d){\\line(0,1){1}}" j i)) ++
+   printf "\\put(0,0){\\line(1,0){%d}}" (2*n) :
+   (do
+      ((i,j),(x,display)) <- BoxedArray.toAssociations xs
+      guard $ display || not (null hiddenMacro)
+      let cell :: String
+          cell =
+             if display
+                then printf "%d" x
+                else printf "\\%s{%d}" hiddenMacro x
+      [printf "\\put(%d,%d){\\makebox(2,1)[c]{%s}}" (n-1-i + 2*j) (n-i-1) cell])
+   ++
    (case mops of
       Left _ -> []
       Right ops ->
          let half = 0.5 :: Double in
          BoxedArray.toAssociations ops >>= \((i,j),op) ->
+            let h = n-i-1 in
             [printf "\\put(%d,%d){\\textcolor{white}{\\circle*{0.5}}}"
-               (n-i + 2*j) (n-i),
-             printf "\\put(%d,%d){\\circle{0.5}}" (n-i + 2*j) (n-i),
+               (n-i + 2*j) h,
+             printf "\\put(%d,%d){\\circle{0.5}}" (n-i + 2*j) h,
              printf "\\put(%.1f,%.1f){\\makebox(1,1)[c]{$%s$}}"
-               (fromIntegral (n-i + 2*j) - half) (fromIntegral (n-i) - half)
+               (fromIntegral (n-i + 2*j) - half) (fromIntegral h - half)
                (case op of Add -> "+"; Mul -> "\\times{}")]) ++
    "\\end{picture}" :
    []
@@ -143,7 +145,7 @@
 mainCreate ::
    (SolutionCheck Integer, (Allowed,Bool)) ->
    Int -> Int -> Integer -> String -> String -> IO ()
-mainCreate (check,(allowed,displayOps)) n number maxV env hidden =
+mainCreate (check,(allowed,displayOps)) n number maxV env hiddenMacro =
    putStr . unlines .
       concatMap
          ((if null env
@@ -151,7 +153,7 @@
             else (\pic ->
                      printf "\\begin{%s}" env : pic ++
                      printf "\\end{%s}" env : []))
-            . uncurry (latexFromPuzzle hidden)
+            . uncurry (latexFromPuzzle hiddenMacro)
             . mapFst (if displayOps then Right else const (Left n))) .
       MS.evalState (replicateM number $ construct check allowed n maxV)
          =<< Random.initStdGen
@@ -163,10 +165,11 @@
          <*>
             (
                (OP.flag'
-                  (\ops xs -> LinAlg.solvable (sizeFromOps ops) (map fst xs),
+                  (\ops xs ->
+                        LinAlg.solvable (sizeFromOps ops) (Map.keysSet xs),
                      (Allowed {allowedAdd = True, allowedMul = False}, False)) $
                   OP.long "allow-gaps" <>
-                  OP.help "Employ both addition and multiplication")
+                  OP.help "Puzzles maybe not be solvable step by step")
                <|>
                (fmap ((,) Logic.solvableMixed) $
                   (OP.flag'
@@ -249,12 +252,10 @@
 573549
 13604001
 -}
-mainCount :: (Int -> [(Int,Int)] -> Bool) -> IO ()
+mainCount :: (Int -> Set (Int,Int) -> Bool) -> IO ()
 mainCount check =
    for_ [0..] $ \n ->
-      print $ length $ filter (check n) $
-      Combinatorics.tuples n $ Shape.indices $
-      Shape.lowerTriangular $ Shape.ZeroBased n
+      print $ length $ filter (check n) $ allCellSelections n
 
 commandCount :: OP.Mod OP.CommandFields (IO ())
 commandCount =
diff --git a/src/UniqueLogic.hs b/src/UniqueLogic.hs
--- a/src/UniqueLogic.hs
+++ b/src/UniqueLogic.hs
@@ -6,23 +6,24 @@
 import qualified UniqueLogic.ST.TF.System.Simple as Sys
 import qualified UniqueLogic.ST.TF.ZeroFractional as ZeroFrac
 
-import qualified Combinatorics
-
 import Control.Monad.ST (ST, runST)
 
 import qualified Data.Array.Comfort.Boxed as BoxedArray
 import qualified Data.Array.Comfort.Shape as Shape
 import qualified Data.Traversable as Trav
 import qualified Data.Foldable as Fold
+import qualified Data.Map as Map
 import Data.Array.Comfort.Boxed ((!))
+import Data.Map (Map)
+import Data.Set (Set)
 import Data.Foldable (for_, traverse_)
 import Data.Maybe (isJust)
-import Data.Tuple.HT (mapSnd)
 
 
 {- $setup
 >>> import qualified Data.Array.Comfort.Boxed as BoxedArray
 >>> import qualified Data.Array.Comfort.Shape as Shape
+>>> import qualified Data.Map as Map
 >>> import Common
 -}
 
@@ -32,11 +33,8 @@
 
 system ::
    BoxedArray.Array
-      (Shape.LowerTriangular ShapeInt)
-      (Variable s a ->
-       Variable s a ->
-       Variable s a ->
-       System s ()) ->
+      (Shape.LowerTriangular ShapeOp)
+      (Variable s a -> Variable s a -> Variable s a -> System s ()) ->
    ST s
       (BoxedArray.Array (Shape.LowerTriangular ShapeInt) (Variable s a),
        System s ())
@@ -55,40 +53,38 @@
 
 
 {- |
->>> solve 3 [((0,0),1), ((1,0),1), ((2,0),1::Integer)]
+>>> solve 3 $ Map.fromList [((0,0),1), ((1,0),1), ((2,0),1::Integer)]
 BoxedArray...Triangular... 3... [Just 1,Just 1,Just 0,Just 1,Just 0,Just 0]
 
->>> solve 3 [((0,0),1), ((2,0),1), ((2,2),1::Integer)]
+>>> solve 3 $ Map.fromList [((0,0),1), ((2,0),1), ((2,2),1::Integer)]
 BoxedArray...Triangular... 3... [Just 1,Nothing,Nothing,Just 1,Nothing,Just 1]
 -}
 solve ::
    (Num a) =>
-   Int -> [((Int,Int),a)] ->
+   Int -> Map (Int,Int) a ->
    BoxedArray.Array (Shape.LowerTriangular ShapeInt) (Maybe a)
 solve n xs =
    runST
       (do
          (vars, sys) <-
             system $
-            BoxedArray.replicate
-               (Shape.lowerTriangular $ Shape.ZeroBased $ n-1)
-               Rule.add
+            BoxedArray.replicate (Shape.lowerTriangular $ ShapeOp n) Rule.add
          Sys.solve $ do
             sys
-            for_ xs $ \(ij,x) ->
+            for_ (Map.toList xs) $ \(ij,x) ->
                Rule.equ (vars!ij) =<< Sys.constant x
          traverse Sys.query vars)
 
 {- |
->>> solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ Shape.ZeroBased 1) [Mul]) [((0,0),0), ((1,1),0::Rational)]
+>>> solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ ShapeOp 2) [Mul]) (Map.fromList [((0,0),0), ((1,1),0::Rational)])
 BoxedArray...Triangular... 2... [Just (0 % 1),Nothing,Just (0 % 1)]
->>> solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ Shape.ZeroBased 1) [Mul]) [((0,0),0), ((1,1),5::Rational)]
+>>> solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ ShapeOp 2) [Mul]) (Map.fromList [((0,0),0), ((1,1),5::Rational)])
 BoxedArray...Triangular... 2... [Just (0 % 1),Just (0 % 1),Just (5 % 1)]
 -}
 solveMixed ::
    (ZeroFrac.C a) =>
-   BoxedArray.Array (Shape.LowerTriangular ShapeInt) Op ->
-   [((Int,Int),a)] ->
+   BoxedArray.Array (Shape.LowerTriangular ShapeOp) Op ->
+   Map (Int,Int) a ->
    BoxedArray.Array (Shape.LowerTriangular ShapeInt) (Maybe a)
 solveMixed ops xs =
    runST
@@ -100,21 +96,22 @@
          (vars, sys) <- system $ fmap rule ops
          Sys.solve $ do
             sys
-            for_ xs $ \(ij,x) ->
+            for_ (Map.toList xs) $ \(ij,x) ->
                Rule.equ (vars!ij) =<< Sys.constant x
          traverse Sys.query vars)
 
 
-solvable :: Int -> [(Int, Int)] -> Bool
-solvable n = Fold.all isJust . solve n . map (\ij -> (ij, 0::Integer))
+solvable :: Int -> Set (Int, Int) -> Bool
+solvable n = Fold.all isJust . solve n . Map.fromSet (const (0::Integer))
 
-solvables :: Int -> [[(Int,Int)]]
-solvables n =
-   filter (solvable n) $
-   Combinatorics.tuples n $ Shape.indices $
-   Shape.lowerTriangular $ Shape.ZeroBased n
+{- |
+>>> map (length . solvables) [0..5]
+[1,1,3,16,122,1188]
+-}
+solvables :: Int -> [Set (Int,Int)]
+solvables n = filter (solvable n) $ allCellSelections n
 
 
 solvableMixed :: SolutionCheck Integer
 solvableMixed ops puzzle =
-   Fold.all isJust $ solveMixed ops $ map (mapSnd toRational) puzzle
+   Fold.all isJust $ solveMixed ops $ fmap toRational puzzle
diff --git a/sum-pyramid.cabal b/sum-pyramid.cabal
--- a/sum-pyramid.cabal
+++ b/sum-pyramid.cabal
@@ -1,5 +1,5 @@
 Name:                sum-pyramid
-Version:             0.0
+Version:             0.0.1
 Synopsis:            Create Sum Pyramid (Additionstreppe) exercises
 Description:
   Create Sum Pyramid (Additionstreppe) exercises.
@@ -7,6 +7,18 @@
   and the program emits LaTeX code for puzzles.
   .
   > sum-pyramid create --size 5
+  .
+  > nix-shell --run "make puzzle.pdf"
+  .
+  You may also use multiplications (@--multiplication@),
+  or both addition and multiplication (@--mixed@).
+  In case of additions only,
+  you may allow puzzles that are uniquely solvable,
+  but possibly not step-by-step (@--allow-gaps@).
+  You can also control via LaTeX macros
+  whether and how unknown cells are displayed.
+  .
+  Example output: http://code.henning-thielemann.de/sum-pyramid/puzzle.pdf
 Homepage:            https://hub.darcs.net/thielema/sum-pyramid
 License:             BSD3
 License-File:        LICENSE
@@ -21,7 +33,7 @@
   puzzle.tex
 
 Source-Repository this
-  Tag:         0.0
+  Tag:         0.0.1
   Type:        darcs
   Location:    https://hub.darcs.net/thielema/sum-pyramid
 
@@ -31,12 +43,12 @@
 
 Executable sum-pyramid
   Build-Depends:
-    lapack >=0.5.1 && <0.6,
+    lapack >=0.5.2 && <0.6,
     unique-logic-tf >=0.5.1 && <0.6,
     combinatorial >=0.1.1 && <0.2,
-    random >=1.2.1 && <1.3,
-    comfort-array >=0.5 && <0.6,
-    containers >=0.5.4 && <0.8,
+    random >=1.2.1 && <1.4,
+    comfort-array >=0.5.5 && <0.6,
+    containers >=0.5.4 && <0.9,
     transformers >=0.3 && <0.7,
     shell-utility >=0.1 && <0.2,
     optparse-applicative >=0.11 && <0.19,
@@ -63,7 +75,7 @@
     containers,
     transformers,
     utility-ht,
-    base
+    base >=4.5 && <5
   Default-Language:    Haskell2010
   GHC-Options:         -Wall
   Hs-Source-Dirs:      src, test
diff --git a/test/Test/LinearAlgebra.hs b/test/Test/LinearAlgebra.hs
--- a/test/Test/LinearAlgebra.hs
+++ b/test/Test/LinearAlgebra.hs
@@ -1,5 +1,5 @@
 -- Do not edit! Automatically created with doctest-extract from src/LinearAlgebra.hs
-{-# LINE 25 "src/LinearAlgebra.hs" #-}
+{-# LINE 27 "src/LinearAlgebra.hs" #-}
 
 module Test.LinearAlgebra where
 
@@ -7,8 +7,9 @@
 import Test.DocTest.Base
 import qualified Test.DocTest.Driver as DocTest
 
-{-# LINE 26 "src/LinearAlgebra.hs" #-}
+{-# LINE 28 "src/LinearAlgebra.hs" #-}
 import     qualified Data.Array.Comfort.Storable as Array
+import     qualified Data.Map as Map
 import     Data.Tuple.HT (mapSnd)
 
 myRound     :: Double -> Integer
@@ -16,10 +17,17 @@
 
 test :: DocTest.T ()
 test = do
- DocTest.printPrefix "LinearAlgebra:81: "
-{-# LINE 81 "src/LinearAlgebra.hs" #-}
+ DocTest.printPrefix "LinearAlgebra:84: "
+{-# LINE 84 "src/LinearAlgebra.hs" #-}
  DocTest.example(
-{-# LINE 81 "src/LinearAlgebra.hs" #-}
-    mapSnd (map myRound . Array.toList) $ solve 3 [((0,0),8), ((2,0),1), ((2,2),3)]
+{-# LINE 84 "src/LinearAlgebra.hs" #-}
+    mapSnd (map myRound . Array.toList) $ solve 3 $ Map.fromList [((0,0),8), ((2,0),1), ((2,2),3)]
   )
   [ExpectedLine [LineChunk "(3,[8,3,5,1,2,3])"]]
+ DocTest.printPrefix "LinearAlgebra:107: "
+{-# LINE 107 "src/LinearAlgebra.hs" #-}
+ DocTest.example(
+{-# LINE 107 "src/LinearAlgebra.hs" #-}
+    map (length . solvables) [0..5]
+  )
+  [ExpectedLine [LineChunk "[1,1,3,17,149,1824]"]]
diff --git a/test/Test/UniqueLogic.hs b/test/Test/UniqueLogic.hs
--- a/test/Test/UniqueLogic.hs
+++ b/test/Test/UniqueLogic.hs
@@ -10,35 +10,43 @@
 {-# LINE 24 "src/UniqueLogic.hs" #-}
 import     qualified Data.Array.Comfort.Boxed as BoxedArray
 import     qualified Data.Array.Comfort.Shape as Shape
+import     qualified Data.Map as Map
 import     Common
 
 test :: DocTest.T ()
 test = do
- DocTest.printPrefix "UniqueLogic:58: "
-{-# LINE 58 "src/UniqueLogic.hs" #-}
+ DocTest.printPrefix "UniqueLogic:56: "
+{-# LINE 56 "src/UniqueLogic.hs" #-}
  DocTest.example(
-{-# LINE 58 "src/UniqueLogic.hs" #-}
-    solve 3 [((0,0),1), ((1,0),1), ((2,0),1::Integer)]
+{-# LINE 56 "src/UniqueLogic.hs" #-}
+    solve 3 $ Map.fromList [((0,0),1), ((1,0),1), ((2,0),1::Integer)]
   )
   [ExpectedLine [LineChunk "BoxedArray",WildCardChunk,LineChunk "Triangular",WildCardChunk,LineChunk " 3",WildCardChunk,LineChunk " [Just 1,Just 1,Just 0,Just 1,Just 0,Just 0]"]]
- DocTest.printPrefix "UniqueLogic:61: "
-{-# LINE 61 "src/UniqueLogic.hs" #-}
+ DocTest.printPrefix "UniqueLogic:59: "
+{-# LINE 59 "src/UniqueLogic.hs" #-}
  DocTest.example(
-{-# LINE 61 "src/UniqueLogic.hs" #-}
-    solve 3 [((0,0),1), ((2,0),1), ((2,2),1::Integer)]
+{-# LINE 59 "src/UniqueLogic.hs" #-}
+    solve 3 $ Map.fromList [((0,0),1), ((2,0),1), ((2,2),1::Integer)]
   )
   [ExpectedLine [LineChunk "BoxedArray",WildCardChunk,LineChunk "Triangular",WildCardChunk,LineChunk " 3",WildCardChunk,LineChunk " [Just 1,Nothing,Nothing,Just 1,Nothing,Just 1]"]]
- DocTest.printPrefix "UniqueLogic:83: "
-{-# LINE 83 "src/UniqueLogic.hs" #-}
+ DocTest.printPrefix "UniqueLogic:79: "
+{-# LINE 79 "src/UniqueLogic.hs" #-}
  DocTest.example(
-{-# LINE 83 "src/UniqueLogic.hs" #-}
-    solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ Shape.ZeroBased 1) [Mul]) [((0,0),0), ((1,1),0::Rational)]
+{-# LINE 79 "src/UniqueLogic.hs" #-}
+    solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ ShapeOp 2) [Mul]) (Map.fromList [((0,0),0), ((1,1),0::Rational)])
   )
   [ExpectedLine [LineChunk "BoxedArray",WildCardChunk,LineChunk "Triangular",WildCardChunk,LineChunk " 2",WildCardChunk,LineChunk " [Just (0 % 1),Nothing,Just (0 % 1)]"]]
- DocTest.printPrefix "UniqueLogic:85: "
-{-# LINE 85 "src/UniqueLogic.hs" #-}
+ DocTest.printPrefix "UniqueLogic:81: "
+{-# LINE 81 "src/UniqueLogic.hs" #-}
  DocTest.example(
-{-# LINE 85 "src/UniqueLogic.hs" #-}
-    solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ Shape.ZeroBased 1) [Mul]) [((0,0),0), ((1,1),5::Rational)]
+{-# LINE 81 "src/UniqueLogic.hs" #-}
+    solveMixed (BoxedArray.fromList (Shape.lowerTriangular $ ShapeOp 2) [Mul]) (Map.fromList [((0,0),0), ((1,1),5::Rational)])
   )
   [ExpectedLine [LineChunk "BoxedArray",WildCardChunk,LineChunk "Triangular",WildCardChunk,LineChunk " 2",WildCardChunk,LineChunk " [Just (0 % 1),Just (0 % 1),Just (5 % 1)]"]]
+ DocTest.printPrefix "UniqueLogic:108: "
+{-# LINE 108 "src/UniqueLogic.hs" #-}
+ DocTest.example(
+{-# LINE 108 "src/UniqueLogic.hs" #-}
+    map (length . solvables) [0..5]
+  )
+  [ExpectedLine [LineChunk "[1,1,3,16,122,1188]"]]
