use crate::{
arch::word::{DoubleWord, Word},
buffer::Buffer,
ibig::IBig,
math::ceil_div,
primitive::{DWORD_BITS_USIZE, WORD_BITS_USIZE},
repr::{Repr, TypedReprRef::*},
ubig::UBig,
};
use dashu_base::Sign;
pub trait BitRng {
fn next_word(&mut self) -> Word;
fn next_double_word(&mut self) -> DoubleWord;
fn next_bool(&mut self) -> bool;
fn fill_words(&mut self, words: &mut [Word]);
fn gen_word_inclusive(&mut self, high: Word) -> Word;
fn gen_dword_exclusive(&mut self, high: DoubleWord) -> DoubleWord;
}
pub struct UniformBits {
bits: usize,
}
impl UniformBits {
#[inline]
pub const fn new(bits: usize) -> Self {
UniformBits { bits }
}
pub fn sample_ubig<R: BitRng + ?Sized>(&self, rng: &mut R) -> UBig {
if self.bits == 0 {
UBig::ZERO
} else if self.bits <= DWORD_BITS_USIZE {
let dword: DoubleWord = rng.next_double_word();
UBig::from_dword(dword >> (DWORD_BITS_USIZE - self.bits))
} else {
let num_words = ceil_div(self.bits, WORD_BITS_USIZE);
let mut buffer = Buffer::allocate(num_words);
buffer.push_zeros(num_words);
rng.fill_words(buffer.as_mut());
let rem = self.bits % WORD_BITS_USIZE;
if rem != 0 {
*buffer.last_mut().unwrap() >>= WORD_BITS_USIZE - rem;
}
UBig(Repr::from_buffer(buffer))
}
}
pub fn sample_ibig<R: BitRng + ?Sized>(&self, rng: &mut R) -> IBig {
loop {
let mag: UBig = self.sample_ubig(rng);
let neg = rng.next_bool();
if mag.is_zero() && neg {
continue;
}
break IBig::from_parts(Sign::from(neg), mag);
}
}
}
pub struct UniformBelow<'a> {
limit: &'a UBig,
}
impl<'a> UniformBelow<'a> {
#[inline]
pub const fn new(limit: &'a UBig) -> Self {
Self { limit }
}
#[inline]
pub fn sample_ubig<R: BitRng + ?Sized>(&self, rng: &mut R) -> UBig {
uniform(self.limit, rng)
}
pub fn sample_ibig<R: BitRng + ?Sized>(&self, rng: &mut R) -> IBig {
loop {
let mag: UBig = uniform(self.limit, rng);
let neg = rng.next_bool();
if mag.is_zero() && neg {
continue;
}
break IBig::from_parts(Sign::from(neg), mag);
}
}
}
fn uniform<R: BitRng + ?Sized>(range: &UBig, rng: &mut R) -> UBig {
debug_assert!(!range.is_zero());
match range.repr() {
RefSmall(dword) => UBig::from(rng.gen_dword_exclusive(dword)),
RefLarge(words) => uniform_large(words, rng),
}
}
fn uniform_large<R: BitRng + ?Sized>(words: &[Word], rng: &mut R) -> UBig {
let mut buffer = Buffer::allocate(words.len());
buffer.push_zeros(words.len());
while !try_fill_uniform(words, rng, &mut buffer) {
}
UBig(Repr::from_buffer(buffer))
}
fn try_fill_uniform<R: BitRng + ?Sized>(words: &[Word], rng: &mut R, result: &mut [Word]) -> bool {
let n = words.len();
debug_assert!(n > 0 && result.len() == n);
let mut i = n - 1;
result[i] = rng.gen_word_inclusive(words[i]);
while result[i] == words[i] {
if i == 0 {
return false;
}
i -= 1;
result[i] = rng.next_word();
if result[i] > words[i] {
return false;
}
}
rng.fill_words(&mut result[..i]);
true
}
pub struct UniformUBig {
pub(crate) range: UBig,
pub(crate) offset: UBig,
}
impl UniformUBig {
#[inline]
pub(crate) const fn from_parts(range: UBig, offset: UBig) -> Self {
UniformUBig { range, offset }
}
#[inline]
pub fn sample<R: BitRng + ?Sized>(&self, rng: &mut R) -> UBig {
uniform(&self.range, rng) + &self.offset
}
}
pub struct UniformIBig {
pub(crate) range: UBig,
pub(crate) offset: IBig,
}
impl UniformIBig {
#[inline]
pub(crate) const fn from_parts(range: UBig, offset: IBig) -> Self {
UniformIBig { range, offset }
}
#[inline]
pub fn sample<R: BitRng + ?Sized>(&self, rng: &mut R) -> IBig {
IBig::from(uniform(&self.range, rng)) + &self.offset
}
}
#[cfg(feature = "rand_v08")]
pub fn bridge_v08<'a, R: rand_v08::Rng + ?Sized>(rng: &'a mut R) -> impl BitRng + 'a {
super::rand_v08::RngBridge(rng)
}
#[cfg(feature = "rand_v09")]
pub fn bridge_v09<'a, R: rand_v09::Rng + ?Sized>(rng: &'a mut R) -> impl BitRng + 'a {
super::rand_v09::RngBridge(rng)
}
#[cfg(feature = "rand_v010")]
pub fn bridge_v010<'a, R: rand_v010::Rng + ?Sized>(rng: &'a mut R) -> impl BitRng + 'a {
super::rand_v010::RngBridge(rng)
}