packages feed

set-cover-0.1.2: example/PythagorasColoring.hs

{- |
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