-
Notifications
You must be signed in to change notification settings - Fork 182
Add validity checks for IntSet and IntMap #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
47f3a08
a6afb19
2a5f770
95ccc3b
aba5a5d
2e0e871
2e81fb5
3008ab9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -180,6 +180,7 @@ module Data.IntSet.Internal ( | |
, suffixBitMask | ||
, prefixBitMask | ||
, bitmapOf | ||
, zero | ||
) where | ||
|
||
import Control.DeepSeq (NFData(rnf)) | ||
|
@@ -250,8 +251,8 @@ data IntSet = Bin {-# UNPACK #-} !Prefix {-# UNPACK #-} !Mask !IntSet !IntSet | |
-- Invariant: In Bin prefix mask left right, left consists of the elements that | ||
-- don't have the mask bit set; right is all the elements that do. | ||
| Tip {-# UNPACK #-} !Prefix {-# UNPACK #-} !BitMap | ||
-- Invariant: The Prefix is zero for all but the last 5 (on 32 bit arches) or 6 | ||
-- bits (on 64 bit arches). The values of the map represented by a tip | ||
-- Invariant: The Prefix is zero for the last 5 (on 32 bit arches) or 6 bits | ||
-- (on 64 bit arches). The values of the set represented by a tip | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this comment just flat-out wrong before? That's confusing and disturbing! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to have been. It took me a little bit to realize that I was implementing the incorrect check :P I went and read the implementation closer and figured that this is I think what it meant to say. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And the test that codifies this interpretation passes, sooo... |
||
-- are the prefix plus the indices of the set bits in the bit map. | ||
| Nil | ||
|
||
|
@@ -323,7 +324,7 @@ size = go 0 | |
|
||
-- | /O(min(n,W))/. Is the value a member of the set? | ||
|
||
-- See Note: Local 'go' functions and capturing] | ||
-- See Note: Local 'go' functions and capturing. | ||
member :: Key -> IntSet -> Bool | ||
member !x = go | ||
where | ||
|
@@ -1250,6 +1251,7 @@ bitmapOf x = bitmapOfSuffix (suffixOf x) | |
{-------------------------------------------------------------------- | ||
Endian independent bit twiddling | ||
--------------------------------------------------------------------} | ||
-- Returns True iff the bits set in i and the Mask m are disjoint. | ||
zero :: Int -> Mask -> Bool | ||
zero i m | ||
= (natFromInt i) .&. (natFromInt m) == 0 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
module IntMapValidity (valid) where | ||
|
||
import Data.Bits (xor, (.&.)) | ||
import Data.IntMap.Internal | ||
import Test.QuickCheck (Property, counterexample, property, (.&&.)) | ||
|
||
{-------------------------------------------------------------------- | ||
Assertions | ||
--------------------------------------------------------------------} | ||
-- | Returns true iff the internal structure of the IntMap is valid. | ||
valid :: IntMap a -> Property | ||
valid t = | ||
counterexample "nilNeverChildOfBin" (nilNeverChildOfBin t) .&&. | ||
counterexample "commonPrefix" (commonPrefix t) .&&. | ||
counterexample "maskRespected" (maskRespected t) | ||
|
||
-- Invariant: Nil is never found as a child of Bin. | ||
nilNeverChildOfBin :: IntMap a -> Bool | ||
nilNeverChildOfBin t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
Bin _ _ l r -> noNilInSet l && noNilInSet r | ||
where | ||
noNilInSet t' = | ||
case t' of | ||
Nil -> False | ||
Tip _ _ -> True | ||
Bin _ _ l' r' -> noNilInSet l' && noNilInSet r' | ||
|
||
-- Invariant: Prefix is the common high-order bits that all elements share to | ||
-- the left of the Mask bit. | ||
commonPrefix :: IntMap a -> Bool | ||
commonPrefix t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
b@(Bin p _ _ _) -> all (sharedPrefix p) (keys b) | ||
where | ||
sharedPrefix :: Prefix -> Int -> Bool | ||
sharedPrefix p a = 0 == (p `xor` (p .&. a)) | ||
|
||
-- Invariant: In Bin prefix mask left right, left consists of the elements that | ||
-- don't have the mask bit set; right is all the elements that do. | ||
maskRespected :: IntMap a -> Bool | ||
maskRespected t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
Bin _ binMask l r -> | ||
all (\x -> zero x binMask) (keys l) && | ||
all (\x -> not (zero x binMask)) (keys r) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
{-# LANGUAGE CPP #-} | ||
module IntSetValidity (valid) where | ||
|
||
import Data.Bits (xor, (.&.)) | ||
import Data.IntSet.Internal | ||
import Test.QuickCheck (Property, counterexample, property, (.&&.)) | ||
|
||
{-------------------------------------------------------------------- | ||
Assertions | ||
--------------------------------------------------------------------} | ||
-- | Returns true iff the internal structure of the IntSet is valid. | ||
valid :: IntSet -> Property | ||
valid t = | ||
counterexample "nilNeverChildOfBin" (nilNeverChildOfBin t) .&&. | ||
counterexample "maskPowerOfTwo" (maskPowerOfTwo t) .&&. | ||
counterexample "commonPrefix" (commonPrefix t) .&&. | ||
counterexample "markRespected" (maskRespected t) .&&. | ||
counterexample "tipsValid" (tipsValid t) | ||
|
||
-- Invariant: Nil is never found as a child of Bin. | ||
nilNeverChildOfBin :: IntSet -> Bool | ||
nilNeverChildOfBin t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
Bin _ _ l r -> noNilInSet l && noNilInSet r | ||
where | ||
noNilInSet t' = | ||
case t' of | ||
Nil -> False | ||
Tip _ _ -> True | ||
Bin _ _ l' r' -> noNilInSet l' && noNilInSet r' | ||
|
||
-- Invariant: The Mask is a power of 2. It is the largest bit position at which | ||
-- two elements of the set differ. | ||
maskPowerOfTwo :: IntSet -> Bool | ||
maskPowerOfTwo t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
Bin _ m l r -> | ||
(m `mod` 2 == 0) && maskPowerOfTwo l && maskPowerOfTwo r | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit late to the party, but
is not a way to test that a mask is a power of 2. Alternative: |
||
|
||
-- Invariant: Prefix is the common high-order bits that all elements share to | ||
-- the left of the Mask bit. | ||
commonPrefix :: IntSet -> Bool | ||
commonPrefix t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
b@(Bin p _ _ _) -> all (sharedPrefix p) (elems b) | ||
where | ||
sharedPrefix :: Prefix -> Int -> Bool | ||
sharedPrefix p a = 0 == (p `xor` (p .&. a)) | ||
|
||
-- Invariant: In Bin prefix mask left right, left consists of the elements that | ||
-- don't have the mask bit set; right is all the elements that do. | ||
maskRespected :: IntSet -> Bool | ||
maskRespected t = | ||
case t of | ||
Nil -> True | ||
Tip _ _ -> True | ||
Bin _ binMask l r -> | ||
all (\x -> zero x binMask) (elems l) && | ||
all (\x -> not (zero x binMask)) (elems r) | ||
|
||
-- Invariant: The Prefix is zero for the last 5 (on 32 bit arches) or 6 bits | ||
-- (on 64 bit arches). The values of the set represented by a tip | ||
-- are the prefix plus the indices of the set bits in the bit map. | ||
-- | ||
-- Note: Valid entries stored in tip omitted. | ||
tipsValid :: IntSet -> Bool | ||
tipsValid t = | ||
case t of | ||
Nil -> True | ||
tip@(Tip p b) -> validTipPrefix p | ||
Bin _ _ l r -> tipsValid l && tipsValid r | ||
|
||
validTipPrefix :: Prefix -> Bool | ||
#if WORD_SIZE_IN_BITS==32 | ||
-- Last 5 bits of the prefix must be zero for 32 bit arches. | ||
validTipPrefix p = (0x0000001F .&. p) == 0 | ||
#else | ||
-- Last 6 bits of the prefix must be zero 64 bit anches. | ||
validTipPrefix p = (0x000000000000003F .&. p) == 0 | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps explain what a
mask
looks like?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.