diff --git a/src/quantity/unit/freeunit.rs b/src/quantity/unit/freeunit.rs new file mode 100644 index 0000000..f3615b3 --- /dev/null +++ b/src/quantity/unit/freeunit.rs @@ -0,0 +1,160 @@ +use std::hash::{Hash, Hasher}; + +use crate::quantity::Scalar; +use crate::quantity::Quantity; +use super::UnitBase; +use super::Prefix; +use super::Unit; + + +#[derive(Debug)] +#[derive(Copy, Clone)] +pub struct FreeUnit { + pub (in super) base: UnitBase, + pub (in super) prefix: Prefix +} + +impl Hash for FreeUnit { + fn hash(&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! quick_base_factor { + (float, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => { + Some(Quantity { + scalar: Scalar::new_float_from_string($s).unwrap(), + unit: 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 { + scalar: Scalar::new_float_from_string($s).unwrap(), + unit: 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(base: UnitBase) -> FreeUnit { + return FreeUnit { base, prefix: Prefix::None } + } + + pub fn from_base_prefix(base: UnitBase, prefix: Prefix) -> FreeUnit { FreeUnit {base, prefix} } + pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = 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 { + let q = match self.base { + // 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. + + UnitBase::Foot => quick_base_factor!(float, + UnitBase::Foot, + "0.3048", + (UnitBase::Meter, 1f64) + ), + + UnitBase::Inch => quick_base_factor!(float, + UnitBase::Inch, + "0.0254", + (UnitBase::Meter, 1f64) + ), + + UnitBase::Mile => quick_base_factor!(rational, + UnitBase::Mile, + "1609", + (UnitBase::Meter, 1f64) + ), + + UnitBase::Minute => quick_base_factor!(rational, + UnitBase::Minute, + "60", + (UnitBase::Second, 1f64) + ), + + UnitBase::Hour => quick_base_factor!(rational, + UnitBase::Hour, + "3600", + (UnitBase::Second, 1f64) + ), + + UnitBase::Day => quick_base_factor!(rational, + UnitBase::Day, + "86400", + (UnitBase::Second, 1f64) + ), + + // Only base units should be missing a conversion factor. + _ => None + }; + + let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap()); + + 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_prefix(self.base, self.prefix), Scalar::new_rational(-1f64).unwrap()); + q *= p; + + return q; + } +} + + +impl ToString for FreeUnit { + fn to_string(&self) -> String { + let s = match self.base { + 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.prefix.to_string(); + + format!("{p}{s}") + } +} diff --git a/src/quantity/unit/mod.rs b/src/quantity/unit/mod.rs new file mode 100644 index 0000000..352682e --- /dev/null +++ b/src/quantity/unit/mod.rs @@ -0,0 +1,40 @@ +use std::hash::Hash; + +mod freeunit; +mod prefix; +mod unit; + +pub use prefix::Prefix; +pub use unit::Unit; +pub use freeunit::FreeUnit; + + + +#[derive(Hash)] +#[derive(Debug)] +#[derive(Copy, Clone)] +#[derive(Eq, PartialEq)] +pub enum UnitBase { + // Base Units + Second, + Meter, + Gram, // Technically kilogram, but that messes with our prefix system. + Ampere, + Kelvin, + Mole, + Candela, + + // Length units + Inch, + Foot, + Mile, + + // Time units + Minute, + Hour, + Day, + //Week, + //Month, +} + + diff --git a/src/quantity/unit/prefix.rs b/src/quantity/unit/prefix.rs new file mode 100644 index 0000000..5d1b6f2 --- /dev/null +++ b/src/quantity/unit/prefix.rs @@ -0,0 +1,110 @@ +use crate::quantity::Quantity; + + + +#[derive(Hash)] +#[derive(Debug)] +#[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 => "" + }) + } +} diff --git a/src/quantity/unit.rs b/src/quantity/unit/unit.rs similarity index 50% rename from src/quantity/unit.rs rename to src/quantity/unit/unit.rs index ad9bec5..4bf44a6 100644 --- a/src/quantity/unit.rs +++ b/src/quantity/unit/unit.rs @@ -1,298 +1,14 @@ use std::collections::HashMap; -use std::hash::{Hash, Hasher}; use std::ops::{ Mul, Div, MulAssign, DivAssign }; -use super::Scalar; -use super::Quantity; - - -#[derive(Hash)] -#[derive(Debug)] -#[derive(Copy, Clone)] -#[derive(Eq, PartialEq)] -pub enum UnitBase { - // Base Units - Second, - Meter, - Gram, // Technically kilogram, but that messes with prefix architecture. - Ampere, - Kelvin, - Mole, - Candela, - - // Length units - Inch, - Foot, - Mile, - - // Time units - Minute, - Hour, - Day, - //Week, - //Month, -} - -#[derive(Hash)] -#[derive(Debug)] -#[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(Debug)] -#[derive(Copy, Clone)] -pub struct FreeUnit { - base: UnitBase, - prefix: Prefix -} - -impl Hash for FreeUnit { - fn hash(&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! quick_base_factor { - (float, $u:expr, $s:expr, $( ($x:expr, $p:expr) ),* ) => { - Some(Quantity { - scalar: Scalar::new_float_from_string($s).unwrap(), - unit: 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 { - scalar: Scalar::new_float_from_string($s).unwrap(), - unit: 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(base: UnitBase) -> FreeUnit { - return FreeUnit { base, prefix: Prefix::None } - } - - pub fn from_base_prefix(base: UnitBase, prefix: Prefix) -> FreeUnit { FreeUnit {base, prefix} } - pub fn set_prefix(&mut self, prefix: Prefix) { self.prefix = 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 { - let q = match self.base { - // 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. - - UnitBase::Foot => quick_base_factor!(float, - UnitBase::Foot, - "0.3048", - (UnitBase::Meter, 1f64) - ), - - UnitBase::Inch => quick_base_factor!(float, - UnitBase::Inch, - "0.0254", - (UnitBase::Meter, 1f64) - ), - - UnitBase::Mile => quick_base_factor!(rational, - UnitBase::Mile, - "1609", - (UnitBase::Meter, 1f64) - ), - - UnitBase::Minute => quick_base_factor!(rational, - UnitBase::Minute, - "60", - (UnitBase::Second, 1f64) - ), - - UnitBase::Hour => quick_base_factor!(rational, - UnitBase::Hour, - "3600", - (UnitBase::Second, 1f64) - ), - - UnitBase::Day => quick_base_factor!(rational, - UnitBase::Day, - "86400", - (UnitBase::Second, 1f64) - ), - - // Only base units should be missing a conversion factor. - _ => None - }; - - let mut q = q.unwrap_or(Quantity::new_rational_from_string("1").unwrap()); - - 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_prefix(self.base, self.prefix), Scalar::new_rational(-1f64).unwrap()); - q *= p; - - return q; - } -} - - -impl ToString for FreeUnit { - fn to_string(&self) -> String { - let s = match self.base { - 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.prefix.to_string(); - - format!("{p}{s}") - } -} +use crate::quantity::Scalar; +use crate::quantity::Quantity; +use super::UnitBase; +use super::Prefix; +use super::FreeUnit; #[derive(Debug)] #[derive(Clone)] @@ -351,7 +67,6 @@ impl ToString for Unit { } } - impl Unit { pub fn new() -> Unit { return Unit {