aboutsummaryrefslogtreecommitdiffstats
path: root/src/querykv1/daterange.hpp
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2024-05-02 20:27:40 +0200
committerLibravatar Rutger Broekhoff2024-05-02 20:27:40 +0200
commit17a3ea880402338420699e03bcb24181e4ff3924 (patch)
treeda666ef91e0b60d20aa0b01529644c136fd1f4ab /src/querykv1/daterange.hpp
downloadoeuf-17a3ea880402338420699e03bcb24181e4ff3924.tar.gz
oeuf-17a3ea880402338420699e03bcb24181e4ff3924.zip
Initial commit
Based on dc4ba6a
Diffstat (limited to 'src/querykv1/daterange.hpp')
-rw-r--r--src/querykv1/daterange.hpp118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/querykv1/daterange.hpp b/src/querykv1/daterange.hpp
new file mode 100644
index 0000000..e34c39c
--- /dev/null
+++ b/src/querykv1/daterange.hpp
@@ -0,0 +1,118 @@
1// vim:set sw=2 ts=2 sts et:
2
3#ifndef OEUF_QUERYKV1_DATERANGE_HPP
4#define OEUF_QUERYKV1_DATERANGE_HPP
5
6#include <cassert>
7#include <chrono>
8#include <concepts>
9#include <iterator>
10#include <utility>
11#include <vector>
12
13// DateRange expresses the date range [from, thru].
14class DateRange {
15 public:
16 class Iterator {
17 friend class DateRange;
18
19 public:
20 Iterator &operator++();
21
22 std::chrono::year_month_day operator*() const;
23 std::chrono::year_month_day ymd() const;
24
25 private:
26 explicit Iterator(std::chrono::year_month_day ymd);
27
28 std::chrono::year_month_day ymd_;
29 };
30
31 explicit DateRange(std::chrono::year_month_day from, std::chrono::year_month_day thru);
32
33 Iterator begin() const;
34 Iterator end() const;
35 bool valid() const;
36 std::chrono::year_month_day from() const;
37 std::chrono::year_month_day thru() const;
38
39 private:
40 std::chrono::year_month_day from_;
41 std::chrono::year_month_day thru_;
42};
43
44bool operator==(const DateRange::Iterator a, const DateRange::Iterator b);
45
46template<typename Tp, typename T>
47concept DerefsTo = requires(Tp p) {
48 { *p } -> std::convertible_to<T>;
49};
50
51class DateRangeSeq {
52 // The way LE and GE are ordered makes a difference for how the sorting
53 // (insertion based on lower_bound) works. Do not carelessly reorder this.
54 enum LeGe {
55 GE, // >=
56 LE, // <=
57 };
58
59 std::vector<DateRange> ranges_;
60
61 public:
62 template<std::input_iterator InputIt>
63 requires DerefsTo<InputIt, DateRange>
64 explicit DateRangeSeq(InputIt begin, InputIt end) {
65 // We convert every inclusive date range [x, y] into (x, >=) and (y, <=)
66 // and put these into a list, using binary search to make sure that these
67 // stay ordered. We then reduce this list, removing tautological
68 // predicates, giving us a final list of ranges that do not overlap.
69
70 std::vector<std::pair<std::chrono::year_month_day, LeGe>> preds;
71
72 size_t n = 0;
73 for (auto it = begin; it != end; it++) {
74 auto &range = *it;
75 if (!range.valid()) continue;
76
77 auto a = std::make_pair(range.from(), GE);
78 auto b = std::make_pair(range.thru(), LE);
79 preds.insert(std::lower_bound(preds.begin(), preds.end(), a), a);
80 preds.insert(std::lower_bound(preds.begin(), preds.end(), b), b);
81
82 n++;
83 }
84
85 if (preds.empty())
86 return;
87
88 assert(preds.size() >= 2);
89 assert(preds.front().second == GE);
90 assert(preds.back().second == LE);
91
92 std::chrono::year_month_day begin_ymd = preds[0].first;
93 for (size_t i = 1; i < preds.size(); i++) {
94 if (preds[i].second == LE && (i + 1 == preds.size() || preds[i + 1].second == GE)) {
95 std::chrono::year_month_day end_ymd = preds[i].first;
96 if (!ranges_.empty() && ranges_.back().thru() == begin_ymd)
97 ranges_.back() = DateRange(ranges_.back().from(), end_ymd);
98 else
99 ranges_.push_back(DateRange(begin_ymd, end_ymd));
100 if (i + 1 != preds.size()) {
101 begin_ymd = preds[i + 1].first;
102 i++;
103 }
104 }
105 }
106 }
107
108 explicit DateRangeSeq(std::initializer_list<DateRange> ranges);
109
110 DateRangeSeq clampFrom(std::chrono::year_month_day from) const;
111 DateRangeSeq clampThru(std::chrono::year_month_day thru) const;
112
113 public:
114 std::vector<DateRange>::const_iterator begin() const;
115 std::vector<DateRange>::const_iterator end() const;
116};
117
118#endif // OEUF_QUERYKV1_DATERANGE_HPP