use rand::Rng;
use primes::is_prime;
use num::Integer;
fn first_prime_above(mut value: u64) -> u64 {
while !is_prime(value) {
value += 1;
}
value
}
pub struct RandomSequence {
curr: u64,
modulus: u64,
offset: u64,
range_size: u64,
a: u64,
c: u64,
num_generated: u64,
}
impl RandomSequence {
fn new(start: u64, end: u64) -> Self {
assert!(end >= start);
let length = end - start;
let seed: u64 = rand::thread_rng().gen();
let m = first_prime_above(length);
let a: u64 = (m * 2) + 1;
let c: u64 = rand::thread_rng().gen();
let mut c = c % m;
if c == 0 {
c += 1;
}
assert!(is_prime(m));
assert_ne!(c, 0);
assert!(m.gcd(&c) == 1 && c.gcd(&m) == 1);
assert_eq!((a - 1) % m, 0);
if m % 4 == 0 {
assert_eq!((a - 1) % 4, 0);
}
Self {
curr: seed % m,
modulus: m,
offset: start,
range_size: length,
a,
c,
num_generated: 0,
}
}
}
impl Iterator for RandomSequence {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if self.num_generated > self.range_size {
return None
}
loop {
self.curr = ((self.a as u128 * self.curr as u128 + self.c as u128) % self.modulus as u128) as u64;
if self.curr <= self.range_size {
self.num_generated += 1;
break;
}
}
Some(self.curr + self.offset)
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::collections::HashMap;
use crate::RandomSequence;
#[test]
fn test() {
let start = 1000;
let end = 100000;
let mut seen = HashMap::new();
for i in RandomSequence::new(start, end) {
*seen.entry(i).or_insert(0) += 1;
}
for i in start..end {
assert_eq!(seen.get(&i), Some(&1));
}
}
}