module Data.Mapping.DecisionSpec where

import Prelude hiding ((&&), (||), not, all)
import qualified Data.Map as M
import qualified Data.Set as S
import Test.Hspec
import Data.Algebra.Boolean ((&&), (||), not, all)
import Data.Mapping
import Data.Mapping.Decision
import Data.Mapping.Piecewise


boolAct ::    Ord a
           => Decision Bool OnBool a Bool
           -> [a]
           -> Bool
boolAct a s = act a (`S.member` S.fromList s)


spec :: Spec
spec = do

  let x = test "x"
  let y = test "y"

  describe "Basic tests of act" $ do

    it "x false on {}" $ do
      boolAct x []    `shouldBe` False
    it "x true on {x}" $ do
      boolAct x ["x"] `shouldBe` True

  describe "Basic tests of mmap" $ do

    it "not x true on {}" $ do
      boolAct (not x) []    `shouldBe` True
    it "not x false on {x}" $ do
      boolAct (not x) ["x"] `shouldBe` False

  describe "Basic tests of merge" $ do

    it "x && y true on {}" $ do
      boolAct (x && y) []         `shouldBe` False
    it "x && y true on {x}" $ do
      boolAct (x && y) ["x"]      `shouldBe` False
    it "x && y true on {y}" $ do
      boolAct (x && y) ["y"]      `shouldBe` False
    it "x && y true on {x,y}" $ do
      boolAct (x && y) ["x", "y"] `shouldBe` True

    it "x || y true on {}" $ do
      boolAct (x || y) []         `shouldBe` False
    it "x || y true on {x}" $ do
      boolAct (x || y) ["x"]      `shouldBe` True
    it "x || y true on {y}" $ do
      boolAct (x || y) ["y"]      `shouldBe` True
    it "x || y true on {x,y}" $ do
      boolAct (x || y) ["x", "y"] `shouldBe` True

    it "y && x true on {}" $ do
      boolAct (y && x) []         `shouldBe` False
    it "y && x true on {x}" $ do
      boolAct (y && x) ["x"]      `shouldBe` False
    it "y && x true on {y}" $ do
      boolAct (y && x) ["y"]      `shouldBe` False
    it "y && x true on {x,y}" $ do
      boolAct (y && x) ["x", "y"] `shouldBe` True

    it "y || x true on {}" $ do
      boolAct (y || x) []         `shouldBe` False
    it "y || x true on {x}" $ do
      boolAct (y || x) ["x"]      `shouldBe` True
    it "y || x true on {y}" $ do
      boolAct (y || x) ["y"]      `shouldBe` True
    it "y || x true on {x,y}" $ do
      boolAct (y || x) ["x", "y"] `shouldBe` True

  describe "Check of listTrue" $ do

    let x0y0 = M.fromList [("x", False), ("y", False)]
    let x0y1 = M.fromList [("x", False), ("y", True)]
    let x1y0 = M.fromList [("x", True), ("y", False)]
    let x1y1 = M.fromList [("x", True), ("y", True)]

    it "Should work on &&" $ do
      S.fromList (listTrue (S.fromList ["x", "y"]) (x && y))
        `shouldBe` S.fromList [x1y1]
    it "Should work on ||" $ do
      S.fromList (listTrue (S.fromList ["x", "y"]) (x || y))
        `shouldBe` S.fromList [x0y1, x1y0, x1y1]
    it "Should work on not (1)" $ do
      S.fromList (listTrue (S.fromList ["x", "y"]) (not x))
        `shouldBe` S.fromList [x0y0, x0y1]
    it "Should work on not (2)" $ do
      S.fromList (listTrue (S.fromList ["x", "y"]) (not y))
        `shouldBe` S.fromList [x0y0, x1y0]

  describe "Properties of independent sets in C_100" $ do

    let l2 = (100,1):[(n,n+1) | n <- [1..99]]
    let l3 = (99,100,1):(100,1,2):[(n,n+1,n+2) | n <- [1..98]]
    let independent = all (\(i,j) -> not (test i && test j)) l2
    let maximal = all (\(i,j,k) -> test i || test j || test k) l3
    let t = independent && maximal

    -- Mentioned in Knuth
    it "should have the right count" $ do
      numberTrue (1::Int) 100 t `shouldBe` 1630580875002

  describe "Decision trees for monomial divisibility" $ do

    let xy2 = M.fromList [("X", 1::Int), ("Y", 2)]
    let x2y = M.fromList [("X", 2), ("Y", 1)]
    let xyz = M.fromList [("X", 1), ("Y", 1), ("Z", 1)]
    let monomials = M.fromList [(xy2, 1::Int), (x2y, 2), (xyz, 3)]
    let f i b = if b then i else 0
    let d = M.foldlWithKey' (\t m i -> merge max t (mmap (f i) . buildAll $ fmap atLeast m)) (cst 0) monomials
    let mapAct m = act d (\a -> M.findWithDefault 0 a $ M.fromList m)

    it "should get the right monomial for w^2y^4" $ do
      mapAct [("W", 2), ("Y", 4)] `shouldBe` 0

    it "should get the right monomial for w^3xy^2" $ do
      mapAct [("W", 3), ("X", 1), ("Y", 2)] `shouldBe` 1

    it "should get the right monomial for w^2x^2y^2" $ do
      mapAct [("W", 2), ("X", 2), ("Y", 2)] `shouldBe` 2

    it "should get the right monomial for w^2x^2y^2z^2" $ do
      mapAct [("W", 2), ("X", 2), ("Y", 2), ("Z", 2)] `shouldBe` 3

    it "should calculate neighbours correctly" $ do
      neighbours d `shouldBe` S.fromList [(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)]
