tinymt/
tinymt64.rs

1use core::cmp::min;
2
3use crate::TinyMT64;
4
5const TINYMT64_MEXP: usize = 127;
6const TINYMT64_SH0: u64 = 12;
7const TINYMT64_SH1: u64 = 11;
8const TINYMT64_SH8: u64 = 8;
9const TINYMT64_MASK: u64 = 0x7fff_ffff_ffff_ffff_u64;
10const TINYMT64_MUL: f64 = 1.0 / 9_007_199_254_740_992.0;
11const MIN_LOOP: usize = 8;
12
13impl TinyMT64 {
14  pub fn new(status: [u64; 2], mat1: u32, mat2: u32, tmat: u64) -> TinyMT64 {
15    TinyMT64 { status, mat1, mat2, tmat }
16  }
17}
18
19/// This function represents a function used in the initialization by init_by_array.
20#[inline]
21fn ini_func1(x: u64) -> u64 {
22  (x ^ (x >> 59)).wrapping_mul(2_173_292_883_993_u64)
23}
24
25/// This function represents a function used in the initialization by init_by_array
26#[inline]
27fn ini_func2(x: u64) -> u64 {
28  (x ^ (x >> 59)).wrapping_mul(58_885_565_329_898_161_u64)
29}
30
31/// This function certificate the period of 2^127-1.
32#[inline]
33fn period_certification(random: &mut TinyMT64) {
34  if random.status[0] & TINYMT64_MASK == 0 && random.status[1] == 0 {
35    random.status[0] = 'T' as u64;
36    random.status[1] = 'M' as u64;
37  }
38}
39
40/// This function initializes the internal state array with a 64-bit unsigned integer seed.
41/// @param seed a 64-bit unsigned integer used as a seed.
42pub fn tinymt64_init(random: &mut TinyMT64, seed: u64) {
43  random.status[0] = seed ^ ((random.mat1 as u64) << 32);
44  random.status[1] = (random.mat2 as u64) ^ random.tmat;
45  for i in 1..MIN_LOOP {
46    random.status[i & 1] ^= (i as u64).wrapping_add(
47      6_364_136_223_846_793_005_u64
48        .wrapping_mul(random.status[(i - 1) & 1] ^ (random.status[(i - 1) & 1] >> 62)),
49    );
50  }
51  period_certification(random);
52}
53
54/// This function initializes the internal state array, with an array of 64-bit unsigned integers used as seeds
55/// @param init_key the array of 64-bit integers, used as a seed.
56/// @param key_length the length of init_key.
57pub fn tinymt64_init_by_array(random: &mut TinyMT64, init_key: &[u64]) {
58  let lag: usize = 1;
59  let mid: usize = 1;
60  let size: usize = 4;
61  let key_length: usize = init_key.len();
62
63  let mut st: [u64; 4] = [0, random.mat1 as u64, random.mat2 as u64, random.tmat];
64  let mut count: usize = if key_length + 1 > MIN_LOOP { key_length + 1 } else { MIN_LOOP };
65  let mut r: u64 = ini_func1(st[0] ^ st[mid % size] ^ st[(size - 1) % size]);
66  st[mid % size] += r;
67  r += key_length as u64;
68  st[(mid + lag) % size] += r;
69  st[0] = r;
70  count -= 1;
71  let mut i = 1;
72  let boundary = min(count, key_length);
73  for key in init_key.iter().take(boundary) {
74    r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
75    st[(i + mid) % size] = st[(i + mid) % size].wrapping_add(r);
76    r += key + i as u64;
77    st[(i + mid + lag) % size] = st[(i + mid + lag) % size].wrapping_add(r);
78    st[i] = r;
79    i = (i + 1) % size;
80  }
81  for _ in boundary..count {
82    r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]);
83    st[(i + mid) % size] = st[(i + mid) % size].wrapping_add(r);
84    r += i as u64;
85    st[(i + mid + lag) % size] = st[(i + mid + lag) % size].wrapping_add(r);
86    st[i] = r;
87    i = (i + 1) % size;
88  }
89  for _ in 0..size {
90    r = ini_func2(st[i].wrapping_add(st[(i + mid) % size]).wrapping_add(st[(i + size - 1) % size]));
91    st[(i + mid) % size] ^= r;
92    r -= i as u64;
93    st[(i + mid + lag) % size] ^= r;
94    st[i] = r;
95    i = (i + 1) % size;
96  }
97  random.status[0] = st[0] ^ st[1];
98  random.status[1] = st[2] ^ st[3];
99  period_certification(random);
100}
101
102/// This function always returns 127.
103#[inline]
104pub fn tinymt64_get_mexp(_: &TinyMT64) -> usize {
105  TINYMT64_MEXP
106}
107
108/**
109 * This function changes internal state of tinymt64. Users should not call this function directly.
110 * @param random tinymt internal status
111 */
112#[inline]
113pub fn tinymt64_next_state(random: &mut TinyMT64) {
114  random.status[0] &= TINYMT64_MASK;
115  let mut x: u64 = random.status[0] ^ random.status[1];
116  x ^= x << TINYMT64_SH0;
117  x ^= x >> 32;
118  x ^= x << 32;
119  x ^= x << TINYMT64_SH1;
120  random.status[0] = random.status[1];
121  random.status[1] = x;
122  random.status[0] ^= (-((x & 1) as i64) as u64) & (random.mat1 as u64);
123  random.status[1] ^= (-((x & 1) as i64) as u64) & ((random.mat2 as u64) << 32);
124}
125
126/// This function outputs 64-bit unsigned integer from internal state. Users should not call this function directly.
127/// @return 64-bit unsigned pseudorandom number
128#[inline]
129pub fn tinymt64_temper(random: &TinyMT64) -> u64 {
130  // defined(LINEARITY_CHECK)
131  // x = random->status[0] ^ random->status[1];
132  let mut x = random.status[0].wrapping_add(random.status[1]);
133  x ^= random.status[0] >> TINYMT64_SH8;
134  x ^ (-((x & 1) as i64) as u64) & random.tmat
135}
136
137/// This function outputs floating point number from internal state. Users should not call this function directly.
138/// @return floating point number r (1.0 <= r < 2.0)
139#[inline]
140pub fn tinymt64_temper_conv(random: &TinyMT64) -> f64 {
141  // defined(LINEARITY_CHECK)
142  // x = random->status[0] ^ random->status[1];
143  let mut x = random.status[0].wrapping_add(random.status[1]);
144  x ^= random.status[0] >> TINYMT64_SH8;
145  x = ((x ^ ((-((x & 1) as i64) as u64) & random.tmat)) >> 12) | 0x3ff0_0000_0000_0000_u64;
146  f64::from_le_bytes(x.to_le_bytes())
147}
148
149/// This function outputs floating point number from internal state. Users should not call this function directly.
150/// @return floating point number r (1.0 < r < 2.0)
151#[inline]
152pub fn tinymt64_temper_conv_open(random: &TinyMT64) -> f64 {
153  // defined(LINEARITY_CHECK)
154  // x = random->status[0] ^ random->status[1];
155  let mut x = random.status[0].wrapping_add(random.status[1]);
156  x ^= random.status[0] >> TINYMT64_SH8;
157  x = ((x ^ ((-((x & 1) as i64) as u64) & random.tmat)) >> 12) | 0x3ff0_0000_0000_0001_u64;
158  f64::from_le_bytes(x.to_le_bytes())
159}
160
161/// This function outputs 64-bit unsigned integer from internal state.
162/// @return 64-bit unsigned integer r (0 <= r < 2^64)
163#[inline]
164pub fn tinymt64_generate_uint64(random: &mut TinyMT64) -> u64 {
165  tinymt64_next_state(random);
166  tinymt64_temper(random)
167}
168
169/// This function outputs floating point number from internal state. This function is implemented
170/// using multiplying by (1 / 2^53).
171/// @return floating point number r (0.0 <= r < 1.0)
172#[inline]
173pub fn tinymt64_generate_double(random: &mut TinyMT64) -> f64 {
174  tinymt64_next_state(random);
175  ((tinymt64_temper(random) >> 11) as f64) * TINYMT64_MUL
176}
177
178/// This function outputs floating point number from internal state. This function is implemented
179/// using union trick.
180/// @return floating point number r (0.0 <= r < 1.0)
181#[inline]
182pub fn tinymt64_generate_double01(random: &mut TinyMT64) -> f64 {
183  tinymt64_next_state(random);
184  tinymt64_temper_conv(random) - 1.0
185}
186
187/// This function outputs floating point number from internal state. This function is implemented
188/// using union trick.
189/// @return floating point number r (1.0 <= r < 2.0)
190#[inline]
191pub fn tinymt64_generate_double12(random: &mut TinyMT64) -> f64 {
192  tinymt64_next_state(random);
193  tinymt64_temper_conv(random)
194}
195
196/// This function outputs floating point number from internal state. This function is implemented
197/// using union trick.
198/// @return floating point number r (0.0 < r <= 1.0)
199#[inline]
200pub fn tinymt64_generate_double_oc(random: &mut TinyMT64) -> f64 {
201  tinymt64_next_state(random);
202  2.0 - tinymt64_temper_conv(random)
203}
204
205/// This function outputs floating point number from internal state. This function is implemented
206/// using union trick.
207/// @return floating point number r (0.0 < r < 1.0)
208pub fn tinymt64_generate_double_oo(random: &mut TinyMT64) -> f64 {
209  tinymt64_next_state(random);
210  tinymt64_temper_conv_open(random) - 1.0
211}