use crate::{
rbig::{RBig, Relaxed},
repr::Repr,
};
use dashu_base::{Approximation, ConversionError, DivRem, Gcd};
use dashu_float::{
round::{ErrorBounds, Round, Rounded},
Context, FBig, Repr as FBigRepr,
};
use dashu_int::{UBig, Word};
impl<R: Round, const B: Word> From<Repr> for FBig<R, B> {
#[inline]
fn from(v: Repr) -> Self {
let Repr {
numerator,
denominator,
} = v;
FBig::from(numerator) / FBig::from(denominator)
}
}
#[allow(dead_code)]
fn fbig_try_from_rbig<R: Round, const B: Word>(v: Repr) -> Result<FBig<R, B>, ConversionError> {
let Repr {
numerator,
denominator,
} = v;
let float_den = FBig::from(denominator);
let float_num = FBig::from(numerator);
if !float_num
.repr()
.significand()
.gcd(float_den.repr().significand())
.is_one()
{
return Err(ConversionError::LossOfPrecision);
}
Ok(float_den / float_num)
}
impl<const B: Word> TryFrom<FBigRepr<B>> for Repr {
type Error = ConversionError;
fn try_from(value: FBigRepr<B>) -> Result<Self, Self::Error> {
if value.is_infinite() {
Err(ConversionError::OutOfBounds)
} else {
let (signif, exp) = value.into_parts();
let (numerator, denominator) = if exp >= 0 {
(signif * UBig::from_word(B).pow(exp as usize), UBig::ONE)
} else {
(signif, UBig::from_word(B).pow((-exp) as usize))
};
Ok(Repr {
numerator,
denominator,
})
}
}
}
impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for Repr {
type Error = ConversionError;
#[inline]
fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
value.into_repr().try_into()
}
}
macro_rules! forward_conversion_to_repr {
($t:ident, $reduce:ident) => {
impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
#[inline]
fn from(v: $t) -> Self {
v.0.into()
}
}
impl<const B: Word> TryFrom<FBigRepr<B>> for $t {
type Error = ConversionError;
#[inline]
fn try_from(value: FBigRepr<B>) -> Result<Self, Self::Error> {
Repr::try_from(value).map(|repr| $t(repr.$reduce()))
}
}
impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
type Error = ConversionError;
#[inline]
fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
Repr::try_from(value).map(|repr| $t(repr.$reduce()))
}
}
};
}
forward_conversion_to_repr!(RBig, reduce);
forward_conversion_to_repr!(Relaxed, reduce2);
impl Repr {
fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
assert!(precision > 0);
if self.numerator.is_zero() {
return FBig::ZERO.with_precision(precision);
}
let base = UBig::from_word(B);
let num_digits = self.numerator.ilog(&base);
let den_digits = self.denominator.ilog(&base);
let shift;
let (q, r) = if num_digits >= precision + den_digits {
shift = 0;
(&self.numerator).div_rem(&self.denominator)
} else {
shift = (precision + den_digits) - num_digits;
if B == 2 {
(&self.numerator << shift).div_rem(&self.denominator)
} else {
(&self.numerator * base.pow(shift)).div_rem(&self.denominator)
}
};
let rounded = if r.is_zero() {
Approximation::Exact(q)
} else {
let adjust = R::round_ratio(&q, r, self.denominator.as_ibig());
Approximation::Inexact(q + adjust, adjust)
};
let context = Context::<R>::new(precision);
rounded
.and_then(|n| context.convert_int(n))
.map(|f| f >> (shift as isize))
}
}
impl RBig {
#[inline]
pub fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
self.0.to_float(precision)
}
pub fn simplest_from_float<R: ErrorBounds, const B: Word>(f: &FBig<R, B>) -> Option<Self> {
if f.repr().is_infinite() {
return None;
} else if f.repr().is_zero() {
return Some(Self::ZERO);
}
let (l, r, incl_l, incl_r) = R::error_bounds(f);
let lb = f - l.with_precision(f.precision() + 1).unwrap();
let rb = f + r.with_precision(f.precision() + 1).unwrap();
let left = Self::try_from(lb).unwrap();
let right = Self::try_from(rb).unwrap();
let mut simplest = Self::simplest_in(left.clone(), right.clone());
if incl_l && left.is_simpler_than(&simplest) {
simplest = left;
}
if incl_r && right.is_simpler_than(&simplest) {
simplest = right;
}
Some(simplest)
}
}
impl Relaxed {
#[inline]
pub fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
self.0.to_float(precision)
}
}