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 pub(crate) base: BaseUnit,
11
12 pub(crate) hertz: i8,
14 pub(crate) newton: i8,
16 pub(crate) pascal: i8,
18 pub(crate) joule: i8,
20 pub(crate) watt: i8,
22 pub(crate) coulomb: i8,
24 pub(crate) volt: i8,
26 pub(crate) farad: i8,
28 pub(crate) ohm: i8,
30 pub(crate) siemens: i8,
32 pub(crate) weber: i8,
34 pub(crate) tesla: i8,
36 pub(crate) henry: i8,
38 pub(crate) lux: i8,
40 pub(crate) becquerel: i8,
42 pub(crate) gray: i8,
44 pub(crate) sievert: i8,
46 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
290pub const HERTZ: DerivedUnit = DerivedUnit {
292 hertz: 1,
293 ..UNITLESS
294};
295
296pub const NEWTON: DerivedUnit = DerivedUnit {
298 newton: 1,
299 ..UNITLESS
300};
301
302pub const PASCAL: DerivedUnit = DerivedUnit {
304 pascal: 1,
305 ..UNITLESS
306};
307
308pub const JOULE: DerivedUnit = DerivedUnit {
310 joule: 1,
311 ..UNITLESS
312};
313
314pub const WATT: DerivedUnit = DerivedUnit {
316 watt: 1,
317 ..UNITLESS
318};
319
320pub const COULOMB: DerivedUnit = DerivedUnit {
322 coulomb: 1,
323 ..UNITLESS
324};
325
326pub const VOLT: DerivedUnit = DerivedUnit {
328 volt: 1,
329 ..UNITLESS
330};
331
332pub const FARAD: DerivedUnit = DerivedUnit {
334 farad: 1,
335 ..UNITLESS
336};
337
338pub const OHM: DerivedUnit = DerivedUnit { ohm: 1, ..UNITLESS };
340
341pub const SIEMENS: DerivedUnit = DerivedUnit {
343 siemens: 1,
344 ..UNITLESS
345};
346
347pub const WEBER: DerivedUnit = DerivedUnit {
349 weber: 1,
350 ..UNITLESS
351};
352
353pub const TESLA: DerivedUnit = DerivedUnit {
355 tesla: 1,
356 ..UNITLESS
357};
358
359pub const HENRY: DerivedUnit = DerivedUnit {
361 henry: 1,
362 ..UNITLESS
363};
364
365pub const LUX: DerivedUnit = DerivedUnit { lux: 1, ..UNITLESS };
372
373pub const BECQUEREL: DerivedUnit = DerivedUnit {
375 becquerel: 1,
376 ..UNITLESS
377};
378
379pub const GRAY: DerivedUnit = DerivedUnit {
381 gray: 1,
382 ..UNITLESS
383};
384
385pub const SIEVERT: DerivedUnit = DerivedUnit {
387 sievert: 1,
388 ..UNITLESS
389};
390
391pub 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}