From 84d2d68aa137b91441ecc49dbcb8847907ffcc0a Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Apr 2023 09:01:18 -0700 Subject: [PATCH] Added conversion between prefixed units --- src/quantity/quantity.rs | 44 ++++++++++++++++---- src/quantity/unit.rs | 89 +++++++++++++++++++++++++++++++++------- 2 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/quantity/quantity.rs b/src/quantity/quantity.rs index c785165..747fe8b 100644 --- a/src/quantity/quantity.rs +++ b/src/quantity/quantity.rs @@ -93,7 +93,6 @@ impl Quantity { pub fn set_unit(&mut self, u: Unit) { self.u = u; } - pub fn convert_to(self, other: Quantity) -> Option { let fa = self.u.to_base_factor(); let fb = other.u.to_base_factor(); @@ -185,8 +184,13 @@ impl Add for Quantity { fn add(self, other: Self) -> Self::Output { if self.u != other.u { panic!() } + let mut o = other; + if !o.u.prefixes_match(&self.u) { + o = o.convert_to(self.clone()).unwrap(); + } + Quantity { - v: self.v + other.v, + v: self.v + o.v, u: self.u } } @@ -195,7 +199,13 @@ impl Add for Quantity { impl AddAssign for Quantity where { fn add_assign(&mut self, other: Self) { if self.u != other.u { panic!() } - self.v += other.v + + let mut o = other; + if !o.u.prefixes_match(&self.u) { + o = o.convert_to(self.clone()).unwrap(); + } + + self.v += o.v } } @@ -205,8 +215,13 @@ impl Sub for Quantity { fn sub(self, other: Self) -> Self::Output { if self.u != other.u { panic!() } + let mut o = other; + if !o.u.prefixes_match(&self.u) { + o = o.convert_to(self.clone()).unwrap(); + } + Quantity { - v: self.v - other.v, + v: self.v - o.v, u: self.u } } @@ -215,7 +230,13 @@ impl Sub for Quantity { impl SubAssign for Quantity where { fn sub_assign(&mut self, other: Self) { if self.u != other.u { panic!() } - self.v -= other.v + + let mut o = other; + if !o.u.prefixes_match(&self.u) { + o = o.convert_to(self.clone()).unwrap(); + } + + self.v -= o.v } } @@ -223,17 +244,22 @@ impl Mul for Quantity { type Output = Self; fn mul(self, other: Self) -> Self::Output { + + let f = self.u.match_prefix_factor(&other.u); + Quantity { - v: self.v * other.v, - u: self.u * other.u + v: self.v * other.v * f.v, + u: self.u * other.u * f.u, } } } impl MulAssign for Quantity where { fn mul_assign(&mut self, other: Self) { - self.v *= other.v; - self.u *= other.u; + let f = self.u.match_prefix_factor(&other.u); + + self.v *= other.v * f.v; + self.u *= other.u * f.u; } } diff --git a/src/quantity/unit.rs b/src/quantity/unit.rs index b78023b..3cff12e 100644 --- a/src/quantity/unit.rs +++ b/src/quantity/unit.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::f32::consts::E; +use std::hash::{Hash, Hasher}; use std::ops::{ Mul, Div, MulAssign, DivAssign @@ -142,15 +142,27 @@ impl ToString for Prefix { } -#[derive(Hash)] #[derive(Debug)] #[derive(Copy, Clone)] -#[derive(Eq, PartialEq)] pub struct FreeUnit { u: UnitBase, p: Prefix } +impl Hash for FreeUnit { + fn hash(&self, state: &mut H) { + self.u.hash(state); + } +} + +impl Eq for FreeUnit {} +impl PartialEq for FreeUnit { + fn eq(&self, other: &Self) -> bool { + self.u.eq(&other.u) + } +} + + macro_rules! quick_base_factor { (float, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => { Some(Quantity { @@ -180,19 +192,17 @@ macro_rules! quick_base_factor { impl FreeUnit { pub fn from_base(u: UnitBase) -> FreeUnit { - return FreeUnit { - u, - p: Prefix::None - } + return FreeUnit { u, p: Prefix::None } } - pub fn from_base_prefix(u: UnitBase, p: Prefix) -> FreeUnit { - return FreeUnit { u, p } + pub fn from_base_prefix(u: UnitBase, p: Prefix) -> FreeUnit { FreeUnit {u, p} } + pub fn set_prefix(&mut self, p: Prefix) { self.p = p; } + pub fn get_prefix(&self) -> Prefix { self.p } + + pub fn same_with_prefix(&self, other: &FreeUnit) -> bool { + self.u.eq(&other.u) && self.p.eq(&other.p) } - pub fn set_prefix(&mut self, p: Prefix) { - self.p = p; - } pub fn to_base_factor(&self) -> Quantity { let q = match self.u { @@ -360,6 +370,46 @@ 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 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.p.to_ratio(); + p.insert_unit(FreeUnit::from_base(ou.u), Scalar::new_rational(1f64).unwrap()); + p.insert_unit(FreeUnit::from_base_prefix(ou.u, ou.p), Scalar::new_rational(-1f64).unwrap()); + + // Conversion factor su -> basic + let mut q = su.p.to_ratio(); + q.insert_unit(FreeUnit::from_base(su.u), Scalar::new_rational(1f64).unwrap()); + q.insert_unit(FreeUnit::from_base_prefix(su.u, su.p), Scalar::new_rational(-1f64).unwrap()); + + f = f * (p / q).pow(Quantity::from_scalar(op.clone())); + } + } + + return f; + } + pub fn unitless(&self) -> bool { self.get_val().len() == 0 } pub fn insert(&mut self, u: FreeUnit, p: Scalar) { @@ -419,8 +469,19 @@ impl Unit { if b.is_none() { if s == "kg" { let mut u = Unit::new(); - let mut b = FreeUnit::from_base(UnitBase::Gram); - b.set_prefix(Prefix::Kilo); + let b = FreeUnit::from_base_prefix(UnitBase::Gram, Prefix::Kilo); + + u.insert(b, Scalar::new_rational(1f64).unwrap()); + + let mut q = Quantity::new_rational(1f64).unwrap(); + q.set_unit(u); + + return Some(q); + } + + if s == "km" { + let mut u = Unit::new(); + let b = FreeUnit::from_base_prefix(UnitBase::Meter, Prefix::Kilo); u.insert(b, Scalar::new_rational(1f64).unwrap());