Skip to main content

deep_time/dt/
helpers.rs

1use crate::{
2    ATTOS_PER_FS_I128, ATTOS_PER_HOUR, ATTOS_PER_MIN, ATTOS_PER_MS_I128, ATTOS_PER_NS_I128,
3    ATTOS_PER_PS_I128, ATTOS_PER_SEC_I128, ATTOS_PER_SECF, ATTOS_PER_US_I128, Dt, Real,
4};
5
6impl Dt {
7    /// Clamps an `i128` to the representable range of `i64`.
8    pub const fn to_i64(x: i128) -> i64 {
9        let y = x as i64;
10        if x == y as i128 {
11            y
12        } else if x > 0 {
13            i64::MAX
14        } else {
15            i64::MIN
16        }
17    }
18
19    /// Clamps an `i128` to the representable range of `u64`.
20    pub const fn to_u64(x: i128) -> u64 {
21        if x > u64::MAX as i128 {
22            u64::MAX
23        } else if x < u64::MIN as i128 {
24            u64::MIN
25        } else {
26            x as u64
27        }
28    }
29
30    /// Converts a `u128` to `i128`, saturating at [`i128::MAX`] when the value would wrap.
31    ///
32    /// A bare `x as i128` is wrapping: values above `i128::MAX` become negative
33    /// (e.g. `u128::MAX as i128 == -1`). Prefer this helper whenever a non-negative
34    /// attosecond (or other) magnitude stored as `u128` is fed into signed `i128` arithmetic.
35    pub const fn to_i128(x: u128) -> i128 {
36        if x > i128::MAX as u128 {
37            i128::MAX
38        } else {
39            x as i128
40        }
41    }
42
43    /// Combines a whole unit count and fractional attoseconds within that unit into total
44    /// attoseconds.
45    ///
46    /// Computes `whole * unit_attos + frac_attos`. The fractional part is always **added**, even
47    /// when `whole` is negative — the Euclidean / floor split used by
48    /// [`to_ms_floor`](../struct.Dt.html#method.to_ms_floor),
49    /// [`to_ns_floor`](../struct.Dt.html#method.to_ns_floor).
50    ///
51    /// This is **not** the same as pairing with truncating extractors like
52    /// [`to_ms`](../struct.Dt.html#method.to_ms) (signed remainder).
53    ///
54    /// The fraction is never subtracted even when `whole` is negative.
55    ///
56    /// For the truncating / signed-remainder split, use
57    /// [`unit_and_signed_attos_to_attos`](../struct.Dt.html#method.unit_and_signed_attos_to_attos).
58    #[inline(always)]
59    pub const fn unit_and_attos_to_attos(whole: i128, frac_attos: u128, unit_attos: i128) -> i128 {
60        whole
61            .saturating_mul(unit_attos)
62            .saturating_add(Self::to_i128(frac_attos))
63    }
64
65    /// Combines a whole unit count and a signed fractional remainder into total attoseconds.
66    ///
67    /// The two parts are the left and right sides of a decimal: e.g. `-1.3` units is
68    /// `whole = -1` and a negative `frac_attos` for the `0.3` **(expressed in attoseconds, not
69    /// in the unit itself)**.
70    ///
71    /// Used by constructors such as
72    /// [`from_ms`](../struct.Dt.html#method.from_ms).
73    ///
74    /// For the floor form (fraction always non-negative and always added), use
75    /// [`unit_and_attos_to_attos`](../struct.Dt.html#method.unit_and_attos_to_attos).
76    #[inline(always)]
77    pub const fn unit_and_signed_attos_to_attos(
78        whole: i128,
79        frac_attos: i128,
80        unit_attos: i128,
81    ) -> i128 {
82        whole.saturating_mul(unit_attos).saturating_add(frac_attos)
83    }
84
85    /// Converts whole femtoseconds → total attoseconds (`× 10³`).
86    #[inline(always)]
87    pub const fn fs_to_attos(fs: i128) -> i128 {
88        fs.saturating_mul(ATTOS_PER_FS_I128)
89    }
90
91    /// Converts whole picoseconds → total attoseconds (`× 10⁶`).
92    #[inline(always)]
93    pub const fn ps_to_attos(ps: i128) -> i128 {
94        ps.saturating_mul(ATTOS_PER_PS_I128)
95    }
96
97    /// Converts whole nanoseconds → total attoseconds (`× 10⁹`).
98    #[inline(always)]
99    pub const fn ns_to_attos(ns: i128) -> i128 {
100        ns.saturating_mul(ATTOS_PER_NS_I128)
101    }
102
103    /// Converts whole microseconds → total attoseconds (`× 10¹²`).
104    #[inline(always)]
105    pub const fn us_to_attos(us: i128) -> i128 {
106        us.saturating_mul(ATTOS_PER_US_I128)
107    }
108
109    /// Converts whole milliseconds → total attoseconds (`× 10¹⁵`).
110    #[inline(always)]
111    pub const fn ms_to_attos(ms: i128) -> i128 {
112        ms.saturating_mul(ATTOS_PER_MS_I128)
113    }
114
115    /// Converts whole seconds → total attoseconds (`× 10¹⁸`).
116    #[inline(always)]
117    pub const fn sec_to_attos(sec: i128) -> i128 {
118        sec.saturating_mul(ATTOS_PER_SEC_I128)
119    }
120
121    /// Converts whole minutes → total attoseconds (`× 60 × 10¹⁸`).
122    #[inline(always)]
123    pub const fn mins_to_attos(mins: i128) -> i128 {
124        mins.saturating_mul(ATTOS_PER_MIN)
125    }
126
127    /// Converts whole hours → total attoseconds (`× 3600 × 10¹⁸`).
128    #[inline(always)]
129    pub const fn hours_to_attos(hours: i128) -> i128 {
130        hours.saturating_mul(ATTOS_PER_HOUR)
131    }
132
133    /// Converts total attoseconds → whole seconds as i64
134    #[inline(always)]
135    pub const fn attos_to_sec_i64(attos: i128) -> i64 {
136        Self::to_i64(attos / ATTOS_PER_SEC_I128)
137    }
138
139    /// Clamps `value` to the range `[min, max]`.
140    ///
141    /// This is a `const fn`, so it can be used in const contexts
142    /// (e.g. const generics, statics, const evaluation, etc.).
143    ///
144    /// If `min > max`, the result is equivalent to clamping to `[max, min]`.
145    pub const fn clamp_u8(value: u8, min: u8, max: u8) -> u8 {
146        if value < min {
147            min
148        } else if value > max {
149            max
150        } else {
151            value
152        }
153    }
154
155    /// Clamps `value` to the range `[min, max]`.
156    ///
157    /// This is a `const fn`, so it can be used in const contexts
158    /// (e.g. const generics, statics, const evaluation, etc.).
159    ///
160    /// If `min > max`, the result is equivalent to clamping to `[max, min]`.
161    pub const fn clamp_u64(value: u64, min: u64, max: u64) -> u64 {
162        if value < min {
163            min
164        } else if value > max {
165            max
166        } else {
167            value
168        }
169    }
170
171    /// **Lossy** conversion of u128 attoseconds to → float seconds (s).
172    #[inline(always)]
173    pub const fn attos_to_sec_f(attos: u128) -> Real {
174        f!(attos) / ATTOS_PER_SECF
175    }
176}