packages feed

combinatorial-0.1: test/Test.hs

module Main (main) where

import qualified Combinatorics.Permutation.WithoutSomeFixpoints as PermWOFP
import qualified Combinatorics.Mastermind as Mastermind
import qualified Combinatorics.CardPairs as CardPairs
import qualified Combinatorics.Partitions as Parts
import qualified Combinatorics.BellNumbers as Bell
import qualified Combinatorics.Private as CombPriv
import qualified Combinatorics as Comb

import qualified Test.QuickCheck as QC
import Test.QuickCheck (Testable, quickCheck, )

import Control.Monad (liftM2, liftM3, )
import Control.Applicative ((<$>), )

import qualified Data.List.Match as Match
import qualified Data.List.Key as Key
import qualified Data.List as List
import qualified Data.Set as Set
import Data.Array ((!), )
import Data.Tuple.HT (uncurry3, )
import Data.List.HT (allEqual, isAscending, )
import Data.List (sort, nub, )
import Data.Eq.HT (equating, )



permuteSum :: [Int] -> Bool
permuteSum xs =
   sum (map sum (Comb.permute xs)) ==
   sum xs * Comb.factorial (length xs)

permute :: Ord a => [a] -> Bool
permute xs =
   allEqual $
   map (\p -> sort (p xs)) $
      Comb.permute :
      Comb.permuteFast :
      Comb.permuteShare :
      []

permuteAlt :: Eq a => [a] -> Bool
permuteAlt xs = CombPriv.permuteRec xs == CombPriv.permuteMSL xs


genPermuteRep :: Int -> QC.Gen [(Char, Int)]
genPermuteRep n = do
   xns <- QC.listOf $ liftM2 (,) QC.arbitrary $ QC.choose (0,n)
   return $ Match.take (takeWhile (<=n) $ scanl1 (+) $ map snd xns) xns

permuteRepM :: Eq a => [(a, Int)] -> Bool
permuteRepM xs = CombPriv.permuteRep xs == CombPriv.permuteRepM xs

permuteRepNub :: Eq a => [(a, Int)] -> Bool
permuteRepNub xs =
   let perms = Comb.permuteRep $ Key.nub fst xs
   in  perms == nub perms

permuteRepNubBig :: Ord a => [(a, Int)] -> Bool
permuteRepNubBig xs =
   let perms = Comb.permuteRep $ Key.nub fst xs
   in  List.sort perms == Set.toList (Set.fromList perms)

permuteRepMonotony :: Ord a => [(a, Int)] -> Bool
permuteRepMonotony = isAscending . Comb.permuteRep . Key.nub fst . sort

permuteRepChoose :: Int -> Int -> Bool
permuteRepChoose n k =
   Comb.choose n k == Comb.permuteRep [(False, n-k), (True, k)]

chooseLength :: Int -> Int -> Bool
chooseLength n k =
   all
      (\x  ->  n == length x  &&  k == length (filter id x))
      (Comb.choose n k)


genChoose :: QC.Gen (Int, Int)
genChoose = do
   n <- QC.choose (0,15)
   k <- QC.choose (-2,n)
   return (n,k)

choose :: Int -> Int -> Bool
choose n k =
   allEqual $
      CombPriv.chooseRec n k :
      CombPriv.chooseMSL n k :
      CombPriv.chooseMSL0 n k :
      []

genChooseIndex :: QC.Gen (Integer, Integer, Integer)
genChooseIndex = do
   n <- QC.choose (0,25)
   k <- QC.choose (0,n)
   i <- QC.choose (0, Comb.binomial n k - 1)
   return (n,k,i)

chooseUnrank :: Integer -> Integer -> Integer -> Bool
chooseUnrank n k i =
   CombPriv.chooseUnrankRec n k i  ==  CombPriv.chooseUnrankList n k i

chooseUnrankSequence :: Int -> Int -> Bool
chooseUnrankSequence n k =
   map (Comb.chooseUnrank n k) [0 .. Comb.binomial n k - 1]
     ==  Comb.choose n k

chooseRankUnrank :: Integer -> Integer -> Integer -> Bool
chooseRankUnrank n k i =
   Comb.chooseRank (Comb.chooseUnrank n k i)  ==  (n, k, i)

chooseUnrankRank :: [Bool] -> Bool
chooseUnrankRank bs =
   uncurry3 Comb.chooseUnrank
      (Comb.chooseRank bs :: (Integer, Integer, Integer))
     ==  bs



genVariate :: QC.Gen [Char]
genVariate = take 7 <$> QC.arbitrary

variate :: Eq a => Int -> [a] -> Bool
variate n xs =
   CombPriv.variateRec n xs == CombPriv.variateMSL n xs

variateRep :: Eq a => Int -> [a] -> Bool
variateRep n xs =
   CombPriv.variateRep n xs == CombPriv.variateRepM n xs

variatePermute :: Eq a => [a] -> Bool
variatePermute xs =
   Comb.variate (length xs) xs == Comb.permute xs

variatePermuteClip :: Eq a => Int -> [a] -> Bool
variatePermuteClip n xs =
   equating (take n) (Comb.variate (length xs) xs) (Comb.permute xs)



genTuples :: QC.Gen (Int, [Char])
genTuples = do
   xs <- take 16 <$> QC.arbitrary
   n <- QC.choose (-1, length xs + 1)
   return (n,xs)

tuples :: Eq a => Int -> [a] -> Bool
tuples n xs =
   allEqual $
      CombPriv.tuplesRec n xs :
      CombPriv.tuplesRec0 n xs :
      CombPriv.tuplesMSL n xs :
      CombPriv.tuplesMSL0 n xs :
      []


_setPartitionsMonotony :: Ord a => Int -> [a] -> Bool
_setPartitionsMonotony k =
   isAscending . Comb.setPartitions k . nub . sort

rectificationsMonotony :: Ord a => Int -> [a] -> Bool
rectificationsMonotony k =
   isAscending . Comb.rectifications k . nub . sort



factorial :: [Char] -> Bool
factorial xs =
   length (Comb.permute xs) == Comb.factorial (length xs)


binomial :: [Char] -> Int -> Bool
binomial xs k =
   length (Comb.tuples k xs) == Comb.binomial (length xs) k


genBinomial :: QC.Gen (Integer, Integer)
genBinomial = do
   n <- QC.choose (0,100)
   k <- QC.choose (0,n)
   return (n,k)

binomialFactorial :: Integer -> Integer -> Bool
binomialFactorial n k =
   let (q, r) =
         divMod
            (Comb.factorial n)
            (Comb.factorial k * Comb.factorial (n-k))
   in  r == 0 && Comb.binomial n k == q


binomialChoose :: Int -> Int -> Bool
binomialChoose n k =
   length (Comb.choose n k) == Comb.binomial n k

multinomialPermuteRep :: [(Char,Int)] -> Bool
multinomialPermuteRep xs =
   length (Comb.permuteRep xs) == Comb.multinomial (map snd xs)

multinomialCommutative :: [Integer] -> Bool
multinomialCommutative xs =
   Comb.multinomial xs == Comb.multinomial (sort xs)

setPartitionNumbers :: Int -> [Int] -> Bool
setPartitionNumbers k xs =
   length (Comb.setPartitions k xs) ==
   (Comb.setPartitionNumbers !! length xs ++ repeat 0) !! k

rectificationNumbers :: Int -> [Int] -> Bool
rectificationNumbers k xs =
   length (Comb.rectifications k xs) ==
   (Comb.setPartitionNumbers !! k ++ repeat 0) !! length xs


surjectiveMappingNumber :: Int -> Bool
surjectiveMappingNumber =
   equalFuncList2 Comb.surjectiveMappingNumber Comb.surjectiveMappingNumbers

surjectiveMappingNumbers :: Int -> Bool
surjectiveMappingNumbers n =
   allEqual $ map (take n) $ (
      CombPriv.surjectiveMappingNumbersPS :
      CombPriv.surjectiveMappingNumbersStirling :
      [] :: [[[Integer]]])


equalFuncList :: (Integer -> Integer) -> [Integer] -> Int -> Bool
equalFuncList f xs n =
   equating (take n) xs (map f $ iterate (1+) 0)

factorials :: Int -> Bool
factorials = equalFuncList Comb.factorial Comb.factorials

equalFuncList2 :: (Integer -> Integer -> Integer) -> [[Integer]] -> Int -> Bool
equalFuncList2 f xs n =
   equating (take n) xs (zipWith (map . f) [0..] $ tail $ List.inits [0..])

binomials :: Int -> Bool
binomials = equalFuncList2 Comb.binomial Comb.binomials

catalanNumbers :: Int -> Bool
catalanNumbers = equalFuncList Comb.catalanNumber Comb.catalanNumbers

fibonacciNumbers :: Int -> Bool
fibonacciNumbers = equalFuncList Comb.fibonacciNumber Comb.fibonacciNumbers

derangementNumber :: Int -> Bool
derangementNumber = equalFuncList Comb.derangementNumber Comb.derangementNumbers

derangementNumbers :: Int -> Bool
derangementNumbers n =
   allEqual $ map (take n) $ (
      CombPriv.derangementNumbersPS0 :
      CombPriv.derangementNumbersPS1 :
      CombPriv.derangementNumbersInclExcl :
      [] :: [[Integer]])


bellSeries :: Int -> Bool
bellSeries =
   equalFuncList
      (\k -> round (Bell.bellSeries (fromInteger k) :: Double))
      (Bell.bellRec :: [Integer])


genPermutationWOFP :: QC.Gen (Int, String)
genPermutationWOFP = do
   xs <- take 6 . nub <$> QC.arbitrary
   k <- QC.choose (0, length xs)
   return (k,xs)

permutationWOFP :: Int -> String -> Bool
permutationWOFP k xs =
   PermWOFP.numbers !! length xs !! k == length (PermWOFP.enumerate k xs)

permutationWOFPFactorial :: Int -> Bool
permutationWOFPFactorial k =
   Comb.factorial (toInteger k) == PermWOFP.numbers !! k !! 0

permutationWOFPDerangement :: Int -> Bool
permutationWOFPDerangement k =
   Comb.derangementNumber (toInteger k) == PermWOFP.numbers !! k !! k


cardPairs1 :: Bool
cardPairs1 =
   case CardPairs.testCardsBorderDynamic of
      (x,y,z)  ->  x == y  &&  y == z

genCardCount :: QC.Gen (CardPairs.CardCount Int)
genCardCount =
   liftM3 CardPairs.CardCount
      (QC.choose (0,5)) (QC.choose (0,5)) (QC.choose (0,5))

cardPairs :: CardPairs.CardCount Int -> Bool
cardPairs cc =
   let x = CardPairs.possibilitiesCardsBorderNaive cc
       y = CardPairs.possibilitiesCardsBorderDynamic cc ! cc
       z = CardPairs.possibilitiesCardsBorder2Dynamic cc ! cc
   in  x == y  &&  y == z


genMastermindDistinct :: QC.Gen (Int, Int, Int, Int)
genMastermindDistinct = do
   n <- QC.choose (0,12)
   k <- QC.choose (0, min 5 n)
   b <- QC.choose (0,k)
   w <- QC.choose (0,k-b)
   return (n,k,b,w)

mastermindDistinct :: Int -> Int -> Int -> Int -> Bool
mastermindDistinct n k b w =
   let alphabet = take n ['a'..]
       code = take k alphabet
   in  Mastermind.numberDistinct n k b w ==
       (toInteger $ length $
        filter ((Mastermind.Eval b w ==) . Mastermind.evaluate code) $
        Comb.variate k alphabet)



testUnit :: Testable prop => String -> prop -> IO ()
testUnit label p = putStr (label++": ") >> quickCheck p

main :: IO ()
main =
   sequence_ $
      testUnit "permutation sums"
         (QC.forAll (take 6 <$> QC.arbitrary) permuteSum) :
      testUnit "permutations"
         (QC.forAll (take 6 <$> QC.arbitrary :: QC.Gen [Int]) permute) :
      testUnit "permuteAlt"
         (QC.forAll (take 6 <$> QC.arbitrary :: QC.Gen [Int]) permuteAlt) :
      testUnit "permuteRepM"
         (QC.forAll (genPermuteRep 10) permuteRepM) :
      testUnit "permuteRepNub"
         (QC.forAll (genPermuteRep  7) permuteRepNub) :
      testUnit "permuteRepNubBig"
         (QC.forAll (genPermuteRep 10) permuteRepNubBig) :
      testUnit "permuteRepMonotony"
         (QC.forAll (genPermuteRep 10) permuteRepMonotony) :
      testUnit "permuteRepChoose"
         (QC.forAll (QC.choose (0,10)) permuteRepChoose) :
      testUnit "choose"
         (QC.forAll genChoose (uncurry choose)) :
      testUnit "chooseLength"
         (QC.forAll (QC.choose (0,10)) chooseLength) :
      testUnit "chooseUnrank"
         (QC.forAll genChooseIndex $ uncurry3 chooseUnrank) :
      testUnit "chooseUnrankSequence"
         (QC.forAll (QC.choose (0,10)) chooseUnrankSequence) :
      testUnit "chooseRankUnrank"
         (QC.forAll genChooseIndex $ uncurry3 chooseRankUnrank) :
      testUnit "chooseUnrankRank" chooseUnrankRank :
      testUnit "variation without repetitions with list monad"
         (QC.forAll (QC.choose (-1,7)) $ \n ->
          QC.forAll genVariate $ variate n) :
      testUnit "variation with repetitions with list monad"
         (QC.forAll (QC.choose (-1,7)) $ \n ->
          QC.forAll genVariate $ variateRep n) :
      testUnit "variatePermute" (QC.forAll genVariate variatePermute) :
      testUnit "tuples" (QC.forAll genTuples (uncurry tuples)) :
      testUnit "permute expressed by variate"
         (variatePermuteClip 1000 :: String -> Bool) :
      testUnit "binomial vs. choose"
         (QC.forAll (QC.choose (0,12)) binomialChoose) :
      testUnit "multinomial vs. permutation with repetitions"
         (QC.forAll (genPermuteRep 10) multinomialPermuteRep) :
      testUnit "multinomial commutative"
         (QC.forAll (QC.listOf $ QC.choose (0,300)) multinomialCommutative) :
      testUnit "factorial vs. permute"
         (QC.forAll (take 8 <$> QC.arbitrary) factorial) :
      testUnit "binomial vs. tuples"
         (QC.forAll (take 16 <$> QC.arbitrary) binomial) :
      testUnit "binomial by factorial"
         (QC.forAll genBinomial $ uncurry binomialFactorial) :
      testUnit "factorial vs. factorials" (factorials 1000) :
      testUnit "binomial vs. binomials" (binomials 100) :
      testUnit "catalan numbers" (catalanNumbers 1000) :
      testUnit "fibonacci numbers" (fibonacciNumbers 10000) :
      testUnit "derangement number" (derangementNumber 1000) :
      testUnit "derangement numbers" (derangementNumbers 1000) :
      testUnit "set partition numbers"
         (QC.forAll (QC.choose (0,10000)) $ \n ->
          QC.forAll (take 7 <$> QC.arbitrary) $ setPartitionNumbers n) :
      testUnit "rectification numbers"
         (QC.forAll (QC.choose (0,7)) $ \n xs -> rectificationNumbers n xs) :
      testUnit "rectification montony"
         (QC.forAll (QC.choose (0,7)) $ \n xs ->
            rectificationsMonotony n (xs::[Int])) :
      testUnit "surjective mapping number" (surjectiveMappingNumber 20) :
      testUnit "surjective mapping numbers" (surjectiveMappingNumbers 20) :
      testUnit "bell series" (bellSeries 20) :
      testUnit "permutation without some fixpoints"
         (QC.forAll genPermutationWOFP $ uncurry permutationWOFP) :
      testUnit "permutation without some fixpoints vs. factorial"
         (QC.forAll (QC.choose (0,100)) permutationWOFPFactorial) :
      testUnit "permutation without some fixpoints vs. derangement"
         (QC.forAll (QC.choose (0,100)) permutationWOFPDerangement) :
      testUnit "partitions infinite linear factors"
         (QC.forAll (QC.choose (0,100)) Parts.propInfProdLinearFactors) :
      testUnit "partitions pentagonal power series"
         (Parts.propPentagonalPowerSeries 1000) :
      testUnit "partitions positive pentagonal numbers"
         (Parts.propPentagonalsDifP 10000) :
      testUnit "partitions negative pentagonal numbers"
         (Parts.propPentagonalsDifN 10000) :
      testUnit "partitions"
         (QC.forAll (QC.choose (1,10)) $ \k ->
          QC.forAll (QC.choose (0,50)) $ \n -> Parts.propPartitions k n) :
      testUnit "partitions count" (Parts.propNumPartitions 30) :
      testUnit "card pairs" cardPairs1 :
      testUnit "card pairs many" (QC.forAll genCardCount cardPairs) :
      testUnit "mastermind with distinct symbols"
         (QC.forAll genMastermindDistinct $ \(n,k,b,w) ->
            mastermindDistinct n k b w) :
      []