1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//! Functions for [Linear Congruential //! Generators](https://en.wikipedia.org/wiki/Linear_congruential_generator). //! //! An LCG is just one multiply and one add, so it's very fast. However, they're //! not much in terms of statistical quality, so you need to be using the `u128` //! version if you want the kind of quality it takes to pass a PRNG test suite. //! Accordingly, the LCGs given here aren't actually supported with a struct to //! hold the state and all that. It's just the necessary math expression wrapped //! in a function. //! //! The Period of an LCG is equal to the numeric range of the state type used. //! //! * lcg8 is for `u8` and has a period of 2^8 //! * lcg16 is for `u16` and has a period of 2^16 //! * and so on //! //! # Constants //! //! As you might guess, different LCG multipliers give better or worse quality. //! There's apparently a thing called a [Spectral //! Test](https://en.wikipedia.org/wiki/Spectral_test) you can do to rate the //! quality of different LCG multipliers, but I don't know much about it. The //! `pcg` module has a good multiplier for each LCG size. //! //! Also, the additive value that you pick can affect the output stream as well. //! This allows some flexibility, because even with a single good multiplier //! selected you still have many output streams available to you. The main limit //! here is that the value must be odd, but that's quite a bit of freedom. //! Again, the `pcg` module offers some defaults if you'd like. //! //! There are also some LCG implementations here with pre-baked constants. //! They're based on the generators used in various games you might have played //! before. They are "just for fun", and not actually any higher quality than //! normal for an LCG of their size. //! //! # Jump Ahead //! //! It's possible to do some funny math and advance an LCG _as if_ you'd gone //! through N steps in only log(N) time. There is a jump function available for //! each size LCG offered. If you'd like to jump "backward" instead of forward //! that also works, since PRNG sequences are looped. Just cast a signed value //! into an unsigned value (eg: -5i32 as u32) and jump by that much. It has to //! go forward the "long way", but you arrive at the right value. use super::*; /// The `u8` LCG pub const fn lcg8(state: u8, mult: u8, inc: NonZeroU8) -> u8 { state.wrapping_mul(mult).wrapping_add(inc.get() | 1) } /// The `u16` LCG pub const fn lcg16(state: u16, mult: u16, inc: NonZeroU16) -> u16 { state.wrapping_mul(mult).wrapping_add(inc.get() | 1) } /// The `u32` LCG pub const fn lcg32(state: u32, mult: u32, inc: NonZeroU32) -> u32 { state.wrapping_mul(mult).wrapping_add(inc.get() | 1) } /// The `u64` LCG pub const fn lcg64(state: u64, mult: u64, inc: NonZeroU64) -> u64 { state.wrapping_mul(mult).wrapping_add(inc.get() | 1) } /// The `u128` LCG pub const fn lcg128(state: u128, mult: u128, inc: NonZeroU128) -> u128 { state.wrapping_mul(mult).wrapping_add(inc.get() | 1) } // /// The LCG from a game with "colosseum" in its name. pub const fn lcg32_colosseum(state: u32) -> u32 { lcg32(state, 0x000343FD, unsafe { NonZeroU32::new_unchecked(0x00269EC3) }) } /// The LCG from the gen3 and gen4 games. pub const fn lcg32_gen3gen4(state: u32) -> u32 { lcg32(state, 0x41C64E6D, unsafe { NonZeroU32::new_unchecked(0x00006073) }) } /// The alternate LCG from the gen4 games. pub const fn lcg32_gen4alt(state: u32) -> u32 { lcg32(state, 0x6C078965, unsafe { NonZeroU32::new_unchecked(0x00000001) }) } /// The LCG from the gen5 and gen6 games. pub const fn lcg64_gen5gen6(state: u64) -> u64 { lcg64(state, 0x5D588B656C078965, unsafe { NonZeroU64::new_unchecked(0x0000000000269EC3) }) } // macro_rules! make_jump_lcgX { ($(#[$attr:meta])* $f:ident, $u:ty, $nzu:ty) => { $(#[$attr])* pub fn $f(mut delta: $u, state: $u, mult: $u, inc: $nzu) -> $u { let mut cur_mult: $u = mult; let mut cur_plus: $u = inc.get(); let mut acc_mult: $u = 1; let mut acc_plus: $u = 0; while delta > 0 { if (delta & 1) > 0 { acc_mult = acc_mult.wrapping_mul(cur_mult); acc_plus = acc_plus.wrapping_mul(cur_mult).wrapping_add(cur_plus); } cur_plus = cur_mult.wrapping_add(1).wrapping_mul(cur_plus); cur_mult = cur_mult.wrapping_mul(cur_mult); delta /= 2; } acc_mult.wrapping_mul(state).wrapping_add(acc_plus) } }; } make_jump_lcgX! { /// Gives the `lcg8` output `delta` steps from now in log(delta) time. jump_lcg8, u8, NonZeroU8 } make_jump_lcgX! { /// Gives the `lcg16` output `delta` steps from now in log(delta) time. jump_lcg16, u16, NonZeroU16 } make_jump_lcgX! { /// Gives the `lcg32` output `delta` steps from now in log(delta) time. jump_lcg32, u32, NonZeroU32 } make_jump_lcgX! { /// Gives the `lcg64` output `delta` steps from now in log(delta) time. jump_lcg64, u64, NonZeroU64 } make_jump_lcgX! { /// Gives the `lcg128` output `delta` steps from now in log(delta) time. jump_lcg128, u128, NonZeroU128 }