95 lines
3.7 KiB
Rust
95 lines
3.7 KiB
Rust
use crate::RandomGenerator;
|
|
|
|
// DAS COMMENT:
|
|
// The prompt for creating this code was verbatim: Okay, now problem 3 is an interesting pro AI
|
|
// take. The prompt for problem 3 is "Write code to implement PCG32 in my favorite programming
|
|
// language (Rust!)" Please do similar statistics and procedures as LFSR and LCG.
|
|
//
|
|
// For your context, previous RNGs have had a little help for me to understand how traits work in
|
|
// Rust, but the implementations are almost entirely mine. This was generated using a Claude Code
|
|
// session with Sonnet 4.5.
|
|
|
|
// PCG32 - Permuted Congruential Generator
|
|
// A modern, high-quality PRNG with excellent statistical properties
|
|
// Uses a 64-bit LCG internally with output permutation
|
|
pub struct Pcg32 {
|
|
state: u64, // Internal LCG state (64-bit)
|
|
increment: u64, // Must be odd
|
|
}
|
|
|
|
impl Pcg32 {
|
|
// Standard PCG32 parameters
|
|
const MULTIPLIER: u64 = 6364136223846793005;
|
|
const DEFAULT_INCREMENT: u64 = 1442695040888963407;
|
|
|
|
pub fn new(seed: u64) -> Self {
|
|
// DAS - Any new instance of Pcg immediately runs through the new_with_increment function
|
|
Pcg32::new_with_increment(seed, Self::DEFAULT_INCREMENT)
|
|
}
|
|
|
|
pub fn new_with_increment(seed: u64, increment: u64) -> Self {
|
|
let mut pcg = Pcg32 {
|
|
state: 0,
|
|
increment: (increment << 1) | 1, // Ensure increment is odd
|
|
// DAS - I did NOT tell claude to do this. It basically copied the prose entirely on
|
|
// it's own, and Rust's syntax is almost exactly the same as written. This shifts the
|
|
// whole increment one bit left, and ensures the 2.pow(0) bit is always true (aka, 1,
|
|
// and always odd).
|
|
};
|
|
|
|
// Initialize state properly
|
|
//
|
|
// DAS - This is that first LCG step. Claude here I think is
|
|
// technically skipping step 3. It's just adding the value to the seed, and then stepping
|
|
// (steps 4 and 5 only). For the purpose of AI authorship, I'm going to let it rock and
|
|
// we'll see what happens.
|
|
|
|
pcg.state = pcg.state.wrapping_add(seed);
|
|
pcg.step();
|
|
|
|
pcg
|
|
}
|
|
|
|
// Internal LCG step
|
|
fn step(&mut self) {
|
|
// DAS - This is a basic LCG implementation.
|
|
self.state = self
|
|
.state
|
|
.wrapping_mul(Self::MULTIPLIER)
|
|
.wrapping_add(self.increment);
|
|
}
|
|
|
|
// PCG output permutation: XSH-RR (xorshift high, random rotation)
|
|
fn output(state: u64) -> u32 {
|
|
// XOR high and low parts
|
|
let xorshifted = (((state >> 18) ^ state) >> 27) as u32;
|
|
let rot = (state >> 59) as u32;
|
|
|
|
// Random rotation
|
|
//
|
|
// DAS - DGC, I think your implementation of this is wrong in the homework. You say "Return
|
|
// (xorshifted >> rot)", but wouldn't that pad zeros and not truly 'rotate' the number? At
|
|
// least, that's what Claude is intuiting here by using rotate_right() instead.
|
|
xorshifted.rotate_right(rot)
|
|
}
|
|
}
|
|
|
|
impl RandomGenerator for Pcg32 {
|
|
//DAS - This is implemenating the trait for this specific module. Man, I love Rust.
|
|
fn next(&mut self) -> u64 {
|
|
let old_state = self.state;
|
|
|
|
self.step();
|
|
//DAS - Techincally we've been fudging things as u32. We need to go back to u64 for all our
|
|
//nice traits to work with the plotting functions and statistics in the main script.
|
|
Self::output(old_state) as u64
|
|
|
|
//DAS - This does exactly as first described in the HW. Save the old state, advance the
|
|
//state, permute on the old state and output the permuted old state.
|
|
}
|
|
|
|
fn modulus(&self) -> u64 {
|
|
u32::MAX as u64
|
|
}
|
|
}
|