Added prefix foundation

pull/2/head
Mark 2023-04-13 08:05:52 -07:00
parent 600c5f76cc
commit 1f0f7e0ac9
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
4 changed files with 323 additions and 109 deletions

View File

@ -13,6 +13,7 @@ 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;
@ -112,8 +113,7 @@ impl PreToken {
return Ok(Token::Constant(a, b)); return Ok(Token::Constant(a, b));
} }
let c = Quantity::from_unit_string(&s); let c = Unit::from_string(&s);
if c.is_some() { return Ok(Token::Number(c.unwrap())); } if c.is_some() { return Ok(Token::Number(c.unwrap())); }
return Err((l, ParserError::Undefined(s))); return Err((l, ParserError::Undefined(s)));

View File

@ -21,7 +21,7 @@ pub(in crate::quantity) use crate::quantity::scalar::Scalar;
mod unit; mod unit;
pub use crate::quantity::unit::Unit; pub use crate::quantity::unit::Unit;
pub use crate::quantity::unit::BaseUnit; pub use crate::quantity::unit::FreeUnit;
mod quantity; mod quantity;
pub use crate::quantity::quantity::Quantity; pub use crate::quantity::quantity::Quantity;

View File

@ -8,7 +8,7 @@ use std::ops::{
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::quantity::Unit; use crate::quantity::Unit;
use crate::quantity::BaseUnit; use crate::quantity::FreeUnit;
use crate::quantity::Scalar; use crate::quantity::Scalar;
@ -89,38 +89,10 @@ impl Quantity {
}; };
} }
pub fn insert_unit(&mut self, ui: BaseUnit, pi: Scalar) { self.u.insert(ui, pi) } pub fn insert_unit(&mut self, ui: FreeUnit, 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; }
pub fn from_unit_string(s: &str) -> Option<Quantity> {
// Base Units
let b = match s {
"m" => Some(BaseUnit::Meter),
"s" => Some(BaseUnit::Second),
"kg" => Some(BaseUnit::Kilogram),
"a" => Some(BaseUnit::Ampere),
"k" => Some(BaseUnit::Kelvin),
"mol" => Some(BaseUnit::Mole),
"c" => Some(BaseUnit::Candela),
"ft" => Some(BaseUnit::Foot),
"mile" => Some(BaseUnit::Mile),
"hour" => Some(BaseUnit::Hour),
"min" => Some(BaseUnit::Minute),
_ => { None }
};
if b.is_some() {
let mut u = Unit::new();
u.insert(b.unwrap(), Scalar::new_rational(1f64).unwrap());
let mut q = Quantity::new_rational(1f64).unwrap();
q.set_unit(u);
return Some(q);
};
return None;
}
pub fn convert_to(self, other: Quantity) -> Option<Quantity> { pub fn convert_to(self, other: Quantity) -> Option<Quantity> {
let fa = self.u.to_base_factor(); let fa = self.u.to_base_factor();
@ -132,7 +104,6 @@ impl Quantity {
return Some(r); return Some(r);
} }
} }

View File

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::f32::consts::E;
use std::ops::{ use std::ops::{
Mul, Div, Mul, Div,
MulAssign, DivAssign MulAssign, DivAssign
@ -7,29 +8,194 @@ use std::ops::{
use super::Scalar; use super::Scalar;
use super::Quantity; use super::Quantity;
#[derive(Debug)]
#[derive(Hash)] #[derive(Hash)]
#[derive(Eq, PartialEq)] #[derive(Debug)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum BaseUnit { #[derive(Eq, PartialEq)]
pub enum UnitBase {
// Base Units
Second, Second,
Meter, Meter,
Kilogram, Gram, // Technically kilogram, but that messes with prefix architecture.
Ampere, Ampere,
Kelvin, Kelvin,
Mole, Mole,
Candela, Candela,
// Length units
Inch,
Foot, Foot,
Mile, Mile,
// Time units
Minute, Minute,
Hour Hour,
Day,
//Week,
//Month,
} }
impl BaseUnit { #[derive(Hash)]
pub fn to_base(&self) -> Option<Quantity> { #[derive(Debug)]
match self { #[derive(Copy, Clone)]
#[derive(Eq, PartialEq)]
pub enum Prefix {
None,
Quetta,
Ronna,
Yotta,
Zetta,
Exa,
Peta,
Tera,
Giga,
Mega,
Kilo,
Hecto,
Deka,
Deci,
Centi,
Milli,
Micro,
Nano,
Pico,
Femto,
Atto,
Zepto,
Yocto,
Ronto,
Quecto
}
impl Prefix {
pub fn to_ratio(&self) -> Quantity {
let q = Quantity::new_rational_from_string(match self {
Prefix::Quetta => "1e30",
Prefix::Ronna => "1e27",
Prefix::Yotta => "1e24",
Prefix::Zetta => "1e21",
Prefix::Exa => "1e18",
Prefix::Peta => "1e15",
Prefix::Tera => "1e12",
Prefix::Giga => "1e9",
Prefix::Mega => "1e6",
Prefix::Kilo => "1e3",
Prefix::Hecto => "1e2",
Prefix::Deka => "1e1",
Prefix::Deci => "1e-1",
Prefix::Centi => "1e-2",
Prefix::Milli => "1e-3",
Prefix::Micro => "1e-6",
Prefix::Nano => "1e-9",
Prefix::Pico => "1e-12",
Prefix::Femto => "1e-15",
Prefix::Atto => "1e-18",
Prefix::Zepto => "1e-21",
Prefix::Yocto => "1e-24",
Prefix::Ronto => "1e-27",
Prefix::Quecto => "1e-30",
Prefix::None => { "1" }
}).unwrap();
return q;
}
}
impl ToString for Prefix {
fn to_string(&self) -> String {
String::from(match self {
Prefix::Quetta => "Q",
Prefix::Ronna => "R",
Prefix::Yotta => "Y",
Prefix::Zetta => "Z",
Prefix::Exa => "E",
Prefix::Peta => "P",
Prefix::Tera => "T",
Prefix::Giga => "G",
Prefix::Mega => "M",
Prefix::Kilo => "k",
Prefix::Hecto => "h",
Prefix::Deka => "da",
Prefix::Deci => "d",
Prefix::Centi => "c",
Prefix::Milli => "m",
Prefix::Micro => "u",
Prefix::Nano => "n",
Prefix::Pico => "p",
Prefix::Femto => "f",
Prefix::Atto => "a",
Prefix::Zepto => "z",
Prefix::Yocto => "y",
Prefix::Ronto => "r",
Prefix::Quecto => "q",
Prefix::None => ""
})
}
}
#[derive(Hash)]
#[derive(Debug)]
#[derive(Copy, Clone)]
#[derive(Eq, PartialEq)]
pub struct FreeUnit {
u: UnitBase,
p: Prefix
}
macro_rules! quick_base_factor {
(float, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => {
Some(Quantity {
v: Scalar::new_float_from_string($s).unwrap(),
u: Unit::from_array(&[
$(
(FreeUnit::from_base($x), Scalar::new_rational($p).unwrap()),
)*
(FreeUnit::from_base($u), Scalar::new_rational(-1f64).unwrap())
])
})
};
(rational, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => {
Some(Quantity {
v: Scalar::new_float_from_string($s).unwrap(),
u: Unit::from_array(&[
$(
(FreeUnit::from_base($x), Scalar::new_rational($p).unwrap()),
)*
(FreeUnit::from_base($u), Scalar::new_rational(-1f64).unwrap())
])
})
};
}
impl FreeUnit {
pub fn from_base(u: UnitBase) -> FreeUnit {
return FreeUnit {
u,
p: Prefix::None
}
}
pub fn from_base_prefix(u: UnitBase, p: Prefix) -> FreeUnit {
return FreeUnit { u, p }
}
pub fn set_prefix(&mut self, p: Prefix) {
self.p = p;
}
pub fn to_base_factor(&self) -> Quantity {
let q = match self.u {
// Returns the unit we need to multiply by to get a base // Returns the unit we need to multiply by to get a base
// unit, or `None` if this is already a base unit. // unit, or `None` if this is already a base unit.
// //
@ -39,55 +205,92 @@ impl BaseUnit {
// //
// The units here MUST be in terms of base units. // The units here MUST be in terms of base units.
// If they aren't, things will break. // If they aren't, things will break.
BaseUnit::Foot => Some(Quantity {
v: Scalar::new_float_from_string("0.3048").unwrap(),
u: Unit::from_array(&[
(BaseUnit::Meter, Scalar::new_rational(1f64).unwrap()),
(BaseUnit::Foot, Scalar::new_rational(-1f64).unwrap())
])
}),
BaseUnit::Mile => Some(Quantity { UnitBase::Foot => quick_base_factor!(float,
v: Scalar::new_float_from_string("1609").unwrap(), UnitBase::Foot,
u: Unit::from_array(&[ "0.3048",
(BaseUnit::Meter, Scalar::new_rational(1f64).unwrap()), (UnitBase::Meter, 1f64)
(BaseUnit::Mile, Scalar::new_rational(-1f64).unwrap()) ),
])
}),
UnitBase::Inch => quick_base_factor!(float,
UnitBase::Inch,
"0.0254",
(UnitBase::Meter, 1f64)
),
BaseUnit::Minute => Some(Quantity { UnitBase::Mile => quick_base_factor!(rational,
v: Scalar::new_rational_from_string("60").unwrap(), UnitBase::Mile,
u: Unit::from_array(&[ "1609",
(BaseUnit::Second, Scalar::new_rational(1f64).unwrap()), (UnitBase::Meter, 1f64)
(BaseUnit::Minute, Scalar::new_rational(-1f64).unwrap()) ),
])
}),
UnitBase::Minute => quick_base_factor!(rational,
UnitBase::Minute,
"60",
(UnitBase::Second, 1f64)
),
BaseUnit::Hour => Some(Quantity { UnitBase::Hour => quick_base_factor!(rational,
v: Scalar::new_rational_from_string("3600").unwrap(), UnitBase::Hour,
u: Unit::from_array(&[ "3600",
(BaseUnit::Second, Scalar::new_rational(1f64).unwrap()), (UnitBase::Second, 1f64)
(BaseUnit::Hour, Scalar::new_rational(-1f64).unwrap()) ),
])
}), UnitBase::Day => quick_base_factor!(rational,
UnitBase::Day,
"86400",
(UnitBase::Second, 1f64)
),
// Only base units should be missing a conversion factor. // Only base units should be missing a conversion factor.
_ => None _ => None
} };
let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap());
let mut p = self.p.to_ratio();
p.insert_unit(FreeUnit::from_base(self.u), Scalar::new_rational(1f64).unwrap());
p.insert_unit(FreeUnit::from_base_prefix(self.u, self.p), Scalar::new_rational(-1f64).unwrap());
q *= p;
return q;
} }
} }
impl ToString for FreeUnit {
fn to_string(&self) -> String {
let s = match self.u {
UnitBase::Second => "s",
UnitBase::Meter => "m",
UnitBase::Gram => "g",
UnitBase::Ampere => "a",
UnitBase::Kelvin => "k",
UnitBase::Mole => "mol",
UnitBase::Candela => "c",
UnitBase::Foot => "ft",
UnitBase::Inch => "in",
UnitBase::Mile => "mile",
UnitBase::Hour => "hour",
UnitBase::Minute => "min",
UnitBase::Day => "day",
};
let p = self.p.to_string();
format!("{p}{s}")
}
}
#[derive(Debug)] #[derive(Debug)]
#[derive(Clone)] #[derive(Clone)]
pub struct Unit { pub struct Unit {
// Unit, power. // Unit, power.
pub val: HashMap<BaseUnit, Scalar> pub val: HashMap<FreeUnit, Scalar>
} }
impl ToString for Unit { impl ToString for Unit {
fn to_string(&self) -> String { fn to_string(&self) -> String {
if self.unitless() { return String::new(); }; if self.unitless() { return String::new(); };
@ -95,7 +298,7 @@ impl ToString for Unit {
let mut top_empty = true; let mut top_empty = true;
let mut bottom_empty = true; let mut bottom_empty = true;
for (_, p) in &self.val { for (_, p) in self.get_val() {
if p.is_positive() { if p.is_positive() {
top_empty = false; top_empty = false;
} else { } else {
@ -106,21 +309,8 @@ impl ToString for Unit {
let mut t = String::new(); let mut t = String::new();
let mut b = String::new(); let mut b = String::new();
for (u, p) in &self.val { for (u, p) in self.get_val() {
let c = match u { let c = u.to_string();
BaseUnit::Second => "s",
BaseUnit::Meter => "m",
BaseUnit::Kilogram => "kg",
BaseUnit::Ampere => "a",
BaseUnit::Kelvin => "k",
BaseUnit::Mole => "mol",
BaseUnit::Candela => "c",
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() {
t.push_str(&format!("{c}·")); t.push_str(&format!("{c}·"));
@ -153,14 +343,16 @@ impl ToString for Unit {
impl Unit { impl Unit {
pub fn new() -> Unit { pub fn new() -> Unit {
return Unit { return Unit {
val: HashMap::new() val: HashMap::new()
} }
} }
pub fn from_array(a: &[(BaseUnit, Scalar)]) -> Unit { pub fn get_val(&self) -> &HashMap<FreeUnit, Scalar> { &self.val }
pub fn get_val_mut(&mut self) -> &mut HashMap<FreeUnit, Scalar> { &mut self.val }
pub fn from_array(a: &[(FreeUnit, Scalar)]) -> Unit {
let mut n = Unit::new(); let mut n = Unit::new();
for (u, p) in a.iter() { for (u, p) in a.iter() {
n.insert(*u, p.clone()); n.insert(*u, p.clone());
@ -168,37 +360,87 @@ impl Unit {
return n; return n;
} }
pub fn unitless(&self) -> bool { self.val.len() == 0 } pub fn unitless(&self) -> bool { self.get_val().len() == 0 }
pub fn insert(&mut self, u: BaseUnit, p: Scalar) { pub fn insert(&mut self, u: FreeUnit, p: Scalar) {
match self.val.get_mut(&u) { let v = self.get_val_mut();
match v.get_mut(&u) {
Some(i) => { Some(i) => {
let n = i.clone() + p; let n = i.clone() + p;
if n.is_zero() { if n.is_zero() {
self.val.remove(&u); v.remove(&u);
} else { *i = n; } } else { *i = n; }
}, },
None => { self.val.insert(u, p); } None => { v.insert(u, p); }
}; };
} }
pub fn pow(&self, pwr: Scalar) -> Unit { pub fn pow(&self, pwr: Scalar) -> Unit {
let mut u = self.clone(); let mut u = self.clone();
for (_, p) in &mut u.val { for (_, p) in u.get_val_mut() {
*p *= pwr.clone(); *p *= pwr.clone();
}; };
return u; return u;
} }
pub fn from_string(s: &str) -> Option<Quantity> {
// Base Units
let b = match s {
"m" => Some(UnitBase::Meter),
"s" => Some(UnitBase::Second),
"sec" => Some(UnitBase::Second),
"g" => Some(UnitBase::Gram),
"a" => Some(UnitBase::Ampere),
"k" => Some(UnitBase::Kelvin),
"mol" => Some(UnitBase::Mole),
"c" => Some(UnitBase::Candela),
"ft" => Some(UnitBase::Foot),
"mile" => Some(UnitBase::Mile),
"hour" => Some(UnitBase::Hour),
"min" => Some(UnitBase::Minute),
"day" => Some(UnitBase::Day),
_ => { None }
};
if b.is_some() {
let mut u = Unit::new();
let b = FreeUnit::from_base(b.unwrap());
u.insert(b, Scalar::new_rational(1f64).unwrap());
let mut q = Quantity::new_rational(1f64).unwrap();
q.set_unit(u);
return Some(q);
};
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);
u.insert(b, Scalar::new_rational(1f64).unwrap());
let mut q = Quantity::new_rational(1f64).unwrap();
q.set_unit(u);
return Some(q);
}
}
return None;
}
pub fn to_base_factor(&self) -> Quantity { pub fn to_base_factor(&self) -> Quantity {
let mut q = Quantity::new_rational(1f64).unwrap(); let mut q = Quantity::new_rational(1f64).unwrap();
for (u, p) in self.val.iter() { for (u, p) in self.get_val().iter() {
let b = u.to_base(); let b = u.to_base_factor();
if b.is_some() { q *= b.pow(Quantity::from_scalar(p.clone()));
q *= b.unwrap().pow(Quantity::from_scalar(p.clone()));
}
} }
return q; return q;
@ -208,8 +450,9 @@ impl Unit {
impl PartialEq for Unit { impl PartialEq for Unit {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
for (u, p) in &other.val { let v = self.get_val();
match self.val.get(u) { for (u, p) in other.get_val() {
match v.get(u) {
Some(i) => { if i != p { return false; } }, Some(i) => { if i != p { return false; } },
None => { return false; } None => { return false; }
}; };
@ -223,14 +466,14 @@ impl Mul for Unit {
fn mul(self, other: Self) -> Self::Output { fn mul(self, other: Self) -> Self::Output {
let mut o = self.clone(); let mut o = self.clone();
for (u, p) in &other.val { o.insert(*u, p.clone()); } for (u, p) in other.get_val() { o.insert(*u, p.clone()); }
return o; return o;
} }
} }
impl MulAssign for Unit where { impl MulAssign for Unit where {
fn mul_assign(&mut self, other: Self) { fn mul_assign(&mut self, other: Self) {
for (u, p) in &other.val { self.insert(*u, p.clone()); } for (u, p) in other.get_val() { self.insert(*u, p.clone()); }
} }
} }
@ -239,13 +482,13 @@ impl Div for Unit {
fn div(self, other: Self) -> Self::Output { fn div(self, other: Self) -> Self::Output {
let mut o = self.clone(); let mut o = self.clone();
for (u, p) in &other.val { o.insert(*u, -p.clone()); } for (u, p) in other.get_val() { o.insert(*u, -p.clone()); }
return o; return o;
} }
} }
impl DivAssign for Unit where { impl DivAssign for Unit where {
fn div_assign(&mut self, other: Self) { fn div_assign(&mut self, other: Self) {
for (u, p) in &other.val { self.insert(*u, -p.clone()); } for (u, p) in other.get_val() { self.insert(*u, -p.clone()); }
} }
} }