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 | ||