gba/random.rs
1// Note(Lokathor): We have a generic LCG type below, but for now we can hide the
2// process of having to pick what multiplier and increment to use behind a
3// newtype that selects some default constants.
4
5/// A [Linear Congruential Generator][wp-lcg] with 32-bits of output.
6///
7/// [wp-lcg]: https://en.wikipedia.org/wiki/Linear_congruential_generator
8///
9/// This holds a single `u32` as the state. Advancing the generator requires
10/// only a multiply and an add, so it's fairly fast as a PRNG. The output
11/// quality isn't particularly great as a result, and if you need less than 32
12/// bits of randomness at a time you're advised to use the *upper* bits from
13/// each output of this generator, which will be the better bits. The [Gen32]
14/// impl of this type will handle that for you, if you use that trait.
15#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[repr(transparent)]
17// This particular `MUL` value comes from "Tables of linear congruential
18// generators of different sizes and good lattice structure" by Pierre L'Ecuyer.
19// We select 1 as the `ADD` value just to have an odd increment, and because
20// `ADD` values of 3 bits or less can be encoded with an immediate
21// instruction in both a32 and t32 code.
22pub struct Lcg32(GenericLcg32<32310901, 1>);
23impl Lcg32 {
24 /// Wraps the `u32` as an `Lcg32`.
25 ///
26 /// This doesn't do any manipulation of the input state to try and help seed
27 /// the value, it just uses the value directly.
28 #[inline]
29 pub const fn new(state: u32) -> Self {
30 Self(GenericLcg32::new(state))
31 }
32
33 /// Advances the generator one step, producing a `u32` of output.
34 #[inline]
35 pub fn next_u32(&mut self) -> u32 {
36 self.0.next_u32()
37 }
38
39 /// Advances the generator by `delta` steps all at once.
40 ///
41 /// Because the generator output sequence loops, large `delta` values allow
42 /// you to "reverse" the generator.
43 #[inline]
44 pub fn jump_state(&mut self, delta: u32) {
45 self.0.jump_state(delta)
46 }
47}
48
49/// A [Linear Congruential Generator][wp-lcg] with a const generic multiplier
50/// and increment.
51///
52/// [wp-lcg]: https://en.wikipedia.org/wiki/Linear_congruential_generator
53///
54/// The `ADD` value can be any value at all. Different `ADD` values will reorder
55/// the sequence that you get from a particular `MUL` value. For best results
56/// `ADD` should be an odd value. An even `ADD` value gives a generator with a
57/// significantly shorter period than an odd `ADD` value.
58///
59/// The `MUL` value must be carefully selected, because it has an overwhelming
60/// impact on the generator's output quality. See the linked wikipedia article
61/// (and its references) if you want information on what `MUL` values you might
62/// want to use.
63#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[repr(transparent)]
65struct GenericLcg32<const MUL: u32, const ADD: u32>(u32);
66impl<const MUL: u32, const ADD: u32> GenericLcg32<MUL, ADD> {
67 /// Wraps the `u32` as a generic LCG.
68 ///
69 /// This doesn't do any manipulation of the input state to try and help seed
70 /// the value, it just uses the value directly.
71 #[inline]
72 pub const fn new(state: u32) -> Self {
73 Self(state)
74 }
75
76 /// Advances the generator one step, producing a `u32` of output.
77 #[inline]
78 pub fn next_u32(&mut self) -> u32 {
79 let next_state = self.0.wrapping_mul(MUL).wrapping_add(ADD);
80 self.0 = next_state;
81 next_state
82 }
83
84 /// Advances the generator by `delta` steps all at once.
85 ///
86 /// Because the generator output sequence loops, large `delta` values allow
87 /// you to "reverse" the generator.
88 #[inline]
89 pub fn jump_state(&mut self, mut delta: u32) {
90 let mut cur_mult: u32 = MUL;
91 let mut cur_plus: u32 = ADD;
92 let mut acc_mult: u32 = 1;
93 let mut acc_plus: u32 = 0;
94 while delta > 0 {
95 if (delta & 1) > 0 {
96 acc_mult = acc_mult.wrapping_mul(cur_mult);
97 acc_plus = acc_plus.wrapping_mul(cur_mult).wrapping_add(cur_plus);
98 }
99 cur_plus = cur_mult.wrapping_add(1).wrapping_mul(cur_plus);
100 cur_mult = cur_mult.wrapping_mul(cur_mult);
101 delta /= 2;
102 }
103 self.0 = acc_mult.wrapping_mul(self.0).wrapping_add(acc_plus);
104 }
105}
106
107/// A trait for pseudorandom number generators that have `u32`
108/// output from each step of the generator.
109pub trait Gen32 {
110 /// Advance the generator to produce the next `u32`.
111 fn next_u32(&mut self) -> u32;
112
113 /// Produce a `u16`.
114 #[inline]
115 fn next_u16(&mut self) -> u16 {
116 (self.next_u32() >> 16) as u16
117 }
118
119 /// Produce a `u8`.
120 #[inline]
121 fn next_u8(&mut self) -> u8 {
122 (self.next_u32() >> 24) as u8
123 }
124
125 /// Produce a `bool`.
126 #[inline]
127 fn next_bool(&mut self) -> bool {
128 (self.next_u16() as i32) < 0
129 }
130
131 /// Produce a value that's strictly less than `b`.
132 ///
133 /// ## Panics
134 /// * If `b` is zero.
135 #[inline]
136 #[track_caller]
137 fn next_bounded(&mut self, b: u16) -> u16 {
138 assert!(b != 0, "Gen32::next_bounded> Bound must be non-zero.");
139 let mut x: u32 = self.next_u16() as u32;
140 let mut mul: u32 = (b as u32).wrapping_mul(x);
141 let mut low: u16 = mul as u16;
142 if low < b {
143 let threshold = b.wrapping_neg() % b;
144 while low < threshold {
145 x = self.next_u32();
146 mul = (b as u32).wrapping_mul(x);
147 low = mul as u16;
148 }
149 }
150 let high = (mul >> 16) as u16;
151 high
152 }
153
154 /// Pick a random element of the slice, by value.
155 ///
156 /// ## Panics
157 /// * If the length is 0.
158 #[inline]
159 fn pick<T: Copy>(&mut self, buf: &[T]) -> T {
160 let len16: u16 = saturating_usize_as_u16(buf.len());
161 buf[self.next_bounded(len16) as usize]
162 }
163
164 /// Pick a random element of the slice, by shared reference.
165 ///
166 /// ## Panics
167 /// * If the length is 0.
168 #[inline]
169 fn pick_ref<'b, T>(&mut self, buf: &'b [T]) -> &'b T {
170 let len16: u16 = saturating_usize_as_u16(buf.len());
171 &buf[self.next_bounded(len16) as usize]
172 }
173
174 /// Pick a random element of the slice, by unique reference.
175 ///
176 /// ## Panics
177 /// * If the length is 0.
178 #[inline]
179 fn pick_mut<'b, T>(&mut self, buf: &'b mut [T]) -> &'b mut T {
180 let len16: u16 = saturating_usize_as_u16(buf.len());
181 &mut buf[self.next_bounded(len16) as usize]
182 }
183
184 /// Shuffles the elements of the slice.
185 ///
186 /// On an empty slice this is a no-op.
187 #[inline]
188 fn shuffle<T>(&mut self, buf: &mut [T]) {
189 if let Some(end_index) = buf.len().checked_sub(1) {
190 let mut possibility_count: u16 = saturating_usize_as_u16(buf.len());
191 let mut this_index: usize = 0;
192 while this_index < end_index {
193 let offset = self.next_bounded(possibility_count) as usize;
194 buf.swap(this_index, this_index + offset);
195 possibility_count -= 1;
196 this_index += 1;
197 }
198 }
199 }
200}
201
202impl Gen32 for Lcg32 {
203 #[inline]
204 fn next_u32(&mut self) -> u32 {
205 Lcg32::next_u32(self)
206 }
207}
208
209#[inline]
210const fn saturating_usize_as_u16(val: usize) -> u16 {
211 if val <= u16::MAX as usize {
212 val as u16
213 } else {
214 u16::MAX
215 }
216}