use num::pow::Pow; use num::{BigInt, FromPrimitive, Integer, Signed, ToPrimitive}; use std::cmp::Ordering; 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 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); } 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()), } } 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)), } } }