1use 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
9pub 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 fn new(value: f64) -> Self;
29 fn value(&self) -> f64;
31 fn is_normal(&self) -> bool;
33 fn is_finite(&self) -> bool;
35 fn abs(&self) -> Self;
37 fn max(&self, other: Self) -> Self;
39 fn min(&self, other: Self) -> Self;
41 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering;
43}
44
45macro_rules! base_unit_struct {
46 ($name:ident) => {
47 #[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 pub fn new(value: f64) -> Self {
103 $name(value)
104 }
105 pub fn value(&self) -> f64 {
107 self.0
108 }
109 pub fn is_normal(&self) -> bool {
111 self.0.is_normal()
112 }
113 pub fn is_finite(&self) -> bool {
115 self.0.is_finite()
116 }
117 pub fn abs(&self) -> Self {
119 $name(self.0.abs())
120 }
121 pub fn max(&self, other: Self) -> Self {
123 Self(self.0.max(other.0))
124 }
125 pub fn min(&self, other: Self) -> Self {
127 Self(self.0.min(other.0))
128 }
129 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 fn new(value: f64) -> Self {
137 Self::new(value)
138 }
139 fn value(&self) -> f64 {
141 Self::value(&self)
142 }
143 fn is_normal(&self) -> bool {
145 Self::is_normal(&self)
146 }
147 fn is_finite(&self) -> bool {
149 Self::is_finite(&self)
150 }
151 fn abs(&self) -> Self {
153 Self::abs(&self)
154 }
155 fn max(&self, other: Self) -> Self {
157 Self::max(&self, other)
158 }
159 fn min(&self, other: Self) -> Self {
161 Self::min(&self, other)
162 }
163 fn total_cmp(&self, other: &Self) -> std::cmp::Ordering {
165 Self::total_cmp(&self, other)
166 }
167 }
168 };
169}
170
171base_unit_struct!(Dimensionless);
173
174impl 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 pub fn powi(self, rhs: i32) -> Self {
197 Dimensionless(self.0.powi(rhs))
198 }
199}
200
201macro_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
227unit_struct!(Money);
229unit_struct!(Flow);
230unit_struct!(Activity);
231unit_struct!(Capacity);
232unit_struct!(Year);
233
234unit_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
285impl_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);