Skip to content
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

Introduce invariant check. #63

Merged
merged 1 commit into from
Aug 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion src/Data/TypeRepMap/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ import Data.Function (on)
import Data.Kind (Type)
import Data.Type.Equality ((:~:) (..), TestEquality (..))
import Data.List (intercalate, nubBy)
import Data.Maybe (fromMaybe)
import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', readArray, sizeofArray,
thawArray, unsafeFreezeArray, writeArray)
import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
import Data.Semigroup (Semigroup (..))
import Data.Semigroup (Semigroup (..), All(..))
import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
import GHC.Exts (IsList (..), inline, sortWith)
import GHC.Fingerprint (Fingerprint (..))
Expand Down Expand Up @@ -448,3 +449,62 @@ fromSortedList l = runST $ do
newFirst <- loop (2 * i + 1) first
writeArray result i (indexArray origin newFirst)
loop (2 * i + 2) (newFirst + 1)

----------------------------------------------------------------------------
-- Helper functions.
----------------------------------------------------------------------------

-- | Check that invariant of the structure is hold.
-- The structure maintains the following invariant.
-- For each element @A@ at index @i@:
--
-- 1. if there is an element @B@ at index @2*i+1@,
-- then @B < A@.
--
-- 2. if there is an element @C@ at index @2*i+2@,
-- then @A < C@.
--
invariantCheck :: TypeRepMap f -> Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you, please, add documentation to what this invariant actually checks? It's not clear to me what is the invariant of TypeRepMap

invariantCheck TypeRepMap{..} = getAll (check 0)
where
lastMay [] = Nothing
lastMay [x] = Just x
lastMay (_:xs) = lastMay xs
sz = sizeofPrimArray fingerprintAs
check i | i >= sz = All True
| otherwise =
let left = i*2+1
right = i*2+2
-- maximum value in the left branch
leftMax =
fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
$ lastMay
$ takeWhile (<sz)
$ iterate (\j -> j*2+2) left
-- minimum value in the right branch
rightMin =
fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
$ lastMay
$ takeWhile (<sz)
$ iterate (\j -> j*2+1) right
in mconcat
[ All $
if left < sz
then
case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs left of
LT -> False
EQ -> indexPrimArray fingerprintBs i >= indexPrimArray fingerprintBs left
GT -> True
else True
, All $
if right < sz
then
case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs right of
LT -> True
EQ -> indexPrimArray fingerprintBs i <= indexPrimArray fingerprintBs right
GT -> False
else True
, All $ fromMaybe True $ (<=) <$> leftMax <*> rightMin
, check (i+1)
]

17 changes: 14 additions & 3 deletions test/Test/TypeRep/MapProperty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import Data.Semigroup (Semigroup (..))
import GHC.Exts (fromList)
import GHC.Stack (HasCallStack)
import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
import Hedgehog (MonadGen, PropertyT, forAll, property, (===), assert)
import Test.Tasty (TestName, TestTree)
import Test.Tasty.Hedgehog (testProperty)

import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, lookup, member)
import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, lookup, member, invariantCheck)

import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
Expand All @@ -41,7 +41,6 @@ test_InsertLookup :: PropertyTest
test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
m <- forAll genMap
WrapTypeable (proxy :: IntProxy n) <- forAll genTF

lookup @n @IntProxy (insert proxy m) === Just proxy

test_InsertInsert :: PropertyTest
Expand All @@ -62,6 +61,18 @@ test_DeleteMember = prop "member k . delete k == False" $ do
else
member @n (delete @n m) === False

test_InsertInvariant :: PropertyTest
test_InsertInvariant = prop "invariantCheck (insert k b) == True" $ do
m <- forAll genMap
WrapTypeable a <- forAll genTF
assert $ invariantCheck (insert a m)

test_DeleteInvariant :: PropertyTest
test_DeleteInvariant = prop "invariantCheck (delete k b) == True" $ do
m <- forAll genMap
WrapTypeable (_ :: IntProxy n) <- forAll genTF
assert $ invariantCheck (delete @n m)

----------------------------------------------------------------------------
-- Semigroup and Monoid laws
----------------------------------------------------------------------------
Expand Down