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 {
numerator: Int,
scale: i64,
}
impl Dyadic {
pub fn zero() -> Dyadic {
Dyadic {
numerator: Int::ZERO,
scale: 0,
}
}
pub fn one() -> Dyadic {
Dyadic {
numerator: Int::ONE,
scale: 0,
}
}
pub fn new(n: Int, k: i64) -> Dyadic {
if n.is_zero() {
return Dyadic::zero();
}
let t = n.trailing_zeros();
if t > 0 {
Dyadic {
numerator: n.div_2k_trunc(t),
scale: k - t as i64,
}
} else {
Dyadic {
numerator: n,
scale: k,
}
}
}
#[inline]
pub fn from_int(n: Int) -> Dyadic {
Dyadic::new(n, 0)
}
#[inline]
pub fn numerator(&self) -> &Int {
&self.numerator
}
#[inline]
pub fn scale(&self) -> i64 {
self.scale
}
#[inline]
pub fn is_zero(&self) -> bool {
self.numerator.is_zero()
}
#[inline]
pub fn is_integer(&self) -> bool {
self.is_zero() || self.scale <= 0
}
#[inline]
pub fn sign(&self) -> Sign {
self.numerator.sign()
}
pub fn neg(&self) -> Dyadic {
Dyadic {
numerator: self.numerator.neg(),
scale: self.scale,
}
}
pub fn abs(&self) -> Dyadic {
Dyadic {
numerator: self.numerator.abs(),
scale: self.scale,
}
}
pub fn mul_2k(&self, k: i64) -> Dyadic {
if self.is_zero() {
return Dyadic::zero();
}
Dyadic {
numerator: self.numerator.clone(),
scale: self.scale - k,
}
}
fn aligned(&self, other: &Dyadic) -> (Int, Int, i64) {
let smax = self.scale.max(other.scale);
let a = self.numerator.mul_2k((smax - self.scale) as u32);
let b = other.numerator.mul_2k((smax - other.scale) as u32);
(a, b, smax)
}
pub fn add(&self, rhs: &Dyadic) -> Dyadic {
if self.is_zero() {
return rhs.clone();
}
if rhs.is_zero() {
return self.clone();
}
let (a, b, smax) = self.aligned(rhs);
Dyadic::new(a.add(&b), smax)
}
pub fn sub(&self, rhs: &Dyadic) -> Dyadic {
self.add(&rhs.neg())
}
pub fn mul(&self, rhs: &Dyadic) -> Dyadic {
Dyadic::new(self.numerator.mul(&rhs.numerator), self.scale + rhs.scale)
}
pub fn pow(&self, exp: u32) -> Dyadic {
if exp == 0 {
return Dyadic::one();
}
Dyadic::new(self.numerator.pow(exp), self.scale * exp as i64)
}
pub fn floor(&self) -> Int {
if self.scale <= 0 {
self.numerator.mul_2k((-self.scale) as u32)
} else {
self.numerator
.div_floor(&Int::ONE.mul_2k(self.scale as u32))
}
}
pub fn trunc(&self) -> Int {
if self.scale <= 0 {
self.numerator.mul_2k((-self.scale) as u32)
} else {
self.numerator.div_2k_trunc(self.scale 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.numerator.is_negative() {
f.write_str("-")?;
}
let mag = self.numerator.magnitude();
if self.scale <= 0 {
return fmt::Display::fmt(&mag.shl((-self.scale) as u64), f);
}
let k = self.scale 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.numerator, self.scale)
}
}
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.scale <= 0 {
Rational::from_integer(self.numerator.mul_2k((-self.scale) as u32))
} else {
Rational::new(self.numerator.clone(), Int::ONE.mul_2k(self.scale 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 numerator = Int::from_sign_magnitude(f.sign(), sig.clone());
Some(Dyadic::new(numerator, -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()?))
}
}