packages feed

liquidhaskell-0.8.10.7: tests/todo/Intervals.hs

{-@ LIQUID "--no-termination" @-}

module Intervals where

type Offset = Int

-- | Invariant: Intervals are non-empty
{-@ data Interval = I
      { from :: Int
      , to   :: {v: Int | from < v }
      }
  @-}
data Interval  = I { from :: Offset, to :: Offset }

-- | Invariant: Intervals are sorted, disjoint and non-adjacent
{-@ type OrdIntervals N = [{v:Interval | N <= from v}]<{\fld v -> (to fld <= from v)}> @-}
type OrdIntervals = [Interval]

-- | Invariant: Intervals start with lower-bound of 0
{-@ data Intervals = Intervals { itvs :: OrdIntervals 0 } @-}
data Intervals = Intervals {itvs :: OrdIntervals }

-- | Invariant as a Haskell Predicate
{-@ okIntervals :: lb:Nat -> is:OrdIntervals lb -> {v : Bool | v } / [len is] @-}
okIntervals :: Int -> OrdIntervals -> Bool
okIntervals _ []            = True
okIntervals lb ((I f t) : is) = lb <= f && f < t && okIntervals t is

--------------------------------------------------------------------------------
-- | Unit tests
--------------------------------------------------------------------------------
okItv  = I 10 20
badItv = I 20 10

okItvs  = Intervals [I 10 20, I 30 40, I 40 50]
badItvs = Intervals [I 10 20, I 40 50, I 30 40]

--------------------------------------------------------------------------------
-- | Intersection
--------------------------------------------------------------------------------
intersect :: Intervals -> Intervals -> Intervals
intersect (Intervals is1) (Intervals is2) = Intervals (go 0 is1 is2)
  where
    {- AUTO! go :: lb:Int -> is1:OrdIntervals lb -> is2:OrdIntervals lb -> OrdIntervals lb @-}
    go :: Int -> OrdIntervals -> OrdIntervals -> OrdIntervals
    go _ _ [] = []
    go _ [] _ = []
    go lb (i1@(I f1 t1) : is1) (i2@(I f2 t2) : is2)
      -- reorder for symmetry
      | t1 < t2   = go lb (i2:is2) (i1:is1)
      -- disjoint
      | f1 >= f2  = go lb (i1:is1) is2
      -- subset
      | t1 == t2  = I f' t2 : go t2 is1 is2
      -- overlapping
      | otherwise = I f' t2 : go t2 ((I t2 t1) : is1) is2
      where
        f'        = max f1 f2

--------------------------------------------------------------------------------
-- | Union
--------------------------------------------------------------------------------
union :: Intervals -> Intervals -> Intervals
union (Intervals is1) (Intervals is2) = Intervals (go 0 is1 is2)
  where
    {- AUTO! go :: lb:Int -> is1:OrdIntervals lb -> is2:OrdIntervals lb -> OrdIntervals lb @-}
    go _ is [] = is
    go _ [] is = is
    go lb (i1@(I f1 t1) : is1) (i2@(I f2 t2) : is2)
      -- reorder for symmetry
      | t1 < t2 = go lb (i2:is2) (i1:is1)
      -- disjoint
      | f1 > t2 = i2 : go t2 (i1:is1) is2
      -- overlapping
      | otherwise  = go lb (i1 { from = f'} : is1) is2
      where f' = min (from i1) (from i2)

--------------------------------------------------------------------------------
-- | Difference
--------------------------------------------------------------------------------
subtract :: Intervals -> Intervals -> Intervals
subtract (Intervals is1) (Intervals is2) = Intervals (go 0 is1 is2)
  where
    {- AUTO! go :: lb:Int -> is1:OrdIntervals lb -> is2:OrdIntervals lb -> OrdIntervals lb @-}
    go _ is [] = is
    go _ [] _  = []
    go lb (i1@(I f1 t1) : is1) (i2@(I f2 t2) : is2)
      -- i2 past i1
      | t1 <= f2 = i1 : go t1 is1 (i2:is2)
      -- i1 past i2
      | t2 <= f1 = go lb (i1:is1) is2
      -- i1 contained in i2
      | f2 <= f1 , t1 <= t2 = go lb is1 (i2:is2)
      -- i2 covers beginning of i1
      | f1 >= f2 = go t2 (i1 { from = t2} : is1) is2
      -- i2 covers end of i1
      | t1 <= t2 = i1 { to = f2 } : go f2 is1 (i2:is2)
      -- i2 in the middle of i1
      | otherwise = I f1 f2 : go f2 (I t2 t1 : is1) is2