Skip to main content

physical_units/
derived.rs

1use std::ops::{Add, Div, Mul, Sub};
2
3use thiserror::Error;
4
5use crate::base::{self, BaseUnit};
6
7#[derive(Default, Clone, Copy)]
8pub struct DerivedUnit {
9    /// remaining base units
10    pub(crate) base: BaseUnit,
11
12    /// hertz (Hz)
13    pub(crate) hertz: i8,
14    /// newton (N)
15    pub(crate) newton: i8,
16    /// pascal (Pa)
17    pub(crate) pascal: i8,
18    /// joule (J)
19    pub(crate) joule: i8,
20    /// watt (W)
21    pub(crate) watt: i8,
22    /// coulomb (C)
23    pub(crate) coulomb: i8,
24    /// volt (V)
25    pub(crate) volt: i8,
26    /// farad (F)
27    pub(crate) farad: i8,
28    /// ohm (Ω)
29    pub(crate) ohm: i8,
30    /// siemens (S)
31    pub(crate) siemens: i8,
32    /// weber (Wb)
33    pub(crate) weber: i8,
34    /// tesla (T)
35    pub(crate) tesla: i8,
36    /// henry (H)
37    pub(crate) henry: i8,
38    /// lux (lx)
39    pub(crate) lux: i8,
40    /// becquerel (Bq)
41    pub(crate) becquerel: i8,
42    /// gray (Gy)
43    pub(crate) gray: i8,
44    /// sievert (Sv)
45    pub(crate) sievert: i8,
46    /// katal (kat)
47    pub(crate) katal: i8,
48}
49
50impl PartialEq for DerivedUnit {
51    fn eq(&self, other: &Self) -> bool {
52        self.to_base() == other.to_base()
53    }
54}
55impl Eq for DerivedUnit {}
56
57impl DerivedUnit {
58    pub const fn multiply(self, other: Self) -> Self {
59        Self {
60            base: self.base.multiply(other.base),
61            hertz: self.hertz + other.hertz,
62            newton: self.newton + other.newton,
63            pascal: self.pascal + other.pascal,
64            joule: self.joule + other.joule,
65            watt: self.watt + other.watt,
66            coulomb: self.coulomb + other.coulomb,
67            volt: self.volt + other.volt,
68            farad: self.farad + other.farad,
69            ohm: self.ohm + other.ohm,
70            siemens: self.siemens + other.siemens,
71            weber: self.weber + other.weber,
72            tesla: self.tesla + other.tesla,
73            henry: self.henry + other.henry,
74            lux: self.lux + other.lux,
75            becquerel: self.becquerel + other.becquerel,
76            gray: self.gray + other.gray,
77            sievert: self.sievert + other.sievert,
78            katal: self.katal + other.katal,
79        }
80    }
81
82    pub const fn divide(self, other: Self) -> Self {
83        Self {
84            base: self.base.divide(other.base),
85            hertz: self.hertz - other.hertz,
86            newton: self.newton - other.newton,
87            pascal: self.pascal - other.pascal,
88            joule: self.joule - other.joule,
89            watt: self.watt - other.watt,
90            coulomb: self.coulomb - other.coulomb,
91            volt: self.volt - other.volt,
92            farad: self.farad - other.farad,
93            ohm: self.ohm - other.ohm,
94            siemens: self.siemens - other.siemens,
95            weber: self.weber - other.weber,
96            tesla: self.tesla - other.tesla,
97            henry: self.henry - other.henry,
98            lux: self.lux - other.lux,
99            becquerel: self.becquerel - other.becquerel,
100            gray: self.gray - other.gray,
101            sievert: self.sievert - other.sievert,
102            katal: self.katal - other.katal,
103        }
104    }
105
106    pub(crate) fn magnitude(self) -> u16 {
107        self.base.magnitude()
108            + self.hertz.abs() as u16
109            + self.newton.abs() as u16
110            + self.pascal.abs() as u16
111            + self.joule.abs() as u16
112            + self.watt.abs() as u16
113            + self.coulomb.abs() as u16
114            + self.volt.abs() as u16
115            + self.farad.abs() as u16
116            + self.ohm.abs() as u16
117            + self.siemens.abs() as u16
118            + self.weber.abs() as u16
119            + self.tesla.abs() as u16
120            + self.henry.abs() as u16
121            + self.lux.abs() as u16
122            + self.becquerel.abs() as u16
123            + self.gray.abs() as u16
124            + self.sievert.abs() as u16
125            + self.katal.abs() as u16
126    }
127}
128
129impl Mul for DerivedUnit {
130    type Output = DerivedUnit;
131
132    fn mul(self, rhs: Self) -> Self::Output {
133        self.multiply(rhs)
134    }
135}
136
137impl Div for DerivedUnit {
138    type Output = DerivedUnit;
139
140    fn div(self, rhs: Self) -> Self::Output {
141        self.divide(rhs)
142    }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub struct DerivedValue<Number> {
147    pub(crate) unit: DerivedUnit,
148    pub(crate) number: Number,
149}
150
151#[derive(Error, Debug)]
152#[error("Unit '{lhs}' didn't match '{rhs}'")]
153pub struct UnitMismatch {
154    pub lhs: DerivedUnit,
155    pub rhs: DerivedUnit,
156}
157
158impl<Number> Add for DerivedValue<Number>
159where
160    Number: Add<Output = Number>,
161{
162    type Output = Result<Self, UnitMismatch>;
163
164    fn add(self, rhs: Self) -> Self::Output {
165        if self.unit == rhs.unit {
166            Ok(Self {
167                unit: self.unit,
168                number: self.number + rhs.number,
169            })
170        } else {
171            Err(UnitMismatch {
172                lhs: self.unit,
173                rhs: rhs.unit,
174            })
175        }
176    }
177}
178
179impl<Number> Sub for DerivedValue<Number>
180where
181    Number: Sub<Output = Number>,
182{
183    type Output = Result<Self, UnitMismatch>;
184
185    fn sub(self, rhs: Self) -> Self::Output {
186        if self.unit == rhs.unit {
187            Ok(Self {
188                unit: self.unit,
189                number: self.number - rhs.number,
190            })
191        } else {
192            Err(UnitMismatch {
193                lhs: self.unit,
194                rhs: rhs.unit,
195            })
196        }
197    }
198}
199
200impl<Number> Mul for DerivedValue<Number>
201where
202    Number: Mul<Output = Number>,
203{
204    type Output = Self;
205
206    fn mul(self, rhs: Self) -> Self::Output {
207        Self {
208            unit: self.unit * rhs.unit,
209            number: self.number * rhs.number,
210        }
211    }
212}
213
214impl<Number> Div for DerivedValue<Number>
215where
216    Number: Div<Output = Number>,
217{
218    type Output = Self;
219
220    fn div(self, rhs: Self) -> Self::Output {
221        Self {
222            unit: self.unit / rhs.unit,
223            number: self.number / rhs.number,
224        }
225    }
226}
227
228pub const UNITLESS: DerivedUnit = DerivedUnit {
229    base: base::UNITLESS,
230    hertz: 0,
231    newton: 0,
232    pascal: 0,
233    joule: 0,
234    watt: 0,
235    coulomb: 0,
236    volt: 0,
237    farad: 0,
238    ohm: 0,
239    siemens: 0,
240    weber: 0,
241    tesla: 0,
242    henry: 0,
243    lux: 0,
244    becquerel: 0,
245    gray: 0,
246    sievert: 0,
247    katal: 0,
248};
249
250pub const METER: DerivedUnit = DerivedUnit {
251    base: base::METER,
252    ..UNITLESS
253};
254
255pub const SECOND: DerivedUnit = DerivedUnit {
256    base: base::SECOND,
257    ..UNITLESS
258};
259
260pub const MOLE: DerivedUnit = DerivedUnit {
261    base: base::MOLE,
262    ..UNITLESS
263};
264
265pub const AMPERE: DerivedUnit = DerivedUnit {
266    base: base::AMPERE,
267    ..UNITLESS
268};
269
270pub const KELVIN: DerivedUnit = DerivedUnit {
271    base: base::KELVIN,
272    ..UNITLESS
273};
274
275pub const CANDELA: DerivedUnit = DerivedUnit {
276    base: base::CANDELA,
277    ..UNITLESS
278};
279
280pub const KILOGRAM: DerivedUnit = DerivedUnit {
281    base: base::KILOGRAM,
282    ..UNITLESS
283};
284
285pub const METER_SQ: DerivedUnit = DerivedUnit {
286    base: base::METER_SQ,
287    ..UNITLESS
288};
289
290/// hertz (Hz)
291pub const HERTZ: DerivedUnit = DerivedUnit {
292    hertz: 1,
293    ..UNITLESS
294};
295
296/// newton (N)
297pub const NEWTON: DerivedUnit = DerivedUnit {
298    newton: 1,
299    ..UNITLESS
300};
301
302/// pascal (Pa)
303pub const PASCAL: DerivedUnit = DerivedUnit {
304    pascal: 1,
305    ..UNITLESS
306};
307
308/// joule (J)
309pub const JOULE: DerivedUnit = DerivedUnit {
310    joule: 1,
311    ..UNITLESS
312};
313
314/// watt (W)
315pub const WATT: DerivedUnit = DerivedUnit {
316    watt: 1,
317    ..UNITLESS
318};
319
320/// coulomb (C)
321pub const COULOMB: DerivedUnit = DerivedUnit {
322    coulomb: 1,
323    ..UNITLESS
324};
325
326/// volt (V)
327pub const VOLT: DerivedUnit = DerivedUnit {
328    volt: 1,
329    ..UNITLESS
330};
331
332/// farad (F)
333pub const FARAD: DerivedUnit = DerivedUnit {
334    farad: 1,
335    ..UNITLESS
336};
337
338/// ohm (Ω)
339pub const OHM: DerivedUnit = DerivedUnit { ohm: 1, ..UNITLESS };
340
341/// siemens (S)
342pub const SIEMENS: DerivedUnit = DerivedUnit {
343    siemens: 1,
344    ..UNITLESS
345};
346
347/// weber (Wb)
348pub const WEBER: DerivedUnit = DerivedUnit {
349    weber: 1,
350    ..UNITLESS
351};
352
353/// tesla (T)
354pub const TESLA: DerivedUnit = DerivedUnit {
355    tesla: 1,
356    ..UNITLESS
357};
358
359/// henry (H)
360pub const HENRY: DerivedUnit = DerivedUnit {
361    henry: 1,
362    ..UNITLESS
363};
364
365// TODO: celsius (C)
366
367// TODO: lumen (lm)
368
369
370/// lux (lx)
371pub const LUX: DerivedUnit = DerivedUnit { lux: 1, ..UNITLESS };
372
373/// becquerel (Bq)
374pub const BECQUEREL: DerivedUnit = DerivedUnit {
375    becquerel: 1,
376    ..UNITLESS
377};
378
379/// gray (Gy)
380pub const GRAY: DerivedUnit = DerivedUnit {
381    gray: 1,
382    ..UNITLESS
383};
384
385/// sievert (Sv)
386pub const SIEVERT: DerivedUnit = DerivedUnit {
387    sievert: 1,
388    ..UNITLESS
389};
390
391/// katal (kat)
392pub const KATAL: DerivedUnit = DerivedUnit {
393    katal: 1,
394    ..UNITLESS
395};
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn test_base_identities() {
403        assert_eq!(HERTZ, UNITLESS / SECOND);
404
405        assert_eq!(
406            NEWTON,
407            KILOGRAM * METER / (SECOND * SECOND)
408        );
409
410        assert_eq!(PASCAL, NEWTON / (METER * METER));
411
412        assert_eq!(JOULE, METER * NEWTON);
413        assert_eq!(JOULE, COULOMB * VOLT);
414        assert_eq!(JOULE, WATT * SECOND);
415
416        assert_eq!(WATT, JOULE / SECOND);
417        assert_eq!(WATT, VOLT * AMPERE);
418
419        assert_eq!(COULOMB, SECOND * AMPERE);
420        assert_eq!(COULOMB, FARAD * VOLT);
421
422        assert_eq!(VOLT, WATT / AMPERE);
423        assert_eq!(VOLT, JOULE / COULOMB);
424
425        assert_eq!(FARAD, COULOMB / VOLT);
426        assert_eq!(FARAD, SECOND / OHM);
427
428        assert_eq!(OHM, UNITLESS / SIEMENS);
429        assert_eq!(OHM, VOLT / AMPERE);
430
431        assert_eq!(SIEMENS, UNITLESS / OHM);
432        assert_eq!(SIEMENS, AMPERE / VOLT);
433
434        assert_eq!(WEBER, JOULE / AMPERE);
435        assert_eq!(WEBER, TESLA * METER * METER);
436        assert_eq!(WEBER, VOLT * SECOND);
437
438        assert_eq!(
439            TESLA,
440            VOLT * SECOND / (METER * METER)
441        );
442        assert_eq!(TESLA, WEBER / (METER * METER));
443        assert_eq!(TESLA, NEWTON / (AMPERE * METER));
444
445        assert_eq!(HENRY, VOLT * SECOND / AMPERE);
446        assert_eq!(HENRY, OHM * SECOND);
447        assert_eq!(HENRY, WEBER / AMPERE);
448
449        assert_eq!(LUX, CANDELA / (METER * METER));
450
451        assert_eq!(BECQUEREL, UNITLESS / SECOND);
452
453        assert_eq!(GRAY, JOULE / KILOGRAM);
454
455        assert_eq!(SIEVERT, JOULE / KILOGRAM);
456
457        assert_eq!(KATAL, MOLE / SECOND);
458    }
459}