diff options
Diffstat (limited to 'src/eval.rs')
| -rw-r--r-- | src/eval.rs | 178 |
1 files changed, 72 insertions, 106 deletions
diff --git a/src/eval.rs b/src/eval.rs index 1045b17..c11614d 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,13 +1,12 @@ -use num::pow::Pow; -use num::{BigInt, FromPrimitive, Integer, Signed, ToPrimitive}; use std::fmt::{Display, Formatter}; +#[derive(Clone, Copy)] pub enum Num { - Int(BigInt), + Int(i64), Float(f64), } -pub fn int(i: BigInt) -> Num { +pub fn int(i: i64) -> Num { Num::Int(i) } @@ -15,43 +14,45 @@ pub fn float(f: f64) -> Num { Num::Float(f) } -fn float_from_int(i: &BigInt) -> f64 { - if let Some(f) = i.to_f64() { - return f; - } - if i > &BigInt::ZERO { - std::f64::INFINITY - } else { - std::f64::NEG_INFINITY - } -} - impl Num { - fn as_float(&self) -> f64 { + fn as_float(self) -> f64 { match self { - Num::Int(i) => float_from_int(i), - Num::Float(f) => *f, + Num::Int(i) => i as f64, + Num::Float(f) => f, } } pub fn pow(self, other: Num) -> Num { match (self, other) { + (_, Num::Int(0)) => return int(1), + (Num::Int(-1), Num::Int(i2)) => { + if i2 % 2 == 0 { + return int(1); + } else { + return int(-1); + } + } + (Num::Int(0), _) => return int(0), + (Num::Int(1), _) => return int(1), (Num::Int(i1), Num::Int(i2)) => { - if i2 < BigInt::ZERO { - return float(float_from_int(&i1).powf(float_from_int(&i2))); + if let Ok(u) = u32::try_from(i2) { + if let Some(p) = i1.checked_pow(u) { + return int(p); + } } - let (_, u2) = i2.into_parts(); - int(i1.pow(u2)) } - (n1, n2) => float(n1.as_float().powf(n2.as_float())), + _ => (), } + float(self.as_float().powf(other.as_float())) } pub fn mul(self, other: Num) -> Num { - match (self, other) { - (Num::Int(i1), Num::Int(i2)) => int(i1 * i2), - (n1, n2) => float(n1.as_float() * n2.as_float()), + if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let Some(p) = i1.checked_mul(i2) { + return int(p); + } } + float(self.as_float() * other.as_float()) } pub fn div(self, other: Num) -> Num { @@ -59,57 +60,48 @@ impl Num { } pub fn int_div(self, other: Num) -> Num { - match (self, other) { - (Num::Int(i1), Num::Int(i2)) => int(i1 / i2), - (Num::Float(f1), Num::Float(f2)) => { - let r = f1 / f2; - if let Some(i) = BigInt::from_f64(r) { - return int(i); - } - float(r) - } - (Num::Int(i), Num::Float(f)) => { - if let Some(fi) = BigInt::from_f64(f) { - return int(i / fi); - } - float(float_from_int(&i) / f) - } - (Num::Float(f), Num::Int(i)) => { - if let Some(fi) = BigInt::from_f64(f) { - return int(fi / i); - } - float(f / float_from_int(&i)) + if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let Some(q) = i1.checked_div(i2) { + return int(q); } } + int((self.as_float() / other.as_float()) as i64) } pub fn modulo(self, other: Num) -> Num { - match (self, other) { - (Num::Int(i1), Num::Int(i2)) => int(i1.mod_floor(&i2)), - (n1, n2) => { - let n1 = n1.as_float(); - let n2 = n2.as_float(); - let r = n1 % n2; - if r < 0. { - return float(r + n2); + if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let Some(r) = i1.checked_rem(i2) { + if i2 > 0 && r < 0 || i2 < 0 && r > 0 { + return int(r + i2); } - float(r) + return int(r); } } + let n1 = self.as_float(); + let n2 = other.as_float(); + let r = n1 % n2; + if n2 > 0. && r < 0. || n2 < 0. && r > 0. { + return float(r + n2); + } + float(r) } pub fn add(self, other: Num) -> Num { - match (self, other) { - (Num::Int(i1), Num::Int(i2)) => int(i1 + i2), - (n1, n2) => float(n1.as_float() + n2.as_float()), + if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let Some(s) = i1.checked_add(i2) { + return int(s); + } } + float(self.as_float() + other.as_float()) } pub fn sub(self, other: Num) -> Num { - match (self, other) { - (Num::Int(i1), Num::Int(i2)) => int(i1 - i2), - (n1, n2) => Num::Float(n1.as_float() - n2.as_float()), + if let (Num::Int(i1), Num::Int(i2)) = (self, other) { + if let Some(d) = i1.checked_sub(i2) { + return int(d); + } } + float(self.as_float() - other.as_float()) } pub fn sin(self) -> Num { @@ -126,15 +118,23 @@ impl Num { pub fn sqrt(self) -> Num { match self { - Num::Int(i) if i >= BigInt::ZERO => { - let s = i.sqrt(); - if i != &s * &s { - return float(float_from_int(&i).sqrt()); + Num::Int(i) if i >= 0 => { + if i <= 1 { + return int(i); + } + let mut x0 = i / 2; + let mut x1 = (x0 + i / x0) / 2; + while x1 < x0 { + x0 = x1; + x1 = (x0 + i / x0) / 2; + } + if x0 * x0 == i { + return int(x0); } - int(s) } - n => float(n.as_float().sqrt()), + _ => (), } + float(self.as_float().sqrt()) } pub fn log(self) -> Num { @@ -152,36 +152,21 @@ impl Num { pub fn floor(self) -> Num { match self { Num::Int(i) => int(i), - Num::Float(f) => { - if let Some(i) = BigInt::from_f64(f.floor()) { - return int(i); - } - float(f) - } + Num::Float(f) => int(f.floor() as i64), } } pub fn ceil(self) -> Num { match self { Num::Int(i) => int(i), - Num::Float(f) => { - if let Some(i) = BigInt::from_f64(f.ceil()) { - return int(i); - } - float(f) - } + Num::Float(f) => int(f.ceil() as i64), } } pub fn round(self) -> Num { match self { Num::Int(i) => int(i), - Num::Float(f) => { - if let Some(i) = BigInt::from_f64(f.round_ties_even()) { - return int(i); - } - float(f) - } + Num::Float(f) => int(f.round_ties_even() as i64), } } @@ -193,31 +178,12 @@ impl Num { } } -fn log10(n: &BigInt) -> u64 { - ((n.bits() - 1) as f64 / std::f64::consts::LOG2_10) as u64 -} - impl Display for Num { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { match self { - Num::Int(n) => { - if n.bits() < 100 { - return write!(f, "{n}"); - } - let len = log10(n) + 1; - let rounded = i64::try_from(n / BigInt::from(10).pow(len - 8)).unwrap(); - let rounded_str = format!("{rounded}"); - write!( - f, - "{}.{}e{}", - &rounded_str[..1], - &rounded_str[1..], - len - 8 + rounded_str.len() as u64 - 1 - ) - } + Num::Int(n) => write!(f, "{n}"), Num::Float(n) => { - let n = *n; - if !(-1e30..=1e30).contains(&n) { + if !(-1e30..=1e30).contains(n) { return write!(f, "{n:.7e}"); } let mut buf = format!("{n:.7}"); |
