aligner/internal/time_types.rs
1// This file is part of the Rust library and binary `aligner`.
2//
3// Copyright (C) 2017 kaegi
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19use std;
20use std::cmp::{Ordering, max, min};
21use std::ops::*;
22
23/// Implements conversion to integer variables for TimeDelta and TimePoint.
24macro_rules! impl_from {
25 ($f:ty, $t:ty) => {
26 impl From<$f> for $t { fn from(t: $f) -> $t { t.0 as $t } }
27 }
28}
29
30/// This struct represents a time difference between two `TimePoints`.
31/// Internally its an integer type.
32#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
33pub struct TimeDelta(i64);
34
35impl TimeDelta {
36 /// No difference in time.
37 pub fn zero() -> TimeDelta {
38 TimeDelta(Default::default())
39 }
40
41 /// Smallest positive time difference the library can work with.
42 pub fn one() -> TimeDelta {
43 TimeDelta(1)
44 }
45}
46
47
48impl_from!(TimeDelta, i32);
49impl_from!(TimeDelta, u32);
50impl_from!(TimeDelta, i64);
51impl_from!(TimeDelta, u64);
52
53impl std::iter::Sum for TimeDelta {
54 fn sum<I: Iterator<Item = TimeDelta>>(iter: I) -> TimeDelta {
55 TimeDelta(iter.map(|d| d.0).sum())
56 }
57}
58
59impl std::fmt::Display for TimePoint {
60 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
61 write!(f, "{}", self.0)
62 }
63}
64impl std::fmt::Display for TimeDelta {
65 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
66 write!(f, "{}", self.0)
67 }
68}
69
70impl Add for TimeDelta {
71 type Output = TimeDelta;
72 fn add(self, rhs: TimeDelta) -> TimeDelta {
73 TimeDelta(self.0 + rhs.0)
74 }
75}
76
77impl AddAssign<TimeDelta> for TimeDelta {
78 fn add_assign(&mut self, rhs: TimeDelta) {
79 self.0 += rhs.0;
80 }
81}
82
83
84impl Sub<TimeDelta> for TimeDelta {
85 type Output = TimeDelta;
86 fn sub(self, rhs: TimeDelta) -> TimeDelta {
87 TimeDelta(self.0 - rhs.0)
88 }
89}
90
91impl SubAssign<TimeDelta> for TimeDelta {
92 fn sub_assign(&mut self, rhs: TimeDelta) {
93 self.0 -= rhs.0;
94 }
95}
96
97impl Mul<i64> for TimeDelta {
98 type Output = TimeDelta;
99 fn mul(self, rhs: i64) -> TimeDelta {
100 TimeDelta(self.0 * rhs)
101 }
102}
103
104impl MulAssign<i64> for TimeDelta {
105 fn mul_assign(&mut self, rhs: i64) {
106 self.0 *= rhs;
107 }
108}
109
110impl Mul<TimeDelta> for i64 {
111 type Output = TimeDelta;
112 fn mul(self, rhs: TimeDelta) -> TimeDelta {
113 TimeDelta(self * rhs.0)
114 }
115}
116
117impl Neg for TimeDelta {
118 type Output = TimeDelta;
119 fn neg(self) -> TimeDelta {
120 TimeDelta(-self.0)
121 }
122}
123
124// //////////////////////////////////////////////////////////////////////////////////////////////////
125// struct TimeSpan
126
127/// Represents a timepoint in your own metric.
128///
129/// A timepoint is internally represented by an integer (because the align
130/// algorithm needs discrete
131/// time steps). You will have to choose your own metric: for example 1i64 means
132/// 2ms. The internal algorithm does not use any non-user given `TimePoint`s
133/// (so its interpretation is
134/// up to you).
135///
136/// This is the reason this library works with `TimePoint` and `TimeDelta`: to
137/// enforce
138/// an absolute and delta relationship an a own metric.
139///
140/// The only way to create a new `TimePoint` is with `TimePoint::from({i64})`.
141///
142/// ```
143/// use aligner::TimePoint;
144///
145/// let p = TimePoint::from(10);
146///
147/// // to get that i64 again
148/// let i1: i64 = p.into();
149/// let i2 = i64::from(p);
150/// ```
151///
152#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
153pub struct TimePoint(i64);
154
155impl From<i64> for TimePoint {
156 fn from(f: i64) -> TimePoint {
157 TimePoint(f)
158 }
159}
160impl_from!(TimePoint, i64);
161
162
163impl Sub for TimePoint {
164 type Output = TimeDelta;
165 fn sub(self, rhs: TimePoint) -> TimeDelta {
166 TimeDelta(self.0 - rhs.0)
167 }
168}
169
170impl Add<TimeDelta> for TimePoint {
171 type Output = TimePoint;
172 fn add(self, rhs: TimeDelta) -> TimePoint {
173 TimePoint(self.0 + rhs.0)
174 }
175}
176
177impl AddAssign<TimeDelta> for TimePoint {
178 fn add_assign(&mut self, rhs: TimeDelta) {
179 self.0 += rhs.0;
180 }
181}
182
183impl Sub<TimeDelta> for TimePoint {
184 type Output = TimePoint;
185 fn sub(self, rhs: TimeDelta) -> TimePoint {
186 TimePoint(self.0 - rhs.0)
187 }
188}
189
190impl SubAssign<TimeDelta> for TimePoint {
191 fn sub_assign(&mut self, rhs: TimeDelta) {
192 self.0 -= rhs.0;
193 }
194}
195
196// //////////////////////////////////////////////////////////////////////////////////////////////////
197// struct TimeSpan
198
199/// Represents a time span from "start" (included) to "end" (excluded).
200///
201/// The constructors will ensure "start <= end", this condition will hold at
202/// any given time.
203#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
204pub struct TimeSpan {
205 /// The first time point of the time span (inclusive)
206 start: TimePoint,
207
208 /// The last time point of the time span (excluded)
209 end: TimePoint,
210}
211
212impl TimeSpan {
213 /// Create a new TimeSpan with `start` and `end`.
214 ///
215 /// # Examples
216 /// ```rust
217 /// use aligner::{TimeSpan, TimePoint};
218 ///
219 /// let t0 = TimePoint::from(0);
220 /// let t10 = TimePoint::from(10);
221 ///
222 /// let ts = TimeSpan::new(t0, t10);
223 /// ```
224 ///
225 /// # Panics
226 ///
227 ///
228 /// This function asserts that `start` is less or equal `end`.
229 ///
230 /// ```rust,should_panic
231 /// use aligner::{TimeSpan, TimePoint};
232 ///
233 /// let t0 = TimePoint::from(0);
234 /// let t10 = TimePoint::from(10);
235 ///
236 /// // this will case a panic
237 /// let ts = TimeSpan::new(t10, t0);
238 /// ```
239 pub fn new(start: TimePoint, end: TimePoint) -> TimeSpan {
240 assert!(start <= end);
241 TimeSpan {
242 start: start,
243 end: end,
244 }
245 }
246
247
248 /// Create a new TimeSpan with `start` and `end`. This function will not
249 /// panic on `end < start`, but
250 /// swap the values before calling `TimeSpan::new()`.
251 ///
252 /// # Examples
253 /// ```rust
254 /// use aligner::{TimeSpan, TimePoint};
255 ///
256 /// let t0 = TimePoint::from(0);
257 /// let t10 = TimePoint::from(10);
258 ///
259 /// let ts = TimeSpan::new_safe(t10, t0);
260 /// assert!(ts.start() == t0 && ts.end() == t10);
261 /// ```
262 pub fn new_safe(start: TimePoint, end: TimePoint) -> TimeSpan {
263 if end < start {
264 TimeSpan::new(end, start)
265 } else {
266 TimeSpan::new(start, end)
267 }
268 }
269
270
271
272 /// Mutates a `TimeSpan`s end.
273 ///
274 /// # Panics
275 ///
276 /// Will panic if `new_end` is less than current `start`.
277 pub fn new_copy_with_end(self, new_end: TimePoint) -> TimeSpan {
278 TimeSpan::new(self.start, new_end)
279 }
280
281 /// Returns the length of the `TimeSpan`.
282 ///
283 /// `len()` is zero, if and only if `start` is `end`.
284 pub fn len(self) -> TimeDelta {
285 self.end - self.start
286 }
287
288 /// Returns true if `start == end`.
289 pub fn is_empty(self) -> bool {
290 self.end == self.start
291 }
292
293 /// Returns the start point of the `TimeSpan`.
294 pub fn start(self) -> TimePoint {
295 self.start
296 }
297
298 /// Returns the end point of the `TimeSpan`.
299 pub fn end(self) -> TimePoint {
300 self.end
301 }
302
303 /// Returns true if `self` contains `TimeSpan` `other`.
304 ///
305 /// # Examples
306 /// ```
307 /// use aligner::{TimeSpan, TimePoint};
308 /// ```
309 pub fn contains(self, other: TimeSpan) -> bool {
310 other.start >= self.start && other.end <= self.end
311 }
312
313 /// Returns the smallest difference between two `TimeSpan`s.
314 ///
315 /// ```
316 /// use aligner::{TimeSpan, TimePoint, TimeDelta};
317 ///
318 /// let p = TimePoint::from(0);
319 /// let d = TimeDelta::one();
320 ///
321 /// let ts1 = TimeSpan::new(p, p + 10 * d);
322 /// let ts4 = TimeSpan::new(p + 20 * d, p + 100 * d);
323 ///
324 /// assert!(TimeSpan::fast_distance_to(ts1, ts1) == 0 * d);
325 /// assert!(TimeSpan::fast_distance_to(ts1, ts4) == 10 * d);
326 /// assert!(TimeSpan::fast_distance_to(ts4, ts1) == 10 * d);
327 /// assert!(TimeSpan::fast_distance_to(ts4, ts4) == 0 * d);
328 /// ```
329 pub fn fast_distance_to(self, other: TimeSpan) -> TimeDelta {
330 // self < other
331 if self.end < other.start {
332 other.start - self.end
333 }
334 // self > other
335 else if self.start > other.end {
336 self.start - other.end
337 }
338 // self and other overlap
339 else {
340 TimeDelta::zero()
341 }
342 }
343
344 /// Returns the smallest difference between two `TimeSpan`s.
345 pub fn get_overlapping_length(self, other: TimeSpan) -> TimeDelta {
346 let start_max = max(self.start, other.start);
347 let end_min = min(self.end, other.end);
348 max(TimeDelta::zero(), end_min - start_max)
349 }
350
351 /// Compares two `TimeSpan`s by their start timepoint.
352 pub fn cmp_start(self, other: TimeSpan) -> Ordering {
353 self.start.cmp(&other.start)
354 }
355
356 /// Compares two `TimeSpan`s by their end timepoint.
357 pub fn cmp_end(self, other: TimeSpan) -> Ordering {
358 self.end.cmp(&other.end)
359 }
360}
361
362impl Add<TimeDelta> for TimeSpan {
363 type Output = TimeSpan;
364 fn add(self, rhs: TimeDelta) -> TimeSpan {
365 TimeSpan::new(self.start + rhs, self.end + rhs)
366 }
367}