From 7b77c49ba4514d267d5725b96988f58da5fccd42 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 28 Jul 2023 10:33:51 -0700 Subject: [PATCH] Fixed unit matching --- src/quantity/quantity.rs | 18 +++++++------- src/quantity/unit/unit.rs | 49 +++++++++++++++++++++++++++++++++++++++ src/tests.rs | 2 ++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs index 24fd39b..2080078 100644 --- a/src/quantity/quantity.rs +++ b/src/quantity/quantity.rs @@ -111,7 +111,6 @@ impl Quantity { return Some(n.mul_no_convert(fa).div_no_convert(fb)) } - pub fn match_units(&mut self, other: &Quantity) { let mut new_units = Quantity::new_rational_from_string("1").unwrap(); @@ -123,14 +122,15 @@ impl Quantity { // Check if `us` matches some unit in `other` for (uo, _) in other.unit.get_val() { - if { - uo.to_base().unit.compatible_with(&us.to_base().unit) - } { - // If it does, convert `us` to `uo` - new_units.insert_unit(uo.clone(), ps.clone()); - flag = true; - break; - } + // Use generalized compatible_with check to match reciprocal units + // (for example, 1Hz * 1 sec.) + let f = uo.to_base().unit.compatible_with_power(&us.to_base().unit); + if f.is_none() { continue; } + let f = f.unwrap(); + + new_units.insert_unit(uo.clone(), ps.clone() * f); + flag = true; + break; } if !flag { // If no unit in `other` matches `us`, don't convert `us` diff --git a/src/quantity/unit/unit.rs b/src/quantity/unit/unit.rs index 073d3c9..b068504 100644 --- a/src/quantity/unit/unit.rs +++ b/src/quantity/unit/unit.rs @@ -162,6 +162,55 @@ impl Unit { return o == s; } + + // True if all base units are the same AND there is a constant factor between their powers. + // This is a generalization of `compatible_with`. `compatible_with` is true iff + // `compatible_with_power` is one. + pub fn compatible_with_power(&self, other: &Unit) -> Option { + let mut flag; + let mut pow_factor: Option = None; + + let sbu = self.to_base().unit; + let obu = other.to_base().unit; + + for (us, ps) in sbu.get_val() { + flag = false; + for (uo, po) in obu.get_val() { + if uo.whole == us.whole { + if pow_factor.is_none() { + pow_factor = Some(po.clone() / ps.clone()); + } else if let Some(ref f) = pow_factor { + if *f != po.clone() / ps.clone() { return None; } + } + + flag = true; + break; + } + } + if !flag { return None; } + } + + pow_factor = None; + for (uo, po) in obu.get_val() { + flag = false; + for (us, ps) in sbu.get_val() { + if uo.whole == us.whole { + if pow_factor.is_none() { + pow_factor = Some(po.clone() / ps.clone()); + } else if let Some(ref f) = pow_factor { + if *f != po.clone() / ps.clone() { return None; } + } + + flag = true; + break; + } + } + if !flag { return None; } + } + + return pow_factor; + } + pub fn insert(&mut self, u: FreeUnit, p: Scalar) { let v = self.get_val_mut(); match v.get_mut(&u) { diff --git a/src/tests.rs b/src/tests.rs index cdd8568..19e94f9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -211,4 +211,6 @@ fn complex_units() { good_expr("20 mi", "10 mph * 2 hours"); good_expr("120 m", "1 (m/s) * 2 min"); good_expr("120 m", "(2 min) * (1 m/s)"); + good_expr("180", "1 Hz * 3 min"); + good_expr("3600", "1 hour * 1 Hz"); } \ No newline at end of file