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}