use rand::distr::{Distribution, StandardUniform};
use rand::Rng;
use super::{BigInt, BigUint};
use oxinum_core::Sign;
impl BigUint {
pub fn random_bits<R: Rng + ?Sized>(rng: &mut R, n_bits: u64) -> Self {
if n_bits == 0 {
return BigUint::zero();
}
let n_limbs = n_bits.div_ceil(64);
let mut limbs = vec![0u64; n_limbs as usize];
for limb in limbs.iter_mut() {
*limb = rng.next_u64();
}
let top_bits = n_bits % 64;
if top_bits != 0 {
let mask = (1u64 << top_bits).wrapping_sub(1);
if let Some(last) = limbs.last_mut() {
*last &= mask;
}
}
BigUint::from_le_limbs(&limbs)
}
pub fn random_in_range<R: Rng + ?Sized>(rng: &mut R, low: &BigUint, high: &BigUint) -> Self {
assert!(
high > low,
"random_in_range: high must be strictly greater than low"
);
let range = high
.checked_sub(low)
.expect("random_in_range: high > low guarantees non-underflow");
let n_bits = range.bit_length();
loop {
let candidate = BigUint::random_bits(rng, n_bits);
if candidate < range {
return low.clone() + candidate;
}
}
}
}
impl BigInt {
pub fn random_in_range<R: Rng + ?Sized>(rng: &mut R, low: &BigInt, high: &BigInt) -> Self {
assert!(
high > low,
"random_in_range: high must be strictly greater than low"
);
let diff = high.clone() - low.clone();
let (_, range) = diff.into_parts();
let n_bits = range.bit_length();
loop {
let offset = BigUint::random_bits(rng, n_bits);
if offset < range {
let offset_int = BigInt::from_parts(Sign::Positive, offset);
return low.clone() + offset_int;
}
}
}
}
pub struct BigUintBits(pub u64);
impl Distribution<BigUint> for BigUintBits {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> BigUint {
BigUint::random_bits(rng, self.0)
}
}
impl Distribution<BigUint> for StandardUniform {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> BigUint {
BigUint::random_bits(rng, 256)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
#[test]
fn random_bits_zero_bits_is_zero() {
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let v = BigUint::random_bits(&mut rng, 0);
assert!(v.is_zero());
}
#[test]
fn random_bits_single_limb_bounded() {
let mut rng = rand::rngs::StdRng::seed_from_u64(1);
for _ in 0..200 {
let v = BigUint::random_bits(&mut rng, 10);
assert!(v.bit_length() <= 10);
}
}
#[test]
fn random_bits_multi_limb_bounded() {
let mut rng = rand::rngs::StdRng::seed_from_u64(2);
for _ in 0..100 {
let v = BigUint::random_bits(&mut rng, 130);
assert!(v.bit_length() <= 130);
}
}
#[test]
fn random_in_range_basic() {
let mut rng = rand::rngs::StdRng::seed_from_u64(3);
let low = BigUint::from(0u64);
let high = BigUint::from(100u64);
for _ in 0..500 {
let v = BigUint::random_in_range(&mut rng, &low, &high);
assert!(v < high);
}
}
#[test]
fn bigint_range_contains_negatives_and_positives() {
let mut rng = rand::rngs::StdRng::seed_from_u64(4);
let low = BigInt::from(-50i64);
let high = BigInt::from(50i64);
let mut saw_neg = false;
let mut saw_pos = false;
for _ in 0..500 {
let v = BigInt::random_in_range(&mut rng, &low, &high);
if v.is_negative() {
saw_neg = true;
}
if v.is_positive() {
saw_pos = true;
}
}
assert!(saw_neg);
assert!(saw_pos);
}
#[test]
fn distribution_big_uint_bits() {
use rand::distr::Distribution;
let mut rng = rand::rngs::StdRng::seed_from_u64(5);
let dist = BigUintBits(64);
for _ in 0..100 {
let v: BigUint = dist.sample(&mut rng);
assert!(v.bit_length() <= 64);
}
}
#[test]
fn distribution_standard_uniform() {
use rand::distr::Distribution;
let mut rng = rand::rngs::StdRng::seed_from_u64(6);
for _ in 0..50 {
let v: BigUint = StandardUniform.sample(&mut rng);
assert!(v.bit_length() <= 256);
}
}
}