Skip to main content

qtty_core/
quantity.rs

1//! Quantity type and its implementations.
2
3use crate::dimension::{DimDiv, DimMul, Dimension};
4use crate::scalar::{Exact, Real, Scalar, Transcendental};
5use crate::unit::{Per, Prod, Unit};
6use core::cmp::Ordering;
7use core::iter::Sum;
8use core::marker::PhantomData;
9use core::ops::*;
10
11/// A quantity with a specific unit and scalar type.
12///
13/// `Quantity<U, S>` wraps a scalar value of type `S` together with phantom type
14/// information about its unit `U`. This enables compile-time dimensional analysis
15/// while maintaining zero runtime cost beyond the scalar's size.
16///
17/// The default scalar type is `f64`, so `Quantity<Meter>` is equivalent to
18/// `Quantity<Meter, f64>`.
19///
20/// # Examples
21///
22/// Basic usage with default `f64`:
23///
24/// ```rust
25/// use qtty_core::length::{Meter, Meters};
26/// use qtty_core::Quantity;
27///
28/// let x = Meters::new(5.0);
29/// let y = Meters::new(3.0);
30/// let sum = x + y;
31/// assert_eq!(sum.value(), 8.0);
32/// ```
33///
34/// Using `f32` for memory efficiency:
35///
36/// ```rust
37/// use qtty_core::length::Meter;
38/// use qtty_core::Quantity;
39///
40/// let x: Quantity<Meter, f32> = Quantity::new(5.0_f32);
41/// assert_eq!(x.value(), 5.0_f32);
42/// ```
43#[derive(Clone, Copy, Debug, PartialEq)]
44pub struct Quantity<U: Unit, S: Scalar = f64>(S, PhantomData<U>);
45
46// ─────────────────────────────────────────────────────────────────────────────
47// Type aliases for common scalar types
48// ─────────────────────────────────────────────────────────────────────────────
49
50/// A quantity backed by `f64` (the default).
51pub type Quantity64<U> = Quantity<U, f64>;
52
53/// A quantity backed by `f32`.
54pub type Quantity32<U> = Quantity<U, f32>;
55
56/// A quantity backed by `rust_decimal::Decimal`.
57#[cfg(feature = "scalar-decimal")]
58pub type QuantityDecimal<U> = Quantity<U, rust_decimal::Decimal>;
59
60/// A quantity backed by `num_rational::Rational64`.
61#[cfg(feature = "scalar-rational")]
62pub type QuantityRational<U> = Quantity<U, num_rational::Rational64>;
63
64/// A quantity backed by `i8`.
65pub type QuantityI8<U> = Quantity<U, i8>;
66
67/// A quantity backed by `i16`.
68pub type QuantityI16<U> = Quantity<U, i16>;
69
70/// A quantity backed by `i32`.
71pub type QuantityI32<U> = Quantity<U, i32>;
72
73/// A quantity backed by `i64`.
74pub type QuantityI64<U> = Quantity<U, i64>;
75
76/// A quantity backed by `i128`.
77pub type QuantityI128<U> = Quantity<U, i128>;
78
79// ─────────────────────────────────────────────────────────────────────────────
80// Core implementation for all Scalar types
81// ─────────────────────────────────────────────────────────────────────────────
82
83impl<U: Unit, S: Scalar> Quantity<U, S> {
84    /// Creates a new quantity with the given value.
85    ///
86    /// ```rust
87    /// use qtty_core::length::Meters;
88    /// let d = Meters::new(3.0);
89    /// assert_eq!(d.value(), 3.0);
90    /// ```
91    #[inline]
92    pub const fn new(value: S) -> Self {
93        Self(value, PhantomData)
94    }
95
96    /// Returns the raw numeric value.
97    ///
98    /// ```rust
99    /// use qtty_core::time::Seconds;
100    /// let t = Seconds::new(2.5);
101    /// assert_eq!(t.value(), 2.5);
102    /// ```
103    #[inline]
104    pub const fn value(self) -> S {
105        self.0
106    }
107
108    /// Returns a reference to the raw numeric value.
109    #[inline]
110    pub const fn value_ref(&self) -> &S {
111        &self.0
112    }
113
114    /// Returns the absolute value.
115    ///
116    /// ```rust
117    /// use qtty_core::angular::Degrees;
118    /// let a = Degrees::new(-10.0);
119    /// assert_eq!(a.abs().value(), 10.0);
120    /// ```
121    #[inline]
122    pub fn abs(self) -> Self {
123        Self::new(self.0.abs())
124    }
125
126    /// Returns the minimum of this quantity and another.
127    ///
128    /// ```rust
129    /// use qtty_core::length::Meters;
130    /// let a = Meters::new(3.0);
131    /// let b = Meters::new(5.0);
132    /// assert_eq!(a.min(b).value(), 3.0);
133    /// ```
134    #[inline]
135    pub fn min(self, other: Self) -> Self {
136        Self::new(self.0.min(other.0))
137    }
138
139    /// Returns the maximum of this quantity and another.
140    #[inline]
141    pub fn max(self, other: Self) -> Self {
142        Self::new(self.0.max(other.0))
143    }
144
145    /// Returns the arithmetic mean (midpoint) of this quantity and another.
146    ///
147    /// For integer-backed quantities this uses integer division semantics
148    /// (truncation toward zero).
149    ///
150    /// ```rust
151    /// use qtty_core::length::Meters;
152    /// let a = Meters::new(10.0);
153    /// let b = Meters::new(14.0);
154    /// assert_eq!(a.mean(b).value(), 12.0);
155    /// ```
156    #[inline]
157    pub fn mean(self, other: Self) -> Self {
158        Self::new((self.0 + other.0) / (S::ONE + S::ONE))
159    }
160
161    /// A constant representing the zero value for this quantity type.
162    #[inline]
163    pub const fn zero() -> Self {
164        Self::new(S::ZERO)
165    }
166
167    /// A constant representing the unit value (one) for this quantity type.
168    #[inline]
169    pub const fn one() -> Self {
170        Self::new(S::ONE)
171    }
172}
173
174// ─────────────────────────────────────────────────────────────────────────────
175// Real-specific implementations (f32, f64, Decimal, etc.)
176// ─────────────────────────────────────────────────────────────────────────────
177
178impl<U: Unit, S: Real> Quantity<U, S> {
179    /// A constant representing NaN for this quantity type.
180    ///
181    /// Note: For types without NaN (like `Decimal`), this may not be a true NaN.
182    ///
183    /// ```rust
184    /// use qtty_core::length::Meters;
185    /// assert!(Meters::NAN.value().is_nan());
186    /// ```
187    pub const NAN: Self = Self(S::NAN, PhantomData);
188
189    /// A constant representing positive infinity.
190    pub const INFINITY: Self = Self(S::INFINITY, PhantomData);
191
192    /// A constant representing negative infinity.
193    pub const NEG_INFINITY: Self = Self(S::NEG_INFINITY, PhantomData);
194
195    /// Returns true if the value is NaN.
196    #[inline]
197    pub fn is_nan(self) -> bool {
198        self.0.is_nan()
199    }
200
201    /// Returns true if the value is infinite.
202    #[inline]
203    pub fn is_infinite(self) -> bool {
204        self.0.is_infinite()
205    }
206
207    /// Returns true if the value is finite.
208    #[inline]
209    pub fn is_finite(self) -> bool {
210        self.0.is_finite()
211    }
212
213    /// Converts this quantity to another unit of the same dimension.
214    ///
215    /// # Example
216    ///
217    /// ```rust
218    /// use qtty_core::length::{Meter, Kilometer, Kilometers};
219    /// use qtty_core::Quantity;
220    ///
221    /// let km = Kilometers::new(1.0);
222    /// let m: Quantity<Meter> = km.to();
223    /// assert_eq!(m.value(), 1000.0);
224    /// ```
225    #[inline]
226    pub fn to<T: Unit<Dim = U::Dim>>(self) -> Quantity<T, S> {
227        let ratio = S::from_f64(U::RATIO / T::RATIO);
228        Quantity::<T, S>::new(self.0 * ratio)
229    }
230
231    /// Convert the scalar type while preserving the unit.
232    ///
233    /// This converts via `f64`, so precision may be lost for types with
234    /// higher precision than `f64`.
235    ///
236    /// # Example
237    ///
238    /// ```rust
239    /// use qtty_core::length::{Meter, Meters};
240    /// use qtty_core::Quantity;
241    ///
242    /// let meters_f64 = Meters::new(100.0);
243    /// let meters_f32: Quantity<Meter, f32> = meters_f64.cast();
244    /// assert_eq!(meters_f32.value(), 100.0_f32);
245    /// ```
246    #[inline]
247    pub fn cast<T: Real>(self) -> Quantity<U, T> {
248        Quantity::new(T::from_f64(self.0.to_f64()))
249    }
250
251    /// Sign of the value.
252    #[inline]
253    pub fn signum(self) -> S {
254        self.0.signum()
255    }
256
257    /// Returns the square root.
258    ///
259    /// Note: This returns the scalar square root of the value. The resulting
260    /// quantity still has the same unit type, which may not be physically
261    /// meaningful in all contexts.
262    #[inline]
263    pub fn sqrt(self) -> Self {
264        Self::new(self.0.sqrt())
265    }
266
267    /// Returns the smallest integer quantity greater than or equal to this value.
268    #[inline]
269    pub fn ceil(self) -> Self {
270        Self::new(self.0.ceil())
271    }
272
273    /// Checks equality with a quantity of a different unit in the same dimension.
274    ///
275    /// The `other` quantity is converted to unit `U` before comparison.
276    /// Note that floating-point conversion may introduce rounding; for exact
277    /// equality checks consider converting both to a common unit first and using
278    /// an epsilon tolerance.
279    ///
280    /// # Example
281    ///
282    /// ```rust
283    /// use qtty_core::length::{Kilometers, Meters};
284    ///
285    /// let km = Kilometers::new(1.0);
286    /// let m = Meters::new(1000.0);
287    /// assert!(km.eq_unit(&m));
288    /// ```
289    #[inline]
290    pub fn eq_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> bool {
291        self.0 == other.to::<U>().value()
292    }
293
294    /// Compares with a quantity of a different unit in the same dimension.
295    ///
296    /// The `other` quantity is converted to unit `U` before comparison.
297    ///
298    /// # Example
299    ///
300    /// ```rust
301    /// use qtty_core::length::{Kilometers, Meters};
302    /// use core::cmp::Ordering;
303    ///
304    /// let km = Kilometers::new(2.0);
305    /// let m = Meters::new(500.0);
306    /// assert_eq!(km.cmp_unit(&m), Some(Ordering::Greater));
307    /// ```
308    #[inline]
309    pub fn cmp_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> Option<Ordering> {
310        self.0.partial_cmp(&other.to::<U>().value())
311    }
312}
313
314// ─────────────────────────────────────────────────────────────────────────────
315// Exact-specific implementations (integers, rationals, etc.)
316// ─────────────────────────────────────────────────────────────────────────────
317
318impl<U: Unit, S: Exact> Quantity<U, S> {
319    /// Converts this quantity to another unit of the same dimension (lossy).
320    ///
321    /// For integer scalars this performs the conversion through `f64` intermediate
322    /// arithmetic, then truncates back to the integer type. The result may lose
323    /// precision due to truncation.
324    ///
325    /// # Example
326    ///
327    /// ```rust
328    /// use qtty_core::Quantity;
329    /// use qtty_core::length::{Meter, Kilometer};
330    ///
331    /// let m: Quantity<Meter, i32> = Quantity::new(1500);
332    /// let km: Quantity<Kilometer, i32> = m.to_lossy();
333    /// assert_eq!(km.value(), 1); // truncated from 1.5
334    /// ```
335    #[inline]
336    pub fn to_lossy<T: Unit<Dim = U::Dim>>(self) -> Quantity<T, S> {
337        let value_f64 = self.0.to_f64_approx();
338        let ratio = U::RATIO / T::RATIO;
339        Quantity::<T, S>::new(S::from_f64_approx(value_f64 * ratio))
340    }
341}
342
343// ─────────────────────────────────────────────────────────────────────────────
344// Const methods for f64 (backward compatibility)
345// ─────────────────────────────────────────────────────────────────────────────
346
347impl<U: Unit + Copy> Quantity<U, f64> {
348    /// Const addition of two quantities.
349    ///
350    /// ```rust
351    /// use qtty_core::length::Meters;
352    /// let a = Meters::new(1.0);
353    /// let b = Meters::new(2.0);
354    /// assert_eq!(a.const_add(b).value(), 3.0);
355    /// ```
356    #[inline]
357    pub const fn const_add(self, other: Self) -> Self {
358        Self(self.0 + other.0, PhantomData)
359    }
360
361    /// Const subtraction of two quantities.
362    #[inline]
363    pub const fn const_sub(self, other: Self) -> Self {
364        Self(self.0 - other.0, PhantomData)
365    }
366
367    /// Const multiplication by a scalar.
368    #[inline]
369    pub const fn const_mul(self, rhs: f64) -> Self {
370        Self(self.0 * rhs, PhantomData)
371    }
372
373    /// Const division by a scalar.
374    #[inline]
375    pub const fn const_div(self, rhs: f64) -> Self {
376        Self(self.0 / rhs, PhantomData)
377    }
378
379    /// Const conversion to another unit.
380    #[inline]
381    pub const fn to_const<T: Unit<Dim = U::Dim> + Copy>(self) -> Quantity<T, f64> {
382        Quantity::<T, f64>(self.0 * (U::RATIO / T::RATIO), PhantomData)
383    }
384
385    /// Const min of two quantities.
386    #[inline]
387    pub const fn min_const(self, other: Self) -> Self {
388        if self.0 < other.0 {
389            self
390        } else {
391            other
392        }
393    }
394
395    /// Const max of two quantities.
396    #[inline]
397    pub const fn max_const(self, other: Self) -> Self {
398        if self.0 > other.0 {
399            self
400        } else {
401            other
402        }
403    }
404}
405
406// ─────────────────────────────────────────────────────────────────────────────
407// Const methods for f32
408// ─────────────────────────────────────────────────────────────────────────────
409
410impl<U: Unit + Copy> Quantity<U, f32> {
411    /// Const addition of two quantities.
412    #[inline]
413    pub const fn const_add(self, other: Self) -> Self {
414        Self(self.0 + other.0, PhantomData)
415    }
416
417    /// Const subtraction of two quantities.
418    #[inline]
419    pub const fn const_sub(self, other: Self) -> Self {
420        Self(self.0 - other.0, PhantomData)
421    }
422
423    /// Const multiplication by a scalar.
424    #[inline]
425    pub const fn const_mul(self, rhs: f32) -> Self {
426        Self(self.0 * rhs, PhantomData)
427    }
428
429    /// Const division by a scalar.
430    #[inline]
431    pub const fn const_div(self, rhs: f32) -> Self {
432        Self(self.0 / rhs, PhantomData)
433    }
434
435    /// Const conversion to another unit.
436    #[inline]
437    pub const fn to_const<T: Unit<Dim = U::Dim> + Copy>(self) -> Quantity<T, f32> {
438        Quantity::<T, f32>(self.0 * (U::RATIO as f32 / T::RATIO as f32), PhantomData)
439    }
440
441    /// Const min of two quantities.
442    #[inline]
443    pub const fn min_const(self, other: Self) -> Self {
444        if self.0 < other.0 {
445            self
446        } else {
447            other
448        }
449    }
450
451    /// Const max of two quantities.
452    #[inline]
453    pub const fn max_const(self, other: Self) -> Self {
454        if self.0 > other.0 {
455            self
456        } else {
457            other
458        }
459    }
460}
461
462// ─────────────────────────────────────────────────────────────────────────────
463// Const methods for signed integer types
464// ─────────────────────────────────────────────────────────────────────────────
465
466macro_rules! impl_const_for_int {
467    ($($t:ty),*) => { $(
468        impl<U: Unit + Copy> Quantity<U, $t> {
469            /// Const addition of two quantities.
470            #[inline]
471            pub const fn const_add(self, other: Self) -> Self {
472                Self(self.0 + other.0, PhantomData)
473            }
474
475            /// Const subtraction of two quantities.
476            #[inline]
477            pub const fn const_sub(self, other: Self) -> Self {
478                Self(self.0 - other.0, PhantomData)
479            }
480
481            /// Const multiplication by a scalar.
482            #[inline]
483            pub const fn const_mul(self, rhs: $t) -> Self {
484                Self(self.0 * rhs, PhantomData)
485            }
486
487            /// Const division by a scalar.
488            #[inline]
489            pub const fn const_div(self, rhs: $t) -> Self {
490                Self(self.0 / rhs, PhantomData)
491            }
492
493            /// Const min of two quantities.
494            #[inline]
495            pub const fn min_const(self, other: Self) -> Self {
496                if self.0 < other.0 {
497                    self
498                } else {
499                    other
500                }
501            }
502
503            /// Const max of two quantities.
504            #[inline]
505            pub const fn max_const(self, other: Self) -> Self {
506                if self.0 > other.0 {
507                    self
508                } else {
509                    other
510                }
511            }
512        }
513    )* };
514}
515
516impl_const_for_int!(i8, i16, i32, i64, i128);
517
518// ─────────────────────────────────────────────────────────────────────────────
519// Operator implementations
520// ─────────────────────────────────────────────────────────────────────────────
521
522impl<U: Unit, S: Scalar> Add for Quantity<U, S> {
523    type Output = Self;
524    #[inline]
525    fn add(self, rhs: Self) -> Self {
526        Self::new(self.0 + rhs.0)
527    }
528}
529
530impl<U: Unit, S: Scalar> AddAssign for Quantity<U, S> {
531    #[inline]
532    fn add_assign(&mut self, rhs: Self) {
533        self.0 += rhs.0;
534    }
535}
536
537impl<U: Unit, S: Scalar> Sub for Quantity<U, S> {
538    type Output = Self;
539    #[inline]
540    fn sub(self, rhs: Self) -> Self {
541        Self::new(self.0 - rhs.0)
542    }
543}
544
545impl<U: Unit, S: Scalar> SubAssign for Quantity<U, S> {
546    #[inline]
547    fn sub_assign(&mut self, rhs: Self) {
548        self.0 -= rhs.0;
549    }
550}
551
552impl<U: Unit, S: Scalar> Mul<S> for Quantity<U, S> {
553    type Output = Self;
554    #[inline]
555    fn mul(self, rhs: S) -> Self {
556        Self::new(self.0 * rhs)
557    }
558}
559
560impl<U: Unit, S: Scalar> Div<S> for Quantity<U, S> {
561    type Output = Self;
562    #[inline]
563    fn div(self, rhs: S) -> Self {
564        Self::new(self.0 / rhs)
565    }
566}
567
568impl<U: Unit, S: Scalar> DivAssign<S> for Quantity<U, S> {
569    /// In-place scalar division.
570    ///
571    /// ```rust
572    /// use qtty_core::length::Meters;
573    ///
574    /// let mut d = Meters::new(120.0);
575    /// d /= 60.0;
576    /// assert_eq!(d.value(), 2.0);
577    /// ```
578    ///
579    /// ```compile_fail
580    /// use qtty_core::length::Meters;
581    ///
582    /// let mut d = Meters::new(120.0);
583    /// d /= Meters::new(60.0);
584    /// ```
585    #[inline]
586    fn div_assign(&mut self, rhs: S) {
587        self.0 /= rhs;
588    }
589}
590
591impl<U: Unit, S: Scalar> Neg for Quantity<U, S> {
592    type Output = Self;
593    #[inline]
594    fn neg(self) -> Self {
595        Self::new(-self.0)
596    }
597}
598
599// Multiplication of f64 * Quantity<U, f64>
600impl<U: Unit> Mul<Quantity<U, f64>> for f64 {
601    type Output = Quantity<U, f64>;
602    #[inline]
603    fn mul(self, rhs: Quantity<U, f64>) -> Self::Output {
604        rhs * self
605    }
606}
607
608// Multiplication of f32 * Quantity<U, f32>
609impl<U: Unit> Mul<Quantity<U, f32>> for f32 {
610    type Output = Quantity<U, f32>;
611    #[inline]
612    fn mul(self, rhs: Quantity<U, f32>) -> Self::Output {
613        rhs * self
614    }
615}
616
617// Multiplication for Decimal (feature-gated)
618#[cfg(feature = "scalar-decimal")]
619impl<U: Unit> Mul<Quantity<U, rust_decimal::Decimal>> for rust_decimal::Decimal {
620    type Output = Quantity<U, rust_decimal::Decimal>;
621    #[inline]
622    fn mul(self, rhs: Quantity<U, rust_decimal::Decimal>) -> Self::Output {
623        rhs * self
624    }
625}
626
627// Multiplication for Rational64 (feature-gated)
628#[cfg(feature = "scalar-rational")]
629impl<U: Unit> Mul<Quantity<U, num_rational::Rational64>> for num_rational::Rational64 {
630    type Output = Quantity<U, num_rational::Rational64>;
631    #[inline]
632    fn mul(self, rhs: Quantity<U, num_rational::Rational64>) -> Self::Output {
633        rhs * self
634    }
635}
636
637// Multiplication for Rational32 (feature-gated)
638#[cfg(feature = "scalar-rational")]
639impl<U: Unit> Mul<Quantity<U, num_rational::Rational32>> for num_rational::Rational32 {
640    type Output = Quantity<U, num_rational::Rational32>;
641    #[inline]
642    fn mul(self, rhs: Quantity<U, num_rational::Rational32>) -> Self::Output {
643        rhs * self
644    }
645}
646
647// Commutative multiplication for signed integer scalars
648macro_rules! impl_int_commutative_mul {
649    ($($t:ty),*) => { $(
650        impl<U: Unit> Mul<Quantity<U, $t>> for $t {
651            type Output = Quantity<U, $t>;
652            #[inline]
653            fn mul(self, rhs: Quantity<U, $t>) -> Self::Output {
654                rhs * self
655            }
656        }
657    )* };
658}
659
660impl_int_commutative_mul!(i8, i16, i32, i64, i128);
661
662// Rem for types that implement Rem (floats and integers)
663impl<U: Unit, S: Scalar + Rem<Output = S>> Rem<S> for Quantity<U, S> {
664    type Output = Self;
665    #[inline]
666    fn rem(self, rhs: S) -> Self {
667        Self::new(self.0 % rhs)
668    }
669}
670
671// PartialEq with scalar
672impl<U: Unit, S: Scalar> PartialEq<S> for Quantity<U, S> {
673    #[inline]
674    fn eq(&self, other: &S) -> bool {
675        self.0 == *other
676    }
677}
678
679// PartialOrd with scalar
680impl<U: Unit, S: Scalar> PartialOrd<S> for Quantity<U, S> {
681    #[inline]
682    fn partial_cmp(&self, other: &S) -> Option<Ordering> {
683        self.0.partial_cmp(other)
684    }
685}
686
687// PartialOrd between quantities of the same unit/scalar.
688impl<U: Unit, S: Scalar> PartialOrd for Quantity<U, S> {
689    #[inline]
690    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
691        self.0.partial_cmp(&other.0)
692    }
693}
694
695// Eq for scalar types that support total equality (integers, rationals, decimals)
696impl<U: Unit, S: Scalar + Eq> Eq for Quantity<U, S> {}
697
698// Ord for scalar types that support total ordering (integers, rationals, decimals)
699impl<U: Unit, S: Scalar + Ord> Ord for Quantity<U, S> {
700    #[inline]
701    fn cmp(&self, other: &Self) -> Ordering {
702        self.0.cmp(&other.0)
703    }
704}
705
706// From scalar
707impl<U: Unit, S: Scalar> From<S> for Quantity<U, S> {
708    #[inline]
709    fn from(value: S) -> Self {
710        Self::new(value)
711    }
712}
713
714// Sum quantities into a quantity of the same unit/scalar.
715impl<U: Unit, S: Scalar> Sum for Quantity<U, S> {
716    #[inline]
717    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
718        iter.fold(Self::zero(), |acc, q| acc + q)
719    }
720}
721
722impl<'a, U: Unit, S: Scalar> Sum<&'a Quantity<U, S>> for Quantity<U, S> {
723    #[inline]
724    fn sum<I: Iterator<Item = &'a Quantity<U, S>>>(iter: I) -> Self {
725        iter.fold(Self::zero(), |acc, q| acc + *q)
726    }
727}
728
729// Sum quantities directly into their raw scalar for ergonomic iterator use.
730impl<U: Unit> Sum<Quantity<U, f64>> for f64 {
731    #[inline]
732    fn sum<I: Iterator<Item = Quantity<U, f64>>>(iter: I) -> Self {
733        iter.fold(0.0, |acc, q| acc + q.value())
734    }
735}
736
737impl<'a, U: Unit> Sum<&'a Quantity<U, f64>> for f64 {
738    #[inline]
739    fn sum<I: Iterator<Item = &'a Quantity<U, f64>>>(iter: I) -> Self {
740        iter.fold(0.0, |acc, q| acc + q.value())
741    }
742}
743
744// ─────────────────────────────────────────────────────────────────────────────
745// Division producing Per<N, D>
746// ─────────────────────────────────────────────────────────────────────────────
747
748impl<N: Unit, D: Unit, S: Scalar> Div<Quantity<D, S>> for Quantity<N, S>
749where
750    N::Dim: DimDiv<D::Dim>,
751    <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
752{
753    type Output = Quantity<Per<N, D>, S>;
754    #[inline]
755    fn div(self, rhs: Quantity<D, S>) -> Self::Output {
756        Quantity::new(self.0 / rhs.0)
757    }
758}
759
760// ─────────────────────────────────────────────────────────────────────────────
761// Multiplication producing Prod<A, B>
762// ─────────────────────────────────────────────────────────────────────────────
763
764impl<A: Unit, B: Unit, S: Scalar> Mul<Quantity<B, S>> for Quantity<A, S>
765where
766    A::Dim: DimMul<B::Dim>,
767    <A::Dim as DimMul<B::Dim>>::Output: Dimension,
768{
769    type Output = Quantity<Prod<A, B>, S>;
770
771    #[inline]
772    fn mul(self, rhs: Quantity<B, S>) -> Self::Output {
773        Quantity::<Prod<A, B>, S>::new(self.0 * rhs.0)
774    }
775}
776
777// ─────────────────────────────────────────────────────────────────────────────
778// Special methods for Per<U, U> (unitless ratios)
779// ─────────────────────────────────────────────────────────────────────────────
780
781impl<U: Unit, S: Transcendental> Quantity<Per<U, U>, S>
782where
783    U::Dim: DimDiv<U::Dim>,
784    <U::Dim as DimDiv<U::Dim>>::Output: Dimension,
785{
786    /// Arc sine of a unitless ratio.
787    ///
788    /// ```rust
789    /// use qtty_core::length::Meters;
790    /// let ratio = Meters::new(1.0) / Meters::new(2.0);
791    /// let angle_rad = ratio.asin();
792    /// assert!((angle_rad - core::f64::consts::FRAC_PI_6).abs() < 1e-12);
793    /// ```
794    #[inline]
795    pub fn asin(&self) -> S {
796        self.0.asin()
797    }
798
799    /// Arc cosine of a unitless ratio.
800    #[inline]
801    pub fn acos(&self) -> S {
802        self.0.acos()
803    }
804
805    /// Arc tangent of a unitless ratio.
806    #[inline]
807    pub fn atan(&self) -> S {
808        self.0.atan()
809    }
810}