summaryrefslogtreecommitdiffstats
path: root/server/src/time.cppm
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/time.cppm')
-rw-r--r--server/src/time.cppm353
1 files changed, 202 insertions, 151 deletions
diff --git a/server/src/time.cppm b/server/src/time.cppm
index 767883c..907cec4 100644
--- a/server/src/time.cppm
+++ b/server/src/time.cppm
@@ -4,202 +4,253 @@ import std;
4 4
5export namespace routemon::time { 5export namespace routemon::time {
6 6
7 using timestamp = std::chrono::time_point<std::chrono::utc_clock>; 7using timestamp = std::chrono::time_point<std::chrono::utc_clock>;
8 8
9 class period { 9class period
10 // Assuming [start, end). Unfortunately the DATEX II model is not 10{
11 // clear about this. 11 // Assuming [start, end). Unfortunately the DATEX II model is not
12 timestamp start_; 12 // clear about this.
13 timestamp end_; 13 timestamp start_;
14 timestamp end_;
14 15
15 public: 16public:
16 explicit period(timestamp start, timestamp end) 17 explicit period(timestamp start, timestamp end) : start_{start}, end_{end}
17 : start_{start}, end_{end} 18 {
19 if (start >= end)
18 { 20 {
19 if (start >= end) { 21 throw std::invalid_argument("period: start should be before end");
20 throw std::invalid_argument("period: start should be before end");
21 }
22 } 22 }
23 }
23 24
24 [[nodiscard]] auto intersect(period other) const -> std::optional<period> { 25 [[nodiscard]] auto intersect(period other) const -> std::optional<period>
25 auto const new_start = start_ < other.start() ? other.start() : start_; 26 {
26 auto const new_end = other.end() < end_ ? other.end() : end_; 27 auto const new_start = start_ < other.start() ? other.start() : start_;
27 return new_start < new_end ? std::make_optional(period{new_start, new_end}) : std::nullopt; 28 auto const new_end = other.end() < end_ ? other.end() : end_;
28 } 29 return new_start < new_end ? std::make_optional(period{new_start, new_end})
30 : std::nullopt;
31 }
29 32
30 [[nodiscard]] auto except(period other) const -> std::pair<std::optional<period>, std::optional<period>> { 33 [[nodiscard]] auto except(period other) const
31 auto const before_start = start_; 34 -> std::pair<std::optional<period>, std::optional<period>>
32 auto const before_end = other.start(); 35 {
33 auto const after_start = end_; 36 auto const before_start = start_;
34 auto const after_end = other.end(); 37 auto const before_end = other.start();
35 std::optional<period> before, after; 38 auto const after_start = end_;
36 if (before_start < before_end) 39 auto const after_end = other.end();
37 before = period{before_start, before_end}; 40 std::optional<period> before, after;
38 if (after_start < after_end) 41 if (before_start < before_end)
39 after = period{after_end, after_start}; 42 before = period{before_start, before_end};
40 return std::make_pair(before, after); 43 if (after_start < after_end)
41 } 44 after = period{after_end, after_start};
45 return std::make_pair(before, after);
46 }
42 47
43 [[nodiscard]] auto start() const -> timestamp { return start_; } 48 [[nodiscard]] auto start() const -> timestamp { return start_; }
44 [[nodiscard]] auto end() const -> timestamp { return end_; } 49 [[nodiscard]] auto end() const -> timestamp { return end_; }
45 }; 50};
46 51
47 class period_seq { 52class period_seq
48 std::vector<period> periods_; 53{
54 std::vector<period> periods_;
49 55
50 // The way lt and ge are ordered makes a difference for how the sorting 56 // The way lt and ge are ordered makes a difference for how the sorting
51 // (insertion based on lower_bound) works. Do not carelessly reorder this. 57 // (insertion based on lower_bound) works. Do not carelessly reorder this.
52 enum lt_ge : std::uint8_t { 58 enum lt_ge : std::uint8_t
53 ge, // >= 59 {
54 lt, // < 60 ge, // >=
55 }; 61 lt, // <
62 };
56 63
57 // O(n log n) 64 // O(n log n)
58 template<std::input_iterator I, std::sentinel_for<I> S> 65 template <std::input_iterator I, std::sentinel_for<I> S>
59 requires std::same_as<std::iter_value_t<I>, period> 66 requires std::same_as<std::iter_value_t<I>, period>
60 static auto consolidate(I begin, S end) -> std::vector<period> { 67 static auto consolidate(I begin, S end) -> std::vector<period>
61 auto periods = std::vector<period>{}; 68 {
62 auto preds = std::vector<std::pair<timestamp, lt_ge>>{}; 69 auto periods = std::vector<period>{};
70 auto preds = std::vector<std::pair<timestamp, lt_ge>>{};
63 71
64 for (auto it = begin; it != end; it++) { 72 for (auto it = begin; it != end; it++)
65 auto const& period = *it; 73 {
74 auto const& period = *it;
66 75
67 auto const a = std::make_pair(period.start(), ge); 76 auto const a = std::make_pair(period.start(), ge);
68 auto const b = std::make_pair(period.end(), lt); 77 auto const b = std::make_pair(period.end(), lt);
69 preds.insert(std::lower_bound(preds.begin(), preds.end(), a), a); 78 preds.insert(std::lower_bound(preds.begin(), preds.end(), a), a);
70 preds.insert(std::lower_bound(preds.begin(), preds.end(), b), b); 79 preds.insert(std::lower_bound(preds.begin(), preds.end(), b), b);
71 } 80 }
72 81
73 if (preds.empty()) 82 if (preds.empty())
74 return periods; 83 return periods;
75 84
76 if (preds.size() < 2) 85 if (preds.size() < 2)
77 throw std::logic_error{"period_seq::consolidate: amount of predicates should be >= 2"}; 86 throw std::logic_error{
78 if (preds.front().second != ge) 87 "period_seq::consolidate: amount of predicates should be >= 2"
79 throw std::logic_error{"period_seq::consolidate: first element of preds should be a ge-element"}; 88 };
80 if (preds.back().second != lt) 89 if (preds.front().second != ge)
81 throw std::logic_error{"period_seq::consolidate: last element of preds should be an lt-element"}; 90 throw std::logic_error{"period_seq::consolidate: first element of preds "
91 "should be a ge-element"};
92 if (preds.back().second != lt)
93 throw std::logic_error{"period_seq::consolidate: last element of preds "
94 "should be an lt-element"};
82 95
83 auto period_start = preds[0].first; 96 auto period_start = preds[0].first;
84 for (std::size_t i = 1; i < preds.size(); i++) { 97 for (std::size_t i = 1; i < preds.size(); i++)
85 if (preds[i].second == lt && (i + 1 == preds.size() || preds[i + 1].second == ge)) { 98 {
86 auto const period_end = preds[i].first; 99 if (preds[i].second == lt
87 if (!periods.empty() && periods.back().start() == period_start) 100 && (i + 1 == preds.size() || preds[i + 1].second == ge))
88 periods.back() = period{periods.back().end(), period_end}; 101 {
89 else 102 auto const period_end = preds[i].first;
90 periods.emplace_back(period_start, period_end); 103 if (!periods.empty() && periods.back().start() == period_start)
91 if (i + 1 != preds.size()) { 104 periods.back() = period{periods.back().end(), period_end};
92 period_start = preds[i + 1].first; 105 else
93 i++; 106 periods.emplace_back(period_start, period_end);
94 } 107 if (i + 1 != preds.size())
108 {
109 period_start = preds[i + 1].first;
110 i++;
95 } 111 }
96 } 112 }
97
98 return periods;
99 } 113 }
100 114
101 explicit period_seq(std::vector<period> periods) 115 return periods;
102 : periods_{std::move(periods)} 116 }
117
118 explicit period_seq(std::vector<period> periods)
119 : periods_{std::move(periods)}
120 {
121 for (auto i = 0uz; i < periods_.size(); i++)
103 { 122 {
104 for (auto i = 0uz; i < periods_.size(); i++) { 123 if (i + 1 < periods_.size())
105 if (i + 1 < periods_.size()) { 124 {
106 if (periods_[i].end() >= periods_[i + 1].start()) { 125 if (periods_[i].end() >= periods_[i + 1].start())
107 throw std::logic_error{"period_seq: vector provided to private constructor not ordered properly"}; 126 {
108 } 127 throw std::logic_error{"period_seq: vector provided to private "
128 "constructor not ordered properly"};
109 } 129 }
110 } 130 }
111 } 131 }
132 }
112 133
113 public: 134public:
114 template<std::input_iterator I, std::sentinel_for<I> S> 135 template <std::input_iterator I, std::sentinel_for<I> S>
115 requires std::same_as<std::iter_value_t<I>, period> 136 requires std::same_as<std::iter_value_t<I>, period>
116 explicit period_seq(I begin, S end) 137 explicit period_seq(I begin, S end) : periods_{consolidate(begin, end)}
117 : periods_{consolidate(begin, end)} 138 {
118 {} 139 }
119 140
120 explicit period_seq(period singleton) 141 explicit period_seq(period singleton) : periods_{singleton} {}
121 : periods_{singleton}
122 {}
123 142
124 [[nodiscard]] auto intersect(period_seq const& other) const -> period_seq { 143 [[nodiscard]] auto intersect(period_seq const& other) const -> period_seq
125 auto it1 = periods_.begin(); auto end1 = periods_.end(); 144 {
126 auto it2 = other.periods_.begin(); auto end2 = other.periods_.end(); 145 auto it1 = periods_.begin();
146 auto end1 = periods_.end();
147 auto it2 = other.periods_.begin();
148 auto end2 = other.periods_.end();
127 149
128 auto res = std::vector<period>{}; 150 auto res = std::vector<period>{};
129 while (it1 != end1 && it2 != end2) { 151 while (it1 != end1 && it2 != end2)
130 auto overlap = it1->intersect(*it2); 152 {
131 if (overlap) { 153 auto overlap = it1->intersect(*it2);
132 res.push_back(*overlap); 154 if (overlap)
133 if (it1->end() < it2->end()) { 155 {
134 it1++; 156 res.push_back(*overlap);
135 } else { 157 if (it1->end() < it2->end())
136 it2++; 158 {
137 } 159 it1++;
138 } else { 160 }
139 if (it1->end() < it2->start()) { 161 else
140 it1++; 162 {
141 } else { 163 it2++;
142 it2++; 164 }
143 } 165 }
166 else
167 {
168 if (it1->end() < it2->start())
169 {
170 it1++;
171 }
172 else
173 {
174 it2++;
144 } 175 }
145 } 176 }
146
147 return period_seq{res};
148 } 177 }
149 178
150 [[nodiscard]] auto except(period_seq const& other) const -> period_seq { 179 return period_seq{res};
151 // This code was pretty tricky to write, I wouldn't be surprised if it has some bugs in it. 180 }
181
182 [[nodiscard]] auto except(period_seq const& other) const -> period_seq
183 {
184 // This code was pretty tricky to write, I wouldn't be surprised if it
185 // has some bugs in it.
152 186
153 auto it1 = periods_.begin(); auto end1 = periods_.end(); 187 auto it1 = periods_.begin();
154 auto it2 = other.periods_.begin(); auto end2 = other.periods_.end(); 188 auto end1 = periods_.end();
189 auto it2 = other.periods_.begin();
190 auto end2 = other.periods_.end();
155 191
156 auto res = std::vector<period>{}; 192 auto res = std::vector<period>{};
157 if (it1 == end1) 193 if (it1 == end1)
158 return period_seq{res}; 194 return period_seq{res};
159 if (it2 == end2) 195 if (it2 == end2)
160 return period_seq{periods_}; 196 return period_seq{periods_};
161 auto period1 = period{*it1++}; 197 auto period1 = period{*it1++};
162 198
163 while (it1 != end1 && it2 != end2) { 199 while (it1 != end1 && it2 != end2)
164 if (period1.end() <= it2->start()) { 200 {
165 res.push_back(period1); 201 if (period1.end() <= it2->start())
202 {
203 res.push_back(period1);
204 period1 = *it1++;
205 }
206 else if (it2->end() <= period1.start())
207 {
208 it2++;
209 }
210 else /* period1.begin() < it2->end() && it2->begin() <
211 period1.end() */
212 {
213 auto const [mbefore, mafter] = period1.except(*it2);
214 if (mbefore)
215 res.push_back(*mbefore);
216 if (mafter)
217 {
218 period1 = *mafter;
219 }
220 else
221 {
166 period1 = *it1++; 222 period1 = *it1++;
167 } else if (it2->end() <= period1.start()) {
168 it2++;
169 } else /* period1.begin() < it2->end() && it2->begin() < period1.end() */ {
170 auto const [mbefore, mafter] = period1.except(*it2);
171 if (mbefore)
172 res.push_back(*mbefore);
173 if (mafter) {
174 period1 = *mafter;
175 } else {
176 period1 = *it1++;
177 }
178 } 223 }
179 } 224 }
180
181 return period_seq{res};
182 } 225 }
183 226
184 [[nodiscard]] auto periods() const -> std::vector<period> const& { 227 return period_seq{res};
185 return periods_; 228 }
186 }
187 };
188 229
189 auto operator<<(std::ostream& os, period const& p) -> std::ostream& { 230 [[nodiscard]] auto periods() const -> std::vector<period> const&
190 return os << "[" << p.start() << ", " << p.end() << ")"; 231 {
232 return periods_;
191 } 233 }
234};
192 235
193 auto operator<<(std::ostream &os, period_seq const& ps) -> std::ostream& { 236auto operator<<(std::ostream& os, period const& p) -> std::ostream&
194 os << "{"; 237{
195 auto it = ps.periods().begin(); 238 return os << "[" << p.start() << ", " << p.end() << ")";
196 while (it != ps.periods().end()) { 239}
197 os << " " << *it; 240
198 if (++it != ps.periods().end()) { 241auto operator<<(std::ostream& os, period_seq const& ps) -> std::ostream&
199 os << ","; 242{
200 } 243 os << "{";
244 auto it = ps.periods().begin();
245 while (it != ps.periods().end())
246 {
247 os << " " << *it;
248 if (++it != ps.periods().end())
249 {
250 os << ",";
201 } 251 }
202 return os << " }";
203 } 252 }
253 return os << " }";
254}
204 255
205} // namespace routemon::time 256} // namespace routemon::time