use core::cmp::min;
use crate::TinyMT32;
const TINYMT32_MEXP: usize = 127;
const TINYMT32_SH0: u32 = 1;
const TINYMT32_SH1: u32 = 10;
const TINYMT32_SH8: u32 = 8;
const TINYMT32_MASK: u32 = 0x7fff_ffff_u32;
const TINYMT32_MUL: f64 = 1.0f64 / 16_777_216.0_f64;
const MIN_LOOP: usize = 8;
const PRE_LOOP: usize = 8;
impl TinyMT32 {
pub fn new(status: [u32; 4], mat1: u32, mat2: u32, tmat: u32) -> TinyMT32 {
TinyMT32 { status, mat1, mat2, tmat }
}
}
fn ini_func1(x: u32) -> u32 {
(x ^ (x >> 27)).wrapping_mul(1_664_525_u32)
}
fn ini_func2(x: u32) -> u32 {
(x ^ (x >> 27)).wrapping_mul(1_566_083_941_u32)
}
fn period_certification(random: &mut TinyMT32) {
if random.status[0] & TINYMT32_MASK == 0
&& random.status[1] == 0
&& random.status[2] == 0
&& random.status[3] == 0
{
random.status[0] = 'T' as u32;
random.status[1] = 'I' as u32;
random.status[2] = 'N' as u32;
random.status[3] = 'Y' as u32;
}
}
pub fn tinymt32_init(random: &mut TinyMT32, seed: u32) {
random.status[0] = seed;
random.status[1] = random.mat1;
random.status[2] = random.mat2;
random.status[3] = random.tmat;
for i in 1..MIN_LOOP {
random.status[i & 3] ^= (i as u32).wrapping_add(
1_812_433_253_u32
.wrapping_mul(random.status[(i - 1) & 3] ^ (random.status[(i - 1) & 3] >> 30)),
);
}
period_certification(random);
for _ in 0..PRE_LOOP {
tinymt32_next_state(random);
}
}
pub fn tinymt32_init_by_array(random: &mut TinyMT32, init_key: &[u32]) {
let key_length: usize = init_key.len();
let lag: usize = 1;
let mid: usize = 1;
let size: usize = 4;
let st: &mut [u32; 4] = &mut random.status;
st[0] = 0;
st[1] = random.mat1;
st[2] = random.mat2;
st[3] = random.tmat;
let mut count: usize = if key_length + 1 > MIN_LOOP { key_length + 1 } else { MIN_LOOP };
let mut r: u32 = ini_func1(st[0] ^ st[mid % size] ^ st[(size - 1) % size]);
st[mid % size] = st[mid % size].wrapping_add(r);
r += key_length as u32;
st[(mid + lag) % size] = st[(mid + lag) % size].wrapping_add(r);
st[0] = r;
count -= 1;
let mut i: usize = 1;
let boundary = min(count, key_length);
for key in init_key.iter().take(boundary) {
r = ini_func1(st[i % size] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
st[(i + mid) % size] = st[(i + mid) % size].wrapping_add(r);
r += key + i as u32;
st[(i + mid + lag) % size] = st[(i + mid + lag) % size].wrapping_add(r);
st[i % size] = r;
i = (i + 1) % size;
}
for _ in min(count, key_length)..count {
r = ini_func1(st[i % size] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
st[(i + mid) % size] = st[(i + mid) % size].wrapping_add(r);
r += i as u32;
st[(i + mid + lag) % size] = st[(i + mid + lag) % size].wrapping_add(r);
st[i % size] = r;
i = (i + 1) % size;
}
for _ in 0..size {
r = ini_func2(
st[i % size].wrapping_add(st[(i + mid) % size]).wrapping_add(st[(i + size - 1) % size]),
);
st[(i + mid) % size] ^= r;
r -= i as u32;
st[(i + mid + lag) % size] ^= r;
st[i % size] = r;
i = (i + 1) % size;
}
period_certification(random);
for _ in 0..PRE_LOOP {
tinymt32_next_state(random);
}
}
#[inline]
pub fn tinymt32_get_mexp(_: &TinyMT32) -> usize {
TINYMT32_MEXP
}
#[inline]
pub fn tinymt32_next_state(random: &mut TinyMT32) {
let mut y: u32 = random.status[3];
let mut x: u32 = (random.status[0] & TINYMT32_MASK) ^ random.status[1] ^ random.status[2];
x ^= x << TINYMT32_SH0;
y ^= (y >> TINYMT32_SH0) ^ x;
random.status[0] = random.status[1];
random.status[1] = random.status[2];
random.status[2] = x ^ (y << TINYMT32_SH1);
random.status[3] = y;
random.status[1] ^= (-((y & 1) as i32) as u32) & random.mat1;
random.status[2] ^= (-((y & 1) as i32) as u32) & random.mat2;
}
#[inline]
pub fn tinymt32_temper(random: &mut TinyMT32) -> u32 {
let mut t0: u32 = random.status[3];
let t1: u32 = random.status[0].wrapping_add(random.status[2] >> TINYMT32_SH8);
t0 ^= t1;
t0 ^ (-((t1 & 1) as i32) as u32) & random.tmat
}
#[inline]
pub fn tinymt32_temper_conv(random: &mut TinyMT32) -> f32 {
let mut t0: u32 = random.status[3];
let t1: u32 = random.status[0].wrapping_add(random.status[2] >> TINYMT32_SH8);
t0 ^= t1;
let u: u32 = ((t0 ^ ((-((t1 & 1) as i32) as u32) & random.tmat)) >> 9) | 0x3f80_0000_u32;
f32::from_le_bytes(u.to_le_bytes())
}
#[inline]
pub fn tinymt32_temper_conv_open(random: &mut TinyMT32) -> f32 {
let mut t0: u32 = random.status[3];
let t1: u32 = random.status[0].wrapping_add(random.status[2] >> TINYMT32_SH8);
t0 ^= t1;
let u: u32 = ((t0 ^ ((-((t1 & 1) as i32) as u32) & random.tmat)) >> 9) | 0x3f80_0001_u32;
f32::from_le_bytes(u.to_le_bytes())
}
#[inline]
pub fn tinymt32_generate_uint32(random: &mut TinyMT32) -> u32 {
tinymt32_next_state(random);
tinymt32_temper(random)
}
#[inline]
pub fn tinymt32_generate_float(random: &mut TinyMT32) -> f32 {
tinymt32_next_state(random);
((tinymt32_temper(random) >> 8) as f64 * TINYMT32_MUL) as f32
}
#[inline]
pub fn tinymt32_generate_float12(random: &mut TinyMT32) -> f32 {
tinymt32_next_state(random);
tinymt32_temper_conv(random)
}
#[inline]
pub fn tinymt32_generate_float01(random: &mut TinyMT32) -> f32 {
tinymt32_next_state(random);
tinymt32_temper_conv(random) - 1.0f32
}
#[inline]
pub fn tinymt32_generate_float_oc(random: &mut TinyMT32) -> f32 {
tinymt32_next_state(random);
1.0f32 - tinymt32_generate_float(random)
}
#[inline]
pub fn tinymt32_generate_float_oo(random: &mut TinyMT32) -> f32 {
tinymt32_next_state(random);
tinymt32_temper_conv_open(random) - 1.0f32
}
#[inline]
pub fn tinymt32_generate_32double(random: &mut TinyMT32) -> f64 {
tinymt32_next_state(random);
tinymt32_temper(random) as f64 * (1.0f64 / 4_294_967_296.0_f64)
}