summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Halle <niklas@niklashalle.net>2020-10-31 20:44:44 +0100
committerNiklas Halle <niklas@niklashalle.net>2020-10-31 20:44:44 +0100
commit3e4fb93a594f11a80904653396698d6c70e07cb0 (patch)
tree95fe5198762a4af36c8d501ac11d7c3dbe89450d
downloadunits-main.tar.gz
units-main.zip
Initial public releasemain
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock5
-rw-r--r--Cargo.toml9
-rw-r--r--src/README.md0
-rw-r--r--src/lib.rs99
-rw-r--r--src/macros.rs205
-rw-r--r--src/metric.rs137
-rw-r--r--src/metric/absorbed_dose.rs10
-rw-r--r--src/metric/acceleration.rs10
-rw-r--r--src/metric/amount_of_substance.rs10
-rw-r--r--src/metric/area.rs28
-rw-r--r--src/metric/capacitance.rs10
-rw-r--r--src/metric/catalytic_activity.rs10
-rw-r--r--src/metric/conductance.rs39
-rw-r--r--src/metric/dose_equivalent.rs30
-rw-r--r--src/metric/electric_charge.rs10
-rw-r--r--src/metric/electric_current.rs10
-rw-r--r--src/metric/electric_potential.rs10
-rw-r--r--src/metric/electric_resistance.rs10
-rw-r--r--src/metric/energy.rs32
-rw-r--r--src/metric/force.rs10
-rw-r--r--src/metric/frequency.rs59
-rw-r--r--src/metric/illuminance.rs10
-rw-r--r--src/metric/inductance.rs10
-rw-r--r--src/metric/length.rs44
-rw-r--r--src/metric/luminous_flux.rs10
-rw-r--r--src/metric/luminous_intensity.rs10
-rw-r--r--src/metric/magnetic_flux.rs10
-rw-r--r--src/metric/magnetic_flux_density.rs10
-rw-r--r--src/metric/mass.rs36
-rw-r--r--src/metric/plane_angle.rs28
-rw-r--r--src/metric/power.rs10
-rw-r--r--src/metric/pressure.rs28
-rw-r--r--src/metric/solid_angle.rs10
-rw-r--r--src/metric/temperature.rs89
-rw-r--r--src/metric/time.rs36
-rw-r--r--src/metric/velocity.rs10
-rw-r--r--src/metric/volume.rs32
38 files changed, 1129 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3d5af4d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+.vscode/
+target/
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..250e506
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "units"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..9d9235e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "units"
+version = "0.1.0"
+authors = [
+ "Constantin Mauf-Clausen <maufcons@informatik.hu-berlin.de>",
+ "Stefan Zabka <zabkaste@informatik.hu-berlin.de>",
+ "Niklas Halle <niklas.halle@informatik.hu-berlin.de>"
+]
+edition = "2018" \ No newline at end of file
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/README.md
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..b3a8853
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,99 @@
+mod macros;
+pub mod metric;
+
+#[cfg(test)]
+mod tests {
+ use crate::metric::*;
+ use crate::metric::Mass;
+ use crate::metric::Time;
+ use crate::metric::Force;
+ use crate::metric::Length;
+ use crate::metric::Pressure;
+ use crate::metric::CelsiusTemperature;
+
+ /*
+ use crate::metric::Unit;
+ use crate::metric::Primitive;
+ */
+
+ fn avg_force(ds: Length, dt: Time, m: Mass) -> Force {
+ let dv = ds / dt;
+ let a = dv / dt;
+ m * a
+ }
+
+ fn main() {
+ let dist = Mile(100.0);
+ let time = Minute(12.5);
+ let mass = Pound(800.0);
+ let force = avg_force(dist, time, mass);
+ let area = Litre(100.0) / Foot(5.0);
+
+ let result = match Bar(0.0).to_si_unit() {
+ Pressure::Base(_, from_si, symbol) => format!("{}[{}]", from_si((force / area).to_si_primitive()), symbol),
+ _ => panic!("not base")
+ };
+
+ println!(
+ "Force on body with mass {m} that moves {d} in {t}: {f}\n\
+ The pressure per {a} is {p}",
+ d = dist, t = time, m = mass, f = force, a = area, p = result
+ );
+ }
+
+ #[test]
+ fn it_works() {
+ println!();
+ main();
+
+ println!();
+ let single = Kilogram(1.0);
+ let mut double = single;
+ double *= 2.0;
+ let mut dived = double;
+ dived /= 2.0;
+
+ println!("single: {}", single);
+ println!("double: {}", double);
+ println!("single * 2.0: {}", single * 2.0);
+ println!("2.0 * single: {}", 2.0 * single);
+ println!("double / 2.0: {}", double / 2.0);
+ println!("dived: {}", dived);
+
+ println!("single + double: {}", single + double);
+
+ println!();
+ let gram = Gram(1000.0);
+ println!("gram: {}", gram);
+ println!("gram * 2.0: {}", gram * 2.0);
+
+ println!();
+ println!("gram + double = {}", gram + double);
+ println!("double + gram = {}", double + gram);
+ println!("gram - double = {}", gram - double);
+ println!("double - gram = {}", double - gram);
+
+ println!();
+ let k = Kelvin(20.0);
+ let c = Celsius(0.0) + k;
+ println!("{}", c);
+
+ println!();
+ let cel = Celsius(20.0);
+ let fah = match Fahrenheit(0.0).to_si_unit() {
+ CelsiusTemperature::Base(_, from_si, symbol) => CelsiusTemperature::Base(cel.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si after to_si() call")
+ };
+ println!("{} = {}", cel, fah);
+
+ println!();
+ let d = Degree(180.0);
+ let rad = Radian(0.0);
+ println!("{}", (rad + d).to_si_primitive() - std::f64::consts::PI);
+
+ println!();
+ let e = Kilocalorie(70.0);
+ let j = KiloJoule(0.0) + e;
+ println!("{} = {}", e, j);
+ }
+} \ No newline at end of file
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..f6925a0
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,205 @@
+#[macro_export]
+macro_rules! declare_simple_unit {
+ ($unit_enum:ident, $si_unit:ident, $si_symbol:literal) => {
+ #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+ pub enum $unit_enum {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ $si_unit(f64)
+ }
+
+ impl Unit<$unit_enum> for $unit_enum {
+ fn to_si_unit(&self) -> $unit_enum {
+ match self {
+ $unit_enum::Base(_, _, _) => self.clone(),
+ $unit_enum::Canonical(value) => $unit_enum::Base(value.clone(), |x| x, $si_symbol),
+ $unit_enum::$si_unit(value) => $unit_enum::Base(value.clone(), |x| x, $si_symbol),
+ }
+ }
+ }
+
+ declare_unit_basics!($unit_enum);
+ };
+}
+
+#[macro_export]
+macro_rules! declare_unit_basics {
+ ($unit_enum:ident) => {
+ impl Primitive for $unit_enum {
+ fn to_si_primitive(&self) -> f64 {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, _, _) => value,
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Add for $unit_enum {
+ type Output = $unit_enum;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => $unit_enum::Base(value + rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl AddAssign for $unit_enum {
+ fn add_assign(&mut self, rhs: Self) {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => *self = $unit_enum::Base(value + rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Sub for $unit_enum {
+ type Output = $unit_enum;
+
+ fn sub(self, rhs: Self) -> Self::Output {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => $unit_enum::Base(value - rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl SubAssign for $unit_enum {
+ fn sub_assign(&mut self, rhs: Self) {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => *self = $unit_enum::Base(value - rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Mul<$unit_enum> for f64 {
+ type Output = $unit_enum;
+
+ fn mul(self, rhs: $unit_enum) -> Self::Output {
+ match rhs.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => $unit_enum::Base(self * value, from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Mul<f64> for $unit_enum {
+ type Output = $unit_enum;
+
+ fn mul(self, rhs: f64) -> Self::Output {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => $unit_enum::Base(value * rhs, from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl MulAssign<f64> for $unit_enum {
+ fn mul_assign(&mut self, rhs: f64) {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => *self = $unit_enum::Base(rhs * value, from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Div<f64> for $unit_enum {
+ type Output = $unit_enum;
+
+ fn div(self, rhs: f64) -> Self::Output {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => $unit_enum::Base(value / rhs, from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl DivAssign<f64> for $unit_enum {
+ fn div_assign(&mut self, rhs: f64) {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => *self = $unit_enum::Base(value / rhs, from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ impl Div<$unit_enum> for $unit_enum {
+ type Output = f64;
+
+ fn div(self, rhs: $unit_enum) -> Self::Output {
+ self.to_si_primitive() / rhs.to_si_primitive()
+ }
+ }
+
+ impl Display for $unit_enum {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ match self.to_si_unit() {
+ $unit_enum::Base(value, from_si, symbol) => write!(f, "{}[{}]", from_si(value), symbol),
+ _ => panic!("Display: non si unit post to_si_unit() call"),
+ }
+ }
+ }
+
+ pub use $unit_enum::*;
+ };
+}
+
+#[macro_export]
+macro_rules! declare_unit_square {
+ ($base:ident, $product:ident) => {
+ impl Mul<$base> for $base {
+ type Output = $product;
+
+ fn mul(self, rhs: $base) -> Self::Output {
+ $product::Canonical(self.to_si_primitive() * rhs.to_si_primitive())
+ }
+ }
+
+ impl Div<$base> for $product {
+ type Output = $base;
+
+ fn div(self, rhs: $base) -> Self::Output {
+ $base::Canonical(self.to_si_primitive() / rhs.to_si_primitive())
+ }
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! declare_unit_multiplication {
+ ($factor_a:ident, $factor_b:ident, $product:ident) => {
+ impl Mul<$factor_a> for $factor_b {
+ type Output = $product;
+
+ fn mul(self, rhs: $factor_a) -> Self::Output {
+ $product::Canonical(self.to_si_primitive() * rhs.to_si_primitive())
+ }
+ }
+
+ impl Mul<$factor_b> for $factor_a {
+ type Output = $product;
+
+ fn mul(self, rhs: $factor_b) -> Self::Output {
+ $product::Canonical(self.to_si_primitive() * rhs.to_si_primitive())
+ }
+ }
+
+ impl Div<$factor_a> for $product {
+ type Output = $factor_b;
+
+ fn div(self, rhs: $factor_a) -> Self::Output {
+ $factor_b::Canonical(self.to_si_primitive() / rhs.to_si_primitive())
+ }
+ }
+
+ impl Div<$factor_b> for $product {
+ type Output = $factor_a;
+
+ fn div(self, rhs: $factor_b) -> Self::Output {
+ $factor_a::Canonical(self.to_si_primitive() / rhs.to_si_primitive())
+ }
+ }
+ };
+} \ No newline at end of file
diff --git a/src/metric.rs b/src/metric.rs
new file mode 100644
index 0000000..209c31c
--- /dev/null
+++ b/src/metric.rs
@@ -0,0 +1,137 @@
+pub mod area;
+pub mod mass;
+pub mod time;
+pub mod force;
+pub mod power;
+pub mod energy;
+pub mod length;
+pub mod volume;
+pub mod pressure;
+pub mod velocity;
+pub mod frequency;
+pub mod inductance;
+pub mod capacitance;
+pub mod conductance;
+pub mod illuminance;
+pub mod plane_angle;
+pub mod solid_angle;
+pub mod temperature;
+pub mod acceleration;
+pub mod absorbed_dose;
+pub mod luminous_flux;
+pub mod magnetic_flux;
+pub mod electric_charge;
+pub mod dose_equivalent;
+pub mod electric_current;
+pub mod catalytic_activity;
+pub mod electric_potential;
+pub mod luminous_intensity;
+pub mod electric_resistance;
+pub mod amount_of_substance;
+pub mod magnetic_flux_density;
+
+pub use area::*;
+pub use mass::*;
+pub use time::*;
+pub use force::*;
+pub use power::*;
+pub use energy::*;
+pub use length::*;
+pub use volume::*;
+pub use pressure::*;
+pub use velocity::*;
+pub use frequency::*;
+pub use inductance::*;
+pub use capacitance::*;
+pub use illuminance::*;
+pub use plane_angle::*;
+pub use solid_angle::*;
+pub use temperature::*;
+pub use acceleration::*;
+pub use luminous_flux::*;
+pub use magnetic_flux::*;
+pub use electric_charge::*;
+pub use dose_equivalent::*;
+pub use electric_current::*;
+pub use catalytic_activity::*;
+pub use electric_potential::*;
+pub use luminous_intensity::*;
+pub use electric_resistance::*;
+pub use amount_of_substance::*;
+pub use magnetic_flux_density::*;
+
+use crate::declare_unit_square;
+use crate::declare_unit_multiplication;
+
+use std::ops::{Div, Mul};
+
+/* ---------------------------------------- unit traits ---------------------------------------- */
+
+pub trait Unit<Physical> {
+ fn to_si_unit(&self) -> Physical;
+}
+
+pub trait Primitive {
+ fn to_si_primitive(&self) -> f64;
+}
+
+/* -------------------------------------- here be dragons -------------------------------------- */
+/* (unit-interoperability) */
+
+
+// derived units without special names
+// m * m² => m³
+declare_unit_multiplication!(Length, Area, Volume);
+// m * m => m²
+declare_unit_square!(Length, Area);
+// v = m/s * s => m
+declare_unit_multiplication!(Velocity, Time, Length);
+// accel * s = m/s² * s = m/s => v
+declare_unit_multiplication!(Acceleration, Time, Velocity);
+
+// kg * m/s² => N
+declare_unit_multiplication!(Mass, Acceleration, Force);
+// N * m => J
+declare_unit_multiplication!(Force, Length, Energy);
+// katal * s = mol/s * s => mol
+declare_unit_multiplication!(CatalyticActivity, Time, AmountOfSubstance);
+// A * s => C
+declare_unit_multiplication!(ElectricCurrent, Time, ElectricCharge);
+// lux = lm/m² * m² => lm
+declare_unit_multiplication!(Illuminance, Area, LuminousFlux);
+
+// Pa * m² = N/m² * m² => N
+declare_unit_multiplication!(Pressure, Area, Force);
+// W * s = J/s * s => J
+declare_unit_multiplication!(Power, Time, Energy);
+// Wb * s = V/s * s => V
+declare_unit_multiplication!(MagneticFlux, Time, ElectricPotential);
+// F * V = C/V * V => C
+declare_unit_multiplication!(Capacitance, ElectricPotential, ElectricCharge);
+// cd * sr => lumen
+declare_unit_multiplication!(LuminousIntensity, SolidAngle, LuminousFlux);
+
+// gray * kg = J/kg * kg => J
+// alias is DoseEquivalent, see below
+// Activity is an alias to Frequency, which has both Hertz and Becquerel
+// henry * A = Wb/A * A => Wb
+declare_unit_multiplication!(Inductance, ElectricCurrent, MagneticFlux);
+// V * A = W/A * A => W
+declare_unit_multiplication!(ElectricPotential, ElectricCurrent, Power);
+// Ω * A = V/A * A => V
+declare_unit_multiplication!(ElectricResistance, ElectricCurrent, ElectricPotential);
+
+// sievert * kg = J/kg * kg => J
+declare_unit_multiplication!(DoseEquivalent, Mass, Energy);
+// Hertz is in its file
+// tesla * m² = Wb/m² * m² => Wb
+declare_unit_multiplication!(MagneticFluxDensity, Area, MagneticFlux);
+// Conductance is in its file
+
+// extra conversions, because they work : TODO: add some more
+// Hz * m = m/s => v
+declare_unit_multiplication!(Frequency, Length, Velocity);
+// Pa * m² = N/m² * m³ = N*m => J
+declare_unit_multiplication!(Pressure, Volume, Energy);
+// C * V = A*s * W/A = W/s = J/s * 1/s => J
+declare_unit_multiplication!(ElectricCharge, ElectricPotential, Energy); \ No newline at end of file
diff --git a/src/metric/absorbed_dose.rs b/src/metric/absorbed_dose.rs
new file mode 100644
index 0000000..41ef508
--- /dev/null
+++ b/src/metric/absorbed_dose.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(AbsorbedDose, Gray, "Gy"); \ No newline at end of file
diff --git a/src/metric/acceleration.rs b/src/metric/acceleration.rs
new file mode 100644
index 0000000..3b3d50a
--- /dev/null
+++ b/src/metric/acceleration.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Acceleration, Accel, "m/s²"); \ No newline at end of file
diff --git a/src/metric/amount_of_substance.rs b/src/metric/amount_of_substance.rs
new file mode 100644
index 0000000..34058c7
--- /dev/null
+++ b/src/metric/amount_of_substance.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(AmountOfSubstance, Mole, "mol"); \ No newline at end of file
diff --git a/src/metric/area.rs b/src/metric/area.rs
new file mode 100644
index 0000000..eb2b0c3
--- /dev/null
+++ b/src/metric/area.rs
@@ -0,0 +1,28 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Area {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ SquareMeter(f64),
+ SquareMetre(f64),
+}
+
+impl Unit<Area> for Area {
+ fn to_si_unit(&self) -> Area {
+ match self {
+ Area::Base(_, _, _) => self.clone(),
+ Area::Canonical(value) => Area::Base(value.clone(), |x| x, "m²"),
+ Area::SquareMeter(value) => Area::Base(value.clone(), |x| x, "m²"),
+ Area::SquareMetre(value) => Area::Base(value.clone(), |x| x, "m²"),
+ }
+ }
+}
+
+declare_unit_basics!(Area); \ No newline at end of file
diff --git a/src/metric/capacitance.rs b/src/metric/capacitance.rs
new file mode 100644
index 0000000..a98c203
--- /dev/null
+++ b/src/metric/capacitance.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Capacitance, Farad, "F"); \ No newline at end of file
diff --git a/src/metric/catalytic_activity.rs b/src/metric/catalytic_activity.rs
new file mode 100644
index 0000000..710a87a
--- /dev/null
+++ b/src/metric/catalytic_activity.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(CatalyticActivity, Katal, "kat"); \ No newline at end of file
diff --git a/src/metric/conductance.rs b/src/metric/conductance.rs
new file mode 100644
index 0000000..29712e0
--- /dev/null
+++ b/src/metric/conductance.rs
@@ -0,0 +1,39 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+use crate::metric::electric_resistance::ElectricResistance;
+
+declare_simple_unit!(Conductance, Siemens, "S");
+
+// 1/s = Hz
+impl Div<ElectricResistance> for f64 {
+ type Output = Conductance;
+
+ fn div(self, rhs: ElectricResistance) -> Self::Output {
+ Conductance::Canonical(self * rhs.to_si_primitive())
+ }
+}
+
+// Hz * s = 1
+impl Mul<ElectricResistance> for Conductance {
+ type Output = f64;
+
+ fn mul(self, rhs: ElectricResistance) -> Self::Output {
+ self.to_si_primitive() * rhs.to_si_primitive()
+ }
+}
+
+// s * Hz = 1
+impl Mul<Conductance> for ElectricResistance {
+ type Output = f64;
+
+ fn mul(self, rhs: Conductance) -> Self::Output {
+ self.to_si_primitive() * rhs.to_si_primitive()
+ }
+} \ No newline at end of file
diff --git a/src/metric/dose_equivalent.rs b/src/metric/dose_equivalent.rs
new file mode 100644
index 0000000..408c7de
--- /dev/null
+++ b/src/metric/dose_equivalent.rs
@@ -0,0 +1,30 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum DoseEquivalent {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Sievert(f64),
+ Gray(f64),
+}
+
+impl Unit<DoseEquivalent> for DoseEquivalent {
+ fn to_si_unit(&self) -> DoseEquivalent {
+ match self {
+ DoseEquivalent::Base(_, _, _) => self.clone(),
+ DoseEquivalent::Canonical(value) => DoseEquivalent::Base(value.clone(), |x| x, "Sv"),
+ DoseEquivalent::Sievert(value) => DoseEquivalent::Base(value.clone(), |x| x, "Sv"),
+ DoseEquivalent::Gray(value) => DoseEquivalent::Base(value.clone(), |x| x, "Gy"),
+ }
+ }
+}
+
+declare_unit_basics!(DoseEquivalent);
+
+pub type AbsorbedDose = DoseEquivalent; \ No newline at end of file
diff --git a/src/metric/electric_charge.rs b/src/metric/electric_charge.rs
new file mode 100644
index 0000000..9e6e970
--- /dev/null
+++ b/src/metric/electric_charge.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(ElectricCharge, Coulomb, "C"); \ No newline at end of file
diff --git a/src/metric/electric_current.rs b/src/metric/electric_current.rs
new file mode 100644
index 0000000..376e426
--- /dev/null
+++ b/src/metric/electric_current.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(ElectricCurrent, Ampere, "A"); \ No newline at end of file
diff --git a/src/metric/electric_potential.rs b/src/metric/electric_potential.rs
new file mode 100644
index 0000000..9fdbde8
--- /dev/null
+++ b/src/metric/electric_potential.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(ElectricPotential, Volt, "V"); \ No newline at end of file
diff --git a/src/metric/electric_resistance.rs b/src/metric/electric_resistance.rs
new file mode 100644
index 0000000..3d3d9f8
--- /dev/null
+++ b/src/metric/electric_resistance.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(ElectricResistance, Ohm, "Ω"); \ No newline at end of file
diff --git a/src/metric/energy.rs b/src/metric/energy.rs
new file mode 100644
index 0000000..5884a2f
--- /dev/null
+++ b/src/metric/energy.rs
@@ -0,0 +1,32 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Energy {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Joule(f64),
+ KiloJoule(f64),
+ Calorie(f64),
+ Kilocalorie(f64),
+}
+
+impl Unit<Energy> for Energy {
+ fn to_si_unit(&self) -> Energy {
+ match self {
+ Energy::Base(_, _, _) => self.clone(),
+ Energy::Canonical(value) => Energy::Base(value.clone(), |x| x, "J"),
+ Energy::Joule(value) => Energy::Base(value.clone(), |x| x, "J"),
+ Energy::KiloJoule(value) => Energy::Base(value * 1000.0, |x| x / 1000.0, "kJ"),
+ Energy::Calorie(value) => Energy::Base(value * 4.184, |x| x / 4.184, "cal"),
+ Energy::Kilocalorie(value) => Energy::Base(value * 4184.0, |x| x / 4184.0, "kcal"),
+ }
+ }
+}
+
+declare_unit_basics!(Energy); \ No newline at end of file
diff --git a/src/metric/force.rs b/src/metric/force.rs
new file mode 100644
index 0000000..5011ce6
--- /dev/null
+++ b/src/metric/force.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Force, Newton, "N"); \ No newline at end of file
diff --git a/src/metric/frequency.rs b/src/metric/frequency.rs
new file mode 100644
index 0000000..c35f874
--- /dev/null
+++ b/src/metric/frequency.rs
@@ -0,0 +1,59 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+use crate::metric::time::Time;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Frequency {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Hertz(f64),
+ Becquerel(f64),
+}
+
+impl Unit<Frequency> for Frequency {
+ fn to_si_unit(&self) -> Frequency {
+ match self {
+ Frequency::Base(_, _, _) => self.clone(),
+ Frequency::Canonical(value) => Frequency::Base(value.clone(), |x| x, "Hz"),
+ Frequency::Hertz(value) => Frequency::Base(value.clone(), |x| x, "Hz"),
+ Frequency::Becquerel(value) => Frequency::Base(value.clone(), |x| x, "Bq"),
+ }
+ }
+}
+
+declare_unit_basics!(Frequency);
+
+// 1/s = Hz
+impl Div<Time> for f64 {
+ type Output = Frequency;
+
+ fn div(self, rhs: Time) -> Self::Output {
+ Frequency::Canonical(self * rhs.to_si_primitive())
+ }
+}
+
+// Hz * s = 1
+impl Mul<Time> for Frequency {
+ type Output = f64;
+
+ fn mul(self, rhs: Time) -> Self::Output {
+ self.to_si_primitive() * rhs.to_si_primitive()
+ }
+}
+
+// s * Hz = 1
+impl Mul<Frequency> for Time {
+ type Output = f64;
+
+ fn mul(self, rhs: Frequency) -> Self::Output {
+ self.to_si_primitive() * rhs.to_si_primitive()
+ }
+}
+
+pub type Activity = Frequency; \ No newline at end of file
diff --git a/src/metric/illuminance.rs b/src/metric/illuminance.rs
new file mode 100644
index 0000000..a6010e0
--- /dev/null
+++ b/src/metric/illuminance.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Illuminance, Lux, "lx"); \ No newline at end of file
diff --git a/src/metric/inductance.rs b/src/metric/inductance.rs
new file mode 100644
index 0000000..702e67a
--- /dev/null
+++ b/src/metric/inductance.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Inductance, Henry, "H"); \ No newline at end of file
diff --git a/src/metric/length.rs b/src/metric/length.rs
new file mode 100644
index 0000000..a605ad2
--- /dev/null
+++ b/src/metric/length.rs
@@ -0,0 +1,44 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Length {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Meter(f64),
+ Metre(f64),
+ Centimeter(f64),
+ Centimetre(f64),
+ Kilometer(f64),
+ Kilometre(f64),
+ Inch(f64), // Zoll
+ Foot(f64), // Fuß
+ Yard(f64), // Schritt
+ Mile(f64), // Meile
+}
+
+impl Unit<Length> for Length {
+ fn to_si_unit(&self) -> Length {
+ match self {
+ Length::Base(_, _, _) => self.clone(),
+ Length::Canonical(value) => Length::Base(value.clone(), |x| x, "m"),
+ Length::Meter(value) => Length::Base(value.clone(), |x| x, "m"),
+ Length::Metre(value) => Length::Base(value.clone(), |x| x, "m"),
+ Length::Centimeter(value) => Length::Base(value / 100.0, |x| x * 100.0, "cm"),
+ Length::Centimetre(value) => Length::Base(value / 100.0, |x| x * 100.0, "cm"),
+ Length::Kilometer(value) => Length::Base(value * 1000.0, |x| x / 1000.0, "km"),
+ Length::Kilometre(value) => Length::Base(value * 1000.0, |x| x / 1000.0, "km"),
+ Length::Inch(value) => Length::Base(value * 0.0254, |x| x / 0.0254, "in"),
+ Length::Foot(value) => Length::Base(value * 0.3048, |x| x / 0.3048, "foot"),
+ Length::Yard(value) => Length::Base(value * 0.9144, |x| x / 0.9144, "yard"),
+ Length::Mile(value) => Length::Base(value * 1609.344, |x| x / 1609.344, "mile"),
+ }
+ }
+}
+
+declare_unit_basics!(Length); \ No newline at end of file
diff --git a/src/metric/luminous_flux.rs b/src/metric/luminous_flux.rs
new file mode 100644
index 0000000..ba3a296
--- /dev/null
+++ b/src/metric/luminous_flux.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(LuminousFlux, Lumen, "lm"); \ No newline at end of file
diff --git a/src/metric/luminous_intensity.rs b/src/metric/luminous_intensity.rs
new file mode 100644
index 0000000..d2bfe42
--- /dev/null
+++ b/src/metric/luminous_intensity.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(LuminousIntensity, Candela, "cd"); \ No newline at end of file
diff --git a/src/metric/magnetic_flux.rs b/src/metric/magnetic_flux.rs
new file mode 100644
index 0000000..8846a14
--- /dev/null
+++ b/src/metric/magnetic_flux.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(MagneticFlux, Weber, "Wb"); \ No newline at end of file
diff --git a/src/metric/magnetic_flux_density.rs b/src/metric/magnetic_flux_density.rs
new file mode 100644
index 0000000..1d6a58c
--- /dev/null
+++ b/src/metric/magnetic_flux_density.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(MagneticFluxDensity, Tesla, "T"); \ No newline at end of file
diff --git a/src/metric/mass.rs b/src/metric/mass.rs
new file mode 100644
index 0000000..462d840
--- /dev/null
+++ b/src/metric/mass.rs
@@ -0,0 +1,36 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Mass {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Kilogram(f64),
+ Milligram(f64),
+ Gram(f64),
+ Tonne(f64),
+ Ounce(f64), // international avoirdupois ounce
+ Pound(f64), // International avoirdupois pound
+}
+
+impl Unit<Mass> for Mass {
+ fn to_si_unit(&self) -> Mass {
+ match self {
+ Mass::Base(_, _, _) => self.clone(),
+ Mass::Canonical(value) => Mass::Base(value.clone(), |x| x, "kg"),
+ Mass::Kilogram(value) => Mass::Base(value.clone(), |x| x, "kg"),
+ Mass::Gram(value) => Mass::Base(value / 1000.0, |x| x * 1000.0, "g"),
+ Mass::Milligram(value) => Mass::Base(value / 1_000_000.0, |x| x * 1_000_000.0, "mg"),
+ Mass::Tonne(value) => Mass::Base(value * 1000.0, |x| x / 1000.0, "t"),
+ Mass::Ounce(value) => Mass::Base(value * 28.349523125 / 1000.0, |x| x / 28.349523125 * 1000.0, "℥"),
+ Mass::Pound(value) => Mass::Base(value * 0.45359237, |x| x / 0.45359237, "lb"),
+ }
+ }
+}
+
+declare_unit_basics!(Mass); \ No newline at end of file
diff --git a/src/metric/plane_angle.rs b/src/metric/plane_angle.rs
new file mode 100644
index 0000000..86bc17c
--- /dev/null
+++ b/src/metric/plane_angle.rs
@@ -0,0 +1,28 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum PlaneAngle {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Radian(f64),
+ Degree(f64),
+}
+
+impl Unit<PlaneAngle> for PlaneAngle {
+ fn to_si_unit(&self) -> PlaneAngle {
+ match self {
+ PlaneAngle::Base(_, _, _) => self.clone(),
+ PlaneAngle::Canonical(value) => PlaneAngle::Base(value.clone(), |x| x, "m²"),
+ PlaneAngle::Radian(value) => PlaneAngle::Base(value.clone(), |x| x, "rad"),
+ PlaneAngle::Degree(value) => PlaneAngle::Base(value * std::f64::consts::PI / 180.0, |x| x / std::f64::consts::PI * 180.0, "°"),
+ }
+ }
+}
+
+declare_unit_basics!(PlaneAngle); \ No newline at end of file
diff --git a/src/metric/power.rs b/src/metric/power.rs
new file mode 100644
index 0000000..51d6cf0
--- /dev/null
+++ b/src/metric/power.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Power, Watt, "W"); \ No newline at end of file
diff --git a/src/metric/pressure.rs b/src/metric/pressure.rs
new file mode 100644
index 0000000..b28c87c
--- /dev/null
+++ b/src/metric/pressure.rs
@@ -0,0 +1,28 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Pressure {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Pascal(f64),
+ Bar(f64),
+}
+
+impl Unit<Pressure> for Pressure {
+ fn to_si_unit(&self) -> Pressure {
+ match self {
+ Pressure::Base(_, _, _) => self.clone(),
+ Pressure::Canonical(value) => Pressure::Base(value.clone(), |x| x, "Pa"),
+ Pressure::Pascal(value) => Pressure::Base(value.clone(), |x| x, "Pa"),
+ Pressure::Bar(value) => Pressure::Base(value * 100_000.0, |x| x / 100_000.0, "bar"),
+ }
+ }
+}
+
+declare_unit_basics!(Pressure); \ No newline at end of file
diff --git a/src/metric/solid_angle.rs b/src/metric/solid_angle.rs
new file mode 100644
index 0000000..9db73ef
--- /dev/null
+++ b/src/metric/solid_angle.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(SolidAngle, Sterdian, "sr"); \ No newline at end of file
diff --git a/src/metric/temperature.rs b/src/metric/temperature.rs
new file mode 100644
index 0000000..06c1489
--- /dev/null
+++ b/src/metric/temperature.rs
@@ -0,0 +1,89 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum ThermodynamicTemperature {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Kelvin(f64),
+}
+
+impl Unit<ThermodynamicTemperature> for ThermodynamicTemperature {
+ fn to_si_unit(&self) -> ThermodynamicTemperature {
+ match self {
+ ThermodynamicTemperature::Base(_, _, _) => self.clone(),
+ ThermodynamicTemperature::Canonical(value) => ThermodynamicTemperature::Base(value.clone(), |x| x, "K"),
+ ThermodynamicTemperature::Kelvin(value) => ThermodynamicTemperature::Base(value.clone(), |x| x, "K"),
+ }
+ }
+}
+
+declare_unit_basics!(ThermodynamicTemperature);
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum CelsiusTemperature {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Celsius(f64),
+ Fahrenheit(f64),
+}
+
+impl Unit<CelsiusTemperature> for CelsiusTemperature {
+ fn to_si_unit(&self) -> CelsiusTemperature {
+ match self {
+ CelsiusTemperature::Base(_, _, _) => self.clone(),
+ CelsiusTemperature::Canonical(value) => CelsiusTemperature::Base(value.clone(), |x| x, "K"),
+ CelsiusTemperature::Celsius(value) => CelsiusTemperature::Base(value.clone(), |x| x, "°C"),
+ CelsiusTemperature::Fahrenheit(value) => CelsiusTemperature::Base((value - 32.0) / 1.8 , |x| x * 1.8 + 32.0, "°F"),
+ }
+ }
+}
+
+declare_unit_basics!(CelsiusTemperature);
+
+/* --- temperature interoperability: C +/- K --- */
+
+impl Add<ThermodynamicTemperature> for CelsiusTemperature {
+ type Output = CelsiusTemperature;
+
+ fn add(self, rhs: ThermodynamicTemperature) -> Self::Output {
+ match self.to_si_unit() {
+ CelsiusTemperature::Base(value, from_si, symbol) => CelsiusTemperature::Base(value + rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call")
+ }
+ }
+}
+
+impl AddAssign<ThermodynamicTemperature> for CelsiusTemperature {
+ fn add_assign(&mut self, rhs: ThermodynamicTemperature) {
+ match self.to_si_unit() {
+ CelsiusTemperature::Base(value, from_si, symbol) => *self = CelsiusTemperature::Base(value + rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call")
+ }
+ }
+}
+
+impl Sub<ThermodynamicTemperature> for CelsiusTemperature {
+ type Output = CelsiusTemperature;
+
+ fn sub(self, rhs: ThermodynamicTemperature) -> Self::Output {
+ match self.to_si_unit() {
+ CelsiusTemperature::Base(value, from_si, symbol) => CelsiusTemperature::Base(value + rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call")
+ }
+ }
+}
+
+impl SubAssign<ThermodynamicTemperature> for CelsiusTemperature {
+ fn sub_assign(&mut self, rhs: ThermodynamicTemperature) {
+ match self.to_si_unit() {
+ CelsiusTemperature::Base(value, from_si, symbol) => *self = CelsiusTemperature::Base(value - rhs.to_si_primitive(), from_si, symbol),
+ _ => panic!("non si unit post to_si_unit() call")
+ }
+ }
+} \ No newline at end of file
diff --git a/src/metric/time.rs b/src/metric/time.rs
new file mode 100644
index 0000000..f4ff4b8
--- /dev/null
+++ b/src/metric/time.rs
@@ -0,0 +1,36 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
+pub enum Time {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ Second(f64),
+ Millisecond(f64),
+ Minute(f64),
+ Hour(f64),
+ Day(f64),
+ Year(f64),
+}
+
+impl Unit<Time> for Time {
+ fn to_si_unit(&self) -> Time {
+ match self {
+ Time::Base(_, _, _) => self.clone(),
+ Time::Canonical(value) => Time::Base(value.clone(), |x| x, "s"),
+ Time::Second(value) => Time::Base(value.clone(), |x| x, "s"),
+ Time::Millisecond(value) => Time::Base(value / 1000.0, |x| x * 1000.0, "ms"),
+ Time::Minute(value) => Time::Base(value * 60.0, |x| x / 60.0, "min"),
+ Time::Hour(value) => Time::Base(value * 3600.0, |x| x / 3600.0, "h"),
+ Time::Day(value) => Time::Base(value * 86400.0, |x| x / 86400.0, "d"),
+ Time::Year(value) => Time::Base(value * 31536000.0, |x| x / 31536000.0, "a")
+ }
+ }
+}
+
+declare_unit_basics!(Time); \ No newline at end of file
diff --git a/src/metric/velocity.rs b/src/metric/velocity.rs
new file mode 100644
index 0000000..3aa65d0
--- /dev/null
+++ b/src/metric/velocity.rs
@@ -0,0 +1,10 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+use crate::declare_simple_unit;
+
+declare_simple_unit!(Velocity, Speed, "m/s"); \ No newline at end of file
diff --git a/src/metric/volume.rs b/src/metric/volume.rs
new file mode 100644
index 0000000..064912d
--- /dev/null
+++ b/src/metric/volume.rs
@@ -0,0 +1,32 @@
+use std::fmt::{Display, Formatter, Result};
+use std::ops::{Add, Sub, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign};
+
+use crate::metric::Unit;
+use crate::metric::Primitive;
+
+use crate::declare_unit_basics;
+
+#[derive(Copy, Clone, PartialOrd, PartialEq)]
+pub enum Volume {
+ Base(f64, fn(f64) -> f64, &'static str),
+ Canonical(f64),
+ CubicMeter(f64),
+ CubicMetre(f64),
+ Litre(f64),
+ Liter(f64)
+}
+
+impl Unit<Volume> for Volume {
+ fn to_si_unit(&self) -> Volume {
+ match self {
+ Volume::Base(_, _, _) => self.clone(),
+ Volume::Canonical(value) => Volume::Base(value.clone(), |x| x, "m³"),
+ Volume::CubicMeter(value) => Volume::Base(value.clone(), |x| x, "m³"),
+ Volume::CubicMetre(value) => Volume::Base(value.clone(), |x| x, "m³"),
+ Volume::Litre(value) => Volume::Base(value / 1000.0, |x| x * 1000.0, "l"),
+ Volume::Liter(value) => Volume::Base(value / 1000.0, |x| x * 1000.0, "l"),
+ }
+ }
+}
+
+declare_unit_basics!(Volume); \ No newline at end of file