packages feed

multisets-0.1.0.0: test/Test/Valid.hs

{-# LANGUAGE TypeApplications #-}

module Test.Valid (
    tests,
) where

import Data.Bifunctor
import Data.Coerce
import Data.Functor.Identity
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Map as M
import qualified Data.MultiSet.Natural as MS
import qualified Data.Set as S
import Test.Gen
import Test.Tasty
import Test.Tasty.QuickCheck

valid :: (Ord a) => MS.MultiSet a -> Bool
valid = (&&) <$> M.valid . MS.toMap <*> all (> 0) . MS.toMap

tests :: TestTree
tests =
    testGroup
        "validity"
        [ constructs @() @Int "empty" $
            const MS.empty
        , constructs @Int
            "singleton"
            MS.singleton
        , constructs @(Int, LNat) "singletonMany" $
            \(x, LNat n) -> MS.singletonMany x n
        , constructs @[(Int, LNat)] "fromMultiplicityList" $
            MS.fromMultiplicityList . map (second getLNat)
        , constructs @[Int]
            "fromList"
            MS.fromList
        , constructs @(S.Set Int)
            "fromSet"
            MS.fromSet
        , constructs @(M.Map Int LNat) "fromMap" $
            MS.fromMap . fmap getLNat
        , preserves "deleteMin" MS.deleteMin
        , preserves "deleteMax" MS.deleteMax
        , preserves "deleteMinAll" MS.deleteMinAll
        , preserves "deleteMaxAll" MS.deleteMaxAll
        , preserves1 "insert" MS.insert
        , preserves1 "delete" MS.delete
        , preserves1 "deleteAll" MS.deleteAll
        , preserves1 "union" $
            \(AMS xs) -> (`MS.union` xs)
        , preserves1 "unions" $
            \xss ms -> MS.unions $ ms : (getAMS <$> xss)
        , preserves1 "difference" $
            \(AMS xs) -> (`MS.difference` xs)
        , preserves1 "symmetricDifference" $
            \(AMS xs) -> (`MS.symmetricDifference` xs)
        , preserves1 "intersection" $
            \(AMS xs) -> (`MS.intersection` xs)
        , preserves1 "intersections" $
            \xss ms -> MS.intersections $ ms :| (getAMS <$> xss)
        , preserves1 "maxUnion" $
            \(AMS xs) -> (`MS.maxUnion` xs)
        , preserves1 @AMS "cartesianProduct" $
            \(AMS xs) -> (`MS.cartesianProduct` xs)
        , preserves2 "insertMany" $
            \x (LNat n) -> MS.insertMany x n
        , preserves2 "deleteMany" $
            \x (LNat n) -> MS.deleteMany x n
        , preserves2 "setMultiplicity" $
            \x (LNat n) -> MS.setMultiplicity x n
        , preservesFun "filter" MS.filter
        , preservesFun @(Int, Natural') @Bool "filterWithMultiplicity" $
            coerce (MS.filterWithMultiplicity . curry)
        , preservesFun "filterA" $
            \f -> runIdentity . MS.filterA (Identity . f)
        , preservesFun @(Int, Natural') @Bool "filterWithMultiplicityA" $
            coerce $
                \f -> runIdentity . MS.filterWithMultiplicityA ((Identity .) . curry f)
        , preservesFun @Int @Int "map" MS.map
        , preservesFun @(Int, Natural') @(Char, Natural') @Char "mapWithMultiplicity" $
            coerce (MS.mapWithMultiplicity . curry)
        , preservesFun @Natural' @Natural' "mapMultiplicities" $
            coerce MS.mapMultiplicities
        , preservesFun @Int @(Maybe Int) "mapMaybe" MS.mapMaybe
        , preservesFun @(Int, Natural') @(Maybe (Integer, Natural')) @Integer "mapMaybeWithMultiplicity" $
            coerce (MS.mapMaybeWithMultiplicity . curry)
        , preservesFun @Int @AMS "concatMap" $
            \f -> MS.concatMap (getAMS . f)
        , preservesFun @Int @String "traverse" $
            \f -> runIdentity . MS.traverse (Identity . f)
        , preservesFun @Int @(Maybe Float) "traverseMaybe" $
            \f -> runIdentity . MS.traverseMaybe (Identity . f)
        , preservesFun @(Int, Natural') @(Bool, Natural') @Bool "traverseWithMultiplicity" $
            coerce $
                \f -> runIdentity . MS.traverseWithMultiplicity (\x n -> Identity $ f (x, n))
        , preservesFun @(Int, Natural') @(Maybe (Word, Natural')) @Word "traverseMaybeWithMultiplicity" $
            coerce $
                \f -> runIdentity . MS.traverseMaybeWithMultiplicity (\x n -> Identity $ f (x, n))
        , preservesAllFun "partition" $
            \f -> pair . MS.partition f
        , preservesAllFun @(Int, Natural') @Bool @[] "partitionWithMultiplicity" $
            coerce $
                \f -> pair . MS.partitionWithMultiplicity (curry f)
        , preservesAllFun "partitionA" $
            \f -> pair . runIdentity . MS.partitionA (Identity . f)
        , preservesAllFun @(Int, Natural') @Bool @[] "partitionWithMultiplicityA" $
            coerce $ \f ->
                pair
                    . runIdentity
                    . MS.partitionWithMultiplicityA ((Identity .) . curry f)
        , preservesAll "minView" $
            fmap snd . MS.minView
        , preservesAll "maxView" $
            fmap snd . MS.maxView
        , preservesAll "minViewWithMultiplicity" $
            fmap snd . MS.minViewWithMultiplicity
        , preservesAll "maxViewWithMultiplicity" $
            fmap snd . MS.maxViewWithMultiplicity
        , preservesAll1 "split" $
            \x ms -> let (lt, _, gt) = MS.split x ms in [lt, gt]
        , preservesFun1 @Natural' @Natural' @Int "alterMultiplicity" $
            coerce MS.alterMultiplicity
        , preservesFun1 @Natural' @Natural' @Int "alterMultiplicityF" $
            coerce $
                \f x -> runIdentity . MS.alterMultiplicityF (Identity . f) x
        ]

constructs :: (Arbitrary a, Show a, Ord b) => String -> (a -> MS.MultiSet b) -> TestTree
constructs name = testProperty name . (valid .)

preserves :: (Ord b) => String -> (MS.MultiSet Int -> MS.MultiSet b) -> TestTree
preserves name f = testProperty name $ \(AMS ms) -> valid $ f ms

preserves1 ::
    (Arbitrary a, Show a, Ord b) => String -> (a -> MS.MultiSet Int -> MS.MultiSet b) -> TestTree
preserves1 name f = testProperty name $ \x (AMS ms) -> valid $ f x ms

preserves2 ::
    (Arbitrary a, Show a, Arbitrary b, Show b, Ord c) =>
    String -> (a -> b -> MS.MultiSet Int -> MS.MultiSet c) -> TestTree
preserves2 name f = testProperty name $ \x y (AMS ms) -> valid $ f x y ms

preservesFun ::
    ( Function a
    , CoArbitrary a
    , Show a
    , Arbitrary b
    , Show b
    , Ord c
    ) =>
    String ->
    ((a -> b) -> MS.MultiSet Int -> MS.MultiSet c) ->
    TestTree
preservesFun name f = testProperty name $ \fun (AMS ms) -> valid $ f (applyFun fun) ms

preservesAllFun ::
    ( Function a
    , CoArbitrary a
    , Show a
    , Arbitrary b
    , Show b
    , Foldable t
    , Ord c
    ) =>
    String ->
    ((a -> b) -> MS.MultiSet Int -> t (MS.MultiSet c)) ->
    TestTree
preservesAllFun name f = testProperty name $ \fun (AMS ms) -> all valid $ f (applyFun fun) ms

pair :: (a, a) -> [a]
pair (x, y) = [x, y]

preservesAll ::
    (Foldable t, Ord b) =>
    String ->
    (MS.MultiSet Int -> t (MS.MultiSet b)) ->
    TestTree
preservesAll name f = testProperty name $ \(AMS ms) -> all valid $ f ms

preservesAll1 ::
    (Arbitrary a, Show a, Foldable t, Ord b) =>
    String ->
    (a -> MS.MultiSet Int -> t (MS.MultiSet b)) ->
    TestTree
preservesAll1 name f = testProperty name $ \x (AMS ms) -> all valid $ f x ms

preservesFun1 ::
    ( Function a
    , CoArbitrary a
    , Show a
    , Arbitrary b
    , Show b
    , Arbitrary c
    , Show c
    , Ord d
    ) =>
    String ->
    ((a -> b) -> c -> MS.MultiSet Int -> MS.MultiSet d) ->
    TestTree
preservesFun1 name f = testProperty name $ \fun x (AMS ms) -> valid $ f (applyFun fun) x ms