packages feed

int-interval-map-0.0.1.0: tests/Tests.hs

{- Copyright 2020 Luis Pedro Coelho
 - License: MIT
 -}
{-# LANGUAGE TemplateHaskell, QuasiQuotes, FlexibleContexts #-}
module Main where

import Test.Tasty.HUnit
import Test.Tasty.TH (defaultMainGenerator)
import qualified Hedgehog as H
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Tasty.Hedgehog



import qualified Data.Vector.Storable as VS
import qualified Data.IntervalIntMap as IMA
import qualified Data.IntervalIntMap.Internal.IntervalIntIntMap as IM
import qualified Data.IntervalIntMap.Internal.GrowableVector as GV
import           Data.Foldable (forM_, for_)
import qualified Data.IntSet as IS


tData =
    [ IM.IntervalValue 0  2 0
    , IM.IntervalValue 0  2 1
    , IM.IntervalValue 1  2 2
    , IM.IntervalValue 3  6 3
    , IM.IntervalValue 3  4 4
    , IM.IntervalValue 1  4 5
    , IM.IntervalValue 4  7 6
    , IM.IntervalValue 4  6 7
    , IM.IntervalValue 8 10 8
    , IM.IntervalValue 1 12 9
    ]

tDataN :: IM.NaiveIntervalInt
tDataN = VS.fromList tData

below x (IM.IntervalValue _ e _) = x >= e
above x (IM.IntervalValue s _ _) = x < s

case_partition =
    for_ [0..14] $ \split -> do
        let (left,center,right) = IM.partition split tDataN
        all (below $ toEnum split) (VS.toList left) @? "Center does not include split"
        all (IM.intervalContains $ toEnum split) (VS.toList center) @? "Left is not below split"
        all (above $ toEnum split) (VS.toList right) @? "Right is not above split"
        VS.length left + VS.length center + VS.length right @=? VS.length tDataN

case_small_build_tree_find = do
    let t = IM.mkTree 4 tDataN
    for_ [0..14] $ \x ->
        IM.lookup x t @=? IM.naiveIntervalMapLookup x tDataN

genSimpleInterval space = do
    s <- Gen.integral (Range.linear 0 space)
    len <- Gen.integral (Range.linear 0 space)
    return (s, s + len)

prop_build_tree_find = H.property $ do
    -- the smaller values will generate more crowded inputs
    space <- H.forAll $ Gen.integral (Range.linear 100 2000)
    intervals <- H.forAll $ Gen.list (Range.linear 0 2000) $ genSimpleInterval space
    ps <- H.forAll $ Gen.list (Range.linear 0 5) $ Gen.integral $ Range.linear 0 10000
    H.classify "empty" $ length intervals == 0
    H.classify "small (N< 100)" $ length intervals < 100
    H.classify "large (N>=100)" $ length intervals >= 100
    let naive = VS.fromList [IM.IntervalValue s e ix | ((s,e),ix) <- zip intervals [0..]]
        t = IM.mkTree 16 naive
    for_ ps $ \p ->
        IM.lookup p t H.=== IM.naiveIntervalMapLookup p naive



prop_naive_overlaps1 = H.property $ do
    (s,e) <- H.forAll $ genSimpleInterval 100
    (s',e') <- H.forAll $ genSimpleInterval 100
    let naive = VS.fromList [IM.IntervalValue (toEnum s) (toEnum e) 0]
        i = IM.Interval s' e'
        doesOverlap
            | s >= e = False
            | otherwise = any (\c -> s' <= c && c < e') [s..(e-1)]
    H.classify "empty interval" $ s == e || s' == e'
    H.classify "overlaps" $ doesOverlap
    H.classify "no overlap" $ not doesOverlap
    (not . IS.null $ IM.naiveOverlaps i naive) H.=== doesOverlap

prop_growable_vector = H.property $ do
    values <- H.forAll $ Gen.list (Range.linear 0 1000) $ Gen.integral (Range.linear 0 1000)
    let direct = VS.fromList (values :: [Int])
    built <- do
        gv <- GV.new
        forM_ values (`GV.pushBack` gv)
        GV.unsafeFreeze gv
    direct H.=== built

case_simple_ima = do
    acc <- IMA.new
    IMA.insert (IMA.Interval 2 4) (7 :: Int) acc
    IMA.insert (IMA.Interval 3 6) (8 :: Int) acc
    im <- IMA.unsafeFreeze acc
    IMA.lookup 1 im @=? []
    IMA.lookup 2 im @=? [7]
    IS.fromList (IMA.lookup 3 im) @=? IS.fromList [7,8]
    IMA.lookup 4 im @=? [8]
    (length $ IMA.overlaps (IMA.Interval 2 5) im) @=? 2
    (length $ IMA.overlapsWithKeys (IMA.Interval 2 5) im) @=? 2

    (length $ IMA.overlaps (IMA.Interval 4 5) im) @=? 1
    (length $ IMA.overlapsWithKeys (IMA.Interval 4 5) im) @=? 1

    (length $ IMA.overlaps (IMA.Interval 4 9) im) @=? 1
    (length $ IMA.overlapsWithKeys (IMA.Interval 4 9) im) @=? 1

case_fromList = do
    let im = IMA.fromList [(IMA.Interval (fromEnum s) (fromEnum e), v) | (IM.IntervalValue s e v) <- tData]
    IMA.overlapsWithKeys (IM.Interval 11 15)  im @?= [(IM.Interval 1 12, 9)]


main :: IO ()
main = $(defaultMainGenerator)