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<Self> for Quantity<U, S> {
569    #[inline]
570    fn div_assign(&mut self, rhs: Self) {
571        self.0 /= rhs.0;
572    }
573}
574
575impl<U: Unit, S: Scalar> Neg for Quantity<U, S> {
576    type Output = Self;
577    #[inline]
578    fn neg(self) -> Self {
579        Self::new(-self.0)
580    }
581}
582
583// Multiplication of f64 * Quantity<U, f64>
584impl<U: Unit> Mul<Quantity<U, f64>> for f64 {
585    type Output = Quantity<U, f64>;
586    #[inline]
587    fn mul(self, rhs: Quantity<U, f64>) -> Self::Output {
588        rhs * self
589    }
590}
591
592// Multiplication of f32 * Quantity<U, f32>
593impl<U: Unit> Mul<Quantity<U, f32>> for f32 {
594    type Output = Quantity<U, f32>;
595    #[inline]
596    fn mul(self, rhs: Quantity<U, f32>) -> Self::Output {
597        rhs * self
598    }
599}
600
601// Multiplication for Decimal (feature-gated)
602#[cfg(feature = "scalar-decimal")]
603impl<U: Unit> Mul<Quantity<U, rust_decimal::Decimal>> for rust_decimal::Decimal {
604    type Output = Quantity<U, rust_decimal::Decimal>;
605    #[inline]
606    fn mul(self, rhs: Quantity<U, rust_decimal::Decimal>) -> Self::Output {
607        rhs * self
608    }
609}
610
611// Multiplication for Rational64 (feature-gated)
612#[cfg(feature = "scalar-rational")]
613impl<U: Unit> Mul<Quantity<U, num_rational::Rational64>> for num_rational::Rational64 {
614    type Output = Quantity<U, num_rational::Rational64>;
615    #[inline]
616    fn mul(self, rhs: Quantity<U, num_rational::Rational64>) -> Self::Output {
617        rhs * self
618    }
619}
620
621// Multiplication for Rational32 (feature-gated)
622#[cfg(feature = "scalar-rational")]
623impl<U: Unit> Mul<Quantity<U, num_rational::Rational32>> for num_rational::Rational32 {
624    type Output = Quantity<U, num_rational::Rational32>;
625    #[inline]
626    fn mul(self, rhs: Quantity<U, num_rational::Rational32>) -> Self::Output {
627        rhs * self
628    }
629}
630
631// Commutative multiplication for signed integer scalars
632macro_rules! impl_int_commutative_mul {
633    ($($t:ty),*) => { $(
634        impl<U: Unit> Mul<Quantity<U, $t>> for $t {
635            type Output = Quantity<U, $t>;
636            #[inline]
637            fn mul(self, rhs: Quantity<U, $t>) -> Self::Output {
638                rhs * self
639            }
640        }
641    )* };
642}
643
644impl_int_commutative_mul!(i8, i16, i32, i64, i128);
645
646// Rem for types that implement Rem (floats and integers)
647impl<U: Unit, S: Scalar + Rem<Output = S>> Rem<S> for Quantity<U, S> {
648    type Output = Self;
649    #[inline]
650    fn rem(self, rhs: S) -> Self {
651        Self::new(self.0 % rhs)
652    }
653}
654
655// PartialEq with scalar
656impl<U: Unit, S: Scalar> PartialEq<S> for Quantity<U, S> {
657    #[inline]
658    fn eq(&self, other: &S) -> bool {
659        self.0 == *other
660    }
661}
662
663// PartialOrd with scalar
664impl<U: Unit, S: Scalar> PartialOrd<S> for Quantity<U, S> {
665    #[inline]
666    fn partial_cmp(&self, other: &S) -> Option<Ordering> {
667        self.0.partial_cmp(other)
668    }
669}
670
671// PartialOrd between quantities of the same unit/scalar.
672impl<U: Unit, S: Scalar> PartialOrd for Quantity<U, S> {
673    #[inline]
674    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
675        self.0.partial_cmp(&other.0)
676    }
677}
678
679// Eq for scalar types that support total equality (integers, rationals, decimals)
680impl<U: Unit, S: Scalar + Eq> Eq for Quantity<U, S> {}
681
682// Ord for scalar types that support total ordering (integers, rationals, decimals)
683impl<U: Unit, S: Scalar + Ord> Ord for Quantity<U, S> {
684    #[inline]
685    fn cmp(&self, other: &Self) -> Ordering {
686        self.0.cmp(&other.0)
687    }
688}
689
690// From scalar
691impl<U: Unit, S: Scalar> From<S> for Quantity<U, S> {
692    #[inline]
693    fn from(value: S) -> Self {
694        Self::new(value)
695    }
696}
697
698// Sum quantities into a quantity of the same unit/scalar.
699impl<U: Unit, S: Scalar> Sum for Quantity<U, S> {
700    #[inline]
701    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
702        iter.fold(Self::zero(), |acc, q| acc + q)
703    }
704}
705
706impl<'a, U: Unit, S: Scalar> Sum<&'a Quantity<U, S>> for Quantity<U, S> {
707    #[inline]
708    fn sum<I: Iterator<Item = &'a Quantity<U, S>>>(iter: I) -> Self {
709        iter.fold(Self::zero(), |acc, q| acc + *q)
710    }
711}
712
713// Sum quantities directly into their raw scalar for ergonomic iterator use.
714impl<U: Unit> Sum<Quantity<U, f64>> for f64 {
715    #[inline]
716    fn sum<I: Iterator<Item = Quantity<U, f64>>>(iter: I) -> Self {
717        iter.fold(0.0, |acc, q| acc + q.value())
718    }
719}
720
721impl<'a, U: Unit> Sum<&'a Quantity<U, f64>> for f64 {
722    #[inline]
723    fn sum<I: Iterator<Item = &'a Quantity<U, f64>>>(iter: I) -> Self {
724        iter.fold(0.0, |acc, q| acc + q.value())
725    }
726}
727
728// ─────────────────────────────────────────────────────────────────────────────
729// Division producing Per<N, D>
730// ─────────────────────────────────────────────────────────────────────────────
731
732impl<N: Unit, D: Unit, S: Scalar> Div<Quantity<D, S>> for Quantity<N, S>
733where
734    N::Dim: DimDiv<D::Dim>,
735    <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
736{
737    type Output = Quantity<Per<N, D>, S>;
738    #[inline]
739    fn div(self, rhs: Quantity<D, S>) -> Self::Output {
740        Quantity::new(self.0 / rhs.0)
741    }
742}
743
744// ─────────────────────────────────────────────────────────────────────────────
745// Multiplication producing Prod<A, B>
746// ─────────────────────────────────────────────────────────────────────────────
747
748impl<A: Unit, B: Unit, S: Scalar> Mul<Quantity<B, S>> for Quantity<A, S>
749where
750    A::Dim: DimMul<B::Dim>,
751    <A::Dim as DimMul<B::Dim>>::Output: Dimension,
752{
753    type Output = Quantity<Prod<A, B>, S>;
754
755    #[inline]
756    fn mul(self, rhs: Quantity<B, S>) -> Self::Output {
757        Quantity::<Prod<A, B>, S>::new(self.0 * rhs.0)
758    }
759}
760
761// ─────────────────────────────────────────────────────────────────────────────
762// Special methods for Per<U, U> (unitless ratios)
763// ─────────────────────────────────────────────────────────────────────────────
764
765impl<U: Unit, S: Transcendental> Quantity<Per<U, U>, S>
766where
767    U::Dim: DimDiv<U::Dim>,
768    <U::Dim as DimDiv<U::Dim>>::Output: Dimension,
769{
770    /// Arc sine of a unitless ratio.
771    ///
772    /// ```rust
773    /// use qtty_core::length::Meters;
774    /// let ratio = Meters::new(1.0) / Meters::new(2.0);
775    /// let angle_rad = ratio.asin();
776    /// assert!((angle_rad - core::f64::consts::FRAC_PI_6).abs() < 1e-12);
777    /// ```
778    #[inline]
779    pub fn asin(&self) -> S {
780        self.0.asin()
781    }
782
783    /// Arc cosine of a unitless ratio.
784    #[inline]
785    pub fn acos(&self) -> S {
786        self.0.acos()
787    }
788
789    /// Arc tangent of a unitless ratio.
790    #[inline]
791    pub fn atan(&self) -> S {
792        self.0.atan()
793    }
794}