set-cover 0.1.1.1 → 0.1.2
raw patch · 17 files changed
+1074/−85 lines, 17 filesdep +doctest-exitcode-stdiodep +doctest-libdep +enumsetdep ~basedep ~containersdep ~enummapsetnew-component:exe:graph-coloringnew-component:exe:pythagoras-coloring
Dependencies added: doctest-exitcode-stdio, doctest-lib, enumset
Dependency ranges changed: base, containers, enummapset, llvm-dsl, llvm-extra, llvm-tf, non-empty, semigroups, timeit, utility-ht
Files
- Makefile +7/−1
- example/ConwayPuzzle.hs +5/−3
- example/GraphColoring.hs +74/−0
- example/GraphColoring/CutEnumSet.hs +182/−0
- example/GraphColoring/CutSet.hs +104/−0
- example/GraphColoring/Solve.hs +276/−0
- example/PythagorasColoring.hs +108/−0
- set-cover.cabal +48/−13
- src/Math/SetCover/Exact.hs +40/−8
- src/Math/SetCover/Exact/Knead.hs +2/−2
- src/Math/SetCover/Exact/Knead/Saturated.hs +27/−27
- src/Math/SetCover/Exact/Knead/Symbolic.hs +13/−13
- src/Math/SetCover/Exact/Knead/Vector.hs +13/−13
- test/DocTest/GraphColoring/CutEnumSet.hs +46/−0
- test/DocTest/GraphColoring/Solve.hs +106/−0
- test/DocTest/Main.hs +12/−0
- test/Test.hs +11/−5
Makefile view
@@ -1,4 +1,4 @@-run-test:+run-test: update-test runhaskell Setup configure --user -fbuildExamples --enable-tests runhaskell Setup build runhaskell Setup configure --user -fbuildExamples -fllvm --enable-tests --enable-benchmarks@@ -7,3 +7,9 @@ dist/build/set-cover-test/set-cover-test # more portable, but suppresses live QuickCheck test counter: # runhaskell Setup test --show-details=streaming++update-test:+ doctest-extract-0.1 -i example/ -o test/ \+ --module-prefix DocTest --library-main=Main --import-tested \+ GraphColoring.Solve \+ GraphColoring.CutEnumSet
example/ConwayPuzzle.hs view
@@ -20,7 +20,9 @@ import qualified Data.Foldable as Fold import qualified Data.Map as Map import qualified Data.Set as Set+import qualified Data.NonEmpty as NonEmpty import qualified Data.List as List+import Data.NonEmpty ((!:)) import Data.IntSet (IntSet) import Data.Foldable (foldMap) @@ -131,9 +133,9 @@ ESC.State [FormatMask] IntSet initState s3asn = case ESC.intSetFromSetAssigns $- s3asn : map (\mask -> ESC.assign [mask] mask) squares of- asns@(s3:_) -> ESC.updateState s3 $ ESC.initState asns- [] -> error "ESC.bitVectorFromSetAssigns lost first assignment"+ s3asn !: map (\mask -> ESC.assign [mask] mask) squares of+ asns@(NonEmpty.Cons s3 _) ->+ ESC.updateState s3 $ ESC.initState $ NonEmpty.flatten asns formatIdent :: Int -> Char
+ example/GraphColoring.hs view
@@ -0,0 +1,74 @@+module Main where++import qualified GraphColoring.CutEnumSet as CutEnumSet+import qualified GraphColoring.CutSet as CutSet+import GraphColoring.Solve++import qualified Math.SetCover.Exact as ESC++import qualified Data.Set as Set+import Text.Printf (printf)++import System.TimeIt (timeIt)+++mainCount :: IO ()+mainCount = do+ let example = (['a'..'g'], complete 7)+ printf "plain: %d\n" $+ length $ ESC.search $+ ESC.compressState Set.toList $ initialize assigns example+ printf "cut branch by set: %d\n" $+ length $ CutSet.search $ CutSet.initState $+ ESC.intSetFromSetAssigns $ uncurry assigns example+ printf "cut branch by enumset: %d\n" $+ length $ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $ uncurry assignsEdgeColor example++ printf "cut branch by enumset big: %d\n" $+ length $ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $+ uncurry assignsEdgeColor (['a'..'z'], complete 30)++mainSolution :: IO ()+mainSolution =+ CutEnumSet.run $ ESC.intSetFromSetAssigns $+ uncurry assignsEdgeColor example5+++{-+The algorithm that cuts away branches+was intended to be more efficient,+but actually needs much more time:++plain:+coloring impossible+CPU time: 8.95s++cut branch:+coloring impossible+CPU time: 61.92s+++Reason:+The improved algorithm actually did not cut away cases,+it only fixes the first color,+and thus is factor n slower than the algorithm that fixes the first two colors.+-}+mainImpossible :: IO ()+mainImpossible = do+ let example = (['a'..'h'], complete 10)+ putStrLn "\nplain:"+ timeIt $ run $ ESC.compressState Set.toList $ initialize assigns example+ putStrLn "\nreserve edge:"+ timeIt $ run $ ESC.compressState Set.toList $+ initialize assignsReserveEdge example+ putStrLn "\ncut branch by set:"+ timeIt $ CutSet.run $ ESC.intSetFromSetAssigns $ uncurry assigns example+ putStrLn "\ncut branch by enumset:"+ timeIt $ CutEnumSet.run $ ESC.intSetFromSetAssigns $+ uncurry assignsEdgeColor example+++main :: IO ()+main = mainSolution
+ example/GraphColoring/CutEnumSet.hs view
@@ -0,0 +1,182 @@+module GraphColoring.CutEnumSet where++import qualified Math.SetCover.Exact as ESC++import qualified Data.EnumBitSet as EnumBitSet+import qualified Data.EnumSet as EnumSet+import qualified Data.List as List+import Data.Monoid (First(First, getFirst))+import Data.EnumSet (EnumSet)+import Data.Ord.HT (comparing)+import Data.Either.HT (maybeRight)+import Data.Maybe.HT (toMaybe)+import Data.Maybe (mapMaybe)+import Data.Word (Word8)+import Text.Printf (printf)++{- $setup+>>> import qualified Math.SetCover.Exact as ESC+>>>+>>> import qualified Data.EnumSet as EnumSet+>>> import qualified Data.List.Key as Key+>>> import Data.Word (Word32)+>>>+>>> import Control.Applicative (liftA2)+>>>+>>> import qualified Test.QuickCheck as QC+>>> import Test.QuickCheck ((===))+-}+++type Label node color = Either (color, color) (node, color)+type Assign node color set = ESC.Assign (Label node color) set++{- |+Re-implementation of SetCover.Exact functions with an extended state,+that also maintains the already used colors in an efficient 'EnumSet'.+-}+data State node color set =+ State {+ stateSetCover :: ESC.State (Label node color) set,+ stateUsedColors :: EnumSet color+ }++initState :: (Enum color, ESC.Set set) =>+ [Assign node color set] -> State node color set+initState asns =+ State {+ stateSetCover = ESC.initState asns,+ stateUsedColors = EnumSet.empty+ }++{-# INLINE updateState #-}+updateState :: (Enum color, ESC.Set set) =>+ Assign node color set ->+ State node color set -> State node color set+updateState asn s =+ State {+ stateSetCover = ESC.updateState asn $ stateSetCover s,+ stateUsedColors =+ stateUsedColors s <>+ case ESC.label asn of+ Right (_,color) -> EnumSet.singleton color+ Left (color0,color1) ->+ EnumSet.singleton color0 <> EnumSet.singleton color1+ }++{- |+Without loss of generality, when new colors are choosen,+only try the lexicographically lowest single color or color pair.+-}+uniqueNewColors :: (Enum color) =>+ EnumSet color -> [Assign node color set] -> [Assign node color set]+uniqueNewColors alreadyUsedColors =+ (\(ordinaryAttempts,+ tryNewColors0, tryNewColors1, tryNewColors2, tryNewColors3) ->+ ordinaryAttempts +++ mapMaybe getFirst+ [tryNewColors0, tryNewColors1, tryNewColors2, tryNewColors3])+ .+ foldMap+ (\asn ->+ let single = First . Just in+ case ESC.label asn of+ Left (color0,color1) ->+ case (EnumSet.member color0 alreadyUsedColors,+ EnumSet.member color1 alreadyUsedColors) of+ (True, True) -> ([asn], mempty, mempty, mempty, mempty)+ (False, False) ->+ (mempty, mempty, single asn, mempty, mempty)+ (False, True) ->+ (mempty, mempty, mempty, single asn, mempty)+ (True, False) ->+ (mempty, mempty, mempty, mempty, single asn)+ Right (_node,color) ->+ if EnumSet.member color alreadyUsedColors+ then ([asn], mempty, mempty, mempty, mempty)+ else (mempty, single asn, mempty, mempty, mempty))++++data UsagePattern = NewNode | NewEdge0 | NewEdge1 | NewEdge01+ deriving (Eq, Ord, Enum, Show)++{- |+This is the same as uniqueNewColors+but performs a single scan through the list+and bookkeeps already seen patterns of new colors.+Order of assignments is maintained.+++prop> :{+ QC.forAll (fmap (\n -> take (1 + mod n 5) ['a'..]) QC.arbitrary) $ \set ->+ QC.forAll (QC.sublistOf set) $ \used ->+ let usedSet = EnumSet.fromList used in+ let genLabel =+ QC.oneof+ [fmap Left $ liftA2 (,) (QC.elements set) (QC.elements set),+ fmap Right $+ liftA2 (,) (QC.arbitrary :: QC.Gen Integer) (QC.elements set)]+ genAssignSet :: QC.Gen Word32+ genAssignSet = QC.arbitrary+ in+ QC.forAll (QC.listOf (liftA2 ESC.assign genLabel genAssignSet)) $ \assigns ->+ let normalize =+ Key.sort fst . map (\(ESC.Assign label aset) -> (label,aset)) in++ normalize (uniqueNewColors usedSet assigns)+ ===+ normalize (uniqueNewColorsStable usedSet assigns)+:}+-}+uniqueNewColorsStable :: (Enum color) =>+ EnumSet color -> [Assign node color set] -> [Assign node color set]+uniqueNewColorsStable alreadyUsedColors asns =+ map fst $ filter snd $ zip asns $+ snd $+ List.mapAccumL+ (\seen mpat ->+ case mpat of+ Nothing -> (seen, True)+ Just pat ->+ (EnumBitSet.set pat seen, not (EnumBitSet.get pat seen)))+ (EnumBitSet.empty :: EnumBitSet.T Word8 UsagePattern)+ $+ flip map asns+ (\asn ->+ case ESC.label asn of+ Left (color0,color1) ->+ case (EnumSet.member color0 alreadyUsedColors,+ EnumSet.member color1 alreadyUsedColors) of+ (True, True) -> Nothing+ (False, False) -> Just NewEdge01+ (False, True) -> Just NewEdge0+ (True, False) -> Just NewEdge1+ Right (_node,color) ->+ toMaybe (EnumSet.notMember color alreadyUsedColors) NewNode)++{-# INLINE step #-}+step :: (Enum color, ESC.Set set) =>+ State node color set -> [State node color set]+step s =+ map (flip updateState s) $+ uniqueNewColorsStable (stateUsedColors s) $+ let se = stateSetCover s in+ ESC.minimize (ESC.freeElements se) (ESC.availableSubsets se)++{-# INLINE search #-}+search :: (Enum color, ESC.Set set) =>+ State node color set -> [[Label node color]]+search s =+ let se = stateSetCover s in+ if ESC.null (ESC.freeElements se)+ then [ESC.usedSubsets se]+ else step s >>= search++run :: (ESC.Set set) => [Assign Int Char set] -> IO ()+run asns =+ case search $ initState asns of+ [] -> putStrLn "coloring impossible"+ solution:_ ->+ mapM_ (\(node,color) -> printf "Node %d: %c\n" node color) $+ List.sortBy (comparing fst) $ mapMaybe maybeRight solution
+ example/GraphColoring/CutSet.hs view
@@ -0,0 +1,104 @@+module GraphColoring.CutSet where++import qualified Math.SetCover.Exact as ESC++import qualified Data.Set as Set+import qualified Data.List as List+import Data.Set (Set)+import Data.Ord.HT (comparing)+import Data.Maybe (catMaybes)+import Text.Printf (printf)+++type Label node color = Maybe (node, color)++{- |+Re-implementation of SetCover.Exact functions with an extended state,+that also maintains the already used colors in an efficient 'Set'.+-}+data State node color set =+ State {+ stateSetCover :: ESC.State (Label node color) set,+ stateUsedColors :: Set color+ }++initState :: (Ord color, ESC.Set set) =>+ [ESC.Assign (Label node color) set] -> State node color set+initState asns =+ State {+ stateSetCover = ESC.initState asns,+ stateUsedColors = Set.empty+ }++{-# INLINE updateState #-}+updateState :: (Ord color, ESC.Set set) =>+ ESC.Assign (Label node color) set ->+ State node color set -> State node color set+updateState asn s =+ State {+ stateSetCover = ESC.updateState asn $ stateSetCover s,+ stateUsedColors =+ stateUsedColors s <> foldMap (Set.singleton . snd) (ESC.label asn)+ }++{- |+Without loss of generality, when new colors are choosen,+only try the lowest free color.+This is a bit simplistic, because it only respects colors introduced by nodes,+but not colors or color pairs introduced by edges.+Effectively this only chooses a unique first color+and then all other colors are still fully permuted,+because they are all introduced by edges.+See 'CutEnumSet' for a better solution.+-}+{-# INLINE step #-}+step :: (Ord color, ESC.Set set) =>+ State node color set -> [State node color set]+step s =+ map (flip updateState s) $+ (\(ordinaryAttempts, tryNewColors) ->+ ordinaryAttempts ++ take 1 tryNewColors) $+ List.partition+ (\asn ->+ case ESC.label asn of+ Nothing -> True+ Just (_node,color) -> Set.member color $ stateUsedColors s) $+ let se = stateSetCover s in+ ESC.minimize (ESC.freeElements se) (ESC.availableSubsets se)++{-+This is faster, but excludes possible solutions.+In other words, it is fundamentally broken.+-}+{-# INLINE stepPreFilter #-}+stepPreFilter :: (Ord color, ESC.Set set) =>+ State node color set -> [State node color set]+stepPreFilter s =+ map (flip updateState s) $+ let se = stateSetCover s in+ ESC.minimize (ESC.freeElements se) $+ (\(ordinaryAttempts, tryNewColors) ->+ ordinaryAttempts ++ take 1 tryNewColors) $+ List.partition+ (\asn ->+ case ESC.label asn of+ Nothing -> True+ Just (_node,color) -> Set.member color $ stateUsedColors s)+ (ESC.availableSubsets se)++{-# INLINE search #-}+search :: (Ord color, ESC.Set set) =>+ State node color set -> [[Label node color]]+search s =+ let se = stateSetCover s in+ if ESC.null (ESC.freeElements se)+ then [ESC.usedSubsets se]+ else step s >>= search++run :: (ESC.Set set) => [ESC.Assign (Label Int Char) set] -> IO ()+run asns =+ case search $ initState asns of+ [] -> putStrLn "coloring impossible"+ solution:_ ->+ mapM_ (\(node,color) -> printf "Node %d: %c\n" node color) $+ List.sortBy (comparing fst) $ catMaybes solution
+ example/GraphColoring/Solve.hs view
@@ -0,0 +1,276 @@+module GraphColoring.Solve where++import qualified Math.SetCover.Exact as ESC++import Control.Monad (guard)+import Control.Applicative (liftA2)++import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.List as List+import Data.Set (Set)+import Data.Ord.HT (comparing)+import Data.Maybe (catMaybes)+import Text.Printf (printf)++{- $setup+>>> import qualified GraphColoring.CutEnumSet as CutEnumSet+>>> import qualified Math.SetCover.Exact as ESC+>>>+>>> import qualified Data.Set as Set+>>> import qualified Data.List.Key as Key+>>> import Data.Either.HT (maybeRight)+>>> import Data.Maybe (catMaybes, mapMaybe)+>>>+>>> import qualified Test.QuickCheck as QC+>>> import Test.QuickCheck ((===))+>>>+>>> solve :: ([Char], [Edge Int]) -> [[(Int, Char)]]+>>> solve =+>>> map (Key.sort fst . catMaybes) . ESC.search .+>>> ESC.compressState Set.toList . initialize assigns+-}+++type Edge node = (node,node)++data X node color =+ Node node | Edge node node | ColorEdge node node color |+ EdgeColor node node color+ deriving (Eq, Ord, Show)++type Label node color = Maybe (node, color)+type ExtLabel node color = Either (color,color) (node, color)+type Assign node color = ESC.Assign (Label node color) (Set (X node color))+++{- |+For every node, block all but one color at outgoing edges.++Specify every edge only once, freely choose a direction.+Strange things might happend if you specify an edge in both directions.+-}+assignNodeColors ::+ (Ord color, Ord node) =>+ ((node, color) -> lab) ->+ [color] -> [Edge node] -> [ESC.Assign lab (Set (X node color))]+assignNodeColors lab colors edges =+ liftA2+ (\color (node,connected) ->+ ESC.assign (lab (node,color))+ (Set.fromList $+ Node node :+ liftA2 (ColorEdge node)+ (Set.toList connected)+ (List.delete color colors)))+ colors+ (Map.toList $ Map.fromListWith Set.union $+ edges >>=+ \(from,to) -> [(from, Set.singleton to), (to, Set.singleton from)])++{- FixMe:+Solver always succeeds when only one color is allowed.+When there is only one color, we do not generate edges, at all,+because we only generate edges with different colors at either ends.+-}+assigns ::+ (Ord color, Ord node) => [color] -> [Edge node] -> [Assign node color]+assigns colors edges =+ assignNodeColors Just colors edges+ +++ (do+ (from,to) <- edges+ fromColor <- colors+ toColor <- colors+ guard $ fromColor /= toColor+ return $ ESC.assign Nothing $+ Set.fromList [ColorEdge from to fromColor, ColorEdge to from toColor])++{- |+Register every used edge.+This is redundant but may speedup search.+Actually, it slows down the search.+-}+assignsReserveEdge ::+ (Ord color, Ord node) => [color] -> [Edge node] -> [Assign node color]+assignsReserveEdge colors edges =+ assignNodeColors Just colors edges+ +++ (do+ (from,to) <- edges+ fromColor <- colors+ toColor <- colors+ guard $ fromColor /= toColor+ return $ ESC.assign Nothing $+ Set.fromList+ [ColorEdge from to fromColor,+ ColorEdge to from toColor,+ Edge from to])++{- |+For every edge, 'assigns' needs a number of sets+proportional to the square of the number of colors.+'assignsSplitEdge' reduces this to a linear number.+Unfortunately solution becomes slower.+-}+assignsSplitEdge ::+ (Ord color, Ord node) => [color] -> [Edge node] -> [Assign node color]+assignsSplitEdge colors edges =+ assignNodeColors Just colors edges+ +++ (do+ (from,to) <- edges+ color <- colors+ [ESC.assign Nothing $ Set.fromList [EdgeColor from to color],+ ESC.assign Nothing $+ Set.fromList+ [ColorEdge from to color, EdgeColor from to color, Edge from to],+ ESC.assign Nothing $+ Set.fromList+ [ColorEdge to from color, EdgeColor from to color, Edge to from]+ ])+++{- |+Same rules as 'assign' but also gives labels for edge colors.+-}+assignsEdgeColor ::+ (Ord color, Ord node) =>+ [color] -> [Edge node] ->+ [ESC.Assign (ExtLabel node color) (Set (X node color))]+assignsEdgeColor colors edges =+ assignNodeColors Right colors edges+ +++ (do+ (from,to) <- edges+ fromColor <- colors+ toColor <- colors+ guard $ fromColor /= toColor+ return $ ESC.assign (Left (fromColor, toColor)) $+ Set.fromList [ColorEdge from to fromColor, ColorEdge to from toColor])++++{- |+>>> solve example0+[[(0,'a'),(1,'b'),(2,'a'),(3,'b'),(4,'a'),(5,'b')]]+-}+example0 :: ([Char], [Edge Int])+example0 = (['a'..'b'], [(0,1),(1,2),(2,3),(3,4),(4,5)])++{- |+>>> minimum $ solve example1+[(0,'a'),(1,'b'),(2,'a'),(3,'b'),(4,'c')]+-}+example1 :: ([Char], [Edge Int])+example1 = (['a'..'c'], [(0,1),(1,2),(2,3),(3,4),(4,0)])++{- |+>>> solve example2+[[(0,'a'),(1,'b'),(2,'c')]]+-}+example2 :: ([Char], [Edge Int])+example2 = (['a'..'c'], [(0,1),(1,2),(0,2)])++{- |+>>> solve example3+[[(0,'a'),(1,'b'),(2,'c'),(3,'b')]]+-}+example3 :: ([Char], [Edge Int])+example3 = (['a'..'c'], [(0,1),(1,2),(2,3),(3,0),(0,2)])++{- |+>>> solve example4+[]+-}+example4 :: ([Char], [Edge Int])+example4 = (['a'..'c'], [(0,1),(1,2),(2,3),(3,0),(0,2),(1,3)])++-- FixMe: fix cases n<2 or k<2+{- |+>>> length $ ESC.search $ ESC.compressState Set.toList $ ESC.initState $ assigns ['a'..'e'] (complete 5)+120++prop> :{+ QC.forAll (QC.choose (2,5)) $ \n ->+ QC.forAll (QC.choose (2,7)) $ \k ->++ (length $ ESC.search $ ESC.compressState Set.toList $+ ESC.initState $ assigns (take k ['a'..]) (complete n))+ ===+ product (take n [k,(k-1)..])+:}++prop> :{+ QC.forAll (QC.choose (2,5)) $ \n ->+ QC.forAll (QC.choose (2,7)) $ \k ->++ (length $ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $ assignsEdgeColor (take k ['a'..]) (complete n))+ ===+ if n<=k then 1 else 0+:}+-}+complete :: Int -> [Edge Int]+complete n = do from <- [1..n]; to <- [from+1 .. n]; [(from,to)]++{- |+>>> :{+ map (Key.sort fst . mapMaybe maybeRight) $+ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $ uncurry assignsEdgeColor example5+:}+[[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e'),(6,'f'),(7,'g'),(8,'h'),(9,'i'),(10,'j')]]+-}+example5 :: ([Char], [Edge Int])+example5 = (['a'..'j'], complete 10)++++{- |+Choose first two colors for the first two connected nodes,+without loss of generality.+This accelerates proof of impossibility.+However, for the following colors still plain color permutations are tried.+We could exclude them by modifying the search algorithm+such that at most one unused color is tried in every search step.+-}+initialize ::+ (Eq node, Eq color, ESC.Set set) =>+ ([color] -> [Edge node] -> [ESC.Assign (Label node color) set]) ->+ ([color], [Edge node]) -> ESC.State (Label node color) set+initialize assigner (colors,edges) =+ let asns = assigner colors edges in+ case (colors,edges) of+ (c0:c1:_, (from,to):_) ->+ foldl (flip ESC.updateState) (ESC.initState asns) $+ filter+ (\asn ->+ ESC.label asn == Just (from,c0) ||+ ESC.label asn == Just (to,c1))+ asns+ _ -> ESC.initState asns++{- |+The algorithm needs a fixed number of colors to choose from.+In some applications the number of colors is not given, but shall be minimal.+In this case you might start with as many colors as nodes,+and scan the results for solutions with less used colors.+You can then restart the algorithm with a smaller reservoir of colors.+You may also perform a bisection on the number of colors+or run solvers for multiple color sets in parallel.+I guess, for large color sets you will get a positive result quickly+and for small color sets you will get a negative answer quickly.++Finding a solution usually happens fast.+Proof of impossibility requires much more time.+For a real application you might consider setting a timeout+or trying to solve with different sets of colors in parallel.+-}+run :: (ESC.Set set) => ESC.State (Maybe (Int, Char)) set -> IO ()+run state =+ case ESC.search state of+ [] -> putStrLn "coloring impossible"+ solution:_ ->+ mapM_ (\(node,color) -> printf "Node %d: %c\n" node color) $+ List.sortBy (comparing fst) $ catMaybes solution
+ example/PythagorasColoring.hs view
@@ -0,0 +1,108 @@+{- |+Assign one of two colors to every natural number+such that there is no uniformly colored Pythagorean triple.++<https://en.wikipedia.org/wiki/Boolean_Pythagorean_triples_problem>+<https://dorfuchs93.github.io/boolean-pythagorean-triples/>+-}+module Main where++import qualified Math.SetCover.Exact as ESC++import qualified Data.Traversable as Trav+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.Set as Set+import Data.IntMap (IntMap)+import Data.IntSet (IntSet)+import Data.Set (Set)+import Data.Maybe (catMaybes)++import Control.Monad (guard)+import Control.Applicative (liftA2)+++data Color = Red | Blue+ deriving (Eq, Ord, Show)++data X =+ Number Int+ | PythagoreanTriple IntSet | PythagoreanColor IntSet Int Color+ deriving (Eq, Ord, Show)++type Assign = ESC.Assign (Maybe (Int, Color)) (Set X)+++colorNumber :: [IntSet] -> Int -> Color -> Assign+colorNumber tuples n c =+ ESC.assign (Just (n,c)) $ Set.fromList $+ Number n+ :+ map (\tuple -> PythagoreanColor tuple n c)+ (filter (IntSet.member n) tuples)++constIntMap :: a -> IntSet -> IntMap a+constIntMap x = IntMap.fromSet (const x)++admissibleTupleColorings :: IntSet -> [IntMap Color]+admissibleTupleColorings xs =+ filter+ (\tuple ->+ tuple /= constIntMap Red xs+ &&+ tuple /= constIntMap Blue xs) $+ Trav.sequence $ constIntMap [Red,Blue] xs+++assigns :: [IntSet] -> [Assign]+assigns tuples =+ let omega = IntSet.unions tuples in+ liftA2 (colorNumber tuples) (IntSet.toList omega) [Red, Blue]+ +++ (do+ tuple <- tuples+ tupleColoring <- admissibleTupleColorings tuple+ [ESC.assign Nothing $ Set.fromList $+ PythagoreanTriple tuple :+ IntMap.elems+ (IntMap.mapWithKey (PythagoreanColor tuple) tupleColoring)])+++intMapFromLabels :: [Maybe (Int, Color)] -> IntMap Color+intMapFromLabels = IntMap.fromList . catMaybes++pythagoreanTriples :: [IntSet]+pythagoreanTriples =+ IntSet.fromList [3,4,5] :+ IntSet.fromList [5,12,13] :+ IntSet.fromList [9,12,15] :+ IntSet.fromList [12,16,20] :+ []+++sqr :: Num a => a -> a+sqr a = a*a++{-+Alternatively we could use Euclidean's formula.+-}+pythagoreanTriplesUpTo :: Int -> [IntSet]+pythagoreanTriplesUpTo n = do+ a <- [1..n]+ b <- [a+1..n]+ let a2b2 = sqr a + sqr b+ let c = round $ sqrt (fromIntegral a2b2 :: Double)+ guard $ c<=n && a2b2 == sqr c+ return $ IntSet.fromList [a,b,c]+++mainAll :: IO ()+mainAll =+ mapM_ (print . intMapFromLabels) $+ ESC.partitions $ assigns pythagoreanTriples++main :: IO ()+main =+ mapM_ (print . intMapFromLabels) $ take 1 $+ ESC.partitions $ ESC.intSetFromSetAssigns $+ assigns $ pythagoreanTriplesUpTo 1500
set-cover.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: set-cover-Version: 0.1.1.1+Version: 0.1.2 License: BSD-3-Clause License-File: LICENSE Author: Henning Thielemann, Helmut Podhaisky@@ -11,7 +11,8 @@ Description: Solver for exact set cover problems. Included examples:- Sudoku, Nonogram, 8 Queens, Domino tiling, Mastermind, Alphametics,+ Sudoku, Nonogram, 8 Queens, Domino tiling, Mastermind,+ Alphametics, Graph coloring, Soma Cube, Tetris Cube, Cube of L's, Logika's Baumeister puzzle, Lonpos pyramid, Conway's puzzle. The generic algorithm allows to choose between@@ -32,9 +33,11 @@ Tested-With: GHC==7.4.2, GHC==7.6.3, GHC==7.8.2 Build-Type: Simple Extra-Source-Files:- Changes.md Makefile +Extra-Doc-Files:+ Changes.md+ Flag buildExamples Description: Build example executables Default: False@@ -45,7 +48,7 @@ Default: False Source-Repository this- Tag: 0.1.1.1+ Tag: 0.1.2 Type: darcs Location: https://hub.darcs.net/thielema/set-cover/ @@ -59,9 +62,9 @@ enummapset >=0.1 && <0.8, transformers >=0.2 && <0.7, array >=0.4 && <0.6,- containers >=0.4 && <0.8,+ containers >=0.4 && <0.9, non-empty >=0.2 && <0.4,- semigroups >=0.1 && <1.0,+ semigroups >=0.1 && <1, utility-ht >=0.0.12 && <0.1, prelude-compat ==0.*, base >=4 && <5@@ -91,9 +94,9 @@ If flag(llvm) Build-Depends: knead >=1.0 && <1.1,- llvm-dsl >=0.1 && <0.2,- llvm-extra >=0.11 && <0.12,- llvm-tf >=9.2 && <17.1,+ llvm-dsl >=0.2 && <0.3,+ llvm-extra >=0.12.1 && <0.14,+ llvm-tf >=9.2 && <21.1, tfp >=1.0 && <1.1, comfort-array >=0.3 && <0.6, storable-record >=0.0.5 && <0.1,@@ -113,9 +116,12 @@ set-cover, transformers >=0.2 && <0.7, enummapset,+ enumset, containers, array >=0.1 && <0.6, utility-ht,+ doctest-exitcode-stdio >=0.0 && <0.1,+ doctest-lib >=0.1.1 && <0.2, QuickCheck >=2.5 && <3.0, base Main-Is: Test.hs@@ -130,6 +136,9 @@ Mastermind.Test Mastermind.Guess Mastermind.Utility+ DocTest.GraphColoring.CutEnumSet, GraphColoring.CutEnumSet+ DocTest.GraphColoring.Solve, GraphColoring.Solve+ DocTest.Main Test.Knead Test.Utility @@ -182,7 +191,7 @@ If flag(buildExamples) Build-Depends: haha >=0.3.1 && <0.4,- random >=1.0 && <1.3,+ random >=1.0 && <1.4, transformers >=0.2 && <0.7, array >=0.1 && <0.6, containers@@ -276,7 +285,7 @@ Import: example If flag(buildExamples) Build-Depends:- random >=1.0 && <1.3,+ random >=1.0 && <1.4, transformers >=0.2 && <0.7, array >=0.1 && <0.6, enummapset,@@ -297,7 +306,7 @@ GHC-Prof-Options: -rtsopts -auto-all Build-Depends: haha >=0.3.1 && <0.4,- random >=1.0 && <1.3,+ random >=1.0 && <1.4, lazyio, transformers >=0.2 && <0.7, array >=0.1 && <0.6,@@ -320,7 +329,7 @@ Build-Depends: timeit, QuickCheck >=2.5 && <3.0,- random >=1.0 && <1.3,+ random >=1.0 && <1.4, transformers >=0.2 && <0.7, array >=0.1 && <0.6, enummapset,@@ -346,6 +355,7 @@ Build-Depends: pooled-io >=0.0 && <0.1, transformers,+ non-empty, containers Else Buildable: False@@ -353,3 +363,28 @@ Main-Is: ConwayPuzzle.hs Other-Modules: Utility++Executable graph-coloring+ Import: example+ If flag(buildExamples)+ Build-Depends:+ timeit >=0.9 && <3,+ enumset >=0.1 && <0.2,+ enummapset,+ containers+ Else+ Buildable: False+ Main-Is: GraphColoring.hs+ Other-Modules:+ GraphColoring.Solve+ GraphColoring.CutSet+ GraphColoring.CutEnumSet++Executable pythagoras-coloring+ Import: example+ If flag(buildExamples)+ Build-Depends:+ containers+ Else+ Buildable: False+ Main-Is: PythagorasColoring.hs
src/Math/SetCover/Exact.hs view
@@ -6,7 +6,7 @@ Assign(..), assign, bitVectorFromSetAssigns, intSetFromSetAssigns, partitions, search, step,- State(..), initState, updateState,+ State(..), initState, updateState, compressState, Set(..), Tree(..), decisionTree, completeTree, Choose(..),@@ -25,6 +25,7 @@ import qualified Data.List.Match as Match import qualified Data.List as List import qualified Data.Foldable as Fold+import Data.Traversable (Traversable) import Data.Function.HT (compose2) import Data.Maybe.HT (toMaybe) import Data.Tuple.HT (mapFst, mapSnd)@@ -174,6 +175,7 @@ label :: label, labeledSet :: set }+ deriving (Show) {- | Construction of a labeled set.@@ -190,11 +192,11 @@ The output of 'bitVectorFromSetAssigns' should go into the solver as is. -} bitVectorFromSetAssigns ::- (Ord a) =>- [Assign label (Set.Set a)] -> [Assign label (BitSet.Set Integer)]+ (Traversable f, Ord a) =>+ f (Assign label (Set.Set a)) -> f (Assign label (BitSet.Set Integer)) bitVectorFromSetAssigns asns = let bitVec = Fold.foldl' setBit 0 . mapIntFromSet asns- in map (fmap (BitSet.Set . bitVec)) asns+ in fmap (fmap (BitSet.Set . bitVec)) asns {- | Like 'bitVectorFromSetAssigns' but generates 'IntSet.IntSet'@@ -203,16 +205,19 @@ 'IntSet.IntSet' should usually be more efficient than 'Integer'. -} intSetFromSetAssigns ::- (Ord a) => [Assign label (Set.Set a)] -> [Assign label IntSet.IntSet]+ (Traversable f, Ord a) =>+ f (Assign label (Set.Set a)) -> f (Assign label IntSet.IntSet) intSetFromSetAssigns asns = let intSet = IntSet.fromList . Map.elems . mapIntFromSet asns- in map (fmap intSet) asns+ in fmap (fmap intSet) asns mapIntFromSet ::- (Ord a) => [Assign label (Set.Set a)] -> Set.Set a -> Map.Map a Int+ (Foldable f, Ord a) =>+ f (Assign label (Set.Set a)) -> Set.Set a -> Map.Map a Int mapIntFromSet asns = let mapToInt =- Map.fromList $ zip (Set.toList $ unions $ map labeledSet asns) [0..]+ Map.fromList $+ zip (Set.toList $ unions $ map labeledSet $ Fold.toList asns) [0..] in Map.intersection mapToInt . constMap () {- |@@ -261,6 +266,33 @@ availableSubsets s, freeElements = difference (freeElements s) attemptedSet, usedSubsets = attemptLabel : usedSubsets s+ }+++{- |+This is an optional low-level optimization.+It turns 'freeElements' into a contiguous 'IntSet' starting at 0.+In sparse sets this might reduce memory consumption+and number of logical operations.++The functional parameter enables you to choose the type of the source set.+If it is already an 'IntSet',+you would call @compressState IntSet.toList state@.+-}+compressState :: (Ord a) =>+ (set -> [a]) -> State label set -> State label IntSet.IntSet+compressState listFromSet state =+ let mapToInt =+ Map.fromList $ flip zip [0..] $ listFromSet $ freeElements state in+ State {+ availableSubsets =+ -- ToDo: could also be done with Map.intersection+ map (\(Assign lab set) ->+ Assign lab $+ IntSet.fromList $ map (mapToInt Map.!) $ listFromSet set) $+ availableSubsets state,+ freeElements = IntSet.fromList $ Map.elems mapToInt,+ usedSubsets = usedSubsets state }
src/Math/SetCover/Exact/Knead.hs view
@@ -33,8 +33,8 @@ import qualified Data.Array.Knead.Symbolic as Symb import qualified Data.Array.Knead.Symbolic.Slice as Slice import qualified Data.Array.Knead.Shape as Shape-import qualified Data.Array.Knead.Expression as Expr-import Data.Array.Knead.Expression ((.|.*))+import qualified LLVM.DSL.Expression as Expr+import LLVM.DSL.Expression ((.|.*)) import qualified Data.Array.Comfort.Shape as ComfortShape import qualified Data.Array.Comfort.Boxed as Array
src/Math/SetCover/Exact/Knead/Saturated.hs view
@@ -30,10 +30,10 @@ import qualified Data.Array.Comfort.Boxed as Array import Data.Array.Comfort.Boxed (Array) -import qualified LLVM.Extra.Multi.Vector as MultiVector-import qualified LLVM.Extra.Multi.Value.Vector as MultiValueVec-import qualified LLVM.Extra.Multi.Value as MultiValue-import LLVM.Extra.Multi.Value (atom)+import qualified LLVM.Extra.Nice.Vector as NiceVector+import qualified LLVM.Extra.Nice.Value.Vector as NiceValueVec+import qualified LLVM.Extra.Nice.Value as NiceValue+import LLVM.Extra.Nice.Value (atom) import qualified LLVM.Util.Intrinsic as Intr import qualified LLVM.Core as LLVM@@ -131,9 +131,9 @@ LLVM.Value Counters -> LLVM.Value Counters -> LLVM.CodeGenFunction r (LLVM.Value Counters) incSatGeneric x y =- (\(MultiValue.Cons z) -> z)+ (\(NiceValue.Cons z) -> z) <$>- Expr.unliftM2 incSat (MultiValue.Cons x) (MultiValue.Cons y)+ Expr.unliftM2 incSat (NiceValue.Cons x) (NiceValue.Cons y) incSatX86 :: Exp Counters -> Exp Counters -> Exp Counters incSatX86 =@@ -199,16 +199,16 @@ (pure (fst (availableSubsets s) Array.! k : usedSubsets s)) -mvvec :: MultiValue.T (LLVM.Vector n a) -> MultiVector.T n a-mvvec (MultiValue.Cons x) = MultiVector.Cons x+nvvec :: NiceValue.T (LLVM.Vector n a) -> NiceVector.T n a+nvvec (NiceValue.Cons x) = NiceVector.Cons x extract ::- (TypeNum.Positive n, MultiVector.C a) =>+ (TypeNum.Positive n, NiceVector.C a) => Exp CounterId -> Exp (LLVM.Vector n a) -> Exp a extract = Expr.liftM2- (\(MultiValue.Cons k) v ->- flip MultiVector.extract (mvvec v) =<< LLVM.zext k)+ (\(NiceValue.Cons k) v ->+ flip NiceVector.extract (nvvec v) =<< LLVM.zext k) extractBlock :: Exp CounterId -> Exp Block -> Exp Subblock extractBlock =@@ -230,12 +230,12 @@ argMin ::- (MultiValue.Select x, MultiValue.Select y, MultiValue.Comparison y) =>+ (NiceValue.Select x, NiceValue.Select y, NiceValue.Comparison y) => Exp (x,y) -> Exp (x,y) -> Exp (x,y) argMin xy0 xy1 = Expr.select (Expr.snd xy0 <* Expr.snd xy1) xy0 xy1 argMinimum ::- (Shape.C sh, Shape.Index sh ~ ix, MultiValue.Select ix) =>+ (Shape.C sh, Shape.Index sh ~ ix, NiceValue.Select ix) => Symb.Array sh Counter -> Exp ix argMinimum = Expr.fst . Symb.fold1All argMin . Symb.mapWithIndex Expr.zip @@ -248,7 +248,7 @@ argMinMasked ::- (MultiValue.Select x, MultiValue.Select y, MultiValue.Comparison y) =>+ (NiceValue.Select x, NiceValue.Select y, NiceValue.Comparison y) => Exp (Bool, (x,y)) -> Exp (Bool, (x,y)) -> Exp (Bool, (x,y)) argMinMasked xy0 xy1 = Expr.select (Expr.fst xy1)@@ -289,7 +289,7 @@ argMinVec :: (TypeNum.Positive n,- MultiVector.Select x, MultiVector.Select y, MultiVector.Comparison y) =>+ NiceVector.Select x, NiceVector.Select y, NiceVector.Comparison y) => Exp (LLVM.Vector n (x,y)) -> Exp (LLVM.Vector n (x,y)) -> Exp (LLVM.Vector n (x,y)) argMinVec xy0 xy1 =@@ -298,7 +298,7 @@ argMinMaskedVec :: (TypeNum.Positive n,- MultiVector.Select x, MultiVector.Select y, MultiVector.Comparison y) =>+ NiceVector.Select x, NiceVector.Select y, NiceVector.Comparison y) => Exp (LLVM.Vector n (Bool, (x,y))) -> Exp (LLVM.Vector n (Bool, (x,y))) -> Exp (LLVM.Vector n (Bool, (x,y))) argMinMaskedVec xy0 xy1 =@@ -340,29 +340,29 @@ Exp ((BlockId, CounterId), BitId) _keepMinimumMaskedVector = Expr.liftM- (fmap (MultiValue.fst . MultiValue.snd) .+ (fmap (NiceValue.fst . NiceValue.snd) . foldM (Expr.unliftM2 argMinMasked)- (MultiValue.zip (MultiValue.cons False) MultiValue.undef)- <=< MultiValueVec.dissect)+ (NiceValue.zip (NiceValue.cons False) NiceValue.undef)+ <=< NiceValueVec.dissect) . ExprVec.mapSnd (ExprVec.mapFst (ExprVec.mapFst (flip ExprVec.zip counterIds))) type IxVector n =- MultiValue.T (LLVM.Vector n+ NiceValue.T (LLVM.Vector n (Bool, (((BlockId, CounterId), BitId), Counter))) argMinMaskedVecHalf :: (TypeNum.Positive n, TypeNum.Positive n2, (n:+:n) ~ n2,- MultiVector.Select x, MultiVector.Select y, MultiVector.Comparison y) =>- MultiValue.T (LLVM.Vector n2 (Bool, (x, y))) ->- LLVM.CodeGenFunction r (MultiValue.T (LLVM.Vector n (Bool, (x, y))))+ NiceVector.Select x, NiceVector.Select y, NiceVector.Comparison y) =>+ NiceValue.T (LLVM.Vector n2 (Bool, (x, y))) ->+ LLVM.CodeGenFunction r (NiceValue.T (LLVM.Vector n (Bool, (x, y)))) argMinMaskedVecHalf x = Monad.liftJoin2 (Expr.unliftM2 argMinMaskedVec)- (MultiValueVec.take x)- (MultiValueVec.takeRev x)+ (NiceValueVec.take x)+ (NiceValueVec.takeRev x) keepMinimumMaskedCascade :: Exp (LLVM.Vector NumCounters (Bool, ((BlockId, BitId), Counter))) ->@@ -376,8 +376,8 @@ x4 <- argMinMaskedVecHalf x8 x2 <- argMinMaskedVecHalf x4 Monad.liftJoin2 (Expr.unliftM2 argMinMasked)- (MultiValueVec.extract (LLVM.valueOf 0) (x2 :: IxVector TypeNum.D2))- (MultiValueVec.extract (LLVM.valueOf 1) x2))+ (NiceValueVec.extract (LLVM.valueOf 0) (x2 :: IxVector TypeNum.D2))+ (NiceValueVec.extract (LLVM.valueOf 1) x2)) . ExprVec.mapSnd (ExprVec.mapFst (ExprVec.mapFst (flip ExprVec.zip counterIds)))
src/Math/SetCover/Exact/Knead/Symbolic.hs view
@@ -33,9 +33,9 @@ import qualified Data.Array.Comfort.Boxed as Array import Data.Array.Comfort.Boxed (Array) -import qualified LLVM.Extra.Multi.Value.Storable as Storable-import qualified LLVM.Extra.Multi.Value as MultiValue-import LLVM.Extra.Multi.Value (atom)+import qualified LLVM.Extra.Nice.Value.Storable as Storable+import qualified LLVM.Extra.Nice.Value as NiceValue+import LLVM.Extra.Nice.Value (atom) import qualified Data.Word as Word import qualified Data.Int as Int@@ -48,7 +48,7 @@ -class (MultiValue.Logic block) => BitSet block where+class (NiceValue.Logic block) => BitSet block where nullBlock :: Exp block -> Exp Bool blocksFromSets :: (Ord a) => [Set a] -> ([[block]], [block]) keepMinimumBit :: Exp block -> Exp block@@ -74,9 +74,9 @@ keepMinimumBit = keepMinimumBitPrim keepMinimumBitPrim ::- (MultiValue.Additive a, MultiValue.Logic a) => Exp a -> Exp a+ (NiceValue.Additive a, NiceValue.Logic a) => Exp a -> Exp a keepMinimumBitPrim =- Expr.liftM (\x -> MultiValue.and x =<< MultiValue.neg x)+ Expr.liftM (\x -> NiceValue.and x =<< NiceValue.neg x) @@ -92,7 +92,7 @@ type DigitDim = Shape.ZeroBased DigitId -addLow, addHigh :: MultiValue.Logic a => Exp a -> Exp a -> Exp a -> Exp a+addLow, addHigh :: NiceValue.Logic a => Exp a -> Exp a -> Exp a -> Exp a addLow a b c = a `xor` b `xor` c addHigh a b c = c.&.*(a.|.*b) .|.* a.&.*b @@ -108,7 +108,7 @@ (Symb.map Expr.tuple $ halfBags xs) -zbAtom :: Shape.ZeroBased (MultiValue.Atom a)+zbAtom :: Shape.ZeroBased (NiceValue.Atom a) zbAtom = Shape.ZeroBased atom halfBags ::@@ -132,7 +132,7 @@ Shape.ZeroBased (numDigits+1))) (Symb.shape xs)) -elseIfThen :: MultiValue.C a => Exp a -> Exp Bool -> Exp a -> Exp a+elseIfThen :: NiceValue.C a => Exp a -> Exp Bool -> Exp a -> Exp a elseIfThen y c x = Expr.ifThenElse c x y @@ -234,11 +234,11 @@ sumBags3 = liftA2 (<=<) sumLoop add3 -difference :: (MultiValue.Logic a) => Exp a -> Exp a -> Exp a+difference :: (NiceValue.Logic a) => Exp a -> Exp a -> Exp a difference x y = x .&.* Expr.complement y differenceWithRow ::- (Shape.C k, MultiValue.Logic block) =>+ (Shape.C k, NiceValue.Logic block) => Symb.Array BlockDim block -> Exp (Shape.Index k) -> Symb.Array (k,BlockDim) block -> Symb.Array BlockDim block differenceWithRow x k bag =@@ -249,7 +249,7 @@ disjoint x y = nullBlock $ x .&.* y getRow ::- (Shape.C k, MultiValue.C block) =>+ (Shape.C k, NiceValue.C block) => Exp (Shape.Index k) -> Symb.Array (k, BlockDim) block -> Symb.Array BlockDim block getRow k = Slice.apply (Slice.pickFst k)@@ -288,7 +288,7 @@ Render.run $ \k -> findIndices . disjointRows k collectRows ::- (MultiValue.C block) =>+ (NiceValue.C block) => Symb.Array SetDim SetId -> Symb.Array (SetDim,BlockDim) block -> Symb.Array (SetDim,BlockDim) block collectRows rows sets =
src/Math/SetCover/Exact/Knead/Vector.hs view
@@ -12,8 +12,8 @@ import qualified LLVM.DSL.Expression as Expr -import qualified LLVM.Extra.Multi.Value.Storable as Storable-import qualified LLVM.Extra.Multi.Value as MultiValue+import qualified LLVM.Extra.Nice.Value.Storable as Storable+import qualified LLVM.Extra.Nice.Value as NiceValue import qualified LLVM.Extra.Arithmetic as A import qualified LLVM.Core as LLVM@@ -60,13 +60,13 @@ let ptr64 = castPtr ptr in liftA2 Block (peekLE ptr64) (peekLE (advancePtr ptr64 1)) -instance MultiValue.C Block where+instance NiceValue.C Block where type Repr Block = LLVM.Value ByteVector- cons = MultiValue.consPrimitive . blockVector- undef = MultiValue.undefPrimitive- zero = MultiValue.zeroPrimitive- phi = MultiValue.phiPrimitive- addPhi = MultiValue.addPhiPrimitive+ cons = NiceValue.consPrimitive . blockVector+ undef = NiceValue.undefPrimitive+ zero = NiceValue.zeroPrimitive+ phi = NiceValue.phiPrimitive+ addPhi = NiceValue.addPhiPrimitive blockVector :: Block -> ByteVector blockVector (Block x0 x1) =@@ -77,13 +77,13 @@ in if k<split then getByte k x0 else getByte (k-split) x1) $ NonEmptyC.iterate (1+) 0 -instance MultiValue.Logic Block where- and = MultiValue.liftM2 LLVM.and; or = MultiValue.liftM2 LLVM.or- xor = MultiValue.liftM2 LLVM.xor; inv = MultiValue.liftM LLVM.inv+instance NiceValue.Logic Block where+ and = NiceValue.liftM2 LLVM.and; or = NiceValue.liftM2 LLVM.or+ xor = NiceValue.liftM2 LLVM.xor; inv = NiceValue.liftM LLVM.inv instance Storable.C Block where- load = fmap MultiValue.cast . Storable.load <=< castBlockPtr- store b = Storable.store (MultiValue.cast b) <=< castBlockPtr+ load = fmap NiceValue.cast . Storable.load <=< castBlockPtr+ store b = Storable.store (NiceValue.cast b) <=< castBlockPtr castBlockPtr :: LLVM.Value (Ptr Block) ->
+ test/DocTest/GraphColoring/CutEnumSet.hs view
@@ -0,0 +1,46 @@+-- Do not edit! Automatically created with doctest-extract from example/GraphColoring/CutEnumSet.hs+{-# LINE 17 "example/GraphColoring/CutEnumSet.hs" #-}++module DocTest.GraphColoring.CutEnumSet where++import GraphColoring.CutEnumSet+import qualified Test.DocTest.Driver as DocTest++{-# LINE 18 "example/GraphColoring/CutEnumSet.hs" #-}+import qualified Math.SetCover.Exact as ESC++import qualified Data.EnumSet as EnumSet+import qualified Data.List.Key as Key+import Data.Word (Word32)++import Control.Applicative (liftA2)++import qualified Test.QuickCheck as QC+import Test.QuickCheck ((===))++test :: DocTest.T ()+test = do+ DocTest.printPrefix "GraphColoring.CutEnumSet:111: "+{-# LINE 111 "example/GraphColoring/CutEnumSet.hs" #-}+ DocTest.property(+{-# LINE 111 "example/GraphColoring/CutEnumSet.hs" #-}+ + QC.forAll (fmap (\n -> take (1 + mod n 5) ['a'..]) QC.arbitrary) $ \set ->+ QC.forAll (QC.sublistOf set) $ \used ->+ let usedSet = EnumSet.fromList used in+ let genLabel =+ QC.oneof+ [fmap Left $ liftA2 (,) (QC.elements set) (QC.elements set),+ fmap Right $+ liftA2 (,) (QC.arbitrary :: QC.Gen Integer) (QC.elements set)]+ genAssignSet :: QC.Gen Word32+ genAssignSet = QC.arbitrary+ in+ QC.forAll (QC.listOf (liftA2 ESC.assign genLabel genAssignSet)) $ \assigns ->+ let normalize =+ Key.sort fst . map (\(ESC.Assign label aset) -> (label,aset)) in++ normalize (uniqueNewColors usedSet assigns)+ ===+ normalize (uniqueNewColorsStable usedSet assigns)+ )
+ test/DocTest/GraphColoring/Solve.hs view
@@ -0,0 +1,106 @@+-- Do not edit! Automatically created with doctest-extract from example/GraphColoring/Solve.hs+{-# LINE 16 "example/GraphColoring/Solve.hs" #-}++module DocTest.GraphColoring.Solve where++import GraphColoring.Solve+import Test.DocTest.Base+import qualified Test.DocTest.Driver as DocTest++{-# LINE 17 "example/GraphColoring/Solve.hs" #-}+import qualified GraphColoring.CutEnumSet as CutEnumSet+import qualified Math.SetCover.Exact as ESC++import qualified Data.Set as Set+import qualified Data.List.Key as Key+import Data.Either.HT (maybeRight)+import Data.Maybe (catMaybes, mapMaybe)++import qualified Test.QuickCheck as QC+import Test.QuickCheck ((===))++solve :: ([Char], [Edge Int]) -> [[(Int, Char)]]+solve =+ map (Key.sort fst . catMaybes) . ESC.search .+ ESC.compressState Set.toList . initialize assigns++test :: DocTest.T ()+test = do+ DocTest.printPrefix "GraphColoring.Solve:155: "+{-# LINE 155 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 155 "example/GraphColoring/Solve.hs" #-}+ solve example0+ )+ [ExpectedLine [LineChunk "[[(0,'a'),(1,'b'),(2,'a'),(3,'b'),(4,'a'),(5,'b')]]"]]+ DocTest.printPrefix "GraphColoring.Solve:162: "+{-# LINE 162 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 162 "example/GraphColoring/Solve.hs" #-}+ minimum $ solve example1+ )+ [ExpectedLine [LineChunk "[(0,'a'),(1,'b'),(2,'a'),(3,'b'),(4,'c')]"]]+ DocTest.printPrefix "GraphColoring.Solve:169: "+{-# LINE 169 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 169 "example/GraphColoring/Solve.hs" #-}+ solve example2+ )+ [ExpectedLine [LineChunk "[[(0,'a'),(1,'b'),(2,'c')]]"]]+ DocTest.printPrefix "GraphColoring.Solve:176: "+{-# LINE 176 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 176 "example/GraphColoring/Solve.hs" #-}+ solve example3+ )+ [ExpectedLine [LineChunk "[[(0,'a'),(1,'b'),(2,'c'),(3,'b')]]"]]+ DocTest.printPrefix "GraphColoring.Solve:183: "+{-# LINE 183 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 183 "example/GraphColoring/Solve.hs" #-}+ solve example4+ )+ [ExpectedLine [LineChunk "[]"]]+ DocTest.printPrefix "GraphColoring.Solve:191: "+{-# LINE 191 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 191 "example/GraphColoring/Solve.hs" #-}+ length $ ESC.search $ ESC.compressState Set.toList $ ESC.initState $ assigns ['a'..'e'] (complete 5)+ )+ [ExpectedLine [LineChunk "120"]]+ DocTest.printPrefix "GraphColoring.Solve:194: "+{-# LINE 194 "example/GraphColoring/Solve.hs" #-}+ DocTest.property(+{-# LINE 194 "example/GraphColoring/Solve.hs" #-}+ + QC.forAll (QC.choose (2,5)) $ \n ->+ QC.forAll (QC.choose (2,7)) $ \k ->++ (length $ ESC.search $ ESC.compressState Set.toList $+ ESC.initState $ assigns (take k ['a'..]) (complete n))+ ===+ product (take n [k,(k-1)..])+ )+ DocTest.printPrefix "GraphColoring.Solve:204: "+{-# LINE 204 "example/GraphColoring/Solve.hs" #-}+ DocTest.property(+{-# LINE 204 "example/GraphColoring/Solve.hs" #-}+ + QC.forAll (QC.choose (2,5)) $ \n ->+ QC.forAll (QC.choose (2,7)) $ \k ->++ (length $ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $ assignsEdgeColor (take k ['a'..]) (complete n))+ ===+ if n<=k then 1 else 0+ )+ DocTest.printPrefix "GraphColoring.Solve:218: "+{-# LINE 218 "example/GraphColoring/Solve.hs" #-}+ DocTest.example(+{-# LINE 218 "example/GraphColoring/Solve.hs" #-}+ + map (Key.sort fst . mapMaybe maybeRight) $+ CutEnumSet.search $ CutEnumSet.initState $+ ESC.intSetFromSetAssigns $ uncurry assignsEdgeColor example5+ )+ [ExpectedLine [LineChunk "[[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e'),(6,'f'),(7,'g'),(8,'h'),(9,'i'),(10,'j')]]"]]
+ test/DocTest/Main.hs view
@@ -0,0 +1,12 @@+-- Do not edit! Automatically created with doctest-extract.+module DocTest.Main where++import qualified DocTest.GraphColoring.Solve+import qualified DocTest.GraphColoring.CutEnumSet++import qualified Test.DocTest.Driver as DocTest++main :: DocTest.T ()+main = do+ DocTest.GraphColoring.Solve.test+ DocTest.GraphColoring.CutEnumSet.test
test/Test.hs view
@@ -1,5 +1,6 @@ module Main where +import qualified DocTest.Main as DocTest import qualified Mastermind.Test as Mastermind import qualified Test.Knead as TestKnead import Test.Utility@@ -12,6 +13,9 @@ import qualified Math.SetCover.Exact as ESC import qualified Math.SetCover.Queue as Queue +import qualified Control.Monad.Trans.Class as MT+import qualified Control.Monad.Trans.Reader as MR+import Control.Monad.IO.Class (liftIO) import Control.Monad (liftM2) import Control.Applicative ((<$>)) @@ -25,6 +29,7 @@ import Data.Eq.HT (equating) import Data.Tuple.HT (mapFst, mapSnd) +import qualified Test.DocTest.Driver as DocTest import qualified Test.QuickCheck as QC @@ -303,12 +308,13 @@ [] -quickCheck :: (Int, QC.Property) -> IO ()-quickCheck (count, prop) =- QC.quickCheckWith (QC.stdArgs {QC.maxSuccess = count}) prop- main :: IO () main =- mapM_ (\(msg,prop) -> putStr (msg++": ") >> quickCheck prop) $+ DocTest.run $ (>> DocTest.main) $ MT.lift $+ mapM_ (\(msg, (count, prop)) -> do+ liftIO $ putStr (msg++": ")+ MR.runReaderT+ (DocTest.property prop)+ (QC.stdArgs {QC.maxSuccess = count, QC.chatty=True})) $ tests ++ map (mapFst ("Mastermind."++)) Mastermind.tests