mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 10:51:30 +11:00
docs.
This commit is contained in:
parent
df65520930
commit
c2e2398f79
|
@ -2,39 +2,88 @@
|
||||||
// process of having to pick what multiplier and increment to use behind a
|
// process of having to pick what multiplier and increment to use behind a
|
||||||
// newtype that selects some default constants.
|
// newtype that selects some default constants.
|
||||||
|
|
||||||
|
/// A [Linear Congruential Generator][wp-lcg] with 32-bits of output.
|
||||||
|
///
|
||||||
|
/// [wp-lcg]: https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||||
|
///
|
||||||
|
/// This holds a single `u32` as the state. Advancing the generator requires
|
||||||
|
/// only a multiply and an add, so it's fairly fast as a PRNG. The output
|
||||||
|
/// quality isn't particularly great as a result, and if you need less than 32
|
||||||
|
/// bits of randomness at a time you're advised to use the *upper* bits from
|
||||||
|
/// each output of this generator, which will be the better bits. The [Gen32]
|
||||||
|
/// impl of this type will handle that for you, if you use that trait.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
// This particular `MUL` value comes from "Tables of linear congruential
|
||||||
|
// generators of different sizes and good lattice structure" by Pierre L'Ecuyer.
|
||||||
|
// We select 1 as the `ADD` value just to have an odd increment, and because
|
||||||
|
// `ADD` values of 3 bits or less can be encoded with an immediate
|
||||||
|
// instruction in both a32 and t32 code.
|
||||||
pub struct Lcg32(GenericLcg32<32310901, 1>);
|
pub struct Lcg32(GenericLcg32<32310901, 1>);
|
||||||
impl Lcg32 {
|
impl Lcg32 {
|
||||||
|
/// Wraps the `u32` as an `Lcg32`.
|
||||||
|
///
|
||||||
|
/// This doesn't do any manipulation of the input state to try and help seed
|
||||||
|
/// the value, it just uses the value directly.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new(state: u32) -> Self {
|
pub const fn new(state: u32) -> Self {
|
||||||
Self(GenericLcg32::new(state))
|
Self(GenericLcg32::new(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances the generator one step, producing a `u32` of output.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_u32(&mut self) -> u32 {
|
pub fn next_u32(&mut self) -> u32 {
|
||||||
self.0.next_u32()
|
self.0.next_u32()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances the generator by `delta` steps all at once.
|
||||||
|
///
|
||||||
|
/// Because the generator output sequence loops, large `delta` values allow
|
||||||
|
/// you to "reverse" the generator.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn jump_state(&mut self, delta: u32) {
|
pub fn jump_state(&mut self, delta: u32) {
|
||||||
self.0.jump_state(delta)
|
self.0.jump_state(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [Linear Congruential Generator][wp-lcg] with a const generic multiplier
|
||||||
|
/// and increment.
|
||||||
|
///
|
||||||
|
/// [wp-lcg]: https://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||||
|
///
|
||||||
|
/// The `ADD` value can be any value at all. Different `ADD` values will reorder
|
||||||
|
/// the sequence that you get from a particular `MUL` value. For best results
|
||||||
|
/// `ADD` should be an odd value. An even `ADD` value gives a generator with a
|
||||||
|
/// significantly shorter period than an odd `ADD` value.
|
||||||
|
///
|
||||||
|
/// The `MUL` value must be carefully selected, because it has an overwhelming
|
||||||
|
/// impact on the generator's output quality. See the linked wikipedia article
|
||||||
|
/// (and its references) if you want information on what `MUL` values you might
|
||||||
|
/// want to use.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct GenericLcg32<const MUL: u32, const ADD: u32>(u32);
|
struct GenericLcg32<const MUL: u32, const ADD: u32>(u32);
|
||||||
impl<const MUL: u32, const ADD: u32> GenericLcg32<MUL, ADD> {
|
impl<const MUL: u32, const ADD: u32> GenericLcg32<MUL, ADD> {
|
||||||
|
/// Wraps the `u32` as a generic LCG.
|
||||||
|
///
|
||||||
|
/// This doesn't do any manipulation of the input state to try and help seed
|
||||||
|
/// the value, it just uses the value directly.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new(state: u32) -> Self {
|
pub const fn new(state: u32) -> Self {
|
||||||
Self(state)
|
Self(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances the generator one step, producing a `u32` of output.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_u32(&mut self) -> u32 {
|
pub fn next_u32(&mut self) -> u32 {
|
||||||
let next_state = self.0.wrapping_mul(MUL).wrapping_add(ADD);
|
let next_state = self.0.wrapping_mul(MUL).wrapping_add(ADD);
|
||||||
next_state
|
next_state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances the generator by `delta` steps all at once.
|
||||||
|
///
|
||||||
|
/// Because the generator output sequence loops, large `delta` values allow
|
||||||
|
/// you to "reverse" the generator.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn jump_state(&mut self, mut delta: u32) {
|
pub fn jump_state(&mut self, mut delta: u32) {
|
||||||
let mut cur_mult: u32 = MUL;
|
let mut cur_mult: u32 = MUL;
|
||||||
|
@ -54,24 +103,34 @@ impl<const MUL: u32, const ADD: u32> GenericLcg32<MUL, ADD> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for pseudorandom number generators that have `u32`
|
||||||
|
/// output from each step of the generator.
|
||||||
pub trait Gen32 {
|
pub trait Gen32 {
|
||||||
|
/// Advance the generator to produce the next `u32`.
|
||||||
fn next_u32(&mut self) -> u32;
|
fn next_u32(&mut self) -> u32;
|
||||||
|
|
||||||
|
/// Produce a `u16`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_u16(&mut self) -> u16 {
|
fn next_u16(&mut self) -> u16 {
|
||||||
(self.next_u32() >> 16) as u16
|
(self.next_u32() >> 16) as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produce a `u8`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_u8(&mut self) -> u8 {
|
fn next_u8(&mut self) -> u8 {
|
||||||
(self.next_u32() >> 24) as u8
|
(self.next_u32() >> 24) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produce a `bool`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_bool(&mut self) -> bool {
|
fn next_bool(&mut self) -> bool {
|
||||||
(self.next_u16() as i32) < 0
|
(self.next_u16() as i32) < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produce a value that's strictly less than `b`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// * If `b` is zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn next_bounded(&mut self, b: u16) -> u16 {
|
fn next_bounded(&mut self, b: u16) -> u16 {
|
||||||
|
@ -91,34 +150,50 @@ pub trait Gen32 {
|
||||||
high
|
high
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pick a random element of the slice, by value.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// * If the length is 0.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pick<T: Copy>(&mut self, buf: &[T]) -> T {
|
fn pick<T: Copy>(&mut self, buf: &[T]) -> T {
|
||||||
let len16: u16 = saturating_usize_as_u16(buf.len());
|
let len16: u16 = saturating_usize_as_u16(buf.len());
|
||||||
buf[self.next_bounded(len16) as usize]
|
buf[self.next_bounded(len16) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pick a random element of the slice, by shared reference.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// * If the length is 0.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pick_ref<'b, T>(&mut self, buf: &'b [T]) -> &'b T {
|
fn pick_ref<'b, T>(&mut self, buf: &'b [T]) -> &'b T {
|
||||||
let len16: u16 = saturating_usize_as_u16(buf.len());
|
let len16: u16 = saturating_usize_as_u16(buf.len());
|
||||||
&buf[self.next_bounded(len16) as usize]
|
&buf[self.next_bounded(len16) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pick a random element of the slice, by unique reference.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// * If the length is 0.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pick_mut<'b, T>(&mut self, buf: &'b mut [T]) -> &'b mut T {
|
fn pick_mut<'b, T>(&mut self, buf: &'b mut [T]) -> &'b mut T {
|
||||||
let len16: u16 = saturating_usize_as_u16(buf.len());
|
let len16: u16 = saturating_usize_as_u16(buf.len());
|
||||||
&mut buf[self.next_bounded(len16) as usize]
|
&mut buf[self.next_bounded(len16) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shuffles the elements of the slice.
|
||||||
|
///
|
||||||
|
/// On an empty slice this is a no-op.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn shuffle<T>(&mut self, buf: &mut [T]) {
|
fn shuffle<T>(&mut self, buf: &mut [T]) {
|
||||||
let mut possibility_count: u16 = saturating_usize_as_u16(buf.len());
|
if let Some(end_index) = buf.len().checked_sub(1) {
|
||||||
let mut this_index: usize = 0;
|
let mut possibility_count: u16 = saturating_usize_as_u16(buf.len());
|
||||||
let end_index = buf.len() - 1;
|
let mut this_index: usize = 0;
|
||||||
while this_index < end_index {
|
while this_index < end_index {
|
||||||
let offset = self.next_bounded(possibility_count) as usize;
|
let offset = self.next_bounded(possibility_count) as usize;
|
||||||
buf.swap(this_index, this_index + offset);
|
buf.swap(this_index, this_index + offset);
|
||||||
possibility_count -= 1;
|
possibility_count -= 1;
|
||||||
this_index += 1;
|
this_index += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue