packages feed

multisets-0.1.0.0: test/Test/ToFrom.hs

{-# LANGUAGE TupleSections #-}

module Test.ToFrom (
    tests,
) where

import Data.List
import qualified Data.List.NonEmpty as NE
import qualified Data.Map.Strict 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

tests :: TestTree
tests =
    testGroup
        "constructors"
        [ testProperty "empty" prop_empty
        , testProperty "singleton" prop_singleton
        , testProperty "singletonMany" prop_singletonMany
        , testProperty "fromMultiplicityList" prop_fromMultiplicityList
        , testProperty "fromMultiplicityList/zero" prop_fromMultiplicityListZero
        , testProperty "fromMultiplicityList/duplicates" prop_fromMultiplicityListDuplicates
        , testProperty "fromList" prop_fromList
        , testProperty "fromSet" prop_fromSet
        , testProperty "fromMap" prop_fromMap
        , testProperty "fromMultiplicityList . toMultiplicityList" prop_multiplicityListRoundtrip
        , testProperty "fromList . toList" prop_listRoundtrip
        , testProperty "fromMap . toMap" prop_mapRoundtrip
        , testProperty "toSet . fromSet" prop_setRoundtrip
        , testProperty "toDistinctList . fromList" prop_distinctList
        , testProperty "toMultiplicityList/ascending" prop_toMultiplicityListAscending
        , testProperty "toList/ascending" prop_toListAscending
        , testProperty "toDistinctList/ascending" prop_toDistinctListAscending
        , testProperty "toMultiplicityList/toDistinctList" prop_toMultiplicityListDistinct
        , testProperty "toMultiplicityList/toMap" prop_toMultiplicityListMap
        , testProperty "toDistinctList/toSet" prop_toDistinctListSet
        , testProperty "toMultiplicityList/distinctSize" prop_toMultiplicityListDistinctSize
        , testProperty "toDistinctList/distinctSize" prop_toDistinctListDistinctSize
        , testProperty "toList/toMultiplicityList" prop_toListMultiplicityList
        ]

prop_empty :: Property
prop_empty = MS.toMap (MS.empty :: MS.MultiSet Int) === M.empty

prop_singleton :: Int -> Property
prop_singleton x = MS.toMap (MS.singleton x) === M.singleton x 1

prop_singletonMany :: Int -> LNat -> Property
prop_singletonMany x (LNat n) =
    MS.toMap (MS.singletonMany x n) === if n == 0 then M.empty else M.singleton x n

prop_fromMultiplicityList :: [(Int, LNat)] -> Property
prop_fromMultiplicityList =
    ((===) <$> MS.toMap . MS.fromMultiplicityList <*> M.filter (> 0) . M.fromListWith (+))
        . fmap (fmap getLNat)

prop_fromMultiplicityListZero :: Int -> Property
prop_fromMultiplicityListZero x = MS.fromMultiplicityList [(x, 0)] === MS.empty

prop_fromMultiplicityListDuplicates :: Int -> LNat -> LNat -> Property
prop_fromMultiplicityListDuplicates x (LNat n) (LNat m) =
    MS.toMap (MS.fromMultiplicityList [(x, n), (x, m)])
        === if n + m == 0 then M.empty else M.singleton x (n + m)

prop_fromList :: [Int] -> Property
prop_fromList = (===) <$> MS.toMap . MS.fromList <*> M.fromListWith (+) . fmap (,1)

prop_fromSet :: S.Set Int -> Property
prop_fromSet xs = MS.toMap (MS.fromSet xs) === M.fromSet (const 1) xs

prop_fromMap :: M.Map Int LNat -> Property
prop_fromMap xs = MS.toMap (MS.fromMap ys) === M.filter (> 0) ys
  where
    ys = getLNat <$> xs

prop_multiplicityListRoundtrip :: AMS -> Property
prop_multiplicityListRoundtrip (AMS xs) = MS.fromMultiplicityList (MS.toMultiplicityList xs) === xs

prop_mapRoundtrip :: AMS -> Property
prop_mapRoundtrip (AMS xs) = MS.fromMap (MS.toMap xs) === xs

-- not based on AMS to avoid the explosion of toList with AMS
prop_listRoundtrip :: [Int] -> Property
prop_listRoundtrip = (===) <$> MS.toList . MS.fromList <*> sort

prop_setRoundtrip :: S.Set Int -> Property
prop_setRoundtrip xs = MS.toSet (MS.fromSet xs) === xs

prop_distinctList :: [Int] -> Property
prop_distinctList = (===) <$> MS.toDistinctList . MS.fromList <*> S.toAscList . S.fromList

prop_toMultiplicityListAscending :: AMS -> Property
prop_toMultiplicityListAscending (AMS xs) =
    property $ strictlyAscending $ map fst $ MS.toMultiplicityList xs

prop_toListAscending :: [Int] -> Property
prop_toListAscending = ((===) <$> MS.toList <*> sort . MS.toList) . MS.fromList

prop_toDistinctListAscending :: AMS -> Property
prop_toDistinctListAscending (AMS xs) = property $ strictlyAscending $ MS.toDistinctList xs

prop_toMultiplicityListDistinct :: AMS -> Property
prop_toMultiplicityListDistinct (AMS xs) =
    map fst (MS.toMultiplicityList xs) === MS.toDistinctList xs

prop_toMultiplicityListMap :: AMS -> Property
prop_toMultiplicityListMap (AMS xs) = MS.toMultiplicityList xs === M.toAscList (MS.toMap xs)

prop_toDistinctListSet :: AMS -> Property
prop_toDistinctListSet (AMS xs) = MS.toDistinctList xs === S.toAscList (MS.toSet xs)

prop_toMultiplicityListDistinctSize :: AMS -> Property
prop_toMultiplicityListDistinctSize (AMS xs) =
    length (MS.toMultiplicityList xs) === MS.distinctSize xs

prop_toDistinctListDistinctSize :: AMS -> Property
prop_toDistinctListDistinctSize (AMS xs) = length (MS.toDistinctList xs) === MS.distinctSize xs

prop_toListMultiplicityList :: [Int] -> Property
prop_toListMultiplicityList = ((===) <$> MS.toMultiplicityList <*> counts . MS.toList) . MS.fromList
  where
    counts = map ((,) <$> NE.head <*> fromIntegral . length) . NE.group

strictlyAscending :: (Ord a) => [a] -> Bool
strictlyAscending xs = and $ zipWith (<) xs (drop 1 xs)