use core::cmp::Ordering;
use core::fmt;
use alloc::string::ToString;
use crate::int::{Int, Sign};
use crate::nat::Nat;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Dyadic {
mantissa: Int,
exponent: i64,
}
impl Dyadic {
pub fn zero() -> Dyadic {
Dyadic {
mantissa: Int::ZERO,
exponent: 0,
}
}
pub fn one() -> Dyadic {
Dyadic {
mantissa: Int::ONE,
exponent: 0,
}
}
pub fn new(mantissa: Int, exponent: i64) -> Dyadic {
if mantissa.is_zero() {
return Dyadic::zero();
}
let t = mantissa.trailing_zeros();
if t > 0 {
Dyadic {
mantissa: mantissa.div_2k_trunc(t),
exponent: exponent + t as i64,
}
} else {
Dyadic { mantissa, exponent }
}
}
#[inline]
pub fn from_int(n: Int) -> Dyadic {
Dyadic::new(n, 0)
}
#[inline]
pub fn mantissa(&self) -> &Int {
&self.mantissa
}
#[inline]
pub fn exponent(&self) -> i64 {
self.exponent
}
#[inline]
pub fn is_zero(&self) -> bool {
self.mantissa.is_zero()
}
#[inline]
pub fn is_integer(&self) -> bool {
self.is_zero() || self.exponent >= 0
}
#[inline]
pub fn sign(&self) -> Sign {
self.mantissa.sign()
}
pub fn neg(&self) -> Dyadic {
Dyadic {
mantissa: self.mantissa.neg(),
exponent: self.exponent,
}
}
pub fn abs(&self) -> Dyadic {
Dyadic {
mantissa: self.mantissa.abs(),
exponent: self.exponent,
}
}
pub fn mul_2k(&self, k: i64) -> Dyadic {
if self.is_zero() {
return Dyadic::zero();
}
Dyadic {
mantissa: self.mantissa.clone(),
exponent: self.exponent + k,
}
}
fn aligned(&self, other: &Dyadic) -> (Int, Int, i64) {
let emin = self.exponent.min(other.exponent);
let a = self.mantissa.mul_2k((self.exponent - emin) as u32);
let b = other.mantissa.mul_2k((other.exponent - emin) as u32);
(a, b, emin)
}
pub fn add(&self, rhs: &Dyadic) -> Dyadic {
if self.is_zero() {
return rhs.clone();
}
if rhs.is_zero() {
return self.clone();
}
let (a, b, emin) = self.aligned(rhs);
Dyadic::new(a.add(&b), emin)
}
pub fn sub(&self, rhs: &Dyadic) -> Dyadic {
self.add(&rhs.neg())
}
pub fn mul(&self, rhs: &Dyadic) -> Dyadic {
Dyadic::new(
self.mantissa.mul(&rhs.mantissa),
self.exponent + rhs.exponent,
)
}
pub fn pow(&self, exp: u32) -> Dyadic {
if exp == 0 {
return Dyadic::one();
}
Dyadic::new(self.mantissa.pow(exp), self.exponent * exp as i64)
}
pub fn floor(&self) -> Int {
if self.exponent >= 0 {
self.mantissa.mul_2k(self.exponent as u32)
} else {
self.mantissa
.div_floor(&Int::ONE.mul_2k((-self.exponent) as u32))
}
}
pub fn trunc(&self) -> Int {
if self.exponent >= 0 {
self.mantissa.mul_2k(self.exponent as u32)
} else {
self.mantissa.div_2k_trunc((-self.exponent) as u32)
}
}
}
impl PartialOrd for Dyadic {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Dyadic {
fn cmp(&self, other: &Self) -> Ordering {
let (a, b, _) = self.aligned(other);
a.cmp(&b)
}
}
impl Default for Dyadic {
#[inline]
fn default() -> Dyadic {
Dyadic::zero()
}
}
impl From<Int> for Dyadic {
#[inline]
fn from(n: Int) -> Dyadic {
Dyadic::from_int(n)
}
}
impl From<i64> for Dyadic {
#[inline]
fn from(v: i64) -> Dyadic {
Dyadic::from_int(Int::from_i64(v))
}
}
impl fmt::Display for Dyadic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return f.write_str("0");
}
if self.mantissa.is_negative() {
f.write_str("-")?;
}
let mag = self.mantissa.magnitude();
if self.exponent >= 0 {
return fmt::Display::fmt(&mag.shl(self.exponent as u64), f);
}
let k = (-self.exponent) as u32;
let scaled = mag.mul(&Nat::from_u64(5).pow(k));
let digits = scaled.to_string();
let k = k as usize;
if digits.len() <= k {
f.write_str("0.")?;
for _ in 0..k - digits.len() {
f.write_str("0")?;
}
f.write_str(&digits)
} else {
let point = digits.len() - k;
f.write_str(&digits[..point])?;
f.write_str(".")?;
f.write_str(&digits[point..])
}
}
}
impl fmt::Debug for Dyadic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Dyadic({} · 2^{})", self.mantissa, self.exponent)
}
}
macro_rules! dyadic_binop {
($tr:ident, $m:ident, $atr:ident, $am:ident) => {
impl core::ops::$tr for Dyadic {
type Output = Dyadic;
#[inline]
fn $m(self, rhs: Dyadic) -> Dyadic {
Dyadic::$m(&self, &rhs)
}
}
impl core::ops::$tr<&Dyadic> for &Dyadic {
type Output = Dyadic;
#[inline]
fn $m(self, rhs: &Dyadic) -> Dyadic {
Dyadic::$m(self, rhs)
}
}
impl core::ops::$atr for Dyadic {
#[inline]
fn $am(&mut self, rhs: Dyadic) {
*self = Dyadic::$m(self, &rhs);
}
}
};
}
dyadic_binop!(Add, add, AddAssign, add_assign);
dyadic_binop!(Sub, sub, SubAssign, sub_assign);
dyadic_binop!(Mul, mul, MulAssign, mul_assign);
impl core::ops::Neg for Dyadic {
type Output = Dyadic;
#[inline]
fn neg(self) -> Dyadic {
Dyadic::neg(&self)
}
}
#[cfg(feature = "rational")]
impl Dyadic {
pub fn to_rational(&self) -> crate::rational::Rational {
use crate::rational::Rational;
if self.exponent >= 0 {
Rational::from_integer(self.mantissa.mul_2k(self.exponent as u32))
} else {
Rational::new(
self.mantissa.clone(),
Int::ONE.mul_2k((-self.exponent) as u32),
)
}
}
pub fn try_from_rational(r: &crate::rational::Rational) -> Option<Dyadic> {
let k = r.denominator().is_power_of_two()?;
Some(Dyadic::new(r.numerator().clone(), -(k as i64)))
}
}
#[cfg(feature = "float")]
impl Dyadic {
pub fn to_float(
&self,
precision: u64,
mode: crate::float::RoundingMode,
) -> crate::float::Float {
crate::float::Float::from_rational(&self.to_rational(), precision, mode)
}
pub fn from_float(f: &crate::float::Float) -> Option<Dyadic> {
if f.is_zero() {
return Some(Dyadic::zero());
}
let sig = f.significand()?; let exp = f.exponent()?;
let mantissa = Int::from_sign_magnitude(f.sign(), sig.clone());
Some(Dyadic::new(mantissa, exp))
}
}
impl core::str::FromStr for Dyadic {
type Err = crate::error::Error;
#[cfg(feature = "rational")]
fn from_str(s: &str) -> crate::error::Result<Dyadic> {
let r: crate::rational::Rational = s.parse()?;
Dyadic::try_from_rational(&r).ok_or(crate::error::Error::Parse)
}
#[cfg(not(feature = "rational"))]
fn from_str(s: &str) -> crate::error::Result<Dyadic> {
Ok(Dyadic::from_int(s.parse()?))
}
}