]> git.nega.tv - josh/narcissus/commitdiff
Add basic random numbers
authorJoshua Simmons <josh@nega.tv>
Tue, 8 Nov 2022 22:25:55 +0000 (23:25 +0100)
committerJoshua Simmons <josh@nega.tv>
Tue, 8 Nov 2022 22:26:49 +0000 (23:26 +0100)
narcissus-core/src/lib.rs
narcissus-core/src/rand.rs [new file with mode: 0644]

index 6c2a35cf310921670d1b079903db0b86d0e2f337..fd9684a09e2c3cf8687297db9749bab62ebd7589 100644 (file)
@@ -7,6 +7,7 @@ pub mod manual_arc;
 mod mutex;
 pub mod obj;
 mod pool;
+pub mod rand;
 mod ref_count;
 pub mod slice;
 mod uuid;
diff --git a/narcissus-core/src/rand.rs b/narcissus-core/src/rand.rs
new file mode 100644 (file)
index 0000000..2522807
--- /dev/null
@@ -0,0 +1,101 @@
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Pcg32 {
+    state: u64,
+}
+
+impl Pcg32 {
+    pub const fn new() -> Self {
+        Self {
+            state: 0x853c49e6748fea9b,
+        }
+    }
+
+    pub fn with_seed(seed: u64) -> Self {
+        let mut rng = Self { state: 0 };
+        let _ = rng.next();
+        rng.state += seed;
+        let _ = rng.next();
+        rng
+    }
+
+    /// Generates a uniformly distributed random number in the range `0..2^32`.
+    #[inline]
+    #[must_use]
+    pub fn next(&mut self) -> u32 {
+        let old_state = self.state;
+        self.state = old_state
+            .wrapping_mul(6364136223846793005)
+            .wrapping_add(1442695040888963407);
+        let xorshift = (((old_state >> 18) ^ old_state) >> 27) as u32;
+        xorshift.rotate_right((old_state >> 59) as u32)
+    }
+}
+
+impl Default for Pcg32 {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Pcg64 {
+    state: u128,
+}
+
+impl Pcg64 {
+    pub const fn new() -> Self {
+        Self {
+            state: 0x979c9a98d84620057d3e9cb6cfe0549b,
+        }
+    }
+
+    pub fn with_seed(seed: u128) -> Self {
+        let mut rng = Self { state: 0 };
+        let _ = rng.next();
+        rng.state += seed;
+        let _ = rng.next();
+        rng
+    }
+
+    /// Generates a uniformly distributed random number in the range `0..2^64`.
+    #[inline]
+    #[must_use]
+    pub fn next(&mut self) -> u64 {
+        let old_state = self.state;
+        self.state = old_state
+            .wrapping_mul(25492979953554139244865540595714422341)
+            .wrapping_add(63641362238467930051442695040888963407);
+        ((old_state >> 64) ^ old_state).rotate_right((old_state >> 122) as u32) as u64
+    }
+}
+
+impl Default for Pcg64 {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn pcg32_default_sequence() {
+        let mut rng = Pcg32::new();
+        assert_eq!(rng.next(), 355248013);
+        assert_eq!(rng.next(), 1055580183);
+        assert_eq!(rng.next(), 3222338950);
+        assert_eq!(rng.next(), 2908720768);
+        assert_eq!(rng.next(), 1758754096);
+    }
+
+    #[test]
+    fn pcg64_default_sequence() {
+        let mut rng = Pcg64::new();
+        assert_eq!(rng.next(), 14322063641855463473);
+        assert_eq!(rng.next(), 14211086133763074855);
+        assert_eq!(rng.next(), 2051302165745047857);
+        assert_eq!(rng.next(), 11538586989805838516);
+        assert_eq!(rng.next(), 486667062142511543);
+    }
+}