1use std::ops::{Add, Div, Mul, Sub};
2
3use thiserror::Error;
4
5#[derive(Default, Clone, Copy, PartialEq, Eq)]
6pub struct BaseUnit {
7 pub(crate) kilogram: i8,
9 pub(crate) meter: i8,
11 pub(crate) second: i8,
13 pub(crate) mole: i8,
15 pub(crate) ampere: i8,
17 pub(crate) kelvin: i8,
19 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
168pub const METER: BaseUnit = BaseUnit {
170 meter: 1,
171 ..UNITLESS
172};
173
174pub const SECOND: BaseUnit = BaseUnit {
176 meter: 0,
177 second: 1,
178 ..UNITLESS
179};
180
181pub const MOLE: BaseUnit = BaseUnit {
183 mole: 1,
184 ..UNITLESS
185};
186
187pub const AMPERE: BaseUnit = BaseUnit {
189 ampere: 1,
190 ..UNITLESS
191};
192
193pub const KELVIN: BaseUnit = BaseUnit {
195 kelvin: 1,
196 ..UNITLESS
197};
198
199pub const CANDELA: BaseUnit = BaseUnit {
201 candela: 1,
202 ..UNITLESS
203};
204
205pub const KILOGRAM: BaseUnit = BaseUnit {
207 kilogram: 1,
208 ..UNITLESS
209};
210
211pub const METER_SQ: BaseUnit = BaseUnit {
213 meter: 2,
214 ..UNITLESS
215};
216
217pub const HERTZ: BaseUnit = BaseUnit {
219 second: -1,
220 ..UNITLESS
221};
222
223pub const NEWTON: BaseUnit = BaseUnit {
225 kilogram: 1,
226 meter: 1,
227 second: -2,
228 ..UNITLESS
229};
230
231pub const PASCAL: BaseUnit = BaseUnit {
233 kilogram: 1,
234 meter: -1,
235 second: -2,
236 ..UNITLESS
237};
238
239pub const JOULE: BaseUnit = BaseUnit {
241 kilogram: 1,
242 meter: 2,
243 second: -2,
244 ..UNITLESS
245};
246
247pub const WATT: BaseUnit = BaseUnit {
249 meter: 2,
250 second: -3,
251 kilogram: 1,
252 ..UNITLESS
253};
254
255pub const COULOMB: BaseUnit = BaseUnit {
257 second: 1,
258 ampere: 1,
259 ..UNITLESS
260};
261
262pub const VOLT: BaseUnit = BaseUnit {
264 meter: 2,
265 second: -3,
266 ampere: -1,
267 kilogram: 1,
268 ..UNITLESS
269};
270
271pub const FARAD: BaseUnit = BaseUnit {
273 meter: -2,
274 second: 4,
275 ampere: 2,
276 kilogram: -1,
277 ..UNITLESS
278};
279
280pub const OHM: BaseUnit = BaseUnit {
282 kilogram: 1,
283 meter: 2,
284 second: -3,
285 ampere: -2,
286 ..UNITLESS
287};
288
289pub const SIEMENS: BaseUnit = BaseUnit {
291 kilogram: -1,
292 meter: -2,
293 second: 3,
294 ampere: 2,
295 ..UNITLESS
296};
297
298pub const WEBER: BaseUnit = BaseUnit {
300 kilogram: 1,
301 meter: 2,
302 second: -2,
303 ampere: -1,
304 ..UNITLESS
305};
306
307pub const TESLA: BaseUnit = BaseUnit {
309 kilogram: 1,
310 second: -2,
311 ampere: -1,
312 ..UNITLESS
313};
314
315pub const HENRY: BaseUnit = BaseUnit {
317 kilogram: 1,
318 meter: 2,
319 second: -2,
320 ampere: -2,
321 ..UNITLESS
322};
323
324pub const LUX: BaseUnit = BaseUnit {
326 meter: -2,
327 candela: 1,
328 ..UNITLESS
329};
330
331pub const BECQUEREL: BaseUnit = BaseUnit {
333 second: -1,
334 ..UNITLESS
335};
336
337pub const GRAY: BaseUnit = BaseUnit {
339 meter: 2,
340 second: -2,
341 ..UNITLESS
342};
343
344pub const SIEVERT: BaseUnit = BaseUnit {
346 meter: 2,
347 second: -2,
348 ..UNITLESS
349};
350
351pub 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 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 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 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 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}