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