diff --git a/Cargo.lock b/Cargo.lock index 825a11a..6a952ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "daisy" -version = "0.2.3" +version = "0.2.4" dependencies = [ "cfg-if", "rug", diff --git a/Cargo.toml b/Cargo.toml index 1d9a4df..a93371f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daisy" -version = "0.2.3" +version = "0.2.4" edition = "2021" [profile.release] diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs index 10dd277..147ec68 100644 --- a/src/quantity/quantity.rs +++ b/src/quantity/quantity.rs @@ -99,7 +99,7 @@ impl Quantity { let fa = self.unit.to_base_factor(); let fb = other.unit.to_base_factor(); - return Some(self * fa / fb) + return Some(self.mul_no_convert(fa).div_no_convert(fb)) } } @@ -271,10 +271,16 @@ impl Mul for Quantity { fn mul(self, other: Self) -> Self::Output { + let mut o = other; if self.unit != o.unit { - if self.unit.compatible_with(&o.unit) { + if o.unit.compatible_with(&self.unit) { o = o.convert_to(self.clone()).unwrap() + } else { + let cf = self.unit.common_factor(&o.unit); + if let Some(f) = cf { + o = o.convert_to(f).unwrap(); + } } } @@ -288,10 +294,16 @@ impl Mul for Quantity { impl MulAssign for Quantity where { fn mul_assign(&mut self, other: Self) { + let mut o = other; if self.unit != o.unit { if o.unit.compatible_with(&self.unit) { o = o.convert_to(self.clone()).unwrap() + } else { + let cf = self.unit.common_factor(&o.unit); + if let Some(f) = cf { + o = o.convert_to(f).unwrap(); + } } } @@ -307,8 +319,13 @@ impl Div for Quantity { let mut o = other; if self.unit != o.unit { - if self.unit.compatible_with(&o.unit) { + if o.unit.compatible_with(&self.unit) { o = o.convert_to(self.clone()).unwrap() + } else { + let cf = self.unit.common_factor(&o.unit); + if let Some(f) = cf { + o = o.convert_to(f).unwrap(); + } } } @@ -324,8 +341,13 @@ impl DivAssign for Quantity where { let mut o = other; if self.unit != o.unit { - if self.unit.compatible_with(&o.unit) { + if o.unit.compatible_with(&self.unit) { o = o.convert_to(self.clone()).unwrap() + } else { + let cf = self.unit.common_factor(&o.unit); + if let Some(f) = cf { + o = o.convert_to(f).unwrap(); + } } } diff --git a/src/quantity/unit/freeunit.rs b/src/quantity/unit/freeunit.rs index 63ac88c..8abee67 100644 --- a/src/quantity/unit/freeunit.rs +++ b/src/quantity/unit/freeunit.rs @@ -8,8 +8,8 @@ use super::Unit; use super::unit_db; -#[derive(Hash)] #[derive(Debug)] +#[derive(Hash)] #[derive(Copy, Clone)] #[derive(PartialEq, Eq)] pub struct FreeUnit { @@ -18,6 +18,25 @@ pub struct FreeUnit { } +macro_rules! unpack_string { + ( + $u:expr, $s:expr, + $( $_:expr ),* + ) => { $s }; +} + +impl ToString for FreeUnit { + fn to_string(&self) -> String { + + let s = unit_db!(self.base, unpack_string); + let p = self.prefix.to_string(); + + format!("{p}{s}") + } +} + + + macro_rules! unpack_base_factor { ( $unit:expr, @@ -90,6 +109,8 @@ impl FreeUnit { pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = prefix; } pub fn get_prefix(&self) -> Prefix { self.prefix } + /// Returns a quantity q, so that self * q + /// gives a quantity in base units. pub fn to_base_factor(&self) -> Quantity { let q = unit_db!(self.base, unpack_base_factor); @@ -102,23 +123,17 @@ impl FreeUnit { return q; } -} + // Get this unit in terms of base units + pub fn get_base(&self) -> Quantity { + let q = unit_db!(self.base, unpack_base_factor); + let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap()); + // Don't divide by self + q.insert_unit(FreeUnit::from_base(self.base), Scalar::new_rational(1f64).unwrap()); -macro_rules! unpack_string { - ( - $u:expr, $s:expr, - $( $_:expr ),* - ) => { $s }; -} - -impl ToString for FreeUnit { - fn to_string(&self) -> String { - - let s = unit_db!(self.base, unpack_string); - let p = self.prefix.to_string(); - - format!("{p}{s}") + return q; } } + + diff --git a/src/quantity/unit/unit.rs b/src/quantity/unit/unit.rs index 25b9e5d..93c385b 100644 --- a/src/quantity/unit/unit.rs +++ b/src/quantity/unit/unit.rs @@ -97,12 +97,60 @@ impl Unit { return n; } + // True if base units are the same 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; } + // True if these two units have a common factor + pub fn common_factor(&self, other: &Unit) -> Option { + + if self.unitless() || other.unitless() { return None; } + + + let mut failed = false; + + // What to convert `other` to before multiplying + let mut factor = Quantity::new_rational_from_string("1").unwrap(); + let mut flag; + for (us, _) in self.get_val() { + flag = false; + for (uo, po) in other.get_val() { + if { + us.get_base().unit.compatible_with(&uo.get_base().unit) + } { + factor.insert_unit(us.clone(), po.clone()); + flag = true; + break; + } + } + if !flag { failed = true } + } + + if !failed { return Some(factor);} + + + let mut factor = Quantity::new_rational_from_string("1").unwrap(); + for (uo, po) in other.get_val() { + flag = false; + for (us, _) in self.get_val() { + if { + us.get_base().unit.compatible_with(&uo.get_base().unit) + } { + factor.insert_unit(us.clone(), po.clone()); + flag = true; + break; + } + } + if !flag { return None; } + } + + return Some(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 60e9aa3..d23a477 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -208,4 +208,5 @@ fn complex_units() { good_expr("3280.8 ft", "1km to ft"); good_expr("62.137 mi/h", "100 km/h to mph"); good_expr("20 mi", "10 mph * 2 hours"); + good_expr("120 m", "1 (m/s) * 2 min"); } \ No newline at end of file