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 unit type representing a dimensionless 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            /// Create from an f64 value
101            pub fn new(value: f64) -> Self {
102                $name(value)
103            }
104            /// Returns the underlying f64 value.
105            pub fn value(&self) -> f64 {
106                self.0
107            }
108            /// Returns true if the value is a normal number.
109            pub fn is_normal(&self) -> bool {
110                self.0.is_normal()
111            }
112            /// Returns true if the value is finite.
113            pub fn is_finite(&self) -> bool {
114                self.0.is_finite()
115            }
116            /// Returns the absolute value of this unit.
117            pub fn abs(&self) -> Self {
118                $name(self.0.abs())
119            }
120            /// Returns the max of two values
121            pub fn max(&self, other: Self) -> Self {
122                Self(self.0.max(other.0))
123            }
124            /// Returns the min of two values
125            pub fn min(&self, other: Self) -> Self {
126                Self(self.0.min(other.0))
127            }
128            /// Returns ordering between self and other
129            pub fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
130                self.0.total_cmp(&other.0)
131            }
132        }
133        impl<'de> Deserialize<'de> for $name {
134            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135            where
136                D: serde::Deserializer<'de>,
137            {
138                let value = f64::deserialize(deserializer)?;
139                if !value.is_finite() {
140                    Err(serde::de::Error::custom("Value cannot be NaN or infinite"))?;
141                }
142
143                Ok($name(value))
144            }
145        }
146        impl UnitType for $name {
147            /// Create from an f64 value
148            fn new(value: f64) -> Self {
149                Self::new(value)
150            }
151            /// Returns the underlying f64 value.
152            fn value(&self) -> f64 {
153                Self::value(&self)
154            }
155            /// Returns true if the value is a normal number.
156            fn is_normal(&self) -> bool {
157                Self::is_normal(&self)
158            }
159            /// Returns true if the value is finite.
160            fn is_finite(&self) -> bool {
161                Self::is_finite(&self)
162            }
163            /// Returns the absolute value of this unit.
164            fn abs(&self) -> Self {
165                Self::abs(&self)
166            }
167            /// Returns the max of two values
168            fn max(&self, other: Self) -> Self {
169                Self::max(&self, other)
170            }
171            /// Returns the min of two values
172            fn min(&self, other: Self) -> Self {
173                Self::min(&self, other)
174            }
175            /// Returns ordering between self and other
176            fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
177                Self::total_cmp(&self, other)
178            }
179        }
180    };
181}
182
183// Define Dimensionless first
184base_unit_struct!(Dimensionless);
185
186// Add extra methods for Dimensionless
187impl From<f64> for Dimensionless {
188    fn from(val: f64) -> Self {
189        Self(val)
190    }
191}
192impl From<Dimensionless> for f64 {
193    fn from(val: Dimensionless) -> Self {
194        val.0
195    }
196}
197
198impl Mul for Dimensionless {
199    type Output = Dimensionless;
200
201    fn mul(self, rhs: Self) -> Self::Output {
202        Dimensionless(self.0 * rhs.0)
203    }
204}
205
206impl Dimensionless {
207    /// Raises this dimensionless number to the power of `rhs`.
208    pub fn powi(self, rhs: i32) -> Self {
209        Dimensionless(self.0.powi(rhs))
210    }
211}
212
213// Define all other units with Dimensionless interactions
214macro_rules! unit_struct {
215    ($name:ident) => {
216        base_unit_struct!($name);
217
218        impl std::ops::Mul<Dimensionless> for $name {
219            type Output = $name;
220            fn mul(self, rhs: Dimensionless) -> $name {
221                $name(self.0 * rhs.0)
222            }
223        }
224        impl std::ops::Mul<$name> for Dimensionless {
225            type Output = $name;
226            fn mul(self, rhs: $name) -> $name {
227                $name(self.0 * rhs.0)
228            }
229        }
230        impl std::ops::Div<Dimensionless> for $name {
231            type Output = $name;
232            fn div(self, rhs: Dimensionless) -> $name {
233                $name(self.0 / rhs.0)
234            }
235        }
236    };
237}
238
239// Base quantities
240unit_struct!(Money);
241unit_struct!(Flow);
242unit_struct!(Activity);
243unit_struct!(Capacity);
244unit_struct!(Year);
245
246// Derived quantities
247unit_struct!(MoneyPerYear);
248unit_struct!(MoneyPerFlow);
249unit_struct!(MoneyPerCapacity);
250unit_struct!(MoneyPerCapacityPerYear);
251unit_struct!(MoneyPerActivity);
252unit_struct!(ActivityPerCapacity);
253unit_struct!(FlowPerActivity);
254unit_struct!(FlowPerCapacity);
255
256macro_rules! impl_div {
257    ($Lhs:ident, $Rhs:ident, $Out:ident) => {
258        impl std::ops::Div<$Rhs> for $Lhs {
259            type Output = $Out;
260            fn div(self, rhs: $Rhs) -> $Out {
261                $Out(self.0 / rhs.0)
262            }
263        }
264        impl std::ops::Div<$Out> for $Lhs {
265            type Output = $Rhs;
266            fn div(self, rhs: $Out) -> $Rhs {
267                $Rhs(self.0 / rhs.0)
268            }
269        }
270        impl std::ops::Mul<$Rhs> for $Out {
271            type Output = $Lhs;
272            fn mul(self, by: $Rhs) -> $Lhs {
273                $Lhs(self.0 * by.0)
274            }
275        }
276        impl std::ops::Mul<$Lhs> for $Out {
277            type Output = $Rhs;
278            fn mul(self, by: $Lhs) -> $Rhs {
279                $Rhs(self.0 * by.0)
280            }
281        }
282        impl std::ops::Mul<$Out> for $Rhs {
283            type Output = $Lhs;
284            fn mul(self, by: $Out) -> $Lhs {
285                $Lhs(self.0 * by.0)
286            }
287        }
288        impl std::ops::Mul<$Out> for $Lhs {
289            type Output = $Rhs;
290            fn mul(self, by: $Out) -> $Rhs {
291                $Rhs(self.0 * by.0)
292            }
293        }
294    };
295}
296
297// Division rules for derived quantities
298impl_div!(Flow, Activity, FlowPerActivity);
299impl_div!(Flow, Capacity, FlowPerCapacity);
300impl_div!(Money, Year, MoneyPerYear);
301impl_div!(Money, Flow, MoneyPerFlow);
302impl_div!(Money, Capacity, MoneyPerCapacity);
303impl_div!(Money, Activity, MoneyPerActivity);
304impl_div!(Activity, Capacity, ActivityPerCapacity);
305impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
306impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
307impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
308impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);