packages feed

bitset-1.2: tests/Tests.hs

module Main (main) where

import Control.Applicative ((<$>))
import Data.List ((\\), intersect, union, nub, sort)
import Data.Monoid (mempty, mappend)
import Data.Word (Word16)

import Test.Framework (Test, defaultMain)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck (Property, Arbitrary(..), (==>), classify)

import Data.BitSet (BitSet)
import qualified Data.BitSet as BitSet

instance (Arbitrary a, Enum a) => Arbitrary (BitSet a) where
    arbitrary = BitSet.fromList <$> arbitrary


propSize :: [Word16] -> Bool
propSize = go . nub where
  go xs = length xs == BitSet.size (BitSet.fromList xs)

propSizeAfterInsert :: Word16 -> BitSet Word16 -> Bool
propSizeAfterInsert x bs =
    BitSet.size (BitSet.insert x bs) == BitSet.size bs + diff
  where
    diff :: Int
    diff = if x `BitSet.member` bs then 0 else 1

propSizeAfterDelete :: Word16 -> BitSet Word16 -> Bool
propSizeAfterDelete x bs =
    BitSet.size (BitSet.delete x bs) == BitSet.size bs - diff
  where
    diff :: Int
    diff = if x `BitSet.member` bs then 1 else 0

propInsertMember :: Word16 -> BitSet Word16 -> Bool
propInsertMember x bs = x `BitSet.member` BitSet.insert x bs

propDeleteMember :: Word16 -> BitSet Word16 -> Bool
propDeleteMember x bs = x `BitSet.notMember` BitSet.delete x bs

propInsertDeleteIdempotent :: Word16 -> BitSet Word16 -> Property
propInsertDeleteIdempotent x bs =
    x `BitSet.notMember` bs ==> bs == BitSet.delete x (BitSet.insert x bs)

propDeleteIdempotent :: Word16 -> BitSet Word16 -> Property
propDeleteIdempotent x bs =
    classify (x `BitSet.member` bs) "x in bs" $
    classify (x `BitSet.notMember` bs) "x not in bs" $
    BitSet.delete x bs == BitSet.delete x (BitSet.delete x bs)

propInsertIdempotent :: Word16 -> BitSet Word16 -> Bool
propInsertIdempotent x bs =
    BitSet.insert x bs == BitSet.insert x (BitSet.insert x bs)

propToList :: [Word16] -> Bool
propToList xs = nub (sort xs) == BitSet.toList bs where
  bs :: BitSet Word16
  bs = BitSet.fromList xs

propFromList :: [Word16] -> Bool
propFromList xs = all (`BitSet.member` bs) xs where
  bs :: BitSet Word16
  bs = BitSet.fromList xs

propEmpty :: Word16 -> Bool
propEmpty x = x `BitSet.notMember` BitSet.empty

propUnsafeFromIntegral :: Word16 -> Bool
propUnsafeFromIntegral x =
    bs == BitSet.unsafeFromIntegral (BitSet.toIntegral bs :: Integer)
  where
    bs :: BitSet Word16
    bs = BitSet.singleton x

propUnions :: [Word16] -> Bool
propUnions xs = all (`BitSet.member` bs) xs where
  n      = length xs
  (l, r) = splitAt (n `div` 2) xs

  bs :: BitSet Word16
  bs = BitSet.unions $ map BitSet.fromList [l, r, l, r, l]

propIntersectionWithSelf :: [Word16] -> Bool
propIntersectionWithSelf xs = all (`BitSet.member` bs) xs
  where
    bs :: BitSet Word16
    bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.intersection` bs0

propIntersection :: [Word16] -> Bool
propIntersection xs =
    all (`BitSet.member` bs) (l `intersect` r) &&
    all (`BitSet.notMember` bs) (dl `union` dr)
  where
    n      = length xs
    (l, r) = splitAt (n `div` 2) $ nub xs

    dl = l \\ r
    dr = r \\ l

    bs :: BitSet Word16
    bs = let bs1 = BitSet.fromList l
             bs2 = BitSet.fromList r
         in bs1 `BitSet.intersection` bs2

propDifferenceWithSelf :: [Word16] -> Bool
propDifferenceWithSelf xs = bs == BitSet.empty where
  bs :: BitSet Word16
  bs = let bs0 = BitSet.fromList xs in bs0 `BitSet.difference` bs0

propDifference :: [Word16] -> Property
propDifference xs = n > 0 ==>
                    all (`BitSet.member` bs) (l \\ r) &&
                    all (`BitSet.notMember` bs) (l `intersect` r)
  where
    n      = length xs
    (l, r) = splitAt (n `div` 2) $ nub xs

    bs :: BitSet Word16
    bs = let bs1 = BitSet.fromList l
             bs2 = BitSet.fromList r
         in bs1 `BitSet.difference` bs2

propMonoidLaws :: BitSet Word16 -> BitSet Word16 -> BitSet Word16 -> Bool
propMonoidLaws bs1 bs2 bs3 =
    bs1 `mappend` mempty == bs1 &&
    mempty `mappend` bs1 == bs1 &&
    mappend bs1 (bs2 `mappend` bs3) == (bs1 `mappend` bs2) `mappend` bs3

propIsSubsetOfSelf :: BitSet Word16 -> Bool
propIsSubsetOfSelf bs = bs `BitSet.isSubsetOf` bs &&
                        not (bs `BitSet.isProperSubsetOf` bs)

propIsSubsetOf :: [Word16] -> Bool
propIsSubsetOf xs =
    bs1 `BitSet.isSubsetOf` bs && bs2 `BitSet.isSubsetOf` bs
  where
    n = length xs

    bs :: BitSet Word16
    bs = BitSet.fromList xs

    bs1 :: BitSet Word16
    bs1 = BitSet.fromList $ take (n `div` 2) xs

    bs2 :: BitSet Word16
    bs2 = BitSet.fromList $ drop (n `div` 2) xs

main :: IO ()
main = defaultMain tests where
  tests :: [Test]
  tests = [ testProperty "size" propSize
          , testProperty "size after insert" propSizeAfterInsert
          , testProperty "size after delete" propSizeAfterDelete
          , testProperty "insert" propInsertMember
          , testProperty "delete" propDeleteMember
          , testProperty "insert and delete are idempotent" propInsertDeleteIdempotent
          , testProperty "delete is idempotent" propDeleteIdempotent
          , testProperty "insert is idempotent" propInsertIdempotent
          , testProperty "toList" propToList
          , testProperty "fromList" propFromList
          , testProperty "empty" propEmpty
          , testProperty "unsafe construction from integral" propUnsafeFromIntegral
          , testProperty "unions" propUnions
          , testProperty "intersection with self" propIntersectionWithSelf
          , testProperty "intersection" propIntersection
          , testProperty "difference with self" propDifferenceWithSelf
          , testProperty "difference" propDifference
          , testProperty "monoid laws" propMonoidLaws
          , testProperty "is subset of self" propIsSubsetOfSelf
          , testProperty "is subset of" propIsSubsetOf
          ]