src/math/random/rand.bend
bend-collections-laws-crypto@1.0.0.0 · 324 lines · 52 definitions
1import Base
2import bend-collections-laws-crypto@1.0.0.0/src/math/u64.bend as W
3import bend-collections-laws-crypto@1.0.0.0/src/math/w64.bend as X
4import bend-collections-laws-crypto@1.0.0.0/src/math/f64.bend as F
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58def fst(-A: Data, -S: Data, p: A & S) -> A:
59 (a, s) = p
60 a
61
62def snd(-A: Data, -S: Data, p: A & S) -> S:
63 (a, s) = p
64 s
65
66def uint64(~S: Data, ~next: S -> W.U64 & S, s: S) -> W.U64 & S:
67 next(s)
68
69def top32(-S: Data, p: W.U64 & S) -> U32 & S:
70 (x, s) = p
71 (X.hi(x), s)
72
73def uint32(~S: Data, ~next: S -> W.U64 & S, s: S) -> U32 & S:
74 top32(S, next(s))
75
76def low63(-S: Data, p: W.U64 & S) -> W.U64 & S:
77 (+x, s) = p
78 (W.U64{X.lo(x), U32.and(X.hi(x), 2147483647)}, s)
79
80def int64(~S: Data, ~next: S -> W.U64 & S, s: S) -> W.U64 & S:
81 low63(S, next(s))
82
83def top31(-S: Data, p: W.U64 & S) -> U32 & S:
84 (x, s) = p
85 (U32.shr(X.hi(x)), s)
86
87def int32(~S: Data, ~next: S -> W.U64 & S, s: S) -> U32 & S:
88 top31(S, next(s))
89
90
91
92def and64(+a: W.U64, +b: W.U64) -> W.U64:
93 W.U64{U32.and(X.lo(a), X.lo(b)), U32.and(X.hi(a), X.hi(b))}
94
95
96def is_pow2(+n: W.U64) -> Bool:
97 X.is_zero(and64(n, X.sub(n, W.U64{1, 0})))
98
99def mask(-S: Data, +n: W.U64, p: W.U64 & S) -> W.U64 & S:
100 (+x, s) = p
101 (and64(x, X.sub(n, W.U64{1, 0})), s)
102
103
104def thresh(+n: W.U64) -> W.U64:
105 X.rem(X.sub(W.U64{0, 0}, n), n)
106
107
108type Draw<-S: Data> is Data:
109 D{hi: W.U64, lo: W.U64, s: S}
110
111def draw_fin(-S: Data, s: S, p: W.U64 & W.U64) -> Draw<S>:
112 (lo, hi) = p
113 D{hi, lo, s}
114
115def draw(-S: Data, +n: W.U64, p: W.U64 & S) -> Draw<S>:
116 (+x, s) = p
117 draw_fin(S, s, X.mul128(x, n))
118
119
120def again(~S: Data, ~next: S -> W.U64 & S, +n: W.U64, +hi: W.U64, +lo: W.U64, s: S, reject: Bool) -> Draw<S>:
121 match reject:
122 case False{}:
123 D{hi, lo, s}
124 case True{}:
125 draw(S, n, next(s))
126
127def step(~S: Data, ~next: S -> W.U64 & S, +n: W.U64, +t: W.U64, d: Draw<S>) -> Draw<S>:
128 match d:
129 case D{+hi, +lo, s}:
130 again(~S, ~next, n, hi, lo, s, X.lt(lo, t))
131
132
133def retry(~S: Data, ~next: S -> W.U64 & S, fuel: Nat, +n: W.U64, +t: W.U64, d: Draw<S>) -> Draw<S>:
134 match fuel:
135 case 0n:
136 d
137 case 1n+f:
138 retry(~S, ~next, f, n, t, step(~S, ~next, n, t, d))
139
140def result(-S: Data, d: Draw<S>) -> W.U64 & S:
141 match d:
142 case D{hi, lo, s}:
143 (hi, s)
144
145
146
147def lemire_small(~S: Data, ~next: S -> W.U64 & S, +n: W.U64, +hi: W.U64, +lo: W.U64, s: S, small: Bool) -> W.U64 & S:
148 match small:
149 case False{}:
150 (hi, s)
151 case True{}:
152 result(S, retry(~S, ~next, 127n, n, thresh(n), D{hi, lo, s}))
153
154def lemire_first(~S: Data, ~next: S -> W.U64 & S, +n: W.U64, d: Draw<S>) -> W.U64 & S:
155 match d:
156 case D{+hi, +lo, s}:
157 lemire_small(~S, ~next, n, hi, lo, s, X.lt(lo, n))
158
159def uint64n_pick(~S: Data, ~next: S -> W.U64 & S, s: S, +n: W.U64, pow2: Bool) -> W.U64 & S:
160 match pow2:
161 case True{}:
162 mask(S, n, next(s))
163 case False{}:
164 lemire_first(~S, ~next, n, draw(S, n, next(s)))
165
166
167def uint64n(~S: Data, ~next: S -> W.U64 & S, s: S, +n: W.U64) -> W.U64 & S:
168 uint64n_pick(~S, ~next, s, n, is_pow2(n))
169
170def uint_below(~S: Data, ~next: S -> W.U64 & S, s: S, +n: W.U64) -> W.U64 & S:
171 uint64n(~S, ~next, s, n)
172
173def lo32(-S: Data, p: W.U64 & S) -> U32 & S:
174 (x, s) = p
175 (X.lo(x), s)
176
177
178def uint32n(~S: Data, ~next: S -> W.U64 & S, s: S, +n: U32) -> U32 & S:
179 lo32(S, uint64n(~S, ~next, s, W.U64{n, 0}))
180
181
182def w_go(k: Nat, +n: Nat) -> U32:
183 match k:
184 case 0n:
185 0
186 case 1n+j:
187 U32.add(U32.from_nat(Nat.mod(n, 256n)), U32.mul(w_go(j, Nat.div(n, 256n)), X.pow2(8n)))
188
189
190def skip(k: Nat, +n: Nat) -> Nat:
191 match k:
192 case 0n:
193 n
194 case 1n+j:
195 skip(j, Nat.div(n, 256n))
196
197
198def nat64(+n: Nat) -> W.U64:
199 W.U64{w_go(4n, n), w_go(4n, skip(4n, n))}
200
201def to_nat(-S: Data, p: W.U64 & S) -> Nat & S:
202 (x, s) = p
203 (X.n48(x), s)
204
205def intn_z(~S: Data, ~next: S -> W.U64 & S, s: S, +n: Nat, z: Bool) -> Nat & S:
206 match z:
207 case True{}:
208 (0n, s)
209 case False{}:
210 to_nat(S, uint64n(~S, ~next, s, nat64(n)))
211
212
213def intn(~S: Data, ~next: S -> W.U64 & S, s: S, +n: Nat) -> Nat & S:
214 intn_z(~S, ~next, s, n, Nat.is_eq(n, 0n))
215
216def plus(-S: Data, +lo: Nat, p: Nat & S) -> Nat & S:
217 (k, s) = p
218 (Nat.add(lo, k), s)
219
220
221def int_range(~S: Data, ~next: S -> W.U64 & S, s: S, +lo: Nat, +hi: Nat) -> Nat & S:
222 plus(S, lo, intn(~S, ~next, s, Nat.sub(hi, lo)))
223
224
225
226
227
228
229def f53(+m: W.U64, +c: Nat) -> F.F64:
230 F.Bits{X.lo(X.shl(m, Nat.sub(c, 11n))), U32.add(U32.mul(U32.from_nat(Nat.sub(1033n, c)), 1048576), U32.and(X.hi(X.shl(m, Nat.sub(c, 11n))), 1048575))}
231
232def f53_z(+m: W.U64, z: Bool) -> F.F64:
233 match z:
234 case True{}:
235 F.zero(False{})
236 case False{}:
237 f53(m, X.clz(m))
238
239
240def to_float(+x: W.U64) -> F.F64:
241 f53_z(W.U64{X.lo(x), U32.and(X.hi(x), 2097151)}, X.is_zero(W.U64{X.lo(x), U32.and(X.hi(x), 2097151)}))
242
243def float_of(-S: Data, p: W.U64 & S) -> F.F64 & S:
244 (+x, s) = p
245 (to_float(x), s)
246
247def float64(~S: Data, ~next: S -> W.U64 & S, s: S) -> F.F64 & S:
248 float_of(S, next(s))
249
250
251
252def nth(-A: Data, xs: List<&2, A>, i: Nat) -> Maybe<&2, A>:
253 match xs i:
254 case Nil{} _:
255 None{}
256 case Con{h, t} 0n:
257 Some{h}
258 case Con{h, t} 1n+p:
259 nth(A, t, p)
260
261def put(-A: Data, xs: List<&2, A>, i: Nat, x: A) -> List<&2, A>:
262 match xs i:
263 case Nil{} _:
264 Nil{}
265 case Con{h, t} 0n:
266 Con{x, t}
267 case Con{h, t} 1n+p:
268 Con{h, put(A, t, p, x)}
269
270def swap_m(-A: Data, xs: List<&2, A>, +i: Nat, +j: Nat, a: Maybe<&2, A>, b: Maybe<&2, A>) -> List<&2, A>:
271 match a b:
272 case Some{x} Some{y}:
273 put(A, put(A, xs, i, y), j, x)
274 case _ _:
275 xs
276
277
278def swap(-A: Data, +xs: List<&2, A>, +i: Nat, +j: Nat) -> List<&2, A>:
279 swap_m(A, xs, i, j, nth(A, xs, i), nth(A, xs, j))
280
281def swap_at(-A: Data, -S: Data, xs: List<&2, A>, +i: Nat, p: W.U64 & S) -> List<&2, A> & S:
282 (+j, s) = p
283 (swap(A, xs, i, X.n48(j)), s)
284
285
286def len64(-A: Data, xs: List<&2, A>) -> W.U64:
287 match xs:
288 case Nil{}:
289 W.U64{0, 0}
290 case Con{x, rest}:
291 X.add(len64(A, rest), W.U64{1, 0})
292
293
294def shuffle_step(~A: Data, ~S: Data, ~next: S -> W.U64 & S, +i: Nat, +n: W.U64, st: List<&2, A> & S) -> List<&2, A> & S:
295 (xs, s) = st
296 swap_at(A, S, xs, i, uint64n(~S, ~next, s, n))
297
298
299def shuffle_go(~A: Data, ~S: Data, ~next: S -> W.U64 & S, k: Nat, +n: W.U64, st: List<&2, A> & S) -> List<&2, A> & S:
300 match k:
301 case 0n:
302 st
303 case 1n+ +p:
304 shuffle_go(~A, ~S, ~next, p, X.sub(n, W.U64{1, 0}), shuffle_step(~A, ~S, ~next, 1n+p, n, st))
305
306
307
308def shuffle(~A: Data, ~S: Data, ~next: S -> W.U64 & S, s: S, +xs: List<&2, A>) -> List<&2, A> & S:
309 shuffle_go(~A, ~S, ~next, Nat.sub(List.length(&2, A, xs), 1n), len64(A, xs), (xs, s))
310
311def range_go(n: Nat, acc: List<&2, Nat>) -> List<&2, Nat>:
312 match n:
313 case 0n:
314 acc
315 case 1n+ +p:
316 range_go(p, Con{p, acc})
317
318
319def range(n: Nat) -> List<&2, Nat>:
320 range_go(n, [])
321
322
323def perm(~S: Data, ~next: S -> W.U64 & S, s: S, +n: Nat) -> List<&2, Nat> & S:
324 shuffle(~Nat, ~S, ~next, s, range(n))