~/bend-docscommunity

proofs/math/random/lemire.bend checks

raw source on the hub · import bend-collections-laws-crypto@1.0.0.0/proofs/math/random/lemire.bend as Lemire

10 imports
import Base
import ../../../spec/lib/common.bend as C
import ../../../spec/math/random/rand.bend as SR
import ../../lib/logic.bend as L
import ../../lib/nat.bend as N
import ../typed/width.bend as WI
import ../natural/arith.bend as AR
import ../../lib/arith.bend as LA
import ../../lib/u32alg.bend as UA
import ../../lib/lemmas/proofs/nat_algebra.bend as NA

Definitions

def b2n source · line 25 · raw

@b:Bool -> Nat

def and_false source · line 30 · raw

@+b:Bool -> {Bool.and(b, False{}) == False{} : Bool}

def and_lt source · line 38 · raw

@+lo:Nat -> @+n:Nat -> @+t:Nat -> @+ht:{Nat.is_lt(t, n) == True{} : Bool} -> @+c:Bool -> @+hc:{Nat.is_lt(lo, t) == c : Bool} -> {Bool.and(Nat.is_lt(lo, n), Nat.is_lt(lo, t)) == c : Bool}

lo < n and lo < t is lo < t, when t < n

def hit_acc source · line 48 · raw

@+hi:Nat -> @+k:Nat -> @+ok:Bool -> Nat

the hit of an accepted or rejected hi

def le_plus source · line 52 · raw

@+t:Nat -> @+s:Nat -> {Nat.is_le(s, Nat.add(t, s)) == True{} : Bool}

s <= t + s

def fin_lt source · line 58 · raw

@+hi:Nat -> @+k:Nat -> @+ok:Bool -> @+h:{Nat.is_lt(hi, k) == True{} : Bool} -> {Nat.add(hit_acc(hi, k, ok), 1n) == 1n : Nat}

def term_lt source · line 67 · raw

@+w:Nat -> @+k:Nat -> @+t:Nat -> @+lo:Nat -> @+hi:Nat -> @+ok:Bool -> @+hlo:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, lo) == True{} : Bool} -> @+hab:{Nat.is_le(Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool} -> @+h:{Nat.is_lt(hi, k) == True{} : Bool} -> {Nat.add(hit_acc(hi, k, ok), b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k))))) == b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k))) : Nat}

hi < k: rejected or not, x n lies below both thresholds

def fin_eq source · line 76 · raw

@+k:Nat -> @+c:Bool -> {Nat.add(hit_acc(k, k, Bool.not(c)), b2n(c)) == 1n : Nat}

def term_eq source · line 86 · raw

@+w:Nat -> @+k:Nat -> @+t:Nat -> @+lo:Nat -> @+hlo:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, lo) == True{} : Bool} -> {Nat.add(hit_acc(k, k, Bool.not(Nat.is_lt(lo, t))), b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k))))) == b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k))) : Nat}

hi == k: x n is below the upper threshold, and below the lower one exactly when it is rejected

def fin_gt source · line 91 · raw

@+hi:Nat -> @+k:Nat -> @+ok:Bool -> @+h:{Nat.is_lt(k, hi) == True{} : Bool} -> {Nat.add(hit_acc(hi, k, ok), 0n) == 0n : Nat}

def term_gt source · line 100 · raw

@+w:Nat -> @+k:Nat -> @+t:Nat -> @+lo:Nat -> @+hi:Nat -> @+ok:Bool -> @+hab:{Nat.is_le(Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool} -> @+h:{Nat.is_lt(k, hi) == True{} : Bool} -> {Nat.add(hit_acc(hi, k, ok), b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k))))) == b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k))) : Nat}

hi > k: x n is at or above both thresholds

def term_cases source · line 108 · raw

@+w:Nat -> @+n:Nat -> @+k:Nat -> @+t:Nat -> @+lo:Nat -> @+hi:Nat -> @+hlo:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, lo) == True{} : Bool} -> @+ht:{Nat.is_lt(t, n) == True{} : Bool} -> @+hab:{Nat.is_le(Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool} -> @+lt:Bool -> @+eq:Bool -> @+hlt:{Nat.is_lt(hi, k) == lt : Bool} -> @+heq:{Nat.is_eq(hi, k) == eq : Bool} -> {Nat.add(hit_acc(hi, k, Bool.not(Bool.and(Nat.is_lt(lo, n), Nat.is_lt(lo, t)))), b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), Nat.add(t, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k))))) == b2n(Nat.is_lt(Nat.add(lo, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, hi)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k))) : Nat}

def term source · line 122 · raw

@+w:Nat -> @+n:Nat -> @+k:Nat -> @+y:Nat -> @+ht:{Nat.is_lt(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, n), n) == True{} : Bool} -> @+hab:{Nat.is_le(Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, n), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool} -> {Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.hit(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.lemire(w, n, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.high(w, y), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.low(w, y)), k), b2n(Nat.is_lt(y, Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, n), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k))))) == b2n(Nat.is_lt(y, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k))) : Nat}

THEOREM (one output, Lemire's branch): with t = 2^w mod n < n and k 2^w + t <= (k + 1) 2^w, y = x n draws k exactly when k 2^w + t <= y < (k + 1) 2^w

def cnt_lt source · line 132 · raw

@+n:Nat -> @+c:Nat -> @N:Nat -> Nat

#{x < N : x n < c}

def draw_lemire source · line 140 · raw

@+w:Nat -> @+m:Nat -> @+x:Nat -> @+hp:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == False{} : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.draw(w, x, 1n+m) == 0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.lemire(w, 1n+m, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.high(w, Nat.mul(x, 1n+m)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.low(w, Nat.mul(x, 1n+m))) : Maybe<&2, Nat>}

the draw is Lemire's when n is not a power of two

def add4 source · line 145 · raw

@+a:Nat -> @+h:Nat -> @+b:Nat -> @+e:Nat -> {Nat.add(Nat.add(a, h), Nat.add(b, e)) == Nat.add(Nat.add(a, b), Nat.add(h, e)) : Nat}

(a + h) + (b + e) == (a + b) + (h + e)

def sum_lemire source · line 159 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+N:Nat -> @+hp:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == False{} : Bool} -> @+ht:{Nat.is_lt(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m), 1n+m) == True{} : Bool} -> @+hab:{Nat.is_le(Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool} -> {Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.count(w, 1n+m, k, N), cnt_lt(1n+m, Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), N)) == cnt_lt(1n+m, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k), N) : Nat}

the Lemire outputs below N plus those with x n below the lower threshold are those with x n below the upper one

def le_succ_lt source · line 179 · raw

@+a:Nat -> @+b:Nat -> {Nat.is_le(1n+a, b) == Nat.is_lt(a, b) : Bool}

a + 1 <= b is a < b

def lt_ceil source · line 191 · raw

@+bp:Nat -> @+x:Nat -> @+c:Nat -> {Nat.is_lt(Nat.mul(x, 1n+bp), c) == Nat.is_lt(x, Nat.div(Nat.add(c, bp), 1n+bp)) : Bool}

x n < c is x < ceil(c / n) = (c + n - 1) / n, n = 1 + bp

def min_zero source · line 206 · raw

@+n:Nat -> {Nat.min(n, 0n) == 0n : Nat}

def min_succ source · line 214 · raw

@+n:Nat -> @+c:Nat -> {Nat.min(1n+n, c) == Nat.add(Nat.min(n, c), b2n(Nat.is_lt(n, c))) : Nat}

min(N + 1, C) = min(N, C) + [N < C]

def cnt_min source · line 226 · raw

@+bp:Nat -> @+c:Nat -> @+N:Nat -> {cnt_lt(1n+bp, c, N) == Nat.min(N, Nat.div(Nat.add(c, bp), 1n+bp)) : Nat}

THEOREM: #{x < N : x n < c} = min(N, ceil(c / n))

def dbl_add source · line 240 · raw

@+z:Nat -> {Nat.double(z) == Nat.add(z, z) : Nat}

def pm source · line 249 · raw

@+w:Nat -> @+m:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m) == Nat.mod(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n), 1n+m) : Nat}

Go's -n % n: 2^w mod n

def div_add_mul source · line 263 · raw

@+q:Nat -> @+m:Nat -> @+r:Nat -> {Nat.div(Nat.add(Nat.mul(q, 1n+m), r), 1n+m) == Nat.add(q, Nat.div(r, 1n+m)) : Nat}

(q n + r) / n = q + r / n

def min_le source · line 271 · raw

@+n:Nat -> @+c:Nat -> @+h:{Nat.is_le(c, n) == True{} : Bool} -> {Nat.min(n, c) == c : Nat}

def lt_add2 source · line 281 · raw

@+a:Nat -> @+b:Nat -> @+c:Nat -> @+d:Nat -> @+hab:{Nat.is_le(a, b) == True{} : Bool} -> @+hcd:{Nat.is_lt(c, d) == True{} : Bool} -> {Nat.is_lt(Nat.add(a, c), Nat.add(b, d)) == True{} : Bool}

a <= b and c < d give a + c < b + d

def cb_le source · line 285 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+hk:{Nat.is_lt(k, 1n+m) == True{} : Bool} -> {Nat.is_le(Nat.div(Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k), m), 1n+m), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n)) == True{} : Bool}

ceil((k + 1) 2^w / n) <= 2^w for k < n

def hab_of source · line 304 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+hw:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, 1n+m) == True{} : Bool} -> {Nat.is_le(Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k)) == True{} : Bool}

t <= 2^w for t < n <= 2^w

def bm_eq source · line 315 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> {Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n+k), m) == Nat.add(Nat.mul(Nat.div(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n), 1n+m), 1n+m), Nat.add(Nat.add(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.pow2mod(w, 1n+m), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, k)), m)) : Nat}

(k + 1) 2^w + m = q n + (t + k 2^w + m), q n + t = 2^w

def lemire_count source · line 327 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+hp:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == False{} : Bool} -> @+hw:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, 1n+m) == True{} : Bool} -> @+hk:{Nat.is_lt(k, 1n+m) == True{} : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.count(w, 1n+m, k, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n)) == Nat.div(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n), 1n+m) : Nat}

THEOREM (Lemire's branch): floor(2^w / n) outputs draw k

def cm source · line 358 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @N:Nat -> Nat

#{x < N : and_bits(w, x, m) == k}

def cm2 source · line 366 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @N:Nat -> Nat

the same count taken over the pairs 2y, 2y + 1 with y < N

def split source · line 373 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+N:Nat -> {cm(w, m, k, Nat.double(N)) == cm2(w, m, k, N) : Nat}

def pair_eq source · line 385 · raw

@+a:Nat -> @+k:Nat -> {Nat.add(b2n(Nat.is_eq(Nat.double(a), k)), b2n(Nat.is_eq(1n+Nat.double(a), k))) == b2n(Nat.is_eq(a, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(k))) : Nat}

[2a == k] + [2a + 1 == k] == [a == k / 2]

def bit_dbl0 source · line 400 · raw

@+y:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.bit(Nat.double(y)) == 0n : Nat}

def half_dbl0 source · line 403 · raw

@+y:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(Nat.double(y)) == y : Nat}

def bit_dbl1 source · line 406 · raw

@+y:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.bit(1n+Nat.double(y)) == 1n : Nat}

def half_dbl1 source · line 409 · raw

@+y:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(1n+Nat.double(y)) == y : Nat}

def pair_and source · line 414 · raw

@+v:Nat -> @+h:Nat -> @+k:Nat -> @+y:Nat -> {Nat.add(b2n(Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(1n+v, Nat.double(y), 1n+Nat.double(h)), k)), b2n(Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(1n+v, 1n+Nat.double(y), 1n+Nat.double(h)), k))) == b2n(Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(v, y, h), 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(k))) : Nat}

with m = 2h + 1: the pair 2y, 2y + 1 draws k once exactly when y draws k / 2 one width lower with h

def pairs source · line 425 · raw

@+v:Nat -> @+h:Nat -> @+k:Nat -> @+N:Nat -> {cm2(1n+v, 1n+Nat.double(h), k, N) == cm(v, h, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(k), N) : Nat}

the pairs one width up count what the lower width counts

def and0 source · line 435 · raw

@+w:Nat -> @+x:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, x, 0n) == 0n : Nat}

x & 0 == 0

def cm0 source · line 445 · raw

@+w:Nat -> @+N:Nat -> {cm(w, 0n, 0n, N) == N : Nat}

n = 1: every output draws 0

def cm0k source · line 454 · raw

@+w:Nat -> @+k:Nat -> @+N:Nat -> @+hk:{Nat.is_lt(k, 1n) == True{} : Bool} -> {cm(w, 0n, k, N) == N : Nat}

def div_one source · line 461 · raw

@+N:Nat -> {Nat.div(N, 1n) == N : Nat}

def div_double source · line 466 · raw

@+a:Nat -> @+h:Nat -> {Nat.div(Nat.double(a), 2n+Nat.double(h)) == Nat.div(a, 1n+h) : Nat}

2a / 2n = a / n

def bitsq_b source · line 475 · raw

@+b:Nat -> @+h:{Nat.is_le(b, 1n) == True{} : Bool} -> {Nat.mul(b, b) == b : Nat}

bit * bit == bit

def and_self source · line 485 · raw

@+w:Nat -> @+x:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, x, x) == 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.low(w, x) : Nat}

x & x is the low w bits of x

def dbl0 source · line 493 · raw

@+z:Nat -> @+h:{Nat.double(z) == 0n : Nat} -> {z == 0n : Nat}

def decomp source · line 501 · raw

@+m:Nat -> @+b:Nat -> @+h:Nat -> @+hb:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.bit(m) == b : Nat} -> @+hh:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(m) == h : Nat} -> {m == Nat.add(b, Nat.double(h)) : Nat}

m = bit(m) + 2 half(m)

def and_even source · line 508 · raw

@+w:Nat -> @+h:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(1n+w, 2n+Nat.double(h), 1n+Nat.double(h)) == Nat.double(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+h, h)) : Nat}

n = 2h + 2, m = 2h + 1: n & m is twice (h + 1) & h one width lower

def and_odd source · line 515 · raw

@+w:Nat -> @+g:Nat -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(1n+w, 3n+Nat.double(g), 2n+Nat.double(g)) == Nat.double(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.low(w, 1n+g)) : Nat}

n = 2g + 3, m = 2g + 2: n & m is twice the low bits of g + 1

def mask source · line 524 · raw

@+v:Nat -> @+m:Nat -> @+k:Nat -> @+b:Nat -> @+h:Nat -> @+hb:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.bit(m) == b : Nat} -> @+hh:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.half(m) == h : Nat} -> @+hp:{0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(v, 1n+m, m) == 0n : Nat} -> @+hw:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(v, 1n+m) == True{} : Bool} -> @+hk:{Nat.is_lt(k, 1n+m) == True{} : Bool} -> {cm(v, m, k, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(v, 1n)) == Nat.div(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(v, 1n), 1n+m) : Nat}

THEOREM (the mask branch): with n = 1 + m, n & m == 0 and n < 2^v, each k < n is the low bits of floor(2^v / n) outputs

def draw_mask source · line 576 · raw

@+w:Nat -> @+m:Nat -> @+x:Nat -> @+hp:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == True{} : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.draw(w, x, 1n+m) == Some{0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, x, m)} : Maybe<&2, Nat>}

the draw is the mask when n is a power of two

def count_mask source · line 580 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+N:Nat -> @+hp:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == True{} : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.count(w, 1n+m, k, N) == cm(w, m, k, N) : Nat}

def unbiased_c source · line 589 · raw

@+w:Nat -> @+m:Nat -> @+k:Nat -> @+hw:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, 1n+m) == True{} : Bool} -> @+hk:{Nat.is_lt(k, 1n+m) == True{} : Bool} -> @+c:Bool -> @+hc:{Nat.is_eq(0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.and_bits(w, 1n+m, m), 0n) == c : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.count(w, 1n+m, k, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n)) == Nat.div(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n), 1n+m) : Nat}

def unbiased source · line 600 · raw

@+w:Nat -> @+n:Nat -> @+k:Nat -> @+hn:{Nat.is_lt(0n, n) == True{} : Bool} -> @+hw:{0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.fits(w, n) == True{} : Bool} -> @+hk:{Nat.is_lt(k, n) == True{} : Bool} -> {0xa7e654f9780078ca65bf9e187da99d3e/spec/math/random/rand.count(w, n, k, 0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n)) == Nat.div(0xa7e654f9780078ca65bf9e187da99d3e/spec/lib/common.shift(w, 1n), n) : Nat}

THEOREM (Lemire.unbiased): for every width w, bound 0 < n < 2^w and k < n, exactly floor(2^w / n) of the 2^w outputs draw k