set-cover-0.0.6: example/Nonogram/Encoding/Combinatoric.hs
{- |
In this module we generate for every line all possible layouts of bricks.
This leads to a big number of sets,
but still allows for the fastest solution
and a minimum number of solution steps.
The solver tends to need very few guesses.
-}
module Nonogram.Encoding.Combinatoric
(assigns, assignsBW, bitAssigns, intSetAssigns, bitVectorAssigns) where
import qualified Nonogram.Base as Base
import Nonogram.Base
(Strip(Strip), strip, Orientation(Horizontal, Vertical),
Color(White, Black), noAssign)
import qualified Math.SetCover.BitSet as BitSet
import qualified Math.SetCover.Exact as ESC
import Data.Bits (bit, setBit)
import Control.Monad (guard)
import Control.Applicative ((<$>))
import qualified Data.IntSet as IntSet; import Data.IntSet (IntSet)
import qualified Data.Map as Map; import Data.Map (Map)
import qualified Data.NonEmpty as NonEmpty
import Data.Foldable (foldMap, fold)
import Data.Monoid (Monoid)
import Data.Word (Word64)
import Data.Set (Set)
data Item = Line | Position Int Color
deriving (Eq, Ord, Show)
instance Base.Position Item where position = Position
type Assign map = ESC.Assign map (Map Strip (Set Item))
{-
quickCheck $ \n0 ns0 -> let n = abs n0; ns = map abs ns0 in spread n ns == spreadNaive n ns
-}
spread :: Int -> [Int] -> [(Int, [(Int, Int)])]
spread width0 sizes0 =
case NonEmpty.init $ NonEmpty.scanr (+) (-1) (map succ sizes0) of
[] -> return (width0, [])
minWidth0:sums -> do
remWidth0 <- reverse [minWidth0 .. width0]
let go width [] = guard (width == 0) >> return []
go width [(_0,size)] =
guard (width>=size) >> return [(size, width-size)]
go width ((minWidth,size):sizes) = do
remWidth <- reverse [minWidth .. width-1]
((size, width-remWidth):) <$> go (remWidth-size) sizes
chain <- go remWidth0 $ zip (sums++[0]) sizes0
return (width0-remWidth0, chain)
_spreadNaive :: Int -> [Int] -> [(Int, [(Int, Int)])]
_spreadNaive width0 sizes0 = do
start <- [0..width0]
let go width [] = guard (width == 0) >> return []
go width [size] = guard (width>=size) >> return [(size, width-size)]
go width (size:sizes) = do
space <- [1 .. width-size]
((size,space):) <$> go (width-size-space) sizes
chain <- go (width0-start) sizes0
return (start, chain)
assignsFromLine ::
(Monoid map) => Orientation -> Int -> Int -> [Int] -> [Assign map]
assignsFromLine orient width line xs =
map
(noAssign . strip orient line . (Line :) .
zipWith Position [0..] .
(\(start,bricks) ->
replicate start Black ++
concatMap
(\(size,space) -> replicate size White ++ replicate space Black)
bricks)) $
spread width xs
assignsGen ::
(Monoid map) =>
(Int -> Int -> Color -> map) -> [[Int]] -> [[Int]] -> [Assign map]
assignsGen square rows columns =
concat (zipWith (assignsFromLine Horizontal (length columns)) [0..] rows)
++
concat (zipWith (assignsFromLine Vertical (length rows)) [0..] columns)
++
Base.assignsFromPositions square rows columns
assigns :: [[Int]] -> [[Int]] -> [Assign (Set (Int,Int))]
assigns = assignsGen Base.square
assignsBW :: [[Int]] -> [[Int]] -> [Assign (Map (Int,Int) Color)]
assignsBW = assignsGen Base.squareBW
type Mask = BitSet.Set Word64
bitAssigns ::
[ESC.Assign map (Map Strip (Set Item))] -> [ESC.Assign map (Map Strip Mask)]
bitAssigns = map (fmap (fmap (foldMap (BitSet.Set . bitFromItem))))
bitFromItem :: Item -> Word64
bitFromItem x =
case x of
Line -> bit 63
Position n color ->
if n<31
then bit (n + 31 * fromEnum color)
else error "bitFromItem: position too big"
intSetAssigns ::
Int -> Int -> [ESC.Assign map (Map Strip (Set Item))] -> [ESC.Assign map IntSet]
intSetAssigns nr nc =
map (fmap (fold . Map.mapWithKey (intSetFromItems nr nc)))
intSetFromItems :: Int -> Int -> Strip -> Set Item -> IntSet
intSetFromItems nr nc (Strip orient k) items =
case orient of
Horizontal ->
flip foldMap items $ \item ->
IntSet.singleton $ intFromItem nr nc k item
Vertical ->
flip foldMap items $ \item ->
IntSet.singleton $ nr + 2*nr*nc + intFromItem nc nr k item
intFromItem :: Int -> Int -> Int -> Item -> Int
intFromItem nr nc k item =
case item of
Line -> k
Position j color -> nr + 2*(nc*k+j) + fromEnum color
type BitVector = BitSet.Set Integer
bitVectorAssigns ::
Int -> Int ->
[ESC.Assign map (Map Strip (Set Item))] -> [ESC.Assign map BitVector]
bitVectorAssigns nr nc =
map (fmap (fold . Map.mapWithKey (bitVectorFromItems nr nc)))
bitVectorFromItems :: Int -> Int -> Strip -> Set Item -> BitVector
bitVectorFromItems nr nc x =
BitSet.Set . foldl setBit 0 . IntSet.toList . intSetFromItems nr nc x