Added unit conversion prototype

pull/2/head
Mark 2023-04-11 11:48:15 -07:00
parent 7ecdfae6fc
commit 600c5f76cc
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
4 changed files with 110 additions and 17 deletions

View File

@ -13,7 +13,6 @@ use crate::parser::treeify::treeify;
use crate::parser::find_subs::find_subs; use crate::parser::find_subs::find_subs;
use crate::quantity::Quantity; use crate::quantity::Quantity;
use crate::quantity::Unit;
use crate::tokens::Token; use crate::tokens::Token;

View File

@ -82,6 +82,13 @@ impl Quantity {
}); });
} }
pub fn from_scalar(s: Scalar) -> Quantity {
return Quantity{
v: s,
u: Unit::new()
};
}
pub fn insert_unit(&mut self, ui: BaseUnit, pi: Scalar) { self.u.insert(ui, pi) } pub fn insert_unit(&mut self, ui: BaseUnit, pi: Scalar) { self.u.insert(ui, pi) }
pub fn set_unit(&mut self, u: Unit) { self.u = u; } pub fn set_unit(&mut self, u: Unit) { self.u = u; }
@ -96,6 +103,9 @@ impl Quantity {
"mol" => Some(BaseUnit::Mole), "mol" => Some(BaseUnit::Mole),
"c" => Some(BaseUnit::Candela), "c" => Some(BaseUnit::Candela),
"ft" => Some(BaseUnit::Foot), "ft" => Some(BaseUnit::Foot),
"mile" => Some(BaseUnit::Mile),
"hour" => Some(BaseUnit::Hour),
"min" => Some(BaseUnit::Minute),
_ => { None } _ => { None }
}; };
@ -112,6 +122,17 @@ impl Quantity {
return None; return None;
} }
pub fn convert_to(self, other: Quantity) -> Option<Quantity> {
let fa = self.u.to_base_factor();
let fb = other.u.to_base_factor();
let r = self * fa / fb;
// If this didn't work, units are incompatible
if r.u != other.u { return None; };
return Some(r);
}
} }

View File

@ -21,30 +21,59 @@ pub enum BaseUnit {
Candela, Candela,
Foot, Foot,
Mile,
Minute,
Hour
} }
impl BaseUnit { impl BaseUnit {
pub fn is_base(&self) -> bool {
match self {
BaseUnit::Second
| BaseUnit::Meter
| BaseUnit::Kilogram
| BaseUnit::Ampere
| BaseUnit::Kelvin
| BaseUnit::Mole
| BaseUnit::Candela
=> true,
_ => false
}
}
pub fn to_base(&self) -> Option<Quantity> { pub fn to_base(&self) -> Option<Quantity> {
match self { match self {
// Returns the unit we need to multiply by to get a base
// unit, or `None` if this is already a base unit.
//
// Example:
// 1 foot = 0.3048 m,
// so 1 ft * (0.3084 m / ft) will give meters.
//
// The units here MUST be in terms of base units.
// If they aren't, things will break.
BaseUnit::Foot => Some(Quantity { BaseUnit::Foot => Some(Quantity {
v: Scalar::new_float_from_string("0.3048").unwrap(), v: Scalar::new_float_from_string("0.3048").unwrap(),
u: Unit::from_array(&[(BaseUnit::Meter, Scalar::new_rational(1f64).unwrap())]) u: Unit::from_array(&[
(BaseUnit::Meter, Scalar::new_rational(1f64).unwrap()),
(BaseUnit::Foot, Scalar::new_rational(-1f64).unwrap())
])
}), }),
BaseUnit::Mile => Some(Quantity {
v: Scalar::new_float_from_string("1609").unwrap(),
u: Unit::from_array(&[
(BaseUnit::Meter, Scalar::new_rational(1f64).unwrap()),
(BaseUnit::Mile, Scalar::new_rational(-1f64).unwrap())
])
}),
BaseUnit::Minute => Some(Quantity {
v: Scalar::new_rational_from_string("60").unwrap(),
u: Unit::from_array(&[
(BaseUnit::Second, Scalar::new_rational(1f64).unwrap()),
(BaseUnit::Minute, Scalar::new_rational(-1f64).unwrap())
])
}),
BaseUnit::Hour => Some(Quantity {
v: Scalar::new_rational_from_string("3600").unwrap(),
u: Unit::from_array(&[
(BaseUnit::Second, Scalar::new_rational(1f64).unwrap()),
(BaseUnit::Hour, Scalar::new_rational(-1f64).unwrap())
])
}),
// Only base units should be missing a conversion factor.
_ => None _ => None
} }
} }
@ -88,6 +117,9 @@ impl ToString for Unit {
BaseUnit::Candela => "c", BaseUnit::Candela => "c",
BaseUnit::Foot => "ft", BaseUnit::Foot => "ft",
BaseUnit::Mile => "mile",
BaseUnit::Hour => "hour",
BaseUnit::Minute => "min",
}; };
if *p == Scalar::new_rational(1f64).unwrap() { if *p == Scalar::new_rational(1f64).unwrap() {
@ -158,6 +190,19 @@ impl Unit {
}; };
return u; return u;
} }
pub fn to_base_factor(&self) -> Quantity {
let mut q = Quantity::new_rational(1f64).unwrap();
for (u, p) in self.val.iter() {
let b = u.to_base();
if b.is_some() {
q *= b.unwrap().pow(Quantity::from_scalar(p.clone()));
}
}
return q;
}
} }

View File

@ -13,6 +13,7 @@ use crate::quantity::Quantity;
#[repr(usize)] #[repr(usize)]
pub enum Operator { pub enum Operator {
ModuloLong = 0, // Mod invoked with "mod" ModuloLong = 0, // Mod invoked with "mod"
UnitConvert,
Subtract, Subtract,
Add, Add,
Divide, Divide,
@ -99,6 +100,14 @@ impl Operator {
); );
}, },
Operator::UnitConvert => {
return format!(
"{} to {}",
self.add_parens_to_arg(&args[0]),
self.add_parens_to_arg(&args[1])
);
},
Operator::Modulo => { Operator::Modulo => {
return format!( return format!(
"{} % {}", "{} % {}",
@ -211,6 +220,7 @@ impl Operator {
"i*" => {Some( Operator::ImplicitMultiply )}, "i*" => {Some( Operator::ImplicitMultiply )},
"%" => {Some( Operator::Modulo )}, "%" => {Some( Operator::Modulo )},
"mod" => {Some( Operator::ModuloLong )}, "mod" => {Some( Operator::ModuloLong )},
"to" => {Some( Operator::UnitConvert )},
"^"|"**" => {Some( Operator::Power )}, "^"|"**" => {Some( Operator::Power )},
"!" => {Some( Operator::Factorial )}, "!" => {Some( Operator::Factorial )},
"sqrt"|"rt"|"" => {Some( Operator::Sqrt )}, "sqrt"|"rt"|"" => {Some( Operator::Sqrt )},
@ -327,6 +337,7 @@ impl Operator {
| Operator::Modulo | Operator::Modulo
| Operator::Power | Operator::Power
| Operator::ModuloLong | Operator::ModuloLong
| Operator::UnitConvert
=> { Token::Operator(self, args) }, => { Token::Operator(self, args) },
} }
} }
@ -420,6 +431,23 @@ impl Operator{
} else { panic!(); } } else { panic!(); }
}, },
Operator::UnitConvert
=> {
if args.len() != 2 {panic!()};
let a = args[0].as_number();
let b = args[1].as_number();
if let Token::Number(va) = a {
if let Token::Number(vb) = b {
let n = va.convert_to(vb);
if n.is_none() {
return Err(EvalError::IncompatibleUnit);
}
return Ok(Token::Number(n.unwrap()));
} else { panic!(); }
} else { panic!(); }
},
Operator::Power => { Operator::Power => {
if args.len() != 2 {panic!()}; if args.len() != 2 {panic!()};
let a = args[0].as_number(); let a = args[0].as_number();