Skip to main content

qtty_core/units/
velocity.rs

1//! Velocity unit aliases (`Length / Time`).
2//!
3//! This module defines velocity units as *pure type aliases* over [`Per`] using
4//! length and time units already defined elsewhere in the crate.
5//!
6//! No standalone velocity units are introduced: every velocity is represented as
7//! `Length / Time` at the type level.
8//!
9//! ## Design notes
10//!
11//! - The velocity *dimension* is [`Velocity`] = [`Length`] / [`Time`].
12//! - All velocity units are zero-cost type aliases.
13//! - Conversions are handled automatically via the underlying length and time units.
14//! - No assumptions are made about reference frames, relativistic effects, or media.
15//!
16//! ## Examples
17//!
18//! ```rust
19//! use qtty_core::length::{Kilometer, Kilometers};
20//! use qtty_core::time::{Second, Seconds};
21//! use qtty_core::velocity::Velocity;
22//!
23//! let d = Kilometers::new(42.0);
24//! let t = Seconds::new(2.0);
25//! let v: Velocity<Kilometer, Second> = d / t;
26//! assert!((v.value() - 21.0).abs() < 1e-12);
27//! ```
28//!
29//! ```rust
30//! use qtty_core::length::{Meter, Meters};
31//! use qtty_core::time::{Hour, Hours};
32//! use qtty_core::velocity::Velocity;
33//!
34//! let v: Velocity<Meter, Hour> = Meters::new(3_600.0) / Hours::new(1.0);
35//! assert!((v.value() - 3_600.0).abs() < 1e-12);
36//! ```
37
38use crate::{Per, Quantity, Unit};
39
40/// Re-export the velocity dimension from the dimension module.
41pub use crate::dimension::VelocityDim;
42
43/// Marker trait for any unit whose dimension is [`VelocityDim`].
44pub trait VelocityUnit: Unit<Dim = VelocityDim> {}
45impl<T: Unit<Dim = VelocityDim>> VelocityUnit for T {}
46
47/// A velocity quantity parameterized by length and time units.
48///
49/// # Examples
50///
51/// ```rust
52/// use qtty_core::length::{Kilometer, Meter};
53/// use qtty_core::time::{Second, Hour};
54/// use qtty_core::velocity::Velocity;
55///
56/// let v1: Velocity<Meter, Second> = Velocity::new(10.0);
57/// let v2: Velocity<Kilometer, Hour> = Velocity::new(36.0);
58/// ```
59pub type Velocity<N, D> = Quantity<Per<N, D>>;
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::units::length::{Au, Kilometer, Kilometers, Meter};
65    use crate::units::time::{Day, Hour, Second, Seconds};
66    use crate::Per;
67    use approx::{assert_abs_diff_eq, assert_relative_eq};
68    use proptest::prelude::*;
69
70    // ─────────────────────────────────────────────────────────────────────────────
71    // Basic velocity conversions
72    // ─────────────────────────────────────────────────────────────────────────────
73
74    #[test]
75    fn km_per_s_to_m_per_s() {
76        let v: Velocity<Kilometer, Second> = Velocity::new(1.0);
77        let v_mps: Velocity<Meter, Second> = v.to();
78        assert_abs_diff_eq!(v_mps.value(), 1000.0, epsilon = 1e-9);
79    }
80
81    #[test]
82    fn m_per_s_to_km_per_s() {
83        let v: Velocity<Meter, Second> = Velocity::new(1000.0);
84        let v_kps: Velocity<Kilometer, Second> = v.to();
85        assert_abs_diff_eq!(v_kps.value(), 1.0, epsilon = 1e-12);
86    }
87
88    #[test]
89    fn km_per_h_to_m_per_s() {
90        let v: Velocity<Kilometer, Hour> = Velocity::new(3.6);
91        let v_mps: Velocity<Meter, Second> = v.to();
92        // 3.6 km/h = 1 m/s
93        assert_abs_diff_eq!(v_mps.value(), 1.0, epsilon = 1e-12);
94    }
95
96    #[test]
97    fn km_per_h_to_km_per_s() {
98        let v: Velocity<Kilometer, Hour> = Velocity::new(3600.0);
99        let v_kps: Velocity<Kilometer, Second> = v.to();
100        // 3600 km/h = 1 km/s
101        assert_abs_diff_eq!(v_kps.value(), 1.0, epsilon = 1e-12);
102    }
103
104    #[test]
105    fn au_per_day_to_km_per_s() {
106        let v: Velocity<Au, Day> = Velocity::new(1.0);
107        let v_kps: Velocity<Kilometer, Second> = v.to();
108        // 1 AU/day = 149,597,870.7 km / 86400 s ≈ 1731.5 km/s
109        assert_relative_eq!(v_kps.value(), 1731.5, max_relative = 1e-3);
110    }
111
112    // ─────────────────────────────────────────────────────────────────────────────
113    // Per ratio behavior
114    // ─────────────────────────────────────────────────────────────────────────────
115
116    #[test]
117    fn per_ratio_km_s() {
118        // Per<Kilometer, Second> should have RATIO = 1000 / 1 = 1000
119        let ratio = <Per<Kilometer, Second>>::RATIO;
120        // Kilometer::RATIO = 1000, Second::RATIO = 1.0
121        // So Per ratio = 1000 / 1.0 = 1000
122        assert_relative_eq!(ratio, 1000.0, max_relative = 1e-12);
123    }
124
125    #[test]
126    fn per_ratio_m_s() {
127        // Per<Meter, Second> has RATIO = 1 / 1 = 1
128        let ratio = <Per<Meter, Second>>::RATIO;
129        assert_relative_eq!(ratio, 1.0, max_relative = 1e-12);
130    }
131
132    // ─────────────────────────────────────────────────────────────────────────────
133    // Velocity * Time = Length
134    // ─────────────────────────────────────────────────────────────────────────────
135
136    #[test]
137    fn velocity_times_time() {
138        let v: Velocity<Kilometer, Second> = Velocity::new(10.0);
139        let t: Seconds = Seconds::new(5.0);
140        let d: Kilometers = (v * t).to();
141        assert_abs_diff_eq!(d.value(), 50.0, epsilon = 1e-9);
142    }
143
144    #[test]
145    fn time_times_velocity() {
146        let v: Velocity<Kilometer, Second> = Velocity::new(10.0);
147        let t: Seconds = Seconds::new(5.0);
148        let d: Kilometers = (t * v).to();
149        assert_abs_diff_eq!(d.value(), 50.0, epsilon = 1e-9);
150    }
151
152    // ─────────────────────────────────────────────────────────────────────────────
153    // Length / Time = Velocity
154    // ─────────────────────────────────────────────────────────────────────────────
155
156    #[test]
157    fn length_div_time() {
158        let d: Kilometers = Kilometers::new(100.0);
159        let t: Seconds = Seconds::new(10.0);
160        let v: Velocity<Kilometer, Second> = d / t;
161        assert_abs_diff_eq!(v.value(), 10.0, epsilon = 1e-9);
162    }
163
164    // ─────────────────────────────────────────────────────────────────────────────
165    // Roundtrip conversions
166    // ─────────────────────────────────────────────────────────────────────────────
167
168    #[test]
169    fn roundtrip_mps_kps() {
170        let original: Velocity<Meter, Second> = Velocity::new(500.0);
171        let converted: Velocity<Kilometer, Second> = original.to();
172        let back: Velocity<Meter, Second> = converted.to();
173        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
174    }
175
176    // ─────────────────────────────────────────────────────────────────────────────
177    // Property-based tests
178    // ─────────────────────────────────────────────────────────────────────────────
179
180    proptest! {
181        #[test]
182        fn prop_roundtrip_mps_kps(v in 1e-6..1e6f64) {
183            let original: Velocity<Meter, Second> = Velocity::new(v);
184            let converted: Velocity<Kilometer, Second> = original.to();
185            let back: Velocity<Meter, Second> = converted.to();
186            prop_assert!((back.value() - original.value()).abs() < 1e-9 * v.abs().max(1.0));
187        }
188
189        #[test]
190        fn prop_mps_kps_ratio(v in 1e-6..1e6f64) {
191            let mps: Velocity<Meter, Second> = Velocity::new(v);
192            let kps: Velocity<Kilometer, Second> = mps.to();
193            // 1000 m/s = 1 km/s
194            prop_assert!((mps.value() / kps.value() - 1000.0).abs() < 1e-9);
195        }
196
197        #[test]
198        fn prop_velocity_time_roundtrip(
199            v_val in 1e-3..1e3f64,
200            t_val in 1e-3..1e3f64
201        ) {
202            let v: Velocity<Kilometer, Second> = Velocity::new(v_val);
203            let t: Seconds = Seconds::new(t_val);
204            let d: Kilometers = (v * t).to();
205            // d / t should give back v
206            let v_back: Velocity<Kilometer, Second> = d / t;
207            prop_assert!((v_back.value() - v.value()).abs() / v.value() < 1e-12);
208        }
209    }
210}