devela/num/rand/xorshift/
mod.rs

1// devela::num::rand::xorshift
2//
3//! Pseudo-random number generators based on [Xorshift].
4//!
5//! This module defines several types:
6//! - classic *XorShift* algorithms:
7//!   ([`XorShift32`], [`XorShift64`], [`XorShift128`], [`XorShift128p`]).
8//! - variations with a smaller state:
9//!   ([`XorShift16`], [`XorShift8`]).
10//!
11//! - Original paper: <https://www.jstatsoft.org/article/view/v008i14>
12//!
13//! [Xorshift]: https://en.wikipedia.org/wiki/Xorshift
14//
15
16mod u128p;
17pub use u128p::XorShift128p; // (canonical)
18
19#[cfg(feature = "rand")]
20crate::items! {
21    mod u128; // (11, 8,19)   (canonical)
22    mod u16;  // ( 7, 9, 8)
23    mod u32;  // ( 5,17,13)   (customizable, canonical default)
24    mod u64;  // (13, 7,17)   (customizable, canonical default)
25    mod u8;   // ( 3, 4, 2)   (customizable)
26    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "rand")))]
27    pub use {
28        u128::XorShift128,
29        u16::XorShift16,
30        u32::XorShift32,
31        u64::XorShift64,
32        u8::XorShift8,
33    };
34
35    #[doc(hidden)]
36    pub use {u16::XOROSHIFT_16_TRIPLETS, u32::XOROSHIFT_32_TRIPLETS, u64::XOROSHIFT_64_TRIPLETS};
37
38    /// Constructs a *XorShift* prng with custom bits, basis, triplet and seed.
39    ///
40    /// It can construct custom instances of [`XorShift16`], [`XorShift32`] and [`XorShift64`].
41    ///
42    /// The given `$triplet` is an index for an array of good triples with a maximum of:
43    /// - 3 for 16-bit,
44    /// - 80 for 32-bit
45    /// - 274 for 64-bit
46    ///
47    /// ## Usage:
48    /// `xorshift_with![bits: 32, basis: 1, triplet: 40, seed: 5334];`
49    ///
50    /// Valid argument values:
51    /// - `$bits`:    `16`, `32` or `64`.
52    /// - `$basis`:   in range `0..=7`.
53    /// - `$triplet`: `0..=3` for 16-bit; `0..=80` for 32-bit; `0..=274` for 64-bit.
54    /// - `$seed`:    any value. If `0` is given the default seed will be used.
55    ///
56    /// # Panics
57    /// If the `$basis` is outside range `0..=7`.
58    //
59    // The reason for this macro is that is not possible to operate with const-generics,
60    // so we can't make a method using an INDEX const-generic to index in a const array.
61    // Also: Inner items do not inherit the generic parameters from the items they are embedded in.
62    // WAIT: [generic_const_expr](https://github.com/rust-lang/rust/issues/76560)
63    #[doc(hidden)]
64    #[macro_export]
65    #[rustfmt::skip]
66    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "rand")))]
67    macro_rules! _xorshift_custom {
68        (bits:$bits:literal, basis:$basis:expr, triplet:$triplet:expr, seed:$seed:expr) => {{
69            $crate::paste! {
70                const T: (u8, u8, u8) = $crate::[<XOROSHIFT_ $bits _TRIPLETS>][{ $triplet }];
71                    $crate::[<XorShift $bits>]
72                        ::<{$basis}, {T.0 as usize}, {T.1 as usize}, {T.2 as usize}>
73                        ::new($seed)
74            }
75        }};
76    }
77    #[doc(inline)]
78    pub use _xorshift_custom as xorshift_custom;
79
80    /// Generates a XORSHIFT sequence using the given operation basis and shift triplet.
81    ///
82    /// # Usage:
83    /// `xorshift_basis![<basis>: (<a>, <b>, <c>) <state>];`
84    ///
85    /// Notes
86    /// - The `state` has to be different from 0.
87    /// - The `basis` has to be a value between 0 and 7.
88    /// - The `(a, b, c)` triplet has to be a valid one for its bit-size.
89    /// - When `basis` is a constant the macro is optimized away and has 0 overhead.
90    ///
91    /// # Panics
92    /// If the basis is outside range `0..=7`.
93    //
94    // - The canonical 32-bit example uses triplet  #40:( 5,17,13) with basis 1.
95    // - The canonical 64-bit example uses triplet #155:(13, 7,17) with basis 0.
96    macro_rules! xorshift_basis {
97        [$state:ident, $basis:expr, ($a:expr, $b:expr, $c:expr)] => {
98            match $basis {
99                0 => { $state^=$state << $a; $state^=$state >> $b; $state^=$state << $c; },
100                1 => { $state^=$state << $c; $state^=$state >> $b; $state^=$state << $a; },
101                2 => { $state^=$state >> $a; $state^=$state << $b; $state^=$state >> $c; },
102                3 => { $state^=$state >> $c; $state^=$state << $b; $state^=$state >> $a; },
103                4 => { $state^=$state << $a; $state^=$state << $c; $state^=$state >> $b; },
104                5 => { $state^=$state << $c; $state^=$state << $a; $state^=$state >> $b; },
105                6 => { $state^=$state >> $a; $state^=$state >> $c; $state^=$state << $b; },
106                7 => { $state^=$state >> $c; $state^=$state >> $a; $state^=$state << $b; },
107                _ => panic!("Error: xorshift $basis must be between 0..=7"),
108            }
109        };
110    }
111    pub(crate) use xorshift_basis;
112}