use core:: {
cmp:: { PartialOrd, Ordering },
num:: { NonZeroU128, NonZeroI128 },
ops:: { Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg },
str::FromStr,
};
use combine::{
Parser,
error::StringStreamError,
stream::position,
};
use crate::error::Error;
#[cfg(feature = "ratio")]
use core::fmt::Debug;
#[cfg(feature = "ratio")]
use num_rational::Ratio;
#[cfg(feature = "ratio")]
use num_integer::Integer;
mod parser;
mod div;
mod u256;
#[cfg(feature = "tokenize")]
pub mod syn;
use u256::U256;
const PI: u128 = 0x3C813F6636D984F2595B6A372A53D; const PI_2: i16 = -31;
const PI_5: i16 = -35;
const NON_ZERO_ONE: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(1) };
macro_rules! checked {
($expr:expr, $fall_back:expr) => {
match $expr {
Some(p) => p,
None => return $fall_back,
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
struct Exp {
two: i16, five: i16, pi: i16,
}
impl Exp {
const fn new(two: i16, five: i16, pi: i16) -> Self {
Self {
two, five, pi,
}
}
const fn factor_out(mut self, mut rhs: Self) -> (Self, Self, Exp) {
const fn choice(left: i16, right: i16) -> (i16, i16, i16) {
if left > right {
(left - right, 0, right)
} else {
(0, right - left, left)
}
}
let two;
let five;
let pi;
(self.two, rhs.two, two) = choice(self.two, rhs.two);
(self.five, rhs.five, five) = choice(self.five, rhs.five);
(self.pi, rhs.pi, pi) = choice(self.pi, rhs.pi);
(self, rhs, Exp { two, five, pi })
}
const fn add(self, rhs: Self) -> Self {
Self {
two: self.two + rhs.two,
five: self.five + rhs.five,
pi: self.pi + rhs.pi,
}
}
const fn neg(self) -> Self {
Self {
two: -self.two,
five: -self.five,
pi: -self.pi,
}
}
}
struct BigNumer(U256, Exp);
impl BigNumer {
pub const fn exp_pi256(mut self) -> Self {
while self.1.pi > 0 {
let (tmp, e2) = self.0.mul128(PI);
self.1.two += e2 as i16;
self.0 = tmp;
self.1.pi -= 1;
}
self
}
const fn consume_pi(self) -> Self {
if self.1.pi > 0 {
self.exp_pi256()
} else {
self
}
}
const fn consume_5(mut self) -> Self {
if self.1.five > 0 {
let e2;
(self.0, e2) = self.0.mul_exp5(self.1.five as u16);
self.1.two += e2 as i16;
}
self
}
const fn consume_2(mut self) -> Self {
if self.1.two > 0 {
let mut shift = self.1.two as u16;
let shfit_max = self.0.leading_zeros() as u16;
let e2;
(shift, e2) = match shift.checked_sub(shfit_max) {
Some(d) => (shfit_max, d),
None => (shift, 0),
};
self.0 = self.0.shl(shift as u8);
self.1.two = e2 as i16;
}
self
}
const fn consume_exp(self) -> Self {
self.consume_pi().consume_5().consume_2()
}
}
const fn factorize_25(mut u: u128) -> (u128, u16, u16) {
let exp2 = u.trailing_zeros();
let mut exp5 = 0;
u >>= exp2;
loop {
let (q, r) = (u / 5, u % 5);
if r != 0 {
break;
}
exp5 += 1;
u = q;
}
(u, exp2 as u16, exp5)
}
const fn pow128(u: u128, mut exp: u16) -> (u128, u16) {
let mut overflowed = 0;
let mut result = u;
while exp > 0 {
let big = U256::from_prod(result, u);
let (r, e) = big.round_down();
result = r;
overflowed += e as u16;
exp -= 1
}
(result, overflowed)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, Hash)]
pub struct Frac {
neg: bool,
numer: u128,
denom: NonZeroU128,
exp: Exp,
}
impl Frac {
pub const fn is_positive(&self) -> bool { !self.is_negative() }
pub const fn is_negative(&self) -> bool { self.neg }
pub const fn numer(&self) -> u128 { self.numer }
pub const fn denom(&self) -> u128 { self.denom.get() }
pub const fn exp2(&self) -> i16 { self.exp.two }
pub const fn exp5(&self) -> i16 { self.exp.five }
pub const fn exp_pi(&self) -> i16 { self.exp.pi }
pub const fn new(neg: bool, numer: u128, denom: u128, exp2: i16, exp5: i16, exp_pi: i16) -> Self {
assert!( denom != 0);
Self {
neg, numer, exp: Exp::new(
exp2,
exp5,
exp_pi),
denom: unsafe { NonZeroU128::new_unchecked(denom) },
}.regularize()
}
pub const fn from_int(i: i128) -> Self {
if i == 0 {
Self {
neg: false,
numer: 0,
denom: NON_ZERO_ONE,
exp: Exp::new(0, 0, 0),
}
} else {
let neg = i < 0;
Self {
neg,
numer: if neg { -i } else { i } as u128,
denom: NON_ZERO_ONE,
exp: Exp::new(0, 0, 0),
}.regularize()
}
}
pub const fn from_exp10(exp10: i16) -> Self {
Self {
neg: false,
numer: 1,
denom: NON_ZERO_ONE,
exp: Exp::new(exp10, exp10, 0),
}.regularize()
}
pub const fn from_exp_pi(exp_pi: i16) -> Self {
Self {
neg: false,
numer: 1,
denom: NON_ZERO_ONE,
exp: Exp {
two: exp_pi * PI_2,
five: exp_pi * PI_5,
pi: exp_pi,
},
}
}
pub const fn from_ratio(numer: i128, denom: u128, exp10: i16) -> Self {
let neg = if denom == 0 {
panic!()
} else {
numer < 0
};
Self {
neg,
numer: if neg { -numer } else { numer } as u128,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp: Exp::new(exp10, exp10, 0),
}.regularize()
}
pub const fn from_f64(d: f64) -> Self {
let (neg, numer, exp2) = div::decode_f64(d);
Self {
neg,
numer: numer as u128,
denom: NON_ZERO_ONE,
exp: Exp::new(if exp2 == -(div::BIAS as i16) {
1 - f64::MANTISSA_DIGITS as i16
} else {
exp2 - f64::MANTISSA_DIGITS as i16 + 1
},0, 0),
}.regularize()
}
pub const fn regularize(self) -> Self {
if self.numer == 0 {
Self::from_int(0)
} else {
let (numer, denom) = div::reduce(self.numer, self.denom.get());
let (numer, nexp2, nexp5) = factorize_25(numer);
let (denom, dexp2, dexp5) = factorize_25(denom);
Self {
numer,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp: Exp::new(
self.exp.two
+ if nexp2 == 0 { -(dexp2 as i16) } else { nexp2 as i16 },
self.exp.five
+ if nexp5 == 0 { -(dexp5 as i16) } else { nexp5 as i16 },
self.exp.pi
),
.. self
}
}
}
pub const fn recip(self) -> Self {
assert!(self.numer != 0);
Self {
numer: self.denom.get(),
denom: unsafe { NonZeroU128::new_unchecked(self.numer) },
exp: self.exp.neg(),
.. self
}
}
const fn reduce128(u: u128, exp: Exp) -> (u128, i16) {
let frac = Self {
neg: false,
numer: u,
denom: NON_ZERO_ONE,
exp,
};
match frac.consume_exp() {
Some(p) => (p.numer, p.exp.two),
None => {
let big = BigNumer(
U256::new(frac.numer),
frac.exp
).consume_exp();
let (u, e2) = big.0.round_down();
(u, frac.exp.two + big.1.two + e2 as i16)
}
}
}
const fn reduce_exp(self, except2: bool) -> Self {
let (numer, ne2) = Self::reduce128(self.numer, Exp {
two: if except2 { 0 } else { self.exp.two },
.. self.exp
});
let (denom, de2) = Self::reduce128(self.denom(), Exp {
two: if except2 { 0 } else { -self.exp.two },
five: -self.exp.five,
pi: -self.exp.pi,
});
Self {
numer,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp: Exp {
two: ne2 - de2 + if except2 { self.exp.two } else { 0 },
five: 0,
pi: 0,
},
.. self
}
}
const fn consume_exp_pi(self) -> Option<Self> {
Some(if self.exp.pi > 0 {
let pi = checked!(PI.checked_pow(self.exp.pi as u32), None);
Self {
numer: checked!(self.numer.checked_mul(pi), None),
exp: Exp { pi: 0, .. self.exp },
.. self
}
} else {
self
})
}
const fn consume_exp2(self) -> Option<Self> {
if self.exp.two > 0 {
let n = self.numer.leading_zeros() as i16;
if self.exp.two > n {
None
} else {
Some(Self {
numer: self.numer << self.exp.two,
exp: Exp { two: 0, .. self.exp },
.. self
})
}
} else {
Some(self)
}
}
const fn consume_exp5(self) -> Option<Self> {
Some(if self.exp.five > 0 {
let pow5 = checked!(5u128.checked_pow(self.exp.five as u32), None);
Self {
numer: checked!(self.numer.checked_mul(pow5), None),
exp: Exp { five: self.exp.five, .. self.exp },
.. self
}
} else {
self
})
}
const fn consume_exp(self) -> Option<Self> {
Some(checked!(
checked!(
checked!(
self.consume_exp_pi(), None
).consume_exp5(), None
).consume_exp2(), None
))
}
pub const fn add(mut self, mut rhs: Self) -> Self {
if self.numer == 0 {
return rhs;
} else if rhs.numer == 0{
return self;
}
let mut common: Exp;
(self.exp, rhs.exp, common) = self.exp.factor_out(rhs.exp);
self = checked!(self.consume_exp(), self.big_add(rhs, common));
rhs = checked!(rhs.consume_exp(), self.big_add(rhs, common));
if self.exp.two != rhs.exp.two {
return self.big_add(rhs, common);
}
common.two += self.exp.two;
self.exp.two = 0;
rhs.exp.two = 0;
self.numer = checked!(self.numer.checked_mul(rhs.denom()), self.big_add(rhs, common));
rhs.numer = checked!(rhs.numer.checked_mul(self.denom()), self.big_add(rhs, common));
if self.neg != rhs.neg {
match self.numer.overflowing_sub(rhs.numer) {
(d, false) => self.numer = d,
(d, true) => {
self.numer = -(d as i128) as u128;
self.neg = rhs.neg;
},
}
} else {
self.numer = checked!(self.numer.checked_add(rhs.numer), self.big_add(rhs, common));
}
self = self.regularize();
let tmp = self.denom;
self.denom = rhs.denom;
self = self.regularize();
self.denom = tmp;
rhs.numer = 0;
let denom = checked!(self.denom().checked_mul(rhs.denom()), self.big_add(rhs, common));
self.denom = unsafe { NonZeroU128::new_unchecked(denom) };
self.exp = self.exp.add(common);
self
}
const fn big_add(self, rhs: Self, mut common: Exp) -> Self {
let mut lnumer = BigNumer(
U256::from_prod(self.numer, rhs.denom.get()),
self.exp
);
let mut rnumer = BigNumer(
U256::from_prod(self.denom.get(), rhs.numer),
rhs.exp
);
lnumer = lnumer.consume_exp();
rnumer = rnumer.consume_exp();
let mut e2;
(lnumer.0, e2) = lnumer.0.mul128(rhs.denom());
lnumer.1.two += e2 as i16;
(rnumer.0, e2) = rnumer.0.mul128(self.denom());
rnumer.1.two += e2 as i16;
let d = lnumer.1.two - rnumer.1.two;
match d.signum() {
1 => {
rnumer.0 = if d >= U256::BITS as i16 {
U256::new(0)
} else {
rnumer.0.shr(d as u8)
};
common.two += lnumer.1.two;
},
-1 => {
lnumer.0 = if -d >= U256::BITS as i16 {
U256::new(0)
} else {
lnumer.0.shr(-d as u8)
};
common.two += rnumer.1.two;
},
_ => common.two += lnumer.1.two,
}
lnumer.1.two = 0;
rnumer.1.two = 0;
let mut extr;
let neg = if self.neg != rhs.neg {
extr = false;
match lnumer.0.diff(rnumer.0) {
(d, false) => {
lnumer.0 = d;
self.neg
},
(d, true) => {
lnumer.0 = d;
rhs.neg
}
}
} else {
(lnumer.0, extr) = lnumer.0.overflowing_add(rnumer.0);
self.neg
};
let e2;
(lnumer.0, e2) = lnumer.0.extract2();
common.two += e2 as i16;
if extr && e2 > 0 {
lnumer.0 = lnumer.0.bitor(U256::new(1).shl((U256::BITS - e2 as u16) as u8));
extr = false;
}
let e5;
(lnumer.0, e5) = lnumer.0.extract5(extr);
if extr && e5 == 0 {
common.two += 1;
lnumer.0 = lnumer.0.shr(1).bitor(U256::new(1).shl((U256::BITS - 1) as u8));
}
let denom = U256::from_prod(self.denom(), rhs.denom());
let (numer, denom) = U256::reduce(lnumer.0, denom);
let (numer, e2) = numer.round_down();
common.two += e2 as i16;
common.five += e5 as i16;
let (denom, e2) = denom.round_down();
common.two -= e2 as i16;
Self {
neg,
numer,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp: common,
}
}
pub const fn sub(self, rhs: Self) -> Self {
self.add(Self {
neg: !rhs.neg,
.. rhs
})
}
pub const fn mul(self, rhs: Self) -> Self {
if self.numer == 0 || rhs.numer == 0 {
Self::from_int(0)
} else {
let (lnumer, ldenom) = div::reduce(self.numer, rhs.denom.get());
let (rnumer, rdenom) = div::reduce(rhs.numer, self.denom.get());
let mut exp = self.exp.add(rhs.exp);
let numer = match lnumer.checked_mul(rnumer) {
Some(p) => p,
None => {
let big = U256::from_prod(lnumer, rnumer);
let (n, e) = big.round_down();
exp.two += e as i16;
n
},
};
let denom = match ldenom.checked_mul(rdenom) {
Some(p) => p,
None => {
let big = U256::from_prod(ldenom, rdenom);
let (n, e) = big.round_down();
exp.two -= e as i16;
n
}
};
Self {
neg: self.neg != rhs.neg,
numer,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp,
}.regularize()
}
}
pub const fn div(self, rhs: Self) -> Self {
self.mul(rhs.recip())
}
pub const fn neg(self) -> Self {
Self {
neg: !self.neg,
.. self
}
}
pub const fn powi(self, exp: i16) -> Self {
if exp == 0 {
self
} else if self.numer == 0 {
if exp < 0 {
panic!();
}
Self::from_int(0)
} else {
let mut exp2 = 0;
let numer = match self.numer.checked_pow(exp.abs() as u32) {
Some(p) => p,
None => {
let (p, e) = pow128(self.numer, exp.abs() as u16);
exp2 += e as i16;
p
},
};
let denom = match self.denom.get().checked_pow(exp.abs() as u32) {
Some(p) => p,
None => {
let (p, e) = pow128(self.denom.get(), exp.abs() as u16);
exp2 -= e as i16;
p
}
};
let neg = self.neg as i16 & exp & 1 == 1;
if exp < 0 {
Self {
neg,
numer: denom,
denom: unsafe { NonZeroU128::new_unchecked(numer) },
exp: Exp {
two: self.exp.two * exp - exp2,
five: self.exp.five * exp,
pi: self.exp.pi * exp,
},
}
} else {
Self {
neg,
numer,
denom: unsafe { NonZeroU128::new_unchecked(denom) },
exp: Exp {
two: self.exp.two * exp + exp2,
five: self.exp.five * exp,
pi: self.exp.pi * exp,
},
}
}.regularize()
}
}
pub const fn to_f64(mut self) -> f64 {
if self.numer == 0 {
return 0f64;
}
self = self.reduce_exp(true);
div::const_div(
self.numer,
self.denom.get(),
self.neg,
self.exp.two
)
}
pub const fn to_ratio(mut self) -> Result<(i128, NonZeroI128), Error> {
self = self.reduce_exp(false);
if self.numer < 1 << 127 && self.denom.get() < 1 << 127 {
Err(Error::Overflow)
} else {
Ok((
if self.neg {
-(self.numer as i128)
} else {
self.numer as i128
},
unsafe { NonZeroI128::new_unchecked(
self.denom.get() as i128
) }
))
}
}
#[cfg(feature = "ratio")]
pub const fn to_rational128(self) -> Ratio<i128> {
match self.to_ratio() {
Ok((numer, denom)) => Ratio::new_raw(numer, denom.get()),
_ => panic!(),
}
}
}
impl FromStr for Frac {
type Err = StringStreamError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match parser::parser().parse(position::Stream::new(s)) {
Ok((output, _remaining_input)) => Ok(output),
Err(err) => Err(err),
}
}
}
impl Default for Frac {
fn default() -> Self {
Self::from_int(0)
}
}
impl From<f64> for Frac {
fn from(d: f64) -> Self {
Self::from_f64(d)
}
}
impl Into<f64> for Frac {
fn into(self) -> f64 {
self.to_f64()
}
}
#[cfg(feature = "ratio")]
impl<T> Into<Ratio<T>> for Frac
where
T: Clone + Debug + From<i128> + Integer,
{
fn into(self) -> Ratio<T> {
let r128 = self.to_rational128();
let pair: (T, T) = ((*r128.numer()).into(), (*r128.denom()).into());
pair.into()
}
}
macro_rules! impl_ops {
($bin_trait:ident, $bin_method:ident, $assign_trait:ident, $assign_method:ident) => {
impl $bin_trait for Frac {
type Output = Self;
fn $bin_method(self, rhs: Self) -> Self {
self.$bin_method(rhs)
}
}
impl $bin_trait<&Frac> for Frac {
type Output = Self;
fn $bin_method(self, rhs: &Self) -> Self {
$bin_trait::$bin_method(self, *rhs)
}
}
impl $assign_trait for Frac {
fn $assign_method(&mut self, rhs: Self) {
*self = self.$bin_method(rhs);
}
}
impl $assign_trait<&Frac> for Frac {
fn $assign_method(&mut self, rhs: &Self) {
$assign_trait::$assign_method(self, *rhs);
}
}
};
}
impl_ops! { Add, add, AddAssign, add_assign }
impl_ops! { Sub, sub, SubAssign, sub_assign }
impl_ops! { Mul, mul, MulAssign, mul_assign }
impl_ops! { Div, div, DivAssign, div_assign }
impl Neg for Frac {
type Output = Self;
fn neg(self) -> Self::Output {
Frac::neg(self)
}
}
impl PartialOrd for Frac {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self == other {
Some(Ordering::Equal)
} else if self.neg ^ other.neg {
if self.neg {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
} else if other.numer == 0 {
if self.neg {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
} else {
let r = self.div(*other);
if (r.to_f64() > 1f64) ^ self.neg {
Some(Ordering::Greater)
} else {
Some(Ordering::Less)
}
}
}
}
impl crate::real::Real for Frac {
fn from_int(i: isize) -> Self {
Self::from_int(i as i128)
}
fn from_f64(d: f64) -> Self {
Self::from_f64(d)
}
fn from_frac(frac: Self) -> Self {
frac
}
fn add_ref(self, rhs: &Self) -> Self {
self + rhs
}
fn sub_ref(self, rhs: &Self) -> Self {
self - rhs
}
fn mul_ref(self, rhs: &Self) -> Self {
self * rhs
}
fn div_ref(self, rhs: &Self) -> Self {
self / rhs
}
fn poweri(self, n: i16) -> Self {
Frac::powi(self, n)
}
}
#[cfg(test)]
mod tests {
use super::*;
use num::Float;
use core::f64;
#[test]
fn test_conv() {
let pound2gram = Frac::from_ratio(45359237, 100000, 0);
let left = pound2gram.to_f64().integer_decode();
let right = 453.59237.integer_decode();
assert_eq!(left, right,
"\n{:b}\n{:b}\n",
left.0,
right.0
);
let rankine2kelvin = Frac::from_ratio(5, 9, 0);
let left = rankine2kelvin.to_f64().integer_decode();
let right = (5.0/9.0).integer_decode();
assert_eq!(left, right,
"\n{:b}\n{:b}\n",
left.0,
right.0
);
let btu2calorie = pound2gram * rankine2kelvin;
let left = btu2calorie.to_f64();
let right = 453.59237 * 5.0 / 9.0;
assert!(1.0 - left / right < f64::EPSILON);
let left = btu2calorie.to_f64().integer_decode();
let mut right = 453.59237;
right *= 5.0;
right /= 9.0;
let right = right.integer_decode();
assert_eq!(left.2, right.2);
assert_eq!(left.1, right.1);
assert!(left.0 ^ right.0 <= 1);
}
#[test]
fn test_add() {
let left = Frac::from_int(314) * Frac::from_exp10(-2);
let right = Frac::from_int(86) * Frac::from_exp10(-2);
let sum = left + right;
assert_eq!(left.to_f64(), 3.14);
assert_eq!(right.to_f64(), 0.86);
assert_eq!(sum.to_f64(), 4.0);
}
#[test]
fn test_sub() {
let left = Frac::from_int(348_051856) * Frac::from_exp10(-6);
let right = Frac::from_int(334_133017) * Frac::from_exp10(-6);
let diff = left - right;
assert_eq!(left.to_f64(), 348.051856);
assert_eq!(right.to_f64(), 334.133017);
assert_eq!(
diff.to_f64(),
13.918839,
"\n{}\n", diff.to_f64()
);
}
#[test]
fn test_pi() {
let pi = Frac::from_exp_pi(-1);
let pi_f64 = pi.to_f64();
let left = pi_f64.integer_decode();
let inv_pi = 1f64 / f64::consts::PI;
let right = inv_pi.integer_decode();
assert_eq!(pi_f64, inv_pi, "\n{:b}, {}\n{:b}, {}\n", left.0, left.1, right.0, right.1);
}
}