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            Deserialize,
57            derive_more::Add,
58            derive_more::Sub,
59        )]
60        pub struct $name(pub f64);
61
62        impl std::ops::Div<$name> for $name {
63            type Output = Dimensionless;
64            fn div(self, rhs: $name) -> Dimensionless {
65                Dimensionless(self.0 / rhs.0)
66            }
67        }
68        impl std::iter::Sum for $name {
69            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
70                iter.fold($name(0.0), |a, b| $name(a.0 + b.0))
71            }
72        }
73        impl AddAssign for $name {
74            fn add_assign(&mut self, other: Self) {
75                self.0 += other.0;
76            }
77        }
78        impl SubAssign for $name {
79            fn sub_assign(&mut self, other: Self) {
80                self.0 -= other.0;
81            }
82        }
83        impl fmt::Display for $name {
84            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85                write!(f, "{}", self.0)
86            }
87        }
88        impl float_cmp::ApproxEq for $name {
89            type Margin = float_cmp::F64Margin;
90            fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
91                self.0.approx_eq(other.0, margin)
92            }
93        }
94        impl std::ops::Neg for $name {
95            type Output = $name;
96            fn neg(self) -> $name {
97                $name(-self.0)
98            }
99        }
100        impl $name {
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 UnitType for $name {
135            /// Create from an f64 value
136            fn new(value: f64) -> Self {
137                Self::new(value)
138            }
139            /// Returns the underlying f64 value.
140            fn value(&self) -> f64 {
141                Self::value(&self)
142            }
143            /// Returns true if the value is a normal number.
144            fn is_normal(&self) -> bool {
145                Self::is_normal(&self)
146            }
147            /// Returns true if the value is finite.
148            fn is_finite(&self) -> bool {
149                Self::is_finite(&self)
150            }
151            /// Returns the absolute value of this unit.
152            fn abs(&self) -> Self {
153                Self::abs(&self)
154            }
155            /// Returns the max of two values
156            fn max(&self, other: Self) -> Self {
157                Self::max(&self, other)
158            }
159            /// Returns the min of two values
160            fn min(&self, other: Self) -> Self {
161                Self::min(&self, other)
162            }
163            /// Returns ordering between self and other
164            fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
165                Self::total_cmp(&self, other)
166            }
167        }
168    };
169}
170
171// Define Dimensionless first
172base_unit_struct!(Dimensionless);
173
174// Add extra methods for Dimensionless
175impl From<f64> for Dimensionless {
176    fn from(val: f64) -> Self {
177        Self(val)
178    }
179}
180impl From<Dimensionless> for f64 {
181    fn from(val: Dimensionless) -> Self {
182        val.0
183    }
184}
185
186impl Mul for Dimensionless {
187    type Output = Dimensionless;
188
189    fn mul(self, rhs: Self) -> Self::Output {
190        Dimensionless(self.0 * rhs.0)
191    }
192}
193
194impl Dimensionless {
195    /// Raises this dimensionless number to the power of `rhs`.
196    pub fn powi(self, rhs: i32) -> Self {
197        Dimensionless(self.0.powi(rhs))
198    }
199}
200
201// Define all other units with Dimensionless interactions
202macro_rules! unit_struct {
203    ($name:ident) => {
204        base_unit_struct!($name);
205
206        impl std::ops::Mul<Dimensionless> for $name {
207            type Output = $name;
208            fn mul(self, rhs: Dimensionless) -> $name {
209                $name(self.0 * rhs.0)
210            }
211        }
212        impl std::ops::Mul<$name> for Dimensionless {
213            type Output = $name;
214            fn mul(self, rhs: $name) -> $name {
215                $name(self.0 * rhs.0)
216            }
217        }
218        impl std::ops::Div<Dimensionless> for $name {
219            type Output = $name;
220            fn div(self, rhs: Dimensionless) -> $name {
221                $name(self.0 / rhs.0)
222            }
223        }
224    };
225}
226
227// Base quantities
228unit_struct!(Money);
229unit_struct!(Flow);
230unit_struct!(Activity);
231unit_struct!(Capacity);
232unit_struct!(Year);
233
234// Derived quantities
235unit_struct!(MoneyPerYear);
236unit_struct!(MoneyPerFlow);
237unit_struct!(MoneyPerCapacity);
238unit_struct!(MoneyPerCapacityPerYear);
239unit_struct!(MoneyPerActivity);
240unit_struct!(ActivityPerCapacity);
241unit_struct!(FlowPerActivity);
242unit_struct!(FlowPerCapacity);
243
244macro_rules! impl_div {
245    ($Lhs:ident, $Rhs:ident, $Out:ident) => {
246        impl std::ops::Div<$Rhs> for $Lhs {
247            type Output = $Out;
248            fn div(self, rhs: $Rhs) -> $Out {
249                $Out(self.0 / rhs.0)
250            }
251        }
252        impl std::ops::Div<$Out> for $Lhs {
253            type Output = $Rhs;
254            fn div(self, rhs: $Out) -> $Rhs {
255                $Rhs(self.0 / rhs.0)
256            }
257        }
258        impl std::ops::Mul<$Rhs> for $Out {
259            type Output = $Lhs;
260            fn mul(self, by: $Rhs) -> $Lhs {
261                $Lhs(self.0 * by.0)
262            }
263        }
264        impl std::ops::Mul<$Lhs> for $Out {
265            type Output = $Rhs;
266            fn mul(self, by: $Lhs) -> $Rhs {
267                $Rhs(self.0 * by.0)
268            }
269        }
270        impl std::ops::Mul<$Out> for $Rhs {
271            type Output = $Lhs;
272            fn mul(self, by: $Out) -> $Lhs {
273                $Lhs(self.0 * by.0)
274            }
275        }
276        impl std::ops::Mul<$Out> for $Lhs {
277            type Output = $Rhs;
278            fn mul(self, by: $Out) -> $Rhs {
279                $Rhs(self.0 * by.0)
280            }
281        }
282    };
283}
284
285// Division rules for derived quantities
286impl_div!(Flow, Activity, FlowPerActivity);
287impl_div!(Flow, Capacity, FlowPerCapacity);
288impl_div!(Money, Year, MoneyPerYear);
289impl_div!(Money, Flow, MoneyPerFlow);
290impl_div!(Money, Capacity, MoneyPerCapacity);
291impl_div!(Money, Activity, MoneyPerActivity);
292impl_div!(Activity, Capacity, ActivityPerCapacity);
293impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
294impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
295impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
296impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);