Fixed unit conversion

pull/2/head
Mark 2023-04-13 13:36:10 -07:00
parent 7560b7a931
commit 5de314e11c
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
4 changed files with 96 additions and 89 deletions

View File

@ -94,14 +94,12 @@ impl Quantity {
pub fn convert_to(self, other: Quantity) -> Option<Quantity> { pub fn convert_to(self, other: Quantity) -> Option<Quantity> {
if !self.unit.compatible_with(&other.unit) { return None; }
let fa = self.unit.to_base_factor(); let fa = self.unit.to_base_factor();
let fb = other.unit.to_base_factor(); let fb = other.unit.to_base_factor();
let r = self * fa / fb;
// If this didn't work, units are incompatible return Some(self * fa / fb)
if r.unit != other.unit { return None; };
return Some(r);
} }
} }
@ -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 { impl Neg for Quantity where {
type Output = Self; type Output = Self;
@ -182,10 +207,10 @@ impl Add for Quantity {
type Output = Self; type Output = Self;
fn add(self, other: Self) -> Self::Output { 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; let mut o = other;
if !o.unit.prefixes_match(&self.unit) { if self.unit != o.unit {
o = o.convert_to(self.clone()).unwrap(); o = o.convert_to(self.clone()).unwrap();
} }
@ -198,10 +223,10 @@ impl Add for Quantity {
impl AddAssign for Quantity where { impl AddAssign for Quantity where {
fn add_assign(&mut self, other: Self) { 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; let mut o = other;
if !o.unit.prefixes_match(&self.unit) { if self.unit != o.unit {
o = o.convert_to(self.clone()).unwrap(); o = o.convert_to(self.clone()).unwrap();
} }
@ -213,10 +238,10 @@ impl Sub for Quantity {
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> Self::Output { 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; let mut o = other;
if !o.unit.prefixes_match(&self.unit) { if self.unit != o.unit {
o = o.convert_to(self.clone()).unwrap(); o = o.convert_to(self.clone()).unwrap();
} }
@ -229,10 +254,10 @@ impl Sub for Quantity {
impl SubAssign for Quantity where { impl SubAssign for Quantity where {
fn sub_assign(&mut self, other: Self) { 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; let mut o = other;
if !o.unit.prefixes_match(&self.unit) { if self.unit != o.unit {
o = o.convert_to(self.clone()).unwrap(); o = o.convert_to(self.clone()).unwrap();
} }
@ -240,26 +265,38 @@ impl SubAssign for Quantity where {
} }
} }
impl Mul for Quantity { impl Mul for Quantity {
type Output = Self; type Output = Self;
fn mul(self, other: Self) -> Self::Output { 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 { Quantity {
scalar: self.scalar * other.scalar * f.scalar, scalar: self.scalar * o.scalar,
unit: self.unit * other.unit * f.unit, unit: self.unit * o.unit
} }
} }
} }
impl MulAssign for Quantity where { impl MulAssign for Quantity where {
fn mul_assign(&mut self, other: Self) { fn mul_assign(&mut self, other: Self) {
let f = self.unit.match_prefix_factor(&other.unit);
self.scalar *= other.scalar * f.scalar; let mut o = other;
self.unit *= other.unit * f.unit; 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; type Output = Self;
fn div(self, other: Self) -> Self::Output { 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 { Quantity {
scalar: self.scalar / other.scalar, scalar: self.scalar / o.scalar,
unit: self.unit / other.unit unit: self.unit / o.unit
} }
} }
} }
impl DivAssign for Quantity where { impl DivAssign for Quantity where {
fn div_assign(&mut self, other: Self) { 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;
} }
} }

View File

@ -1,4 +1,4 @@
use std::hash::{Hash, Hasher}; use std::hash::Hash;
use crate::quantity::Scalar; use crate::quantity::Scalar;
use crate::quantity::Quantity; use crate::quantity::Quantity;
@ -8,26 +8,15 @@ use super::Unit;
use super::unit_db; use super::unit_db;
#[derive(Hash)]
#[derive(Debug)] #[derive(Debug)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[derive(PartialEq, Eq)]
pub struct FreeUnit { pub struct FreeUnit {
pub (in super) base: UnitBase, pub (in super) base: UnitBase,
pub (in super) prefix: Prefix pub (in super) prefix: Prefix
} }
impl Hash for FreeUnit {
fn hash<H: Hasher>(&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 { macro_rules! unpack_base_factor {
( (
@ -83,11 +72,6 @@ impl FreeUnit {
pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = prefix; } pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = prefix; }
pub fn get_prefix(&self) -> Prefix { self.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 { pub fn to_base_factor(&self) -> Quantity {
let q = unit_db!(self.base, unpack_base_factor); let q = unit_db!(self.base, unpack_base_factor);
@ -96,7 +80,7 @@ impl FreeUnit {
let mut p = self.prefix.to_ratio(); 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(self.base), Scalar::new_rational(1f64).unwrap());
p.insert_unit(FreeUnit::from_base_prefix(self.base, self.prefix), 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; return q;
} }

View File

@ -97,47 +97,12 @@ impl Unit {
return n; return n;
} }
pub fn prefixes_match(&self, other: &Unit) -> bool { pub fn compatible_with(&self, other: &Unit) -> bool {
let v = self.get_val(); let s = self.clone() * self.to_base_factor().unit;
for (u, _) in other.get_val() { let o = other.clone() * other.to_base_factor().unit;
let k = v.get_key_value(u); return o == s;
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.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) { pub fn insert(&mut self, u: FreeUnit, p: Scalar) {
let v = self.get_val_mut(); let v = self.get_val_mut();
match v.get_mut(&u) { match v.get_mut(&u) {
@ -165,15 +130,13 @@ impl Unit {
for (u, p) in self.get_val().iter() { for (u, p) in self.get_val().iter() {
let b = u.to_base_factor(); 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; return q;
} }
} }
impl Unit { impl Unit {
pub fn from_string(s: &str) -> Option<Quantity> { pub fn from_string(s: &str) -> Option<Quantity> {
macro_rules! unpack_fromstring { macro_rules! unpack_fromstring {
@ -220,8 +183,6 @@ impl Unit {
} }
impl PartialEq for Unit { impl PartialEq for Unit {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
let v = self.get_val(); let v = self.get_val();
@ -231,6 +192,15 @@ impl PartialEq for Unit {
None => { return false; } 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; return true;
} }
} }

View File

@ -383,7 +383,7 @@ impl Operator{
let j = args[i].as_number(); let j = args[i].as_number();
if let Token::Number(v) = j { if let Token::Number(v) = j {
if sum.unit() != v.unit() { if !sum.unit.compatible_with(&v.unit) {
return Err(EvalError::IncompatibleUnit); return Err(EvalError::IncompatibleUnit);
} }
@ -456,7 +456,7 @@ impl Operator{
if let Token::Number(va) = a { if let Token::Number(va) = a {
if let Token::Number(vb) = b { if let Token::Number(vb) = b {
if va.unit() != vb.unit() { if !vb.unitless() {
return Err(EvalError::IncompatibleUnit); return Err(EvalError::IncompatibleUnit);
} }