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}