1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! This module contains types to work with ticks.
//!
//! Each iteration of a Hydroflow transducer loop is called a tick. Associated with the transducer
//! is a clock value, which tells you how many ticks were executed by this transducer prior to the
//! current tick. Each transducer produces totally ordered, sequentially increasing clock values,
//! which you can think of as the "local logical time" at the transducer.

use std::fmt::{Display, Formatter};
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};

use serde::{Deserialize, Serialize};

/// A point in time during execution on transducer.
///
/// `TickInstant` instances can be subtracted to calculate the `TickDuration` between them.
///
/// ```
/// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
///
/// assert_eq!(TickInstant(1) - TickInstant(0), TickDuration::SINGLE_TICK);
/// assert_eq!(TickInstant(0) - TickInstant(1), -TickDuration::SINGLE_TICK);
/// ```
#[derive(
    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
)]
pub struct TickInstant(pub u64);

/// The duration between two ticks.
///
/// `TickDuration` instances can be negative to allow for calculation of `TickInstant` instances in the past.
///
/// ```
/// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
/// assert_eq!(TickInstant(1) + TickDuration::new(-1), TickInstant(0))
/// ```
/// `TickDuration` instances can be added/subtracted to/from other `TickDuration` instances
///
/// ```
/// # use dfir_rs::scheduled::ticks::TickDuration;
/// assert_eq!(TickDuration::ZERO + TickDuration::ZERO, TickDuration::ZERO);
/// assert_eq!(
///     TickDuration::ZERO + TickDuration::SINGLE_TICK,
///     TickDuration::SINGLE_TICK
/// );
/// assert_eq!(
///     TickDuration::SINGLE_TICK - TickDuration::ZERO,
///     TickDuration::SINGLE_TICK
/// );
/// assert_eq!(
///     TickDuration::SINGLE_TICK - TickDuration::SINGLE_TICK,
///     TickDuration::ZERO
/// );
/// assert_eq!(
///     TickDuration::ZERO - TickDuration::SINGLE_TICK,
///     -TickDuration::SINGLE_TICK
/// );
/// ```
#[derive(
    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
)]
pub struct TickDuration {
    /// The length of the duration, measured in ticks.
    pub ticks: i64,
}

impl TickInstant {
    /// Create a new TickInstant
    ///
    /// The specified parameter indicates the number of ticks that have elapsed on the transducer,
    /// prior to this one.
    pub fn new(ticks: u64) -> Self {
        TickInstant(ticks)
    }
}

impl TickDuration {
    /// A zero duration
    ///
    /// It is the identity element for addition for both `TickDuration` and
    /// `TickInstant` (i.e. adding zero duration to a `TickInstant` or `TickDuration` results in
    /// the same `TickInstant` or `TickDuration`.
    ///
    /// ```
    /// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
    /// # use dfir_lang::graph::ops::DelayType::Tick;
    /// let ticks = TickInstant::new(100);
    /// assert_eq!(ticks + TickDuration::ZERO, ticks);
    /// assert_eq!(ticks - TickDuration::ZERO, ticks);
    ///
    /// let duration = TickDuration::new(100);
    /// assert_eq!(duration + TickDuration::ZERO, duration);
    /// assert_eq!(duration - TickDuration::ZERO, duration);
    /// ```
    pub const ZERO: Self = TickDuration { ticks: 0 };

    /// A single tick duration.
    ///
    /// It is the duration between two consecutive `TickInstant` instances.
    ///
    /// ```
    /// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
    /// assert_eq!(TickInstant(0) + TickDuration::SINGLE_TICK, TickInstant(1))
    /// ```
    pub const SINGLE_TICK: Self = TickDuration { ticks: 1 };

    /// Create a new `TickDuration` for the specified tick interval.
    ///
    /// A negative duration allows for calculating `TickInstants` in the past and represents a
    /// backward movement in time.
    pub fn new(ticks: i64) -> TickDuration {
        TickDuration { ticks }
    }
}

impl Add<TickDuration> for TickInstant {
    type Output = TickInstant;

    fn add(self, rhs: TickDuration) -> Self::Output {
        let mut result = self;
        result += rhs;
        result
    }
}

impl AddAssign<TickDuration> for TickInstant {
    fn add_assign(&mut self, rhs: TickDuration) {
        self.0 = self
            .0
            .checked_add_signed(rhs.ticks)
            .expect("overflow while adding tick duration to tick instant.");
    }
}

impl Sub<TickDuration> for TickInstant {
    type Output = TickInstant;

    fn sub(self, rhs: TickDuration) -> Self::Output {
        let mut result = self;
        result -= rhs;
        result
    }
}

impl SubAssign<TickDuration> for TickInstant {
    fn sub_assign(&mut self, rhs: TickDuration) {
        if rhs.ticks.is_positive() {
            self.0 = self
                .0
                .checked_sub(rhs.ticks.unsigned_abs())
                .expect("overflow while subtracting duration from instant.");
        } else if rhs.ticks.is_negative() {
            self.0 = self
                .0
                .checked_add(rhs.ticks.unsigned_abs())
                .expect("overflow while subtracting duration from instant.")
        }
    }
}

impl Sub for TickInstant {
    type Output = TickDuration;

    fn sub(self, rhs: TickInstant) -> Self::Output {
        let minuend = (self.0 as i64).wrapping_add(i64::MIN);
        let subtrahend = (rhs.0 as i64).wrapping_add(i64::MIN);
        let (difference, overflowed) = minuend.overflowing_sub(subtrahend);
        if overflowed {
            panic!("overflow while subtracting two TickInstants.")
        }
        TickDuration { ticks: difference }
    }
}

impl Add for TickDuration {
    type Output = TickDuration;

    fn add(self, rhs: Self) -> Self::Output {
        let mut result = self;
        result += rhs;
        result
    }
}

impl AddAssign for TickDuration {
    fn add_assign(&mut self, rhs: Self) {
        self.ticks = self
            .ticks
            .checked_add(rhs.ticks)
            .expect("Overflow occurred while adding TickDuration instances.")
    }
}

impl Sub for TickDuration {
    type Output = TickDuration;

    fn sub(self, rhs: Self) -> Self::Output {
        let mut result = self;
        result -= rhs;
        result
    }
}

impl SubAssign for TickDuration {
    fn sub_assign(&mut self, rhs: Self) {
        self.ticks = self
            .ticks
            .checked_sub(rhs.ticks)
            .expect("Overflow occurred while subtracting TickDuration instances.");
    }
}

impl Neg for TickDuration {
    type Output = TickDuration;

    fn neg(self) -> Self::Output {
        TickDuration {
            ticks: self
                .ticks
                .checked_neg()
                .expect("Overflow while negating duration."),
        }
    }
}

impl Display for TickInstant {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "[{}]", self.0)
    }
}

impl Display for TickDuration {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "<{}>", self.ticks)
    }
}

impl From<TickInstant> for u64 {
    fn from(value: TickInstant) -> Self {
        value.0
    }
}

impl From<TickDuration> for i64 {
    fn from(value: TickDuration) -> Self {
        value.ticks
    }
}