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}