Add functions to dump and restore PRNG state.
1 files changed, 84 insertions(+), 0 deletions(-)

M src/lib.rs
M src/lib.rs +84 -0
@@ 69,6 69,22 @@ impl Rand32 {
         rng
     }
 
+    /// Returns the internal state of the PRNG; the values returned
+    /// can be fed back into `Rand32::new_inc()` to...
+    /// No they can't actually, 'cause `new_inc()` does some extra
+    /// initialization work.
+    pub fn state(&self) -> (u64, u64) {
+        (self.state, self.inc)
+    }
+
+    /// Creates a new PRNG from a saved state from `Rand32::state()`.
+    /// This is NOT the same as `new_inc()` because `new_inc()` does
+    /// extra setup work to initialize.
+    pub fn from_state(state: (u64, u64)) -> Self {
+        let (state, inc) = state;
+        Self { state, inc }
+    }
+
     /// Produces a random `u32` in the range `[0, u32::MAX]`.
     pub fn rand_u32(&mut self) -> u32 {
         let oldstate: u64 = self.state;

          
@@ 177,6 193,22 @@ impl Rand64 {
         rng
     }
 
+    /// Returns the internal state of the PRNG; the values returned
+    /// can be fed back into `Rand32::new_inc()` to...
+    /// No they can't actually, 'cause `new_inc()` does some extra
+    /// initialization work.
+    pub fn state(&self) -> (u128, u128) {
+        (self.state, self.inc)
+    }
+
+    /// Creates a new PRNG from a saved state from `Rand32::state()`.
+    /// This is NOT the same as `new_inc()` because `new_inc()` does
+    /// extra setup work to initialize.
+    pub fn from_state(state: (u128, u128)) -> Self {
+        let (state, inc) = state;
+        Self { state, inc }
+    }
+
     /// Produces a random `u64` in the range`[0, u64::MAX]`.
     pub fn rand_u64(&mut self) -> u64 {
         let oldstate: u128 = self.state;

          
@@ 462,4 494,56 @@ mod tests {
             }
         }
     }
+
+    /// Make sure that saving a RNG state and restoring
+    /// it works.
+    /// See https://todo.sr.ht/~icefox/oorandom/1
+    #[test]
+    fn test_save_restore() {
+        {
+            let seed = 54321;
+            let mut r1 = Rand32::new(seed);
+            let s1 = r1.state();
+            let mut r2 = Rand32::from_state(s1);
+            assert_eq!(r1, r2);
+            for _ in 0..1000 {
+                assert_eq!(r1.rand_u32(), r2.rand_u32());
+            }
+        }
+
+        {
+            let seed = 3141592653;
+            let inc = 0xDEADBEEF;
+            let mut r1 = Rand32::new_inc(seed, inc);
+            let s1 = r1.state();
+            let mut r2 = Rand32::from_state(s1);
+            assert_eq!(r1, r2);
+            for _ in 0..1000 {
+                assert_eq!(r1.rand_u32(), r2.rand_u32());
+            }
+        }
+
+        {
+            let seed = 54321;
+            let mut r1 = Rand64::new(seed);
+            let s1 = r1.state();
+            let mut r2 = Rand64::from_state(s1);
+            assert_eq!(r1, r2);
+            for _ in 0..1000 {
+                assert_eq!(r1.rand_u64(), r2.rand_u64());
+            }
+        }
+
+        {
+            let seed = 3141592653;
+            let inc = 0xDEADBEEF;
+            let mut r1 = Rand64::new_inc(seed, inc);
+            let s1 = r1.state();
+            let mut r2 = Rand64::from_state(s1);
+            assert_eq!(r1, r2);
+            for _ in 0..1000 {
+                assert_eq!(r1.rand_u64(), r2.rand_u64());
+            }
+        }
+    }
 }