use core::cmp::Ordering;
use core::ops::{Add, Mul, Neg, Sub};
use num_bigint::{BigInt, Sign};
#[derive(Clone, Debug)]
pub struct Dyadic {
mant: BigInt,
exp: i64,
}
impl Dyadic {
pub fn from_f64(x: f64) -> Dyadic {
assert!(x.is_finite(), "Dyadic::from_f64: non-finite input");
let bits = x.to_bits();
let biased_exp = ((bits >> 52) & 0x7ff) as i64;
let fraction = bits & ((1u64 << 52) - 1);
let (magnitude, exp) = if biased_exp == 0 {
(fraction, -1074)
} else {
(fraction | (1u64 << 52), biased_exp - 1075)
};
let sign = if bits >> 63 == 1 {
Sign::Minus
} else {
Sign::Plus
};
Dyadic {
mant: BigInt::from_biguint(sign, magnitude.into()),
exp,
}
}
pub fn sum_of(xs: &[f64]) -> Dyadic {
xs.iter()
.fold(Dyadic::from_f64(0.0), |acc, &x| &acc + &Dyadic::from_f64(x))
}
pub fn sign(&self) -> i8 {
match self.mant.sign() {
Sign::Minus => -1,
Sign::NoSign => 0,
Sign::Plus => 1,
}
}
pub fn is_zero(&self) -> bool {
self.sign() == 0
}
fn mantissa_at(&self, exp: i64) -> BigInt {
let shift = usize::try_from(self.exp - exp).expect("exponent difference fits in usize");
&self.mant << shift
}
}
impl Add for &Dyadic {
type Output = Dyadic;
fn add(self, rhs: &Dyadic) -> Dyadic {
let exp = self.exp.min(rhs.exp);
Dyadic {
mant: self.mantissa_at(exp) + rhs.mantissa_at(exp),
exp,
}
}
}
impl Sub for &Dyadic {
type Output = Dyadic;
fn sub(self, rhs: &Dyadic) -> Dyadic {
let exp = self.exp.min(rhs.exp);
Dyadic {
mant: self.mantissa_at(exp) - rhs.mantissa_at(exp),
exp,
}
}
}
impl Mul for &Dyadic {
type Output = Dyadic;
fn mul(self, rhs: &Dyadic) -> Dyadic {
Dyadic {
mant: &self.mant * &rhs.mant,
exp: self.exp + rhs.exp,
}
}
}
impl Neg for &Dyadic {
type Output = Dyadic;
fn neg(self) -> Dyadic {
Dyadic {
mant: -&self.mant,
exp: self.exp,
}
}
}
macro_rules! forward_owned_binary_op {
($trait:ident, $method:ident) => {
impl $trait for Dyadic {
type Output = Dyadic;
fn $method(self, rhs: Dyadic) -> Dyadic {
$trait::$method(&self, &rhs)
}
}
};
}
forward_owned_binary_op!(Add, add);
forward_owned_binary_op!(Sub, sub);
forward_owned_binary_op!(Mul, mul);
impl Neg for Dyadic {
type Output = Dyadic;
fn neg(self) -> Dyadic {
-&self
}
}
impl PartialEq for Dyadic {
fn eq(&self, other: &Dyadic) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Dyadic {}
impl PartialOrd for Dyadic {
fn partial_cmp(&self, other: &Dyadic) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Dyadic {
fn cmp(&self, other: &Dyadic) -> Ordering {
(self - other).sign().cmp(&0)
}
}