Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Lokathor committed Aug 30, 2023
1 parent 23d85ff commit 3a96b8e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 35 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ default = ["getrandom"]
[dependencies]
bytemuck = { version = "1.13.1", features = ["min_const_generics"] }
getrandom = { version = "0.2.10", optional = true }


[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docs_rs"]
all-features = true
3 changes: 3 additions & 0 deletions src/formulas.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Base formulas used elsewhere in the crate.

/// This is the suggested multiplier for a PCG with 64 bits of state.
pub const PCG_MUL_64: u64 = 6364136223846793005;

/// Advance a 32-bit LCG's state.
#[inline]
#[must_use]
Expand Down
25 changes: 25 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
#![warn(missing_debug_implementations)]
#![warn(clippy::missing_const_for_fn)]
#![warn(clippy::missing_inline_in_public_items)]
#![cfg_attr(docs_rs, feature(doc_cfg))]

//! Pseudo-random number generator crate.
//!
//! NOT FOR CRYPTOGRAPHIC PURPOSES.
//!
//! ## Cargo Features
//!
//! * `getrandom`: This adds additional methods that use the `getrandom` crate
//! to initialize a generator from an external randomness source.

mod bounded_rand;
pub use bounded_rand::*;
Expand All @@ -15,3 +21,22 @@ mod pcg;
pub use pcg::*;

pub mod formulas;

/// A trait for pseudo-random number generators with 32-bit output per step.
pub trait Gen32 {
/// Makes the generator create the next output.
fn next_u32(&mut self) -> u32;
}

impl Gen32 for PCG32 {
#[inline]
fn next_u32(&mut self) -> u32 {
PCG32::next_u32(self)
}
}
impl<const K: usize> Gen32 for PCG32X<K> {
#[inline]
fn next_u32(&mut self) -> u32 {
PCG32X::<K>::next_u32(self)
}
}
84 changes: 49 additions & 35 deletions src/pcg.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
use crate::formulas::{lcg64_jump, lcg64_step, xsh_rr_u64_to_u32};
use crate::formulas::{lcg64_jump, lcg64_step, xsh_rr_u64_to_u32, PCG_MUL_64};

const PCG32_MUL: u64 = 6364136223846793005;

/// A [Permuted congruential generator][wp] with 32-bit output.
/// A [Permuted Congruential Generator][wp] with 32-bit output.
///
/// [wp]: https://en.wikipedia.org/wiki/Permuted_congruential_generator
#[derive(Debug, Clone)]
pub struct PCG32 {
/// The generator's state, changes with each step.
/// The generator's state.
///
/// This changes with each step of the generator. It's the generator's
/// "position" within the output stream.
pub state: u64,

/// The generator's increment, doesn't change.
/// The generator's increment.
///
/// This doesn't change as the generator advances. Instead it determines which
/// of the possible output streams the generator will use. Each `inc` value
/// will give a different ordering of all the possible outputs.
///
/// If this is not an odd value the generator will have a reduced period
/// (though the generator will still otherwise work).
/// * When the `inc` value is **odd** then each stream is `2**64` elements
/// long.
/// * If the `inc` value is **even** the output stream is only `2**62`
/// elements long.
pub inc: u64,
}
impl PCG32 {
Expand All @@ -26,21 +33,24 @@ impl PCG32 {

/// Create a new generator seeded with data from
/// [getrandom](getrandom::getrandom).
///
/// This method ensures that the `inc` of the new generator is odd.
#[cfg(feature = "getrandom")]
#[cfg_attr(docs_rs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn from_getrandom() -> Result<Self, getrandom::Error> {
use bytemuck::bytes_of_mut;

let mut buf = [0_u64; 2];
getrandom::getrandom(bytes_of_mut(&mut buf))?;

Ok(Self::new(buf[0], buf[1]))
Ok(Self::new(buf[0], buf[1] | 1))
}

/// Generate the next `u32` in the sequence.
#[inline]
pub fn next_u32(&mut self) -> u32 {
let new_state = lcg64_step(PCG32_MUL, self.inc, self.state);
let new_state = lcg64_step(PCG_MUL_64, self.inc, self.state);
let out = xsh_rr_u64_to_u32(self.state);
self.state = new_state;
out
Expand All @@ -52,7 +62,7 @@ impl PCG32 {
/// passing `x.wrapping_neg()`.
#[inline]
pub fn jump(&mut self, delta: u64) {
self.state = lcg64_jump(PCG32_MUL, self.inc, self.state, delta);
self.state = lcg64_jump(PCG_MUL_64, self.inc, self.state, delta);
}
}

Expand All @@ -61,24 +71,25 @@ impl PCG32 {
///
/// [wp]: https://en.wikipedia.org/wiki/Permuted_congruential_generator
///
/// The `K` value determines the number of elements in the extension array.
/// This is like the [PCG32], but replaces the single `inc` field with an
/// extension array of `K` elements. At each step of the generator a different
/// element of the array is `XOR`-ed with the output, and every time the
/// generator passes 0 the array is advanced (so that it's also advancing over
/// time). This gives a radically larger generator period, and guarantees that
/// any sequence of `K` outputs in a row will appear at least once within the
/// generator's full output stream.
///
/// * The generator's output sequence will be "`K`-dimensionally
/// equidistributed". In other words, any sequence of up to `K` elements in a
/// row will exist *somewhere* within the complete output sequence.
/// * For best results, `K` should be a power of 2. The type works with other
/// `K` values, but when `K` is a power of 2 then selecting an element from
/// the extension array is faster.
/// For best results, `K` should be a power of 2. The type works with other `K`
/// values, but when `K` is a power of 2 then selecting an element from the
/// extension array is *significantly* faster (a bit mask instead of an integer
/// division).
#[derive(Debug, Clone)]
pub struct PCG32X<const K: usize> {
/// The generator's state, changes with each step.
pub state: u64,

/// The generator's increment, doesn't change.
/// The generator's state.
///
/// If this is not an odd value the generator will have a reduced period
/// (though the generator will still otherwise work).
pub inc: u64,
/// This changes with each step of the generator. It's the generator's
/// "position" within the output stream.
pub state: u64,

/// The generator's extension array. The generator's base output is XOR'd with
/// random elements of this array to determine the final output at each step.
Expand All @@ -88,22 +99,23 @@ impl<const K: usize> PCG32X<K> {
/// Creates a new generator by directly using the value given.
#[inline]
#[must_use]
pub const fn new(state: u64, inc: u64, ext: [u32; K]) -> Self {
Self { state, inc, ext }
pub const fn new(state: u64, ext: [u32; K]) -> Self {
Self { state, ext }
}

/// Create a new generator seeded with data from
/// [getrandom](getrandom::getrandom).
#[cfg(feature = "getrandom")]
#[cfg_attr(docs_rs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn from_getrandom() -> Result<Self, getrandom::Error> {
use bytemuck::bytes_of_mut;

let mut state_inc = [0_u64; 2];
getrandom::getrandom(bytes_of_mut(&mut state_inc))?;
let mut state = 0_u64;
getrandom::getrandom(bytes_of_mut(&mut state))?;

let mut out = Self::new(state_inc[0], state_inc[1], [0_u32; K]);
out.ext_getrandom()?;
let mut out = Self::new(state, [0_u32; K]);
out.scramble_ext_array()?;

Ok(out)
}
Expand All @@ -113,8 +125,9 @@ impl<const K: usize> PCG32X<K> {
/// This will completely scramble the generator's position within the output
/// sequence.
#[cfg(feature = "getrandom")]
#[cfg_attr(docs_rs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn ext_getrandom(&mut self) -> Result<(), getrandom::Error> {
pub fn scramble_ext_array(&mut self) -> Result<(), getrandom::Error> {
use bytemuck::bytes_of_mut;

getrandom::getrandom(bytes_of_mut(&mut self.ext))
Expand All @@ -123,7 +136,7 @@ impl<const K: usize> PCG32X<K> {
/// Generate the next `u32` in the sequence.
#[inline]
pub fn next_u32(&mut self) -> u32 {
let new_state = lcg64_step(PCG32_MUL, self.inc, self.state);
let new_state = lcg64_step(PCG_MUL_64, 1, self.state);
let out = if K > 0 {
let ext_index: usize = self.state as usize % K;
let out = xsh_rr_u64_to_u32(self.state) ^ self.ext[ext_index];
Expand All @@ -142,8 +155,9 @@ impl<const K: usize> PCG32X<K> {
///
/// The given `delta` is added to the lowest index element of the extension
/// array, and if that addition carries then the carry will add one to the
/// next higher element, and so on. Normally the PCG will call this
/// automatically whenever its state value passes 0.
/// next higher element, and so on.
///
/// The generator will call this whenever its state value passes 0.
#[inline(never)]
fn ext_add(&mut self, delta: u32) {
if K == 0 {
Expand Down

0 comments on commit 3a96b8e

Please sign in to comment.