0 | module IotaTime.Interval
2 | import public Data.So
3 | import public IotaTime.Duration
4 | import public IotaTime.Instant
5 | import Derive.Prelude
7 | %language ElabReflection
13 | record IntervalRep where
14 | constructor MkInterval
15 | storedStart : Instant
18 | (toNanosecondsSinceEpoch storedStart <= toNanosecondsSinceEpoch storedEnd)
22 | Interval = IntervalRep
26 | isValidInterval : Instant -> Instant -> Bool
27 | isValidInterval start end =
28 | toNanosecondsSinceEpoch start <= toNanosecondsSinceEpoch end
33 | interval : (startNanoseconds, endNanoseconds : Integer) ->
34 | {auto 0 valid : So (startNanoseconds <= endNanoseconds)} -> Interval
35 | interval startNanoseconds endNanoseconds = MkInterval
36 | (fromNanosecondsSinceEpoch startNanoseconds)
37 | (fromNanosecondsSinceEpoch endNanoseconds)
38 | (rewrite instantNanosecondsRoundTrip startNanoseconds in
39 | rewrite instantNanosecondsRoundTrip endNanoseconds in valid)
41 | checkedInterval : (start, end : Instant) ->
42 | {auto 0 valid : So (isValidInterval start end)} -> Interval
43 | checkedInterval start end = MkInterval start end valid
46 | data IntervalError = ReversedInterval Instant Instant
50 | refineInterval : (start, end : Instant) -> Either IntervalError Interval
51 | refineInterval start end =
52 | case choose (isValidInterval start end) of
53 | Left valid => Right (checkedInterval start end @{valid})
54 | Right _ => Left (ReversedInterval start end)
57 | start : Interval -> Instant
58 | start (MkInterval value _ _) = value
61 | end : Interval -> Instant
62 | end (MkInterval _ value _) = value
66 | 0 intervalIsValid : (value : Interval) ->
67 | So (isValidInterval (start value) (end value))
68 | intervalIsValid (MkInterval _ _ valid) = valid
72 | contains : Interval -> Instant -> Bool
73 | contains value instant = start value <= instant && instant < end value
77 | isEmpty : Interval -> Bool
79 | toNanosecondsSinceEpoch (start value) ==
80 | toNanosecondsSinceEpoch (end value)
84 | overlaps : Interval -> Interval -> Bool
85 | overlaps left right = not (isEmpty left) && not (isEmpty right) &&
86 | toNanosecondsSinceEpoch (start left) <
87 | toNanosecondsSinceEpoch (end right) &&
88 | toNanosecondsSinceEpoch (start right) <
89 | toNanosecondsSinceEpoch (end left)
93 | isAdjacent : Interval -> Interval -> Bool
94 | isAdjacent left right =
95 | toNanosecondsSinceEpoch (end left) ==
96 | toNanosecondsSinceEpoch (start right) ||
97 | toNanosecondsSinceEpoch (end right) ==
98 | toNanosecondsSinceEpoch (start left)
102 | intersectionStart : Interval -> Interval -> Instant
103 | intersectionStart left right = max (start left) (start right)
107 | intersectionEnd : Interval -> Interval -> Instant
108 | intersectionEnd left right = min (end left) (end right)
112 | hasNonEmptyIntersection : Interval -> Interval -> Bool
113 | hasNonEmptyIntersection left right =
114 | isValidInterval (intersectionStart left right) (intersectionEnd left right) &&
115 | toNanosecondsSinceEpoch (intersectionStart left right) <
116 | toNanosecondsSinceEpoch (intersectionEnd left right)
118 | 0 andLeft : (left, right : Bool) -> So (left && right) -> So left
119 | andLeft True True Oh = Oh
121 | 0 andRight : (left, right : Bool) -> So (left && right) -> So right
122 | andRight True True Oh = Oh
126 | intersection : (left, right : Interval) ->
127 | {auto 0 intersects : So
128 | (hasNonEmptyIntersection left right)} ->
130 | intersection left right @{intersects} = MkInterval
131 | (intersectionStart left right)
132 | (intersectionEnd left right)
135 | (intersectionStart left right) (intersectionEnd left right))
136 | (toNanosecondsSinceEpoch (intersectionStart left right) <
137 | toNanosecondsSinceEpoch (intersectionEnd left right))
141 | data IntersectionError = NoNonEmptyIntersection
145 | refineIntersection : (left, right : Interval) ->
146 | Either IntersectionError Interval
147 | refineIntersection left right =
148 | case choose (hasNonEmptyIntersection left right) of
149 | Left intersects => Right (intersection left right @{intersects})
150 | Right _ => Left NoNonEmptyIntersection
154 | unionStart : Interval -> Interval -> Instant
155 | unionStart left right =
156 | if isEmpty left then start right
157 | else if isEmpty right then start left
158 | else if toNanosecondsSinceEpoch (start left) <=
159 | toNanosecondsSinceEpoch (start right)
165 | unionEnd : Interval -> Interval -> Instant
166 | unionEnd left right =
167 | if isEmpty left then end right
168 | else if isEmpty right then end left
169 | else if toNanosecondsSinceEpoch (end left) >=
170 | toNanosecondsSinceEpoch (end right)
175 | connectedRelationship : Interval -> Interval -> Bool
176 | connectedRelationship left right =
177 | isEmpty left || isEmpty right || overlaps left right || isAdjacent left right
180 | isConnected : Interval -> Interval -> Bool
181 | isConnected left right = connectedRelationship left right &&
182 | isValidInterval (unionStart left right) (unionEnd left right)
188 | union : (left, right : Interval) ->
189 | {auto 0 connected : So (isConnected left right)} ->
191 | union left right @{connected} = MkInterval
192 | (unionStart left right)
193 | (unionEnd left right)
195 | (connectedRelationship left right)
196 | (isValidInterval (unionStart left right) (unionEnd left right))
200 | data UnionError = DisconnectedIntervals
204 | refineUnion : (left, right : Interval) -> Either UnionError Interval
205 | refineUnion left right = case choose (isConnected left right) of
206 | Left connected => Right (union left right @{connected})
207 | Right _ => Left DisconnectedIntervals
211 | duration : Interval -> Duration
212 | duration value = difference (end value) (start value)
214 | %runElab derive `{IntervalRep
} [Eq, Ord]
217 | Show IntervalRep where
218 | show value = "interval " ++ show (start value) ++ " " ++ show (end value)
225 | isValidUnboundedInterval : Maybe Instant -> Maybe Instant -> Bool
226 | isValidUnboundedInterval (Just start) (Just end) =
227 | isValidInterval start end
228 | isValidUnboundedInterval _ _ = True
231 | isValidUnboundedNanosecondInterval : Maybe Integer -> Maybe Integer -> Bool
232 | isValidUnboundedNanosecondInterval (Just start) (Just end) = start <= end
233 | isValidUnboundedNanosecondInterval _ _ = True
236 | record UnboundedIntervalRep where
237 | constructor MkUnboundedInterval
238 | storedUnboundedStart : Maybe Instant
239 | storedUnboundedEnd : Maybe Instant
241 | (isValidUnboundedInterval storedUnboundedStart storedUnboundedEnd)
244 | UnboundedInterval : Type
245 | UnboundedInterval = UnboundedIntervalRep
249 | unboundedInterval : (startNanoseconds, endNanoseconds : Maybe Integer) ->
250 | {auto 0 valid : So (isValidUnboundedNanosecondInterval
251 | startNanoseconds endNanoseconds)} ->
253 | unboundedInterval Nothing Nothing = MkUnboundedInterval Nothing Nothing Oh
254 | unboundedInterval Nothing (Just end) = MkUnboundedInterval Nothing
255 | (Just (fromNanosecondsSinceEpoch end)) Oh
256 | unboundedInterval (Just start) Nothing = MkUnboundedInterval
257 | (Just (fromNanosecondsSinceEpoch start)) Nothing Oh
258 | unboundedInterval (Just start) (Just end) @{valid} = MkUnboundedInterval
259 | (Just (fromNanosecondsSinceEpoch start))
260 | (Just (fromNanosecondsSinceEpoch end))
261 | (rewrite instantNanosecondsRoundTrip start in
262 | rewrite instantNanosecondsRoundTrip end in valid)
266 | refineUnboundedInterval : (start, end : Maybe Instant) ->
267 | Either IntervalError UnboundedInterval
268 | refineUnboundedInterval (Just start) (Just end) =
269 | case choose (isValidUnboundedInterval (Just start) (Just end)) of
270 | Left valid => Right (MkUnboundedInterval (Just start) (Just end) valid)
271 | Right _ => Left (ReversedInterval start end)
272 | refineUnboundedInterval Nothing Nothing =
273 | Right (MkUnboundedInterval Nothing Nothing Oh)
274 | refineUnboundedInterval Nothing (Just end) =
275 | Right (MkUnboundedInterval Nothing (Just end) Oh)
276 | refineUnboundedInterval (Just start) Nothing =
277 | Right (MkUnboundedInterval (Just start) Nothing Oh)
280 | unboundedStart : UnboundedInterval -> Maybe Instant
281 | unboundedStart (MkUnboundedInterval value _ _) = value
284 | unboundedEnd : UnboundedInterval -> Maybe Instant
285 | unboundedEnd (MkUnboundedInterval _ value _) = value
290 | 0 unboundedIntervalIsValid : (value : UnboundedInterval) ->
291 | So (isValidUnboundedInterval
292 | (unboundedStart value) (unboundedEnd value))
293 | unboundedIntervalIsValid (MkUnboundedInterval _ _ valid) = valid
297 | toUnboundedInterval : Interval -> UnboundedInterval
298 | toUnboundedInterval value = MkUnboundedInterval (Just (start value))
299 | (Just (end value)) (intervalIsValid value)
303 | toBoundedInterval : UnboundedInterval -> Maybe Interval
305 | (MkUnboundedInterval (Just start) (Just end) valid) =
306 | Just (MkInterval start end valid)
307 | toBoundedInterval _ = Nothing
311 | unboundedContains : UnboundedInterval -> Instant -> Bool
312 | unboundedContains value instant =
313 | case (unboundedStart value, unboundedEnd value) of
314 | (Nothing, Nothing) => True
315 | (Nothing, Just end) => instant < end
316 | (Just start, Nothing) => start <= instant
317 | (Just start, Just end) => start <= instant && instant < end
321 | unboundedIsEmpty : UnboundedInterval -> Bool
322 | unboundedIsEmpty (MkUnboundedInterval (Just start) (Just end) _) =
323 | toNanosecondsSinceEpoch start == toNanosecondsSinceEpoch end
324 | unboundedIsEmpty _ = False
329 | unboundedEndAfterStart : Maybe Instant -> Maybe Instant -> Bool
330 | unboundedEndAfterStart Nothing _ = True
331 | unboundedEndAfterStart _ Nothing = True
332 | unboundedEndAfterStart (Just end) (Just start) =
333 | toNanosecondsSinceEpoch start < toNanosecondsSinceEpoch end
337 | unboundedOverlaps : UnboundedInterval -> UnboundedInterval -> Bool
338 | unboundedOverlaps left right =
339 | not (unboundedIsEmpty left) && not (unboundedIsEmpty right) &&
340 | unboundedEndAfterStart (unboundedEnd left) (unboundedStart right) &&
341 | unboundedEndAfterStart (unboundedEnd right) (unboundedStart left)
345 | finiteUnboundedBoundsEqual : Maybe Instant -> Maybe Instant -> Bool
346 | finiteUnboundedBoundsEqual (Just left) (Just right) =
347 | toNanosecondsSinceEpoch left == toNanosecondsSinceEpoch right
348 | finiteUnboundedBoundsEqual _ _ = False
352 | unboundedIsAdjacent : UnboundedInterval -> UnboundedInterval -> Bool
353 | unboundedIsAdjacent left right =
354 | finiteUnboundedBoundsEqual (unboundedEnd left) (unboundedStart right) ||
355 | finiteUnboundedBoundsEqual (unboundedEnd right) (unboundedStart left)
357 | laterStart : Maybe Instant -> Maybe Instant -> Maybe Instant
358 | laterStart Nothing right = right
359 | laterStart left Nothing = left
360 | laterStart (Just left) (Just right) = Just
361 | (if toNanosecondsSinceEpoch left >= toNanosecondsSinceEpoch right
362 | then left else right)
364 | earlierEnd : Maybe Instant -> Maybe Instant -> Maybe Instant
365 | earlierEnd Nothing right = right
366 | earlierEnd left Nothing = left
367 | earlierEnd (Just left) (Just right) = Just
368 | (if toNanosecondsSinceEpoch left <= toNanosecondsSinceEpoch right
369 | then left else right)
371 | earlierStart : Maybe Instant -> Maybe Instant -> Maybe Instant
372 | earlierStart Nothing _ = Nothing
373 | earlierStart _ Nothing = Nothing
374 | earlierStart (Just left) (Just right) = Just
375 | (if toNanosecondsSinceEpoch left <= toNanosecondsSinceEpoch right
376 | then left else right)
378 | laterEnd : Maybe Instant -> Maybe Instant -> Maybe Instant
379 | laterEnd Nothing _ = Nothing
380 | laterEnd _ Nothing = Nothing
381 | laterEnd (Just left) (Just right) = Just
382 | (if toNanosecondsSinceEpoch left >= toNanosecondsSinceEpoch right
383 | then left else right)
387 | unboundedIntersectionStart : UnboundedInterval -> UnboundedInterval ->
389 | unboundedIntersectionStart left right =
390 | laterStart (unboundedStart left) (unboundedStart right)
394 | unboundedIntersectionEnd : UnboundedInterval -> UnboundedInterval ->
396 | unboundedIntersectionEnd left right =
397 | earlierEnd (unboundedEnd left) (unboundedEnd right)
401 | hasNonEmptyUnboundedIntersection : UnboundedInterval ->
402 | UnboundedInterval -> Bool
403 | hasNonEmptyUnboundedIntersection left right =
404 | isValidUnboundedInterval
405 | (unboundedIntersectionStart left right)
406 | (unboundedIntersectionEnd left right) &&
407 | unboundedOverlaps left right
411 | unboundedIntersection : (left, right : UnboundedInterval) ->
412 | {auto 0 intersects : So
413 | (hasNonEmptyUnboundedIntersection left right)} ->
415 | unboundedIntersection left right @{intersects} = MkUnboundedInterval
416 | (unboundedIntersectionStart left right)
417 | (unboundedIntersectionEnd left right)
419 | (isValidUnboundedInterval
420 | (unboundedIntersectionStart left right)
421 | (unboundedIntersectionEnd left right))
422 | (unboundedOverlaps left right)
428 | refineUnboundedIntersection : (left, right : UnboundedInterval) ->
429 | Either IntersectionError UnboundedInterval
430 | refineUnboundedIntersection left right =
431 | case choose (hasNonEmptyUnboundedIntersection left right) of
433 | Right (unboundedIntersection left right @{intersects})
434 | Right _ => Left NoNonEmptyIntersection
438 | unboundedUnionStart : UnboundedInterval -> UnboundedInterval -> Maybe Instant
439 | unboundedUnionStart left right =
440 | if unboundedIsEmpty left then unboundedStart right
441 | else if unboundedIsEmpty right then unboundedStart left
442 | else earlierStart (unboundedStart left) (unboundedStart right)
446 | unboundedUnionEnd : UnboundedInterval -> UnboundedInterval -> Maybe Instant
447 | unboundedUnionEnd left right =
448 | if unboundedIsEmpty left then unboundedEnd right
449 | else if unboundedIsEmpty right then unboundedEnd left
450 | else laterEnd (unboundedEnd left) (unboundedEnd right)
453 | unboundedConnectedRelationship : UnboundedInterval ->
454 | UnboundedInterval -> Bool
455 | unboundedConnectedRelationship left right =
456 | unboundedIsEmpty left || unboundedIsEmpty right ||
457 | unboundedOverlaps left right || unboundedIsAdjacent left right
460 | unboundedIsConnected : UnboundedInterval -> UnboundedInterval -> Bool
461 | unboundedIsConnected left right = unboundedConnectedRelationship left right &&
462 | isValidUnboundedInterval
463 | (unboundedUnionStart left right) (unboundedUnionEnd left right)
469 | unboundedUnion : (left, right : UnboundedInterval) ->
470 | {auto 0 connected : So (unboundedIsConnected left right)} ->
472 | unboundedUnion left right @{connected} = MkUnboundedInterval
473 | (unboundedUnionStart left right)
474 | (unboundedUnionEnd left right)
476 | (unboundedConnectedRelationship left right)
477 | (isValidUnboundedInterval
478 | (unboundedUnionStart left right) (unboundedUnionEnd left right))
483 | refineUnboundedUnion : (left, right : UnboundedInterval) ->
484 | Either UnionError UnboundedInterval
485 | refineUnboundedUnion left right =
486 | case choose (unboundedIsConnected left right) of
487 | Left connected => Right (unboundedUnion left right @{connected})
488 | Right _ => Left DisconnectedIntervals
492 | unboundedDuration : UnboundedInterval -> Maybe Duration
493 | unboundedDuration (MkUnboundedInterval (Just start) (Just end) _) =
494 | Just (difference end start)
495 | unboundedDuration _ = Nothing
498 | Eq UnboundedIntervalRep where
499 | left == right = unboundedStart left == unboundedStart right &&
500 | unboundedEnd left == unboundedEnd right
502 | compareStarts : Maybe Instant -> Maybe Instant -> Ordering
503 | compareStarts Nothing Nothing = EQ
504 | compareStarts Nothing (Just _) = LT
505 | compareStarts (Just _) Nothing = GT
506 | compareStarts (Just left) (Just right) = compare left right
508 | compareEnds : Maybe Instant -> Maybe Instant -> Ordering
509 | compareEnds Nothing Nothing = EQ
510 | compareEnds Nothing (Just _) = GT
511 | compareEnds (Just _) Nothing = LT
512 | compareEnds (Just left) (Just right) = compare left right
515 | Ord UnboundedIntervalRep where
516 | compare left right = case compareStarts
517 | (unboundedStart left) (unboundedStart right) of
518 | EQ => compareEnds (unboundedEnd left) (unboundedEnd right)
522 | Show UnboundedIntervalRep where
523 | show value = "unboundedInterval " ++ show (unboundedStart value) ++ " " ++
524 | show (unboundedEnd value)