recurrence.bend

bend-kit-scheduler@0.4.0.0 · 61 lines · 0 definitions

1# Pure recurrence on arbitrary-precision Nat ticks: no wrapping or U32 conversion.
2import Base
3
4type Missed is Data:
5  Skip{}
6  CatchUp{bound: Nat}
7
8type Cadence is Data:
9  FixedDelay{}
10  FixedRate{policy: Missed}
11
12type Plan is Data:
13  Planned{replay: List<&2, Nat>, next: Nat}
14
15def replay(p: Plan) -> List<&2, Nat>:
16  match p:
17    case Planned{ticks, next}:
18      ticks
19
20def next(p: Plan) -> Nat:
21  match p:
22    case Planned{ticks, next}:
23      next
24
25# Generate only the bounded replay prefix, never the skipped remainder.
26def ticks(count: Nat, +previous: Nat, +period: Nat) -> List<&2, Nat>:
27  match count:
28    case 0n:
29      Nil{}
30    case 1n+n:
31      +deadline = Nat.add(previous, period)
32      Con{deadline, ticks(n, deadline, period)}
33
34def limit(policy: Missed, overdue: Nat) -> Nat:
35  match policy:
36    case Skip{}:
37      0n
38    case CatchUp{bound}:
39      Nat.min(bound, overdue)
40
41# A tick equal to completion is overdue. The already-completed previous tick
42# is excluded. A positive period keeps all arithmetic exact, without overflow.
43def rate(+previous: Nat, +completion: Nat, +period: Nat, policy: Missed) -> Plan:
44  +overdue = Nat.div(Nat.sub(completion, previous), period)
45  Planned{ticks(limit(policy, overdue), previous, period),
46    Nat.add(previous, Nat.mul(1n+overdue, period))}
47
48def positive(cadence: Cadence, +previous: Nat, +completion: Nat, +period: Nat) -> Plan:
49  match cadence:
50    case FixedDelay{}:
51      Planned{Nil{}, Nat.add(completion, period)}
52    case FixedRate{policy}:
53      rate(previous, completion, period, policy)
54
55# None is an explicit invalid-zero-period result, not a zero cadence.
56def plan(previous: Nat, completion: Nat, period: Nat, cadence: Cadence) -> Maybe<&2, Plan>:
57  match period:
58    case 0n:
59      None{}
60    case 1n+p:
61      Some{positive(cadence, previous, completion, 1n+p)}