{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module HaskellWorks.Data.SegmentSet.StrictSpec ( spec ) where import Data.Foldable import Data.List (sortBy) import Control.Monad.IO.Class import Data.Semigroup import HaskellWorks.Data.FingerTree.Strict (ViewL (..), ViewR (..), viewl, viewr, (<|), (><), (|>)) import HaskellWorks.Data.Gen import HaskellWorks.Data.SegmentSet.Strict import qualified HaskellWorks.Data.FingerTree.Strict as FT import qualified HaskellWorks.Data.SegmentSet.Naive as N import qualified HaskellWorks.Data.SegmentSet.Strict as S (fromList) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import HaskellWorks.Hspec.Hedgehog import Hedgehog import Test.Hspec {-# ANN module ("HLint: ignore Redundant do" :: String) #-} fallbackTo :: Bool fallbackTo = True spec :: Spec spec = describe "HaskellWorks.Data.SegmentSet.StrictSpec" $ do it "should convert SegmentSet to List" $ do let emptySM :: SegmentSet Int = empty segmentSetToList emptySM `shouldBe` [] it "should convert List to SegmentSet" $ do let emptySM :: SegmentSet Int = empty let emptySM2 :: SegmentSet Int = S.fromList [] segmentSetToList emptySM2 `shouldBe` segmentSetToList emptySM it "fromList with no overlap works" $ do let initial = fromList [Segment 1 10, Segment 11 20] :: SegmentSet Int let expected = [Segment 1 20] segmentSetToList initial `shouldBe` expected it "insert with overlap works" $ do let initial = fromList [Segment 1 10, Segment 21 30] :: SegmentSet Int let updated = insert (Segment 11 20) initial let expected = [Segment 1 30] segmentSetToList updated `shouldBe` expected it "insert with overlap works" $ do let initial = fromList [Segment 1 10, Segment 11 20] :: SegmentSet Int let updated = insert (Segment 5 15) initial let expected = [Segment 1 20] segmentSetToList updated `shouldBe` expected it "fromList of two segments in order possibly overlapping" $ do require $ property $ do (Segment aLt aRt, Segment bLt bRt) <- forAll $ do aLt <- Gen.int (Range.linear 1 100) bRt <- Gen.int (Range.linear aLt 100) aRt <- Gen.int (Range.linear aLt bRt) bLt <- Gen.int (Range.linear aLt bRt) return (Segment aLt aRt, Segment bLt bRt) let initial = [Segment aLt aRt, Segment bLt bRt] :: [Segment Int] let actual = segmentSetToList (fromList initial) let aRt' = aRt `min` pred bLt case () of () | aLt == bRt -> actual === [Segment aLt bRt] () | bLt <= aLt && bRt >= aRt -> actual === [Segment bLt bRt] () | succ aRt >= bLt -> actual === [Segment aLt bRt] () | fallbackTo -> actual === [Segment aLt aRt , Segment bLt bRt] it "toList of n segments should be ordered, non-overlapping" $ do require $ property $ do segments <- forAll $ genSegments 100 0 1000 let sSet = fromList segments let lst = segmentSetToList sSet monotonicSegments lst === True it "deleting elements should produce a set with 'holes' in it" $ do require $ property $ do let (bot, top) = (0, 1000) let sSet = singleton $ Segment bot top deletions <- forAll $ genSegments 100 bot top let deletedSet = segmentSetToList $ foldr delete sSet deletions -- This is hacky. We're running the deletions through a Segment Set -- to get an ordered, non-overlapping, merged version. This makes it -- much easier to check the `inverse` property let orderedDeletions = segmentSetToList $ fromList deletions -- Check both directions of inversion. deletedSet === invert bot top orderedDeletions invert bot top deletedSet === orderedDeletions it "fromList [Segment 1 1, Segment 1 1]" $ do let initial = [Segment 1 1, Segment 1 1] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 1] it "fromList [Segment 1 2, Segment 1 1]" $ do let initial = [Segment 1 2, Segment 1 1] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 2] it "fromList [Segment 1 2, Segment 2 2]" $ do let initial = [Segment 1 2, Segment 2 2] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 2] it "fromList [Segment 1 2, Segment 1 2]" $ do let initial = [Segment 1 2, Segment 1 2] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 2] it "fromList [Segment 1 3, Segment 1 1]" $ do let initial = [Segment 1 3, Segment 1 1] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 3] it "fromList [Segment 1 3, Segment 3 3]" $ do let initial = [Segment 1 3, Segment 3 3] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 3] it "fromList [Segment 1 3, Segment 2 2]" $ do let initial = [Segment 1 3, Segment 2 2] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 3] it "fromList [Segment 1 3, Segment 0 1]" $ do let initial = [Segment 1 3, Segment 0 1] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 0 3] it "fromList [Segment 1 3, Segment 3 4]" $ do let initial = [Segment 1 4] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 4] it "fromList [Segment 1 2, Segment 2 7]" $ do let initial = [Segment 1 7] :: [Segment Int] let actual = segmentSetToList (fromList initial) actual `shouldBe` [Segment 1 7] it "fromList (delete (Segment 1 1) [Segment 1 1])" $ do let initial = [Segment 1 1] :: [Segment Int] let actual = segmentSetToList (delete (Segment 1 1) (fromList initial)) actual `shouldBe` [] it "fromList (delete (Segment 1 3) [Segment 2 4])" $ do let initial = [Segment 2 4] :: [Segment Int] let actual = segmentSetToList (delete (Segment 1 3) (fromList initial)) actual `shouldBe` [Segment 4 4] it "fromList (delete (Segment 3 5) [Segment 2 4])" $ do let initial = [Segment 2 4] :: [Segment Int] let actual = segmentSetToList (delete (Segment 3 5) (fromList initial)) actual `shouldBe` [Segment 2 2] it "fromList (delete (Segment 3 5) [Segment 2 4])" $ do let initial = [Segment 2 4] :: [Segment Int] let actual = segmentSetToList (delete (Segment 3 3) (fromList initial)) actual `shouldBe` [Segment 2 2, Segment 4 4] describe "cappedL" $ do let original = FT.Single (Item (Max (11 :: Int)) (Segment 11 20)) it "left of" $ do cappedL 5 original `shouldBe` (FT.Empty, FT.Empty) it "overlapping" $ do cappedL 15 original `shouldBe` (FT.Single (Item (Max (11 :: Int)) (Segment 11 14)), FT.Single (Item (Max 15) (Segment 15 20))) it "right of" $ do cappedL 25 original `shouldBe` (FT.Single (Item (Max 11) (Segment 11 20)), FT.Empty) describe "cappedM" $ do let original = FT.Single (Item (Max (21 :: Int)) (Segment 21 30)) it "left of" $ do cappedM 15 original `shouldBe` FT.Single (Item (Max (21 :: Int)) (Segment 21 30)) it "overlapping" $ do cappedM 25 original `shouldBe` FT.Single (Item (Max (26 :: Int)) (Segment 26 30)) it "left of" $ do cappedM 35 original `shouldBe` FT.Empty it "should behave just live the naive version" $ do require $ property $ do segments <- forAll (genOrderedIntSegments 100 1 100) segmentSetToList (fromList segments) === N.toList (N.fromList segments) -- Takes a min and max bound and a list of segments, and produces the inverse -- i.e. gives you segments where the holes are -- Assumes the input list is ordered and non-overlapping, and that all elements -- fall within (minB, maxB) inclusive. invert :: (Enum k, Eq k) => k -> k -> [Segment k] -> [Segment k] invert minB maxB [] = [Segment minB maxB] invert minB maxB (s:ss) | minB == low s && maxB == high s = [] | minB == low s = theRest | maxB == high s = [next] | otherwise = next : theRest where next = Segment minB (pred $ low s) theRest = invert (succ $ high s) maxB ss monotonicSegments :: Ord k => [Segment k] -> Bool monotonicSegments (x1:x2:xs) = high x1 < low x2 && monotonicSegments (x2:xs) monotonicSegments [x1] = True monotonicSegments [] = True