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