Skip to main content

muse2/
units.rs

1//! This module defines various unit types and their conversions.
2use float_cmp::{ApproxEq, F64Margin};
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::iter::Sum;
6use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
7use std::str::FromStr;
8
9/// A trait encompassing most of the functionality of unit types
10pub trait UnitType:
11    fmt::Debug
12    + Copy
13    + FromStr
14    + PartialEq
15    + PartialOrd
16    + Serialize
17    + Add
18    + Sub
19    + Div
20    + Neg
21    + Mul<Dimensionless, Output = Self>
22    + AddAssign
23    + SubAssign
24    + Sum
25    + ApproxEq<Margin = F64Margin>
26    + fmt::Display
27{
28    /// Create from an f64 value
29    fn new(value: f64) -> Self;
30    /// Returns the underlying f64 value.
31    fn value(&self) -> f64;
32    /// Returns true if the value is a normal number.
33    fn is_normal(&self) -> bool;
34    /// Returns true if the value is finite.
35    fn is_finite(&self) -> bool;
36    /// Returns the absolute value of this unit.
37    fn abs(&self) -> Self;
38    /// Returns the max of two values
39    fn max(&self, other: Self) -> Self;
40    /// Returns the min of two values
41    fn min(&self, other: Self) -> Self;
42    /// Returns ordering between self and other
43    fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
44}
45
46macro_rules! base_unit_struct {
47    ($name:ident $(, $extra_derive:path)* $(,)?) => {
48        /// A basic unit type wrapper around an `f64` scalar value.
49        #[derive(
50            Debug,
51            Clone,
52            Copy,
53            PartialEq,
54            PartialOrd,
55            Serialize,
56            derive_more::Add,
57            derive_more::Sub,
58            derive_more::AddAssign,
59            derive_more::SubAssign,
60            derive_more::Neg,
61            derive_more::Sum,
62            derive_more::Display,
63            derive_more::FromStr,
64            $($extra_derive,)*
65        )]
66        pub struct $name(pub f64);
67
68        impl std::ops::Div<$name> for $name {
69            type Output = Dimensionless;
70            fn div(self, rhs: $name) -> Dimensionless {
71                Dimensionless(self.0 / rhs.0)
72            }
73        }
74        impl std::ops::Mul<Dimensionless> for $name {
75            type Output = $name;
76            fn mul(self, rhs: Dimensionless) -> $name {
77                $name(self.0 * rhs.0)
78            }
79        }
80        impl std::ops::MulAssign<Dimensionless> for $name {
81            fn mul_assign(&mut self, rhs: Dimensionless) {
82                self.0 *= rhs.0;
83            }
84        }
85        impl std::ops::DivAssign<Dimensionless> for $name {
86            fn div_assign(&mut self, rhs: Dimensionless) {
87                self.0 /= rhs.0;
88            }
89        }
90        impl float_cmp::ApproxEq for $name {
91            type Margin = float_cmp::F64Margin;
92            fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
93                self.0.approx_eq(other.0, margin)
94            }
95        }
96        impl $name {
97            /// Small epsilon constant for this unit type.
98            pub const EPSILON: $name = $name(f64::EPSILON);
99
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, derive_more::From, derive_more::Into);
185
186impl Dimensionless {
187    /// Raises this dimensionless number to the power of `rhs`.
188    pub fn powi(self, rhs: i32) -> Self {
189        Dimensionless(self.0.powi(rhs))
190    }
191}
192
193// Define all other units with Dimensionless interactions
194macro_rules! unit_struct {
195    ($name:ident) => {
196        base_unit_struct!($name);
197
198        impl std::ops::Mul<$name> for Dimensionless {
199            type Output = $name;
200            fn mul(self, rhs: $name) -> $name {
201                $name(self.0 * rhs.0)
202            }
203        }
204        impl std::ops::Div<Dimensionless> for $name {
205            type Output = $name;
206            fn div(self, rhs: Dimensionless) -> $name {
207                $name(self.0 / rhs.0)
208            }
209        }
210    };
211}
212
213// Base quantities
214unit_struct!(Money);
215unit_struct!(Flow);
216unit_struct!(Activity);
217unit_struct!(Capacity);
218unit_struct!(Year);
219
220// Derived quantities
221unit_struct!(MoneyPerYear);
222unit_struct!(MoneyPerFlow);
223unit_struct!(MoneyPerCapacity);
224unit_struct!(MoneyPerCapacityPerYear);
225unit_struct!(MoneyPerActivity);
226unit_struct!(ActivityPerCapacity);
227unit_struct!(FlowPerActivity);
228unit_struct!(FlowPerCapacity);
229unit_struct!(CapacityPerYear);
230
231macro_rules! impl_div {
232    ($Lhs:ident, $Rhs:ident, $Out:ident) => {
233        impl std::ops::Div<$Rhs> for $Lhs {
234            type Output = $Out;
235            fn div(self, rhs: $Rhs) -> $Out {
236                $Out(self.0 / rhs.0)
237            }
238        }
239        impl std::ops::Div<$Out> for $Lhs {
240            type Output = $Rhs;
241            fn div(self, rhs: $Out) -> $Rhs {
242                $Rhs(self.0 / rhs.0)
243            }
244        }
245        impl std::ops::Mul<$Rhs> for $Out {
246            type Output = $Lhs;
247            fn mul(self, by: $Rhs) -> $Lhs {
248                $Lhs(self.0 * by.0)
249            }
250        }
251        impl std::ops::Mul<$Lhs> for $Out {
252            type Output = $Rhs;
253            fn mul(self, by: $Lhs) -> $Rhs {
254                $Rhs(self.0 * by.0)
255            }
256        }
257        impl std::ops::Mul<$Out> for $Rhs {
258            type Output = $Lhs;
259            fn mul(self, by: $Out) -> $Lhs {
260                $Lhs(self.0 * by.0)
261            }
262        }
263        impl std::ops::Mul<$Out> for $Lhs {
264            type Output = $Rhs;
265            fn mul(self, by: $Out) -> $Rhs {
266                $Rhs(self.0 * by.0)
267            }
268        }
269    };
270}
271
272// Division rules for derived quantities
273impl_div!(Flow, Activity, FlowPerActivity);
274impl_div!(Flow, Capacity, FlowPerCapacity);
275impl_div!(Money, Year, MoneyPerYear);
276impl_div!(Money, Flow, MoneyPerFlow);
277impl_div!(Money, Capacity, MoneyPerCapacity);
278impl_div!(Money, Activity, MoneyPerActivity);
279impl_div!(Activity, Capacity, ActivityPerCapacity);
280impl_div!(MoneyPerYear, Capacity, MoneyPerCapacityPerYear);
281impl_div!(MoneyPerActivity, FlowPerActivity, MoneyPerFlow);
282impl_div!(MoneyPerCapacity, Year, MoneyPerCapacityPerYear);
283impl_div!(FlowPerCapacity, ActivityPerCapacity, FlowPerActivity);
284impl_div!(Capacity, Year, CapacityPerYear);