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 } }