muse2/
units.rs

1//! This module defines various unit types and their conversions.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::ops::{AddAssign, Mul, SubAssign};
6
7macro_rules! base_unit_struct {
8    ($name:ident) => {
9        /// A unit type representing a dimensionless value.
10        #[derive(
11            Debug,
12            Clone,
13            Copy,
14            PartialEq,
15            PartialOrd,
16            Serialize,
17            Deserialize,
18            derive_more::Add,
19            derive_more::Sub,
20        )]
21        pub struct $name(pub f64);
22
23        impl std::ops::Div<$name> for $name {
24            type Output = Dimensionless;
25            fn div(self, rhs: $name) -> Dimensionless {
26                Dimensionless(self.0 / rhs.0)
27            }
28        }
29        impl std::iter::Sum for $name {
30            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
31                iter.fold($name(0.0), |a, b| $name(a.0 + b.0))
32            }
33        }
34        impl AddAssign for $name {
35            fn add_assign(&mut self, other: Self) {
36                self.0 += other.0;
37            }
38        }
39        impl SubAssign for $name {
40            fn sub_assign(&mut self, other: Self) {
41                self.0 -= other.0;
42            }
43        }
44        impl fmt::Display for $name {
45            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46                write!(f, "{}", self.0)
47            }
48        }
49        impl float_cmp::ApproxEq for $name {
50            type Margin = float_cmp::F64Margin;
51            fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
52                self.0.approx_eq(other.0, margin)
53            }
54        }
55        impl $name {
56            /// Returns the underlying f64 value.
57            pub fn value(&self) -> f64 {
58                self.0
59            }
60            /// Returns true if the value is a normal number.
61            pub fn is_normal(&self) -> bool {
62                self.0.is_normal()
63            }
64            /// Returns true if the value is finite.
65            pub fn is_finite(&self) -> bool {
66                self.0.is_finite()
67            }
68            /// Returns the absolute value of this unit.
69            pub fn abs(&self) -> Self {
70                $name(self.0.abs())
71            }
72            /// Returns the max of two values
73            pub fn max(&self, other: Self) -> Self {
74                Self(self.0.max(other.0))
75            }
76            /// Returns the min of two values
77            pub fn min(&self, other: Self) -> Self {
78                Self(self.0.min(other.0))
79            }
80        }
81    };
82}
83
84// Define Dimensionless first
85base_unit_struct!(Dimensionless);
86
87// Add extra methods for Dimensionless
88impl From<f64> for Dimensionless {
89    fn from(val: f64) -> Self {
90        Self(val)
91    }
92}
93impl From<Dimensionless> for f64 {
94    fn from(val: Dimensionless) -> Self {
95        val.0
96    }
97}
98
99impl Mul for Dimensionless {
100    type Output = Dimensionless;
101
102    fn mul(self, rhs: Self) -> Self::Output {
103        Dimensionless(self.0 * rhs.0)
104    }
105}
106
107impl Dimensionless {
108    /// Raises this dimensionless number to the power of `rhs`.
109    pub fn powi(self, rhs: i32) -> Self {
110        Dimensionless(self.0.powi(rhs))
111    }
112}
113
114// Define all other units with Dimensionless interactions
115macro_rules! unit_struct {
116    ($name:ident) => {
117        base_unit_struct!($name);
118
119        impl std::ops::Mul<Dimensionless> for $name {
120            type Output = $name;
121            fn mul(self, rhs: Dimensionless) -> $name {
122                $name(self.0 * rhs.0)
123            }
124        }
125        impl std::ops::Mul<$name> for Dimensionless {
126            type Output = $name;
127            fn mul(self, rhs: $name) -> $name {
128                $name(self.0 * rhs.0)
129            }
130        }
131        impl std::ops::Div<Dimensionless> for $name {
132            type Output = $name;
133            fn div(self, rhs: Dimensionless) -> $name {
134                $name(self.0 / rhs.0)
135            }
136        }
137    };
138}
139
140// Base quantities
141unit_struct!(Money);
142unit_struct!(Flow);
143unit_struct!(Activity);
144unit_struct!(Capacity);
145unit_struct!(Year);
146
147// Derived quantities
148unit_struct!(MoneyPerYear);
149unit_struct!(MoneyPerFlow);
150unit_struct!(MoneyPerCapacity);
151unit_struct!(MoneyPerCapacityPerYear);
152unit_struct!(MoneyPerActivity);
153unit_struct!(ActivityPerCapacity);
154unit_struct!(FlowPerActivity);
155
156macro_rules! impl_div {
157    ($Lhs:ident, $Rhs:ident, $Out:ident) => {
158        impl std::ops::Div<$Rhs> for $Lhs {
159            type Output = $Out;
160            fn div(self, rhs: $Rhs) -> $Out {
161                $Out(self.0 / rhs.0)
162            }
163        }
164        impl std::ops::Mul<$Rhs> for $Out {
165            type Output = $Lhs;
166            fn mul(self, by: $Rhs) -> $Lhs {
167                $Lhs(self.0 * by.0)
168            }
169        }
170        impl std::ops::Mul<$Lhs> for $Out {
171            type Output = $Rhs;
172            fn mul(self, by: $Lhs) -> $Rhs {
173                $Rhs(self.0 * by.0)
174            }
175        }
176        impl std::ops::Mul<$Out> for $Rhs {
177            type Output = $Lhs;
178            fn mul(self, by: $Out) -> $Lhs {
179                $Lhs(self.0 * by.0)
180            }
181        }
182        impl std::ops::Mul<$Out> for $Lhs {
183            type Output = $Rhs;
184            fn mul(self, by: $Out) -> $Rhs {
185                $Rhs(self.0 * by.0)
186            }
187        }
188    };
189}
190
191// Division rules for derived quantities
192impl_div!(Flow, Activity, FlowPerActivity);
193impl_div!(Money, Year, MoneyPerYear);
194impl_div!(Money, Flow, MoneyPerFlow);
195impl_div!(Money, Capacity, MoneyPerCapacity);
196impl_div!(Money, Activity, MoneyPerActivity);
197impl_div!(Activity, Capacity, ActivityPerCapacity);
198impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
199impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);