RangeMap.hs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. {-# LANGUAGE TemplateHaskell #-}
  2. module Internal.Utils.RangeMap
  3. ( tests
  4. , overlaps
  5. , toListProperty
  6. , coveringRangeProperty
  7. )
  8. where
  9. import Agda.Interaction.Highlighting.Range
  10. import Agda.Utils.Null
  11. import Agda.Utils.RangeMap
  12. import Agda.Utils.Tuple
  13. import Prelude hiding (null, splitAt)
  14. import qualified Data.IntMap as IntMap
  15. import qualified Data.IntSet as IntSet
  16. import Data.List (sort)
  17. import qualified Data.Map.Strict as Map
  18. import Data.Maybe (isJust)
  19. import Data.Semigroup
  20. import Data.Strict.Tuple (Pair(..))
  21. import Internal.Helpers
  22. import Internal.Interaction.Highlighting.Range ()
  23. ------------------------------------------------------------------------
  24. -- Helper definitions
  25. -- | If the two ranges are defined and overlap, then the test case is
  26. -- classified as \"overlapping\".
  27. overlaps :: Testable p => Maybe Range -> Maybe Range -> p -> Property
  28. overlaps r1 r2 =
  29. classify
  30. (maybe False (maybe (const False) overlapping r1) r2)
  31. "overlapping"
  32. -- | Classifies test cases based on whether the intersection of the
  33. -- range and the 'RangeMap' is non-empty, and whether any range in the
  34. -- internal representation of the 'RangeMap' is \"split\" by the given
  35. -- range: to the left, to the right, or on both sides.
  36. intersectsSplits :: Testable p => Range -> RangeMap a -> p -> Property
  37. intersectsSplits r f =
  38. classify (not (null inside)) "non-empty intersection" .
  39. classify (rangeContainingPoint (from r)) "split to the left" .
  40. classify (rangeContainingPoint (to r)) "split to the right" .
  41. classify rangeContainingRange "one range split twice"
  42. where
  43. (inside, outside) = insideAndOutside r f
  44. -- Is there a range that contains the given point, but not as the
  45. -- starting point? In that case the range's endpoint is returned.
  46. rangeContainingPoint' p = case Map.splitLookup p (rangeMap f) of
  47. (_, Just _, _) -> Nothing
  48. (smaller, Nothing, _) -> case Map.lookupMax smaller of
  49. Just (p1, PairInt (p2 :!: _))
  50. | p1 < p && p < p2 -> Just p2
  51. _ -> Nothing
  52. -- Is there a range that contains the given point, but not as the
  53. -- starting point?
  54. rangeContainingPoint = isJust . rangeContainingPoint'
  55. -- Is there a range that contains the range r, starts before r, and
  56. -- ends after r?
  57. rangeContainingRange = case rangeContainingPoint' (from r) of
  58. Nothing -> False
  59. Just end -> to r < end
  60. -- | A property that should be satisfied by 'toList'.
  61. toListProperty :: (Eq a, IsBasicRangeMap a m) => m -> Bool
  62. toListProperty f =
  63. all (not . null) rs
  64. &&
  65. increasing rs
  66. &&
  67. toMap f ==
  68. IntMap.fromList [ (p, m) | (r, m) <- rms, p <- rangeToPositions r ]
  69. where
  70. rms = toList f
  71. rs = map fst rms
  72. increasing [] = True
  73. increasing rs =
  74. and $ zipWith (<=) (map to $ init rs) (map from $ tail rs)
  75. -- | A property that should be satisfied by 'coveringRange'.
  76. coveringRangeProperty :: IsBasicRangeMap a m => m -> Bool
  77. coveringRangeProperty f =
  78. coveringRange f ==
  79. do from <- fst <$> IntMap.lookupMin (toMap f)
  80. to <- fst <$> IntMap.lookupMax (toMap f)
  81. return (Range { from = from, to = to + 1 })
  82. -- | A type synonym used for some tests below.
  83. type RangeMap' = RangeMap [Int]
  84. -- | A variant of 'RangeMap'' with an 'Eq' instance.
  85. newtype RangeMap'Eq = RangeMap'Eq RangeMap'
  86. deriving (Arbitrary, Semigroup, Monoid, Show)
  87. instance Eq RangeMap'Eq where
  88. RangeMap'Eq f1 == RangeMap'Eq f2 = toMap f1 == toMap f2
  89. ------------------------------------------------------------------------
  90. -- Invariants
  91. prop_rangeMapInvariant1 :: RangeMap' -> Bool
  92. prop_rangeMapInvariant1 = rangeMapInvariant
  93. prop_rangeMapInvariant2 :: RangeMap' -> Bool
  94. prop_rangeMapInvariant2 = all rangeMapInvariant . shrink
  95. prop_rangeMapInvariant3 :: [Ranges] -> [Int] -> Bool
  96. prop_rangeMapInvariant3 rs m =
  97. rangeMapInvariant $ several (take 5 rs) m
  98. prop_rangeMapInvariant4 :: Ranges -> [Int] -> Bool
  99. prop_rangeMapInvariant4 r m = rangeMapInvariant $ singleton r m
  100. prop_rangeMapInvariant5 :: [(Range, [Int])] -> Bool
  101. prop_rangeMapInvariant5 rs =
  102. rangeMapInvariant $ fromNonOverlappingNonEmptyAscendingList rs'
  103. where
  104. rs' =
  105. increasing $
  106. filter (not . null . fst) rs
  107. increasing (r1 : r2 : rs)
  108. | to (fst r1) <= from (fst r2) = r1 : increasing (r2 : rs)
  109. | otherwise = increasing (r1 : rs)
  110. increasing rs = rs
  111. prop_rangeMapInvariant6 ::
  112. ([Int] -> [Int] -> [Int]) ->
  113. Range -> [Int] -> RangeMap' -> Property
  114. prop_rangeMapInvariant6 g r m f =
  115. overlaps (Just r) (coveringRange f) $
  116. rangeMapInvariant $ insert g r m f
  117. prop_rangeMapInvariant7 :: RangeMap' -> RangeMap' -> Property
  118. prop_rangeMapInvariant7 f1 f2 =
  119. overlaps (coveringRange f1) (coveringRange f2) $
  120. rangeMapInvariant $ f1 <> f2
  121. prop_rangeMapInvariant8 :: Int -> RangeMap' -> Property
  122. prop_rangeMapInvariant8 p f =
  123. overlaps (Just (Range { from = p, to = p + 1 })) (coveringRange f) $
  124. all rangeMapInvariant $ (\(f1, f2) -> [f1, f2]) $
  125. splitAt p f
  126. prop_rangeMapInvariant9 :: Range -> RangeMap' -> Property
  127. prop_rangeMapInvariant9 r f =
  128. overlaps (Just r) (coveringRange f) $
  129. all rangeMapInvariant $ (\(f1, f2) -> [f1, f2]) $
  130. insideAndOutside r f
  131. -- | A property that is intended to exercise the else branch in
  132. -- 'insideAndOutside'.
  133. prop_rangeMapInvariant10 :: RangeMap' -> Property
  134. prop_rangeMapInvariant10 f =
  135. forAll (rangeInside f) $ \r ->
  136. intersectsSplits r f $
  137. all rangeMapInvariant $ (\(f1, f2) -> [f1, f2]) $
  138. insideAndOutside r f
  139. prop_rangeMapInvariant11 :: Range -> RangeMap' -> Property
  140. prop_rangeMapInvariant11 r f =
  141. overlaps (Just r) (coveringRange f) $
  142. rangeMapInvariant $ restrictTo r f
  143. prop_rangeInvariant1 :: RangeMap' -> Bool
  144. prop_rangeInvariant1 f = all (rangeInvariant . fst) (toList f)
  145. prop_rangeInvariant2 :: RangeMap' -> Bool
  146. prop_rangeInvariant2 = maybe True rangeInvariant . coveringRange
  147. prop_rangeInvariant3 :: RangeMap' -> Property
  148. prop_rangeInvariant3 f = forAll (rangeInside f) rangeInvariant
  149. ------------------------------------------------------------------------
  150. -- Operations
  151. prop_several :: [Ranges] -> [Int] -> Bool
  152. prop_several rss' m =
  153. toMap (several rss m :: RangeMap') ==
  154. IntMap.fromListWith (flip (<>))
  155. [ (p, m)
  156. | rs <- rss
  157. , p <- rangesToPositions rs
  158. ]
  159. where
  160. rss = take 5 rss'
  161. prop_empty :: Bool
  162. prop_empty = toMap (empty :: RangeMap') == IntMap.empty
  163. prop_null :: RangeMap' -> Bool
  164. prop_null f = null f == IntMap.null (toMap f)
  165. prop_singleton :: Ranges -> [Int] -> Bool
  166. prop_singleton rs m =
  167. toMap (singleton rs m :: RangeMap') ==
  168. IntMap.fromList
  169. [ (p, m)
  170. | p <- rangesToPositions rs
  171. ]
  172. prop_toList :: RangeMap' -> Bool
  173. prop_toList = toListProperty
  174. prop_coveringRange :: RangeMap' -> Bool
  175. prop_coveringRange = coveringRangeProperty
  176. prop_fromNonOverlappingNonEmptyAscendingList_toList :: RangeMap' -> Bool
  177. prop_fromNonOverlappingNonEmptyAscendingList_toList f =
  178. toMap (fromNonOverlappingNonEmptyAscendingList (toList f)) ==
  179. toMap f
  180. prop_insert ::
  181. ([Int] -> [Int] -> [Int]) ->
  182. Range -> [Int] -> RangeMap' -> Property
  183. prop_insert g r m f =
  184. overlaps (Just r) (coveringRange f) $
  185. toMap (insert g r m f) ==
  186. IntMap.unionWith
  187. g
  188. (IntMap.fromList [ (p, m) | p <- rangeToPositions r ])
  189. (toMap f)
  190. prop_append :: RangeMap' -> RangeMap' -> Property
  191. prop_append f1 f2 =
  192. overlaps (coveringRange f1) (coveringRange f2) $
  193. toMap (f1 <> f2) ==
  194. IntMap.unionWith mappend (toMap f1) (toMap f2)
  195. prop_splitAt :: Int -> RangeMap' -> Property
  196. prop_splitAt p f =
  197. overlaps (Just (Range { from = p, to = p + 1 })) (coveringRange f) $
  198. all (< p) (positions f1) &&
  199. all (>= p) (positions f2) &&
  200. toMap (f1 <> f2) == toMap f
  201. where
  202. (f1, f2) = splitAt p f
  203. positions = IntMap.keys . toMap
  204. prop_insideAndOutside1 :: Range -> RangeMap' -> Property
  205. prop_insideAndOutside1 r f =
  206. overlaps (Just r) (coveringRange f) $
  207. toMap inside == IntMap.restrictKeys (toMap f) positions
  208. &&
  209. toMap outside == IntMap.withoutKeys (toMap f) positions
  210. where
  211. (inside, outside) = insideAndOutside r f
  212. positions = IntSet.fromList $ rangeToPositions r
  213. -- | A property that is intended to exercise the else branch in
  214. -- 'insideAndOutside'.
  215. prop_insideAndOutside2 :: RangeMap' -> Property
  216. prop_insideAndOutside2 f =
  217. forAll (rangeInside f) $ \r ->
  218. intersectsSplits r f $
  219. prop_insideAndOutside1 r f
  220. prop_restrictTo :: Range -> RangeMap' -> Property
  221. prop_restrictTo r f =
  222. overlaps (Just r) (coveringRange f) $
  223. toMap (restrictTo r f) ==
  224. IntMap.restrictKeys (toMap f) (IntSet.fromList (rangeToPositions r))
  225. ------------------------------------------------------------------------
  226. -- Algebraic properties
  227. -- | 'RangeMap'Eq' is a monoid.
  228. prop_monoid :: Property3 RangeMap'Eq
  229. prop_monoid = isMonoid
  230. ------------------------------------------------------------------------
  231. -- Generators
  232. instance (Arbitrary a, Semigroup a) => Arbitrary (RangeMap a) where
  233. arbitrary = smaller 5 $ do
  234. rs <- (\ns1 ns2 ->
  235. toRanges $ sort $
  236. ns1 ++ concatMap (\n -> [n, succ n]) (ns2 :: [Int])) <$>
  237. arbitrary <*> arbitrary
  238. RangeMap . Map.fromList <$>
  239. mapM (\r -> (\m -> (from r, PairInt (to r :!: m))) <$> arbitrary)
  240. rs
  241. where
  242. toRanges (f : t : rs)
  243. | f == t = toRanges (t : rs)
  244. | otherwise = Range { from = f, to = t } :
  245. toRanges (case rs of
  246. f : rs | t == f -> rs
  247. _ -> rs)
  248. toRanges _ = []
  249. shrink f =
  250. [ RangeMap (Map.deleteAt i (rangeMap f))
  251. | i <- [0 .. Map.size (rangeMap f) - 1]
  252. ] ++
  253. [ RangeMap (Map.updateAt (\_ _ -> Just (PairInt (p :!: x)))
  254. i (rangeMap f))
  255. | i <- [0 .. Map.size (rangeMap f) - 1]
  256. , let (_, PairInt (p :!: x)) = Map.elemAt i (rangeMap f)
  257. , x <- shrink x
  258. ]
  259. instance CoArbitrary a => CoArbitrary (RangeMap a) where
  260. coarbitrary (RangeMap f) =
  261. coarbitrary $
  262. fmap (mapSnd (\(PairInt (x :!: y)) -> (x, y))) $
  263. Map.toAscList f
  264. -- | A range that is entirely inside a given 'RangeMap'.
  265. rangeInside :: RangeMap a -> Gen Range
  266. rangeInside f =
  267. case coveringRange f of
  268. Nothing -> return (Range { from = 0, to = 0 })
  269. Just r -> oneof
  270. [ do
  271. low <- chooseInt (from r, to r)
  272. high <- chooseInt (low, to r)
  273. return (Range { from = low, to = high })
  274. , do
  275. high <- chooseInt (from r, to r)
  276. low <- chooseInt (from r, high)
  277. return (Range { from = low, to = high })
  278. ]
  279. prop_rangeInside :: RangeMap' -> Property
  280. prop_rangeInside f =
  281. forAll (rangeInside f) $ \r ->
  282. case coveringRange f of
  283. Nothing -> from r == to r
  284. Just r' -> from r' <= from r && to r <= to r'
  285. ------------------------------------------------------------------------
  286. -- * All tests
  287. ------------------------------------------------------------------------
  288. -- Template Haskell hack to make the following $allProperties work
  289. -- under ghc-7.8.
  290. return [] -- KEEP!
  291. -- | All tests as collected by 'allProperties'.
  292. --
  293. -- Using 'allProperties' is convenient and superior to the manual
  294. -- enumeration of tests, since the name of the property is added
  295. -- automatically.
  296. tests :: TestTree
  297. tests = testProperties "Internal.Utils.RangeMap" $allProperties