use std::fmt::{Display, Formatter}; #[derive(Clone, Copy)] pub enum Num { Int(i64), Float(f64), } pub fn int(i: i64) -> Num { Num::Int(i) } pub fn float(f: f64) -> Num { Num::Float(f) } impl Num { fn as_float(self) -> f64 { match self { 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 let Ok(u) = u32::try_from(i2) { if let Some(p) = i1.checked_pow(u) { return int(p); } } } _ => (), } float(self.as_float().powf(other.as_float())) } pub fn mul(self, other: Num) -> Num { 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 { float(self.as_float() / other.as_float()) } pub fn int_div(self, other: Num) -> Num { 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 { 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); } 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 { 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 { 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 { 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 asin(self) -> Num { float(self.as_float().asin()) } pub fn acos(self) -> Num { float(self.as_float().acos()) } pub fn atan(self) -> Num { float(self.as_float().atan()) } pub fn sqrt(self) -> Num { match self { 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); } } _ => (), } float(self.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) => int(f.floor() as i64), } } pub fn ceil(self) -> Num { match self { Num::Int(i) => int(i), Num::Float(f) => int(f.ceil() as i64), } } pub fn round(self) -> Num { match self { Num::Int(i) => int(i), Num::Float(f) => int(f.round_ties_even() as i64), } } pub fn abs(self) -> Num { match self { Num::Int(i) => int(i.abs()), Num::Float(f) => float(f.abs()), } } } 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) => { if !(-1e30..=1e30).contains(n) { return write!(f, "{n:.7e}"); } let mut buf = format!("{n:.7}"); while let Some(b'0') = buf.as_bytes().get(buf.len() - 1) { buf.truncate(buf.len() - 1); } write!(f, "{buf}") } } } }