use crate::{
error::assert_finite,
round::{Round, Rounded},
utils::{digit_len, split_digits, split_digits_ref},
};
use core::marker::PhantomData;
use dashu_base::{Approximation::*, EstimatedLog2, Sign};
pub use dashu_int::Word;
use dashu_int::{IBig, UBig};
#[derive(PartialEq, Eq)]
pub struct Repr<const BASE: Word> {
pub(crate) significand: IBig,
pub(crate) exponent: isize,
}
#[derive(Clone, Copy)]
pub struct Context<RoundingMode: Round> {
pub(crate) precision: usize,
_marker: PhantomData<RoundingMode>,
}
impl<const B: Word> Repr<B> {
pub const BASE: UBig = UBig::from_word(B);
#[inline]
pub const fn zero() -> Self {
Self {
significand: IBig::ZERO,
exponent: 0,
}
}
#[inline]
pub const fn one() -> Self {
Self {
significand: IBig::ONE,
exponent: 0,
}
}
#[inline]
pub const fn neg_one() -> Self {
Self {
significand: IBig::NEG_ONE,
exponent: 0,
}
}
#[inline]
pub const fn infinity() -> Self {
Self {
significand: IBig::ZERO,
exponent: 1,
}
}
#[inline]
pub const fn neg_infinity() -> Self {
Self {
significand: IBig::ZERO,
exponent: -1,
}
}
#[inline]
pub const fn is_zero(&self) -> bool {
self.significand.is_zero() && self.exponent == 0
}
#[inline]
pub const fn is_one(&self) -> bool {
self.significand.is_one() && self.exponent == 0
}
#[inline]
pub const fn is_infinite(&self) -> bool {
self.significand.is_zero() && self.exponent != 0
}
#[inline]
pub const fn is_finite(&self) -> bool {
!self.is_infinite()
}
pub fn is_int(&self) -> bool {
if self.is_infinite() {
false
} else {
self.exponent >= 0
}
}
#[inline]
pub const fn sign(&self) -> Sign {
if self.significand.is_zero() {
if self.exponent >= 0 {
Sign::Positive
} else {
Sign::Negative
}
} else {
self.significand.sign()
}
}
pub(crate) fn normalize(self) -> Self {
let Self {
mut significand,
mut exponent,
} = self;
if significand.is_zero() {
return Self::zero();
}
if B == 2 {
let shift = significand.trailing_zeros().unwrap();
significand >>= shift;
exponent += shift as isize;
} else if B.is_power_of_two() {
let bits = B.trailing_zeros() as usize;
let shift = significand.trailing_zeros().unwrap() / bits;
significand >>= shift * bits;
exponent += shift as isize;
} else {
let (sign, mut mag) = significand.into_parts();
let shift = mag.remove(&UBig::from_word(B)).unwrap();
exponent += shift as isize;
significand = IBig::from_parts(sign, mag);
}
Self {
significand,
exponent,
}
}
#[inline]
pub fn digits(&self) -> usize {
assert_finite(self);
digit_len::<B>(&self.significand)
}
#[inline]
pub fn digits_ub(&self) -> usize {
assert_finite(self);
if self.significand.is_zero() {
return 0;
}
let log = match B {
2 => self.significand.log2_bounds().1,
10 => self.significand.log2_bounds().1 * core::f32::consts::LOG10_2,
_ => self.significand.log2_bounds().1 / Self::BASE.log2_bounds().0,
};
log as usize + 1
}
#[inline]
pub fn digits_lb(&self) -> usize {
assert_finite(self);
if self.significand.is_zero() {
return 0;
}
let log = match B {
2 => self.significand.log2_bounds().0,
10 => self.significand.log2_bounds().0 * core::f32::consts::LOG10_2,
_ => self.significand.log2_bounds().0 / Self::BASE.log2_bounds().1,
};
log as usize
}
#[inline]
pub(crate) fn smaller_than_one(&self) -> bool {
debug_assert!(self.is_finite());
self.exponent + (self.digits_ub() as isize) < -1
}
#[inline]
pub fn new(significand: IBig, exponent: isize) -> Self {
Self {
significand,
exponent,
}
.normalize()
}
#[inline]
pub fn significand(&self) -> &IBig {
&self.significand
}
#[inline]
pub fn exponent(&self) -> isize {
self.exponent
}
#[inline]
pub fn into_parts(self) -> (IBig, isize) {
(self.significand, self.exponent)
}
#[doc(hidden)]
#[rustversion::since(1.64)]
#[inline]
pub const unsafe fn from_static_words(
sign: Sign,
significand: &'static [Word],
exponent: isize,
) -> Self {
let significand = IBig::from_static_words(sign, significand);
assert!(!significand.is_multiple_of_const(B as _));
Self {
significand,
exponent,
}
}
}
impl<const B: Word> Clone for Repr<B> {
#[inline]
fn clone(&self) -> Self {
Self {
significand: self.significand.clone(),
exponent: self.exponent,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.significand.clone_from(&source.significand);
self.exponent = source.exponent;
}
}
impl<R: Round> Context<R> {
#[inline]
pub const fn new(precision: usize) -> Self {
Self {
precision,
_marker: PhantomData,
}
}
#[inline]
pub const fn max(lhs: Self, rhs: Self) -> Self {
Self {
precision: if lhs.precision > rhs.precision {
lhs.precision
} else {
rhs.precision
},
_marker: PhantomData,
}
}
#[inline]
pub(crate) const fn is_limited(&self) -> bool {
self.precision != 0
}
#[inline]
pub const fn precision(&self) -> usize {
self.precision
}
pub(crate) fn repr_round<const B: Word>(&self, repr: Repr<B>) -> Rounded<Repr<B>> {
assert_finite(&repr);
if !self.is_limited() {
return Exact(repr);
}
let digits = repr.digits();
if digits > self.precision {
let shift = digits - self.precision;
let (signif_hi, signif_lo) = split_digits::<B>(repr.significand, shift);
let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
Inexact(Repr::new(signif_hi + adjust, repr.exponent + shift as isize), adjust)
} else {
Exact(repr)
}
}
pub(crate) fn repr_round_ref<const B: Word>(&self, repr: &Repr<B>) -> Rounded<Repr<B>> {
assert_finite(repr);
if !self.is_limited() {
return Exact(repr.clone());
}
let digits = repr.digits();
if digits > self.precision {
let shift = digits - self.precision;
let (signif_hi, signif_lo) = split_digits_ref::<B>(&repr.significand, shift);
let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
Inexact(Repr::new(signif_hi + adjust, repr.exponent + shift as isize), adjust)
} else {
Exact(repr.clone())
}
}
}