diff options
author | Rutger Broekhoff | 2024-05-02 20:27:40 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2024-05-02 20:27:40 +0200 |
commit | 17a3ea880402338420699e03bcb24181e4ff3924 (patch) | |
tree | da666ef91e0b60d20aa0b01529644c136fd1f4ab /src/querykv1/daterange.hpp | |
download | oeuf-17a3ea880402338420699e03bcb24181e4ff3924.tar.gz oeuf-17a3ea880402338420699e03bcb24181e4ff3924.zip |
Initial commit
Based on dc4ba6a
Diffstat (limited to 'src/querykv1/daterange.hpp')
-rw-r--r-- | src/querykv1/daterange.hpp | 118 |
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]. | ||
14 | class 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 | |||
44 | bool operator==(const DateRange::Iterator a, const DateRange::Iterator b); | ||
45 | |||
46 | template<typename Tp, typename T> | ||
47 | concept DerefsTo = requires(Tp p) { | ||
48 | { *p } -> std::convertible_to<T>; | ||
49 | }; | ||
50 | |||
51 | class 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 | ||