packages feed

multisets-0.1.0.0: test/Test/Combining.hs

module Test.Combining (
    tests,
) where

import Data.Foldable
import qualified Data.MultiSet.Natural as MS
import Numeric.Natural
import Test.Gen
import Test.Tasty
import Test.Tasty.QuickCheck

tests :: TestTree
tests =
    testGroup
        "combining"
        [ testProperty "union/empty" prop_unionEmpty
        , testProperty "union/commutative" prop_unionCommutative
        , testProperty "union/associative" prop_unionAssociative
        , testProperty "union/multiplicity" prop_unionMultiplicity
        , testProperty "union/size" prop_unionSize
        , testProperty "unions/fold" prop_unionsFold
        , testProperty "difference/empty" prop_differenceEmpty
        , testProperty "difference/self" prop_differenceSelf
        , testProperty "difference/subset" prop_differenceSubset
        , testProperty "difference/multiplicity" prop_differenceMultiplicity
        , testProperty "difference/undo union" prop_differenceUndoUnion
        , testProperty "symmetricDifference/empty" prop_symmetricDifferenceEmpty
        , testProperty "symmetricDifference/self" prop_symmetricDifferenceSelf
        , testProperty "symmetricDifference/commutative" prop_symmetricDifferenceCommutative
        , testProperty "symmetricDifference/multiplicity" prop_symmetricDifferenceMultiplicity
        , testProperty "symmetricDifference/differences" prop_symmetricDifferenceDifferences
        , testProperty "intersection/self" prop_intersectionSelf
        , testProperty "intersection/commutative" prop_intersectionCommutative
        , testProperty "intersection/associative" prop_intersectionAssociative
        , testProperty "intersection/subsets" prop_intersectionSubsets
        , testProperty "intersection/multiplicity" prop_intersectionMultiplicity
        , testProperty "intersections/multiplicity" prop_intersectionsMultiplicity
        , testProperty "intersections/subsets" prop_intersectionsSubsets
        , testProperty "maxUnion/empty" prop_maxUnionEmpty
        , testProperty "maxUnion/self" prop_maxUnionSelf
        , testProperty "maxUnion/commutative" prop_maxUnionCommutative
        , testProperty "maxUnion/associative" prop_maxUnionAssociative
        , testProperty "maxUnion/multiplicity" prop_maxUnionMultiplicity
        , testProperty "cartesianProduct/empty left" prop_cartesianProductEmptyLeft
        , testProperty "cartesianProduct/empty right" prop_cartesianProductEmptyRight
        , testProperty "cartesianProduct/multiplicity" prop_cartesianProductMultiplicity
        , testProperty "cartesianProduct/size" prop_cartesianProductSize
        , testProperty "cartesianProduct/distinctSize" prop_cartesianProductDistinctSize
        , testProperty "difference+intersection decomposition" prop_differenceIntersectionDecomposition
        ]

prop_unionEmpty :: AMS -> Property
prop_unionEmpty (AMS xs) = conjoin [MS.union MS.empty xs === xs, MS.union xs MS.empty === xs]

prop_unionCommutative :: AMS -> AMS -> Property
prop_unionCommutative (AMS xs) (AMS ys) = MS.union xs ys === MS.union ys xs

prop_unionAssociative :: AMS -> AMS -> AMS -> Property
prop_unionAssociative (AMS xs) (AMS ys) (AMS zs) =
    MS.union xs (MS.union ys zs) === MS.union (MS.union xs ys) zs

prop_unionMultiplicity :: AMSWithKey2 -> Property
prop_unionMultiplicity (AMSWithKey2 x xs ys) =
    MS.multiplicity x (MS.union xs ys) === MS.multiplicity x xs + MS.multiplicity x ys

prop_unionSize :: AMS -> AMS -> Property
prop_unionSize (AMS xs) (AMS ys) = MS.size (MS.union xs ys) === MS.size xs + MS.size ys

prop_unionsFold :: [AMS] -> Property
prop_unionsFold xss = MS.unions xs === foldr MS.union MS.empty xs
  where
    xs = getAMS <$> xss

prop_differenceEmpty :: AMS -> Property
prop_differenceEmpty (AMS xs) = MS.difference xs MS.empty === xs

prop_differenceSelf :: AMS -> Property
prop_differenceSelf (AMS xs) = MS.difference xs xs === MS.empty

prop_differenceSubset :: AMS -> AMS -> Property
prop_differenceSubset (AMS xs) (AMS ys) = property $ MS.difference xs ys `MS.isSubsetOf` xs

prop_differenceMultiplicity :: AMSWithKey2 -> Property
prop_differenceMultiplicity (AMSWithKey2 x xs ys) =
    MS.multiplicity x (MS.difference xs ys) === MS.multiplicity x xs `monus` MS.multiplicity x ys

prop_differenceUndoUnion :: AMS -> AMS -> Property
prop_differenceUndoUnion (AMS xs) (AMS ys) = MS.difference (MS.union xs ys) ys === xs

prop_symmetricDifferenceEmpty :: AMS -> Property
prop_symmetricDifferenceEmpty (AMS xs) =
    conjoin [MS.symmetricDifference xs MS.empty === xs, MS.symmetricDifference MS.empty xs === xs]

prop_symmetricDifferenceSelf :: AMS -> Property
prop_symmetricDifferenceSelf (AMS xs) = MS.symmetricDifference xs xs === MS.empty

prop_symmetricDifferenceCommutative :: AMS -> AMS -> Property
prop_symmetricDifferenceCommutative (AMS xs) (AMS ys) =
    MS.symmetricDifference xs ys === MS.symmetricDifference ys xs

prop_symmetricDifferenceMultiplicity :: AMSWithKey2 -> Property
prop_symmetricDifferenceMultiplicity (AMSWithKey2 x xs ys) =
    MS.multiplicity x (MS.symmetricDifference xs ys)
        === distance (MS.multiplicity x xs) (MS.multiplicity x ys)

prop_symmetricDifferenceDifferences :: AMS -> AMS -> Property
prop_symmetricDifferenceDifferences (AMS xs) (AMS ys) =
    MS.symmetricDifference xs ys === MS.union (MS.difference xs ys) (MS.difference ys xs)

prop_intersectionSelf :: AMS -> Property
prop_intersectionSelf (AMS xs) = MS.intersection xs xs === xs

prop_intersectionCommutative :: AMS -> AMS -> Property
prop_intersectionCommutative (AMS xs) (AMS ys) = MS.intersection xs ys === MS.intersection ys xs

prop_intersectionAssociative :: AMS -> AMS -> AMS -> Property
prop_intersectionAssociative (AMS xs) (AMS ys) (AMS zs) =
    MS.intersection xs (MS.intersection ys zs) === MS.intersection (MS.intersection xs ys) zs

prop_intersectionSubsets :: AMS -> AMS -> Property
prop_intersectionSubsets (AMS xs) (AMS ys) =
    conjoin
        [ property $ MS.intersection xs ys `MS.isSubsetOf` xs
        , property $ MS.intersection xs ys `MS.isSubsetOf` ys
        ]

prop_intersectionMultiplicity :: AMSWithKey2 -> Property
prop_intersectionMultiplicity (AMSWithKey2 x xs ys) =
    MS.multiplicity x (MS.intersection xs ys)
        === min (MS.multiplicity x xs) (MS.multiplicity x ys)

prop_intersectionsMultiplicity :: AMSsWithKey -> Property
prop_intersectionsMultiplicity (AMSsWithKey x xss) =
    MS.multiplicity x (MS.intersections xss) === minimum (MS.multiplicity x <$> xss)

prop_intersectionsSubsets :: AMSsWithKey -> Property
prop_intersectionsSubsets (AMSsWithKey _ xss) =
    conjoin $ (res `MS.isSubsetOf`) <$> toList xss
  where
    res = MS.intersections xss

prop_maxUnionEmpty :: AMS -> Property
prop_maxUnionEmpty (AMS xs) =
    conjoin [MS.maxUnion MS.empty xs === xs, MS.maxUnion xs MS.empty === xs]

prop_maxUnionSelf :: AMS -> Property
prop_maxUnionSelf (AMS xs) = MS.maxUnion xs xs === xs

prop_maxUnionCommutative :: AMS -> AMS -> Property
prop_maxUnionCommutative (AMS xs) (AMS ys) = MS.maxUnion xs ys === MS.maxUnion ys xs

prop_maxUnionAssociative :: AMS -> AMS -> AMS -> Property
prop_maxUnionAssociative (AMS xs) (AMS ys) (AMS zs) =
    MS.maxUnion xs (MS.maxUnion ys zs) === MS.maxUnion (MS.maxUnion xs ys) zs

prop_maxUnionMultiplicity :: AMSWithKey2 -> Property
prop_maxUnionMultiplicity (AMSWithKey2 x xs ys) =
    MS.multiplicity x (MS.maxUnion xs ys) === max (MS.multiplicity x xs) (MS.multiplicity x ys)

prop_cartesianProductEmptyLeft :: AMS -> Property
prop_cartesianProductEmptyLeft (AMS ys) =
    MS.cartesianProduct (MS.empty :: MS.MultiSet Int) ys === MS.empty

prop_cartesianProductEmptyRight :: AMS -> Property
prop_cartesianProductEmptyRight (AMS xs) =
    MS.cartesianProduct xs (MS.empty :: MS.MultiSet Int) === MS.empty

prop_cartesianProductMultiplicity :: AMSWithKey -> AMSWithKey -> Property
prop_cartesianProductMultiplicity (AMSWithKey x xs) (AMSWithKey y ys) =
    MS.multiplicity (x, y) (MS.cartesianProduct xs ys)
        === MS.multiplicity x xs * MS.multiplicity y ys

prop_cartesianProductSize :: AMS -> AMS -> Property
prop_cartesianProductSize (AMS xs) (AMS ys) =
    MS.size (MS.cartesianProduct xs ys) === MS.size xs * MS.size ys

prop_cartesianProductDistinctSize :: AMS -> AMS -> Property
prop_cartesianProductDistinctSize (AMS xs) (AMS ys) =
    MS.distinctSize (MS.cartesianProduct xs ys) === MS.distinctSize xs * MS.distinctSize ys

prop_differenceIntersectionDecomposition :: AMS -> AMS -> Property
prop_differenceIntersectionDecomposition (AMS xs) (AMS ys) =
    MS.union (MS.difference xs ys) (MS.intersection xs ys) === xs

monus :: Natural -> Natural -> Natural
monus x y
    | x >= y = x - y
    | otherwise = 0

distance :: Natural -> Natural -> Natural
distance x y = monus x y + monus y x