packages feed

data-interval-2.0.0: test/TestIntervalMap.hs

{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
{-# LANGUAGE CPP, TemplateHaskell, ScopedTypeVariables #-}
module TestIntervalMap (intervalMapTestGroup) where

import Control.DeepSeq
import Control.Exception (evaluate)
import Control.Monad
import Data.Functor.Identity
import qualified Data.Foldable as F
import Data.Generics.Schemes
import Data.Hashable
import Data.Maybe
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
import Data.Traversable (Traversable(..))
#endif
#if __GLASGOW_HASKELL__ < 804
import Data.Semigroup ((<>))
#endif
import Data.Typeable

import Test.ChasingBottoms.IsBottom
import Test.QuickCheck.Function
import Test.Tasty
import Test.Tasty.QuickCheck
import Test.Tasty.HUnit
import Test.Tasty.TH

import Data.Interval ( Interval, Extended (..), (<=..<=), (<=..<), (<..<=), (<..<), (<!))
import qualified Data.Interval as Interval
import qualified Data.IntervalSet as IntervalSet
import Data.IntervalMap.Lazy (IntervalMap)
import qualified Data.IntervalMap.Lazy as IML
import qualified Data.IntervalMap.Strict as IMS

{--------------------------------------------------------------------
  empty
--------------------------------------------------------------------}

prop_empty_is_bottom :: Property
prop_empty_is_bottom =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.isSubmapOf IML.empty a

prop_null_empty :: Property
prop_null_empty =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.null a == (a == IML.empty)

case_null_empty :: Assertion
case_null_empty =
  IML.null (IML.empty :: IntervalMap Rational Integer) @?= True

{--------------------------------------------------------------------
  whole
--------------------------------------------------------------------}

case_nonnull_whole :: Assertion
case_nonnull_whole =
  IML.null (IML.whole 0 :: IntervalMap Rational Integer) @?= False

prop_whole_Lazy_Strict :: Property
prop_whole_Lazy_Strict = do
  forAll arbitrary $ \(a :: Integer) ->
    (IML.whole a :: IntervalMap Rational Integer) == IMS.whole a

case_whole_nonstrict :: Assertion
case_whole_nonstrict = do
  _ <- evaluate (IML.whole bottom :: IntervalMap Rational Integer)
  return ()

case_whole_strict :: Assertion
case_whole_strict =
  isBottom (IMS.whole bottom :: IntervalMap Rational Integer) @?= True

{--------------------------------------------------------------------
  singleton
--------------------------------------------------------------------}

prop_singleton_insert :: Property
prop_singleton_insert = do
  forAll arbitrary $ \(i :: Interval Rational) ->
    forAll arbitrary $ \(a :: Integer) ->
      IML.singleton i a == IML.insert i a IML.empty

prop_singleton_Lazy_Strict :: Property
prop_singleton_Lazy_Strict = do
  forAll arbitrary $ \(i :: Interval Rational) ->
    forAll arbitrary $ \(a :: Integer) ->
      IML.singleton i a == IMS.singleton i a

case_singleton_nonstrict :: Assertion
case_singleton_nonstrict = do
  _ <- evaluate (IML.singleton 0 bottom :: IntervalMap Rational Integer)
  return ()

case_singleton_strict :: Assertion
case_singleton_strict =
  isBottom (IMS.singleton 0 bottom :: IntervalMap Rational Integer) @?= True

{--------------------------------------------------------------------
  insert
--------------------------------------------------------------------}

prop_insert_whole :: Property
prop_insert_whole =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \a ->
      IML.insert Interval.whole a m == IML.whole a

prop_insert_empty :: Property
prop_insert_empty =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \a ->
      IML.insert Interval.empty a m == m

prop_insert_comm :: Property
prop_insert_comm =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(i1,a1) ->
  forAll arbitrary $ \(i2,a2) ->
    Interval.null (Interval.intersection i1 i2)
    ==>
    (IML.insert i1 a1 (IML.insert i2 a2 m) == IML.insert i2 a2 (IML.insert i1 a1 m))

prop_insert_isSubmapOf :: Property
prop_insert_isSubmapOf =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      forAll arbitrary $ \a ->
        IML.isSubmapOf (IML.singleton i a) (IML.insert i a m)

prop_insert_member :: Property
prop_insert_member =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      forAll arbitrary $ \a ->
        case Interval.pickup i of
          Just k -> IML.member k (IML.insert i a m)
          Nothing -> True

prop_insert_lookup :: Property
prop_insert_lookup =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      forAll arbitrary $ \a ->
        case Interval.pickup i of
          Just k -> IML.lookup k (IML.insert i a m) == Just a
          Nothing -> True

prop_insert_bang :: Property
prop_insert_bang =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      forAll arbitrary $ \a ->
        case Interval.pickup i of
          Just k -> IML.insert i a m IML.! k == a
          Nothing -> True

prop_insert_Lazy_Strict :: Property
prop_insert_Lazy_Strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      forAll arbitrary $ \a ->
        IML.insert i a m == IMS.insert i a m

prop_insert_nonstrict :: Property
prop_insert_nonstrict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      IML.insert i bottom m `seq` True

prop_insert_strict :: Property
prop_insert_strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      isBottom $ IMS.insert i bottom m

prop_insertWith_Lazy_Strict :: Property
prop_insertWith_Lazy_Strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \(f :: Fun (Integer,Integer) Integer) ->
      forAll arbitrary $ \i ->
        forAll arbitrary $ \a ->
          IML.insertWith (curry (apply f)) i a m == IMS.insertWith (curry (apply f)) i a m

case_insertWith_nonstrict :: Assertion
case_insertWith_nonstrict = evaluate (IML.insertWith (\_ _ -> bottom) (3 <=..< 7) 1 m) >> return ()
  where
    m :: IntervalMap Rational Integer
    m = IML.singleton (0 <=..< 10) 0

case_insertWith_strict :: Assertion
case_insertWith_strict = isBottom (IMS.insertWith (\_ _ -> bottom) (3 <=..< 7) 1 m) @?= True
  where
    m :: IntervalMap Rational Integer
    m = IMS.singleton (0 <=..< 10) 0

{--------------------------------------------------------------------
  delete / update
--------------------------------------------------------------------}

prop_delete_empty :: Property
prop_delete_empty =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
     IML.delete Interval.empty m == m

prop_delete_whole :: Property
prop_delete_whole =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
     IML.delete Interval.whole m == IML.empty

prop_delete_from_empty :: Property
prop_delete_from_empty =
  forAll arbitrary $ \(i :: Interval Rational) ->
     IML.delete i (IML.empty :: IntervalMap Rational Integer) == IML.empty

prop_delete_comm :: Property
prop_delete_comm =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \i1 ->
  forAll arbitrary $ \i2 ->
     IML.delete i1 (IML.delete i2 m) == IML.delete i2 (IML.delete i1 m)

prop_delete_notMember :: Property
prop_delete_notMember =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      case Interval.pickup i of
        Just k -> IML.notMember k (IML.delete i m)
        Nothing -> True

prop_delete_lookup :: Property
prop_delete_lookup =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      case Interval.pickup i of
        Just k -> IML.lookup k (IML.delete i m) == Nothing
        Nothing -> True

case_adjust :: Assertion
case_adjust = IML.adjust (+1) (3 <=..< 7) m @?= expected
  where
    m :: IntervalMap Rational Integer
    m =
      IML.fromList
      [ (0 <=..< 2, 0)
      , (2 <=..< 4, 2)
      , (4 <=..< 6, 4)
      , (6 <=..< 8, 6)
      , (8 <=..< 10, 8)
      ]
    expected =
      IML.fromList
      [ (0 <=..< 2, 0)
      , (2 <=..< 3, 2)
      , (3 <=..< 4, 3)
      , (4 <=..< 6, 5)
      , (6 <=..< 7, 7)
      , (7 <=..< 8, 6)
      , (8 <=..< 10, 8)
      ]

prop_adjust_Lazy_Strict :: Property
prop_adjust_Lazy_Strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \(f :: Fun Integer Integer) ->
      forAll arbitrary $ \i ->
        IML.adjust (apply f) i m == IMS.adjust (apply f) i m

case_asjust_nonstrict :: Assertion
case_asjust_nonstrict = do
  _ <- evaluate $ IML.adjust (\_ -> bottom) (3 <=..< 7) m
  return ()
  where
    m :: IntervalMap Rational Integer
    m = IML.singleton (0 <=..< 10) 0

case_asjust_strict :: Assertion
case_asjust_strict = isBottom (IMS.adjust (\_ -> bottom) (3 <=..< 7) m) @?= True
  where
    m :: IntervalMap Rational Integer
    m = IMS.singleton (0 <=..< 10) 0

prop_alter :: Property
prop_alter =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
  forAll arbitrary $ \i ->
  forAll arbitrary $ \f ->
    case Interval.pickup i of
      Nothing -> True
      Just k ->
        IML.lookup k (IML.alter (apply f) i m) == apply f (IML.lookup k m)

prop_alter_Lazy_Strict :: Property
prop_alter_Lazy_Strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
  forAll arbitrary $ \i ->
  forAll arbitrary $ \f ->
    IML.alter (apply f) i m == IMS.alter (apply f) i m

prop_alter_nonstrict :: Property
prop_alter_nonstrict =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
  forAll arbitrary $ \i ->
    not (Interval.null i)
    ==>
    (IML.alter (\_ -> Just bottom) i m `seq` True)

prop_alter_strict :: Property
prop_alter_strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
  forAll arbitrary $ \i ->
    not (Interval.null i)
    ==>
    isBottom (IMS.alter (\_ -> Just bottom) i m)

{--------------------------------------------------------------------
  Union
--------------------------------------------------------------------}

prop_union_assoc :: Property
prop_union_assoc =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
  forAll arbitrary $ \c ->
    IML.union a (IML.union b c) == IML.union (IML.union a b) c

prop_union_unitL :: Property
prop_union_unitL =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.union IML.empty a == a

prop_union_unitR :: Property
prop_union_unitR =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.union a IML.empty == a

prop_union_isSubmapOf :: Property
prop_union_isSubmapOf =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
    IML.isSubmapOf a (IML.union a b)

prop_union_isSubmapOf_equiv :: Property
prop_union_isSubmapOf_equiv =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
    IML.isSubmapOf (IML.union a b) b
    == IML.isSubmapOf a b

case_unions_empty_list :: Assertion
case_unions_empty_list =
  IML.unions [] @?= (IML.empty :: IntervalMap Rational Integer)

prop_unions_singleton_list :: Property
prop_unions_singleton_list =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.unions [a] == a

prop_unions_two_elems :: Property
prop_unions_two_elems =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
    IML.unions [a,b] == IML.union a b

case_unionWith :: Assertion
case_unionWith = actual @?= expected
  where
    actual, expected :: IntervalMap Rational Integer
    actual = IML.unionWith (+) (IML.singleton (0 <=..<= 10) 1) (IML.singleton (5 <=..<= 15) 2)
    expected = IML.fromList [(0 <=..< 5, 1), (5 <=..<= 10, 3), (10 <..<= 15, 2)]

prop_unionWith_Lazy_Strict :: Property
prop_unionWith_Lazy_Strict =
  forAll arbitrary $ \(a :: IntervalMap Rational Int) ->
  forAll arbitrary $ \b ->
  forAll arbitrary $ \f ->
    IML.unionWith (curry (apply f)) a b == IMS.unionWith (curry (apply f)) a b

prop_unionWith_nonstrict :: Property
prop_unionWith_nonstrict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
    IML.unionWith (\_ _ -> bottom) a b `seq` True

prop_unionWith_strict :: Property
prop_unionWith_strict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
    not (IntervalSet.null (IMS.keysSet a `IntervalSet.intersection` IMS.keysSet b))
    ==>
    isBottom (IMS.unionWith (\_ _ -> bottom) a b)

{--------------------------------------------------------------------
  Intersection
--------------------------------------------------------------------}

prop_intersection_isSubmapOf :: Property
prop_intersection_isSubmapOf =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \b ->
      IML.isSubmapOf (IML.intersection a b) a

case_intersectionWith :: Assertion
case_intersectionWith = actual @?= expected
  where
    actual, expected :: IntervalMap Rational Integer
    actual = IML.intersectionWith (+) (IML.singleton (0 <=..< 10) 1) (IML.singleton (5 <..<= 5) 1)
    expected = IML.singleton (5 <..< 5) 2

prop_intersectionWith_Lazy_Strict :: Property
prop_intersectionWith_Lazy_Strict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(b :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(f :: Fun (Integer,Integer) Integer) ->
    IML.intersectionWith (curry (apply f)) a b == IMS.intersectionWith (curry (apply f)) a b

prop_intersectionWith_nonstrict :: Property
prop_intersectionWith_nonstrict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(b :: IntervalMap Rational Integer) ->
    IML.intersectionWith (\_ _ -> bottom :: Integer) a b `seq` True

prop_intersectionWith_strict :: Property
prop_intersectionWith_strict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(b :: IntervalMap Rational Integer) ->
    not (IntervalSet.null (IMS.keysSet a `IntervalSet.intersection` IMS.keysSet b))
    ==>
    isBottom (IMS.intersectionWith (\_ _ -> bottom :: Integer) a b)

{--------------------------------------------------------------------
  Difference
--------------------------------------------------------------------}

prop_difference_isSubmapOf :: Property
prop_difference_isSubmapOf =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \(b :: IntervalMap Rational Integer) ->
      IML.isSubmapOf (a IML.\\ b) a

{--------------------------------------------------------------------
  member / lookup
--------------------------------------------------------------------}

prop_notMember_empty :: Property
prop_notMember_empty =
  forAll arbitrary $ \(r::Rational) ->
    r `IML.notMember` (IML.empty :: IntervalMap Rational Integer)

case_findWithDefault_case1 :: Assertion
case_findWithDefault_case1 = IML.findWithDefault "B" 0 m @?= "A"
  where
    m :: IntervalMap Rational String
    m = IML.singleton (0 <=..<1) "A"

case_findWithDefault_case2 :: Assertion
case_findWithDefault_case2 = IML.findWithDefault "B" 1 m @?= "B"
  where
    m :: IntervalMap Rational String
    m = IML.singleton (0 <=..<1) "A"

{--------------------------------------------------------------------
  isSubsetOf
--------------------------------------------------------------------}

prop_isSubmapOf_reflexive :: Property
prop_isSubmapOf_reflexive =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    a `IML.isSubmapOf` a

prop_isProperSubsetOf_irreflexive :: Property
prop_isProperSubsetOf_irreflexive =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    not (a `IML.isProperSubmapOf` a)

{--------------------------------------------------------------------
  span
--------------------------------------------------------------------}

prop_span :: Property
prop_span =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.span a == IntervalSet.span (IML.keysSet a)

{--------------------------------------------------------------------
  map
--------------------------------------------------------------------}

case_mapKeysMonotonic :: Assertion
case_mapKeysMonotonic = IML.mapKeysMonotonic (+1) m1 @?= m2
  where
    m1, m2 :: IntervalMap Rational String
    m1 = IML.fromList [(0 <=..< 1, "A"), (2 <..<= 3, "B")]
    m2 = IML.fromList [(1 <=..< 2, "A"), (3 <..<= 4, "B")]

prop_map_Lazy_Strict :: Property
prop_map_Lazy_Strict =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \(f :: Fun Integer Integer) ->
    IML.map (apply f) m == IMS.map (apply f) m

prop_map_nonstrict :: Property
prop_map_nonstrict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.map (const (bottom :: Integer)) a `seq` True

prop_map_strict :: Property
prop_map_strict =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    not (IMS.null a)
    ==>
    isBottom (IMS.map (const (bottom :: Integer)) a)

{--------------------------------------------------------------------
  Functor / Foldable / Traversal
--------------------------------------------------------------------}

prop_Functor_identity :: Property
prop_Functor_identity =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
    fmap id m == m

prop_Functor_compsition :: Property
prop_Functor_compsition =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
    forAll arbitrary $ \(f :: Fun Int Int) ->
      forAll arbitrary $ \(g :: Fun Int Int) ->
        fmap (apply f . apply g) m == fmap (apply f) (fmap (apply g) m)

prop_Foldable_foldMap :: Property
prop_Foldable_foldMap =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
    forAll arbitrary $ \(f :: Fun Int String) ->
      F.foldMap (apply f) m == F.fold (fmap (apply f) m)

prop_Traversable_identity :: Property
prop_Traversable_identity =
  forAll arbitrary $ \(m :: IntervalMap Rational Int) ->
    traverse Identity m == Identity m

{--------------------------------------------------------------------
  toList / fromList
--------------------------------------------------------------------}

prop_fromList_toList_id :: Property
prop_fromList_toList_id =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.fromList (IML.toList a) == a

prop_toAscList_toDescList :: Property
prop_toAscList_toDescList =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.toDescList a == reverse (IML.toAscList a)

case_fromList :: Assertion
case_fromList = actual @?= expected
  where
    actual, expected :: IntervalMap Rational Integer
    actual = IML.fromList [(0 <=..< 10, 1), (5 <..<= 15, 2)]
    expected = IML.fromList [(0 <=..<= 5, 1), (5 <..<= 15, 2)]

case_fromListWith :: Assertion
case_fromListWith = actual @?= expected
  where
    actual, expected :: IntervalMap Rational Integer
    actual = IML.fromListWith (+) [(0 <=..< 10, 1), (5 <..<= 15, 2)]
    expected = IML.fromList [(0 <=..<= 5, 1), (5 <..< 10, 3), (10 <=..<= 15, 2)]

prop_fromList_Lazy_Strict :: Property
prop_fromList_Lazy_Strict =
  forAll arbitrary $ \xs ->
    (IML.fromList xs :: IntervalMap Rational Integer) == IMS.fromList xs

case_fromList_nonstrict :: Assertion
case_fromList_nonstrict = evaluate m >> return ()
  where
    m :: IntervalMap Rational Integer
    m = IML.fromList [(0 <=..< 10, bottom), (5 <..<= 15, bottom)]

case_fromList_strict :: Assertion
case_fromList_strict = isBottom m @?= True
  where
    m :: IntervalMap Rational Integer
    m = IMS.fromList [(0 <=..< 10, bottom), (5 <..<= 15, bottom)]

prop_fromListWith_Lazy_Strict :: Property
prop_fromListWith_Lazy_Strict =
  forAll arbitrary $ \xs ->
    forAll arbitrary $ \f ->
      (IML.fromListWith (curry (apply f)) xs :: IntervalMap Rational Integer) == IMS.fromListWith (curry (apply f))  xs

case_fromListWith_nonstrict :: Assertion
case_fromListWith_nonstrict = evaluate m >> return ()
  where
    m :: IntervalMap Rational Integer
    m = IML.fromListWith (\_ _ -> bottom) [(0 <=..< 10, 1), (5 <..<= 15, 2)]

case_fromListWith_strict :: Assertion
case_fromListWith_strict = isBottom m @?= True
  where
    m :: IntervalMap Rational Integer
    m = IMS.fromListWith (\_ _ -> bottom) [(0 <=..< 10, 1), (5 <..<= 15, 2)]

{--------------------------------------------------------------------
  Filter
--------------------------------------------------------------------}

case_filter :: Assertion
case_filter = actual @?= expected
  where
    m, expected, actual :: IntervalMap Rational Integer
    m =
      IML.fromList
      [ (2  <..<= 10, 1)
      , (10 <..<= 20, 2)
      , (20 <..<= 30, 3)
      , (30 <..<= 40, 4)
      ]
    expected =
      IML.fromList
      [ (10 <..<= 20, 2)
      , (30 <..<= 40, 4)
      ]
    actual = IML.filter even m

prop_split :: Property
prop_split =
  forAll arbitrary $ \(m :: IntervalMap Rational Integer) ->
    forAll arbitrary $ \i ->
      not (Interval.null i)
      ==>
      (case IML.split i m of
         (m1,m2,m3) ->
           and
           [ and [j <! i | j <- IML.keys m1]
           , and [j `Interval.isSubsetOf` i | j <- IML.keys m2]
           , and [i <! j | j <- IML.keys m3]
           ])

case_split_case1 :: Assertion
case_split_case1 =
  IML.split (5 <=..<= 9) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2  <..<= 10, "A")
      , (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5 <=..<= 9, "A")
      ]
    larger =
      IML.fromList
      [ (9  <..<= 10, "A")
      , (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]

case_split_case2 :: Assertion
case_split_case2 =
  IML.split (5 <=..< 10) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2  <..<= 10, "A")
      , (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2 <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5 <=..< 10, "A")
      ]
    larger =
      IML.fromList
      [ (10, "A")
      , (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]

case_split_case3 :: Assertion
case_split_case3 =
  IML.split (5 <=..<= 10) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2  <..<= 10, "A")
      , (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5 <=..<= 10, "A")
      ]
    larger =
      IML.fromList
      [ (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]

case_split_case4 :: Assertion
case_split_case4 =
  IML.split (5 <=..< 10) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2   <..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5 <=..< 10, "A")
      ]
    larger =
      IML.fromList
      [ (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]

case_split_case5 :: Assertion
case_split_case5 =
  IML.split (5 <=..<= 10) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2   <..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5 <=..< 10, "A")
      , (10, "B")
      ]
    larger =
      IML.fromList
      [ (10 <..<= 20, "B")
      , (20 <..<= 30, "C")
      ]

case_split_case6 :: Assertion
case_split_case6 =
  IML.split (5 <=..< 20) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2   <..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5  <=..< 10, "A")
      , (10 <=..< 20, "B")
      ]
    larger =
      IML.fromList
      [ (20, "B")
      , (20 <..<= 30, "C")
      ]

case_split_case7 :: Assertion
case_split_case7 =
  IML.split (5 <=..<= 20) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2   <..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5  <=..<  10, "A")
      , (10 <=..<= 20, "B")
      ]
    larger =
      IML.fromList
      [ (20 <..<= 30, "C")
      ]

case_split_case8 :: Assertion
case_split_case8 =
  IML.split (5 <=..< 21) m @?= (smaller, middle, larger)
  where
    m :: IntervalMap Rational String
    m =
      IML.fromList
      [ (2   <..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<= 30, "C")
      ]
    smaller =
      IML.fromList
      [ (2  <..< 5, "A")
      ]
    middle =
      IML.fromList
      [ (5  <=..<  10, "A")
      , (10 <=..<= 20, "B")
      , (20  <..<  21, "C")
      ]
    larger =
      IML.fromList
      [ (21 <=..<= 30, "C")
      ]

{--------------------------------------------------------------------
  Eq
--------------------------------------------------------------------}

prop_Eq_reflexive :: Property
prop_Eq_reflexive =
  forAll arbitrary $ \(i :: IntervalMap Rational Integer) ->
    i == i

{--------------------------------------------------------------------
  Show / Read
--------------------------------------------------------------------}

prop_show_read_invariance :: Property
prop_show_read_invariance =
  forAll arbitrary $ \(i :: IntervalMap Rational Integer) ->
    i == read (show i)

{--------------------------------------------------------------------
  Monoid
--------------------------------------------------------------------}

prop_monoid_assoc :: Property
prop_monoid_assoc =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
  forAll arbitrary $ \b ->
  forAll arbitrary $ \c ->
    a <> (b <> c) == (a <> b) <> c

prop_monoid_unitL :: Property
prop_monoid_unitL =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    IML.empty <> a == a

prop_monoid_unitR :: Property
prop_monoid_unitR =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    a <> IML.empty == a

{--------------------------------------------------------------------
  NFData
--------------------------------------------------------------------}

prop_rnf :: Property
prop_rnf =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    rnf a == ()

{--------------------------------------------------------------------
  Hashable
--------------------------------------------------------------------}

prop_hash :: Property
prop_hash =
  forAll arbitrary $ \(a :: IntervalMap Rational Integer) ->
    hash a `seq` True

{- ------------------------------------------------------------------
  Data
------------------------------------------------------------------ -}

case_Data :: Assertion
case_Data = everywhere f i @?= (IML.singleton (1 <=..<= 2) 3 :: IntervalMap Integer Integer)
  where
    i :: IntervalMap Integer Integer
    i = IML.singleton (0 <=..<= 1) 2
    f x
      | Just (y :: Integer) <- cast x = fromJust $ cast (y + 1)
      | otherwise = x

{--------------------------------------------------------------------
  Generators
--------------------------------------------------------------------}

instance Arbitrary Interval.Boundary where
  arbitrary = arbitraryBoundedEnum

instance Arbitrary r => Arbitrary (Extended r) where
  arbitrary =
    oneof
    [ return NegInf
    , return PosInf
    , liftM Finite arbitrary
    ]

instance (Arbitrary r, Ord r) => Arbitrary (Interval r) where
  arbitrary = do
    lb <- arbitrary
    ub <- arbitrary
    return $ Interval.interval lb ub

instance (Arbitrary k, Arbitrary a, Ord k) => Arbitrary (IntervalMap k a) where
  arbitrary = IML.fromList <$> listOf arbitrary

------------------------------------------------------------------------
-- Test harness

intervalMapTestGroup :: TestTree
intervalMapTestGroup = $(testGroupGenerator)