use core::marker::PhantomData;
use crate::{
fbig::FBig,
repr::{Context, Repr, Word},
round::{mode, Round},
};
use dashu_base::EstimatedLog2;
use dashu_int::{
rand::{UniformBelow, UniformBits},
DoubleWord, UBig,
};
use rand_v08::{
distributions::{
uniform::{SampleBorrow, SampleUniform, UniformSampler},
Open01, OpenClosed01, Standard,
},
prelude::Distribution,
Rng,
};
pub struct UniformFBig<R: Round, const B: Word> {
sampler: Uniform01<B>,
scale: Repr<B>,
offset: Repr<B>,
_marker: PhantomData<R>,
}
impl<R: Round, const B: Word> UniformFBig<R, B> {
#[inline]
pub fn new(low: &FBig<R, B>, high: &FBig<R, B>, precision: usize) -> Self {
assert!(low <= high);
Self {
sampler: Uniform01::new(precision),
scale: (high - low).into_repr(),
offset: low.repr().clone(),
_marker: PhantomData,
}
}
#[inline]
pub fn new_inclusive(low: &FBig<R, B>, high: &FBig<R, B>, precision: usize) -> Self {
assert!(low <= high);
Self {
sampler: Uniform01::new_closed(precision),
scale: (high - low).into_repr(),
offset: low.repr().clone(),
_marker: PhantomData,
}
}
}
impl<R: Round, const B: Word> UniformSampler for UniformFBig<R, B> {
type X = FBig<R, B>;
#[inline]
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let precision = low.borrow().precision().max(high.borrow().precision());
UniformFBig::new(low.borrow(), high.borrow(), precision)
}
#[inline]
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let precision = low.borrow().precision().max(high.borrow().precision());
UniformFBig::new_inclusive(low.borrow(), high.borrow(), precision)
}
#[inline]
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> Self::X {
<Self as Distribution<FBig<R, B>>>::sample(self, rng)
}
}
impl<R: Round, const B: Word> Distribution<FBig<R, B>> for UniformFBig<R, B> {
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
let unit: FBig<mode::Down, B> = self.sampler.sample(rng);
let context = unit.context();
let scaled = context.mul(unit.repr(), &self.scale).value();
context
.add(scaled.repr(), &self.offset)
.value()
.with_rounding()
}
}
impl<R: Round, const B: Word> SampleUniform for FBig<R, B> {
type Sampler = UniformFBig<R, B>;
}
#[inline]
fn get_inline_precision<const B: Word>() -> usize {
(DoubleWord::BITS as f32 / B.log2_bounds().1) as _
}
impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Standard {
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
Uniform01::<B>::new(get_inline_precision::<B>()).sample(rng)
}
}
impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Open01 {
#[inline]
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
Uniform01::<B>::new_open(get_inline_precision::<B>()).sample(rng)
}
}
impl<R: Round, const B: Word> Distribution<FBig<R, B>> for OpenClosed01 {
#[inline]
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
Uniform01::<B>::new_open_closed(get_inline_precision::<B>()).sample(rng)
}
}
pub struct Uniform01<const BASE: Word> {
pub(crate) precision: usize,
range: Option<UBig>, include_zero: bool, include_one: bool, }
impl<const B: Word> Uniform01<B> {
#[inline]
pub fn new(precision: usize) -> Self {
let range = match B {
2 => None,
_ => Some(UBig::from_word(B).pow(precision)),
};
Self {
precision,
range,
include_zero: true,
include_one: false,
}
}
#[inline]
pub fn new_closed(precision: usize) -> Self {
let range = Some(UBig::from_word(B).pow(precision) + UBig::ONE);
Self {
precision,
range,
include_zero: true,
include_one: true,
}
}
#[inline]
pub fn new_open(precision: usize) -> Self {
let range = match B {
2 => None,
_ => Some(UBig::from_word(B).pow(precision) - UBig::ONE),
};
Self {
precision,
range,
include_zero: false,
include_one: false,
}
}
#[inline]
pub fn new_open_closed(precision: usize) -> Self {
let range = match B {
2 => None,
_ => Some(UBig::from_word(B).pow(precision)),
};
Self {
precision,
range,
include_zero: false,
include_one: true,
}
}
}
impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Uniform01<B> {
fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
let repr = match (self.include_zero, self.include_one) {
(true, false) => {
let signif: UBig = if B == 2 {
UniformBits::new(self.precision).sample(rng)
} else {
UniformBelow::new(self.range.as_ref().unwrap()).sample(rng)
};
Repr::<B>::new(signif.into(), -(self.precision as isize))
}
(true, true) => {
let signif: UBig = UniformBelow::new(self.range.as_ref().unwrap()).sample(rng);
Repr::new(signif.into(), -(self.precision as isize))
}
(false, false) => {
let signif = if B == 2 {
loop {
let n: UBig = UniformBits::new(self.precision).sample(rng);
if !n.is_zero() {
break n;
}
}
} else {
let n: UBig = UniformBelow::new(self.range.as_ref().unwrap()).sample(rng);
n + UBig::ONE
};
Repr::<B>::new(signif.into(), -(self.precision as isize))
}
(false, true) => {
let signif: UBig = if B == 2 {
UniformBits::new(self.precision).sample(rng)
} else {
UniformBelow::new(self.range.as_ref().unwrap()).sample(rng)
};
Repr::<B>::new((signif + UBig::ONE).into(), -(self.precision as isize))
}
};
let context = Context::<mode::Down>::new(self.precision);
FBig::new(repr, context).with_rounding()
}
}