1use super::*;
6use std::fmt;
7use std::ops::Index;
8
9pub enum BaseDimension {
11 Length = 0,
13 Time = 1,
15 Mass = 2,
17 PlaneAngle = 3,
19 Temperature = 4,
21 ElectricCharge = 5,
23 LuminousIntensity = 6,
25}
26
27const DIMENSION_SIZE: usize = 7;
28const SYMBOL: [&str; DIMENSION_SIZE] = ["m", "s", "g", "rad", "K", "C", "cd"];
29
30#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
32pub enum Dimension {
33 Standard([i8; DIMENSION_SIZE]),
35 Arbitrary(&'static str),
38}
39
40use Dimension::*;
41
42impl Dimension {
43 #[inline]
47 pub const fn less() -> Dimension {
48 Standard([0, 0, 0, 0, 0, 0, 0])
49 }
50
51 #[inline]
55 pub const fn meter() -> Dimension {
56 Standard([1, 0, 0, 0, 0, 0, 0])
57 }
58
59 #[inline]
63 pub const fn second() -> Dimension {
64 Standard([0, 1, 0, 0, 0, 0, 0])
65 }
66
67 #[inline]
71 pub const fn gram() -> Dimension {
72 Standard([0, 0, 1, 0, 0, 0, 0])
73 }
74
75 #[inline]
79 pub const fn radian() -> Dimension {
80 Standard([0, 0, 0, 1, 0, 0, 0])
81 }
82
83 #[inline]
87 pub const fn kelvin() -> Dimension {
88 Standard([0, 0, 0, 0, 1, 0, 0])
89 }
90
91 #[inline]
95 pub const fn coulomb() -> Dimension {
96 Standard([0, 0, 0, 0, 0, 1, 0])
97 }
98
99 #[inline]
103 pub const fn candela() -> Dimension {
104 Standard([0, 0, 0, 0, 0, 0, 1])
105 }
106
107 #[inline]
109 pub const fn arbitrary(name: &'static str) -> Dimension {
110 Arbitrary(name)
111 }
112
113 #[inline]
115 pub fn is_arbitrary(&self) -> bool {
116 matches!(self, Arbitrary(_))
117 }
118
119 #[inline]
121 pub fn raise(&mut self, power: i8) {
122 match self {
123 Standard(a) => {
124 for ai in a.iter_mut() {
125 *ai *= power;
126 }
127 }
128 Arbitrary(_) => unimplemented!(),
129 }
130 }
131
132 #[inline]
134 pub fn raised(self, power: i8) -> Dimension {
135 let mut r = self;
136 r.raise(power);
137 r
138 }
139
140 pub fn invert(&mut self) {
142 self.raise(-1)
143 }
144
145 #[inline]
147 pub fn inverted(&self) -> Dimension {
148 self.raised(-1)
149 }
150}
151
152impl Default for Dimension {
153 fn default() -> Dimension {
154 Dimension::less()
155 }
156}
157
158impl Index<BaseDimension> for Dimension {
159 type Output = i8;
160 fn index(&self, i: BaseDimension) -> &i8 {
161 match self {
162 Standard(a) => &a[i as usize],
163 Arbitrary(_) => &0,
164 }
165 }
166}
167
168impl Mul<Dimension> for Dimension {
171 type Output = Dimension;
172
173 #[allow(clippy::suspicious_arithmetic_impl)]
174 fn mul(self, other: Dimension) -> Self::Output {
175 match (self, other) {
176 (Standard(mut a1), Standard(a2)) => {
177 for i in 0..DIMENSION_SIZE {
178 a1[i] += a2[i];
179 }
180 Standard(a1)
181 }
182 (Arbitrary(_), _) => panic!("Can not multiply: LHS is arbitrary"),
183 (_, Arbitrary(_)) => panic!("Can not multiply: RHS is arbitrary"),
184 }
185 }
186}
187
188impl Div<Dimension> for Dimension {
191 type Output = Dimension;
192
193 #[allow(clippy::suspicious_arithmetic_impl)]
194 fn div(self, other: Dimension) -> Self::Output {
195 match (self, other) {
196 (Standard(mut a1), Standard(a2)) => {
197 for i in 0..DIMENSION_SIZE {
198 a1[i] -= a2[i];
199 }
200 Standard(a1)
201 }
202 (Arbitrary(_), _) => panic!("Can not multiply: LHS is arbitrary"),
203 (_, Arbitrary(_)) => panic!("Can not multiply: RHS is arbitrary"),
204 }
205 }
206}
207
208impl Mul<Dimension> for f64 {
209 type Output = Quantity<f64>;
210
211 fn mul(self, rhs: Dimension) -> Quantity<f64> {
212 Quantity::new(self, rhs)
213 }
214}
215
216impl Mul<Dimension> for i32 {
217 type Output = Quantity<f64>;
218
219 fn mul(self, rhs: Dimension) -> Quantity<f64> {
220 Quantity::new(self as f64, rhs)
221 }
222}
223
224impl fmt::Display for Dimension {
225 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226 match self {
227 Standard(a) => {
228 let mut first = true;
229 for i in 0..DIMENSION_SIZE {
230 if a[i] == 0 {
231 continue;
232 }
233 if first {
234 first = false;
235 } else {
236 write!(f, ".")?;
237 }
238 write!(f, "{}", SYMBOL[i])?;
239 if a[i] == 1 {
240 continue;
241 }
242 let abs = if a[i] < 0 {
243 write!(f, "⁻")?;
244 -a[i]
245 } else {
246 a[i]
247 };
248 match abs {
249 1 => write!(f, "¹")?,
250 2 => write!(f, "²")?,
251 3 => write!(f, "³")?,
252 4 => write!(f, "⁴")?,
253 _ => write!(f, "{}", abs)?,
254 }
255 }
256 Ok(())
257 }
258 Arbitrary(name) => write!(f, "{}", name),
259 }
260 }
261}
262
263pub mod base {
265 use super::Dimension;
266 pub static DIMLESS: Dimension = Dimension::less();
268 pub static M: Dimension = Dimension::meter();
270 pub static S: Dimension = Dimension::second();
272 pub static G: Dimension = Dimension::gram();
274 pub static RAD: Dimension = Dimension::radian();
276 pub static K: Dimension = Dimension::kelvin();
278 pub static C: Dimension = Dimension::coulomb();
280 pub static CD: Dimension = Dimension::candela();
282}
283
284#[cfg(test)]
285mod test {
286 use super::*;
287 use base::*;
288
289 #[test]
290 fn test_dimless() {
291 let u = Dimension::less();
292 assert_eq!(u[BaseDimension::Length], 0);
293 assert_eq!(u[BaseDimension::Time], 0);
294 assert_eq!(u[BaseDimension::Mass], 0);
295 assert_eq!(u[BaseDimension::PlaneAngle], 0);
296 assert_eq!(u[BaseDimension::Temperature], 0);
297 assert_eq!(u[BaseDimension::ElectricCharge], 0);
298 assert_eq!(u[BaseDimension::LuminousIntensity], 0);
299 assert_eq!(base::DIMLESS, u);
300 assert_eq!(&format!("{}", &u), "");
301 }
302
303 #[test]
304 fn test_meter() {
305 let m = Dimension::meter();
306 assert_eq!(m[BaseDimension::Length], 1);
307 assert_eq!(m[BaseDimension::Time], 0);
308 assert_eq!(m[BaseDimension::Mass], 0);
309 assert_eq!(m[BaseDimension::PlaneAngle], 0);
310 assert_eq!(m[BaseDimension::Temperature], 0);
311 assert_eq!(m[BaseDimension::ElectricCharge], 0);
312 assert_eq!(m[BaseDimension::LuminousIntensity], 0);
313 assert_eq!(base::M, m);
314 assert_eq!(&format!("{}", &m), "m");
315 }
316
317 #[test]
318 fn test_second() {
319 let s = Dimension::second();
320 assert_eq!(s[BaseDimension::Length], 0);
321 assert_eq!(s[BaseDimension::Time], 1);
322 assert_eq!(s[BaseDimension::Mass], 0);
323 assert_eq!(s[BaseDimension::PlaneAngle], 0);
324 assert_eq!(s[BaseDimension::Temperature], 0);
325 assert_eq!(s[BaseDimension::ElectricCharge], 0);
326 assert_eq!(s[BaseDimension::LuminousIntensity], 0);
327 assert_eq!(base::S, s);
328 assert_eq!(&format!("{}", &s), "s");
329 }
330
331 #[test]
332 fn test_gram() {
333 let g = Dimension::gram();
334 assert_eq!(g[BaseDimension::Length], 0);
335 assert_eq!(g[BaseDimension::Time], 0);
336 assert_eq!(g[BaseDimension::Mass], 1);
337 assert_eq!(g[BaseDimension::PlaneAngle], 0);
338 assert_eq!(g[BaseDimension::Temperature], 0);
339 assert_eq!(g[BaseDimension::ElectricCharge], 0);
340 assert_eq!(g[BaseDimension::LuminousIntensity], 0);
341 assert_eq!(base::G, g);
342 assert_eq!(&format!("{}", &g), "g");
343 }
344
345 #[test]
346 fn test_radian() {
347 let rad = Dimension::radian();
348 assert_eq!(rad[BaseDimension::Length], 0);
349 assert_eq!(rad[BaseDimension::Time], 0);
350 assert_eq!(rad[BaseDimension::Mass], 0);
351 assert_eq!(rad[BaseDimension::PlaneAngle], 1);
352 assert_eq!(rad[BaseDimension::Temperature], 0);
353 assert_eq!(rad[BaseDimension::ElectricCharge], 0);
354 assert_eq!(rad[BaseDimension::LuminousIntensity], 0);
355 assert_eq!(base::RAD, rad);
356 assert_eq!(&format!("{}", &rad), "rad");
357 }
358
359 #[test]
360 fn test_kelvin() {
361 let k = Dimension::kelvin();
362 assert_eq!(k[BaseDimension::Length], 0);
363 assert_eq!(k[BaseDimension::Time], 0);
364 assert_eq!(k[BaseDimension::Mass], 0);
365 assert_eq!(k[BaseDimension::PlaneAngle], 0);
366 assert_eq!(k[BaseDimension::Temperature], 1);
367 assert_eq!(k[BaseDimension::ElectricCharge], 0);
368 assert_eq!(k[BaseDimension::LuminousIntensity], 0);
369 assert_eq!(base::K, k);
370 assert_eq!(&format!("{}", &k), "K");
371 }
372
373 #[test]
374 fn test_coulomb() {
375 let c = Dimension::coulomb();
376 assert_eq!(c[BaseDimension::Length], 0);
377 assert_eq!(c[BaseDimension::Time], 0);
378 assert_eq!(c[BaseDimension::Mass], 0);
379 assert_eq!(c[BaseDimension::PlaneAngle], 0);
380 assert_eq!(c[BaseDimension::Temperature], 0);
381 assert_eq!(c[BaseDimension::ElectricCharge], 1);
382 assert_eq!(c[BaseDimension::LuminousIntensity], 0);
383 assert_eq!(base::C, c);
384 assert_eq!(&format!("{}", &c), "C");
385 }
386
387 #[test]
388 fn test_candela() {
389 let cd = Dimension::candela();
390 assert_eq!(cd[BaseDimension::Length], 0);
391 assert_eq!(cd[BaseDimension::Time], 0);
392 assert_eq!(cd[BaseDimension::Mass], 0);
393 assert_eq!(cd[BaseDimension::PlaneAngle], 0);
394 assert_eq!(cd[BaseDimension::Temperature], 0);
395 assert_eq!(cd[BaseDimension::ElectricCharge], 0);
396 assert_eq!(cd[BaseDimension::LuminousIntensity], 1);
397 assert_eq!(base::CD, cd);
398 assert_eq!(&format!("{}", &cd), "cd");
399 }
400
401 #[test]
402 fn test_m2() {
403 let m2 = M * M;
404 assert_eq!(m2[BaseDimension::Length], 2);
405 assert_eq!(m2[BaseDimension::Time], 0);
406 assert_eq!(m2[BaseDimension::Mass], 0);
407 assert_eq!(m2[BaseDimension::PlaneAngle], 0);
408 assert_eq!(m2[BaseDimension::Temperature], 0);
409 assert_eq!(m2[BaseDimension::ElectricCharge], 0);
410 assert_eq!(m2[BaseDimension::LuminousIntensity], 0);
411 assert_eq!(&format!("{}", &m2), "m²");
412 }
413
414 #[test]
415 fn test_ms() {
416 let ms = M * S;
417 assert_eq!(ms[BaseDimension::Length], 1);
418 assert_eq!(ms[BaseDimension::Time], 1);
419 assert_eq!(ms[BaseDimension::Mass], 0);
420 assert_eq!(ms[BaseDimension::PlaneAngle], 0);
421 assert_eq!(ms[BaseDimension::Temperature], 0);
422 assert_eq!(ms[BaseDimension::ElectricCharge], 0);
423 assert_eq!(ms[BaseDimension::LuminousIntensity], 0);
424 assert_eq!(&format!("{}", &ms), "m.s");
425 }
426
427 #[test]
428 fn test_mps() {
429 let mps = M / S;
430 assert_eq!(mps[BaseDimension::Length], 1);
431 assert_eq!(mps[BaseDimension::Time], -1);
432 assert_eq!(mps[BaseDimension::Mass], 0);
433 assert_eq!(mps[BaseDimension::PlaneAngle], 0);
434 assert_eq!(mps[BaseDimension::Temperature], 0);
435 assert_eq!(mps[BaseDimension::ElectricCharge], 0);
436 assert_eq!(mps[BaseDimension::LuminousIntensity], 0);
437 assert_eq!(&format!("{}", &mps), "m.s⁻¹");
438 }
439
440 #[test]
441 fn test_mps2() {
442 let mps2 = M / (S * S);
443 assert_eq!(mps2[BaseDimension::Length], 1);
444 assert_eq!(mps2[BaseDimension::Time], -2);
445 assert_eq!(mps2[BaseDimension::Mass], 0);
446 assert_eq!(mps2[BaseDimension::PlaneAngle], 0);
447 assert_eq!(mps2[BaseDimension::Temperature], 0);
448 assert_eq!(mps2[BaseDimension::ElectricCharge], 0);
449 assert_eq!(mps2[BaseDimension::LuminousIntensity], 0);
450 assert_eq!(&format!("{}", &mps2), "m.s⁻²");
451 }
452
453 #[test]
454 fn test_inverted() {
455 let mm1 = M.inverted();
456 assert_eq!(mm1[BaseDimension::Length], -1);
457 assert_eq!(mm1[BaseDimension::Time], 0);
458 assert_eq!(mm1[BaseDimension::Mass], 0);
459 assert_eq!(mm1[BaseDimension::PlaneAngle], 0);
460 assert_eq!(mm1[BaseDimension::Temperature], 0);
461 assert_eq!(mm1[BaseDimension::ElectricCharge], 0);
462 assert_eq!(mm1[BaseDimension::LuminousIntensity], 0);
463 assert_eq!(&format!("{}", &mm1), "m⁻¹");
464
465 let d = (M * M / S).inverted();
466 assert_eq!(d[BaseDimension::Length], -2);
467 assert_eq!(d[BaseDimension::Time], 1);
468 assert_eq!(d[BaseDimension::Mass], 0);
469 assert_eq!(d[BaseDimension::PlaneAngle], 0);
470 assert_eq!(d[BaseDimension::Temperature], 0);
471 assert_eq!(d[BaseDimension::ElectricCharge], 0);
472 assert_eq!(d[BaseDimension::LuminousIntensity], 0);
473 assert_eq!(&format!("{}", &d), "m⁻².s");
474 }
475
476 #[test]
477 fn test_invert() {
478 let mut d = M;
479 d.invert();
480 assert_eq!(d[BaseDimension::Length], -1);
481 assert_eq!(d[BaseDimension::Time], 0);
482 assert_eq!(d[BaseDimension::Mass], 0);
483 assert_eq!(d[BaseDimension::PlaneAngle], 0);
484 assert_eq!(d[BaseDimension::Temperature], 0);
485 assert_eq!(d[BaseDimension::ElectricCharge], 0);
486 assert_eq!(d[BaseDimension::LuminousIntensity], 0);
487 assert_eq!(&format!("{}", &d), "m⁻¹");
488
489 let mut d = M * M / S;
490 d.invert();
491 assert_eq!(d[BaseDimension::Length], -2);
492 assert_eq!(d[BaseDimension::Time], 1);
493 assert_eq!(d[BaseDimension::Mass], 0);
494 assert_eq!(d[BaseDimension::PlaneAngle], 0);
495 assert_eq!(d[BaseDimension::Temperature], 0);
496 assert_eq!(d[BaseDimension::ElectricCharge], 0);
497 assert_eq!(d[BaseDimension::LuminousIntensity], 0);
498 assert_eq!(&format!("{}", &d), "m⁻².s");
499 }
500}