qtty_core/units/
unitless.rs

1//! Dimensionless helpers.
2//!
3//! This module contains small adapters for working with dimensionless values.
4//!
5//! The provided conversion from a length quantity to a unitless quantity is *lossy*: it drops the unit type without
6//! performing any normalization. The numeric value is preserved as-is.
7//!
8//! ```rust
9//! use qtty_core::length::Kilometers;
10//! use qtty_core::{Quantity, Unitless};
11//!
12//! let km = Kilometers::new(3.0);
13//! let u: Quantity<Unitless> = km.into();
14//! assert_eq!(u.value(), 3.0);
15//! ```
16
17use crate::units::length::LengthUnit;
18use crate::{Quantity, Unitless};
19
20impl<U: LengthUnit> From<Quantity<U>> for Quantity<Unitless> {
21    fn from(length: Quantity<U>) -> Self {
22        Self::new(length.value())
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use crate::units::length::Meters;
30    use crate::Unit;
31    use approx::assert_abs_diff_eq;
32    use proptest::prelude::*;
33
34    // ─────────────────────────────────────────────────────────────────────────────
35    // Basic Unitless behavior
36    // ─────────────────────────────────────────────────────────────────────────────
37
38    #[test]
39    fn unitless_new_and_value() {
40        let u: Quantity<Unitless> = Quantity::new(42.0);
41        assert_eq!(u.value(), 42.0);
42    }
43
44    #[test]
45    fn unitless_from_f64() {
46        let u: Quantity<Unitless> = 1.23456.into();
47        assert_abs_diff_eq!(u.value(), 1.23456, epsilon = 1e-12);
48    }
49
50    // ─────────────────────────────────────────────────────────────────────────────
51    // Display formatting
52    // ─────────────────────────────────────────────────────────────────────────────
53
54    #[test]
55    fn display_unitless() {
56        let u: Quantity<Unitless> = Quantity::new(123.456);
57        let s = format!("{}", u);
58        assert_eq!(s, "123.456");
59    }
60
61    #[test]
62    fn display_unitless_integer() {
63        let u: Quantity<Unitless> = Quantity::new(42.0);
64        let s = format!("{}", u);
65        assert_eq!(s, "42");
66    }
67
68    // ─────────────────────────────────────────────────────────────────────────────
69    // Conversion from length
70    // ─────────────────────────────────────────────────────────────────────────────
71
72    #[test]
73    fn from_length() {
74        let m = Meters::new(42.0);
75        let u: Quantity<Unitless> = m.into();
76        assert_eq!(u.value(), 42.0);
77    }
78
79    // ─────────────────────────────────────────────────────────────────────────────
80    // Arithmetic operations
81    // ─────────────────────────────────────────────────────────────────────────────
82
83    #[test]
84    fn unitless_addition() {
85        let a: Quantity<Unitless> = Quantity::new(3.0);
86        let b: Quantity<Unitless> = Quantity::new(4.0);
87        assert_eq!((a + b).value(), 7.0);
88    }
89
90    #[test]
91    fn unitless_subtraction() {
92        let a: Quantity<Unitless> = Quantity::new(10.0);
93        let b: Quantity<Unitless> = Quantity::new(3.0);
94        assert_eq!((a - b).value(), 7.0);
95    }
96
97    #[test]
98    fn unitless_multiplication() {
99        let a: Quantity<Unitless> = Quantity::new(3.0);
100        assert_eq!((a * 4.0).value(), 12.0);
101    }
102
103    #[test]
104    fn unitless_division() {
105        let a: Quantity<Unitless> = Quantity::new(12.0);
106        assert_eq!((a / 4.0).value(), 3.0);
107    }
108
109    // ─────────────────────────────────────────────────────────────────────────────
110    // Unit trait implementation
111    // ─────────────────────────────────────────────────────────────────────────────
112
113    #[test]
114    fn unitless_ratio() {
115        assert_eq!(Unitless::RATIO, 1.0);
116    }
117
118    #[test]
119    fn unitless_symbol() {
120        assert_eq!(Unitless::SYMBOL, "");
121    }
122
123    // ─────────────────────────────────────────────────────────────────────────────
124    // Property-based tests
125    // ─────────────────────────────────────────────────────────────────────────────
126
127    proptest! {
128        #[test]
129        fn prop_unitless_arithmetic(a in -1e6..1e6f64, b in -1e6..1e6f64) {
130            let qa: Quantity<Unitless> = Quantity::new(a);
131            let qb: Quantity<Unitless> = Quantity::new(b);
132
133            // Addition is commutative
134            prop_assert!((((qa + qb).value() - (qb + qa).value()).abs() < 1e-9));
135
136            // Value is preserved
137            prop_assert!(((qa + qb).value() - (a + b)).abs() < 1e-9);
138        }
139
140        #[test]
141        fn prop_from_length_preserves_value(v in -1e6..1e6f64) {
142            let m = Meters::new(v);
143            let u: Quantity<Unitless> = m.into();
144            prop_assert!((u.value() - v).abs() < 1e-12);
145        }
146    }
147}