use std::cmp::Ordering;
pub const FRACTION_ONE: i32 = 1 << 28;
pub const FRACTION_HALF: i32 = 1 << 27;
pub const FRACTION_FOUR: i32 = 1 << 30;
pub const EL_GORDO: i32 = i32::MAX;
fn clamp(x: i64) -> i32 {
x.clamp(-i64::from(EL_GORDO), i64::from(EL_GORDO)) as i32
}
pub fn make_fraction(p: i32, q: i32) -> i32 {
assert!(q != 0, "make_fraction: division by zero");
let negative = (p < 0) != (q < 0);
let (p, q) = (i64::from(p).abs(), i64::from(q).abs());
if p / q >= 8 {
return if negative { -EL_GORDO } else { EL_GORDO };
}
let f = ((p << 29) + q) / (2 * q);
clamp(if negative { -f } else { f })
}
pub fn take_fraction(q: i32, f: i32) -> i32 {
clamp(take_fraction_wide(i64::from(q), i64::from(f)))
}
fn take_fraction_wide(q: i64, f: i64) -> i64 {
let negative = (q < 0) != (f < 0);
let p = (q.abs() * f.abs() + i64::from(FRACTION_HALF)) >> 28;
if negative { -p } else { p }
}
const SPEC_LOG: [i32; 29] = {
let mut t = [0; 29];
let head = [
93032640, 38612034, 17922280, 8662214, 4261238, 2113709, 1052693, 525315, 262400, 131136,
65552, 32772, 16385,
];
let mut k = 1;
while k <= 13 {
t[k] = head[k - 1];
k += 1;
}
while k <= 27 {
t[k] = 1 << (27 - k);
k += 1;
}
t[28] = 1;
t
};
pub fn m_log(mut x: i32) -> i32 {
if x <= 0 {
return 0;
}
let mut y: i32 = 1_302_456_956 + 4 - 100;
let mut z: i32 = 27595 + 6_553_600;
while x < FRACTION_FOUR {
x += x;
y -= 93_032_639;
z -= 48782;
}
y += z / crate::UNITY;
let mut k = 2;
while x > FRACTION_FOUR + 4 {
let mut z = (x - 1) / (1 << k) + 1;
while x < FRACTION_FOUR + z {
z = (z + 1) / 2;
k += 1;
}
y += SPEC_LOG[k];
x -= z;
}
y / 8
}
pub fn ab_vs_cd(a: i32, b: i32, c: i32, d: i32) -> Ordering {
(i64::from(a) * i64::from(b)).cmp(&(i64::from(c) * i64::from(d)))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Randoms {
randoms: [i32; 55],
j: usize,
}
impl Randoms {
pub fn new(seed: i32) -> Self {
let mut r = Randoms {
randoms: [0; 55],
j: 0,
};
r.init(seed);
r
}
pub fn init(&mut self, seed: i32) {
let mut j = i64::from(seed).abs();
while j >= i64::from(FRACTION_ONE) {
j /= 2;
}
let mut j = j as i32;
let mut k: i32 = 1;
for i in 0..55 {
let jj = k;
k = j - k;
j = jj;
if k < 0 {
k += FRACTION_ONE;
}
self.randoms[(i * 21) % 55] = j;
}
self.new_randoms();
self.new_randoms();
self.new_randoms();
}
fn new_randoms(&mut self) {
let r = &mut self.randoms;
for k in 0..55 {
let mut x = r[k] - r[if k < 24 { k + 31 } else { k - 24 }];
if x < 0 {
x += FRACTION_ONE;
}
r[k] = x;
}
self.j = 54;
}
pub fn next_fraction(&mut self) -> i32 {
if self.j == 0 {
self.new_randoms();
} else {
self.j -= 1;
}
self.randoms[self.j]
}
pub fn unif_rand(&mut self, x: i32) -> i32 {
let f = self.next_fraction();
let ax = i64::from(x).abs();
let y = take_fraction_wide(ax, i64::from(f));
if y == ax {
0
} else if x > 0 {
y as i32
} else {
(-y) as i32
}
}
pub fn norm_rand(&mut self) -> i32 {
loop {
let (x, u) = loop {
let x = take_fraction(112_429, self.next_fraction() - FRACTION_HALF);
let u = self.next_fraction();
if x.abs() < u {
break (x, u);
}
};
let x = make_fraction(x, u);
let l = 139_548_960 - m_log(u);
if ab_vs_cd(1024, l, x, x) != Ordering::Less {
return x;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn uniform(seed: i32, x: i32, n: usize) -> Vec<i32> {
let mut r = Randoms::new(seed);
(0..n).map(|_| r.unif_rand(x)).collect()
}
fn normal(seed: i32, n: usize) -> Vec<i32> {
let mut r = Randoms::new(seed);
(0..n).map(|_| r.norm_rand()).collect()
}
#[test]
fn uniform_seed_42() {
assert_eq!(
uniform(42, 1000, 30),
[
788, 356, 360, 48, 154, 905, 286, 377, 754, 921, 893, 58, 813, 784, 283, 776, 46,
483, 570, 839, 427, 743, 537, 577, 15, 614, 88, 711, 425, 755
]
);
assert_eq!(
uniform(42, -1000, 10),
[-788, -356, -360, -48, -154, -905, -286, -377, -754, -921]
);
assert_eq!(
uniform(42, 6, 30),
[
5, 2, 2, 0, 1, 5, 2, 2, 5, 0, 5, 0, 5, 5, 2, 5, 0, 3, 3, 5, 3, 4, 3, 3, 0, 4, 1, 4,
3, 5
]
);
assert_eq!(uniform(42, 1, 10), [0; 10]);
assert_eq!(uniform(42, 0, 3), [0; 3]);
assert_eq!(
uniform(42, EL_GORDO, 5),
[1691999567, 764279616, 772600120, 103650880, 330031416]
);
assert_eq!(
uniform(42, -EL_GORDO, 5),
[-1691999567, -764279616, -772600120, -103650880, -330031416]
);
}
#[test]
fn uniform_other_seeds() {
assert_eq!(uniform(-42, 1000, 3), [788, 356, 360]);
assert_eq!(uniform(43, 1000, 3), [253, 728, 550]);
assert_eq!(uniform(0, 1000, 5), [241, 724, 352, 544, 553]);
assert_eq!(uniform(1, 1000, 5), [706, 96, 542, 580, 544]);
assert_eq!(uniform(2, 1000, 5), [172, 468, 733, 615, 534]);
assert_eq!(uniform(FRACTION_ONE + 5, 1000, 3), [672, 968, 733]);
assert_eq!(uniform(FRACTION_ONE, 1000, 1), [741]);
assert_eq!(uniform(EL_GORDO, 1000, 5), [775, 352, 161, 508, 563]);
assert_eq!(uniform(-EL_GORDO, 1000, 1), [775]);
assert_eq!(uniform(720_667_377, 1_000_000, 1), [565328]);
assert_eq!(
uniform(123_456_789, 1_000_000_007, 60),
[
339280105, 694342525, 33406381, 663534847, 31468977, 920872412, 890775731,
763103345, 352619304, 970706429, 556395658, 661018212, 990435626, 129798145,
863619408, 619866, 169370619, 823904878, 327600645, 795750054, 24217479, 720953581,
705383831, 74538809, 206004822, 709324257, 411170676, 944563850, 472524974,
992473296, 501359921, 564391203, 182562610, 884105465, 115442634, 164019470,
3255386, 306177771, 471044626, 532215923, 638790694, 964107289, 74024737,
964409153, 666308233, 83044526, 4268754, 977702509, 471425347, 884187835,
276770061, 37269928, 402504205, 659495034, 479703087, 780795758, 541605458,
103255720, 732023453, 240494633
]
);
}
#[test]
fn uniform_raw_fractions() {
let got = uniform(1234, FRACTION_ONE, 200);
let mut r = Randoms::new(1234);
let raw: Vec<i32> = (0..200).map(|_| r.next_fraction()).collect();
assert_eq!(got, raw);
assert_eq!(
got[..12],
[
148933938, 868208, 172117591, 200264536, 217942135, 6639995, 144635461, 178301072,
146807680, 191625536, 108818557, 117198514
]
);
assert_eq!(
got[190..],
[
254766369, 19665803, 176208551, 174348460, 140378967, 3606447, 115579941, 15397593,
34731790, 30432757
]
);
}
#[test]
fn normal_sequences() {
assert_eq!(
normal(42, 24),
[
90948, -63802, 31008, 44963, -31499, -105635, 9407, -11063, 7160, -88755, -65191,
-11236, 42221, -119113, -145102, -121987, -103122, 112162, 117308, 18788, -106915,
4377, -88893, -24580
]
);
assert_eq!(
normal(7, 40),
[
-477, 26353, -2376, 34964, 138853, -737, -63343, 19393, 16883, -40185, -48533,
40548, -16297, 4018, -63948, 32782, -10939, 19134, -112276, 29490, -45353, 41991,
60635, 97826, -31185, -6217, -46115, -18400, -47706, -5467, 6379, -12410, -38074,
-37842, 44554, -97469, 27547, -36117, 42533, -25108
]
);
}
#[test]
fn normal_after_uniforms() {
let mut r = Randoms::new(42);
let u: Vec<i32> = (0..3).map(|_| r.unif_rand(1000)).collect();
assert_eq!(u, [788, 356, 360]);
assert_eq!([r.norm_rand(), r.norm_rand()], [-18408, 112861]);
assert_eq!(r.unif_rand(-1000), -776);
let mut r = Randoms::new(42);
assert_eq!(
[r.unif_rand(1000), r.norm_rand(), r.unif_rand(1000)],
[788, -45034, 48]
);
}
#[test]
fn fraction_arithmetic() {
assert_eq!(make_fraction(1, 2), FRACTION_HALF);
assert_eq!(make_fraction(-1, 3), -89478485);
assert_eq!(make_fraction(1, -3), -89478485);
assert_eq!(make_fraction(8, 1), EL_GORDO);
assert_eq!(make_fraction(-9, 1), -EL_GORDO);
assert_eq!(take_fraction(1000, FRACTION_HALF), 500);
assert_eq!(take_fraction(3, FRACTION_HALF), 2);
assert_eq!(take_fraction(-3, FRACTION_HALF), -2);
assert_eq!(take_fraction(EL_GORDO, 4 * FRACTION_ONE), EL_GORDO);
assert_eq!(ab_vs_cd(2, 3, 1, 6), Ordering::Equal);
assert_eq!(ab_vs_cd(-2, 3, 1, 6), Ordering::Less);
assert_eq!(m_log(crate::UNITY), 0);
assert_eq!(m_log(2 * crate::UNITY), 11629080);
assert_eq!(m_log(0), 0);
}
}