compiler/tests/fixtures/runtime/random-sequence/main.ldn
1-- core/random's generator reproduces the sequences of xoshiro256**'s
2-- reference implementation, seeded through splitmix64 as its authors
3-- suggest, on every target: 64-bit shifts, rotations and additions on a
4-- 32-bit Cortex-M0 give what they give on a 64-bit host. The expected
5-- values were printed by the authors' unmodified xoshiro256starstar.c and
6-- splitmix64.c, and those of `below`, `shuffle` and `from_entropy` by
7-- the same routines ported to C over them. Each check that fails
8-- returns its own code.
9import core/io
10import core/random
11
12-- The first eight outputs for seeds 0, 42 and 0x0123456789abcdef.
13seed_zero: [8]u64 = [11091344671253066420, 13793997310169335082,
14 1900383378846508768, 7684712102626143532,
15 13521403990117723737, 18442103541295991498,
16 7788427924976520344, 9881088229871127103]
17seed_answer: [8]u64 = [1546998764402558742, 6990951692964543102,
18 12544586762248559009, 17057574109182124193,
19 18295552978065317476, 14199186830065750584,
20 13267978908934200754, 15679888225317814407]
21seed_pattern: [8]u64 = [11728116837925579837, 431261241542867727,
22 7088239201150201886, 1991960781942118182,
23 16071698363884655823, 4123588241367215080,
24 13086776817198750337, 9243408305086729258]
25
26-- Whether the generator seeded from SEED draws EXPECTED first.
27draws: (seed: u64, expected: []u64) -> (yes: bool) =
28 yes = false
29 mut source := random.new(seed)
30 for value in expected do
31 return when random.next(source) <> value
32 end for
33 yes = true
34end draws
35
36-- Four draws below each of five bounds, one generator after the other,
37-- from seed 0x0123456789abcdef: a bound of one, small ones, one just past
38-- 2^63, which rejects nearly half its draws, and the largest.
39bounds: [5]u64 = [1, 6, 1000, 9223372036854775809, 18446744073709551615]
40below_bounds: [20]u64 = [0, 0, 0, 0,
41 0, 1, 2, 4,
42 46, 355, 359, 736,
43 7734768162860382805, 6756903082987012264,
44 1940808327524255886, 4194713531705948863,
45 8294363530523727346, 1449972046409666307,
46 16686400129591613081, 17908642295857891376]
47
48below_matches: () -> (yes: bool) =
49 yes = false
50 mut source := random.new(0x0123456789abcdef)
51 mut at: usize = 0
52 for bound in bounds do
53 for count in usize(0)..<4 do
54 _ = count
55 drawn: u64 = random.below(source, bound)
56 return when drawn <> below_bounds[at] or drawn >= bound
57 inc at
58 end for
59 end for
60 yes = true
61end below_matches
62
63-- The upper halves of seed 42's first three draws.
64upper_halves: [3]u32 = [360188718, 1627707782, 2920764210]
65
66halves_match: () -> (yes: bool) =
67 yes = false
68 mut source := random.new(42)
69 for value in upper_halves do
70 return when random.next_u32(source) <> value
71 end for
72 yes = true
73end halves_match
74
75-- Ten items shuffled from seed 7.
76shuffled: [10]u8 = [9, 5, 3, 8, 4, 1, 7, 0, 6, 2]
77
78shuffle_matches: () -> (yes: bool) =
79 yes = false
80 mut source := random.new(7)
81 mut values: [10]u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
82 random.shuffle(source, values[0..<10])
83 for at in usize(0)..<10 do
84 return when values[at] <> shuffled[at]
85 end for
86 yes = true
87end shuffle_matches
88
89-- Entropy of the bytes 1 to 32 is the state, read little-endian, and
90-- draws these first.
91counted_bytes: [32]u8 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
92 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
93 29, 30, 31, 32]
94counted_draws: [3]u64 = [5961681302714436584, 13557399450712486528,
95 3242939241321077778]
96
97-- Entropy that is all zero, which xoshiro256** cannot leave, seeds as
98-- seed 0 does; entropy that has run dry fails as the provider fails.
99zero_bytes: [32]u8 = zeroed
100
101entropy_seeds: () -> (yes: bool) ! ... =
102 yes = false
103 mut state: io.entropy_space = zeroed
104 counted := io.entropy_over(state, counted_bytes[0..<32])
105 mut source := try random.from_entropy(counted)
106 for value in counted_draws do
107 return when random.next(source) <> value
108 end for
109 return when state.used <> 32 or state.requests <> 1
110 mut empty_state: io.entropy_space = zeroed
111 zero := io.entropy_over(empty_state, zero_bytes[0..<32])
112 mut zeroed_source := try random.from_entropy(zero)
113 return when random.next(zeroed_source) <> seed_zero[0]
114 dry: random.generator = random.from_entropy(zero) else (problem)
115 yes = problem == io.io_failed and empty_state.used == 32
116 return
117 end
118 _ = dry
119end entropy_seeds
120
121public main: () -> (code: i32) =
122 code = 1
123 return when not draws(0, seed_zero[0..<8])
124 code = 2
125 return when not draws(42, seed_answer[0..<8])
126 code = 3
127 return when not draws(0x0123456789abcdef, seed_pattern[0..<8])
128 code = 4
129 return when not below_matches()
130 code = 5
131 return when not halves_match()
132 code = 6
133 return when not shuffle_matches()
134 code = 7
135 seeded: bool = entropy_seeds() else (problem)
136 _ = problem
137 return
138 end
139 return when not seeded
140 code = 42
141end main