physical_units/
base.rs

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