#pragma once #include #include #include #include namespace alpaqa::util { template requires(std::ranges::view && std::ranges::view) struct set_intersection_iterable : std::ranges::view_interface< set_intersection_iterable> { R1 range1; R2 range2; Comp comp; Proj1 proj1; Proj2 proj2; // P2325R3: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2325r3.html set_intersection_iterable() = default; set_intersection_iterable(R1 &&range1, R2 &&range2, Comp &&comp, Proj1 &&proj1, Proj2 &&proj2) : range1{std::forward(range1)}, range2{std::forward(range2)}, comp{std::forward(comp)}, proj1{std::forward(proj1)}, proj2{std::forward(proj2)} {} struct sentinel_t {}; template S1, std::input_iterator I2, std::sentinel_for S2> struct iter_t { // P2325R3: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2325r3.html iter_t() = default; iter_t(I1 first1, S1 last1, I2 first2, S2 last2, Comp comp, Proj1 proj1, Proj2 proj2) : first1{std::move(first1)}, last1{std::move(last1)}, first2{std::move(first2)}, last2{std::move(last2)}, comp{std::move(comp)}, proj1{std::move(proj1)}, proj2{std::move( proj2)} {} I1 first1; S1 last1; I2 first2; S2 last2; Comp comp; Proj1 proj1; Proj2 proj2; using difference_type = std::ptrdiff_t; // TODO using value_type = std::tuple; bool operator!=(sentinel_t) const { return first1 != last1 && first2 != last2; } bool operator==(sentinel_t s) const { return !(*this != s); } // TODO: For Clang bug friend bool operator!=(sentinel_t s, const iter_t &i) { return i != s; } friend bool operator==(sentinel_t s, const iter_t &i) { return i == s; } iter_t &operator++() { ++first1, ++first2; advance(); return *this; } iter_t operator++(int) { auto tmp = *this; ++*this; return tmp; } value_type operator*() const { return {*first1, *first2}; } void advance() { while (*this != sentinel_t{}) { if (std::invoke(comp, std::invoke(proj1, *first1), std::invoke(proj2, *first2))) ++first1; else if (std::invoke(comp, std::invoke(proj2, *first2), std::invoke(proj1, *first1))) ++first2; else break; } } }; private: template iter_t iter(I1 first1, S1 last1, I2 first2, S2 last2) const { return {first1, last1, first2, last2, comp, proj1, proj2}; } public: auto begin() const -> std::input_or_output_iterator auto{ auto it = iter(std::ranges::begin(range1), std::ranges::end(range1), std::ranges::begin(range2), std::ranges::end(range2)); it.advance(); return it; } auto end() const // -> std::sentinel_for< decltype(std::declval().begin())> auto { return sentinel_t{}; } }; template set_intersection_iterable, std::ranges::views::all_t, Comp, Proj1, Proj2> iter_set_intersection(R1 &&r1, R2 &&r2, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) { static_assert( requires(set_intersection_iterable, std::ranges::views::all_t, Comp, Proj1, Proj2> s) { { s.end() } -> std::sentinel_for; }); return { std::forward(r1), std::forward(r2), std::move(comp), std::move(proj1), std::move(proj2), }; } } // namespace alpaqa::util