dyn_quantity/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4mod unit_exponents_constructors;
5
6use num::Complex;
7use num::complex::ComplexFloat;
8
9use std::error::Error;
10use std::fmt::Display;
11use std::ops::{Div, DivAssign, Mul, MulAssign};
12pub use std::str::FromStr;
13
14#[cfg(feature = "uom")]
15pub mod uom_impl;
16
17#[cfg(feature = "serde")]
18pub mod serde_impl;
19
20#[cfg(feature = "serde")]
21pub mod deserialize_with;
22
23#[cfg(feature = "serde")]
24pub use deserialize_with::{
25    deserialize_angle, deserialize_opt_angle, deserialize_opt_quantity,
26    deserialize_opt_vec_of_quantities, deserialize_quantity, deserialize_vec_of_quantities,
27};
28
29#[cfg(feature = "from_str")]
30pub mod from_str;
31
32/**
33A trait to derive [`UnitExponents`] from a type. This trait bridges the gap
34between (external) types representing physical quantities (such as e.g. the
35[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) type from
36the [uom](https://crates.io/crates/uom) crate) and [`UnitExponents`].
37 */
38pub trait AsUnitExponents {
39    /**
40    This function derives an [`UnitExponents`] from any type which implements
41    [`AsUnitExponents`]. Its default implementation returns an [`UnitExponents`]
42    where all exponents are zero.
43
44    # Examples
45    ```
46    use dyn_quantity::AsUnitExponents;
47    use uom::si::f64::Length;
48
49    // 64-bit floats do not represent a physical quantity
50    let exp = f64::as_unit_exponents();
51    assert_eq!(exp.meter, 0);
52
53    // The "Length" type alias from the uom crate represents a physical quantity (length)
54    let exp = Length::as_unit_exponents();
55    assert_eq!(exp.meter, 1);
56    ```
57    */
58    fn as_unit_exponents() -> UnitExponents {
59        return UnitExponents::default();
60    }
61}
62
63impl AsUnitExponents for f64 {}
64
65impl AsUnitExponents for Complex<f64> {}
66
67mod private {
68    use super::Complex;
69
70    pub trait Sealed {}
71
72    impl Sealed for f64 {}
73    impl Sealed for Complex<f64> {}
74}
75
76/**
77This is an internal trait which is used to convert between [`f64`] and
78[`Complex<f64>`]-based [`DynQuantity`] structs. It needs to be public because
79it is part of the type signature of [`DynQuantity`], but it is not meant to be
80implemented by external types and is therefore sealed.
81*/
82pub trait F64RealOrComplex:
83    ComplexFloat<Real: std::fmt::Display>
84    + std::ops::AddAssign
85    + std::ops::SubAssign
86    + std::fmt::Display
87    + std::ops::MulAssign
88    + std::ops::DivAssign
89    + std::fmt::Debug
90    + private::Sealed
91{
92    /**
93    Tries to convert from a [`Complex<f64>`] to the implementor of this trait,
94    either [`Complex<f64>`] or [`f64`]. The former conversion always succeeds
95    (is a no-op), while the latter fails if `number` has an imaginary component.
96     */
97    fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64>;
98
99    /**
100    Converts a [`Complex<f64>`] or [`f64`] to a [`Complex<f64>`]. This is
101    infallible (and a no-op in case of the former conversion).
102     */
103    fn to_complexf64(self) -> Complex<f64>;
104
105    /**
106    Converts a [`f64`] to a [`Complex<f64>`] or [`f64`]. This is infallible
107    (and a no-op in case of the latter conversion).
108     */
109    fn from_f64(value: f64) -> Self;
110
111    /**
112    Sets the real part of the implementor to `value`.
113     */
114    fn set_re_f64(&mut self, value: f64) -> ();
115
116    /**
117    Sets the imaginary part of the implementor to `value`. If the implementing
118    type is [`f64`], this is a no-op.
119     */
120    fn set_im_f64(&mut self, value: f64) -> ();
121
122    /**
123    Calcute the `n`th root of `self`.
124     */
125    fn nth_root(self, n: i32) -> Self;
126}
127
128impl F64RealOrComplex for f64 {
129    fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
130        if number.im() == 0.0 {
131            return Ok(number.re());
132        } else {
133            return Err(NotConvertibleFromComplexF64 {
134                source: number,
135                target_type: "f64",
136            });
137        }
138    }
139
140    fn to_complexf64(self) -> Complex<f64> {
141        return Complex::new(self, 0.0);
142    }
143
144    fn from_f64(value: f64) -> Self {
145        return value;
146    }
147
148    fn set_re_f64(&mut self, value: f64) -> () {
149        *self = value;
150    }
151
152    fn set_im_f64(&mut self, _: f64) -> () {
153        ();
154    }
155
156    fn nth_root(self, n: i32) -> Self {
157        return self.powf(1.0 / n as f64);
158    }
159}
160
161impl F64RealOrComplex for Complex<f64> {
162    fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
163        return Ok(Complex::<f64>::from(number));
164    }
165
166    fn to_complexf64(self) -> Complex<f64> {
167        return self;
168    }
169
170    fn from_f64(value: f64) -> Self {
171        return Complex::new(value, 0.0);
172    }
173
174    fn set_re_f64(&mut self, value: f64) -> () {
175        self.re = value;
176    }
177
178    fn set_im_f64(&mut self, value: f64) -> () {
179        self.im = value;
180    }
181
182    fn nth_root(self, n: i32) -> Self {
183        return self.powf(1.0 / n as f64);
184    }
185}
186
187/**
188This type represents a physical quantity via its numerical `value` and a unit of
189measurement (field `exponents`). The unit of measurement is not defined via the
190type system, but rather via the values of the [`UnitExponents`]. This means that
191the unit of measurement is not fixed at compile time, but can change dynamically
192at runtime.
193
194This property is very useful when e.g. parsing a user-provided string to a
195physical quantity. For this case, [`DynQuantity`] implements the
196[`FromStr`](`std::str::FromStr`) trait. The module documentation
197[`from_str`](crate::from_str) has more information regarding the available syntax.
198
199The `V` generic parameter needs to implement [`F64RealOrComplex`].
200Currently, implementors are [`f64`] and [`Complex<f64>`]. It is possible to
201convert between those two types using the methods provided by [`F64RealOrComplex`].
202In general, you likely want `V` to be [`f64`] except when dealing with complex
203quantities such as alternating currents.
204
205This struct can also be pretty-print the quantity it represents via its
206[`std::fmt::Display`] implementation:
207
208```
209use std::str::FromStr;
210use dyn_quantity::DynQuantity;
211
212let quantity = DynQuantity::<f64>::from_str("9.81 m/s^2").expect("parseable");
213assert_eq!(quantity.to_string(), "9.81 s^-2 m".to_string());
214```
215
216# Conversion into uom `Quantity`
217
218If the **uom** feature is enabled, a [`DynQuantity`] can be (fallible)
219converted into a
220[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) via
221[`TryFrom`]. In combination with the aforementioned parsing capabilities, this
222allows fallible parsing of strings to statically-typed physical quantities.
223
224# Serialization and deserialization
225
226If the **serde** feature is enabled, this struct can be serialized and
227deserialized. Serialization creates the "standard"
228[serde](https://crates.io/crates/serde) representation one would expect from the
229[`Serialize`] macro. When deserializing however, multiple options are available:
2301) Using the "standard" serialized representation of a struct. For example, the
231yaml representation of a [`DynQuantity<f64>`] looks like this:
232```text
233---
234value: 2.0
235exponents:
236    second: 0
237    meter: 1
238    kilogram: 0
239    ampere: 1
240    kelvin: 0
241    mol: 0
242    candela: 0
243```
244
2452) Deserializing directly from a string. This uses the [`std::str::FromStr`]
246implementation under the hood, see the [`from_str`](crate::from_str) module
247documentation. Only available if the **from_str** feature is enabled.
2483) Deserialize directly from a real or complex value. This option is mainly here
249to allow deserializing a serialized [uom](https://crates.io/crates/uom) quantity
250(whose serialized representation is simply its numerical value without any
251units). For example, deserializing `5.0` into [`DynQuantity<f64>`] produces the
252same result as deserializing:
253```text
254---
255value: 5.0
256exponents:
257    second: 0
258    meter: 0
259    kilogram: 0
260    ampere: 0
261    kelvin: 0
262    mol: 0
263    candela: 0
264```
265
266The three different possibilities are realized via a crate-internal untagged enum.
267*/
268#[derive(Debug, Clone, PartialEq, Default)]
269#[repr(C)]
270pub struct DynQuantity<V: F64RealOrComplex> {
271    /**
272    The value of the physical quantity.
273     */
274    pub value: V,
275    /**
276    The (SI) base units of the physical quantity, represented by their exponents.
277     */
278    pub exponents: UnitExponents,
279}
280
281impl<V: F64RealOrComplex> DynQuantity<V> {
282    /**
283    Returns a new instance of `Self`.
284     */
285    pub fn new(value: V, exponents: UnitExponents) -> Self {
286        return Self { value, exponents };
287    }
288
289    /**
290    Fallible addition of `self` and `other`.
291
292    Physical quantities can only be added together if their units are identical.
293    Hence, this function first compares the `.exponents` fields of `self` and
294    `other`. If they are identical, the `value` fields are added up and the
295    resulting quantity is returned. Otherwise, a [`UnitsOfSummandsNotIdentical`]
296    error is returned.
297
298    # Examples
299    ```
300    use std::str::FromStr;
301    use dyn_quantity::DynQuantity;
302
303    let curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
304    let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
305    let volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
306
307    // The currents can be added ...
308    let curr_sum = curr1.try_add(&curr2).expect("can be added");
309    assert_eq!(curr_sum.value, 2.0);
310    assert_eq!(curr_sum.exponents.ampere, 1);
311
312    // ... but adding a current to a voltage fails.
313    assert!(volt1.try_add(&curr1).is_err());
314    ```
315     */
316    pub fn try_add(&self, other: &Self) -> Result<Self, UnitsOfSummandsNotIdentical> {
317        let mut output = self.clone();
318        output.try_add_assign(other)?;
319        return Ok(output);
320    }
321
322    /**
323    Like [`DynQuantity::try_add`], but assigns the sum of `self` and `other` to
324    `self` instead of returning it. If the addition fails, `self` is not modified.
325
326    # Examples
327    ```
328    use std::str::FromStr;
329    use dyn_quantity::DynQuantity;
330
331    let mut curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
332    let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
333    let mut volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
334
335    // curr1 gets overwritten
336    curr1.try_add_assign(&curr2).expect("can be added");
337    assert_eq!(curr1.value, 2.0);
338    assert_eq!(curr1.exponents.ampere, 1);
339
340    // volt1 does not get modified because the addition failed
341    let volt_cpy = volt1.clone();
342    assert!(volt1.try_add_assign(&curr1).is_err());
343    assert_eq!(volt1, volt_cpy);
344    ```
345     */
346    pub fn try_add_assign(&mut self, other: &Self) -> Result<(), UnitsOfSummandsNotIdentical> {
347        if self.exponents == other.exponents {
348            self.value += other.value;
349            return Ok(());
350        } else {
351            return Err(UnitsOfSummandsNotIdentical(
352                self.exponents.clone(),
353                other.exponents.clone(),
354            ));
355        }
356    }
357
358    /**
359    Fallible subtraction of `self` and `other`.
360
361    Physical quantities can only be subtracted from each other if their units are identical.
362    Hence, this function first compares the `.exponents` fields of `self` and
363    `other`. If they are identical, the `value` fields are subtracted up and the
364    resulting quantity is returned. Otherwise, a [`UnitsOfSummandsNotIdentical`]
365    error is returned.
366
367    # Examples
368    ```
369    use std::str::FromStr;
370    use dyn_quantity::DynQuantity;
371
372    let curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
373    let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
374    let volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
375
376    // The currents can be subtracted ...
377    let curr_sum = curr1.try_sub(&curr2).expect("can be added");
378    assert_eq!(curr_sum.value, 0.0);
379    assert_eq!(curr_sum.exponents.ampere, 1);
380
381    // ... but sbutracting a current from a voltage fails.
382    assert!(volt1.try_sub(&curr1).is_err());
383    ```
384     */
385    pub fn try_sub(&self, other: &Self) -> Result<Self, UnitsOfSummandsNotIdentical> {
386        let mut output = self.clone();
387        output.try_sub_assign(other)?;
388        return Ok(output);
389    }
390
391    /**
392    Like [`DynQuantity::try_sub`], but assigns the difference of `self` and `other` to
393    `self` instead of returning it. If the subtraction fails, `self` is not modified.
394
395    # Examples
396    ```
397    use std::str::FromStr;
398    use dyn_quantity::DynQuantity;
399
400    let mut curr1 = DynQuantity::<f64>::from_str("1 A").expect("valid");
401    let curr2 = DynQuantity::<f64>::from_str("1 V*A / V").expect("valid");
402    let mut volt1 = DynQuantity::<f64>::from_str("-5 V").expect("valid");
403
404    // curr1 gets overwritten
405    curr1.try_sub_assign(&curr2).expect("can be added");
406    assert_eq!(curr1.value, 0.0);
407    assert_eq!(curr1.exponents.ampere, 1);
408
409    // volt1 does not get modified because the addition failed
410    let volt_cpy = volt1.clone();
411    assert!(volt1.try_sub_assign(&curr1).is_err());
412    assert_eq!(volt1, volt_cpy);
413    ```
414     */
415    pub fn try_sub_assign(&mut self, other: &Self) -> Result<(), UnitsOfSummandsNotIdentical> {
416        if self.exponents == other.exponents {
417            self.value -= other.value;
418            return Ok(());
419        } else {
420            return Err(UnitsOfSummandsNotIdentical(
421                self.exponents.clone(),
422                other.exponents.clone(),
423            ));
424        }
425    }
426
427    /**
428    Raises `self` to an integer power.
429
430    # Examples
431    ```
432    use std::str::FromStr;
433    use dyn_quantity::DynQuantity;
434
435    let curr = DynQuantity::<f64>::from_str("2 A^2").expect("valid");
436    let result = curr.powi(3);
437    assert_eq!(result.value, 8.0);
438    assert_eq!(result.exponents.ampere, 6);
439    ```
440     */
441    pub fn powi(mut self, n: i32) -> Self {
442        self.value = self.value.powi(n);
443        self.exponents = self.exponents.powi(n);
444        return self;
445    }
446
447    /**
448    Tries to calculate the `n`th root of self.
449     */
450    pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
451        self.exponents = self.exponents.try_nthroot(n)?;
452        self.value = self.value.nth_root(n);
453        return Ok(self);
454    }
455}
456
457impl<V: F64RealOrComplex> std::fmt::Display for DynQuantity<V> {
458    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459        if self.value.im() == V::zero().im() {
460            write!(f, "{}", self.value.re())?;
461        } else {
462            write!(f, "({})", self.value)?;
463        }
464
465        // Go through all units and add them, if their exponents aren't zero
466        if self.exponents.second != 0 {
467            if self.exponents.second == 1 {
468                write!(f, " s")?;
469            } else {
470                write!(f, " s^{}", self.exponents.second)?;
471            }
472        }
473        if self.exponents.meter != 0 {
474            if self.exponents.meter == 1 {
475                write!(f, " m")?;
476            } else {
477                write!(f, " m^{}", self.exponents.meter)?;
478            }
479        }
480        if self.exponents.kilogram != 0 {
481            if self.exponents.kilogram == 1 {
482                write!(f, " kg")?;
483            } else {
484                write!(f, " kg^{}", self.exponents.kilogram)?;
485            }
486        }
487        if self.exponents.ampere != 0 {
488            if self.exponents.ampere == 1 {
489                write!(f, " A")?;
490            } else {
491                write!(f, " A^{}", self.exponents.ampere)?;
492            }
493        }
494        if self.exponents.kelvin != 0 {
495            if self.exponents.kelvin == 1 {
496                write!(f, " K")?;
497            } else {
498                write!(f, " K^{}", self.exponents.kelvin)?;
499            }
500        }
501        if self.exponents.mol != 0 {
502            if self.exponents.mol == 1 {
503                write!(f, " mol")?;
504            } else {
505                write!(f, " mol^{}", self.exponents.mol)?;
506            }
507        }
508        if self.exponents.candela != 0 {
509            if self.exponents.candela == 1 {
510                write!(f, " cd")?;
511            } else {
512                write!(f, " cd^{}", self.exponents.candela)?;
513            }
514        }
515
516        return Ok(());
517    }
518}
519
520impl<V: F64RealOrComplex> Mul for DynQuantity<V> {
521    type Output = Self;
522
523    fn mul(mut self, rhs: Self) -> Self::Output {
524        self.mul_assign(rhs);
525        return self;
526    }
527}
528
529impl<V: F64RealOrComplex> MulAssign for DynQuantity<V> {
530    fn mul_assign(&mut self, rhs: Self) {
531        self.value *= rhs.value;
532        self.exponents *= rhs.exponents;
533    }
534}
535
536impl<V: F64RealOrComplex> Div for DynQuantity<V> {
537    type Output = Self;
538
539    fn div(mut self, rhs: Self) -> Self::Output {
540        self.div_assign(rhs);
541        return self;
542    }
543}
544
545impl<V: F64RealOrComplex> DivAssign for DynQuantity<V> {
546    fn div_assign(&mut self, rhs: Self) {
547        if self.value.is_infinite() {
548            let mut value = self.value / rhs.value;
549            if value.re().is_nan() {
550                value.set_re_f64(0.0);
551            }
552            if value.im().is_nan() {
553                value.set_im_f64(0.0);
554            }
555            self.value = value;
556        } else {
557            self.value /= rhs.value;
558        }
559        self.exponents /= rhs.exponents;
560    }
561}
562
563impl TryFrom<DynQuantity<Complex<f64>>> for DynQuantity<f64> {
564    type Error = NotConvertibleFromComplexF64;
565
566    fn try_from(quantity: DynQuantity<Complex<f64>>) -> Result<Self, Self::Error> {
567        if quantity.value.im() == 0.0 {
568            return Ok(DynQuantity::new(quantity.value.re(), quantity.exponents));
569        } else {
570            return Err(NotConvertibleFromComplexF64 {
571                source: quantity.value,
572                target_type: "f64",
573            });
574        }
575    }
576}
577
578// ========================================================
579
580/**
581Converts a slice of [`DynQuantity`] to a vector of their values `V`. In
582constrast to [`to_vec_checked`], this function does not check whether the units
583of the individual [`DynQuantity`] elements are identical.
584*/
585pub fn to_vec<V: F64RealOrComplex>(quantity_slice: &[DynQuantity<V>]) -> Vec<V> {
586    let mut output: Vec<V> = Vec::with_capacity(quantity_slice.len());
587    for element in quantity_slice.iter() {
588        output.push(element.value)
589    }
590    return output;
591}
592
593/**
594Checks if all units of the [`DynQuantity`] elements are identical. If that is
595the case, it converts the slice to a vector of their values `V`.
596*/
597pub fn to_vec_checked(quantity_slice: &[DynQuantity<f64>]) -> Result<Vec<f64>, ConversionError> {
598    let mut output: Vec<f64> = Vec::with_capacity(quantity_slice.len());
599    if let Some(first_element) = quantity_slice.first() {
600        let exponents = first_element.exponents.clone();
601        for element in quantity_slice.iter() {
602            if element.exponents != exponents {
603                return Err(ConversionError::UnitMismatch {
604                    expected: exponents,
605                    found: element.exponents.clone(),
606                });
607            }
608            output.push(element.value)
609        }
610    }
611    return Ok(output);
612}
613
614// ====================================================
615
616/**
617Struct representing a unit of measurement in the SI system via the exponents of
618the base units.
619 */
620#[derive(Debug, Clone, PartialEq, Eq, Default)]
621#[repr(C)]
622pub struct UnitExponents {
623    /// Exponent for the SI base unit of time.
624    pub second: i32,
625    /// Exponent for the SI base unit of length.
626    pub meter: i32,
627    /// Exponent for the SI base unit of mass.
628    pub kilogram: i32,
629    /// Exponent for the SI base unit of electrical current.
630    pub ampere: i32,
631    /// Exponent for the SI base unit of temperature.
632    pub kelvin: i32,
633    /// Exponent for the SI base unit of amount of substance.
634    pub mol: i32,
635    /// Exponent for the SI base unit of luminous intensity
636    pub candela: i32,
637}
638
639impl From<[i32; 7]> for UnitExponents {
640    /**
641    Converts an array of seven `i32` values into `UnitExponents`.
642
643    The individual array elements are interpreted as follows:
644    - `array[0]`: Exponent of second
645    - `array[1]`: Exponent of meter
646    - `array[2]`: Exponent of kilogram
647    - `array[3]`: Exponent of ampere
648    - `array[4]`: Exponent of kelvin
649    - `array[5]`: Exponent of mol
650    - `array[6]`: Exponent of candela
651     */
652    fn from(array: [i32; 7]) -> Self {
653        return UnitExponents {
654            second: array[0],
655            meter: array[1],
656            kilogram: array[2],
657            ampere: array[3],
658            kelvin: array[4],
659            mol: array[5],
660            candela: array[6],
661        };
662    }
663}
664
665impl From<UnitExponents> for [i32; 7] {
666    /**
667    Converts an `UnitExponents` into an array of seven `i32`.
668
669    The exponents are put into the array in the following order:
670    - `array[0]`: Exponent of second
671    - `array[1]`: Exponent of meter
672    - `array[2]`: Exponent of kilogram
673    - `array[3]`: Exponent of ampere
674    - `array[4]`: Exponent of kelvin
675    - `array[5]`: Exponent of mol
676    - `array[6]`: Exponent of candela
677     */
678    fn from(value: UnitExponents) -> Self {
679        return [
680            value.second,
681            value.meter,
682            value.kilogram,
683            value.ampere,
684            value.kelvin,
685            value.mol,
686            value.candela,
687        ];
688    }
689}
690
691impl UnitExponents {
692    /**
693    Raises `self` to an integer power.
694
695    # Examples
696    ```
697    use dyn_quantity::UnitExponents;
698
699    let exponents = UnitExponents::from([0, 1, 0, 2, 0, -2, 0]);
700    let array: [i32; 7] = exponents.powi(2).into();
701    assert_eq!(array, [0, 2, 0, 4, 0, -4, 0]);
702    ```
703     */
704    pub fn powi(mut self, n: i32) -> Self {
705        self.second *= n;
706        self.meter *= n;
707        self.kilogram *= n;
708        self.ampere *= n;
709        self.kelvin *= n;
710        self.mol *= n;
711        self.candela *= n;
712        return self;
713    }
714
715    /**
716    Tries to calculate the `n`th root of self. This operation fails if any
717    of the exponents is not divisible by `n`.
718
719    # Examples
720    ```
721    use dyn_quantity::UnitExponents;
722
723    let exponents = UnitExponents::from([0, 2, 0, 2, 0, -4, 0]);
724
725    // It is possible to calculate the square root:
726    let array: [i32; 7] = exponents.clone().try_nthroot(2).unwrap().into();
727    assert_eq!(array, [0, 1, 0, 1, 0, -2, 0]);
728
729    // But not the cubic root (not all exponents are divisible by 3):
730    assert!(exponents.try_nthroot(3).is_err());
731    ```
732     */
733    pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
734        fn try_nthroot_inner(
735            exponents: &UnitExponents,
736            exp: i32,
737            n: i32,
738        ) -> Result<i32, RootError> {
739            if exp % n == 0 {
740                return Ok(exp / n);
741            } else {
742                return Err(RootError {
743                    n,
744                    exponents: exponents.clone(),
745                });
746            }
747        }
748        let init_exp = self.clone();
749        self.second = try_nthroot_inner(&init_exp, self.second, n)?;
750        self.meter = try_nthroot_inner(&init_exp, self.meter, n)?;
751        self.kilogram = try_nthroot_inner(&init_exp, self.kilogram, n)?;
752        self.ampere = try_nthroot_inner(&init_exp, self.ampere, n)?;
753        self.kelvin = try_nthroot_inner(&init_exp, self.kelvin, n)?;
754        self.mol = try_nthroot_inner(&init_exp, self.mol, n)?;
755        self.candela = try_nthroot_inner(&init_exp, self.candela, n)?;
756        return Ok(self);
757    }
758}
759
760impl std::fmt::Display for UnitExponents {
761    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762        write!(
763            f,
764            "s^{} m^{} kg^{} A^{} K^{} mol^{} cd^{}",
765            self.second,
766            self.meter,
767            self.kilogram,
768            self.ampere,
769            self.kelvin,
770            self.mol,
771            self.candela
772        )
773    }
774}
775
776impl Mul for UnitExponents {
777    type Output = Self;
778
779    fn mul(mut self, rhs: Self) -> Self::Output {
780        self.mul_assign(rhs);
781        return self;
782    }
783}
784
785impl MulAssign for UnitExponents {
786    fn mul_assign(&mut self, rhs: Self) {
787        self.second += rhs.second;
788        self.meter += rhs.meter;
789        self.kilogram += rhs.kilogram;
790        self.ampere += rhs.ampere;
791        self.kelvin += rhs.kelvin;
792        self.mol += rhs.mol;
793        self.candela += rhs.candela;
794    }
795}
796
797impl Div for UnitExponents {
798    type Output = Self;
799
800    fn div(mut self, rhs: Self) -> Self::Output {
801        self.div_assign(rhs);
802        return self;
803    }
804}
805
806impl DivAssign for UnitExponents {
807    fn div_assign(&mut self, rhs: Self) {
808        self.second -= rhs.second;
809        self.meter -= rhs.meter;
810        self.kilogram -= rhs.kilogram;
811        self.ampere -= rhs.ampere;
812        self.kelvin -= rhs.kelvin;
813        self.mol -= rhs.mol;
814        self.candela -= rhs.candela;
815    }
816}
817
818/**
819Error representing a failed attempt to add two physical quantities.
820
821Two physical quantities can only be added if their units of measurements are
822identical. If they aren't, the corresponding function will return an instance
823of this struct, which holds the [`UnitExponents`] of both quantities for
824further inspection.
825 */
826#[derive(Debug, Clone, PartialEq, Default)]
827#[repr(C)]
828pub struct UnitsOfSummandsNotIdentical(pub UnitExponents, pub UnitExponents);
829
830impl Display for UnitsOfSummandsNotIdentical {
831    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
832        write!(
833            f,
834            "first summand has exponents {}, but second summand has exponents {}",
835            self.0, self.1
836        )
837    }
838}
839
840impl Error for UnitsOfSummandsNotIdentical {}
841
842/**
843Error representing a failed attempt to calculate the `n`th root of a [`UnitExponents`].
844
845Calculating the `n`th root of a [`UnitExponents`] fails if any of the exponents
846is not divisible by `n`.
847 */
848#[derive(Default, Debug, Clone, PartialEq)]
849pub struct RootError {
850    /// Root index which lead to the error.
851    pub n: i32,
852    /// Exponents for which the `n`th root could not be calculated.
853    pub exponents: UnitExponents,
854}
855
856impl std::fmt::Display for RootError {
857    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
858        write!(
859            f,
860            "not possible to calculate the {}th root (exponents {} cannot be divided by {} without remainder)",
861            &self.n, &self.exponents, &self.n
862        )
863    }
864}
865
866impl std::error::Error for RootError {}
867
868/**
869Error representing a failed attempt to parse a string into a [`DynQuantity`].
870 */
871#[derive(Default, Debug, Clone, PartialEq)]
872pub struct ParseError {
873    /**
874    String which could not be parsed
875     */
876    pub substring: String,
877    /**
878    The span can be used to index into the string to get the exact characters
879    which could not be parsed.
880     */
881    pub span: std::ops::Range<usize>,
882    /**
883    Parsing can fail due to a variety of reasons, see the docstring of [`ParseErrorReason`].
884     */
885    pub reason: ParseErrorReason,
886}
887
888impl std::fmt::Display for ParseError {
889    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
890        write!(f, "could not parse {}: {}", &self.substring, &self.reason)
891    }
892}
893
894/**
895The varying reasons parsing a string to a [`DynQuantity`] can fail.
896This struct is part of [`ParseError`], which contains the information where
897the parsing failed.
898 */
899#[derive(Default, Debug, Clone, PartialEq)]
900#[repr(u8)]
901pub enum ParseErrorReason {
902    /// String contained an unexpected token.
903    UnexpectedToken,
904    /// Input string was empty.
905    InputIsEmpty,
906    /// Brackets in the string are not balanced:
907    /// - "((5)": Closing bracket is missing
908    /// - "(5))": Opening bracket is missing
909    UnbalancedBrackets,
910    /// Two numbers without any combining operator are in the string:
911    /// - "5 32": Invalid because it is unclear how the numbers should
912    /// combined in the resulting [`DynQuantity`].
913    /// - "5 * 32": Valid
914    TwoNumbersWithoutOperator,
915    /// Two operators without a number inbetween are in the string:
916    /// - "3 / * 2": Invalid
917    /// - "3 / 1 * 2": Valid
918    TwoOperatorsWithoutNumber,
919    /// The string must not start with certain characters, for example:
920    /// - Operators such as *, /, ^
921    /// - Closing brackets
922    MustNotStartWith,
923    /**
924    An addition / subtraction of two invalid quantities was defined in the
925    string, e.g. "3 A + 2 V".
926     */
927    UnitsOfSummandsNotIdentical(UnitsOfSummandsNotIdentical),
928    /// See docstring of [`NotConvertibleFromComplexF64`].
929    NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
930    /// Generic fallback error for all other parsing failures
931    #[default]
932    CouldNotParse,
933}
934
935impl std::fmt::Display for ParseErrorReason {
936    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937        match self {
938            ParseErrorReason::UnexpectedToken => {
939                write!(f, "unexpected token")
940            }
941            ParseErrorReason::InputIsEmpty => {
942                write!(f, "input is empty")
943            }
944            ParseErrorReason::CouldNotParse => write!(f, "could not parse the input"),
945            ParseErrorReason::UnbalancedBrackets => {
946                write!(f, "unbalanced number of brackets")
947            }
948            ParseErrorReason::TwoNumbersWithoutOperator => {
949                write!(
950                    f,
951                    "encountered two numbers without an operator (+ or -) between them"
952                )
953            }
954            ParseErrorReason::TwoOperatorsWithoutNumber => {
955                write!(
956                    f,
957                    "encountered two operators (+, -, * or /) without a number between them"
958                )
959            }
960            ParseErrorReason::UnitsOfSummandsNotIdentical(inner) => inner.fmt(f),
961            ParseErrorReason::MustNotStartWith => {
962                write!(f, "input must not start with this token")
963            }
964            ParseErrorReason::NotConvertibleFromComplexF64(err) => err.fmt(f),
965        }
966    }
967}
968
969impl From<UnitsOfSummandsNotIdentical> for ParseErrorReason {
970    fn from(value: UnitsOfSummandsNotIdentical) -> Self {
971        return Self::UnitsOfSummandsNotIdentical(value);
972    }
973}
974
975impl std::error::Error for ParseErrorReason {}
976
977/**
978Error describing a failed attempt to convert a [`Complex<f64>`] into the type
979`V` of [`DynQuantity<V>`].
980
981For example, this error will be returned when trying to parse a string
982representing a complex quantity into a [`DynQuantity<f64>`].
983 */
984#[derive(Debug, Clone, PartialEq)]
985pub struct NotConvertibleFromComplexF64 {
986    /// Number which failed to convert.
987    pub source: Complex<f64>,
988    /// Target type name.
989    pub target_type: &'static str,
990}
991
992impl std::fmt::Display for NotConvertibleFromComplexF64 {
993    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994        write!(
995            f,
996            "could not convert from {} into target type {}",
997            self.source, self.target_type
998        )
999    }
1000}
1001
1002impl std::error::Error for NotConvertibleFromComplexF64 {}
1003
1004/**
1005Error describing a failed attempt to convert between different types representing
1006quantities.
1007
1008This error can e.g. be returned when trying to convert a [`DynQuantity`] to a
1009[`Quantity`](https://docs.rs/uom/latest/uom/si/struct.Quantity.html) via the
1010[`TryFrom`] implementation. See docstring of [`DynQuantity`].
1011*/
1012#[derive(Debug, Clone, PartialEq)]
1013pub enum ConversionError {
1014    /// See docstring of [`NotConvertibleFromComplexF64`].
1015    NotConvertibleFromComplexF64(NotConvertibleFromComplexF64),
1016    /// Expected a certain unit of measurement, but found a different one.
1017    UnitMismatch {
1018        /// Unit of measurement which was expected.
1019        expected: UnitExponents,
1020        /// Unit of measurement which was found.
1021        found: UnitExponents,
1022    },
1023    /// Fallback case for all other errors.
1024    Custom(String),
1025}
1026
1027impl ConversionError {
1028    /// Create [`Self`] from anything which can be converted to a string.
1029    pub fn custom<T: ToString>(err: T) -> Self {
1030        return Self::Custom(err.to_string());
1031    }
1032}
1033
1034impl std::fmt::Display for ConversionError {
1035    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1036        match self {
1037            ConversionError::NotConvertibleFromComplexF64(value) => return value.fmt(f),
1038            ConversionError::UnitMismatch { expected, found } => {
1039                write!(f, "expected {}, found {}", expected, found)
1040            }
1041            ConversionError::Custom(string) => {
1042                write!(f, "{string}")
1043            }
1044        }
1045    }
1046}
1047
1048impl std::error::Error for ConversionError {}