muse2/
units.rs

1//! This module defines various unit types and their conversions.
2
3use float_cmp::{ApproxEq, F64Margin};
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::iter::Sum;
7use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
8
9/// A trait encompassing most of the functionality of unit types
10pub trait UnitType:
11    fmt::Debug
12    + Copy
13    + PartialEq
14    + PartialOrd
15    + Serialize
16    + Add
17    + Sub
18    + Div
19    + Mul<Dimensionless, Output = Self>
20    + AddAssign
21    + SubAssign
22    + Sum
23    + ApproxEq<Margin = F64Margin>
24    + fmt::Display
25    + Default
26{
27    /// Create from an f64 value
28    fn new(value: f64) -> Self;
29    /// Returns the underlying f64 value.
30    fn value(&self) -> f64;
31    /// Returns true if the value is a normal number.
32    fn is_normal(&self) -> bool;
33    /// Returns true if the value is finite.
34    fn is_finite(&self) -> bool;
35    /// Returns the absolute value of this unit.
36    fn abs(&self) -> Self;
37    /// Returns the max of two values
38    fn max(&self, other: Self) -> Self;
39    /// Returns the min of two values
40    fn min(&self, other: Self) -> Self;
41    /// Returns ordering between self and other
42    fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
43}
44
45macro_rules! base_unit_struct {
46    ($name:ident) => {
47        /// A basic unit type wrapper around an `f64` scalar value.
48        #[derive(
49            Debug,
50            Clone,
51            Copy,
52            PartialEq,
53            PartialOrd,
54            Default,
55            Serialize,
56            derive_more::Add,
57            derive_more::Sub,
58        )]
59        pub struct $name(pub f64);
60
61        impl std::ops::Div<$name> for $name {
62            type Output = Dimensionless;
63            fn div(self, rhs: $name) -> Dimensionless {
64                Dimensionless(self.0 / rhs.0)
65            }
66        }
67        impl std::iter::Sum for $name {
68            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
69                iter.fold($name(0.0), |a, b| $name(a.0 + b.0))
70            }
71        }
72        impl AddAssign for $name {
73            fn add_assign(&mut self, other: Self) {
74                self.0 += other.0;
75            }
76        }
77        impl SubAssign for $name {
78            fn sub_assign(&mut self, other: Self) {
79                self.0 -= other.0;
80            }
81        }
82        impl fmt::Display for $name {
83            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84                write!(f, "{}", self.0)
85            }
86        }
87        impl float_cmp::ApproxEq for $name {
88            type Margin = float_cmp::F64Margin;
89            fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
90                self.0.approx_eq(other.0, margin)
91            }
92        }
93        impl std::ops::Neg for $name {
94            type Output = $name;
95            fn neg(self) -> $name {
96                $name(-self.0)
97            }
98        }
99        impl $name {
100            /// Small epsilon constant for this unit type.
101            pub const EPSILON: $name = $name(f64::EPSILON);
102
103            /// Create from an f64 value
104            pub fn new(value: f64) -> Self {
105                $name(value)
106            }
107            /// Returns the underlying f64 value.
108            pub fn value(&self) -> f64 {
109                self.0
110            }
111            /// Returns true if the value is a normal number.
112            pub fn is_normal(&self) -> bool {
113                self.0.is_normal()
114            }
115            /// Returns true if the value is finite.
116            pub fn is_finite(&self) -> bool {
117                self.0.is_finite()
118            }
119            /// Returns the absolute value of this unit.
120            pub fn abs(&self) -> Self {
121                $name(self.0.abs())
122            }
123            /// Returns the max of two values
124            pub fn max(&self, other: Self) -> Self {
125                Self(self.0.max(other.0))
126            }
127            /// Returns the min of two values
128            pub fn min(&self, other: Self) -> Self {
129                Self(self.0.min(other.0))
130            }
131            /// Returns ordering between self and other
132            pub fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
133                self.0.total_cmp(&other.0)
134            }
135        }
136        impl<'de> Deserialize<'de> for $name {
137            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138            where
139                D: serde::Deserializer<'de>,
140            {
141                let value = f64::deserialize(deserializer)?;
142                if !value.is_finite() {
143                    Err(serde::de::Error::custom("Value cannot be NaN or infinite"))?;
144                }
145
146                Ok($name(value))
147            }
148        }
149        impl UnitType for $name {
150            /// Create from an f64 value
151            fn new(value: f64) -> Self {
152                Self::new(value)
153            }
154            /// Returns the underlying f64 value.
155            fn value(&self) -> f64 {
156                Self::value(&self)
157            }
158            /// Returns true if the value is a normal number.
159            fn is_normal(&self) -> bool {
160                Self::is_normal(&self)
161            }
162            /// Returns true if the value is finite.
163            fn is_finite(&self) -> bool {
164                Self::is_finite(&self)
165            }
166            /// Returns the absolute value of this unit.
167            fn abs(&self) -> Self {
168                Self::abs(&self)
169            }
170            /// Returns the max of two values
171            fn max(&self, other: Self) -> Self {
172                Self::max(&self, other)
173            }
174            /// Returns the min of two values
175            fn min(&self, other: Self) -> Self {
176                Self::min(&self, other)
177            }
178            /// Returns ordering between self and other
179            fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
180                Self::total_cmp(&self, other)
181            }
182        }
183    };
184}
185
186// Define Dimensionless first
187base_unit_struct!(Dimensionless);
188
189// Add extra methods for Dimensionless
190impl From<f64> for Dimensionless {
191    fn from(val: f64) -> Self {
192        Self(val)
193    }
194}
195impl From<Dimensionless> for f64 {
196    fn from(val: Dimensionless) -> Self {
197        val.0
198    }
199}
200
201impl Mul for Dimensionless {
202    type Output = Dimensionless;
203
204    fn mul(self, rhs: Self) -> Self::Output {
205        Dimensionless(self.0 * rhs.0)
206    }
207}
208
209impl Dimensionless {
210    /// Raises this dimensionless number to the power of `rhs`.
211    pub fn powi(self, rhs: i32) -> Self {
212        Dimensionless(self.0.powi(rhs))
213    }
214}
215
216// Define all other units with Dimensionless interactions
217macro_rules! unit_struct {
218    ($name:ident) => {
219        base_unit_struct!($name);
220
221        impl std::ops::Mul<Dimensionless> for $name {
222            type Output = $name;
223            fn mul(self, rhs: Dimensionless) -> $name {
224                $name(self.0 * rhs.0)
225            }
226        }
227        impl std::ops::Mul<$name> for Dimensionless {
228            type Output = $name;
229            fn mul(self, rhs: $name) -> $name {
230                $name(self.0 * rhs.0)
231            }
232        }
233        impl std::ops::Div<Dimensionless> for $name {
234            type Output = $name;
235            fn div(self, rhs: Dimensionless) -> $name {
236                $name(self.0 / rhs.0)
237            }
238        }
239    };
240}
241
242// Base quantities
243unit_struct!(Money);
244unit_struct!(Flow);
245unit_struct!(Activity);
246unit_struct!(Capacity);
247unit_struct!(Year);
248
249// Derived quantities
250unit_struct!(MoneyPerYear);
251unit_struct!(MoneyPerFlow);
252unit_struct!(MoneyPerCapacity);
253unit_struct!(MoneyPerCapacityPerYear);
254unit_struct!(MoneyPerActivity);
255unit_struct!(ActivityPerCapacity);
256unit_struct!(FlowPerActivity);
257unit_struct!(FlowPerCapacity);
258
259macro_rules! impl_div {
260    ($Lhs:ident, $Rhs:ident, $Out:ident) => {
261        impl std::ops::Div<$Rhs> for $Lhs {
262            type Output = $Out;
263            fn div(self, rhs: $Rhs) -> $Out {
264                $Out(self.0 / rhs.0)
265            }
266        }
267        impl std::ops::Div<$Out> for $Lhs {
268            type Output = $Rhs;
269            fn div(self, rhs: $Out) -> $Rhs {
270                $Rhs(self.0 / rhs.0)
271            }
272        }
273        impl std::ops::Mul<$Rhs> for $Out {
274            type Output = $Lhs;
275            fn mul(self, by: $Rhs) -> $Lhs {
276                $Lhs(self.0 * by.0)
277            }
278        }
279        impl std::ops::Mul<$Lhs> for $Out {
280            type Output = $Rhs;
281            fn mul(self, by: $Lhs) -> $Rhs {
282                $Rhs(self.0 * by.0)
283            }
284        }
285        impl std::ops::Mul<$Out> for $Rhs {
286            type Output = $Lhs;
287            fn mul(self, by: $Out) -> $Lhs {
288                $Lhs(self.0 * by.0)
289            }
290        }
291        impl std::ops::Mul<$Out> for $Lhs {
292            type Output = $Rhs;
293            fn mul(self, by: $Out) -> $Rhs {
294                $Rhs(self.0 * by.0)
295            }
296        }
297    };
298}
299
300// Division rules for derived quantities
301impl_div!(Flow, Activity, FlowPerActivity);
302impl_div!(Flow, Capacity, FlowPerCapacity);
303impl_div!(Money, Year, MoneyPerYear);
304impl_div!(Money, Flow, MoneyPerFlow);
305impl_div!(Money, Capacity, MoneyPerCapacity);
306impl_div!(Money, Activity, MoneyPerActivity);
307impl_div!(Activity, Capacity, ActivityPerCapacity);
308impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
309impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
310impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
311impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);