use crate::{rbig::RBig, repr::Repr, Relaxed};
use dashu_base::Gcd;
use dashu_int::{
rand::{BitRng, UniformBelow, UniformIBig},
IBig, UBig,
};
pub struct Uniform01<'a> {
pub(crate) limit: &'a UBig,
pub(crate) include_zero: bool,
pub(crate) include_one: bool,
}
impl<'a> Uniform01<'a> {
#[inline]
pub fn new(limit: &'a UBig) -> Self {
Self {
limit,
include_zero: true,
include_one: false,
}
}
#[inline]
pub fn new_closed(limit: &'a UBig) -> Self {
Self {
limit,
include_zero: true,
include_one: true,
}
}
#[inline]
pub fn new_open(limit: &'a UBig) -> Self {
Self {
limit,
include_zero: false,
include_one: false,
}
}
#[inline]
pub fn new_open_closed(limit: &'a UBig) -> Self {
Self {
limit,
include_zero: false,
include_one: true,
}
}
pub fn sample_repr<BR: BitRng + ?Sized>(&self, rng: &mut BR) -> Repr {
match (self.include_zero, self.include_one) {
(true, false) => {
let num: UBig = UniformBelow::new(self.limit).sample_ubig(rng);
Repr {
numerator: num.into(),
denominator: self.limit.clone(),
}
}
(true, true) => {
let num: UBig = UniformBelow::new(self.limit).sample_ubig(rng);
Repr {
numerator: num.into(),
denominator: self.limit.clone() - UBig::ONE,
}
}
(false, false) => {
let num = loop {
let n: UBig = UniformBelow::new(self.limit).sample_ubig(rng);
if !n.is_zero() {
break n;
}
};
Repr {
numerator: num.into(),
denominator: self.limit.clone(),
}
}
(false, true) => {
let num: UBig = UniformBelow::new(self.limit).sample_ubig(rng);
Repr {
numerator: (num + UBig::ONE).into(),
denominator: self.limit.clone(),
}
}
}
}
#[inline]
pub fn sample_rbig<BR: BitRng + ?Sized>(&self, rng: &mut BR) -> RBig {
RBig(self.sample_repr(rng).reduce())
}
#[inline]
pub fn sample_relaxed<BR: BitRng + ?Sized>(&self, rng: &mut BR) -> Relaxed {
Relaxed(self.sample_repr(rng).reduce2())
}
}
pub struct UniformRBig {
pub(crate) num_sampler: UniformIBig,
pub(crate) den: UBig,
}
impl UniformRBig {
pub(crate) fn parse_bounds(low: &RBig, high: &RBig) -> (IBig, IBig, UBig) {
let (low_d, high_d) = (low.denominator(), high.denominator());
let g = low_d.gcd(high_d);
let low_n = high_d / &g * low.numerator();
let high_n = low_d / &g * high.numerator();
let den = low_d / g * high_d;
(low_n, high_n, den)
}
#[inline]
pub(crate) fn from_parts(num_sampler: UniformIBig, den: UBig) -> Self {
UniformRBig { num_sampler, den }
}
#[inline]
pub fn sample_rbig<BR: BitRng + ?Sized>(&self, rng: &mut BR) -> RBig {
RBig::from_parts(self.num_sampler.sample(rng), self.den.clone())
}
}