aboutsummaryrefslogtreecommitdiffstats
path: root/src/num.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-05-27 16:04:31 -0700
committerRose Hogenson <rosehogenson@posteo.net>2024-05-27 16:04:31 -0700
commit4debd88807c538aa430ce69b83fdb291f687b1b1 (patch)
tree5def99861585367826937c2bdbbfba7f23e07f0b /src/num.rs
downloadqc-4debd88807c538aa430ce69b83fdb291f687b1b1.tar.zst
Rewrite in Rust.
Diffstat (limited to 'src/num.rs')
-rw-r--r--src/num.rs183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/num.rs b/src/num.rs
new file mode 100644
index 0000000..f17c076
--- /dev/null
+++ b/src/num.rs
@@ -0,0 +1,183 @@
+use num_bigint::BigInt;
+use num_traits::cast::ToPrimitive;
+use num_traits::pow::Pow;
+use num_traits::FromPrimitive;
+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 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)
+ }
+ }
+ }
+}
+
+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)),
+ }
+ }
+}