1use core::marker::PhantomData;
44
45use crate::{
46 fbig::FBig,
47 repr::{Context, Repr, Word},
48 round::{mode, Round},
49};
50
51use dashu_base::EstimatedLog2;
52use dashu_int::{
53 rand::{UniformBelow, UniformBits},
54 DoubleWord, UBig,
55};
56use rand_v08::{
57 distributions::{
58 uniform::{SampleBorrow, SampleUniform, UniformSampler},
59 Open01, OpenClosed01, Standard,
60 },
61 prelude::Distribution,
62 Rng,
63};
64
65pub struct UniformFBig<R: Round, const B: Word> {
69 sampler: Uniform01<B>,
70
71 scale: Repr<B>,
72 offset: Repr<B>,
73
74 _marker: PhantomData<R>,
77}
78
79impl<R: Round, const B: Word> UniformFBig<R, B> {
80 #[inline]
83 pub fn new(low: &FBig<R, B>, high: &FBig<R, B>, precision: usize) -> Self {
84 assert!(low <= high);
85
86 Self {
87 sampler: Uniform01::new(precision),
88 scale: (high - low).into_repr(),
89 offset: low.repr().clone(),
90 _marker: PhantomData,
91 }
92 }
93
94 #[inline]
97 pub fn new_inclusive(low: &FBig<R, B>, high: &FBig<R, B>, precision: usize) -> Self {
98 assert!(low <= high);
99
100 Self {
101 sampler: Uniform01::new_closed(precision),
102 scale: (high - low).into_repr(),
103 offset: low.repr().clone(),
104 _marker: PhantomData,
105 }
106 }
107}
108
109impl<R: Round, const B: Word> UniformSampler for UniformFBig<R, B> {
110 type X = FBig<R, B>;
111
112 #[inline]
113 fn new<B1, B2>(low: B1, high: B2) -> Self
114 where
115 B1: SampleBorrow<Self::X> + Sized,
116 B2: SampleBorrow<Self::X> + Sized,
117 {
118 let precision = low.borrow().precision().max(high.borrow().precision());
119 UniformFBig::new(low.borrow(), high.borrow(), precision)
120 }
121
122 #[inline]
123 fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
124 where
125 B1: SampleBorrow<Self::X> + Sized,
126 B2: SampleBorrow<Self::X> + Sized,
127 {
128 let precision = low.borrow().precision().max(high.borrow().precision());
129 UniformFBig::new_inclusive(low.borrow(), high.borrow(), precision)
130 }
131
132 #[inline]
133 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> Self::X {
134 <Self as Distribution<FBig<R, B>>>::sample(self, rng)
135 }
136}
137
138impl<R: Round, const B: Word> Distribution<FBig<R, B>> for UniformFBig<R, B> {
139 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
140 let unit: FBig<mode::Down, B> = self.sampler.sample(rng);
143 let context = unit.context();
144 let scaled = context.mul(unit.repr(), &self.scale).value();
145 context
146 .add(scaled.repr(), &self.offset)
147 .value()
148 .with_rounding()
149 }
150}
151
152impl<R: Round, const B: Word> SampleUniform for FBig<R, B> {
153 type Sampler = UniformFBig<R, B>;
154}
155
156#[inline]
159fn get_inline_precision<const B: Word>() -> usize {
160 (DoubleWord::BITS as f32 / B.log2_bounds().1) as _
161}
162
163impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Standard {
164 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
165 Uniform01::<B>::new(get_inline_precision::<B>()).sample(rng)
166 }
167}
168
169impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Open01 {
170 #[inline]
171 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
172 Uniform01::<B>::new_open(get_inline_precision::<B>()).sample(rng)
173 }
174}
175
176impl<R: Round, const B: Word> Distribution<FBig<R, B>> for OpenClosed01 {
177 #[inline]
178 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
179 Uniform01::<B>::new_open_closed(get_inline_precision::<B>()).sample(rng)
180 }
181}
182
183pub struct Uniform01<const BASE: Word> {
187 pub(crate) precision: usize,
188 range: Option<UBig>, include_zero: bool, include_one: bool, }
192
193impl<const B: Word> Uniform01<B> {
194 #[inline]
196 pub fn new(precision: usize) -> Self {
197 let range = match B {
198 2 => None,
199 _ => Some(UBig::from_word(B).pow(precision)),
200 };
201 Self {
202 precision,
203 range,
204 include_zero: true,
205 include_one: false,
206 }
207 }
208
209 #[inline]
211 pub fn new_closed(precision: usize) -> Self {
212 let range = Some(UBig::from_word(B).pow(precision) + UBig::ONE);
213 Self {
214 precision,
215 range,
216 include_zero: true,
217 include_one: true,
218 }
219 }
220
221 #[inline]
223 pub fn new_open(precision: usize) -> Self {
224 let range = match B {
225 2 => None,
226 _ => Some(UBig::from_word(B).pow(precision) - UBig::ONE),
227 };
228 Self {
229 precision,
230 range,
231 include_zero: false,
232 include_one: false,
233 }
234 }
235
236 #[inline]
238 pub fn new_open_closed(precision: usize) -> Self {
239 let range = match B {
240 2 => None,
241 _ => Some(UBig::from_word(B).pow(precision)),
242 };
243 Self {
244 precision,
245 range,
246 include_zero: false,
247 include_one: true,
248 }
249 }
250}
251
252impl<R: Round, const B: Word> Distribution<FBig<R, B>> for Uniform01<B> {
253 fn sample<RNG: Rng + ?Sized>(&self, rng: &mut RNG) -> FBig<R, B> {
254 let repr = match (self.include_zero, self.include_one) {
255 (true, false) => {
256 let signif: UBig = if B == 2 {
258 UniformBits::new(self.precision).sample(rng)
259 } else {
260 UniformBelow::new(self.range.as_ref().unwrap()).sample(rng)
261 };
262 Repr::<B>::new(signif.into(), -(self.precision as isize))
263 }
264 (true, true) => {
265 let signif: UBig = UniformBelow::new(self.range.as_ref().unwrap()).sample(rng);
267 Repr::new(signif.into(), -(self.precision as isize))
268 }
269 (false, false) => {
270 let signif = if B == 2 {
272 loop {
273 let n: UBig = UniformBits::new(self.precision).sample(rng);
275 if !n.is_zero() {
276 break n;
277 }
278 }
279 } else {
280 let n: UBig = UniformBelow::new(self.range.as_ref().unwrap()).sample(rng);
281 n + UBig::ONE
282 };
283 Repr::<B>::new(signif.into(), -(self.precision as isize))
284 }
285 (false, true) => {
286 let signif: UBig = if B == 2 {
288 UniformBits::new(self.precision).sample(rng)
289 } else {
290 UniformBelow::new(self.range.as_ref().unwrap()).sample(rng)
291 };
292 Repr::<B>::new((signif + UBig::ONE).into(), -(self.precision as isize))
293 }
294 };
295
296 let context = Context::<mode::Down>::new(self.precision);
297 FBig::new(repr, context).with_rounding()
298 }
299}