diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2024-05-28 18:12:02 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2024-05-28 18:14:21 -0700 |
| commit | 2df47cf5e375c73917ebcd6690cd16199d57af83 (patch) | |
| tree | 6b5206f512e7b0a5ce7ee8d4c3b76a51919e7a32 /src/eval.rs | |
| parent | 8c0ddfb990057c5ae8d6d15ab0e0ec3f83b07ee4 (diff) | |
| download | qc-2df47cf5e375c73917ebcd6690cd16199d57af83.tar.zst | |
Use num crate.
Diffstat (limited to 'src/eval.rs')
| -rw-r--r-- | src/eval.rs | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..d3b5ddc --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,196 @@ +use num::pow::Pow; +use num::{BigInt, FromPrimitive, Signed, ToPrimitive}; +use std::fmt::{Display, Formatter}; + +pub enum Num { + Int(BigInt), + Float(f64), +} + +pub fn int(i: BigInt) -> Num { + Num::Int(i) +} + +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 { + match self { + Num::Int(i) => float_from_int(i), + Num::Float(f) => *f, + } + } + + pub fn pow(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => { + if i2 < BigInt::ZERO { + return float(float_from_int(&i1).powf(float_from_int(&i2))); + } + let (_, u2) = i2.into_parts(); + int(i1.pow(u2)) + } + (n1, n2) => float(n1.as_float().powf(n2.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()), + } + } + + pub fn div(self, other: Num) -> Num { + float(self.as_float() / other.as_float()) + } + + 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)) + } + } + } + + 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()), + } + } + + 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()), + } + } + + pub fn sin(self) -> Num { + float(self.as_float().sin()) + } + + pub fn cos(self) -> Num { + float(self.as_float().cos()) + } + + pub fn tan(self) -> Num { + float(self.as_float().tan()) + } + + 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()); + } + int(s) + } + n => float(n.as_float().sqrt()), + } + } + + pub fn log(self) -> Num { + float(self.as_float().ln()) + } + + pub fn log10(self) -> Num { + float(self.as_float().log10()) + } + + pub fn log2(self) -> Num { + float(self.as_float().log2()) + } + + 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) + } + } + } + + 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) + } + } + } + + 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) + } + } + } + + pub fn abs(self) -> Num { + match self { + Num::Int(i) => int(i.abs()), + Num::Float(f) => float(f.abs()), + } + } +} + +fn format_float(f: f64) -> String { + let mut s = format!("{f:.7}"); + while let Some(b'0') = s.as_bytes().get(s.len() - 1) { + s.truncate(s.len() - 1); + } + s +} + +impl Display for Num { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + Num::Int(n) => write!(f, "{n}"), + Num::Float(n) => write!(f, "{}", format_float(*n)), + } + } +} |
