sfc_prng/
sfc32.rs

1// SPDX-FileCopyrightText: 2025 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! An implementation of the sfc32 random number generator.
6
7use rand_core::{RngCore, SeedableRng, impls, le};
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11/// A sfc32 random number generator.
12///
13/// The sfc32 algorithm is not suitable for cryptographic uses but is very fast.
14/// This algorithm has a 128-bit state and outputs 32-bit random numbers. The
15/// average period of this algorithm is approximately 2<sup>127</sup>, and the
16/// minimum period is greater than or equal to 2<sup>32</sup>.
17///
18/// The algorithm used here is translated from the reference implementation
19/// provided by [PractRand] version pre0.95, which is licensed under the [public
20/// domain].
21///
22/// # Examples
23///
24/// ```
25/// # use sfc_prng::{
26/// #     Sfc32,
27/// #     rand_core::{RngCore, SeedableRng},
28/// # };
29/// #
30/// let mut rng = Sfc32::from_seed([0; 12]);
31/// assert_eq!(rng.next_u32(), 0xfb52_c520);
32/// ```
33///
34/// [PractRand]: https://pracrand.sourceforge.net/
35/// [public domain]: https://pracrand.sourceforge.net/license.txt
36#[derive(Clone, Debug, Eq, PartialEq)]
37#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
38pub struct Sfc32 {
39    a: u32,
40    b: u32,
41    c: u32,
42    counter: u32,
43}
44
45impl Sfc32 {
46    /// Creates a new `Sfc32` using the given seeds.
47    ///
48    /// If `rounds` is [`None`], the state is mixed up 15 rounds during
49    /// initialization.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// # use sfc_prng::{Sfc32, rand_core::RngCore};
55    /// #
56    /// let mut rng = Sfc32::new(0, 0, 0, None);
57    /// assert_eq!(rng.next_u32(), 0xfb52_c520);
58    /// ```
59    #[must_use]
60    #[inline]
61    pub fn new(a: u32, b: u32, c: u32, rounds: Option<u32>) -> Self {
62        let mut state = Self {
63            a,
64            b,
65            c,
66            counter: 1,
67        };
68        let rounds = rounds.unwrap_or(15);
69        for _ in 0..rounds {
70            state.next_u32();
71        }
72        state
73    }
74
75    #[allow(clippy::cast_possible_truncation)]
76    /// Creates a new `Sfc32` using a [`u64`] seed.
77    ///
78    /// If `rounds` is [`None`], the state is mixed up 12 rounds during
79    /// initialization.
80    ///
81    /// <div class="warning">
82    ///
83    /// Note that the result of this method is different from the result of
84    /// [`Sfc32::seed_from_u64`].
85    ///
86    /// </div>
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use sfc_prng::{Sfc32, rand_core::RngCore};
92    /// #
93    /// let mut rng = Sfc32::new_u64(0, None);
94    /// assert_eq!(rng.next_u32(), 0x5146_76c3);
95    /// ```
96    #[must_use]
97    #[inline]
98    pub fn new_u64(seed: u64, rounds: Option<u32>) -> Self {
99        let (a, b, c) = (0, seed as u32, (seed >> u32::BITS) as u32);
100        let rounds = rounds.or(Some(12));
101        Self::new(a, b, c, rounds)
102    }
103}
104
105impl RngCore for Sfc32 {
106    #[inline]
107    fn next_u32(&mut self) -> u32 {
108        const ROTATION: u32 = 21;
109        const RIGHT_SHIFT: u32 = 9;
110        const LEFT_SHIFT: u32 = 3;
111
112        let tmp = self.a.wrapping_add(self.b).wrapping_add(self.counter);
113        self.a = self.b ^ (self.b >> RIGHT_SHIFT);
114        self.b = self.c.wrapping_add(self.c << LEFT_SHIFT);
115        self.c = self.c.rotate_left(ROTATION).wrapping_add(tmp);
116        self.counter = self.counter.wrapping_add(1);
117        tmp
118    }
119
120    #[inline]
121    fn next_u64(&mut self) -> u64 {
122        impls::next_u64_via_u32(self)
123    }
124
125    #[inline]
126    fn fill_bytes(&mut self, dst: &mut [u8]) {
127        impls::fill_bytes_via_next(self, dst);
128    }
129}
130
131impl SeedableRng for Sfc32 {
132    type Seed = [u8; 12];
133
134    #[inline]
135    fn from_seed(seed: Self::Seed) -> Self {
136        let mut s = [u32::default(); 3];
137        le::read_u32_into(&seed, &mut s);
138        Self::new(s[0], s[1], s[2], None)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use core::{any, mem};
145
146    use super::*;
147
148    static EXPECTED_1: [u32; 16] = [
149        0xfb52_c520,
150        0x3880_2be1,
151        0x9482_79e6,
152        0xec4b_f1d9,
153        0x7cb0_a909,
154        0xfad8_b4a8,
155        0x3ca4_b808,
156        0x3821_b4c5,
157        0x5e70_23ca,
158        0x50f2_6bf7,
159        0xf1e1_b0a2,
160        0x6163_032f,
161        0x3bf3_c9a4,
162        0x6db6_c5e0,
163        0x5733_1c8c,
164        0x2aaf_9993,
165    ];
166    static EXPECTED_BYTES_1: [u8; 64] = [
167        0x20, 0xc5, 0x52, 0xfb, 0xe1, 0x2b, 0x80, 0x38, 0xe6, 0x79, 0x82, 0x94, 0xd9, 0xf1, 0x4b,
168        0xec, 0x09, 0xa9, 0xb0, 0x7c, 0xa8, 0xb4, 0xd8, 0xfa, 0x08, 0xb8, 0xa4, 0x3c, 0xc5, 0xb4,
169        0x21, 0x38, 0xca, 0x23, 0x70, 0x5e, 0xf7, 0x6b, 0xf2, 0x50, 0xa2, 0xb0, 0xe1, 0xf1, 0x2f,
170        0x03, 0x63, 0x61, 0xa4, 0xc9, 0xf3, 0x3b, 0xe0, 0xc5, 0xb6, 0x6d, 0x8c, 0x1c, 0x33, 0x57,
171        0x93, 0x99, 0xaf, 0x2a,
172    ];
173
174    const SEED_2: [u8; 12] = [
175        0x00, 0x00, 0x00, 0x00, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
176    ];
177    static EXPECTED_2: [u32; 16] = [
178        0x35e0_5b54,
179        0x4c62_7ca1,
180        0x33a0_43e2,
181        0xb611_3c67,
182        0x7cab_9699,
183        0x4a52_efeb,
184        0x5936_797f,
185        0x139e_2b9f,
186        0xc7df_3db1,
187        0x61ce_1717,
188        0x5581_e344,
189        0xbc30_16ea,
190        0xa6bf_d381,
191        0xcfed_5524,
192        0x8a34_536c,
193        0x3e3f_a43b,
194    ];
195    static EXPECTED_BYTES_2: [u8; 64] = [
196        0x54, 0x5b, 0xe0, 0x35, 0xa1, 0x7c, 0x62, 0x4c, 0xe2, 0x43, 0xa0, 0x33, 0x67, 0x3c, 0x11,
197        0xb6, 0x99, 0x96, 0xab, 0x7c, 0xeb, 0xef, 0x52, 0x4a, 0x7f, 0x79, 0x36, 0x59, 0x9f, 0x2b,
198        0x9e, 0x13, 0xb1, 0x3d, 0xdf, 0xc7, 0x17, 0x17, 0xce, 0x61, 0x44, 0xe3, 0x81, 0x55, 0xea,
199        0x16, 0x30, 0xbc, 0x81, 0xd3, 0xbf, 0xa6, 0x24, 0x55, 0xed, 0xcf, 0x6c, 0x53, 0x34, 0x8a,
200        0x3b, 0xa4, 0x3f, 0x3e,
201    ];
202
203    #[test]
204    fn clone() {
205        let rng = Sfc32::from_seed(Default::default());
206        assert_eq!(rng.clone(), rng);
207    }
208
209    #[test]
210    fn debug() {
211        {
212            let rng = Sfc32::from_seed(Default::default());
213            assert_eq!(
214                format!("{rng:?}"),
215                "Sfc32 { a: 3033783054, b: 1182722562, c: 4269119441, counter: 16 }"
216            );
217        }
218        {
219            let rng = Sfc32::seed_from_u64(1);
220            assert_eq!(
221                format!("{rng:?}"),
222                "Sfc32 { a: 163349985, b: 1519831815, c: 3040613532, counter: 16 }"
223            );
224        }
225    }
226
227    #[test]
228    fn equality() {
229        assert_eq!(
230            Sfc32::from_seed(Default::default()),
231            Sfc32::from_seed(Default::default())
232        );
233        assert_ne!(
234            Sfc32::from_seed(Default::default()),
235            Sfc32::from_seed([u8::MAX; 12])
236        );
237    }
238
239    #[test]
240    fn new() {
241        {
242            let mut rng = Sfc32::new(u32::default(), u32::default(), u32::default(), None);
243            for e in EXPECTED_1 {
244                assert_eq!(rng.next_u32(), e);
245            }
246        }
247        {
248            let mut rng = Sfc32::new(u32::default(), 0x89ab_cdef, 0x0123_4567, None);
249            for e in EXPECTED_2 {
250                assert_eq!(rng.next_u32(), e);
251            }
252        }
253    }
254
255    #[test]
256    fn new_u64() {
257        {
258            // This test vector was generated by the `RNG_output` command of PractRand
259            // version pre0.95.
260            //
261            // To generate a hex dump:
262            //
263            // ```sh
264            // ./RNG_output sfc32 64 0x0 | xxd -i
265            // ```
266            let expected = [
267                0x5146_76c3,
268                0x08a8_09df,
269                0x3034_9d2b,
270                0xfb52_c520,
271                0x3880_2be1,
272                0x9482_79e6,
273                0xec4b_f1d9,
274                0x7cb0_a909,
275                0xfad8_b4a8,
276                0x3ca4_b808,
277                0x3821_b4c5,
278                0x5e70_23ca,
279                0x50f2_6bf7,
280                0xf1e1_b0a2,
281                0x6163_032f,
282                0x3bf3_c9a4,
283            ];
284
285            let mut rng = Sfc32::new_u64(u64::default(), None);
286            for e in expected {
287                assert_eq!(rng.next_u32(), e);
288            }
289        }
290        {
291            // This test vector was generated by the `RNG_output` command of PractRand
292            // version pre0.95.
293            //
294            // To generate a hex dump:
295            //
296            // ```sh
297            // ./RNG_output sfc32 64 0x123456789abcdef | xxd -i
298            // ```
299            let expected = [
300                0x8471_2d97,
301                0xf5a3_d9c8,
302                0x5cd0_a295,
303                0x35e0_5b54,
304                0x4c62_7ca1,
305                0x33a0_43e2,
306                0xb611_3c67,
307                0x7cab_9699,
308                0x4a52_efeb,
309                0x5936_797f,
310                0x139e_2b9f,
311                0xc7df_3db1,
312                0x61ce_1717,
313                0x5581_e344,
314                0xbc30_16ea,
315                0xa6bf_d381,
316            ];
317
318            let mut rng = Sfc32::new_u64(0x0123_4567_89ab_cdef, None);
319            for e in expected {
320                assert_eq!(rng.next_u32(), e);
321            }
322        }
323    }
324
325    #[test]
326    fn next_u32() {
327        {
328            let mut rng = Sfc32::from_seed(Default::default());
329            for e in EXPECTED_1 {
330                assert_eq!(rng.next_u32(), e);
331            }
332        }
333        {
334            let mut rng = Sfc32::from_seed(SEED_2);
335            for e in EXPECTED_2 {
336                assert_eq!(rng.next_u32(), e);
337            }
338        }
339        {
340            let seed = [
341                0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
342            ];
343            let expected = [
344                0x03b8_0bb8,
345                0xa87d_bc7e,
346                0x1787_178c,
347                0x4c7b_7234,
348                0xc65d_ade2,
349                0x2c69_2349,
350                0xf52c_2153,
351                0xdf09_8072,
352                0x9d49_b03c,
353                0x9562_381a,
354                0xc9b4_1738,
355                0x64b7_5e54,
356                0x36ce_9b32,
357                0xf106_947e,
358                0x0afc_726b,
359                0x549b_bc87,
360            ];
361
362            let mut rng = Sfc32::from_seed(seed);
363            for e in expected {
364                assert_eq!(rng.next_u32(), e);
365            }
366        }
367    }
368
369    #[test]
370    fn next_u64() {
371        {
372            let mut rng = Sfc32::from_seed(Default::default());
373            for e in EXPECTED_1.map(u64::from).chunks_exact(2) {
374                assert_eq!(rng.next_u64(), (e[1] << u32::BITS) | e[0]);
375            }
376        }
377        {
378            let mut rng = Sfc32::from_seed(SEED_2);
379            for e in EXPECTED_2.map(u64::from).chunks_exact(2) {
380                assert_eq!(rng.next_u64(), (e[1] << u32::BITS) | e[0]);
381            }
382        }
383    }
384
385    #[test]
386    fn fill_bytes() {
387        {
388            let mut rng = Sfc32::from_seed(Default::default());
389            let mut dst = [u8::default(); 64];
390            rng.fill_bytes(&mut dst);
391            assert_eq!(dst, EXPECTED_BYTES_1);
392        }
393        {
394            let mut rng = Sfc32::from_seed(SEED_2);
395            let mut dst = [u8::default(); 64];
396            rng.fill_bytes(&mut dst);
397            assert_eq!(dst, EXPECTED_BYTES_2);
398        }
399    }
400
401    #[test]
402    fn fill_bytes_per_chunk() {
403        {
404            let mut rng = Sfc32::from_seed(Default::default());
405            let mut dst = [u8::default(); 4];
406            for e in EXPECTED_BYTES_1.chunks_exact(dst.len()) {
407                rng.fill_bytes(&mut dst);
408                assert_eq!(dst, e);
409            }
410        }
411        {
412            let mut rng = Sfc32::from_seed(SEED_2);
413            let mut dst = [u8::default(); 4];
414            for e in EXPECTED_BYTES_2.chunks_exact(dst.len()) {
415                rng.fill_bytes(&mut dst);
416                assert_eq!(dst, e);
417            }
418        }
419    }
420
421    #[test]
422    fn seed_type() {
423        assert_eq!(
424            any::type_name::<<Sfc32 as SeedableRng>::Seed>(),
425            any::type_name::<[u8; 12]>()
426        );
427        assert_eq!(
428            mem::size_of::<<Sfc32 as SeedableRng>::Seed>(),
429            mem::size_of::<[u8; 12]>()
430        );
431    }
432
433    #[cfg(feature = "serde")]
434    #[test]
435    fn serde() {
436        let mut rng = Sfc32::from_seed(Default::default());
437
438        let json = serde_json::to_string(&rng).unwrap();
439        assert_eq!(
440            json,
441            r#"{"a":3033783054,"b":1182722562,"c":4269119441,"counter":16}"#
442        );
443
444        let mut deserialized_rng = serde_json::from_str::<Sfc32>(&json).unwrap();
445        assert_eq!(deserialized_rng, rng);
446        assert_eq!(deserialized_rng.next_u32(), rng.next_u32());
447    }
448}