Skip to main content

pcg/
lib.rs

1//! # Synopsis
2//!
3//! The PCG crate is a port of the C/C++ PCG library for generating random
4//! numbers. It implements the `RngCore` trait so all of the standard Rust methods
5//! for generating random numbers are available. You can find a reference on the methods provided
6//! by the `Rng` trait here: https://rust-random.github.io/rand/rand/trait.Rng.html
7//!
8//! _Note: you must use the `rand` crate if you want to use the methods provided
9//! by the `Rng` trait._
10//!
11//! ```
12//! use rand::prelude::*;
13//! use pcg::Pcg;
14//!
15//! // Create the PCG struct with state
16//! let mut pcg = Pcg::default();
17//!
18//! // Generate arbitrary random values
19//! let mut some_bool: bool = pcg.gen();
20//! let mut some_f32: f32 = pcg.gen();
21//! let mut some_u32: u32 = pcg.gen();
22//! ```
23
24use crate::consts::{INCREMENTOR, INIT_INC, INIT_STATE};
25
26#[cfg(feature = "std")]
27use std::{
28    hash::{Hash, Hasher},
29    num::Wrapping,
30};
31
32#[cfg(not(feature = "std"))]
33use core::{
34    hash::{Hash, Hasher},
35    num::Wrapping,
36};
37
38use rand_core::{impls, Error, RngCore, SeedableRng};
39
40#[cfg(feature = "serde")]
41use serde::{Deserialize, Serialize};
42
43mod consts;
44
45/// The `Pcg` state struct contains state information for use by the random
46/// number generating functions.
47///
48/// The internals are private and shouldn't be modified by
49/// anything other than the member functions. Note that the random number
50/// generating functions will modify the state of this struct, so you must
51/// initialize `Pcg` as mutable in order to use any of its functionality.
52#[derive(Debug, Clone, Eq, PartialEq)]
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54pub struct Pcg {
55    state: u64,
56    inc: u64,
57}
58
59impl Pcg {
60    /// Constructs a new PCG state struct with a particular seed and sequence.
61    ///
62    /// The function returns a struct with state information for the PCG RNG.  The `seed` param
63    /// supplies an initial state for the RNG, and the `seq` param functionally acts as a stream
64    /// ID. If you're unsure of which params to initialize this struct with, construct the default
65    /// struct.
66    ///
67    /// If you can't think of a seed and a state to initialize this with, just use the default
68    /// struct.
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// use pcg::Pcg;
74    ///
75    /// let mut rng = Pcg::new(0, 0);
76    /// ```
77    pub fn new(seed: u64, seq: u64) -> Pcg {
78        Pcg {
79            state: seed,
80            inc: (seq << 1) | 1,
81        }
82    }
83}
84
85impl Default for Pcg {
86    fn default() -> Self {
87        Pcg {
88            state: INIT_STATE,
89            inc: INIT_INC,
90        }
91    }
92}
93
94impl RngCore for Pcg {
95    fn next_u32(&mut self) -> u32 {
96        self.next_u64() as u32
97    }
98
99    fn next_u64(&mut self) -> u64 {
100        let old_state = self.state;
101        self.state = (Wrapping(old_state) * Wrapping(INCREMENTOR) + Wrapping(self.inc)).0;
102        let xor_shifted = (old_state >> 18) ^ old_state >> 27;
103
104        // need to cast to i64 to allow the `-` operator (casting between integers of
105        // the same size is a no-op)
106        let rot = (old_state >> 59) as i64;
107        (xor_shifted >> rot as u64) | (xor_shifted << ((-rot) & 31))
108    }
109
110    fn fill_bytes(&mut self, dest: &mut [u8]) {
111        impls::fill_bytes_via_next(self, dest)
112    }
113
114    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
115        self.fill_bytes(dest);
116        Ok(())
117    }
118}
119
120/// The number of 8-bit buckets that the seed is made of
121const N: usize = 8;
122
123/// A wrapper type for the PcgSeed
124///
125/// This wrapper allows us to implement a `SeedableRng` for `Pcg`. There are also conversion traits
126/// defined so that you can switch between `PcgSeed` and `U64` easily. The lowest bit in the lowest
127/// index of the underlying array corresponds to the most significant bit in the converted `U64`.
128///
129/// For example: `[0, 1, 2, 3, 4, 5, 6, 7]` corresponds to `01234567` when converted to the packged
130/// unsigned integer representation.
131#[derive(Debug, Copy, Clone, Eq, PartialEq)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133pub struct PcgSeed(pub [u8; N]);
134
135/// A wrapper type for u64 so we can define methods on a built-in primitive
136///
137/// This enables, amongst other things, conversions from `u64` to `PcgSeed`.
138#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub struct U64(pub u64);
141
142/// A bit mask for u8
143const MASK: u8 = 0b11111111;
144
145impl From<PcgSeed> for U64 {
146    fn from(seed: PcgSeed) -> Self {
147        let mut res: u64 = 0;
148
149        // We iterate through the array of bytes, packing them into a u64 by filling in a
150        // byte-sized section at a time
151        for (i, &byte) in seed.0.iter().enumerate() {
152            // We have to subtract from the index because the 0th index of the array corresponds to
153            // the most significant bit (MSB). If the array is [0, 1, 2, 3], we want the resulting
154            // integer to look like 0123.
155            let shift_up = (N - i - 1) * 8;
156            let byte = byte as u64;
157            let block = (byte << shift_up) as u64;
158            res |= block;
159        }
160        U64(res)
161    }
162}
163
164impl Default for PcgSeed {
165    fn default() -> Self {
166        Self([0; N])
167    }
168}
169
170impl AsMut<[u8]> for PcgSeed {
171    fn as_mut(&mut self) -> &mut [u8] {
172        &mut self.0
173    }
174}
175
176impl Hash for PcgSeed {
177    fn hash<H: Hasher>(&self, state: &mut H) {
178        // create a vector from the array
179        let seed_vec = self.0.to_vec();
180        seed_vec.hash(state);
181    }
182}
183
184impl From<u64> for PcgSeed {
185    fn from(init: u64) -> Self {
186        let mut seed: [u8; N] = [0; N];
187
188        for i in 0..N {
189            let shift_factor = (N - i - 1) * 8;
190            let section = (init >> shift_factor) as u8;
191            seed[i] = section & MASK;
192        }
193        PcgSeed(seed)
194    }
195}
196
197impl From<U64> for PcgSeed {
198    fn from(init: U64) -> Self {
199        init.0.into()
200    }
201}
202
203impl SeedableRng for Pcg {
204    type Seed = PcgSeed;
205
206    fn from_seed(seed: Self::Seed) -> Pcg {
207        Pcg::new(U64::from(seed).0, INIT_INC)
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use std::fmt::{Binary, Debug};
215
216    /// A helper function to compare variables of a type that can be represented as a binary string
217    ///
218    /// This provides more helpful/convenient error messages which will show the binary
219    /// representations of integers rather than the base 10 representations.
220    fn assert_eq_binary<T>(a: T, b: T)
221    where
222        T: PartialEq + Debug + Binary,
223    {
224        let b_str_a = format!("{:b}", a);
225        let b_str_b = format!("{:b}", b);
226        assert_eq!(b_str_a, b_str_b);
227    }
228
229    #[test]
230    fn test_seed_to_u64() {
231        let mut seed = PcgSeed::default();
232        seed.0[0] = MASK;
233
234        let converted_int = U64::from(seed);
235        let expected = (MASK as u64) << ((N - 1) * 8);
236        assert_eq_binary(converted_int.0, expected);
237
238        let mut seed = PcgSeed::default();
239        seed.0[N - 1] = MASK;
240        let converted_int = U64::from(seed);
241        let expected = MASK as u64;
242        assert_eq_binary(converted_int.0, expected)
243    }
244
245    #[test]
246    fn test_u64_to_seed() {
247        let integer = (MASK as u64) << ((N - 1) * 8);
248        let seed = PcgSeed::from(integer);
249        let mut expected = PcgSeed::default();
250        expected.0[0] = MASK;
251        assert_eq!(seed, expected);
252
253        let integer = MASK as u64;
254        let seed = PcgSeed::from(integer);
255        let mut expected = PcgSeed::default();
256        expected.0[N - 1] = MASK;
257        assert_eq!(seed, expected);
258    }
259}