From a38f68cd842842df2f0972214c7455dc56012999 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 1 Apr 2023 13:50:52 -0700 Subject: [PATCH] Added Quantity type --- Cargo.lock | 85 ++++++++++ Cargo.toml | 3 +- src/main.rs | 10 +- src/parser/mod.rs | 15 +- src/quantity/mod.rs | 7 + src/quantity/quantity.rs | 336 ++++++++++++++++++++++++++++++++++++++ src/quantity/rationalq.rs | 216 ++++++++++++++++++++++++ src/tokens.rs | 100 ++++++------ 8 files changed, 716 insertions(+), 56 deletions(-) create mode 100644 src/quantity/mod.rs create mode 100644 src/quantity/quantity.rs create mode 100644 src/quantity/rationalq.rs diff --git a/Cargo.lock b/Cargo.lock index 68d631e..265f1ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "bitflags" version = "1.3.2" @@ -12,10 +18,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" name = "calc" version = "0.0.1" dependencies = [ + "rug", "signal-hook", "termion", ] +[[package]] +name = "gmp-mpfr-sys" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b560063e2ffa8ce9c2ef9bf487f2944a97deca5b8de0b5bcd0ae6437ef8b75f" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "libc" version = "0.2.140" @@ -46,6 +63,17 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "rug" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555e8b44763d034526db899c88cd56ccc4486cd38b444c8aa0e79d4e70ae5a34" +dependencies = [ + "az", + "gmp-mpfr-sys", + "libc", +] + [[package]] name = "signal-hook" version = "0.3.15" @@ -76,3 +104,60 @@ dependencies = [ "redox_syscall", "redox_termios", ] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/Cargo.toml b/Cargo.toml index 8314ca6..bd1b88b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ panic = "abort" [dependencies] signal-hook = "0.3.15" -termion = "2.0.1" \ No newline at end of file +termion = "2.0.1" +rug = "1.19.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 51e4bf2..2fbd307 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,15 @@ use termion::{color, style}; pub mod tokens; pub mod parser; -mod promptbuffer; pub mod evaluate; +pub mod quantity; + +mod promptbuffer; + -use crate::tokens::Token; use crate::promptbuffer::PromptBuffer; +//use crate::tokens::Token; //use crate::parser::ParserError; //use crate::parser::LineLocation; @@ -180,6 +183,7 @@ mod tests { use crate::parser; use crate::evaluate; use crate::tokens; + use crate::quantity::Quantity; fn good_expr(r: f64, s: &str) { let s = String::from(s); @@ -187,6 +191,8 @@ mod tests { let g = evaluate::evaluate(g).unwrap(); let n = g.eval().unwrap(); let tokens::Token::Number(v) = n else {panic!()}; + + let r = Quantity::new_rational_from_f64(r).unwrap(); assert_eq!(v, r); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8d0cfb5..2f09658 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12,6 +12,8 @@ use crate::parser::groupify::groupify; use crate::parser::treeify::treeify; use crate::parser::find_subs::find_subs; +use crate::quantity::Quantity; + use crate::tokens::Token; /// Specifies the location of a token in an input string. @@ -71,21 +73,24 @@ impl PreToken { pub fn to_token(self) -> Result{ match self { PreToken::PreNumber(l, s) => { - let n = match s.parse() { + let n: f64 = match s.parse() { Ok(n) => n, Err(_) => return Err((l, ParserError::BadNumber)) }; - return Ok(Token::Number(n)); + return Ok(Token::Number(Quantity::new_rational_from_f64(n).unwrap())); }, PreToken::PreWord(l, s) => { return Ok(match &s[..] { // Mathematical constants - "π"|"pi" => { Token::Constant(3.141592653, String::from("π")) }, - "e" => { Token::Constant(2.71828, String::from("e")) }, - "phi"|"φ" => { Token::Constant(1.61803, String::from("φ")) }, + // 100 digits of each. + "π"|"pi" => { Token::Constant(Quantity::float_from_string("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067"), String::from("π")) }, + "e" => { Token::Constant(Quantity::float_from_string("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713 8217852516642"), String::from("e")) }, + "phi"|"φ" => { Token::Constant(Quantity::float_from_string("1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137"), String::from("φ")) }, + _ => { return Err((l, ParserError::Undefined(s))); } }); } + PreToken::Container(v) => { return Ok(v); } PreToken::PreOperator(_,_) diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs new file mode 100644 index 0000000..c70fc59 --- /dev/null +++ b/src/quantity/mod.rs @@ -0,0 +1,7 @@ +mod rationalq; + +pub mod quantity; +pub use crate::quantity::quantity::Quantity; + +const FLOAT_PRECISION: u32 = 2048; +const PRINT_LEN: usize = 4; // How many significant digits we will show in output \ No newline at end of file diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs new file mode 100644 index 0000000..d8d67f3 --- /dev/null +++ b/src/quantity/quantity.rs @@ -0,0 +1,336 @@ +use rug::Float; +use rug::ops::Pow; + +use std::ops::{ + Add, Sub, Mul, Div, + Neg, Rem, + + AddAssign, SubAssign, + MulAssign, DivAssign +}; +use std::cmp::Ordering; + + +use crate::quantity::rationalq::RationalQ; +use crate::quantity::FLOAT_PRECISION; +use crate::quantity::PRINT_LEN; + +#[derive(Debug)] +#[derive(Clone)] +pub enum Quantity { + Rational{ v: RationalQ }, + Float{ v: Float } +} + +impl ToString for Quantity{ + fn to_string(&self) -> String { + let (sign, mut string, exp) = match self { + Quantity::Float { v } => { v.to_sign_string_exp(10, Some(PRINT_LEN)) } + Quantity::Rational { v } => { v.to_sign_string_exp(10, Some(PRINT_LEN)) } + }; + + // zero, nan, or inf. + let sign = if sign {"-"} else {""}; + if exp.is_none() { return format!("{sign}{string}"); } + let exp = exp.unwrap(); + + // Remove trailing zeros. + // At this point, string is guaranteed to be nonzero. + while string.chars().last().unwrap() == '0' { + string.remove(string.len() - 1); + } + + let exp_u: usize; + + if exp < 0 { + exp_u = (-exp).try_into().unwrap() + } else { + exp_u = exp.try_into().unwrap() + } + + if exp_u >= 4 { + // Exponential notation + let pre = &string[0..1]; + let post = &string[1..]; + + format!( + "{pre}{}{post}e{}{exp}", + if post.len() != 0 {"."} else {""}, + if exp > 0 {"+"} else {""}, + ) + } else { + if exp <= 0 { // Decimal, needs `0.` and leading zeros + format!( + "0.{}{string}", + "0".repeat(exp_u) + ) + } else if exp_u < string.len() { // Decimal, needs only `.` + format!( + "{}.{}", + &string[0..exp_u], + &string[exp_u..] + ) + } else { // Integer, needs trailing zeros + format!( + "{string}{}", + "0".repeat(exp_u - string.len()) + ) + } + } + + } +} + + +macro_rules! quick_quant_fn { + ( $x:ident ) => { + pub fn $x(&self) -> Quantity { + match self { + Quantity::Float { v } => {Quantity::Float{ v:v.clone().$x()}}, + Quantity::Rational { v } => {v.$x()} + } + } + } +} + +impl Quantity { + + pub fn new_float(f: f64) -> Quantity { + return Quantity::Float { + v: Float::with_val(FLOAT_PRECISION, f) + } + } + + pub fn float_from_string(s: &str) -> Quantity { + let v = Float::parse(s); + return Quantity::Float { + v: Float::with_val(FLOAT_PRECISION, v.unwrap()) + } + } + + + pub fn new_rational(top: i64, bottom: i64) -> Quantity { + return Quantity::Rational { + v: RationalQ::new(top, bottom) + } + } + + pub fn new_rational_from_f64(f: f64) -> + Option { + let r = RationalQ::from_f64(f); + + if r.is_some() { + return Some(Quantity::Rational { + v: r.unwrap() + }); + } else { + return None; + } + } + + pub fn to_float(&self) -> Float { + match self { + Quantity::Float { v } => {v.clone()}, + Quantity::Rational { v } => {v.to_float()} + } + } + + quick_quant_fn!(fract); + + quick_quant_fn!(abs); + quick_quant_fn!(floor); + quick_quant_fn!(ceil); + quick_quant_fn!(round); + quick_quant_fn!(sin); + quick_quant_fn!(cos); + quick_quant_fn!(tan); + quick_quant_fn!(asin); + quick_quant_fn!(acos); + quick_quant_fn!(atan); + quick_quant_fn!(sinh); + quick_quant_fn!(cosh); + quick_quant_fn!(tanh); + quick_quant_fn!(asinh); + quick_quant_fn!(acosh); + quick_quant_fn!(atanh); + + quick_quant_fn!(ln); + quick_quant_fn!(log10); + quick_quant_fn!(log2); + + pub fn log(&self, base: Quantity) -> Quantity { + match (&self, &base) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{v: a.clone().log10() / b.clone().log10()}}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{v: a.clone().log10() / b.to_float().log10()}}, + (Quantity::Rational{v:a}, _) => {a.log(base)} + } + } + + pub fn is_zero(&self) -> bool { + match self { + Quantity::Float { v } => {v.is_zero()}, + Quantity::Rational { v } => {v.is_zero()} + } + } + + pub fn pow(&self, exp: Quantity) -> Quantity { + match self { + Quantity::Float { v } => {Quantity::Float {v: v.pow(exp.to_float())}}, + Quantity::Rational { v } => {v.pow(exp) } + } + } + + pub fn is_nan(&self) -> bool { + match self { + Quantity::Float { v } => {v.is_nan()}, + Quantity::Rational { .. } => {panic!()} + } + } +} + +impl Neg for Quantity where { + type Output = Self; + + fn neg(self) -> Self::Output { + match self { + Quantity::Float { v } => {Quantity::Float{ v: -v }}, + Quantity::Rational { v } => {Quantity::Rational { v: -v }}, + } + } +} + +impl Add for Quantity { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a+b }}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{ v: a+b.to_float() }}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.to_float()+b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a+b }}, + } + } +} + +impl AddAssign for Quantity where { + fn add_assign(&mut self, other: Self) { + match (&mut *self, other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a += b}, + (Quantity::Float{v: a}, Quantity::Rational{v:b}) => {*a += b.to_float()}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {*self = Quantity::Float{ v: a.to_float()+b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a += b}, + } + } +} + +impl Sub for Quantity { + type Output = Self; + + fn sub(self, other: Self) -> Self::Output { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a-b }}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{ v: a-b.to_float() }}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.to_float()-b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a-b }}, + } + } +} + +impl SubAssign for Quantity where { + fn sub_assign(&mut self, other: Self) { + match (&mut *self, other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a -= b}, + (Quantity::Float{v: a}, Quantity::Rational{v:b}) => {*a -= b.to_float()}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {*self = Quantity::Float{ v: a.to_float()-b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a -= b}, + } + } +} + +impl Mul for Quantity { + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a*b }}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{ v: a*b.to_float() }}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.to_float()*b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a*b }}, + } + } +} + +impl MulAssign for Quantity where { + fn mul_assign(&mut self, other: Self) { + match (&mut *self, other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a *= b}, + (Quantity::Float{v: a}, Quantity::Rational{v:b}) => {*a *= b.to_float()}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {*self = Quantity::Float{ v: a.to_float() * b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a *= b}, + } + } +} + + +impl Div for Quantity { + type Output = Self; + + fn div(self, other: Self) -> Self::Output { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a/b }}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{ v: a/b.to_float() }}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.to_float()/b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a/b }}, + } + } +} + +impl DivAssign for Quantity where { + fn div_assign(&mut self, other: Self) { + match (&mut *self, other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a /= b}, + (Quantity::Float{v: a}, Quantity::Rational{v:b}) => {*a /= b.to_float()}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {*self = Quantity::Float{ v: a.to_float()/b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a /= b}, + } + } +} + +impl Rem for Quantity { + type Output = Self; + + fn rem(self, modulus: Quantity) -> Self::Output { + match (self, modulus) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a%b }}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {Quantity::Float{ v: a%b.to_float() }}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.to_float()%b }}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational { v: a%b }}, + } + } +} + +impl PartialEq for Quantity { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {a == b}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {*a==b.to_float()}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {a.to_float()==*b}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {a == b}, + } + } +} + +impl PartialOrd for Quantity { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {a.partial_cmp(b)}, + (Quantity::Float{v:a}, Quantity::Rational{v:b}) => {(*a).partial_cmp(&b.to_float())}, + (Quantity::Rational{v:a}, Quantity::Float{v:b}) => {a.to_float().partial_cmp(b)}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {a.partial_cmp(b)}, + } + } +} + + + + diff --git a/src/quantity/rationalq.rs b/src/quantity/rationalq.rs new file mode 100644 index 0000000..e998034 --- /dev/null +++ b/src/quantity/rationalq.rs @@ -0,0 +1,216 @@ +use rug::Float; +use rug::ops::Pow; +use rug::Rational; +use rug::Integer; + +use std::ops::{ + Add, Sub, Mul, Div, + Neg, Rem, + + AddAssign, SubAssign, + MulAssign, DivAssign +}; + +use std::cmp::Ordering; + + +use crate::quantity::Quantity; +use crate::quantity::FLOAT_PRECISION; + +macro_rules! rational { + ( $x:expr ) => { + Quantity::Rational { v: RationalQ { + val : $x + }} + }; +} + +macro_rules! float { + ( $x:expr ) => { + Quantity::Float { v: $x } + }; +} + +#[derive(Debug)] +#[derive(Clone)] +pub struct RationalQ where { + pub val: Rational +} + +impl ToString for RationalQ { + fn to_string(&self) -> String { + self.to_float().to_string() + } +} + +impl RationalQ { + pub fn new(top: i64, bot: i64) -> RationalQ { + return RationalQ { + val: Rational::from((top, bot)) + } + } + + pub fn is_zero(&self) -> bool{ + return self.val == Rational::from((0,1)); + } + pub fn fract(&self) -> Quantity { + rational!(self.val.clone().fract_floor(Integer::new()).0) + } + + pub fn from_f64(f: f64) -> Option { + let v = Rational::from_f64(f); + if v.is_none() { return None } + return Some(RationalQ{ val: v.unwrap() }); + } + + pub fn to_float(&self) -> Float { + Float::with_val(FLOAT_PRECISION, self.val.numer()) / + Float::with_val(FLOAT_PRECISION, self.val.denom()) + } + + pub fn to_string_radix(&self, radix: i32, num_digits: Option) -> String { + self.to_float().to_string_radix(radix, num_digits) + } + + pub fn to_sign_string_exp(&self, radix: i32, num_digits: Option) -> (bool, String, Option) { + self.to_float().to_sign_string_exp(radix, num_digits) + } + + + pub fn abs(&self) -> Quantity {rational!(self.val.clone().abs())} + pub fn floor(&self) -> Quantity {rational!(self.val.clone().floor())} + pub fn ceil(&self) -> Quantity {rational!(self.val.clone().ceil())} + pub fn round(&self) -> Quantity {rational!(self.val.clone().round())} + + pub fn sin(&self) -> Quantity {float!(self.to_float().sin())} + pub fn cos(&self) -> Quantity {float!(self.to_float().cos())} + pub fn tan(&self) -> Quantity {float!(self.to_float().tan())} + pub fn asin(&self) -> Quantity {float!(self.to_float().asin())} + pub fn acos(&self) -> Quantity {float!(self.to_float().acos())} + pub fn atan(&self) -> Quantity {float!(self.to_float().atan())} + + pub fn sinh(&self) -> Quantity {float!(self.to_float().sinh())} + pub fn cosh(&self) -> Quantity {float!(self.to_float().cosh())} + pub fn tanh(&self) -> Quantity {float!(self.to_float().tanh())} + pub fn asinh(&self) -> Quantity {float!(self.to_float().asinh())} + pub fn acosh(&self) -> Quantity {float!(self.to_float().acosh())} + pub fn atanh(&self) -> Quantity {float!(self.to_float().atanh())} + + pub fn ln(&self) -> Quantity {float!(self.to_float().ln())} + pub fn log10(&self) -> Quantity {float!(self.to_float().log10())} + pub fn log2(&self) -> Quantity {float!(self.to_float().log2())} + + pub fn log(&self, base: Quantity) -> Quantity { + float!(self.to_float().log10() / base.to_float().log10()) + } + + + + pub fn pow(&self, exp: Quantity) -> Quantity { + float!(self.to_float().pow(exp.to_float())) + } +} + +impl Add for RationalQ where { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self { + val: self.val + other.val + } + } +} + +impl AddAssign for RationalQ where { + fn add_assign(&mut self, other: Self) { + self.val += other.val; + } +} + +impl Sub for RationalQ { + type Output = Self; + + fn sub(self, other: Self) -> Self::Output { + Self { + val: self.val - other.val + } + } +} + +impl SubAssign for RationalQ where { + fn sub_assign(&mut self, other: Self) { + self.val -= other.val; + } +} + +impl Mul for RationalQ { + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + Self { + val: self.val * other.val + } + } +} + +impl MulAssign for RationalQ where { + fn mul_assign(&mut self, other: Self) { + self.val *= other.val; + } +} + +impl Div for RationalQ { + type Output = Self; + + fn div(self, other: Self) -> Self::Output { + Self { + val: self.val / other.val + } + } +} + +impl DivAssign for RationalQ where { + fn div_assign(&mut self, other: Self) { + self.val /= other.val; + } +} + +impl Neg for RationalQ where { + type Output = Self; + + fn neg(self) -> Self::Output { + Self { + val: -self.val + } + } +} + +impl Rem for RationalQ { + type Output = Self; + + fn rem(self, modulus: RationalQ) -> Self::Output { + if { + *self.val.denom() != 1 || + *modulus.val.denom() != 1 + } { panic!() } + + RationalQ{ + val : Rational::from(( + self.val.numer() % modulus.val.numer(), + 1 + )) + } + } +} + +impl PartialEq for RationalQ { + fn eq(&self, other: &Self) -> bool { + self.val == other.val + } +} + +impl PartialOrd for RationalQ { + fn partial_cmp(&self, other: &Self) -> Option { + self.val.partial_cmp(&other.val) + } +} \ No newline at end of file diff --git a/src/tokens.rs b/src/tokens.rs index af59fbc..b024c6e 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -1,13 +1,17 @@ use std::collections::VecDeque; +use crate::quantity::Quantity; + /// Tokens represent logical objects in an expession. /// /// Tokens starting with `Pre*` are intermediate tokens, and /// will never show up in a fully-parsed expression tree. #[derive(Debug)] +#[derive(Clone)] pub enum Token { - Number(f64), - Constant(f64, String), + Number(Quantity), + + Constant(Quantity, String), Operator( Operator, @@ -44,9 +48,9 @@ impl Token { #[inline(always)] pub fn eval(&self) -> Result { Ok(match self { - Token::Number(v) => { Token::Number(*v) }, - Token::Constant(v,_) => { Token::Number(*v) }, - Token::Operator(o,v) => { o.apply(v)? } + Token::Number(_) => { self.clone() }, + Token::Constant(v,_) => { Token::Number(v.clone()) }, + Token::Operator(o,v) => { o.apply(&v)? } }) } @@ -54,8 +58,8 @@ impl Token { #[inline(always)] pub fn as_number(&self) -> Token { match self { - Token::Number(v) => { Token::Number(*v) }, - Token::Constant(v,_) => { Token::Number(*v) }, + Token::Number(v) => { Token::Number(v.clone()) }, + Token::Constant(v,_) => { Token::Number(v.clone()) }, _ => panic!() } } @@ -152,29 +156,29 @@ impl Function { pub fn apply(&self, args: &VecDeque) -> Result { if args.len() != 1 {panic!()}; let a = args[0].as_number(); - let Token::Number(v) = a else {panic!()}; + let Token::Number(q) = a else {panic!()}; match self { - Function::Abs => { return Ok(Token::Number(v.abs())); }, - Function::Floor => { return Ok(Token::Number(v.floor())); }, - Function::Ceil => { return Ok(Token::Number(v.ceil())); }, - Function::Round => { return Ok(Token::Number(v.round())); }, + Function::Abs => { return Ok(Token::Number(q.abs())); }, + Function::Floor => { return Ok(Token::Number(q.floor())); }, + Function::Ceil => { return Ok(Token::Number(q.ceil())); }, + Function::Round => { return Ok(Token::Number(q.round())); }, - Function::NaturalLog => { return Ok(Token::Number(v.log(2.71828))); }, - Function::TenLog => { return Ok(Token::Number(v.log(10f64))); }, + Function::NaturalLog => { return Ok(Token::Number(q.ln())); }, + Function::TenLog => { return Ok(Token::Number(q.log10())); }, - Function::Sin => { return Ok(Token::Number(v.sin())); }, - Function::Cos => { return Ok(Token::Number(v.cos())); }, - Function::Tan => { return Ok(Token::Number(v.tan())); }, - Function::Asin => { return Ok(Token::Number(v.asin())); }, - Function::Acos => { return Ok(Token::Number(v.acos())); }, - Function::Atan => { return Ok(Token::Number(v.atan())); }, + Function::Sin => { return Ok(Token::Number(q.sin())); }, + Function::Cos => { return Ok(Token::Number(q.cos())); }, + Function::Tan => { return Ok(Token::Number(q.tan())); }, + Function::Asin => { return Ok(Token::Number(q.asin())); }, + Function::Acos => { return Ok(Token::Number(q.acos())); }, + Function::Atan => { return Ok(Token::Number(q.atan())); }, Function::Csc => { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.sin()))) + VecDeque::from(vec!(Token::Number(q.sin()))) ).eval()? ); }, @@ -182,7 +186,7 @@ impl Function { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.cos()))) + VecDeque::from(vec!(Token::Number(q.cos()))) ).eval()? ); }, @@ -190,24 +194,24 @@ impl Function { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.tan()))) + VecDeque::from(vec!(Token::Number(q.tan()))) ).eval()? ); }, - Function::Sinh => { return Ok(Token::Number(v.sinh())); }, - Function::Cosh => { return Ok(Token::Number(v.cosh())); }, - Function::Tanh => { return Ok(Token::Number(v.tanh())); }, - Function::Asinh => { return Ok(Token::Number(v.asinh())); }, - Function::Acosh => { return Ok(Token::Number(v.acosh())); }, - Function::Atanh => { return Ok(Token::Number(v.atanh())); }, + Function::Sinh => { return Ok(Token::Number(q.sinh())); }, + Function::Cosh => { return Ok(Token::Number(q.cosh())); }, + Function::Tanh => { return Ok(Token::Number(q.tanh())); }, + Function::Asinh => { return Ok(Token::Number(q.asinh())); }, + Function::Acosh => { return Ok(Token::Number(q.acosh())); }, + Function::Atanh => { return Ok(Token::Number(q.atanh())); }, Function::Csch => { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.sinh()))) + VecDeque::from(vec!(Token::Number(q.sinh()))) ).eval()? ); }, @@ -215,7 +219,7 @@ impl Function { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.cosh()))) + VecDeque::from(vec!(Token::Number(q.cosh()))) ).eval()? ); }, @@ -223,7 +227,7 @@ impl Function { return Ok( Token::Operator( Operator::Flip, - VecDeque::from(vec!(Token::Number(v.tanh()))) + VecDeque::from(vec!(Token::Number(q.tanh()))) ).eval()? ); }, @@ -457,7 +461,7 @@ impl Operator { Token::Operator( Operator::Power, - VecDeque::from(vec!(a, Token::Number(0.5))) + VecDeque::from(vec!(a, Token::Number(Quantity::new_rational(1,2)))) ) }, @@ -500,13 +504,13 @@ impl Operator{ let args = args[0].as_number(); if let Token::Number(v) = args { - if v == 0f64 { return Err(()); } - return Ok(Token::Number(1f64/v)); + if v.is_zero() { return Err(()); } + return Ok(Token::Number(Quantity::new_rational(1,1)/v)); } else { panic!(); } }, Operator::Add => { - let mut sum: f64 = 0f64; + let mut sum = Quantity::new_rational(0,1); for i in args.iter() { let j = i.as_number(); if let Token::Number(v) = j { @@ -519,7 +523,7 @@ impl Operator{ }, Operator::Multiply => { - let mut prod: f64 = 1f64; + let mut prod = Quantity::new_rational(1,1); for i in args.iter() { let j = i.as_number(); if let Token::Number(v) = j { @@ -539,9 +543,9 @@ impl Operator{ if let Token::Number(va) = a { if let Token::Number(vb) = b { - if vb <= 1f64 { return Err(()); } - if va.fract() != 0f64 { return Err(()); } - if vb.fract() != 0f64 { return Err(()); } + if vb <= Quantity::new_rational(1,1) { return Err(()); } + if va.fract() != Quantity::new_rational(0,1) { return Err(()); } + if vb.fract() != Quantity::new_rational(0,1) { return Err(()); } return Ok(Token::Number(va%vb)); } else { panic!(); } @@ -555,7 +559,7 @@ impl Operator{ if let Token::Number(va) = a { if let Token::Number(vb) = b { - let p = va.powf(vb); + let p = va.pow(vb); if p.is_nan() {return Err(());} return Ok(Token::Number(p)); } else { panic!(); } @@ -567,14 +571,14 @@ impl Operator{ let args = args[0].as_number(); if let Token::Number(v) = args { - if v.fract() != 0f64 { return Err(()); } - if v >= 100f64 { return Err(()); } + if !v.fract().is_zero() { return Err(()); } + if v >= Quantity::new_rational(100, 1) { return Err(()); } - let mut prod = 1f64; - let mut u = v; - while u > 0f64 { - prod *= u; - u -= 1f64; + let mut prod = Quantity::new_rational(1, 1); + let mut u = v.clone(); + while u > Quantity::new_rational(0, 1) { + prod *= u.clone(); + u = u - Quantity::new_rational(1, 1); } return Ok(Token::Number(prod));