module Data.Ranged.RangedSet (
RSet,
rSetRanges,
makeRangedSet,
unsafeRangedSet,
validRangeList,
normaliseRangeList,
rSingleton,
rSetUnfold,
rSetIsEmpty,
rSetIsFull,
(-?-), rSetHas,
(-<=-), rSetIsSubset,
(-<-), rSetIsSubsetStrict,
(-\/-), rSetUnion,
(-/\-), rSetIntersection,
(-!-), rSetDifference,
rSetNegation,
rSetEmpty,
rSetFull,
prop_validNormalised,
prop_has,
prop_unfold,
prop_union,
prop_intersection,
prop_difference,
prop_negation,
prop_not_empty,
prop_empty,
prop_full,
prop_empty_intersection,
prop_full_union,
prop_union_superset,
prop_intersection_subset,
prop_diff_intersect,
prop_subset,
prop_strict_subset,
prop_union_strict_superset,
prop_intersection_commutes,
prop_union_commutes,
prop_intersection_associates,
prop_union_associates,
prop_de_morgan_intersection,
prop_de_morgan_union,
) where
import Data.Ranged.Boundaries
import Data.Ranged.Ranges
import Data.Monoid
import Data.Semigroup as Sem
import Data.List
import Test.QuickCheck
infixl 7 -/\-
infixl 6 -\/-, -!-
infixl 5 -<=-, -<-, -?-
newtype RSet v = RSet {rSetRanges :: [Range v]}
deriving (Eq, Show, Ord)
instance DiscreteOrdered a => Sem.Semigroup (RSet a) where
(<>) = rSetUnion
instance DiscreteOrdered a => Monoid (RSet a) where
mempty = rSetEmpty
mappend = (Sem.<>)
validRangeList :: DiscreteOrdered v => [Range v] -> Bool
validRangeList [] = True
validRangeList [Range lower upper] = lower <= upper
validRangeList rs = and $ zipWith okAdjacent rs (tail rs)
where
okAdjacent (Range lower1 upper1) (Range lower2 upper2) =
lower1 <= upper1 && upper1 <= lower2 && lower2 <= upper2
normaliseRangeList :: DiscreteOrdered v => [Range v] -> [Range v]
normaliseRangeList = normalise . sort . filter (not . rangeIsEmpty)
normalise :: DiscreteOrdered v => [Range v] -> [Range v]
normalise (r1:r2:rs) =
if overlap r1 r2
then normalise $
Range (rangeLower r1)
(max (rangeUpper r1) (rangeUpper r2))
: rs
else r1 : (normalise $ r2 : rs)
where
overlap (Range _ upper1) (Range lower2 _) = upper1 >= lower2
normalise rs = rs
makeRangedSet :: DiscreteOrdered v => [Range v] -> RSet v
makeRangedSet = RSet . normaliseRangeList
unsafeRangedSet :: DiscreteOrdered v => [Range v] -> RSet v
unsafeRangedSet = RSet
rSingleton :: DiscreteOrdered v => v -> RSet v
rSingleton v = unsafeRangedSet [singletonRange v]
rSetIsEmpty :: DiscreteOrdered v => RSet v -> Bool
rSetIsEmpty = null . rSetRanges
rSetIsFull :: DiscreteOrdered v => RSet v -> Bool
rSetIsFull = rSetIsEmpty . rSetNegation
rSetHas, (-?-) :: DiscreteOrdered v => RSet v -> v -> Bool
rSetHas (RSet ls) value = rSetHas1 ls
where
rSetHas1 [] = False
rSetHas1 (r:rs)
| value />/ rangeLower r = rangeHas r value || rSetHas1 rs
| otherwise = False
(-?-) = rSetHas
rSetIsSubset, (-<=-) :: DiscreteOrdered v => RSet v -> RSet v -> Bool
rSetIsSubset rs1 rs2 = rSetIsEmpty (rs1 -!- rs2)
(-<=-) = rSetIsSubset
rSetIsSubsetStrict, (-<-) :: DiscreteOrdered v => RSet v -> RSet v -> Bool
rSetIsSubsetStrict rs1 rs2 =
rSetIsEmpty (rs1 -!- rs2)
&& not (rSetIsEmpty (rs2 -!- rs1))
(-<-) = rSetIsSubsetStrict
rSetUnion, (-\/-) :: DiscreteOrdered v => RSet v -> RSet v -> RSet v
rSetUnion (RSet ls1) (RSet ls2) = RSet $ normalise $ merge ls1 ls2
where
merge ms1 [] = ms1
merge [] ms2 = ms2
merge ms1@(h1:t1) ms2@(h2:t2) =
if h1 < h2
then h1 : merge t1 ms2
else h2 : merge ms1 t2
(-\/-) = rSetUnion
rSetIntersection, (-/\-) :: DiscreteOrdered v => RSet v -> RSet v -> RSet v
rSetIntersection (RSet ls1) (RSet ls2) =
RSet $ filter (not . rangeIsEmpty) $ merge ls1 ls2
where
merge ms1@(h1:t1) ms2@(h2:t2) =
rangeIntersection h1 h2
: if rangeUpper h1 < rangeUpper h2
then merge t1 ms2
else merge ms1 t2
merge _ _ = []
(-/\-) = rSetIntersection
rSetDifference, (-!-) :: DiscreteOrdered v => RSet v -> RSet v -> RSet v
rSetDifference rs1 rs2 = rs1 -/\- (rSetNegation rs2)
(-!-) = rSetDifference
rSetNegation :: DiscreteOrdered a => RSet a -> RSet a
rSetNegation set = RSet $ ranges1 $ setBounds1
where
ranges1 (b1:b2:bs) = Range b1 b2 : ranges1 bs
ranges1 [BoundaryAboveAll] = []
ranges1 [b] = [Range b BoundaryAboveAll]
ranges1 _ = []
setBounds1 = case setBounds of
(BoundaryBelowAll : bs) -> bs
_ -> BoundaryBelowAll : setBounds
setBounds = bounds $ rSetRanges set
bounds (r:rs) = rangeLower r : rangeUpper r : bounds rs
bounds _ = []
rSetEmpty :: DiscreteOrdered a => RSet a
rSetEmpty = RSet []
rSetFull :: DiscreteOrdered a => RSet a
rSetFull = RSet [Range BoundaryBelowAll BoundaryAboveAll]
rSetUnfold :: DiscreteOrdered a =>
Boundary a
-> (Boundary a -> Boundary a)
-> (Boundary a -> Maybe (Boundary a))
-> RSet a
rSetUnfold bound upperFunc succFunc = RSet $ normalise $ ranges1 bound
where
ranges1 b =
Range b (upperFunc b)
: case succFunc b of
Just b2 -> ranges1 b2
Nothing -> []
instance (Arbitrary v, DiscreteOrdered v, Show v) =>
Arbitrary (RSet v)
where
arbitrary = frequency [
(1, return rSetEmpty),
(1, return rSetFull),
(18, do
ls <- arbitrary
return $ makeRangedSet $ rangeList $ sort ls
)]
where
rangeList (b1:b2:bs) = Range b1 b2 : rangeList bs
rangeList _ = []
instance (CoArbitrary v, DiscreteOrdered v, Show v) =>
CoArbitrary (RSet v)
where
coarbitrary (RSet ls) = variant (0 :: Int) . coarbitrary ls
prop_validNormalised :: (DiscreteOrdered a) => [Range a] -> Bool
prop_validNormalised ls = validRangeList $ normaliseRangeList ls
prop_has :: (DiscreteOrdered a) => [Range a] -> a -> Bool
prop_has ls v = (ls `rangeListHas` v) == makeRangedSet ls -?- v
prop_unfold :: Integer -> Bool
prop_unfold v = (v <= 99999 && head (show v) == '1') == (initial1 -?- v)
where
initial1 = rSetUnfold (BoundaryBelow 1) addNines times10
addNines (BoundaryBelow n) = BoundaryAbove $ n * 2 - 1
addNines _ = error "Can't happen"
times10 (BoundaryBelow n) =
if n <= 10000 then Just $ BoundaryBelow $ n * 10 else Nothing
times10 _ = error "Can't happen"
prop_union :: (DiscreteOrdered a ) => RSet a -> RSet a -> a -> Bool
prop_union rs1 rs2 v = (rs1 -?- v || rs2 -?- v) == ((rs1 -\/- rs2) -?- v)
prop_intersection :: (DiscreteOrdered a) => RSet a -> RSet a -> a -> Bool
prop_intersection rs1 rs2 v =
(rs1 -?- v && rs2 -?- v) == ((rs1 -/\- rs2) -?- v)
prop_difference :: (DiscreteOrdered a) => RSet a -> RSet a -> a -> Bool
prop_difference rs1 rs2 v =
(rs1 -?- v && not (rs2 -?- v)) == ((rs1 -!- rs2) -?- v)
prop_negation :: (DiscreteOrdered a) => RSet a -> a -> Bool
prop_negation rs v = rs -?- v == not (rSetNegation rs -?- v)
prop_not_empty :: (DiscreteOrdered a) => RSet a -> a -> Property
prop_not_empty rs v = (rs -?- v) ==> not (rSetIsEmpty rs)
prop_empty :: (DiscreteOrdered a) => a -> Bool
prop_empty v = not (rSetEmpty -?- v)
prop_full :: (DiscreteOrdered a) => a -> Bool
prop_full v = rSetFull -?- v
prop_empty_intersection :: (DiscreteOrdered a) => RSet a -> Bool
prop_empty_intersection rs =
rSetIsEmpty (rs -/\- rSetNegation rs)
prop_full_union :: (DiscreteOrdered a) => RSet a -> Bool
prop_full_union rs =
rSetIsFull (rs -\/- rSetNegation rs)
prop_union_superset :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_union_superset rs1 rs2 =
rs1 -<=- u && rs2 -<=- u
where
u = rs1 -\/- rs2
prop_intersection_subset :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_intersection_subset rs1 rs2 = i -<=- rs1 && i -<=- rs2
where
i = rs1 -/\- rs2
prop_diff_intersect :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_diff_intersect rs1 rs2 = rSetIsEmpty ((rs1 -!- rs2) -/\- rs2)
prop_subset :: (DiscreteOrdered a) => RSet a -> Bool
prop_subset rs = rs -<=- rs
prop_strict_subset :: (DiscreteOrdered a) => RSet a -> Bool
prop_strict_subset rs = not (rs -<- rs)
prop_union_strict_superset :: (DiscreteOrdered a) => RSet a -> RSet a -> Property
prop_union_strict_superset rs1 rs2 =
(not $ rSetIsEmpty (rs1 -!- rs2)) ==> (rs2 -<- (rs1 -\/- rs2))
prop_intersection_commutes :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_intersection_commutes rs1 rs2 = (rs1 -/\- rs2) == (rs2 -/\- rs1)
prop_union_commutes :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_union_commutes rs1 rs2 = (rs1 -\/- rs2) == (rs2 -\/- rs1)
prop_intersection_associates :: (DiscreteOrdered a) =>
RSet a -> RSet a -> RSet a -> Bool
prop_intersection_associates rs1 rs2 rs3 =
((rs1 -/\- rs2) -/\- rs3) == (rs1 -/\- (rs2 -/\- rs3))
prop_union_associates :: (DiscreteOrdered a) =>
RSet a -> RSet a -> RSet a -> Bool
prop_union_associates rs1 rs2 rs3 =
((rs1 -\/- rs2) -\/- rs3) == (rs1 -\/- (rs2 -\/- rs3))
prop_de_morgan_intersection :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_de_morgan_intersection rs1 rs2 =
rSetNegation (rs1 -/\- rs2) == (rSetNegation rs1 -\/- rSetNegation rs2)
prop_de_morgan_union :: (DiscreteOrdered a) => RSet a -> RSet a -> Bool
prop_de_morgan_union rs1 rs2 =
rSetNegation (rs1 -\/- rs2) == (rSetNegation rs1 -/\- rSetNegation rs2)