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
}