eva_rt_common/
time.rs

1//! Time and Time² structs.
2//! 
3//! The **Time** and **Time²** (Time2) structs are **f64** wrappers that
4//! describe time items with nanosecond precision. The structs were built to
5//! better write expressions and formulas, and catch subtle typing errors when
6//! writing the formulas from academic papers into code.
7//! 
8//! The general idea is to overload the standard unary and binary operators of
9//! *f64* to better represent what a combination of different unit object is. As
10//! an example, sum of `Time`s is still a `Time`, while division of `Time`s is a
11//! scalar, and product of `Time`s is a `Time²`.
12//! 
13//! Both struct additionally implement `Eq` and `Ord` for easier comparisons.
14//! They use the [ordered-float](https://crates.io/crates/ordered-float/)
15//! crate's functions for comparisons.
16
17pub mod prelude {
18    pub use super::{
19        Time,
20        Time2,
21    };
22}
23
24#[derive(Debug)]
25#[derive(Clone, Copy)]
26pub struct Time {
27    pub value_ns: f64,
28}
29
30#[derive(Debug)]
31#[derive(Clone, Copy)]
32pub struct Time2 {
33    pub value_ns_2: f64,
34}
35
36// =============================================================================
37
38impl Time {
39    pub const MICRO_TO_NANO: f64 = 1000.0;
40    pub const MILLI_TO_NANO: f64 = 1000_000.0;
41    pub const SECS_TO_NANO: f64 = 1000_000_000.0;
42
43    pub fn zero() -> Self {
44        Self { value_ns: 0.0 }
45    }
46
47    pub fn one() -> Self {
48        Self { value_ns: 1.0 }
49    }
50
51    pub fn nanos(time_ns: f64) -> Self {
52        Self { value_ns: time_ns }
53    }
54
55    pub fn micros(time_us: f64) -> Self {
56        Self { value_ns: time_us * Self::MICRO_TO_NANO }
57    }
58
59    pub fn millis(time_ms: f64) -> Self {
60        Self { value_ns: time_ms * Self::MILLI_TO_NANO }
61    }
62
63    pub fn secs(time_s: f64) -> Self {
64        Self { value_ns: time_s * Self::SECS_TO_NANO }
65    }
66
67    pub fn as_nanos(&self) -> f64 {
68        self.value_ns
69    }
70
71    pub fn as_micros(&self) -> f64 {
72        self.value_ns / Self::MICRO_TO_NANO
73    }
74
75    pub fn as_millis(&self) -> f64 {
76        self.value_ns / Self::MILLI_TO_NANO
77    }
78
79    pub fn as_secs(&self) -> f64 {
80        self.value_ns / Self::SECS_TO_NANO
81    }
82
83    pub fn floor(self) -> Self {
84        Self { value_ns: f64::floor(self.value_ns) }
85    }
86
87    pub fn ceil(self) -> Self {
88        Self { value_ns: f64::ceil(self.value_ns) }
89    }
90
91    pub fn round(self) -> Self {
92        Self { value_ns: f64::round(self.value_ns) }
93    }
94}
95
96impl PartialEq for Time {
97    fn eq(&self, other: &Self) -> bool {
98        let error = 0.5;
99
100        f64::abs(self.value_ns - other.value_ns) < error
101    }
102}
103
104impl Eq for Time { }
105
106impl PartialOrd for Time {
107    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
108        ordered_float::OrderedFloat(self.value_ns)
109            .partial_cmp(&ordered_float::OrderedFloat(other.value_ns))
110    }
111}
112
113impl Ord for Time {
114    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
115        ordered_float::OrderedFloat(self.value_ns)
116            .cmp(&ordered_float::OrderedFloat(other.value_ns))
117    }
118}
119
120impl std::ops::Neg for Time {
121    type Output = Time;
122
123    fn neg(self) -> Self::Output {
124        Self::Output { value_ns: -self.value_ns }
125    }
126}
127
128impl std::ops::Add for Time {
129    type Output = Time;
130
131    fn add(self, rhs: Self) -> Self::Output {
132        Self::Output { value_ns: (self.value_ns + rhs.value_ns) }
133    }
134}
135
136impl std::ops::Sub for Time {
137    type Output = Time;
138
139    fn sub(self, rhs: Self) -> Self::Output {
140        Self::Output { value_ns: (self.value_ns - rhs.value_ns) }
141    }
142}
143
144impl std::ops::Mul<f64> for Time {
145    type Output = Time;
146
147    fn mul(self, rhs: f64) -> Self::Output {
148        Self::Output { value_ns: (self.value_ns * rhs) }
149    }
150}
151
152impl std::ops::Mul<Time> for f64 {
153    type Output = Time;
154
155    fn mul(self, rhs: Time) -> Self::Output {
156        rhs * self
157    }
158}
159
160impl std::ops::Div for Time {
161    type Output = f64;
162
163    fn div(self, rhs: Self) -> Self::Output {
164        self.value_ns / rhs.value_ns
165    }
166}
167
168impl std::ops::Div<f64> for Time {
169    type Output = Time;
170
171    fn div(self, rhs: f64) -> Self::Output {
172        Time { value_ns: self.value_ns / rhs }
173    }
174}
175
176impl std::ops::Rem for Time {
177    type Output = Time;
178
179    fn rem(self, rhs: Self) -> Self::Output {
180        Self::Output { value_ns: self.value_ns.floor() % rhs.value_ns.floor() }
181    }
182}
183
184impl std::iter::Sum for Time {
185    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
186        iter.fold(Time::zero(), |acc, val| acc + val)
187    }
188}
189
190impl std::fmt::Display for Time {
191    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192        let milli = self.value_ns / Self::MILLI_TO_NANO;
193        if milli >= 1.0 {
194            return write!(f, "{milli:.3}ms");
195        }
196
197        let micro = self.value_ns / Self::MICRO_TO_NANO;
198        if micro >= 1.0 {
199            return write!(f, "{micro:.3}us");
200        }
201
202        write!(f, "{:.3}ns", self.value_ns)
203    }
204}
205
206impl serde::Serialize for Time {
207    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208    where
209        S: serde::Serializer,
210    {
211        format!("{} ns", self.value_ns).serialize(serializer)
212    }
213}
214
215impl<'de> serde::Deserialize<'de> for Time {
216    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217    where
218        D: serde::Deserializer<'de>,
219    {
220        let time_string = String::deserialize(deserializer)?;
221
222        let pieces: Vec<_> = time_string.trim().split_whitespace().collect();
223        if pieces.len() == 1 {
224            let time: f64 = pieces[0].parse()
225                .map_err(|err| serde::de::Error::custom(format!("Invalid time: {err}")))?;
226
227            Ok(Time { value_ns: time })
228        } else if pieces.len() == 2 {
229            let time: f64 = pieces[0].parse()
230                .map_err(|err| serde::de::Error::custom(format!("Invalid time: {err}")))?;
231            let unit = match pieces[1] {
232                "s" => Time::SECS_TO_NANO,
233                "ms" => Time::MILLI_TO_NANO,
234                "us" => Time::MICRO_TO_NANO,
235                "ns" => 1.0,
236                u => { return Err(serde::de::Error::custom(format!("Unknown time unit: {u}"))); }
237            };
238
239            Ok(Time::nanos(time * unit))
240        } else {
241            return Err(serde::de::Error::custom("Parsing error, unknown format"));
242        }
243    }
244}
245
246impl Time2 {
247    pub fn new(value: f64) -> Self {
248        Self { value_ns_2: value }
249    }
250
251    pub fn value(&self) -> f64 {
252        self.value_ns_2
253    }
254
255    pub fn sqrt(self) -> Time {
256        Time::nanos(self.value_ns_2.sqrt())
257    }
258}
259
260impl std::ops::Neg for Time2 {
261    type Output = Time2;
262
263    fn neg(self) -> Self::Output {
264        Self::Output { value_ns_2: -self.value_ns_2 }
265    }
266}
267
268impl std::ops::Add for Time2 {
269    type Output = Time2;
270
271    fn add(self, rhs: Self) -> Self::Output {
272        Self::Output { value_ns_2: (self.value_ns_2 + rhs.value_ns_2) }
273    }
274}
275
276impl std::ops::Sub for Time2 {
277    type Output = Time2;
278
279    fn sub(self, rhs: Self) -> Self::Output {
280        Self::Output { value_ns_2: (self.value_ns_2 - rhs.value_ns_2) }
281    }
282}
283
284impl std::ops::Mul<Time> for Time {
285    type Output = Time2;
286
287    fn mul(self, rhs: Time) -> Self::Output {
288        Self::Output { value_ns_2: (self.value_ns * rhs.value_ns) }
289    }
290}
291
292impl std::ops::Mul<f64> for Time2 {
293    type Output = Time2;
294
295    fn mul(self, rhs: f64) -> Self::Output {
296        Self::Output { value_ns_2: (self.value_ns_2 * rhs) }
297    }
298}
299
300impl std::ops::Mul<Time2> for f64 {
301    type Output = Time2;
302
303    fn mul(self, rhs: Time2) -> Self::Output {
304        rhs * self
305    }
306}
307
308impl std::ops::Div<Time> for Time2 {
309    type Output = Time;
310
311    fn div(self, rhs: Time) -> Self::Output {
312        Self::Output { value_ns: self.value_ns_2 / rhs.value_ns }
313    }
314}
315
316impl std::ops::Div<f64> for Time2 {
317    type Output = Time2;
318
319    fn div(self, rhs: f64) -> Self::Output {
320        Self::Output { value_ns_2: self.value_ns_2 / rhs }
321    }
322}