Skip to main content

osom_lib_prng/prngs/
splitmix.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    clippy::cast_possible_wrap,
4)]
5
6use core::{convert::Infallible, ops::RangeBounds};
7
8use osom_lib_reprc::macros::reprc;
9use osom_lib_try_clone::TryClone;
10
11use crate::{
12    prngs::helpers::{
13        fill_raw_from_array_generator, generate_f32_in_range, generate_f64_in_range, generate_i32_in_range,
14        generate_i64_in_range, generate_u32_in_range, generate_u64_in_range
15    },
16    traits::{PRNConcreteBoundedGenerator, PRNConcreteGenerator, PRNGenerator, Seedable},
17};
18
19/// The Rust imlementation of `SplitMix64` algorithm. It is good
20/// for generating seeds and good enough general purpose PRNG.
21#[derive(Debug, PartialEq, Eq, Clone, Copy)]
22#[reprc]
23#[repr(transparent)]
24#[must_use]
25pub struct SplitMix64 {
26    state: u64
27}
28
29impl TryClone for SplitMix64 {
30    type Error = Infallible;
31
32    fn try_clone(&self) -> Result<Self, Self::Error> {
33        Ok(*self)
34    }
35}
36
37impl SplitMix64 {
38    /// Creates a new [`SplitMix64`] from a seed.
39    #[inline(always)]
40    pub const fn with_seed(seed: u64) -> Self {
41        let state = if seed != 0 { seed } else { 1 };
42        Self { state }
43    }
44
45    /// Generates a new pseudo-random u64.
46    pub const fn next(&mut self) -> u64 {
47        self.state = self.state.wrapping_add(0x9e3779b97f4a7c15);
48        let mut result = self.state;
49        result = (result ^ (result >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
50        result = (result ^ (result >> 27)).wrapping_mul(0x94d049bb133111eb);
51        result ^ (result >> 31)
52    }
53}
54
55impl Seedable<u64> for SplitMix64 {
56    fn with_seed(seed: u64) -> Self {
57        Self::with_seed(seed)
58    }
59}
60
61impl PRNGenerator for SplitMix64 {
62    unsafe fn fill_raw(&mut self, dst_ptr: *mut u8, dst_len: usize) {
63        fill_raw_from_array_generator(|| self.next().to_be_bytes(), dst_ptr, dst_len);
64    }
65}
66
67impl PRNConcreteGenerator<SplitMix64> for bool {
68    fn generate(generator: &mut SplitMix64) -> Self {
69        (generator.next() & 1) == 1
70    }
71}
72
73impl<const N: usize> PRNConcreteGenerator<SplitMix64> for [u8; N] {
74    fn generate(generator: &mut SplitMix64) -> Self {
75        if N == 0 {
76            return [0u8; N];
77        }
78        let mut item = core::mem::MaybeUninit::<Self>::uninit();
79        unsafe {
80            generator.fill_raw(item.as_mut_ptr().cast(), size_of::<Self>());
81            item.assume_init()
82        }
83    }
84}
85
86impl PRNConcreteGenerator<SplitMix64> for u8 {
87    fn generate(generator: &mut SplitMix64) -> Self {
88        u8::from_le_bytes(generator.generate::<[u8; 1]>())
89    }
90}
91
92impl PRNConcreteGenerator<SplitMix64> for i8 {
93    fn generate(generator: &mut SplitMix64) -> Self {
94        i8::from_le_bytes(generator.generate::<[u8; 1]>())
95    }
96}
97
98
99impl PRNConcreteGenerator<SplitMix64> for u32 {
100    fn generate(generator: &mut SplitMix64) -> Self {
101        u32::from_le_bytes(generator.generate::<[u8; 4]>())
102    }
103}
104
105impl PRNConcreteGenerator<SplitMix64> for i32 {
106    fn generate(generator: &mut SplitMix64) -> Self {
107        i32::from_le_bytes(generator.generate::<[u8; 4]>())
108    }
109}
110
111impl PRNConcreteGenerator<SplitMix64> for u64 {
112    fn generate(generator: &mut SplitMix64) -> Self {
113        u64::from_le_bytes(generator.generate::<[u8; 8]>())
114    }
115}
116
117impl PRNConcreteGenerator<SplitMix64> for i64 {
118    fn generate(generator: &mut SplitMix64) -> Self {
119        i64::from_le_bytes(generator.generate::<[u8; 8]>())
120    }
121}
122
123impl PRNConcreteBoundedGenerator<SplitMix64> for u32 {
124    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
125        generate_u32_in_range(generator, range)
126    }
127}
128
129impl PRNConcreteBoundedGenerator<SplitMix64> for u64 {
130    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
131        generate_u64_in_range(generator, range)
132    }
133}
134
135impl PRNConcreteBoundedGenerator<SplitMix64> for i32 {
136    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
137        generate_i32_in_range(generator, range)
138    }
139}
140
141impl PRNConcreteBoundedGenerator<SplitMix64> for i64 {
142    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
143        generate_i64_in_range(generator, range)
144    }
145}
146
147impl PRNConcreteBoundedGenerator<SplitMix64> for f32 {
148    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
149        generate_f32_in_range(generator, range)
150    }
151}
152
153impl PRNConcreteBoundedGenerator<SplitMix64> for f64 {
154    fn generate<TBounds: RangeBounds<Self>>(generator: &mut SplitMix64, range: TBounds) -> Self {
155        generate_f64_in_range(generator, range)
156    }
157}