From 5de314e11c5049cfc839eb421ee238037512b5f9 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Apr 2023 13:36:10 -0700 Subject: [PATCH] Fixed unit conversion --- src/quantity/quantity.rs | 99 +++++++++++++++++++++++++++-------- src/quantity/unit/freeunit.rs | 24 ++------- src/quantity/unit/unit.rs | 58 +++++--------------- src/tokens/operator.rs | 4 +- 4 files changed, 96 insertions(+), 89 deletions(-) diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs index e0e090d..10dd277 100644 --- a/src/quantity/quantity.rs +++ b/src/quantity/quantity.rs @@ -94,14 +94,12 @@ impl Quantity { pub fn convert_to(self, other: Quantity) -> Option { + if !self.unit.compatible_with(&other.unit) { return None; } + let fa = self.unit.to_base_factor(); let fb = other.unit.to_base_factor(); - let r = self * fa / fb; - // If this didn't work, units are incompatible - if r.unit != other.unit { return None; }; - - return Some(r); + return Some(self * fa / fb) } } @@ -167,6 +165,33 @@ impl Quantity { } +impl Quantity { + pub fn mul_no_convert(self, other: Self) -> Self { + Quantity { + scalar: self.scalar * other.scalar, + unit: self.unit * other.unit + } + } + + pub fn mul_assign_no_convert(&mut self, other: Self) { + self.scalar *= other.scalar; + self.unit *= other.unit; + } + + pub fn div_no_convert(self, other: Self) -> Self { + Quantity { + scalar: self.scalar / other.scalar, + unit: self.unit / other.unit + } + } + + pub fn div_assign_no_convert(&mut self, other: Self) { + self.scalar *= other.scalar; + self.unit *= other.unit; + } +} + + impl Neg for Quantity where { type Output = Self; @@ -182,10 +207,10 @@ impl Add for Quantity { type Output = Self; fn add(self, other: Self) -> Self::Output { - if self.unit != other.unit { panic!() } + if !self.unit.compatible_with(&other.unit) { panic!() } let mut o = other; - if !o.unit.prefixes_match(&self.unit) { + if self.unit != o.unit { o = o.convert_to(self.clone()).unwrap(); } @@ -198,10 +223,10 @@ impl Add for Quantity { impl AddAssign for Quantity where { fn add_assign(&mut self, other: Self) { - if self.unit != other.unit { panic!() } + if !self.unit.compatible_with(&other.unit) { panic!() } let mut o = other; - if !o.unit.prefixes_match(&self.unit) { + if self.unit != o.unit { o = o.convert_to(self.clone()).unwrap(); } @@ -213,10 +238,10 @@ impl Sub for Quantity { type Output = Self; fn sub(self, other: Self) -> Self::Output { - if self.unit != other.unit { panic!() } + if !self.unit.compatible_with(&other.unit) { panic!() } let mut o = other; - if !o.unit.prefixes_match(&self.unit) { + if self.unit != o.unit { o = o.convert_to(self.clone()).unwrap(); } @@ -229,10 +254,10 @@ impl Sub for Quantity { impl SubAssign for Quantity where { fn sub_assign(&mut self, other: Self) { - if self.unit != other.unit { panic!() } + if !self.unit.compatible_with(&other.unit) { panic!() } let mut o = other; - if !o.unit.prefixes_match(&self.unit) { + if self.unit != o.unit { o = o.convert_to(self.clone()).unwrap(); } @@ -240,26 +265,38 @@ impl SubAssign for Quantity where { } } + impl Mul for Quantity { type Output = Self; fn mul(self, other: Self) -> Self::Output { - let f = self.unit.match_prefix_factor(&other.unit); + let mut o = other; + if self.unit != o.unit { + if self.unit.compatible_with(&o.unit) { + o = o.convert_to(self.clone()).unwrap() + } + } Quantity { - scalar: self.scalar * other.scalar * f.scalar, - unit: self.unit * other.unit * f.unit, + scalar: self.scalar * o.scalar, + unit: self.unit * o.unit } } } impl MulAssign for Quantity where { fn mul_assign(&mut self, other: Self) { - let f = self.unit.match_prefix_factor(&other.unit); - self.scalar *= other.scalar * f.scalar; - self.unit *= other.unit * f.unit; + let mut o = other; + if self.unit != o.unit { + if o.unit.compatible_with(&self.unit) { + o = o.convert_to(self.clone()).unwrap() + } + } + + self.scalar *= o.scalar; + self.unit *= o.unit; } } @@ -267,17 +304,33 @@ impl Div for Quantity { type Output = Self; fn div(self, other: Self) -> Self::Output { + + let mut o = other; + if self.unit != o.unit { + if self.unit.compatible_with(&o.unit) { + o = o.convert_to(self.clone()).unwrap() + } + } + Quantity { - scalar: self.scalar / other.scalar, - unit: self.unit / other.unit + scalar: self.scalar / o.scalar, + unit: self.unit / o.unit } } } impl DivAssign for Quantity where { fn div_assign(&mut self, other: Self) { - self.scalar /= other.scalar; - self.unit /= other.unit; + + let mut o = other; + if self.unit != o.unit { + if self.unit.compatible_with(&o.unit) { + o = o.convert_to(self.clone()).unwrap() + } + } + + self.scalar /= o.scalar; + self.unit /= o.unit; } } diff --git a/src/quantity/unit/freeunit.rs b/src/quantity/unit/freeunit.rs index 6fd4bcf..8567e86 100644 --- a/src/quantity/unit/freeunit.rs +++ b/src/quantity/unit/freeunit.rs @@ -1,4 +1,4 @@ -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use crate::quantity::Scalar; use crate::quantity::Quantity; @@ -8,26 +8,15 @@ use super::Unit; use super::unit_db; +#[derive(Hash)] #[derive(Debug)] #[derive(Copy, Clone)] +#[derive(PartialEq, Eq)] pub struct FreeUnit { pub (in super) base: UnitBase, pub (in super) prefix: Prefix } -impl Hash for FreeUnit { - fn hash(&self, state: &mut H) { - self.base.hash(state); - } -} - -impl Eq for FreeUnit {} -impl PartialEq for FreeUnit { - fn eq(&self, other: &Self) -> bool { - self.base.eq(&other.base) - } -} - macro_rules! unpack_base_factor { ( @@ -83,11 +72,6 @@ impl FreeUnit { pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = prefix; } pub fn get_prefix(&self) -> Prefix { self.prefix } - pub fn same_with_prefix(&self, other: &FreeUnit) -> bool { - self.base.eq(&other.base) && self.prefix.eq(&other.prefix) - } - - pub fn to_base_factor(&self) -> Quantity { let q = unit_db!(self.base, unpack_base_factor); @@ -96,7 +80,7 @@ impl FreeUnit { let mut p = self.prefix.to_ratio(); p.insert_unit(FreeUnit::from_base(self.base), Scalar::new_rational(1f64).unwrap()); p.insert_unit(FreeUnit::from_base_prefix(self.base, self.prefix), Scalar::new_rational(-1f64).unwrap()); - q *= p; + q.mul_assign_no_convert(p); return q; } diff --git a/src/quantity/unit/unit.rs b/src/quantity/unit/unit.rs index 84821e7..25b9e5d 100644 --- a/src/quantity/unit/unit.rs +++ b/src/quantity/unit/unit.rs @@ -97,47 +97,12 @@ impl Unit { return n; } - pub fn prefixes_match(&self, other: &Unit) -> bool { - let v = self.get_val(); - for (u, _) in other.get_val() { - let k = v.get_key_value(u); - - if k.is_some() { - let k = k.unwrap().0; - if !u.same_with_prefix(k) { return false; } - } - } - return true; + pub fn compatible_with(&self, other: &Unit) -> bool { + let s = self.clone() * self.to_base_factor().unit; + let o = other.clone() * other.to_base_factor().unit; + return o == s; } - pub fn match_prefix_factor(&self, other: &Unit) -> Quantity { - let mut f = Quantity::new_rational(1f64).unwrap(); - - let v = self.get_val(); - for (ou, op) in other.get_val() { - let k = v.get_key_value(ou); - - if k.is_some() { - let (su, _) = k.unwrap(); - - // Conversion factor ou -> basic - let mut p = ou.prefix.to_ratio(); - p.insert_unit(FreeUnit::from_base(ou.base), Scalar::new_rational(1f64).unwrap()); - p.insert_unit(FreeUnit::from_base_prefix(ou.base, ou.prefix), Scalar::new_rational(-1f64).unwrap()); - - // Conversion factor su -> basic - let mut q = su.prefix.to_ratio(); - q.insert_unit(FreeUnit::from_base(su.base), Scalar::new_rational(1f64).unwrap()); - q.insert_unit(FreeUnit::from_base_prefix(su.base, su.prefix), Scalar::new_rational(-1f64).unwrap()); - - f = f * (p / q).pow(Quantity::from_scalar(op.clone())); - } - } - - return f; - } - - pub fn insert(&mut self, u: FreeUnit, p: Scalar) { let v = self.get_val_mut(); match v.get_mut(&u) { @@ -165,15 +130,13 @@ impl Unit { for (u, p) in self.get_val().iter() { let b = u.to_base_factor(); - q *= b.pow(Quantity::from_scalar(p.clone())); + q.mul_assign_no_convert(b.pow(Quantity::from_scalar(p.clone()))); } return q; } } - - impl Unit { pub fn from_string(s: &str) -> Option { macro_rules! unpack_fromstring { @@ -220,8 +183,6 @@ impl Unit { } - - impl PartialEq for Unit { fn eq(&self, other: &Self) -> bool { let v = self.get_val(); @@ -231,6 +192,15 @@ impl PartialEq for Unit { None => { return false; } }; } + + let v = other.get_val(); + for (u, p) in self.get_val() { + match v.get(u) { + Some(i) => { if i != p { return false; } }, + None => { return false; } + }; + } + return true; } } diff --git a/src/tokens/operator.rs b/src/tokens/operator.rs index bee9cad..e6cf835 100644 --- a/src/tokens/operator.rs +++ b/src/tokens/operator.rs @@ -383,7 +383,7 @@ impl Operator{ let j = args[i].as_number(); if let Token::Number(v) = j { - if sum.unit() != v.unit() { + if !sum.unit.compatible_with(&v.unit) { return Err(EvalError::IncompatibleUnit); } @@ -456,7 +456,7 @@ impl Operator{ if let Token::Number(va) = a { if let Token::Number(vb) = b { - if va.unit() != vb.unit() { + if !vb.unitless() { return Err(EvalError::IncompatibleUnit); }