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}