use astro_float::{Consts, RoundingMode};
pub(super) const RM: RoundingMode = RoundingMode::ToEven;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(super) struct Prec(u32);
impl Prec {
pub(super) const MAX: Prec = Prec(u32::MAX);
pub(super) fn new(bits: usize) -> Prec {
Prec(u32::try_from(bits).unwrap_or(u32::MAX))
}
pub(super) fn bits(self) -> usize {
self.0 as usize
}
pub(super) fn plus(self, extra: usize) -> Prec {
Prec::new(self.bits().saturating_add(extra))
}
pub(super) fn guarded(self, g: Guard, cap: Prec) -> Option<Prec> {
match g {
Guard::Bits(k) => {
let p = self.0.checked_add(k)?;
(p <= cap.0).then_some(Prec(p))
}
Guard::Unbounded => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Guard {
Bits(u32),
Unbounded,
}
impl Guard {
pub(super) fn for_log2(x: f64) -> Guard {
if x <= 0.0 {
return Guard::Bits(0);
}
crate::base::cast::f64_to_i64(x.ceil())
.and_then(|k| u32::try_from(k).ok())
.map_or(Guard::Unbounded, Guard::Bits)
}
}
pub(super) struct Wp<'a> {
prec: Prec,
pub(super) cc: &'a mut Consts,
}
impl<'a> Wp<'a> {
pub(super) fn new(bits: usize, cc: &'a mut Consts) -> Wp<'a> {
Wp {
prec: Prec::new(bits),
cc,
}
}
pub(super) fn bits(&self) -> usize {
self.prec.bits()
}
pub(super) fn at(&mut self, bits: usize) -> Wp<'_> {
Wp {
prec: Prec::new(bits),
cc: self.cc,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prec_saturates_instead_of_wrapping() {
assert_eq!(Prec::new(128).plus(32).bits(), 160);
assert_eq!(Prec::new(usize::MAX), Prec::MAX);
assert_eq!(Prec::MAX.plus(1), Prec::MAX);
}
#[test]
fn guards_from_f64_estimates() {
assert_eq!(Guard::for_log2(10.2), Guard::Bits(11));
assert_eq!(Guard::for_log2(-3.0), Guard::Bits(0));
assert_eq!(Guard::for_log2(f64::NEG_INFINITY), Guard::Bits(0));
assert_eq!(Guard::for_log2(f64::NAN), Guard::Unbounded);
assert_eq!(Guard::for_log2(f64::INFINITY), Guard::Unbounded);
assert_eq!(Guard::for_log2(1e300), Guard::Unbounded);
let cap = Prec::new(1000);
assert_eq!(
Prec::new(900).guarded(Guard::Bits(100), cap),
Some(Prec::new(1000))
);
assert_eq!(Prec::new(900).guarded(Guard::Bits(101), cap), None);
assert_eq!(Prec::new(900).guarded(Guard::Unbounded, cap), None);
assert_eq!(Prec::MAX.guarded(Guard::Bits(1), Prec::MAX), None);
}
#[test]
fn wp_reborrows_the_cache_at_another_precision() {
let Ok(mut cc) = Consts::new() else {
return;
};
let mut w = Wp::new(128, &mut cc);
let finer = w.at(256);
assert_eq!(finer.bits(), 256);
assert_eq!(w.bits(), 128);
}
}