diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f4adfda..dfc777f 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -81,7 +81,7 @@ impl PreToken { s.insert(0, '0'); } - let r = Quantity::new_rational_from_float_string(&s); + let r = Quantity::new_rational_from_string(&s); if r.is_none() { return Err((l, ParserError::BadNumber)) } diff --git a/src/quantity/floatq.rs b/src/quantity/floatq.rs new file mode 100644 index 0000000..ca93c2f --- /dev/null +++ b/src/quantity/floatq.rs @@ -0,0 +1,263 @@ +use rug::Float; +use rug::Assign; +use rug::ops::AssignRound; +use rug::ops::Pow; + +use std::ops::{ + Add, Sub, Mul, Div, + Neg, Rem, + + AddAssign, SubAssign, + MulAssign, DivAssign +}; + +use std::cmp::Ordering; + + +use crate::quantity::Quantity; +use crate::quantity::QuantBase; +use crate::quantity::FloatBase; +use crate::quantity::PRINT_LEN; + +use super::FLOAT_PRECISION; + + +macro_rules! foward { + ( $x:ident ) => { + fn $x(&self) -> Quantity { + Quantity::Float{v: FloatQ{ val: self.val.clone().$x() }} + } + } +} + +#[derive(Debug)] +#[derive(Clone)] +pub struct FloatQ where { + pub val: Float +} + +impl FloatQ { + pub fn from(a: T) -> Option where + Float: Assign + AssignRound + { + let v = Float::with_val(FLOAT_PRECISION, a); + return Some(FloatQ{ val: v }); + } +} + +impl ToString for FloatQ { + fn to_string(&self) -> String { + let (sign, mut string, exp) = self.val.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 >= PRINT_LEN { + // Exponential notation + let pre = &string[0..1]; + let post = &string[1..]; + + format!( + "{pre}{}{post}e{}", + if post.len() != 0 {"."} else {""}, + //if exp > 0 {"+"} else {""}, + exp - 1 + ) + } else { + if exp <= 0 { // Decimal, needs `0.` and leading zeros + format!( + "{sign}0.{}{string}", + "0".repeat(exp_u) + ) + } else if exp_u < string.len() { // Decimal, needs only `.` + format!( + "{sign}{}.{}", + &string[0..exp_u], + &string[exp_u..] + ) + } else { // Integer, needs trailing zeros + format!( + "{sign}{string}{}", + "0".repeat(exp_u - string.len()) + ) + } + } + + } +} + + +impl QuantBase for FloatQ { + + foward!(fract); + + fn is_zero(&self) -> bool {self.val.is_zero()} + fn is_negative(&self) -> bool { self.val.is_sign_negative() } + fn is_positive(&self) -> bool { self.val.is_sign_positive() } + + foward!(abs); + foward!(floor); + foward!(ceil); + foward!(round); + + foward!(sin); + foward!(cos); + foward!(tan); + foward!(asin); + foward!(acos); + foward!(atan); + + foward!(sinh); + foward!(cosh); + foward!(tanh); + foward!(asinh); + foward!(acosh); + foward!(atanh); + + foward!(exp); + foward!(ln); + foward!(log10); + foward!(log2); + + fn log(&self, base: Quantity) -> Quantity { + Quantity::Float{v: FloatQ{ val: self.val.clone().log10() }} / + Quantity::float_from_rat(&base).log10() + } + + fn pow(&self, base: Quantity) -> Quantity { + match base { + Quantity::Rational { .. } => self.pow(Quantity::float_from_rat(&base)), + Quantity::Float { v } => Quantity::Float{v: FloatQ{ val: self.val.clone().pow(v.val) }} + } + + } + +} + +impl FloatBase for FloatQ { + fn from_f64(f: f64) -> Option { + let v = Float::with_val(FLOAT_PRECISION, f); + return Some(FloatQ{ val: v }); + } + + fn from_string(s: &str) -> Option { + let v = Float::parse(s); + let v = match v { + Ok(x) => x, + Err(_) => return None + }; + + return Some( + FloatQ{ val: + Float::with_val(FLOAT_PRECISION, v) + } + ); + } +} + + + +impl Add for FloatQ where { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self { val: self.val + other.val} + } +} + +impl AddAssign for FloatQ where { + fn add_assign(&mut self, other: Self) { + self.val += other.val; + } +} + +impl Sub for FloatQ { + type Output = Self; + + fn sub(self, other: Self) -> Self::Output { + Self {val: self.val - other.val} + } +} + +impl SubAssign for FloatQ where { + fn sub_assign(&mut self, other: Self) { + self.val -= other.val; + } +} + +impl Mul for FloatQ { + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + Self {val: self.val * other.val} + } +} + +impl MulAssign for FloatQ where { + fn mul_assign(&mut self, other: Self) { + self.val *= other.val; + } +} + +impl Div for FloatQ { + type Output = Self; + + fn div(self, other: Self) -> Self::Output { + Self {val: self.val / other.val} + } +} + +impl DivAssign for FloatQ where { + fn div_assign(&mut self, other: Self) { + self.val /= other.val; + } +} + +impl Neg for FloatQ where { + type Output = Self; + + fn neg(self) -> Self::Output { + Self {val: -self.val} + } +} + +impl Rem for FloatQ { + type Output = Self; + + fn rem(self, modulus: FloatQ) -> Self::Output { + if { + (!self.fract().is_zero()) || + (!modulus.fract().is_zero()) + } { panic!() } + + FloatQ{val : self.val.fract() % modulus.val.fract()} + } +} + +impl PartialEq for FloatQ { + fn eq(&self, other: &Self) -> bool { + self.val == other.val + } +} + +impl PartialOrd for FloatQ { + fn partial_cmp(&self, other: &Self) -> Option { + self.val.partial_cmp(&other.val) + } +} \ No newline at end of file diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs index 86faff0..e875f7c 100644 --- a/src/quantity/mod.rs +++ b/src/quantity/mod.rs @@ -1,7 +1,68 @@ -mod rationalq; +use std::ops::{ + Add, Sub, Mul, Div, + Neg, Rem, + AddAssign, SubAssign, + MulAssign, DivAssign +}; + + + +mod rationalq; +mod floatq; pub mod quantity; + pub use crate::quantity::quantity::Quantity; const FLOAT_PRECISION: u32 = 1024; -const PRINT_LEN: usize = 5; // How many significant digits we will show in output \ No newline at end of file +const PRINT_LEN: usize = 5; // How many significant digits we will show in output + + +pub trait RationalBase: QuantBase { + fn from_frac(top: i64, bot: i64) -> Self; + fn from_f64(f: f64) -> Option where Self: Sized; + fn from_string(s: &str) -> Optionwhere Self: Sized; +} + +pub trait FloatBase: QuantBase { + fn from_f64(f: f64) -> Option where Self: Sized; + fn from_string(s: &str) -> Option where Self: Sized; +} + +pub trait QuantBase: + Sized + ToString + + Add + AddAssign + + Sub + SubAssign + + Mul + MulAssign + + Div + DivAssign + + Neg + Rem + + PartialEq + PartialOrd +{ + fn fract(&self) -> Quantity; + fn is_zero(&self) -> bool; + fn is_negative(&self) -> bool; + fn is_positive(&self) -> bool; + + fn exp(&self) -> Quantity; + fn abs(&self) -> Quantity; + fn floor(&self) -> Quantity; + fn ceil(&self) -> Quantity; + fn round(&self) -> Quantity; + fn sin(&self) -> Quantity; + fn cos(&self) -> Quantity; + fn tan(&self) -> Quantity; + fn asin(&self) -> Quantity; + fn acos(&self) -> Quantity; + fn atan(&self) -> Quantity; + fn sinh(&self) -> Quantity; + fn cosh(&self) -> Quantity; + fn tanh(&self) -> Quantity; + fn asinh(&self) -> Quantity; + fn acosh(&self) -> Quantity; + fn atanh(&self) -> Quantity; + fn ln(&self) -> Quantity; + fn log10(&self) -> Quantity; + fn log2(&self) -> Quantity; + fn log(&self, base: Quantity) -> Quantity; + fn pow(&self, exp: Quantity) -> Quantity; +} \ No newline at end of file diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs index eb9b45f..17cdf80 100644 --- a/src/quantity/quantity.rs +++ b/src/quantity/quantity.rs @@ -1,6 +1,3 @@ -use rug::Float; -use rug::ops::Pow; - use std::ops::{ Add, Sub, Mul, Div, Neg, Rem, @@ -10,258 +7,150 @@ use std::ops::{ }; use std::cmp::Ordering; - use crate::quantity::rationalq::RationalQ; -use crate::quantity::FLOAT_PRECISION; -use crate::quantity::PRINT_LEN; +use crate::quantity::floatq::FloatQ; + + +use crate::quantity::QuantBase; +use crate::quantity::RationalBase; +use crate::quantity::FloatBase; #[derive(Debug)] #[derive(Clone)] pub enum Quantity { Rational{ v: RationalQ }, - Float{ v: Float } + Float{ v: FloatQ } } -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 >= PRINT_LEN { - // Exponential notation - let pre = &string[0..1]; - let post = &string[1..]; - - format!( - "{pre}{}{post}e{}", - if post.len() != 0 {"."} else {""}, - //if exp > 0 {"+"} else {""}, - exp - 1 - ) - } else { - if exp <= 0 { // Decimal, needs `0.` and leading zeros - format!( - "{sign}0.{}{string}", - "0".repeat(exp_u) - ) - } else if exp_u < string.len() { // Decimal, needs only `.` - format!( - "{sign}{}.{}", - &string[0..exp_u], - &string[exp_u..] - ) - } else { // Integer, needs trailing zeros - format!( - "{sign}{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 new_rational(top: i64, bottom: i64) -> Quantity { + return Quantity::Rational { + v: RationalQ::from_frac(top, bottom) } } - pub fn new_float_from_string(s: &str) -> Option { - let v = Float::parse(s); - - let v = match v { - Ok(x) => x, - Err(_) => return None - }; - - return Some(Quantity::Float { - v: Float::with_val(FLOAT_PRECISION, v) - }) - } - - - pub fn new_rational(top: i64, bottom: i64) -> Quantity { - return Quantity::Rational { - v: RationalQ::new(top, bottom) + pub fn new_float(v: f64) -> Quantity { + return Quantity::Float { + v: FloatQ::from_f64(v).unwrap() } } pub fn new_rational_from_string(s: &str) -> Option { let r = RationalQ::from_string(s); if r.is_none() { return None; } - return Some(Quantity::Rational { v: r.unwrap() }); + return Some(Quantity::Rational{v: r.unwrap()}) } - 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 new_float_from_string(s: &str) -> Option { + let v = FloatQ::from_string(s); + if v.is_none() { return None; } + return Some(Quantity::Float{v: v.unwrap()}) } - pub fn new_rational_from_float_string(s: &str) -> Option { - - // Scientific notation - let mut sci = s.split("e"); - let num = sci.next().unwrap(); - let exp = sci.next(); - - let exp = if exp.is_some() { - let r = exp.unwrap().parse::(); - match r { - Ok(x) => x, - Err(_) => return None + pub fn float_from_rat(r: &Quantity) -> Quantity { + match &r { + Quantity::Float { .. } => r.clone(), + Quantity::Rational { v } => Quantity::Float { v: + FloatQ::from(v.val.numer()).unwrap() / + FloatQ::from(v.val.denom()).unwrap() } - } else {0isize}; - - // Split integer and decimal parts - let mut dec = num.split("."); - let a = dec.next().unwrap(); - let b = dec.next(); - let b = if b.is_some() {b.unwrap()} else {""}; - - // Error conditions - if { - dec.next().is_some() || // We should have at most one `.` - sci.next().is_some() || // We should have at most one `e` - a.len() == 0 // We need something in the numerator - } { return None; } - - let s: String; - if exp < 0 { - let exp: usize = (-exp).try_into().unwrap(); - s = format!("{a}{b}/1{}", "0".repeat(b.len() + exp)); - } else if exp > 0 { - let exp: usize = exp.try_into().unwrap(); - s = format!( - "{a}{b}{}/1{}", - "0".repeat(exp), - "0".repeat(b.len()) - ); - } else { // exp == 0 - s = format!("{a}{b}/1{}", "0".repeat(b.len())); - }; - - return Quantity::new_rational_from_string(&s); - } - - 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!(exp); - - 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::Float { v } => {v.val.is_nan()}, Quantity::Rational { .. } => {panic!()} } } +} - pub fn is_negative(&self) -> bool { - match self { - Quantity::Float { v } => {v.is_sign_negative() && v.is_normal()}, - Quantity::Rational { v } => {v.is_negative()} - } - } - pub fn is_positive(&self) -> bool { + +impl ToString for Quantity { + fn to_string(&self) -> String { match self { - Quantity::Float { v } => {v.is_sign_positive() && v.is_normal()}, - Quantity::Rational { v } => {v.is_positive()} + Quantity::Rational{v} => v.to_string(), + Quantity::Float{v} => v.to_string(), } } } + +macro_rules! quant_foward { + ( $x:ident ) => { + fn $x(&self) -> Quantity { + match self { + Quantity::Rational{v} => v.$x(), + Quantity::Float{v} => v.$x(), + } + } + } +} + +impl QuantBase for Quantity { + + fn is_zero(&self) -> bool { + match self { + Quantity::Rational{v} => v.is_zero(), + Quantity::Float{v} => v.is_zero(), + } + } + + fn is_negative(&self) -> bool { + match self { + Quantity::Rational{v} => v.is_negative(), + Quantity::Float{v} => v.is_negative(), + } + } + + fn is_positive(&self) -> bool { + match self { + Quantity::Rational{v} => v.is_positive(), + Quantity::Float{v} => v.is_positive(), + } + } + + quant_foward!(fract); + quant_foward!(abs); + quant_foward!(floor); + quant_foward!(ceil); + quant_foward!(round); + quant_foward!(sin); + quant_foward!(cos); + quant_foward!(tan); + quant_foward!(asin); + quant_foward!(acos); + quant_foward!(atan); + quant_foward!(sinh); + quant_foward!(cosh); + quant_foward!(tanh); + quant_foward!(asinh); + quant_foward!(acosh); + quant_foward!(atanh); + quant_foward!(exp); + quant_foward!(ln); + quant_foward!(log10); + quant_foward!(log2); + + fn log(&self, base: Quantity) -> Quantity { + match self { + Quantity::Rational{v} => v.log(base), + Quantity::Float{v} => v.log(base), + } + } + fn pow(&self, base: Quantity) -> Quantity { + match self { + Quantity::Rational{v} => v.pow(base), + Quantity::Float{v} => v.pow(base), + } + } +} + + + impl Neg for Quantity where { type Output = Self; @@ -277,22 +166,22 @@ 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 }}, + match (&self, &other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.clone()+b.clone() }}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {self + Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(&self) + other}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a.clone()+b.clone() }}, } } } 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}, + match (&mut *self, &other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a += b.clone()}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {*self += Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {*self = Quantity::float_from_rat(self) + other }, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a += b.clone()}, } } } @@ -301,22 +190,22 @@ 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 }}, + match (&self, &other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.clone()-b.clone() }}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {self - Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(&self) - other}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a.clone()-b.clone() }}, } } } 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}, + match (&mut *self, &other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a -= b.clone()}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {*self -= Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {*self = Quantity::float_from_rat(self) - other }, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a -= b.clone()}, } } } @@ -325,47 +214,46 @@ 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 }}, + match (&self, &other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.clone()*b.clone() }}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {self * Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(&self) * self}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a.clone()*b.clone() }}, } } } 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}, + match (&mut *self, &other) { + (Quantity::Float{v: a}, Quantity::Float{v:b}) => {*a *= b.clone()}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {*self *= Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {*self = Quantity::float_from_rat(self) * other }, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a *= b.clone()}, } } } - 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 }}, + match (&self, &other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.clone()/b.clone() }}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {self / Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(&self) / other}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational{ v: a.clone()/b.clone() }}, } } } 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}, + match (&mut *self, &other) { + (Quantity::Float{v: a}, Quantity::Float{v: ref b}) => {*a /= b.clone()}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {*self /= Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {*self = Quantity::float_from_rat(self) / other }, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {*a /= b.clone()}, } } } @@ -373,12 +261,12 @@ impl DivAssign for Quantity where { 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 }}, + fn rem(self, other: Quantity) -> Self::Output { + match (&self, &other) { + (Quantity::Float{v:a}, Quantity::Float{v:b}) => {Quantity::Float{ v: a.clone()%b.clone() }}, + (Quantity::Float{ .. }, Quantity::Rational{ .. }) => {self % Quantity::float_from_rat(&other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(&self) % other}, + (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {Quantity::Rational { v: a.clone()%b.clone() }}, } } } @@ -387,8 +275,8 @@ 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::Float{ .. }, Quantity::Rational{ .. }) => {*self == Quantity::float_from_rat(other)}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(self) == *other}, (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {a == b}, } } @@ -398,13 +286,9 @@ 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::Float{ .. }, Quantity::Rational{ .. }) => {(*self).partial_cmp(&Quantity::float_from_rat(other))}, + (Quantity::Rational{ .. }, Quantity::Float{ .. }) => {Quantity::float_from_rat(self).partial_cmp(other)}, (Quantity::Rational{v:a}, Quantity::Rational{v:b}) => {a.partial_cmp(b)}, } } -} - - - - +} \ No newline at end of file diff --git a/src/quantity/rationalq.rs b/src/quantity/rationalq.rs index 9f6ea23..ae3fcc3 100644 --- a/src/quantity/rationalq.rs +++ b/src/quantity/rationalq.rs @@ -1,5 +1,3 @@ -use rug::Float; -use rug::ops::Pow; use rug::Rational; use rug::Integer; @@ -15,20 +13,19 @@ use std::cmp::Ordering; use crate::quantity::Quantity; -use crate::quantity::FLOAT_PRECISION; +use crate::quantity::QuantBase; +use crate::quantity::RationalBase; -macro_rules! rational { - ( $x:expr ) => { - Quantity::Rational { v: RationalQ { - val : $x - }} - }; +macro_rules! wraprat { + ( $x:expr ) => { Quantity::Rational{v: $x} } } -macro_rules! float { - ( $x:expr ) => { - Quantity::Float { v: $x } - }; +macro_rules! float_foward { + ( $x:ident ) => { + fn $x(&self) -> Quantity { + Quantity::float_from_rat(&wraprat!(self.clone())).$x() + } + } } #[derive(Debug)] @@ -37,94 +34,138 @@ pub struct RationalQ where { pub val: Rational } -impl ToString for RationalQ { +/* +fn to_string_radix(&self, radix: i32, num_digits: Option) -> String { + self.to_float().to_string_radix(radix, num_digits) +} + +fn to_sign_string_exp(&self, radix: i32, num_digits: Option) -> (bool, String, Option) { + self.to_float().to_sign_string_exp(radix, num_digits) +} +*/ + +impl ToString for RationalQ{ fn to_string(&self) -> String { - self.to_float().to_string() + let v = Quantity::float_from_rat(&wraprat!(self.clone())); + return v.to_string(); } } -impl RationalQ { - pub fn new(top: i64, bot: i64) -> RationalQ { +impl QuantBase for RationalQ { + + fn fract(&self) -> Quantity { + wraprat!(RationalQ{val: self.val.clone().fract_floor(Integer::new()).0}) + } + + fn is_zero(&self) -> bool {self.val == Rational::from((0,1))} + fn is_negative(&self) -> bool { self.val.clone().signum() == -1 } + fn is_positive(&self) -> bool { self.val.clone().signum() == 1 } + + fn abs(&self) -> Quantity {wraprat!(RationalQ{val: self.val.clone().abs()})} + fn floor(&self) -> Quantity {wraprat!(RationalQ{val: self.val.clone().floor()})} + fn ceil(&self) -> Quantity {wraprat!(RationalQ{val: self.val.clone().ceil()})} + fn round(&self) -> Quantity {wraprat!(RationalQ{val: self.val.clone().round()})} + + float_foward!(sin); + float_foward!(cos); + float_foward!(tan); + float_foward!(asin); + float_foward!(acos); + float_foward!(atan); + + float_foward!(sinh); + float_foward!(cosh); + float_foward!(tanh); + float_foward!(asinh); + float_foward!(acosh); + float_foward!(atanh); + + float_foward!(exp); + float_foward!(ln); + float_foward!(log10); + float_foward!(log2); + + fn log(&self, base: Quantity) -> Quantity { + Quantity::float_from_rat(&wraprat!(self.clone())).log10() / base.log10() + } + + fn pow(&self, base: Quantity) -> Quantity { + Quantity::float_from_rat(&wraprat!(self.clone())).pow(base) + } + +} + +impl RationalBase for RationalQ { + fn from_frac(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 { + 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 from_string(s: &str) -> Option { - let v = Rational::from_str_radix(s, 10); - let v = match v { + fn from_string(s: &str) -> Option { + // Scientific notation + let mut sci = s.split("e"); + let num = sci.next().unwrap(); + let exp = sci.next(); + + let exp = if exp.is_some() { + let r = exp.unwrap().parse::(); + match r { + Ok(x) => x, + Err(_) => return None + } + } else {0isize}; + + // Split integer and decimal parts + let mut dec = num.split("."); + let a = dec.next().unwrap(); + let b = dec.next(); + let b = if b.is_some() {b.unwrap()} else {""}; + + // Error conditions + if { + dec.next().is_some() || // We should have at most one `.` + sci.next().is_some() || // We should have at most one `e` + a.len() == 0 // We need something in the numerator + } { return None; } + + let s: String; + if exp < 0 { + let exp: usize = (-exp).try_into().unwrap(); + s = format!("{a}{b}/1{}", "0".repeat(b.len() + exp)); + } else if exp > 0 { + let exp: usize = exp.try_into().unwrap(); + s = format!( + "{a}{b}{}/1{}", + "0".repeat(exp), + "0".repeat(b.len()) + ); + } else { // exp == 0 + s = format!("{a}{b}/1{}", "0".repeat(b.len())); + }; + + + // From fraction string + let r = Rational::from_str_radix(&s, 10); + let r = match r { Ok(x) => x, Err(_) => return None }; - return Some(RationalQ{ val: v }); + + return Some(RationalQ{val: r}); + } - 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 is_negative(&self) -> bool { self.val.clone().signum() == -1 } - pub fn is_positive(&self) -> bool { self.val.clone().signum() == 1 } - - pub fn exp(&self) -> Quantity {float!(self.to_float().exp())} - - 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; diff --git a/src/tokens/function.rs b/src/tokens/function.rs index 4efd14a..28a1bd9 100644 --- a/src/tokens/function.rs +++ b/src/tokens/function.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; use crate::tokens::Token; use crate::tokens::Operator; - +use crate::quantity::QuantBase; #[derive(Debug)] #[derive(Copy, Clone)] diff --git a/src/tokens/operator.rs b/src/tokens/operator.rs index 13d2de8..700bbe2 100644 --- a/src/tokens/operator.rs +++ b/src/tokens/operator.rs @@ -4,6 +4,7 @@ use std::cmp::Ordering; use crate::tokens::Token; use crate::tokens::Function; use crate::quantity::Quantity; +use crate::quantity::QuantBase; /// Operator types, in order of increasing priority. #[derive(Debug)]