diff --git a/.clang-format b/.clang-format index e0c2e3e7..7c884e39 100644 --- a/.clang-format +++ b/.clang-format @@ -18,5 +18,6 @@ IndentPPDirectives: AfterHash InsertBraces: true NamespaceIndentation: None PackConstructorInitializers: CurrentLine +SpaceBeforeCpp11BracedList: false SortIncludes: Never TabWidth: 4 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 0e6845e9..d0788325 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,6 +22,8 @@ jobs: test_with: Module - compiler: GCC-14 test_with: Module + - compiler: GCC-15 + test_with: Module - compiler: Clang-17 test_with: Module diff --git a/CMakeLists.txt b/CMakeLists.txt index b3073f86..fc1457ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ target_compile_options(flux-internal INTERFACE -ftemplate-backtrace-limit=0 > - $<$: -fconcepts-diagnostics-depth=2> + $<$: -fconcepts-diagnostics-depth=2 -Wno-missing-field-initializers> $<$: -fconstexpr-backtrace-limit=0> diff --git a/benchmark/multidimensional_memset_benchmark.cpp b/benchmark/multidimensional_memset_benchmark.cpp index 5174bc22..bf1f88a4 100644 --- a/benchmark/multidimensional_memset_benchmark.cpp +++ b/benchmark/multidimensional_memset_benchmark.cpp @@ -18,19 +18,21 @@ namespace an = ankerl::nanobench; // Kernels are placed in a separate translation unit to prevent compilers from // optimizing them based on the input that we'll be giving them and to make it // easier to study their compiled assembly. -extern void memset_2d_reference(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_reference(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M); +extern void memset_2d_reference(double* A, flux::int_t N, flux::int_t M); +extern void memset_2d_std_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M); +extern void memset_2d_flux_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M); +extern void memset_diagonal_2d_reference(double* A, flux::int_t N, flux::int_t M); +extern void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::int_t N, + flux::int_t M); +extern void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::int_t N, + flux::int_t M); int main(int argc, char** argv) { int const n_iters = argc > 1 ? std::atoi(argv[1]) : 40; - constexpr flux::distance_t N = 1024; - constexpr flux::distance_t M = 2048; + constexpr flux::int_t N = 1024; + constexpr flux::int_t M = 2048; std::vector A(N * M); const auto run_benchmark = diff --git a/benchmark/multidimensional_memset_benchmark_kernels.cpp b/benchmark/multidimensional_memset_benchmark_kernels.cpp index 4b3706ce..ee74ec92 100644 --- a/benchmark/multidimensional_memset_benchmark_kernels.cpp +++ b/benchmark/multidimensional_memset_benchmark_kernels.cpp @@ -5,7 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include +#include #include #include @@ -18,14 +18,16 @@ #include #include -void memset_2d_reference(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_reference(double* A, flux::int_t N, flux::int_t M) { - for (flux::distance_t i = 0; i != N; ++i) - for (flux::distance_t j = 0; j != M; ++j) + for (flux::int_t i = 0; i != N; ++i) { + for (flux::int_t j = 0; j != M; ++j) { A[i * M + j] = 0.0; + } + } } -void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_std_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M) { std::ranges::for_each( std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)), @@ -34,7 +36,7 @@ void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::d })); } -void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_flux_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M) { flux::for_each( flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)), @@ -43,14 +45,17 @@ void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux:: })); } -void memset_diagonal_2d_reference(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_reference(double* A, flux::int_t N, flux::int_t M) { - for (flux::distance_t i = 0; i != N; ++i) - for (flux::distance_t j = 0; j != M; ++j) - if (i == j) A[i * M + j] = 0.0; + for (flux::int_t i = 0; i != N; ++i) { + for (flux::int_t j = 0; j != M; ++j) { + if (i == j) + A[i * M + j] = 0.0; + } + } } -void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::int_t N, flux::int_t M) { std::ranges::for_each( std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)) @@ -60,7 +65,7 @@ void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::dista })); } -void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::int_t N, flux::int_t M) { flux::for_each( flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)) diff --git a/example/docs/count.cpp b/example/docs/count.cpp index 51d431f1..57fb3ea8 100644 --- a/example/docs/count.cpp +++ b/example/docs/count.cpp @@ -27,5 +27,5 @@ int main() std::istringstream iss("1 2 3"); auto seq = flux::from_istream(iss); assert(flux::count(seq) == 3); - assert(flux::is_last(seq, flux::first(seq))); // No more elements! + assert(!iss); // No more elements! } \ No newline at end of file diff --git a/example/docs/repeat.cpp b/example/docs/repeat.cpp index 75c9779e..a00573f6 100644 --- a/example/docs/repeat.cpp +++ b/example/docs/repeat.cpp @@ -13,21 +13,21 @@ using namespace std::string_view_literals; int main() { - // flux::repeat(val) is a random-access sequence which endlessly repeats + // flux::repeat(val) is an iterable which endlessly repeats // the given value - auto seq = flux::repeat(3); + auto rep = flux::repeat(3); - auto cursor = flux::first(seq); - assert(flux::read_at(seq, cursor) == 3); - // fast-forward the cursor a lot... - cursor = flux::next(seq, cursor, 1'000'000); - assert(flux::read_at(seq, cursor) == 3); // still returning 3! + auto ctx = flux::iterate(rep); + assert(flux::next_element(ctx).value() == 3); + assert(flux::next_element(ctx).value() == 3); + assert(flux::next_element(ctx).value() == 3); // still returning 3! // We could use the take adaptor to make a repeat sequence finite... - auto taken = flux::take(seq, 5); + auto taken = flux::take(rep, 5); assert(flux::equal(taken, std::array{3, 3, 3, 3, 3})); - // ...but it's easier to use repeat(val, count) instead + // ...but it's better to use repeat(val, count) instead, as this is random-access auto police = flux::repeat("hello"sv, 3); assert(flux::equal(police, std::array{"hello", "hello", "hello"})); + static_assert(flux::random_access_sequence); } \ No newline at end of file diff --git a/example/shortest_path.cpp b/example/shortest_path.cpp index 50e4681f..b7c21026 100644 --- a/example/shortest_path.cpp +++ b/example/shortest_path.cpp @@ -20,8 +20,8 @@ using namespace std::string_literals; -auto intersperse = [](flux::sequence auto seq, std::string sep) -> flux::generator -{ +auto intersperse + = [](flux::sequence auto seq, std::string sep) -> flux::generator { bool first = true; for (auto c: seq) { if (first) { @@ -35,9 +35,7 @@ auto intersperse = [](flux::sequence auto seq, std::string sep) -> flux::generat }; namespace color { - std::string yellow(const std::string& s) { - return "\u001b[33m"s + s + "\u001b[37m"; - } +std::string yellow(const std::string& s) { return "\u001b[33m"s + s + "\u001b[37m"; } } class maze { @@ -66,7 +64,7 @@ class maze { width_(width), height_(height), fields_(width * height, 0) - { + { assert(width > 1 && height > 1); } @@ -75,7 +73,7 @@ class maze { std::mt19937 rng(seed); std::uniform_int_distribution dist(1, 9); maze m(width, height); - + for (auto i: flux::ints(1, flux::size(m.fields_) - 1)) { m.fields_[size_t(i)] = (rng() % 4) == 0 ? wall : dist(rng); } @@ -87,13 +85,13 @@ class maze { auto to_char = [print_costs] (int num) { if (num == wall) { return "#"s; } if (num == path) { return color::yellow("*"); } - return print_costs ? std::to_string(num) : " "s; + return print_costs ? std::to_string(num) : " "s; }; auto width = width_ * 2 + 3; auto edge = flux::single("|"s); auto h_edge = "+"s + std::string(width - 2, '-') + "+\n"s; auto out = std::ostream_iterator(s); - + s << h_edge; for (auto&& row: flux::ref(fields_).map(to_char).chunk(width_)) { intersperse(flux::chain(flux::ref(edge), FLUX_FWD(row), flux::ref(edge)), " "s).output_to(out); @@ -108,15 +106,13 @@ class maze { std::vector> costs(fields_.size()); std::vector> prevs(fields_.size()); - auto valid = [this](std::size_t u) { - return fields_[u] != wall; - }; + auto valid = [this](std::size_t u) { return fields_[u] != wall; }; auto to_adjacent_edges = [this](std::size_t u) { auto to_edge = [](auto e) { return edge_t{std::get<0>(e), std::get<1>(e)}; }; return flux::cartesian_product(flux::single(u), adjacent(u)).map(to_edge) ; }; - + bool updated = false; auto update_costs_and_prevs = [this, &costs, &prevs, &updated](edge_t e) { constexpr auto max = std::numeric_limits::max(); @@ -131,7 +127,7 @@ class maze { do { updated = false; flux::iota(size_t{0}, fields_.size()) - .filter(valid) + .filter(valid) .map(to_adjacent_edges) .flatten() .for_each(update_costs_and_prevs); @@ -143,8 +139,10 @@ class maze { } }; -void print_side_by_side(std::istream& s1, std::istream& s2) { - for (auto [line1, line2]: flux::zip(flux::getlines(s1, '\n'), flux::getlines(s2, '\n'))) { +void print_side_by_side(std::istream& s1, std::istream& s2) +{ + // FIXME: Remove as_range when we can + for (auto [line1, line2] : flux::zip(flux::getlines(s1, '\n'), flux::getlines(s2, '\n'))) { std::cout << line1 << " " << line2 << '\n'; } } diff --git a/example/top10/01_trapping_rain_water.cpp b/example/top10/01_trapping_rain_water.cpp index 270a1399..b375b819 100644 --- a/example/top10/01_trapping_rain_water.cpp +++ b/example/top10/01_trapping_rain_water.cpp @@ -15,8 +15,7 @@ #include -auto const rain_water = [](std::initializer_list heights) -{ +auto const rain_water = [](std::array heights) { // Find the index of the maximum height flux::cursor auto max_idx = flux::find_max(heights); @@ -37,7 +36,7 @@ auto const rain_water = [](std::initializer_list heights) return trapped(left) + trapped(flux::reverse(right)); }; -static_assert(rain_water({0,1,0,2,1,0,1,3,2,1,2,1}) == 6); -static_assert(rain_water({4,2,0,3,2,5}) == 9); +static_assert(rain_water(std::array{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}) == 6); +static_assert(rain_water(std::array{4, 2, 0, 3, 2, 5}) == 9); int main() {} \ No newline at end of file diff --git a/example/top10/08_three_consecutive_odds.cpp b/example/top10/08_three_consecutive_odds.cpp index 05d46d9e..dbaaf0f9 100644 --- a/example/top10/08_three_consecutive_odds.cpp +++ b/example/top10/08_three_consecutive_odds.cpp @@ -17,7 +17,7 @@ namespace version1 { auto const tco = [](std::initializer_list nums) { int odd_count = 0; - auto idx = flux::for_each_while(nums, [&](int i) { + auto idx = flux::seq_for_each_while(nums, [&](int i) { if (i % 2 != 0) { ++odd_count; } else { diff --git a/example/top10/09_skyline.cpp b/example/top10/09_skyline.cpp index 20a1059f..a32fe881 100644 --- a/example/top10/09_skyline.cpp +++ b/example/top10/09_skyline.cpp @@ -14,14 +14,13 @@ #include -auto const skyline = [](std::initializer_list heights) -{ +auto const skyline = [](auto heights) { auto h = flux::ref(heights); return 1 + flux::zip(flux::drop(h, 1), flux::scan(h, flux::cmp::max)) .count_if([](auto p) { return p.first > p.second; }); }; -static_assert(skyline({5, 5, 2, 10, 3, 15, 10}) == 3); +static_assert(skyline(std::array{5, 5, 2, 10, 3, 15, 10}) == 3); int main() {} \ No newline at end of file diff --git a/example/word_count.cpp b/example/word_count.cpp index 26a8754d..b9c006f0 100644 --- a/example/word_count.cpp +++ b/example/word_count.cpp @@ -17,11 +17,9 @@ struct stats_t bool is_last_space = true; }; -auto collect_stats = [](flux::sequence auto&& seq) -{ +auto collect_stats = [](flux::iterable auto&& seq) { stats_t stats; - flux::for_each(FLUX_FWD(seq), [&stats](char val) - { + flux::for_each(FLUX_FWD(seq), [&stats](char val) { stats.chars++; if (not stats.is_last_space and std::isspace(val)) { stats.words++; diff --git a/include/flux.hpp b/include/flux.hpp index a2b348d5..8db9205f 100644 --- a/include/flux.hpp +++ b/include/flux.hpp @@ -9,6 +9,6 @@ #include #include #include -#include +#include #endif diff --git a/include/flux/adaptor/adjacent.hpp b/include/flux/adaptor/adjacent.hpp index 5c5223da..5191fe19 100644 --- a/include/flux/adaptor/adjacent.hpp +++ b/include/flux/adaptor/adjacent.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include @@ -19,7 +19,7 @@ namespace flux { namespace detail { -template +template struct adjacent_sequence_traits_base : default_sequence_traits { protected: struct cursor_type { @@ -91,7 +91,7 @@ struct adjacent_sequence_traits_base : default_sequence_traits { }, cur.arr); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { std::apply([&self, offset](auto&... curs) { @@ -100,21 +100,21 @@ struct adjacent_sequence_traits_base : default_sequence_traits { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return flux::distance(self.base_, from.arr.back(), to.arr.back()); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = (flux::size(self.base_) - N) + 1; - return (cmp::max)(s, distance_t{0}); + return (cmp::max)(s, int_t {0}); } }; -template +template struct adjacent_adaptor : inline_sequence_base> { private: Base base_; @@ -175,7 +175,7 @@ struct adjacent_adaptor : inline_sequence_base> { }; }; -template +template struct adjacent_map_adaptor : inline_sequence_base> { private: Base base_; @@ -202,7 +202,7 @@ struct adjacent_map_adaptor : inline_sequence_base +template struct adjacent_fn { template requires multipass_sequence @@ -213,7 +213,7 @@ struct adjacent_fn { } }; -template +template struct adjacent_map_fn { template requires multipass_sequence && @@ -229,21 +229,21 @@ struct adjacent_map_fn { } // namespace detail FLUX_EXPORT -template - requires (N > 0) -inline constexpr auto adjacent = detail::adjacent_fn{}; +template + requires(N > 0) +inline constexpr auto adjacent = detail::adjacent_fn {}; FLUX_EXPORT inline constexpr auto pairwise = adjacent<2>; FLUX_EXPORT -template - requires (N > 0) -inline constexpr auto adjacent_map = detail::adjacent_map_fn{}; +template + requires(N > 0) +inline constexpr auto adjacent_map = detail::adjacent_map_fn {}; FLUX_EXPORT inline constexpr auto pairwise_map = adjacent_map<2>; template -template +template constexpr auto inline_sequence_base::adjacent() && requires multipass_sequence { @@ -258,7 +258,7 @@ constexpr auto inline_sequence_base::pairwise() && } template -template +template requires multipass_sequence constexpr auto inline_sequence_base::adjacent_map(Func func) && { diff --git a/include/flux/adaptor/adjacent_filter.hpp b/include/flux/adaptor/adjacent_filter.hpp index bed86d14..fe8f7813 100644 --- a/include/flux/adaptor/adjacent_filter.hpp +++ b/include/flux/adaptor/adjacent_filter.hpp @@ -19,12 +19,58 @@ struct adjacent_filter_adaptor FLUX_NO_UNIQUE_ADDRESS Base base_; FLUX_NO_UNIQUE_ADDRESS Pred pred_; + template + struct context_type : immovable { + BaseCtx base_ctx; + FilterPred filter_pred; + using opt_t = decltype(next_element(std::declval())); + opt_t opt = nullopt; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (!opt) { + opt = next_element(base_ctx); + if (!opt) { + return iteration_result::complete; + } + if (!pred(opt.value_unchecked())) { + return iteration_result::incomplete; + } + } + + return base_ctx.run_while([&](auto&& elem) { + if (!filter_pred(opt.value_unchecked(), elem)) { + return loop_continue; + } else { + opt.emplace(elem); + return pred(elem); + } + }); + } + }; + public: constexpr adjacent_filter_adaptor(decays_to auto&& base, Pred pred) : base_(FLUX_FWD(base)), pred_(std::move(pred)) {} + constexpr auto iterate() + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .filter_pred = copy_or_ref(pred_)}; + } + + constexpr auto iterate() const + requires multipass_sequence + && std::predicate> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .filter_pred = copy_or_ref(pred_)}; + } + struct flux_sequence_traits : default_sequence_traits { private: struct cursor_type { @@ -143,8 +189,8 @@ FLUX_EXPORT inline constexpr auto dedup = detail::dedup_fn{}; template template - requires multipass_sequence && - std::predicate, element_t> + requires multipass_sequence + && std::predicate, iterable_element_t> constexpr auto inline_sequence_base::adjacent_filter(Pred pred) && { return flux::adjacent_filter(std::move(derived()), std::move(pred)); @@ -152,8 +198,7 @@ constexpr auto inline_sequence_base::adjacent_filter(Pred pred) && template constexpr auto inline_sequence_base::dedup() && - requires multipass_sequence && - std::equality_comparable> + requires multipass_sequence && std::equality_comparable> { return flux::dedup(std::move(derived())); } diff --git a/include/flux/adaptor/cartesian_base.hpp b/include/flux/adaptor/cartesian_base.hpp index c61e4a42..3791fa14 100644 --- a/include/flux/adaptor/cartesian_base.hpp +++ b/include/flux/adaptor/cartesian_base.hpp @@ -19,7 +19,7 @@ inline constexpr auto checked_pow = { T res{1}; for(U i{0}; i < exponent; i++) { - res = num::mul(res, base, loc); + res = num::checked_mul(res, base, loc); } return res; }; @@ -36,7 +36,7 @@ struct tuple_repeated { using repeater = T; template - static auto make_tuple(std::index_sequence) -> std::tuple...>; + static auto make_tuple(std::index_sequence) -> std::tuple...>; using type = decltype(make_tuple(std::make_index_sequence{})); }; @@ -109,8 +109,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto ra_inc_impl(Self& self, cursor_t& cur, distance_t offset) - -> cursor_t& + static constexpr auto ra_inc_impl(Self& self, cursor_t& cur, int_t offset) + -> cursor_t& { if (offset == 0) { return cur; @@ -129,7 +129,7 @@ struct cartesian_traits_base_impl : default_sequence_traits { // Correct for negative index which may happen when underflowing. if (new_index < 0) { new_index = num::add(new_index, this_size); - offset = num::sub(offset, flux::distance_t(1)); + offset = num::sub(offset, flux::int_t(1)); } // Call the next level down if necessary. @@ -146,9 +146,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto distance_impl(Self& self, - cursor_t const& from, - cursor_t const& to) -> distance_t + static constexpr auto distance_impl(Self& self, cursor_t const& from, + cursor_t const& to) -> int_t { if constexpr (I == 0) { return flux::distance(get_base<0>(self), std::get<0>(from), std::get<0>(to)); @@ -187,20 +186,17 @@ struct cartesian_traits_base_impl : default_sequence_traits { { // We need to iterate right to left. if constexpr (I == Arity - 1) { - std::get(cur) = flux::for_each_while(get_base(self), - [&](auto&& elem) { - keep_going = std::invoke(func, - element_t(FLUX_FWD(partial_elements)..., FLUX_FWD(elem))); - return keep_going; - }); + std::get(cur) = flux::seq_for_each_while(get_base(self), [&](auto&& elem) { + keep_going = std::invoke( + func, element_t(FLUX_FWD(partial_elements)..., FLUX_FWD(elem))); + return keep_going; + }); } else { - std::get(cur) = flux::for_each_while(get_base(self), - [&](auto&& elem) { - for_each_while_impl( - self, keep_going, cur, - func, FLUX_FWD(partial_elements)..., FLUX_FWD(elem)); - return keep_going; - }); + std::get(cur) = flux::seq_for_each_while(get_base(self), [&](auto&& elem) { + for_each_while_impl(self, keep_going, cur, func, + FLUX_FWD(partial_elements)..., FLUX_FWD(elem)); + return keep_going; + }); } } @@ -231,21 +227,21 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto size(Self& self) -> distance_t - requires (CartesianKind == cartesian_kind::product - && (sized_sequence && ...)) + static constexpr auto size(Self& self) -> int_t + requires(CartesianKind == cartesian_kind::product && (sized_sequence && ...)) { - return std::apply([](auto& base0, auto&... bases) { - distance_t sz = flux::size(base0); - ((sz = num::mul(sz, flux::size(bases))), ...); - return sz; - }, self.bases_); + return std::apply( + [](auto& base0, auto&... bases) { + int_t sz = flux::size(base0); + ((sz = num::mul(sz, flux::size(bases))), ...); + return sz; + }, + self.bases_); } template - static constexpr auto size(Self& self) -> distance_t - requires (CartesianKind == cartesian_kind::power - && (sized_sequence && ...)) + static constexpr auto size(Self& self) -> int_t + requires(CartesianKind == cartesian_kind::power && (sized_sequence && ...)) { return checked_pow(flux::size(self.base_), Arity); } @@ -267,9 +263,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto inc(Self& self, cursor_t& cur, distance_t offset) -> cursor_t& - requires ((random_access_sequence && ...) && - (sized_sequence && ...)) + static constexpr auto inc(Self& self, cursor_t& cur, int_t offset) -> cursor_t& + requires((random_access_sequence && ...) && (sized_sequence && ...)) { return ra_inc_impl(self, cur, offset); } @@ -301,11 +296,9 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto distance(Self& self, - cursor_t const& from, - cursor_t const& to) -> distance_t - requires ((random_access_sequence && ...) && - (sized_sequence && ...)) + static constexpr auto distance(Self& self, cursor_t const& from, cursor_t const& to) + -> int_t + requires((random_access_sequence && ...) && (sized_sequence && ...)) { return distance_impl(self, from, to); } diff --git a/include/flux/adaptor/cartesian_power.hpp b/include/flux/adaptor/cartesian_power.hpp index 5dd2f5c5..419c4f01 100644 --- a/include/flux/adaptor/cartesian_power.hpp +++ b/include/flux/adaptor/cartesian_power.hpp @@ -61,9 +61,9 @@ struct cartesian_power_fn { } // end namespace detail FLUX_EXPORT -template - requires (N >= 0) -inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; +template + requires(N >= 0) +inline constexpr auto cartesian_power = detail::cartesian_power_fn {}; } // end namespace flux diff --git a/include/flux/adaptor/cartesian_power_map.hpp b/include/flux/adaptor/cartesian_power_map.hpp index 41737c47..1eaffa59 100644 --- a/include/flux/adaptor/cartesian_power_map.hpp +++ b/include/flux/adaptor/cartesian_power_map.hpp @@ -55,9 +55,9 @@ struct cartesian_power_map_fn } // namespace detail FLUX_EXPORT -template - requires (N >= 0) -inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn{}; +template + requires(N >= 0) +inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn {}; } // namespace flux diff --git a/include/flux/adaptor/chain.hpp b/include/flux/adaptor/chain.hpp index 9051dd76..11f0b1ad 100644 --- a/include/flux/adaptor/chain.hpp +++ b/include/flux/adaptor/chain.hpp @@ -15,17 +15,94 @@ namespace flux { namespace detail { -template +template struct chain_adaptor : inline_sequence_base> { private: std::tuple bases_; friend struct sequence_traits; + template + struct iteration_context { + Parent* parent; + std::variant base_context; + + using element_type = std::common_reference_t; + + template + constexpr auto run_while_impl(auto&& pred) -> iteration_result + { + if constexpr (I < sizeof...(Bases) - 1) { + if (base_context.index() == I) { + auto& ctx = std::get(base_context); + auto result = flux::run_while(ctx, pred); + if (result == iteration_result::incomplete) { + return result; + } else { + base_context.template emplace(emplace_from{ + [&] { return flux::iterate(std::get(parent->bases_)); }}); + return run_while_impl(pred); + } + } else { + return run_while_impl(pred); + } + } else { + if (base_context.index() == I) { + return std::get(base_context).run_while(pred); + } else { + unreachable(); + } + } + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + auto call_with_common_ref + = [&pred](auto&& elem) { return pred(static_cast(FLUX_FWD(elem))); }; + return run_while_impl<0>(call_with_common_ref); + } + }; + public: explicit constexpr chain_adaptor(decays_to auto&&... bases) : bases_(FLUX_FWD(bases)...) - {} + { + } + + [[nodiscard]] + constexpr auto iterate() + { + return iteration_context...>{ + .parent = this, + .base_context = std::variant...>( + std::in_place_index<0>, + emplace_from([&] { return flux::iterate(std::get<0>(bases_)); }))}; + } + + [[nodiscard]] + constexpr auto iterate() const + requires(iterable && ...) + { + return iteration_context...>{ + .parent = this, + .base_context = std::variant...>( + std::in_place_index<0>, + emplace_from([&] { return flux::iterate(std::get<0>(bases_)); }))}; + } + + [[nodiscard]] constexpr auto size() -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return (flux::iterable_size(bases) + ...); }, + bases_); + } + + [[nodiscard]] constexpr auto size() const -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return (flux::iterable_size(bases) + ...); }, + bases_); + } }; template @@ -34,29 +111,26 @@ concept all_have_common_ref = (std::convertible_to> && ...); template -concept chainable = - all_have_common_ref...> && - all_have_common_ref...> && - requires { typename std::common_type_t...>; }; +concept chainable = all_have_common_ref...> + && requires { typename std::common_type_t...>; }; struct chain_fn { - template - requires (sizeof...(Seqs) >= 1) && - chainable + template + requires(sizeof...(Its) >= 1) && chainable [[nodiscard]] - constexpr auto operator()(Seqs&&... seqs) const + constexpr auto operator()(Its&&... its) const { - if constexpr (sizeof...(Seqs) == 1) { - return std::forward(seqs...); + if constexpr (sizeof...(Its) == 1) { + return std::forward(its...); } else { - return chain_adaptor...>(FLUX_FWD(seqs)...); + return chain_adaptor...>(FLUX_FWD(its)...); } } }; } // namespace detail -template +template struct sequence_traits> : default_sequence_traits { using value_type = std::common_type_t...>; @@ -168,7 +242,7 @@ struct sequence_traits> : default_sequence_trait { if constexpr (N < End) { auto& base = std::get(self.bases_); - auto base_cur = flux::for_each_while(base, pred); + auto base_cur = flux::seq_for_each_while(base, pred); if (!flux::is_last(base, base_cur)) { return cursor_type(std::in_place_index, std::move(base_cur)); } else { @@ -176,7 +250,7 @@ struct sequence_traits> : default_sequence_trait } } else { return cursor_type(std::in_place_index, - flux::for_each_while(std::get(self.bases_), pred)); + flux::seq_for_each_while(std::get(self.bases_), pred)); } } @@ -208,9 +282,7 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto inc_ra_impl(Self& self, cursor_type& cur, - distance_t offset) - -> cursor_type& + static constexpr auto inc_ra_impl(Self& self, cursor_type& cur, int_t offset) -> cursor_type& { if constexpr (N < End) { if (N < cur.index()) { @@ -300,11 +372,10 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto distance(Self& self, cursor_type const& from, - cursor_type const& to) - -> distance_t - requires (random_access_sequence> && ...) && - (bounded_sequence> && ...) + static constexpr auto distance(Self& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires(random_access_sequence> && ...) + && (bounded_sequence> && ...) { if (from.index() <= to.index()) { return distance_impl<0>(self, from, to); @@ -314,13 +385,12 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto inc(Self& self, cursor_type& cur, distance_t offset) - requires (random_access_sequence> && ...) && - (bounded_sequence> && ...) + static constexpr auto inc(Self& self, cursor_type& cur, int_t offset) + requires(random_access_sequence> && ...) + && (bounded_sequence> && ...) { inc_ra_impl<0>(self, cur, offset); } - }; FLUX_EXPORT inline constexpr auto chain = detail::chain_fn{}; diff --git a/include/flux/adaptor/chunk.hpp b/include/flux/adaptor/chunk.hpp index e215e91b..4e135832 100644 --- a/include/flux/adaptor/chunk.hpp +++ b/include/flux/adaptor/chunk.hpp @@ -15,16 +15,136 @@ namespace flux { namespace detail { +template +struct chunk_iteration_context : immovable { + BaseCtx base_ctx; + + using OptElem = decltype(next_element(base_ctx)); + + int_t chunk_sz; + OptElem elem = nullopt; + int_t remaining = 0; + bool base_done = false; + + struct inner_iterable { + chunk_iteration_context* outer_ctx; + + struct inner_context_type : immovable { + chunk_iteration_context* outer_ctx; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (true) { + if (outer_ctx->remaining == 0) { + return iteration_result::complete; + } + + if (!outer_ctx->elem) { + if (!outer_ctx->read_next()) { + return iteration_result::complete; + } + } + + --outer_ctx->remaining; + + auto r = pred(*std::move(outer_ctx->elem)); + outer_ctx->elem.reset(); + if (!r) { + return iteration_result::incomplete; + } + } + } + }; + + constexpr auto iterate() const -> inner_context_type + { + return inner_context_type{.outer_ctx = this->outer_ctx}; + } + }; + + constexpr bool read_next() + { + elem = flux::next_element(base_ctx); + return elem.has_value(); + } + + using element_type = inner_iterable; + + constexpr auto run_while(auto&& outer_pred) -> iteration_result + { + while (true) { + while (remaining-- > 0) { + if (!read_next()) { + return iteration_result::complete; + } + } + + if (!read_next()) { + return iteration_result::complete; + } + + remaining = chunk_sz; + + if (!outer_pred(inner_iterable{.outer_ctx = this})) { + return iteration_result::incomplete; + } + } + } +}; + template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; + +public: + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) + : base_(FLUX_FWD(base)), + chunk_sz_(chunk_sz) + { + } + + constexpr auto iterate() + { + return detail::chunk_iteration_context>{ + .base_ctx = flux::iterate(base_), .chunk_sz = chunk_sz_}; + } + + constexpr auto iterate() const + requires iterable + { + return detail::chunk_iteration_context>{ + .base_ctx = flux::iterate(base_), .chunk_sz = chunk_sz_}; + } + + constexpr auto size() -> int_t + requires sized_iterable + { + auto sz = flux::iterable_size(base_); + return sz / chunk_sz_ + (sz % chunk_sz_ == 0 ? 0 : 1); + } + + constexpr auto size() const -> int_t + requires sized_iterable + { + auto sz = flux::iterable_size(base_); + return sz / chunk_sz_ + (sz % chunk_sz_ == 0 ? 0 : 1); + } +}; + +template +struct chunk_adaptor : inline_sequence_base> { +private: + Base base_; + int_t chunk_sz_; optional> cur_ = nullopt; - distance_t rem_ = chunk_sz_; + int_t rem_ = chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -47,18 +167,18 @@ struct chunk_adaptor : inline_sequence_base> { using self_t = chunk_adaptor; public: - struct value_type : inline_sequence_base { + struct inner_sequence : inline_sequence_base { private: chunk_adaptor* parent_; - constexpr explicit value_type(chunk_adaptor& parent) + constexpr explicit inner_sequence(chunk_adaptor& parent) : parent_(std::addressof(parent)) {} friend struct self_t::flux_sequence_traits; public: - value_type(value_type&&) = default; - value_type& operator=(value_type&&) = default; + inner_sequence(inner_sequence&&) = default; + inner_sequence& operator=(inner_sequence&&) = default; struct flux_sequence_traits : default_sequence_traits { private: @@ -66,24 +186,24 @@ struct chunk_adaptor : inline_sequence_base> { inner_cursor(inner_cursor&&) = default; inner_cursor& operator=(inner_cursor&&) = default; - friend struct value_type::flux_sequence_traits; + friend struct inner_sequence::flux_sequence_traits; private: explicit inner_cursor() = default; }; public: - static constexpr auto first(value_type&) -> inner_cursor + static constexpr auto first(inner_sequence&) -> inner_cursor { return inner_cursor{}; } - static constexpr auto is_last(value_type& self, inner_cursor const&) -> bool + static constexpr auto is_last(inner_sequence& self, inner_cursor const&) -> bool { return self.parent_->rem_ == 0; } - static constexpr auto inc(value_type& self, inner_cursor&) -> void + static constexpr auto inc(inner_sequence& self, inner_cursor&) -> void { flux::inc(self.parent_->base_, *self.parent_->cur_); if (flux::is_last(self.parent_->base_, *self.parent_->cur_)) { @@ -93,7 +213,7 @@ struct chunk_adaptor : inline_sequence_base> { } } - static constexpr auto read_at(value_type& self, inner_cursor const&) + static constexpr auto read_at(inner_sequence& self, inner_cursor const&) -> element_t { return flux::read_at(self.parent_->base_, *self.parent_->cur_); @@ -119,12 +239,12 @@ struct chunk_adaptor : inline_sequence_base> { self.rem_ = self.chunk_sz_; } - static constexpr auto read_at(self_t& self, outer_cursor const&) -> value_type + static constexpr auto read_at(self_t& self, outer_cursor const&) -> inner_sequence { - return value_type(self); + return inner_sequence(self); } - static constexpr auto size(self_t& self) -> distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -137,10 +257,10 @@ template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -176,7 +296,7 @@ struct chunk_adaptor : inline_sequence_base> { return flux::last(self.base_); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -189,10 +309,10 @@ template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -201,7 +321,7 @@ struct chunk_adaptor : inline_sequence_base> { private: struct cursor_type { cursor_t cur{}; - distance_t missing = 0; + int_t missing = 0; friend constexpr auto operator==(cursor_type const& lhs, cursor_type const& rhs) -> bool { @@ -259,15 +379,15 @@ struct chunk_adaptor : inline_sequence_base> { static constexpr auto last(auto& self) -> cursor_type requires bounded_sequence && sized_sequence { - distance_t missing = - (self.chunk_sz_ - flux::size(self.base_) % self.chunk_sz_) % self.chunk_sz_; + int_t missing + = (self.chunk_sz_ - flux::size(self.base_) % self.chunk_sz_) % self.chunk_sz_; return cursor_type{ .cur = flux::last(self.base_), .missing = missing }; } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -275,13 +395,13 @@ struct chunk_adaptor : inline_sequence_base> { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return (flux::distance(self.base_, from.cur, to.cur) - from.missing + to.missing)/self.chunk_sz_; } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { if (offset > 0) { @@ -295,14 +415,13 @@ struct chunk_adaptor : inline_sequence_base> { }; struct chunk_fn { - template + template [[nodiscard]] - constexpr auto operator()(Seq&& seq, num::integral auto chunk_sz) const - -> sequence auto + constexpr auto operator()(It&& it, num::integral auto chunk_sz) const //-> iterable auto { - FLUX_ASSERT(chunk_sz > 0); - return chunk_adaptor>(FLUX_FWD(seq), - num::checked_cast(chunk_sz)); + int_t chunk_sz_ = num::checked_cast(chunk_sz); + FLUX_ASSERT(chunk_sz_ > 0); + return chunk_adaptor>(FLUX_FWD(it), chunk_sz_); } }; diff --git a/include/flux/adaptor/chunk_by.hpp b/include/flux/adaptor/chunk_by.hpp index c3e5e31d..73e62932 100644 --- a/include/flux/adaptor/chunk_by.hpp +++ b/include/flux/adaptor/chunk_by.hpp @@ -136,8 +136,8 @@ FLUX_EXPORT inline constexpr auto chunk_by = detail::chunk_by_fn{}; template template - requires multipass_sequence && - std::predicate, element_t> + requires multipass_sequence + && std::predicate, iterable_element_t> constexpr auto inline_sequence_base::chunk_by(Pred pred) && { return flux::chunk_by(std::move(derived()), std::move(pred)); diff --git a/include/flux/adaptor/cursors.hpp b/include/flux/adaptor/cursors.hpp index d77e1966..68f794fe 100644 --- a/include/flux/adaptor/cursors.hpp +++ b/include/flux/adaptor/cursors.hpp @@ -53,14 +53,14 @@ struct cursors_adaptor : inline_sequence_base> { flux::dec(self.base_, cur); } - static constexpr auto inc(auto& self, cursor_t& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_t& cur, int_t offset) -> void requires random_access_sequence { flux::inc(self.base_, cur, offset); } static constexpr auto distance(auto& self, cursor_t const& from, - cursor_t const& to) -> distance_t + cursor_t const& to) -> int_t requires random_access_sequence { return flux::distance(self.base_, from, to); @@ -72,7 +72,7 @@ struct cursors_adaptor : inline_sequence_base> { return flux::last(self.base_); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { return flux::size(self.base_); diff --git a/include/flux/adaptor/cycle.hpp b/include/flux/adaptor/cycle.hpp index a0e98d71..bd9482a3 100644 --- a/include/flux/adaptor/cycle.hpp +++ b/include/flux/adaptor/cycle.hpp @@ -128,7 +128,7 @@ struct cycle_adaptor : inline_sequence_base> { if constexpr (IsInfinite) { std::size_t n = 0; while (true) { - auto cur = flux::for_each_while(self.base_, constify_pred); + auto cur = flux::seq_for_each_while(self.base_, constify_pred); if (!flux::is_last(self.base_, cur)) { return cursor_type{std::move(cur), n}; } @@ -136,7 +136,7 @@ struct cycle_adaptor : inline_sequence_base> { } } else { for (std::size_t n = 0; n < self.data_.count; ++n) { - auto cur = flux::for_each_while(self.base_, constify_pred); + auto cur = flux::seq_for_each_while(self.base_, constify_pred); if (!flux::is_last(self.base_, cur)) { return cursor_type{std::move(cur), n}; } @@ -156,9 +156,9 @@ struct cycle_adaptor : inline_sequence_base> { flux::dec(self.base_, cur.base_cur); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) - requires random_access_sequence && - bounded_sequence + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) + requires random_access_sequence + && bounded_sequence { auto const first = flux::first(self.base_); @@ -180,13 +180,12 @@ struct cycle_adaptor : inline_sequence_base> { cur.base_cur = flux::next(self.base_, first, off); } - static constexpr auto distance(auto& self, - cursor_type const& from, - cursor_type const& to) -> distance_t - requires random_access_sequence && - sized_sequence + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires random_access_sequence + && sized_sequence { - auto dist = num::cast(to.n) - num::cast(from.n); + auto dist = num::cast(to.n) - num::cast(from.n); dist = num::mul(dist, flux::size(self.base_)); return num::add(dist, flux::distance(self.base_, from.base_cur, to.base_cur)); @@ -200,11 +199,10 @@ struct cycle_adaptor : inline_sequence_base> { .n = self.data_.count}; } - static constexpr auto size(auto& self) -> distance_t - requires (!IsInfinite && sized_sequence) + static constexpr auto size(auto& self) -> int_t + requires(!IsInfinite && sized_sequence) { - return num::mul(flux::size(self.base_), - num::cast(self.data_.count)); + return num::mul(flux::size(self.base_), num::cast(self.data_.count)); } }; }; @@ -228,7 +226,7 @@ struct cycle_fn { constexpr auto operator()(Seq&& seq, num::integral auto count) const -> multipass_sequence auto { - auto c = num::checked_cast(count); + auto c = num::checked_cast(count); if (c < 0) { runtime_error("Negative count passed to cycle()"); } diff --git a/include/flux/adaptor/drop.hpp b/include/flux/adaptor/drop.hpp index 52173b34..ca924f09 100644 --- a/include/flux/adaptor/drop.hpp +++ b/include/flux/adaptor/drop.hpp @@ -13,21 +13,103 @@ namespace flux { namespace detail { -template +template +struct drop_iteration_context : immovable { + BaseCtx base_ctx; + int_t remaining; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return base_ctx.run_while([&](auto&& elem) { + if (remaining > 0) { + --remaining; + return loop_continue; + } else { + return pred(FLUX_FWD(elem)); + } + }); + } +}; + +template +struct take_iteration_context : immovable { + BaseCtx base_ctx; + int_t remaining; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (remaining > 0) { + return base_ctx.run_while([&](auto&& elem) { + --remaining; + return pred(FLUX_FWD(elem)) && (remaining > 0); + }); + } else { + return iteration_result::complete; + } + } +}; + +template struct drop_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; - distance_t count_; + int_t count_; public: - constexpr drop_adaptor(decays_to auto&& base, distance_t count) + constexpr drop_adaptor(decays_to auto&& base, int_t count) : base_(FLUX_FWD(base)), count_(count) - {} + { + } constexpr Base& base() & { return base_; } constexpr Base const& base() const& { return base_; } + [[nodiscard]] constexpr auto iterate() + { + return drop_iteration_context>{.base_ctx = flux::iterate(base_), + .remaining = count_}; + } + + [[nodiscard]] constexpr auto iterate() const + requires iterable + { + return drop_iteration_context>{ + .base_ctx = flux::iterate(base_), .remaining = count_}; + } + + [[nodiscard]] constexpr auto reverse_iterate() + requires reverse_iterable && sized_iterable + { + return take_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .remaining = size()}; + } + + [[nodiscard]] constexpr auto reverse_iterate() const + requires reverse_iterable && sized_iterable + { + return take_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .remaining = size()}; + } + + [[nodiscard]] constexpr auto size() -> int_t + requires sized_iterable + { + auto sz = flux::iterable_size(base_); + return sz > count_ ? sz - count_ : 0; + } + + [[nodiscard]] constexpr auto size() const -> int_t + requires sized_iterable + { + auto sz = flux::iterable_size(base_); + return sz > count_ ? sz - count_ : 0; + } + struct flux_sequence_traits : passthrough_traits_base { using value_type = value_t; @@ -43,8 +125,7 @@ struct drop_adaptor : inline_sequence_base> { static constexpr auto size(auto& self) requires sized_sequence { - return (cmp::max)(num::sub(flux::size(self.base()), self.count_), - distance_t{0}); + return (cmp::max)(num::sub(flux::size(self.base()), self.count_), int_t{0}); } static constexpr auto data(auto& self) @@ -61,16 +142,16 @@ struct drop_adaptor : inline_sequence_base> { }; struct drop_fn { - template + template [[nodiscard]] - constexpr auto operator()(Seq&& seq, num::integral auto count) const + constexpr auto operator()(It&& it, num::integral auto count) const { - auto count_ = num::checked_cast(count); + auto count_ = num::checked_cast(count); if (count_ < 0) { runtime_error("Negative argument passed to drop()"); } - return drop_adaptor>(FLUX_FWD(seq), count_); + return drop_adaptor>(FLUX_FWD(it), count_); } }; diff --git a/include/flux/adaptor/drop_while.hpp b/include/flux/adaptor/drop_while.hpp index ecd9c49b..12038d8f 100644 --- a/include/flux/adaptor/drop_while.hpp +++ b/include/flux/adaptor/drop_while.hpp @@ -12,7 +12,7 @@ namespace flux { namespace detail { -template +template struct drop_while_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; @@ -23,20 +23,59 @@ struct drop_while_adaptor : inline_sequence_base> constexpr auto base() & -> Base& { return base_; } constexpr auto base() const& -> Base const& { return base_; } + template + struct context_type : immovable { + BaseCtx base_ctx; + DropPred drop_pred; + bool done = false; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return base_ctx.run_while([&](auto&& elem) { + if (done) { + return std::invoke(pred, FLUX_FWD(elem)); + } else { + if (std::invoke(drop_pred, std::as_const(elem))) { + return loop_continue; + } else { + done = true; + return std::invoke(pred, FLUX_FWD(elem)); + } + } + }); + } + }; + public: constexpr drop_while_adaptor(decays_to auto&& base, decays_to auto&& pred) : base_(FLUX_FWD(base)), pred_(FLUX_FWD(pred)) - {} + { + } - struct flux_sequence_traits : detail::passthrough_traits_base { - using value_type = value_t; + [[nodiscard]] constexpr auto iterate() + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .drop_pred = copy_or_ref(pred_)}; + } + + [[nodiscard]] + constexpr auto iterate() const + requires iterable && std::predicate> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .drop_pred = copy_or_ref(pred_)}; + } + struct flux_sequence_traits : detail::passthrough_traits_base { static constexpr bool disable_multipass = !multipass_sequence; static constexpr auto first(auto& self) + requires sequence { - return flux::for_each_while(self.base_, std::ref(self.pred_)); + return flux::seq_for_each_while(self.base_, std::ref(self.pred_)); } static constexpr auto data(auto& self) @@ -52,12 +91,12 @@ struct drop_while_adaptor : inline_sequence_base> }; struct drop_while_fn { - template - requires std::predicate> + template + requires std::predicate> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const + constexpr auto operator()(It&& it, Pred pred) const { - return drop_while_adaptor, Pred>(FLUX_FWD(seq), std::move(pred)); + return drop_while_adaptor, Pred>(FLUX_FWD(it), std::move(pred)); } }; @@ -67,7 +106,7 @@ FLUX_EXPORT inline constexpr auto drop_while = detail::drop_while_fn{}; template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::drop_while(Pred pred) && { return flux::drop_while(std::move(derived()), std::move(pred)); diff --git a/include/flux/adaptor/filter.hpp b/include/flux/adaptor/filter.hpp index ba5ff208..413d44a8 100644 --- a/include/flux/adaptor/filter.hpp +++ b/include/flux/adaptor/filter.hpp @@ -13,12 +13,30 @@ namespace flux { namespace detail { -template -class filter_adaptor : public inline_sequence_base> -{ +template +class filter_adaptor : public inline_sequence_base> { FLUX_NO_UNIQUE_ADDRESS Base base_; FLUX_NO_UNIQUE_ADDRESS Pred pred_; + template + struct context_type : immovable { + BaseCtx base_ctx; + FilterFn filter_fn; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return base_ctx.run_while([this, &pred](auto&& elem) { + if (std::invoke(filter_fn, elem)) { + return std::invoke(pred, FLUX_FWD(elem)); + } else { + return loop_continue; + } + }); + } + }; + public: constexpr filter_adaptor(decays_to auto&& base, decays_to auto&& pred) : base_(FLUX_FWD(base)), @@ -30,6 +48,34 @@ class filter_adaptor : public inline_sequence_base> [[nodiscard]] constexpr auto base() && -> Base { return std::move(base_); } + constexpr auto iterate() + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .filter_fn = copy_or_ref(pred_)}; + } + + constexpr auto iterate() const + requires iterable && std::invocable> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .filter_fn = copy_or_ref(pred_)}; + } + + constexpr auto reverse_iterate() + requires reverse_iterable + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::reverse_iterate(base_), .filter_fn = copy_or_ref(pred_)}; + } + + constexpr auto reverse_iterate() const + requires reverse_iterable + && std::invocable> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::reverse_iterate(base_), .filter_fn = copy_or_ref(pred_)}; + } + struct flux_sequence_traits { private: struct cursor_type { @@ -41,52 +87,54 @@ class filter_adaptor : public inline_sequence_base> }; public: - using value_type = value_t; + using value_type = iterable_value_t; static constexpr bool disable_multipass = !multipass_sequence; - static constexpr auto first(auto& self) -> cursor_type + static constexpr auto first(auto& self) + -> decltype(cursor_type{flux::find_if(self.base_, std::ref(self.pred_))}) + requires sequence { return cursor_type{flux::find_if(self.base_, std::ref(self.pred_))}; } - static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + static constexpr auto is_last(auto& self, auto const& cur) -> bool { return flux::is_last(self.base_, cur.base_cur); } - static constexpr auto read_at(auto& self, cursor_type const& cur) + static constexpr auto read_at(auto& self, auto const& cur) -> decltype(flux::read_at(self.base_, cur.base_cur)) { return flux::read_at(self.base_, cur.base_cur); } - static constexpr auto read_at_unchecked(auto& self, cursor_type const& cur) + static constexpr auto read_at_unchecked(auto& self, auto const& cur) -> decltype(flux::read_at_unchecked(self.base_, cur.base_cur)) { return flux::read_at_unchecked(self.base_, cur.base_cur); } - static constexpr auto move_at(auto& self, cursor_type const& cur) + static constexpr auto move_at(auto& self, auto const& cur) -> decltype(flux::move_at(self.base_, cur.base_cur)) { return flux::move_at(self.base_, cur.base_cur); } - static constexpr auto move_at_unchecked(auto& self, cursor_type const& cur) + static constexpr auto move_at_unchecked(auto& self, auto const& cur) -> decltype(flux::move_at_unchecked(self.base_, cur.base_cur)) { return flux::move_at_unchecked(self.base_, cur.base_cur); } - static constexpr auto inc(auto& self, cursor_type& cur) -> void + static constexpr auto inc(auto& self, auto& cur) -> void { flux::inc(self.base_, cur.base_cur); cur.base_cur = flux::slice(self.base_, std::move(cur).base_cur, flux::last) .find_if(std::ref(self.pred_)); } - static constexpr auto dec(auto& self, cursor_type& cur) -> void + static constexpr auto dec(auto& self, auto& cur) -> void requires bidirectional_sequence { do { @@ -94,16 +142,15 @@ class filter_adaptor : public inline_sequence_base> } while(!std::invoke(self.pred_, flux::read_at(self.base_, cur.base_cur))); } - static constexpr auto last(auto& self) -> cursor_type + static constexpr auto last(auto& self) requires bounded_sequence { return cursor_type{flux::last(self.base_)}; } static constexpr auto for_each_while(auto& self, auto&& func) - -> cursor_type { - return cursor_type{flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type {flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (std::invoke(self.pred_, elem)) { return std::invoke(func, FLUX_FWD(elem)); } else { @@ -115,12 +162,12 @@ class filter_adaptor : public inline_sequence_base> }; struct filter_fn { - template - requires std::predicate> + template + requires std::predicate const&> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const + constexpr auto operator()(It&& it, Pred pred) const { - return filter_adaptor, Pred>(FLUX_FWD(seq), std::move(pred)); + return filter_adaptor, Pred>(FLUX_FWD(it), std::move(pred)); } }; @@ -130,7 +177,7 @@ FLUX_EXPORT inline constexpr auto filter = detail::filter_fn{}; template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::filter(Pred pred) && { return detail::filter_adaptor(std::move(derived()), std::move(pred)); diff --git a/include/flux/adaptor/filter_map.hpp b/include/flux/adaptor/filter_map.hpp index f5d26e56..4602b7e3 100644 --- a/include/flux/adaptor/filter_map.hpp +++ b/include/flux/adaptor/filter_map.hpp @@ -7,6 +7,7 @@ #define FLUX_ADAPTOR_FILTER_MAP_HPP_INCLUDED #include +#include #include namespace flux { @@ -20,16 +21,20 @@ struct filter_map_fn { using strip_rvalue_ref_t = std::conditional_t< std::is_rvalue_reference_v, std::remove_reference_t, T>; - template - requires (std::invocable> && - optional_like>>>) - constexpr auto operator()(Seq&& seq, Func func) const + template + requires(std::invocable> + && optional_like< + std::remove_cvref_t>>>) + constexpr auto operator()(It&& it, Func func) const { - return flux::map(FLUX_FWD(seq), std::move(func)) - .filter([](auto&& opt) { return static_cast(opt); }) - .map([](auto&& opt) -> strip_rvalue_ref_t { - return *FLUX_FWD(opt); - }); + // FIXME: Change this back to a pipeline later + auto mapped = flux::map(FLUX_FWD(it), std::move(func)); + auto filtered + = flux::filter(std::move(mapped), [](auto&& opt) { return static_cast(opt); }); + return flux::map(std::move(filtered), + [](auto&& opt) -> strip_rvalue_ref_t { + return *FLUX_FWD(opt); + }); } }; @@ -39,8 +44,8 @@ FLUX_EXPORT inline constexpr auto filter_map = detail::filter_map_fn{}; template template -requires std::invocable> && - detail::optional_like>> + requires std::invocable> + && detail::optional_like>> constexpr auto inline_sequence_base::filter_map(Func func) && { return flux::filter_map(derived(), std::move(func)); @@ -50,11 +55,11 @@ namespace detail { struct filter_deref_fn { - template - requires optional_like> - constexpr auto operator()(Seq&& seq) const + template + requires optional_like> + constexpr auto operator()(It&& it) const { - return filter_map(FLUX_FWD(seq), [](auto&& opt) -> decltype(auto) { return FLUX_FWD(opt); }); + return filter_map(FLUX_FWD(it), [](auto&& opt) -> decltype(auto) { return FLUX_FWD(opt); }); } }; @@ -63,7 +68,8 @@ struct filter_deref_fn { FLUX_EXPORT inline constexpr auto filter_deref = detail::filter_deref_fn{}; template -constexpr auto inline_sequence_base::filter_deref() && requires detail::optional_like> +constexpr auto inline_sequence_base::filter_deref() && + requires detail::optional_like> { return flux::filter_deref(derived()); } diff --git a/include/flux/adaptor/flatten.hpp b/include/flux/adaptor/flatten.hpp index bd30e5a6..91abbd19 100644 --- a/include/flux/adaptor/flatten.hpp +++ b/include/flux/adaptor/flatten.hpp @@ -12,8 +12,80 @@ namespace flux { namespace detail { -template +template +struct flatten_iteration_context : immovable { + BaseCtx base_ctx; + + using OptInnerElem = decltype(next_element(base_ctx)); + OptInnerElem inner_elem = nullopt; + + using InnerCtx = decltype(IterateFn(*inner_elem)); + optional inner_ctx = nullopt; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (true) { + if (!inner_ctx.has_value()) { + inner_elem = next_element(base_ctx); + if (!inner_elem) { + return iteration_result::complete; + } + inner_ctx.emplace(detail::emplace_from([&] { return IterateFn(*inner_elem); })); + } + + FLUX_DEBUG_ASSERT(inner_ctx.has_value()); + auto res = flux::run_while(*inner_ctx, pred); + if (res == iteration_result::incomplete) { + return res; + } else { + inner_ctx.reset(); + } + } + } +}; + +template struct flatten_adaptor : inline_sequence_base> { +private: + Base base_; + +public: + constexpr explicit flatten_adaptor(decays_to auto&& base) : base_(FLUX_FWD(base)) { } + + constexpr auto iterate() + { + using Ctx = flatten_iteration_context>; + return Ctx{.base_ctx = flux::iterate(base_)}; + } + + constexpr auto iterate() const + requires iterable && iterable> + { + using Ctx = flatten_iteration_context>; + return Ctx{.base_ctx = flux::iterate(base_)}; + } + + constexpr auto reverse_iterate() + requires reverse_iterable && reverse_iterable> + { + using Ctx + = flatten_iteration_context, flux::reverse_iterate>; + return Ctx{.base_ctx = flux::reverse_iterate(base_)}; + } + + constexpr auto reverse_iterate() const + requires reverse_iterable && reverse_iterable> + { + using Ctx = flatten_iteration_context, + flux::reverse_iterate>; + return Ctx{.base_ctx = flux::reverse_iterate(base_)}; + } +}; + +template +struct flatten_adaptor : inline_sequence_base> { private: using InnerSeq = element_t; @@ -21,9 +93,36 @@ struct flatten_adaptor : inline_sequence_base> { optional inner_ = nullopt; public: - constexpr explicit flatten_adaptor(decays_to auto&& base) - : base_(FLUX_FWD(base)) - {} + constexpr explicit flatten_adaptor(decays_to auto&& base) : base_(FLUX_FWD(base)) { } + + constexpr auto iterate() + { + return flatten_iteration_context>{.base_ctx + = flux::iterate(base_)}; + } + + constexpr auto iterate() const + requires iterable && iterable> + { + return flatten_iteration_context>{.base_ctx + = flux::iterate(base_)}; + } + + constexpr auto reverse_iterate() + requires reverse_iterable && reverse_iterable> + { + using Ctx + = flatten_iteration_context, flux::reverse_iterate>; + return Ctx{.base_ctx = flux::reverse_iterate(base_)}; + } + + constexpr auto reverse_iterate() const + requires reverse_iterable && reverse_iterable> + { + using Ctx = flatten_iteration_context, + flux::reverse_iterate>; + return Ctx{.base_ctx = flux::reverse_iterate(base_)}; + } struct flux_sequence_traits : default_sequence_traits { private: @@ -101,9 +200,30 @@ struct flatten_adaptor : inline_sequence_base> { Base base_; public: - constexpr explicit flatten_adaptor(decays_to auto&& base) - : base_(FLUX_FWD(base)) - {} + constexpr explicit flatten_adaptor(decays_to auto&& base) : base_(FLUX_FWD(base)) { } + + constexpr auto iterate() -> flatten_iteration_context> + { + return {.base_ctx = flux::iterate(base_)}; + } + + constexpr auto iterate() const -> flatten_iteration_context> + requires iterable && iterable> + { + return {.base_ctx = flux::iterate(base_)}; + } + + constexpr auto size() -> int_t + requires sized_sequence && sized_sequence> + { + return num::mul(flux::size(base_), flux::size(flux::read_at(base_, flux::first(base_)))); + } + + constexpr auto size() const -> int_t + requires sized_sequence && sized_sequence> + { + return num::mul(flux::size(base_), flux::size(flux::read_at(base_, flux::first(base_)))); + } struct flux_sequence_traits : default_sequence_traits { private: @@ -187,8 +307,8 @@ struct flatten_adaptor : inline_sequence_base> { static constexpr auto for_each_while(Self& self, auto&& pred) -> cursor_type { auto inner_cur = cursor_t{}; - auto outer_cur = flux::for_each_while(self.base_, [&](auto&& inner_seq) { - inner_cur = flux::for_each_while(inner_seq, pred); + auto outer_cur = flux::seq_for_each_while(self.base_, [&](auto&& inner_seq) { + inner_cur = flux::seq_for_each_while(inner_seq, pred); return flux::is_last(inner_seq, inner_cur); }); return cursor_type{.outer_cur = std::move(outer_cur), @@ -231,12 +351,12 @@ struct flatten_adaptor : inline_sequence_base> { }; struct flatten_fn { - template - requires sequence> + template + requires iterable> [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> sequence auto + constexpr auto operator()(It&& it) const -> iterable auto { - return flatten_adaptor>(FLUX_FWD(seq)); + return flatten_adaptor>(FLUX_FWD(it)); } }; @@ -246,7 +366,7 @@ FLUX_EXPORT inline constexpr auto flatten = detail::flatten_fn{}; template constexpr auto inline_sequence_base::flatten() && - requires sequence> + requires sequence> { return flux::flatten(std::move(derived())); } diff --git a/include/flux/adaptor/flatten_with.hpp b/include/flux/adaptor/flatten_with.hpp index 0b31a93e..ef88fa56 100644 --- a/include/flux/adaptor/flatten_with.hpp +++ b/include/flux/adaptor/flatten_with.hpp @@ -8,7 +8,7 @@ #include -#include +#include namespace flux { @@ -17,8 +17,8 @@ namespace detail { // Workaround for std::variant::emplace not being constexpr in libc++ // See P2231 (C++20 DR) template -inline constexpr auto variant_emplace = -[](std::variant& variant, auto&&... args) { +inline constexpr auto variant_emplace = [](std::variant& variant, + auto&&... args) { if (std::is_constant_evaluated()) { variant = std::variant(std::in_place_index, FLUX_FWD(args)...); // LCOV_EXCL_LINE } else { @@ -26,9 +26,111 @@ inline constexpr auto variant_emplace = } }; +template +struct flatten_with_iteration_context : immovable { + BaseCtx base_ctx; + Pattern pattern; + + using OptInnerElem = decltype(next_element(base_ctx)); + OptInnerElem inner_elem = nullopt; + + using InnerCtx = decltype(flux::iterate(*inner_elem)); + optional inner_ctx = nullopt; + + using PatternCtx = decltype(flux::iterate(pattern)); + optional pattern_ctx = nullopt; + + enum class mode_t { pattern, inner }; + mode_t mode = mode_t::inner; + + constexpr bool try_advance_inner() + { + if (inner_ctx.has_value()) { + return true; + } else { + inner_elem = flux::next_element(base_ctx); + if (!inner_elem) { + return false; + } else { + inner_ctx.emplace(detail::emplace_from([&] { return flux::iterate(*inner_elem); })); + return true; + } + } + } + + using element_type = std::common_reference_t, element_t>; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (!try_advance_inner()) { + return iteration_result::complete; + } + + while (true) { + if (mode == mode_t::inner) { + FLUX_DEBUG_ASSERT(inner_ctx.has_value()); + auto res = flux::run_while(*inner_ctx, pred); + if (res == iteration_result::incomplete) { + return res; + } else { + inner_ctx.reset(); + mode = mode_t::pattern; + } + } else { + if (!try_advance_inner()) { + return iteration_result::complete; + } + + if (!pattern_ctx.has_value()) { + pattern_ctx.emplace( + detail::emplace_from([&] { return flux::iterate(pattern); })); + } + + FLUX_DEBUG_ASSERT(pattern_ctx.has_value()); + auto res = flux::run_while(*pattern_ctx, pred); + if (res == iteration_result::incomplete) { + return res; + } else { + pattern_ctx.reset(); + mode = mode_t::inner; + } + } + } + } +}; + +template +struct flatten_with_adaptor : inline_sequence_base> { +private: + FLUX_NO_UNIQUE_ADDRESS Base base_; + FLUX_NO_UNIQUE_ADDRESS Pattern pattern_; + +public: + constexpr flatten_with_adaptor(decays_to auto&& base, decays_to auto&& pattern) + : base_(FLUX_FWD(base)), + pattern_(FLUX_FWD(pattern)) + { + } + + constexpr auto iterate() + { + return detail::flatten_with_iteration_context, + decltype(flux::mut_ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::mut_ref(pattern_)}; + } + + constexpr auto iterate() const + requires iterable && iterable> + { + return detail::flatten_with_iteration_context, + decltype(flux::ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::ref(pattern_)}; + } +}; + template -struct flatten_with_adaptor : inline_sequence_base> -{ +struct flatten_with_adaptor + : inline_sequence_base> { private: using InnerSeq = element_t; @@ -41,7 +143,23 @@ struct flatten_with_adaptor : inline_sequence_base auto&& pattern) : base_(FLUX_FWD(base)), pattern_(FLUX_FWD(pattern)) - {} + { + } + + constexpr auto iterate() + { + return detail::flatten_with_iteration_context, + decltype(flux::mut_ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::mut_ref(pattern_)}; + } + + constexpr auto iterate() const + requires iterable && iterable> + { + return detail::flatten_with_iteration_context, + decltype(flux::ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::ref(pattern_)}; + } struct flux_sequence_traits : default_sequence_traits { private: @@ -169,6 +287,21 @@ struct flatten_with_adaptor pattern_(FLUX_FWD(pattern)) {} + constexpr auto iterate() + { + return detail::flatten_with_iteration_context, + decltype(flux::mut_ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::mut_ref(pattern_)}; + } + + constexpr auto iterate() const + requires iterable && iterable> + { + return detail::flatten_with_iteration_context, + decltype(flux::ref(pattern_))>{ + .base_ctx = flux::iterate(base_), .pattern = flux::ref(pattern_)}; + } + struct flux_sequence_traits : default_sequence_traits { private: using InnerSeq = element_t; @@ -341,24 +474,22 @@ struct flatten_with_adaptor struct flatten_with_fn { - template - requires sequence> && - multipass_sequence && - flatten_with_compatible, Pattern> - constexpr auto operator()(Seq&& seq, Pattern&& pattern) const - -> sequence auto + template + requires iterable> && multipass_sequence + && flatten_with_compatible, Pattern> + constexpr auto operator()(It&& it, Pattern&& pattern) const -> iterable auto { - return flatten_with_adaptor, std::decay_t>( - FLUX_FWD(seq), FLUX_FWD(pattern)); + return flatten_with_adaptor, std::decay_t>(FLUX_FWD(it), + FLUX_FWD(pattern)); } - template - requires sequence> && - std::movable>> - constexpr auto operator()(Seq&& seq, value_t> value) const - -> sequence auto + template + requires sequence> + && std::movable>> + constexpr auto operator()(It&& it, iterable_value_t> value) const + -> iterable auto { - return (*this)(FLUX_FWD(seq), flux::single(std::move(value))); + return (*this)(FLUX_FWD(it), flux::single(std::move(value))); } }; @@ -368,9 +499,8 @@ FLUX_EXPORT inline constexpr auto flatten_with = detail::flatten_with_fn{}; template template - requires sequence> && - multipass_sequence && - detail::flatten_with_compatible, Pattern> + requires sequence> && multipass_sequence + && detail::flatten_with_compatible, Pattern> constexpr auto inline_sequence_base::flatten_with(Pattern&& pattern) && { return flux::flatten_with(std::move(derived()), FLUX_FWD(pattern)); @@ -378,8 +508,8 @@ constexpr auto inline_sequence_base::flatten_with(Pattern&& pattern) && template template - requires sequence> && - std::constructible_from>, Value&&> + requires sequence> + && std::constructible_from>, Value&&> constexpr auto inline_sequence_base::flatten_with(Value value) && { return flux::flatten_with(std::move(derived()), std::move(value)); diff --git a/include/flux/adaptor/map.hpp b/include/flux/adaptor/map.hpp index 010ba45f..c3d38139 100644 --- a/include/flux/adaptor/map.hpp +++ b/include/flux/adaptor/map.hpp @@ -12,17 +12,28 @@ namespace flux { namespace detail { -template - requires std::is_object_v && - std::regular_invocable> -struct map_adaptor : inline_sequence_base> -{ +template +struct map_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; FLUX_NO_UNIQUE_ADDRESS Func func_; friend struct sequence_traits; + template + struct context_type : immovable { + BaseCtx base_ctx; + MapFn map_fn; + + using element_type = std::invoke_result_t>; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return base_ctx.run_while( + [&pred, this](auto&& elem) { return pred(std::invoke(map_fn, FLUX_FWD(elem))); }); + } + }; + public: constexpr map_adaptor(decays_to auto&& base, decays_to auto&& func) : base_(FLUX_FWD(base)), @@ -34,10 +45,48 @@ struct map_adaptor : inline_sequence_base> constexpr auto base() && -> Base&& { return std::move(base_); } constexpr auto base() const&& -> Base const&& { return std::move(base_); } - struct flux_sequence_traits : detail::passthrough_traits_base + [[nodiscard]] constexpr auto iterate() + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .map_fn = copy_or_ref(func_)}; + } + + [[nodiscard]] constexpr auto iterate() const + requires iterable + && std::regular_invocable> { - using value_type = std::remove_cvref_t>>; + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .map_fn = copy_or_ref(func_)}; + } + + [[nodiscard]] constexpr auto reverse_iterate() + requires reverse_iterable + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::reverse_iterate(base_), .map_fn = copy_or_ref(func_)}; + } + + [[nodiscard]] constexpr auto reverse_iterate() const + requires reverse_iterable + && std::regular_invocable> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::reverse_iterate(base_), .map_fn = copy_or_ref(func_)}; + } + + constexpr auto size() -> int_t + requires sized_iterable + { + return flux::iterable_size(base_); + } + + constexpr auto size() const -> int_t + requires sized_iterable + { + return flux::iterable_size(base_); + } + struct flux_sequence_traits : detail::passthrough_traits_base { static constexpr bool disable_multipass = !multipass_sequence; static constexpr bool is_infinite = infinite_sequence; @@ -57,7 +106,7 @@ struct map_adaptor : inline_sequence_base> static constexpr auto for_each_while(auto& self, auto&& pred) { - return flux::for_each_while(self.base_, [&](auto&& elem) { + return flux::seq_for_each_while(self.base_, [&](auto&& elem) { return std::invoke(pred, std::invoke(self.func_, FLUX_FWD(elem))); }); } @@ -70,12 +119,12 @@ struct map_adaptor : inline_sequence_base> }; struct map_fn { - template - requires std::regular_invocable> + template + requires std::regular_invocable> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func) const + constexpr auto operator()(It&& it, Func func) const { - return map_adaptor, Func>(FLUX_FWD(seq), std::move(func)); + return map_adaptor, Func>(FLUX_FWD(it), std::move(func)); } }; @@ -85,7 +134,7 @@ FLUX_EXPORT inline constexpr auto map = detail::map_fn{}; template template - requires std::invocable> + requires std::invocable> constexpr auto inline_sequence_base::map(Func func) && { return detail::map_adaptor(std::move(derived()), std::move(func)); diff --git a/include/flux/adaptor/mask.hpp b/include/flux/adaptor/mask.hpp index df7ad3b9..f661a2be 100644 --- a/include/flux/adaptor/mask.hpp +++ b/include/flux/adaptor/mask.hpp @@ -12,18 +12,57 @@ namespace flux { namespace detail { -template -struct mask_adaptor : inline_sequence_base> -{ +template +struct mask_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; FLUX_NO_UNIQUE_ADDRESS Mask mask_; + template + struct context_type : immovable { + BaseCtx base_ctx; + MaskCtx mask_ctx; + + using element_type = typename BaseCtx::element_type; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (true) { + auto base_elem = next_element(base_ctx); + auto mask_elem = next_element(mask_ctx); + + if (!base_elem || !mask_elem) { + return iteration_result::complete; + } + + if (*mask_elem) { + if (!pred(*std::move(base_elem))) { + return iteration_result::incomplete; + } + } + } + } + }; + public: constexpr mask_adaptor(decays_to auto&& base, decays_to auto&& mask) : base_(FLUX_FWD(base)), mask_(FLUX_FWD(mask)) - {} + { + } + + [[nodiscard]] constexpr auto iterate() + { + return context_type, iteration_context_t>{ + .base_ctx = flux::iterate(base_), .mask_ctx = flux::iterate(mask_)}; + } + + [[nodiscard]] constexpr auto iterate() const + requires iterable && iterable + { + return context_type, iteration_context_t>{ + .base_ctx = flux::iterate(base_), .mask_ctx = flux::iterate(mask_)}; + } struct flux_sequence_traits : default_sequence_traits { private: @@ -141,8 +180,8 @@ struct mask_adaptor : inline_sequence_base> }; struct mask_fn { - template - requires boolean_testable> + template + requires boolean_testable> [[nodiscard]] constexpr auto operator()(Base&& base, Mask&& mask) const { @@ -157,7 +196,7 @@ FLUX_EXPORT inline constexpr auto mask = detail::mask_fn{}; template template - requires detail::boolean_testable> + requires detail::boolean_testable> constexpr auto inline_sequence_base::mask(Mask&& mask_) && { return flux::mask(std::move(derived()), FLUX_FWD(mask_)); diff --git a/include/flux/adaptor/read_only.hpp b/include/flux/adaptor/read_only.hpp index f68758cf..2e38ea1c 100644 --- a/include/flux/adaptor/read_only.hpp +++ b/include/flux/adaptor/read_only.hpp @@ -18,15 +18,14 @@ struct cast_to_const { constexpr auto operator()(auto&& elem) const -> T { return FLUX_FWD(elem); } }; -template - requires (not read_only_sequence) -struct read_only_adaptor : map_adaptor>> { +template +struct read_only_adaptor : map_adaptor>> { private: - using map = map_adaptor>>; + using map = map_adaptor>>; public: constexpr explicit read_only_adaptor(decays_to auto&& base) - : map(FLUX_FWD(base), cast_to_const>{}) + : map(FLUX_FWD(base), cast_to_const>{}) {} struct flux_sequence_traits : map::flux_sequence_traits { @@ -59,14 +58,14 @@ struct read_only_adaptor : map_adaptor }; struct read_only_fn { - template + template [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> read_only_sequence auto + constexpr auto operator()(It&& it) const -> iterable auto { - if constexpr (read_only_sequence) { - return FLUX_FWD(seq); + if constexpr (std::same_as, iterable_const_element_t>) { + return FLUX_FWD(it); } else { - return read_only_adaptor>(FLUX_FWD(seq)); + return read_only_adaptor>(FLUX_FWD(it)); } } }; diff --git a/include/flux/adaptor/reverse.hpp b/include/flux/adaptor/reverse.hpp index 2296692f..d5441f4c 100644 --- a/include/flux/adaptor/reverse.hpp +++ b/include/flux/adaptor/reverse.hpp @@ -12,160 +12,177 @@ namespace flux { namespace detail { -template - requires bounded_sequence -struct reverse_adaptor : inline_sequence_base> -{ +template +struct reverse_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; + friend struct sequence_traits; + public: - constexpr explicit reverse_adaptor(decays_to auto&& base) - : base_(FLUX_FWD(base)) - {} + constexpr explicit reverse_adaptor(decays_to auto&& base) : base_(FLUX_FWD(base)) { } [[nodiscard]] constexpr auto base() const& -> Base const& { return base_; } [[nodiscard]] constexpr auto base() && -> Base&& { return std::move(base_); } - struct flux_sequence_traits { - private: - struct cursor_type { - cursor_t base_cur; - - friend bool operator==(cursor_type const&, cursor_type const&) - requires std::equality_comparable> - = default; - - friend auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) - -> std::strong_ordering - requires std::three_way_comparable, std::strong_ordering> - { - return rhs.base_cur <=> lhs.base_cur; - } - }; - - public: - using value_type = value_t; - - static constexpr auto first(auto& self) -> cursor_type + struct flux_iterable_traits { + static constexpr auto iterate(auto& self) + requires reverse_iterable { - return cursor_type(flux::last(self.base_)); + return flux::reverse_iterate(self.base_); } - static constexpr auto last(auto& self) -> cursor_type + static constexpr auto reverse_iterate(auto& self) + requires iterable { - return cursor_type(flux::first(self.base_)); + return flux::iterate(self.base_); } - static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + static constexpr auto size(auto& self) + requires sized_iterable { - return cur.base_cur == flux::first(self.base_); + return flux::iterable_size(self.base_); } + }; +}; - template - static constexpr auto read_at(Self& self, cursor_type const& cur) - -> element_t - { - return flux::read_at(self.base_, flux::prev(self.base_, cur.base_cur)); - } +template +inline constexpr bool is_reverse_adaptor = false; - template - static constexpr auto read_at_unchecked(Self& self, cursor_type const& cur) - -> element_t - { - return flux::read_at_unchecked(self.base_, flux::prev(self.base_, cur.base_cur)); - } +template +inline constexpr bool is_reverse_adaptor> = true; - template - static constexpr auto move_at(Self& self, cursor_type const& cur) - -> rvalue_element_t - { - return flux::move_at(self.base_, flux::prev(self.base_, cur.base_cur)); +struct reverse_fn { + template + requires reverse_iterable + [[nodiscard]] + constexpr auto operator()(It&& it) const -> reverse_iterable auto + { + if constexpr (is_reverse_adaptor>) { + return FLUX_FWD(it).base(); + } else { + return reverse_adaptor>(FLUX_FWD(it)); } + } +}; - template - static constexpr auto move_at_unchecked(Self& self, cursor_type const& cur) - -> rvalue_element_t - { - return flux::move_at_unchecked(self.base_, flux::prev(self.base_, cur.base_cur)); - } +} // namespace detail - static constexpr auto inc(auto& self, cursor_type& cur) -> void - { - flux::dec(self.base_, cur.base_cur); - } +template +struct sequence_traits> : default_sequence_traits { +private: + struct cursor_type { + cursor_t base_cur; - static constexpr auto dec(auto& self, cursor_type& cur) -> void - { - flux::inc(self.base_, cur.base_cur); - } + friend bool operator==(cursor_type const&, cursor_type const&) + requires std::equality_comparable> + = default; - static constexpr auto inc(auto& self, cursor_type& cur, distance_t dist) -> void + friend auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) + -> std::strong_ordering + requires std::three_way_comparable, std::strong_ordering> { - flux::inc(self.base_, cur.base_cur, num::neg(dist)); + return rhs.base_cur <=> lhs.base_cur; } + }; - static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t - { - return flux::distance(self.base_, to.base_cur, from.base_cur); - } +public: + using value_type = value_t; - static constexpr auto size(auto& self) -> distance_t - requires sized_sequence - { - return flux::size(self.base_); - } + static constexpr auto first(auto& self) -> cursor_type + { + return cursor_type(flux::last(self.base_)); + } - static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type - requires bidirectional_sequence && - bounded_sequence - { - auto cur = flux::last(self.base_); - const auto end = flux::first(self.base_); - - while (cur != end) { - flux::dec(self.base_, cur); - if (!std::invoke(pred, flux::read_at(self.base_, cur))) { - flux::inc(self.base_, cur); - break; - } - } + static constexpr auto last(auto& self) -> cursor_type + { + return cursor_type(flux::first(self.base_)); + } - return cursor_type(cur); - } - }; -}; + static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + { + return cur.base_cur == flux::first(self.base_); + } -template -inline constexpr bool is_reverse_adaptor = false; + template + static constexpr auto read_at(Self& self, cursor_type const& cur) + -> element_t + { + return flux::read_at(self.base_, flux::prev(self.base_, cur.base_cur)); + } -template -inline constexpr bool is_reverse_adaptor> = true; + template + static constexpr auto read_at_unchecked(Self& self, cursor_type const& cur) + -> element_t + { + return flux::read_at_unchecked(self.base_, flux::prev(self.base_, cur.base_cur)); + } -struct reverse_fn { - template - requires bidirectional_sequence && - bounded_sequence - [[nodiscard]] - constexpr auto operator()(Seq&& seq) const - -> sequence auto + template + static constexpr auto move_at(Self& self, cursor_type const& cur) + -> rvalue_element_t { - if constexpr (is_reverse_adaptor>) { - return FLUX_FWD(seq).base(); - } else { - return reverse_adaptor>(FLUX_FWD(seq)); + return flux::move_at(self.base_, flux::prev(self.base_, cur.base_cur)); + } + + template + static constexpr auto move_at_unchecked(Self& self, cursor_type const& cur) + -> rvalue_element_t + { + return flux::move_at_unchecked(self.base_, flux::prev(self.base_, cur.base_cur)); + } + + static constexpr auto inc(auto& self, cursor_type& cur) -> void + { + flux::dec(self.base_, cur.base_cur); + } + + static constexpr auto dec(auto& self, cursor_type& cur) -> void + { + flux::inc(self.base_, cur.base_cur); + } + + static constexpr auto inc(auto& self, cursor_type& cur, int_t dist) -> void + { + flux::inc(self.base_, cur.base_cur, num::neg(dist)); + } + + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t + { + return flux::distance(self.base_, to.base_cur, from.base_cur); + } + + static constexpr auto size(auto& self) -> int_t + requires sized_sequence + { + return flux::size(self.base_); + } + + static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type + requires bidirectional_sequence + && bounded_sequence + { + auto cur = flux::last(self.base_); + const auto end = flux::first(self.base_); + + while (cur != end) { + flux::dec(self.base_, cur); + if (!std::invoke(pred, flux::read_at(self.base_, cur))) { + flux::inc(self.base_, cur); + break; + } } + + return cursor_type(cur); } }; -} // namespace detail - FLUX_EXPORT inline constexpr auto reverse = detail::reverse_fn{}; template constexpr auto inline_sequence_base::reverse() && - requires bidirectional_sequence && bounded_sequence + requires reverse_iterable { return flux::reverse(std::move(derived())); } diff --git a/include/flux/adaptor/scan.hpp b/include/flux/adaptor/scan.hpp index d3ac9fb7..c021e099 100644 --- a/include/flux/adaptor/scan.hpp +++ b/include/flux/adaptor/scan.hpp @@ -35,6 +35,31 @@ struct scan_adaptor : inline_sequence_base> { FLUX_NO_UNIQUE_ADDRESS Func func_; FLUX_NO_UNIQUE_ADDRESS R accum_; + struct context_type : immovable { + scan_adaptor* parent; + iteration_context_t base_ctx; + bool first = true; + + using element_type = R const&; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if constexpr (Mode == scan_mode::exclusive) { + if (first) { + first = false; + if (!pred(std::as_const(parent->accum_))) { + return iteration_result::incomplete; + } + } + } + return base_ctx.run_while([&](auto&& elem) { + parent->accum_ + = std::invoke(parent->func_, std::move(parent->accum_), FLUX_FWD(elem)); + return pred(std::as_const(parent->accum_)); + }); + } + }; + public: constexpr scan_adaptor(decays_to auto&& base, Func&& func, auto&& init) : base_(FLUX_FWD(base)), @@ -45,6 +70,21 @@ struct scan_adaptor : inline_sequence_base> { scan_adaptor(scan_adaptor&&) = default; scan_adaptor& operator=(scan_adaptor&&) = default; + constexpr auto iterate() -> context_type + { + return context_type{.parent = this, .base_ctx = flux::iterate(base_)}; + } + + constexpr auto size() -> int_t + requires sized_iterable + { + if constexpr (Mode == scan_mode::exclusive) { + return num::add(flux::iterable_size(base_), int_t{1}); + } else { + return flux::iterable_size(base_); + } + } + struct flux_sequence_traits : default_sequence_traits { private: @@ -128,11 +168,11 @@ struct scan_adaptor : inline_sequence_base> { return cur; } - static constexpr auto size(self_t& self) -> distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { if constexpr (Mode == scan_mode::exclusive) { - return num::add(flux::size(self.base_), distance_t{1}); + return num::add(flux::size(self.base_), int_t {1}); } else { return flux::size(self.base_); } @@ -141,7 +181,7 @@ struct scan_adaptor : inline_sequence_base> { static constexpr auto for_each_while(self_t& self, auto&& pred) -> cursor_type requires (Mode != scan_mode::exclusive) { - return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type(flux::seq_for_each_while(self.base_, [&](auto&& elem) { self.accum_ = std::invoke(self.func_, std::move(self.accum_), FLUX_FWD(elem)); return std::invoke(pred, std::as_const(self.accum_)); })); @@ -152,28 +192,26 @@ struct scan_adaptor : inline_sequence_base> { }; struct scan_fn { - template , - typename R = fold_result_t> - requires foldable + template , + typename R = fold_result_t> + requires foldable [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func, Init init = Init{}) const - -> sequence auto + constexpr auto operator()(It&& it, Func func, Init init = Init{}) const -> iterable auto { - return scan_adaptor, Func, R, scan_mode::inclusive>( - FLUX_FWD(seq), std::move(func), std::move(init)); + return scan_adaptor, Func, R, scan_mode::inclusive>( + FLUX_FWD(it), std::move(func), std::move(init)); } }; struct prescan_fn { - template > - requires foldable + template > + requires foldable [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func, Init init) const - -> sequence auto + constexpr auto operator()(It&& it, Func func, Init init) const -> iterable auto { - return scan_adaptor, Func, R, scan_mode::exclusive>( - FLUX_FWD(seq), std::move(func), std::move(init)); + return scan_adaptor, Func, R, scan_mode::exclusive>( + FLUX_FWD(it), std::move(func), std::move(init)); } }; diff --git a/include/flux/adaptor/scan_first.hpp b/include/flux/adaptor/scan_first.hpp index 1c781e71..86496d14 100644 --- a/include/flux/adaptor/scan_first.hpp +++ b/include/flux/adaptor/scan_first.hpp @@ -22,11 +22,56 @@ struct scan_first_adaptor : inline_sequence_base accum_; + struct context_type : immovable { + scan_first_adaptor* parent; + iteration_context_t base_ctx; + + using element_type = R const&; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (!parent->accum_.has_value()) { + base_ctx.run_while([&](auto&& elem) { + parent->accum_.emplace(FLUX_FWD(elem)); + return false; + }); + if (!parent->accum_.has_value()) { + return iteration_result::complete; + } + if (!std::invoke(pred, std::as_const(*parent->accum_))) { + return iteration_result::incomplete; + } + } + + FLUX_DEBUG_ASSERT(parent->accum_.has_value()); + return base_ctx.run_while([&](auto&& elem) { + parent->accum_.emplace( + std::invoke(parent->func_, *std::move(parent->accum_), FLUX_FWD(elem))); + return std::invoke(pred, std::as_const(*parent->accum_)); + }); + } + }; + public: constexpr scan_first_adaptor(decays_to auto&& base, Func&& func) : base_(FLUX_FWD(base)), func_(std::move(func)) - {} + { + } + + scan_first_adaptor(scan_first_adaptor&&) = default; + scan_first_adaptor& operator=(scan_first_adaptor&&) = default; + + [[nodiscard]] constexpr auto iterate() -> context_type + { + return context_type{.parent = this, .base_ctx = flux::iterate(base_)}; + } + + [[nodiscard]] constexpr auto size() -> int_t + requires sized_iterable + { + return flux::iterable_size(base_); + } struct flux_sequence_traits : default_sequence_traits { private: @@ -89,7 +134,7 @@ struct scan_first_adaptor : inline_sequence_base distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { return flux::size(self.base_); @@ -97,7 +142,7 @@ struct scan_first_adaptor : inline_sequence_base cursor_type { - return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type(flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (self.accum_.has_value()) { self.accum_.emplace( std::invoke(self.func_, @@ -113,14 +158,13 @@ struct scan_first_adaptor : inline_sequence_base - requires foldable> + template + requires foldable> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func) const -> sequence auto + constexpr auto operator()(It&& it, Func func) const -> iterable auto { - using R = fold_result_t>; - return scan_first_adaptor, Func, R>( - FLUX_FWD(seq), std::move(func)); + using R = fold_result_t>; + return scan_first_adaptor, Func, R>(FLUX_FWD(it), std::move(func)); } }; @@ -130,7 +174,7 @@ FLUX_EXPORT inline constexpr auto scan_first = detail::scan_first_fn{}; template template - requires foldable> + requires foldable> constexpr auto inline_sequence_base::scan_first(Func func) && { return flux::scan_first(std::move(derived()), std::move(func)); diff --git a/include/flux/adaptor/set_adaptors.hpp b/include/flux/adaptor/set_adaptors.hpp index 80ceb366..008ed57f 100644 --- a/include/flux/adaptor/set_adaptors.hpp +++ b/include/flux/adaptor/set_adaptors.hpp @@ -12,10 +12,8 @@ namespace flux::detail { -template -struct set_union_adaptor - : flux::inline_sequence_base> -{ +template +struct set_union_adaptor : flux::inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base1 base1_; FLUX_NO_UNIQUE_ADDRESS Base2 base2_; @@ -145,10 +143,9 @@ struct set_union_adaptor }; }; -template +template struct set_difference_adaptor - : flux::inline_sequence_base> -{ + : flux::inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base1 base1_; FLUX_NO_UNIQUE_ADDRESS Base2 base2_; @@ -248,10 +245,9 @@ struct set_difference_adaptor }; }; -template +template struct set_symmetric_difference_adaptor - : flux::inline_sequence_base> -{ + : flux::inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base1 base1_; FLUX_NO_UNIQUE_ADDRESS Base2 base2_; @@ -398,10 +394,9 @@ struct set_symmetric_difference_adaptor }; }; -template +template struct set_intersection_adaptor - : flux::inline_sequence_base> -{ + : flux::inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base1 base1_; FLUX_NO_UNIQUE_ADDRESS Base2 base2_; @@ -507,10 +502,11 @@ concept set_op_compatible = requires { typename std::common_type_t, value_t>; }; struct set_union_fn { - template - requires set_op_compatible && - weak_ordering_for && - weak_ordering_for + template + requires multipass_sequence && multipass_sequence + && set_op_compatible && weak_ordering_for + && weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -519,9 +515,10 @@ struct set_union_fn { }; struct set_difference_fn { - template - requires weak_ordering_for && - weak_ordering_for + template + requires multipass_sequence && multipass_sequence + && weak_ordering_for && weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -530,10 +527,11 @@ struct set_difference_fn { }; struct set_symmetric_difference_fn { - template - requires set_op_compatible && - weak_ordering_for && - weak_ordering_for + template + requires multipass_sequence && multipass_sequence + && set_op_compatible && weak_ordering_for + && weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -542,9 +540,10 @@ struct set_symmetric_difference_fn { }; struct set_intersection_fn { - template - requires weak_ordering_for && - weak_ordering_for + template + requires multipass_sequence && multipass_sequence + && weak_ordering_for && weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { diff --git a/include/flux/adaptor/slide.hpp b/include/flux/adaptor/slide.hpp index 038358c3..25329642 100644 --- a/include/flux/adaptor/slide.hpp +++ b/include/flux/adaptor/slide.hpp @@ -17,10 +17,10 @@ template struct slide_adaptor : inline_sequence_base> { private: Base base_; - distance_t win_sz_; + int_t win_sz_; public: - constexpr slide_adaptor(decays_to auto&& base, distance_t win_sz) + constexpr slide_adaptor(decays_to auto&& base, int_t win_sz) : base_(FLUX_FWD(base)), win_sz_(win_sz) {} @@ -49,7 +49,7 @@ struct slide_adaptor : inline_sequence_base> { static constexpr auto first(auto& self) -> cursor_type { auto cur = flux::first(self.base_); auto end = cur; - advance(self.base_, end, num::sub(self.win_sz_, distance_t{1})); + advance(self.base_, end, num::sub(self.win_sz_, int_t {1})); return cursor_type{.from = std::move(cur), .to = std::move(end)}; } @@ -76,7 +76,7 @@ struct slide_adaptor : inline_sequence_base> { { auto end = flux::last(self.base_); auto cur = end; - advance(self.base_, cur, num::sub(distance_t{1}, self.win_sz_)); + advance(self.base_, cur, num::sub(int_t {1}, self.win_sz_)); return cursor_type{.from = std::move(cur), .to = std::move(end)}; } @@ -87,7 +87,7 @@ struct slide_adaptor : inline_sequence_base> { flux::dec(self.base_, cur.to); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { flux::inc(self.base_, cur.from, offset); @@ -95,17 +95,17 @@ struct slide_adaptor : inline_sequence_base> { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return flux::distance(self.base_, from.from, to.from); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { - auto s = num::add(num::sub(flux::size(self.base_), self.win_sz_), distance_t{1}); - return (cmp::max)(s, distance_t{0}); + auto s = num::add(num::sub(flux::size(self.base_), self.win_sz_), int_t {1}); + return (cmp::max)(s, int_t {0}); } }; }; @@ -117,8 +117,7 @@ struct slide_fn { constexpr auto operator()(Seq&& seq, num::integral auto win_sz) const -> sequence auto { - return slide_adaptor>(FLUX_FWD(seq), - num::checked_cast(win_sz)); + return slide_adaptor>(FLUX_FWD(seq), num::checked_cast(win_sz)); } }; diff --git a/include/flux/adaptor/split.hpp b/include/flux/adaptor/split.hpp index 56e132d0..659b5d69 100644 --- a/include/flux/adaptor/split.hpp +++ b/include/flux/adaptor/split.hpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace flux { @@ -207,9 +207,8 @@ FLUX_EXPORT inline constexpr auto split = detail::split_fn{}; template template - requires multipass_sequence && - multipass_sequence && - std::equality_comparable_with, element_t> + requires multipass_sequence && multipass_sequence + && std::equality_comparable_with, iterable_element_t> constexpr auto inline_sequence_base::split(Pattern&& pattern) && { return flux::split(std::move(derived()), FLUX_FWD(pattern)); @@ -217,8 +216,8 @@ constexpr auto inline_sequence_base::split(Pattern&& pattern) && template template - requires multipass_sequence && - std::equality_comparable_with, Delim const&> + requires multipass_sequence + && std::equality_comparable_with, Delim const&> constexpr auto inline_sequence_base::split(Delim&& delim) && { return flux::split(std::move(derived()), FLUX_FWD(delim)); @@ -226,8 +225,7 @@ constexpr auto inline_sequence_base::split(Delim&& delim) && template template - requires multipass_sequence && - std::predicate> + requires multipass_sequence && std::predicate> constexpr auto inline_sequence_base::split(Pred pred) && { return flux::split(std::move(derived()), std::move(pred)); diff --git a/include/flux/adaptor/stride.hpp b/include/flux/adaptor/stride.hpp index c5f623f8..83e278e1 100644 --- a/include/flux/adaptor/stride.hpp +++ b/include/flux/adaptor/stride.hpp @@ -15,10 +15,10 @@ namespace detail { // This is a Flux-ified version of ranges::advance. inline constexpr struct advance_fn { template - constexpr auto operator()(Seq& seq, cursor_t& cur, distance_t offset) const -> distance_t + constexpr auto operator()(Seq& seq, cursor_t& cur, int_t offset) const -> int_t { if (offset > 0) { - distance_t counter = 0; + int_t counter = 0; while (offset-- > 0 && !flux::is_last(seq, cur)) { flux::inc(seq, cur); ++counter; @@ -44,7 +44,7 @@ inline constexpr struct advance_fn { template requires bounded_sequence - constexpr auto operator()(Seq& seq, cursor_t& cur, distance_t offset) const -> distance_t + constexpr auto operator()(Seq& seq, cursor_t& cur, int_t offset) const -> int_t { if (offset > 0) { auto dist = (cmp::min)(flux::distance(seq, cur, flux::last(seq)), offset); @@ -61,14 +61,38 @@ inline constexpr struct advance_fn { } } advance; +template +struct stride_iteration_context : immovable { + BaseCtx base_ctx; + int_t stride; + int_t skip = 0; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return base_ctx.run_while([&](auto&& elem) { + if (skip > 0) { + --skip; + return loop_continue; + } else { + skip = stride - 1; + return std::invoke(pred, FLUX_FWD(elem)); + } + }); + } +}; + template struct stride_adaptor : inline_sequence_base> { private: Base base_; - distance_t stride_; + int_t stride_; + + friend struct sequence_traits; public: - constexpr stride_adaptor(decays_to auto&& base, distance_t stride) + constexpr stride_adaptor(decays_to auto&& base, int_t stride) : base_(FLUX_FWD(base)), stride_(stride) {} @@ -76,189 +100,220 @@ struct stride_adaptor : inline_sequence_base> { constexpr auto base() & -> Base& { return base_; } constexpr auto base() const& -> Base const& { return base_; } - struct flux_sequence_traits : passthrough_traits_base { - - using value_type = value_t; - static inline constexpr bool is_infinite = infinite_sequence; + [[nodiscard]] constexpr auto iterate() + { + return stride_iteration_context>{.base_ctx = flux::iterate(base_), + .stride = stride_}; + } - static constexpr auto inc(auto& self, cursor_t& cur) -> void - { - advance(self.base(), cur, self.stride_); - } + [[nodiscard]] constexpr auto iterate() const + requires iterable + { + return stride_iteration_context>{ + .base_ctx = flux::iterate(base_), .stride = stride_}; + } - // This version of stride is never bidir - static void dec(...) = delete; + [[nodiscard]] constexpr auto reverse_iterate() + requires reverse_iterable && sized_iterable + { + int_t initial_skip + = stride_ - 1 - (stride_ - flux::iterable_size(base_) % stride_) % stride_; + return stride_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .stride = stride_, .skip = initial_skip}; + } - static constexpr auto size(auto& self) -> distance_t - requires sized_sequence - { - auto s = flux::size(self.base_); - return s/self.stride_ + (s % self.stride_ == 0 ? 0 : 1); - } + [[nodiscard]] constexpr auto reverse_iterate() const + requires reverse_iterable && sized_iterable + { + int_t initial_skip + = stride_ - 1 - (stride_ - flux::iterable_size(base_) % stride_) % stride_; + return stride_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .stride = stride_, .skip = initial_skip}; + } - static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_t - requires sequence - { - distance_t n = self.stride_; - return flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { - if (++n < s) { - return true; - } else { - n = 0; - return std::invoke(pred, FLUX_FWD(elem)); - } - }); - } + [[nodiscard]] constexpr auto size() -> int_t + requires sized_iterable + { + int_t sz = flux::iterable_size(base_); + return sz / stride_ + (sz % stride_ == 0 ? 0 : 1); + } - }; + [[nodiscard]] constexpr auto size() const -> int_t + requires sized_iterable + { + int_t sz = flux::iterable_size(base_); + return sz / stride_ + (sz % stride_ == 0 ? 0 : 1); + } }; -template -struct stride_adaptor : inline_sequence_base> { -private: - Base base_; - distance_t stride_; +} // namespace detail -public: - constexpr stride_adaptor(decays_to auto&& base, distance_t stride) - : base_(FLUX_FWD(base)), - stride_(stride) - {} +template +struct sequence_traits> : detail::passthrough_traits_base { - struct flux_sequence_traits : default_sequence_traits { - private: - struct cursor_type { - cursor_t cur{}; - distance_t missing = 0; + using value_type = value_t; + static inline constexpr bool is_infinite = infinite_sequence; - friend constexpr auto operator==(cursor_type const& lhs, cursor_type const& rhs) -> bool - { - return lhs.cur == rhs.cur; - } + static constexpr auto inc(auto& self, cursor_t& cur) -> void + { + detail::advance(self.base(), cur, self.stride_); + } + + // This version of stride is never bidir + static void dec(...) = delete; + + static constexpr auto size(auto& self) -> int_t + requires sized_sequence + { + auto s = flux::size(self.base_); + return s / self.stride_ + (s % self.stride_ == 0 ? 0 : 1); + } - friend constexpr auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) - -> std::strong_ordering - requires ordered_cursor> - { - return lhs.cur <=> rhs.cur; + static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_t + requires sequence + { + int_t n = self.stride_; + return flux::seq_for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { + if (++n < s) { + return true; + } else { + n = 0; + return std::invoke(pred, FLUX_FWD(elem)); } - }; + }); + } +}; - public: - using value_type = value_t; - static constexpr bool is_infinite = infinite_sequence; +template +struct sequence_traits> : default_sequence_traits { +private: + struct cursor_type { + cursor_t cur{}; + int_t missing = 0; - static constexpr auto first(auto& self) -> cursor_type + friend constexpr auto operator==(cursor_type const& lhs, cursor_type const& rhs) -> bool { - return cursor_type { - .cur = flux::first(self.base_), - .missing = 0 - }; + return lhs.cur == rhs.cur; } - static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + friend constexpr auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) + -> std::strong_ordering + requires ordered_cursor> { - return flux::is_last(self.base_, cur.cur); + return lhs.cur <=> rhs.cur; } + }; - static constexpr auto inc(auto& self, cursor_type& cur) -> void - { - cur.missing = advance(self.base_, cur.cur, self.stride_); - } +public: + using value_type = value_t; + static constexpr bool is_infinite = infinite_sequence; - static constexpr auto read_at(auto& self, cursor_type const& cur) - -> decltype(flux::read_at(self.base_, cur.cur)) - { - return flux::read_at(self.base_, cur.cur); - } + static constexpr auto first(auto& self) -> cursor_type + { + return cursor_type{.cur = flux::first(self.base_), .missing = 0}; + } - static constexpr auto move_at(auto& self, cursor_type const& cur) - -> decltype(flux::move_at(self.base_, cur.cur)) - { - return flux::move_at(self.base_, cur.cur); - } + static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + { + return flux::is_last(self.base_, cur.cur); + } - static constexpr auto read_at_unchecked(auto& self, cursor_type const& cur) - -> decltype(flux::read_at_unchecked(self.base_, cur.cur)) - { - return flux::read_at_unchecked(self.base_, cur.cur); - } + static constexpr auto inc(auto& self, cursor_type& cur) -> void + { + cur.missing = detail::advance(self.base_, cur.cur, self.stride_); + } - static constexpr auto move_at_unchecked(auto& self, cursor_type const& cur) - -> decltype(flux::move_at_unchecked(self.base_, cur.cur)) - { - return flux::move_at_unchecked(self.base_, cur.cur); - } + static constexpr auto read_at(auto& self, cursor_type const& cur) + -> decltype(flux::read_at(self.base_, cur.cur)) + { + return flux::read_at(self.base_, cur.cur); + } - static constexpr auto last(auto& self) -> cursor_type - requires bounded_sequence && sized_sequence - { - distance_t missing = - (self.stride_ - flux::size(self.base_) % self.stride_) % self.stride_; - return cursor_type{ - .cur = flux::last(self.base_), - .missing = missing - }; - } + static constexpr auto move_at(auto& self, cursor_type const& cur) + -> decltype(flux::move_at(self.base_, cur.cur)) + { + return flux::move_at(self.base_, cur.cur); + } - static constexpr auto dec(auto& self, cursor_type& cur) -> void - requires bidirectional_sequence - { - advance(self.base_, cur.cur, cur.missing - self.stride_); - cur.missing = 0; - } + static constexpr auto read_at_unchecked(auto& self, cursor_type const& cur) + -> decltype(flux::read_at_unchecked(self.base_, cur.cur)) + { + return flux::read_at_unchecked(self.base_, cur.cur); + } - static constexpr auto size(auto& self) -> distance_t - requires sized_sequence - { - auto s = flux::size(self.base_); - return s/self.stride_ + (s % self.stride_ == 0 ? 0 : 1); - } + static constexpr auto move_at_unchecked(auto& self, cursor_type const& cur) + -> decltype(flux::move_at_unchecked(self.base_, cur.cur)) + { + return flux::move_at_unchecked(self.base_, cur.cur); + } - static constexpr auto distance(auto& self, cursor_type const& from, - cursor_type const& to) -> distance_t - requires random_access_sequence - { - return (flux::distance(self.base_, from.cur, to.cur) - from.missing + to.missing)/self.stride_; - } + static constexpr auto last(auto& self) -> cursor_type + requires bounded_sequence && sized_sequence + { + int_t missing = (self.stride_ - flux::size(self.base_) % self.stride_) % self.stride_; + return cursor_type{.cur = flux::last(self.base_), .missing = missing}; + } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void - requires random_access_sequence - { - if (offset > 0) { - cur.missing = num::mod(advance(self.base_, cur.cur, num::mul(offset, self.stride_)), - self.stride_); - } else if (offset < 0) { - advance(self.base_, cur.cur, num::add(num::mul(offset, self.stride_), cur.missing)); - cur.missing = 0; - } - } + static constexpr auto dec(auto& self, cursor_type& cur) -> void + requires bidirectional_sequence + { + detail::advance(self.base_, cur.cur, cur.missing - self.stride_); + cur.missing = 0; + } - static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type - requires sequence - { - distance_t n = self.stride_; - auto c = flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { - if (++n < s) { - return true; - } else { - n = 0; - return std::invoke(pred, FLUX_FWD(elem)); - } - }); - return cursor_type{std::move(c), (n + 1) % self.stride_}; + static constexpr auto size(auto& self) -> int_t + requires sized_sequence + { + auto s = flux::size(self.base_); + return s / self.stride_ + (s % self.stride_ == 0 ? 0 : 1); + } + + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires random_access_sequence + { + return (flux::distance(self.base_, from.cur, to.cur) - from.missing + to.missing) + / self.stride_; + } + + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void + requires random_access_sequence + { + if (offset > 0) { + cur.missing = num::mod( + detail::advance(self.base_, cur.cur, num::mul(offset, self.stride_)), self.stride_); + } else if (offset < 0) { + detail::advance(self.base_, cur.cur, + num::add(num::mul(offset, self.stride_), cur.missing)); + cur.missing = 0; } - }; + } + + static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type + requires sequence + { + int_t n = self.stride_; + auto c = flux::seq_for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { + if (++n < s) { + return true; + } else { + n = 0; + return std::invoke(pred, FLUX_FWD(elem)); + } + }); + return cursor_type{std::move(c), (n + 1) % self.stride_}; + } }; +namespace detail { + struct stride_fn { - template + template [[nodiscard]] - constexpr auto operator()(Seq&& seq, num::integral auto by) const + constexpr auto operator()(It&& it, num::integral auto by) const { FLUX_ASSERT(by > 0); - return stride_adaptor>(FLUX_FWD(seq), - num::checked_cast(by)); + return stride_adaptor>(FLUX_FWD(it), num::checked_cast(by)); } }; diff --git a/include/flux/adaptor/take.hpp b/include/flux/adaptor/take.hpp index 025a1c73..3f0820d7 100644 --- a/include/flux/adaptor/take.hpp +++ b/include/flux/adaptor/take.hpp @@ -7,20 +7,22 @@ #define FLUX_ADAPTOR_TAKE_HPP_INCLUDED #include +#include namespace flux { namespace detail { -template -struct take_adaptor : inline_sequence_base> -{ +template +struct take_adaptor : inline_sequence_base> { private: Base base_; - distance_t count_; + int_t count_; + + friend struct sequence_traits; public: - constexpr take_adaptor(decays_to auto&& base, distance_t count) + constexpr take_adaptor(decays_to auto&& base, int_t count) : base_(FLUX_FWD(base)), count_(count) {} @@ -28,136 +30,173 @@ struct take_adaptor : inline_sequence_base> [[nodiscard]] constexpr auto base() const& -> Base const& { return base_; } [[nodiscard]] constexpr auto base() && -> Base&& { return std::move(base_); } - struct flux_sequence_traits { - private: - struct cursor_type { - cursor_t base_cur; - distance_t length; + [[nodiscard]] constexpr auto iterate() + { + return take_iteration_context>{.base_ctx = flux::iterate(base_), + .remaining = count_}; + } - friend bool operator==(cursor_type const&, cursor_type const&) = default; - friend auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) = default; - }; + [[nodiscard]] constexpr auto iterate() const + requires iterable + { + return take_iteration_context>{ + .base_ctx = flux::iterate(base_), .remaining = count_}; + } - public: - using value_type = value_t; + [[nodiscard]] constexpr auto reverse_iterate() + requires reverse_iterable && sized_iterable + { + int_t sz = flux::iterable_size(base_); + return drop_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .remaining = sz > count_ ? sz - count_ : 0}; + } - static constexpr auto first(auto& self) -> cursor_type - { - return cursor_type{.base_cur = flux::first(self.base_), - .length = self.count_}; - } + [[nodiscard]] constexpr auto reverse_iterate() const + requires reverse_iterable && sized_iterable + { + int_t sz = flux::iterable_size(base_); + return drop_iteration_context>{ + .base_ctx = flux::reverse_iterate(base_), .remaining = sz > count_ ? sz - count_ : 0}; + } - static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool - { - return cur.length <= 0 || flux::is_last(self.base_, cur.base_cur); - } + [[nodiscard]] constexpr auto size() -> int_t + requires sized_iterable + { + return (cmp::min)(flux::iterable_size(base_), count_); + } - static constexpr auto inc(auto& self, cursor_type& cur) - { - flux::inc(self.base_, cur.base_cur); - cur.length = num::sub(cur.length, distance_t{1}); - } + [[nodiscard]] constexpr auto size() const -> int_t + requires sized_iterable + { + return (cmp::min)(flux::iterable_size(base_), count_); + } +}; - static constexpr auto read_at(auto& self, cursor_type const& cur) - -> decltype(flux::read_at(self.base_, cur.base_cur)) - { - return flux::read_at(self.base_, cur.base_cur); +struct take_fn { + template + [[nodiscard]] + constexpr auto operator()(It&& it, num::integral auto count) const + { + auto count_ = num::checked_cast(count); + if (count_ < 0) { + runtime_error("Negative argument passed to take()"); } - static constexpr auto move_at(auto& self, cursor_type const& cur) - -> decltype(flux::move_at(self.base_, cur.base_cur)) - { - return flux::move_at(self.base_, cur.base_cur); - } + return take_adaptor>(FLUX_FWD(it), count_); + } +}; - static constexpr auto read_at_unchecked(auto& self, cursor_type const& cur) - -> decltype(flux::read_at_unchecked(self.base_, cur.base_cur)) - { - return flux::read_at_unchecked(self.base_, cur.base_cur); - } +} // namespace detail - static constexpr auto move_at_unchecked(auto& self, cursor_type const& cur) - -> decltype(flux::move_at_unchecked(self.base_, cur.base_cur)) - { - return flux::move_at_unchecked(self.base_, cur.base_cur); - } +template +struct sequence_traits> { +private: + struct cursor_type { + cursor_t base_cur; + int_t length; - static constexpr auto dec(auto& self, cursor_type& cur) - requires bidirectional_sequence - { - flux::dec(self.base_, cur.base_cur); - cur.length = num::add(cur.length, distance_t{1}); - } + friend bool operator==(cursor_type const&, cursor_type const&) = default; + friend auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) = default; + }; - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) - requires random_access_sequence - { - flux::inc(self.base_, cur.base_cur, offset); - cur.length = num::sub(cur.length, offset); - } +public: + using value_type = value_t; - static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t - requires random_access_sequence - { - return (cmp::min)(flux::distance(self.base_, from.base_cur, to.base_cur), - num::sub(from.length, to.length)); - } + static constexpr auto first(auto& self) -> cursor_type + { + return cursor_type{.base_cur = flux::first(self.base_), .length = self.count_}; + } - static constexpr auto data(auto& self) - -> decltype(flux::data(self.base_)) - requires contiguous_sequence - { - return flux::data(self.base_); - } + static constexpr auto is_last(auto& self, cursor_type const& cur) -> bool + { + return cur.length <= 0 || flux::is_last(self.base_, cur.base_cur); + } - static constexpr auto size(auto& self) - requires sized_sequence || infinite_sequence - { - if constexpr (infinite_sequence) { - return self.count_; - } else { - return (cmp::min)(flux::size(self.base_), self.count_); - } - } + static constexpr auto inc(auto& self, cursor_type& cur) + { + flux::inc(self.base_, cur.base_cur); + cur.length = num::sub(cur.length, int_t{1}); + } - static constexpr auto last(auto& self) -> cursor_type - requires (random_access_sequence && sized_sequence) || - infinite_sequence - { - return cursor_type{ - .base_cur = flux::next(self.base_, flux::first(self.base_), size(self)), - .length = 0 - }; - } + static constexpr auto read_at(auto& self, cursor_type const& cur) + -> decltype(flux::read_at(self.base_, cur.base_cur)) + { + return flux::read_at(self.base_, cur.base_cur); + } - static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type - { - distance_t len = self.count_; - auto cur = flux::for_each_while(self.base_, [&](auto&& elem) { - return (len-- > 0) && std::invoke(pred, FLUX_FWD(elem)); - }); + static constexpr auto move_at(auto& self, cursor_type const& cur) + -> decltype(flux::move_at(self.base_, cur.base_cur)) + { + return flux::move_at(self.base_, cur.base_cur); + } - return cursor_type{.base_cur = std::move(cur), .length = ++len}; - } - }; -}; + static constexpr auto read_at_unchecked(auto& self, cursor_type const& cur) + -> decltype(flux::read_at_unchecked(self.base_, cur.base_cur)) + { + return flux::read_at_unchecked(self.base_, cur.base_cur); + } -struct take_fn { - template - [[nodiscard]] - constexpr auto operator()(Seq&& seq, num::integral auto count) const + static constexpr auto move_at_unchecked(auto& self, cursor_type const& cur) + -> decltype(flux::move_at_unchecked(self.base_, cur.base_cur)) { - auto count_ = num::checked_cast(count); - if (count_ < 0) { - runtime_error("Negative argument passed to take()"); + return flux::move_at_unchecked(self.base_, cur.base_cur); + } + + static constexpr auto dec(auto& self, cursor_type& cur) + requires bidirectional_sequence + { + flux::dec(self.base_, cur.base_cur); + cur.length = num::add(cur.length, int_t{1}); + } + + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) + requires random_access_sequence + { + flux::inc(self.base_, cur.base_cur, offset); + cur.length = num::sub(cur.length, offset); + } + + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires random_access_sequence + { + return (cmp::min)(flux::distance(self.base_, from.base_cur, to.base_cur), + num::sub(from.length, to.length)); + } + + static constexpr auto data(auto& self) -> decltype(flux::data(self.base_)) + requires contiguous_sequence + { + return flux::data(self.base_); + } + + static constexpr auto size(auto& self) + requires sized_sequence || infinite_sequence + { + if constexpr (infinite_sequence) { + return self.count_; + } else { + return (cmp::min)(flux::size(self.base_), self.count_); } + } - return take_adaptor>(FLUX_FWD(seq), count_); + static constexpr auto last(auto& self) -> cursor_type + requires(random_access_sequence && sized_sequence) || infinite_sequence + { + return cursor_type{.base_cur = flux::next(self.base_, flux::first(self.base_), size(self)), + .length = 0}; } -}; -} // namespace detail + static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type + { + int_t len = self.count_; + auto cur = flux::seq_for_each_while(self.base_, [&](auto&& elem) { + return (len-- > 0) && std::invoke(pred, FLUX_FWD(elem)); + }); + + return cursor_type{.base_cur = std::move(cur), .length = ++len}; + } +}; FLUX_EXPORT inline constexpr auto take = detail::take_fn{}; @@ -165,7 +204,7 @@ template constexpr auto inline_sequence_base::take(num::integral auto count) && { return flux::take(std::move(derived()), count); -} + } } // namespace flux diff --git a/include/flux/adaptor/take_while.hpp b/include/flux/adaptor/take_while.hpp index 464968d0..1dbfa73b 100644 --- a/include/flux/adaptor/take_while.hpp +++ b/include/flux/adaptor/take_while.hpp @@ -12,7 +12,7 @@ namespace flux { namespace detail { -template +template struct take_while_adaptor : inline_sequence_base> { private: Base base_; @@ -23,33 +23,73 @@ struct take_while_adaptor : inline_sequence_base> friend struct sequence_traits; friend struct passthrough_traits_base; + template + struct context_type : immovable { + BaseCtx base_ctx; + TakePred take_pred; + bool done = false; + + using element_type = context_element_t; + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (!done) { + auto res = base_ctx.run_while([&](auto&& elem) { + if (!std::invoke(take_pred, std::as_const(elem))) { + done = true; + return loop_break; + } else { + return std::invoke(pred, FLUX_FWD(elem)); + } + }); + return static_cast(static_cast(res) || done); + } else { + return iteration_result::complete; + } + } + }; + public: constexpr take_while_adaptor(decays_to auto&& base, decays_to auto&& pred) : base_(FLUX_FWD(base)), pred_(FLUX_FWD(pred)) - {} + { + } [[nodiscard]] constexpr auto base() const& -> Base const& { return base_; } [[nodiscard]] constexpr auto base() && -> Base { return std::move(base_); } + + [[nodiscard]] + constexpr auto iterate() + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .take_pred = copy_or_ref(pred_)}; + } + + [[nodiscard]] + constexpr auto iterate() const + requires iterable + && std::predicate const&> + { + return context_type, copy_or_ref_t>{ + .base_ctx = flux::iterate(base_), .take_pred = copy_or_ref(pred_)}; + } }; struct take_while_fn { - template - requires std::predicate> + template + requires std::predicate const&> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const + constexpr auto operator()(It&& it, Pred pred) const { - return take_while_adaptor, Pred>( - FLUX_FWD(seq), std::move(pred)); + return take_while_adaptor, Pred>(FLUX_FWD(it), std::move(pred)); } }; } // namespace detail template -struct sequence_traits> - : detail::passthrough_traits_base -{ +struct sequence_traits> : detail::passthrough_traits_base { using self_t = detail::take_while_adaptor; using value_type = value_t; @@ -73,7 +113,7 @@ struct sequence_traits> static constexpr auto for_each_while(auto& self, auto&& func) { - return flux::for_each_while(self.base_, [&](auto&& elem) { + return flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (!std::invoke(self.pred_, elem)) { return false; } else { @@ -87,7 +127,7 @@ FLUX_EXPORT inline constexpr auto take_while = detail::take_while_fn{}; template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::take_while(Pred pred) && { return flux::take_while(std::move(derived()), std::move(pred)); diff --git a/include/flux/adaptor/zip.hpp b/include/flux/adaptor/zip.hpp index 67c756b1..fed36b13 100644 --- a/include/flux/adaptor/zip.hpp +++ b/include/flux/adaptor/zip.hpp @@ -7,7 +7,7 @@ #define FLUX_ADAPTOR_ZIP_HPP_INCLUDED #include -#include +#include #include // for std::min({ilist...}) @@ -92,8 +92,8 @@ struct zip_traits_base : default_sequence_traits { } template - requires (random_access_sequence> && ...) - static constexpr auto& inc(Self& self, cursor_t& cur, distance_t offset) + requires(random_access_sequence> && ...) + static constexpr auto& inc(Self& self, cursor_t& cur, int_t offset) { [&](std::index_sequence) { (flux::inc(std::get(self.bases_), std::get(cur), offset), ...); @@ -134,7 +134,7 @@ struct zip_traits_base : default_sequence_traits { namespace detail { -template +template struct zip_adaptor : inline_sequence_base> { private: pair_or_tuple_t bases_; @@ -142,26 +142,92 @@ struct zip_adaptor : inline_sequence_base> { friend struct sequence_traits; friend struct zip_traits_base; + template + struct context_type : immovable { + std::tuple base_ctxs; + + using element_type = pair_or_tuple_t...>; + + static constexpr auto run_while_impl(auto& pred, auto&... ctxs) -> iteration_result + { + return [&pred, &ctxs..., ... opts = flux::next_element(ctxs)]() mutable { + while (true) { + if (!(opts.has_value() && ...)) { + return iteration_result::complete; + } + if (!pred(element_type(std::move(opts).value_unchecked()...))) { + return iteration_result::incomplete; + } + ((opts = flux::next_element(ctxs)), ...); + } + }(); + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return std::apply([&pred](auto&... ctxs) { return run_while_impl(pred, ctxs...); }, + base_ctxs); + } + }; + public: + using value_type = pair_or_tuple_t...>; + constexpr explicit zip_adaptor(decays_to auto&&... bases) - : bases_(FLUX_FWD(bases)...) - {} + : bases_(FLUX_FWD(bases)...) { } + + constexpr auto iterate() -> context_type...> + { + return context_type...>{ + .base_ctxs = std::apply( + [&](auto&... bases) { + return std::tuple...>( + emplace_from([&] { return flux::iterate(bases); })...); + }, + bases_)}; + } + + constexpr auto iterate() const + requires(iterable && ...) + { + return context_type...>{ + .base_ctxs = std::apply( + [&](auto&... bases) { + return std::tuple...>( + emplace_from([&] { return flux::iterate(bases); })...); + }, + bases_)}; + } + + constexpr auto size() -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return std::min({flux::iterable_size(bases)...}); }, + bases_); + } + + constexpr auto size() const -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return std::min({flux::iterable_size(bases)...}); }, + bases_); + } }; struct zip_fn { - template + template [[nodiscard]] - constexpr auto operator()(Seqs&&... seqs) const + constexpr auto operator()(Its&&... its) const { - if constexpr (sizeof...(Seqs) == 0) { + if constexpr (sizeof...(Its) == 0) { return empty>; } else { - return zip_adaptor...>(FLUX_FWD(seqs)...); + return zip_adaptor...>(FLUX_FWD(its)...); } } }; -template +template struct zip_map_adaptor : inline_sequence_base> { private: pair_or_tuple_t bases_; @@ -170,31 +236,101 @@ struct zip_map_adaptor : inline_sequence_base> { friend struct sequence_traits; friend struct zip_traits_base; + template + struct context_type : immovable { + Fn fn; + std::tuple base_ctxs; + + using element_type = std::invoke_result_t...>; + + static constexpr auto run_while_impl(auto& fn, auto& pred, auto&... ctxs) + -> iteration_result + { + return [&fn, &pred, &ctxs..., ... opts = flux::next_element(ctxs)]() mutable { + while (true) { + if (!(opts.has_value() && ...)) { + return iteration_result::complete; + } + if (!pred(std::invoke(fn, std::move(opts).value_unchecked()...))) { + return iteration_result::incomplete; + } + ((opts = flux::next_element(ctxs)), ...); + } + }(); + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + return std::apply( + [&fn = fn, &pred](auto&... ctxs) { return run_while_impl(fn, pred, ctxs...); }, + base_ctxs); + } + }; + public: constexpr explicit zip_map_adaptor(Func&& func, decays_to auto&&... bases) : bases_(FLUX_FWD(bases)...), func_(std::move(func)) {} + + constexpr auto iterate() -> context_type, iteration_context_t...> + { + return context_type, iteration_context_t...>{ + .fn = copy_or_ref(func_), + .base_ctxs = std::apply( + [&](auto&... bases) { + return std::tuple...>( + emplace_from([&] { return flux::iterate(bases); })...); + }, + bases_)}; + } + + constexpr auto iterate() const + requires(iterable && ...) + && std::regular_invocable...> + { + return context_type, iteration_context_t...>{ + .fn = copy_or_ref(func_), + .base_ctxs = std::apply( + [&](auto&... bases) { + return std::tuple...>( + emplace_from([&] { return flux::iterate(bases); })...); + }, + bases_)}; + } + + constexpr auto size() -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return std::min({flux::iterable_size(bases)...}); }, + bases_); + } + + constexpr auto size() const -> int_t + requires(sized_iterable && ...) + { + return std::apply([](auto&... bases) { return std::min({flux::iterable_size(bases)...}); }, + bases_); + } }; struct zip_map_fn { - template - requires std::regular_invocable...> + template + requires std::regular_invocable...> [[nodiscard]] - constexpr auto operator()(Func func, Seqs&&... seqs) const + constexpr auto operator()(Func func, Its&&... its) const { - if constexpr (sizeof...(Seqs) == 0) { + if constexpr (sizeof...(Its) == 0) { return empty>; } else { - return zip_map_adaptor...>(std::move(func), FLUX_FWD(seqs)...); + return zip_map_adaptor...>(std::move(func), FLUX_FWD(its)...); } } }; } // namespace detail -template -struct sequence_traits> : zip_traits_base -{ +template +struct sequence_traits> : zip_traits_base { private: using base = zip_traits_base; @@ -243,7 +379,6 @@ struct sequence_traits> : zip_traits_base diff --git a/include/flux/algorithm.hpp b/include/flux/algorithm.hpp index 676d3830..9a507eb2 100644 --- a/include/flux/algorithm.hpp +++ b/include/flux/algorithm.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/flux/algorithm/all_any_none.hpp b/include/flux/algorithm/all_any_none.hpp index 6f0c8b44..6b18c3b6 100644 --- a/include/flux/algorithm/all_any_none.hpp +++ b/include/flux/algorithm/all_any_none.hpp @@ -6,64 +6,55 @@ #ifndef FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED #define FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED -#include +#include namespace flux { -namespace all_detail { - -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct all_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return is_last(seq, for_each_while(seq, [&](auto&& elem) { - return std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::complete; } }; -} // namespace all_detail - -FLUX_EXPORT inline constexpr auto all = all_detail::fn{}; - -namespace none_detail { +FLUX_EXPORT inline constexpr all_t all {}; -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct none_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return is_last(seq, for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::complete; } }; -} // namespace none_detail +FLUX_EXPORT inline constexpr none_t none {}; -FLUX_EXPORT inline constexpr auto none = none_detail::fn{}; - -namespace any_detail { - -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct any_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return !is_last(seq, for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::incomplete; } }; -} // namespace any_detail - -FLUX_EXPORT inline constexpr auto any = any_detail::fn{}; +FLUX_EXPORT inline constexpr any_t any {}; template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::all(Pred pred) { return flux::all(derived(), std::move(pred)); @@ -71,7 +62,7 @@ constexpr auto inline_sequence_base::all(Pred pred) template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::any(Pred pred) { return flux::any(derived(), std::move(pred)); @@ -79,7 +70,7 @@ constexpr auto inline_sequence_base::any(Pred pred) template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::none(Pred pred) { return flux::none(derived(), std::move(pred)); diff --git a/include/flux/algorithm/compare.hpp b/include/flux/algorithm/compare.hpp index 82a1b17e..639da076 100644 --- a/include/flux/algorithm/compare.hpp +++ b/include/flux/algorithm/compare.hpp @@ -14,90 +14,97 @@ namespace flux { -namespace detail { - -struct compare_fn { +FLUX_EXPORT +struct compare_t { private: - template - static constexpr auto impl(Seq1& seq1, Seq2& seq2, Cmp& cmp) - -> std::decay_t< - std::invoke_result_t, element_t>> + template + requires ordering_invocable, iterable_element_t> + static constexpr auto impl(It1& it1, It2& it2, Cmp& cmp) -> std::decay_t< + std::invoke_result_t, iterable_element_t>> { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + auto opt1 = next_element(ctx1); + auto opt2 = next_element(ctx2); - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - if (auto r = std::invoke(cmp, flux::read_at(seq1, cur1), flux::read_at(seq2, cur2)); - r != 0) { - return r; + if (opt1.has_value() && opt2.has_value()) { + auto r = std::invoke(cmp, opt1.value(), opt2.value()); + if (r != 0) { + return r; + } + } else if (opt1.has_value()) { + return std::strong_ordering::greater; + } else if (opt2.has_value()) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::equal; } - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); } + } - return !flux::is_last(seq1, cur1) ? std::strong_ordering::greater : - !flux::is_last(seq2, cur2) ? std::strong_ordering::less : - std::strong_ordering::equal; + template + static constexpr auto memcmp_impl(Seq1& seq1, Seq2& seq2) -> std::strong_ordering + { + auto const seq1_size = flux::usize(seq1); + auto const seq2_size = flux::usize(seq2); + auto min_size = (cmp::min)(seq1_size, seq2_size); + + int cmp_result = 0; + if (min_size > 0) { + auto data1 = flux::data(seq1); + FLUX_ASSERT(data1 != nullptr); + auto data2 = flux::data(seq2); + FLUX_ASSERT(data2 != nullptr); + + cmp_result = std::memcmp(data1, data2, min_size); + } + + if (cmp_result == 0) { + if (seq1_size == seq2_size) { + return std::strong_ordering::equal; + } else if (seq1_size < seq2_size) { + return std::strong_ordering::less; + } else /* seq1_size > seq2_size */ { + return std::strong_ordering::greater; + } + } else if (cmp_result > 0) { + return std::strong_ordering::greater; + } else /* cmp_result < 0 */ { + return std::strong_ordering::less; + } + } + + template + static consteval auto can_memcmp() -> bool + { + return std::same_as && contiguous_sequence + && contiguous_sequence && sized_sequence && sized_sequence + && std::same_as, iterable_value_t> + && std::unsigned_integral> + && ((sizeof(iterable_value_t) == 1) || (std::endian::native == std::endian::big)); } public: - template - requires ordering_invocable, element_t> - constexpr auto operator()(Seq1 &&seq1, Seq2 &&seq2, Cmp cmp = {}) const - -> std::decay_t< - std::invoke_result_t, element_t>> + template + requires ordering_invocable, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2, Cmp cmp = {}) const -> std::decay_t< + std::invoke_result_t, iterable_element_t>> { - constexpr bool can_memcmp = - std::same_as && - contiguous_sequence && - contiguous_sequence && - sized_sequence && - sized_sequence && - std::same_as, value_t> && - std::unsigned_integral> && - ((sizeof(value_t) == 1) || (std::endian::native == std::endian::big)); - - if constexpr (can_memcmp) { + if constexpr (can_memcmp()) { if (std::is_constant_evaluated()) { - return impl(seq1, seq2, cmp); // LCOV_EXCL_LINE + return impl(it1, it2, cmp); // LCOV_EXCL_LINE } else { - auto const seq1_size = flux::usize(seq1); - auto const seq2_size = flux::usize(seq2); - auto min_size = (cmp::min)(seq1_size, seq2_size); - - int cmp_result = 0; - if(min_size > 0) { - auto data1 = flux::data(seq1); - FLUX_ASSERT(data1 != nullptr); - auto data2 = flux::data(seq2); - FLUX_ASSERT(data2 != nullptr); - - cmp_result = std::memcmp(data1, data2, min_size); - } - - if (cmp_result == 0) { - if (seq1_size == seq2_size) { - return std::strong_ordering::equal; - } else if (seq1_size < seq2_size) { - return std::strong_ordering::less; - } else /* seq1_size > seq2_size */ { - return std::strong_ordering::greater; - } - } else if (cmp_result > 0) { - return std::strong_ordering::greater; - } else /* cmp_result < 0 */ { - return std::strong_ordering::less; - } + return memcmp_impl(it1, it2); } } else { - return impl(seq1, seq2, cmp); + return impl(it1, it2, cmp); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto compare = detail::compare_fn{}; +FLUX_EXPORT inline constexpr auto compare = compare_t{}; } // namespace flux diff --git a/include/flux/algorithm/contains.hpp b/include/flux/algorithm/contains.hpp index 95ec9ff9..52eb6e21 100644 --- a/include/flux/algorithm/contains.hpp +++ b/include/flux/algorithm/contains.hpp @@ -6,32 +6,26 @@ #ifndef FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED #define FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct contains_fn { - template - requires std::equality_comparable_with, Value const&> - constexpr auto operator()(Seq&& seq, Value const& value) const - -> bool +FLUX_EXPORT +struct contains_t { + template + requires std::equality_comparable_with, Value const&> + [[nodiscard]] + constexpr auto operator()(It&& it, Value const& value) const -> bool { - return !flux::is_last(seq, flux::for_each_while(seq, [&](auto&& elem) { - return FLUX_FWD(elem) != value; - })); + return any(it, [&](auto&& elem) { return FLUX_FWD(elem) == value; }); } }; - -} // namespace detail - -FLUX_EXPORT inline constexpr auto contains = detail::contains_fn{}; +FLUX_EXPORT inline constexpr contains_t contains {}; template template - requires std::equality_comparable_with, Value const&> + requires std::equality_comparable_with, Value const&> constexpr auto inline_sequence_base::contains(Value const& value) -> bool { return flux::contains(derived(), value); diff --git a/include/flux/algorithm/count.hpp b/include/flux/algorithm/count.hpp index 756fd079..2d46e303 100644 --- a/include/flux/algorithm/count.hpp +++ b/include/flux/algorithm/count.hpp @@ -6,71 +6,61 @@ #ifndef FLUX_ALGORITHM_COUNT_HPP_INCLUDED #define FLUX_ALGORITHM_COUNT_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct count_fn { - template +FLUX_EXPORT +struct count_t { + template [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> distance_t + constexpr auto operator()(It&& it) const -> int_t { - if constexpr (sized_sequence) { - return flux::size(seq); + if constexpr (sized_iterable) { + return flux::iterable_size(it); } else { - distance_t counter = 0; - flux::for_each_while(seq, [&](auto&&) { - ++counter; - return true; - }); + int_t counter = 0; + // LCOV_EXCL_START + for_each(it, [&](auto&&) { counter = num::add(counter, int_t{1}); }); + // LCOV_EXCL_STOP return counter; } } }; -struct count_eq_fn { - template - requires std::equality_comparable_with, Value const&> +FLUX_EXPORT inline constexpr count_t count{}; + +FLUX_EXPORT +struct count_if_t { + template + requires std::predicate> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Value const& value) const - -> distance_t + constexpr auto operator()(It&& it, Pred pred) const -> int_t { - distance_t counter = 0; - flux::for_each_while(seq, [&](auto&& elem) { - if (value == FLUX_FWD(elem)) { + int_t counter = 0; + for_each(it, [&](auto&& elem) { + if (std::invoke(pred, FLUX_FWD(elem))) { ++counter; } - return true; }); return counter; } }; -struct count_if_fn { - template - requires std::predicate> +FLUX_EXPORT inline constexpr count_if_t count_if{}; + +FLUX_EXPORT +struct count_eq_t { + template + requires std::equality_comparable_with, Value const&> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const - -> distance_t + constexpr auto operator()(It&& it, Value const& value) const -> int_t { - distance_t counter = 0; - flux::for_each_while(seq, [&](auto&& elem) { - if (std::invoke(pred, FLUX_FWD(elem))) { - ++counter; - } - return true; - }); - return counter; + return count_if(it, [&](auto&& elem) { return value == FLUX_FWD(elem); }); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto count = detail::count_fn{}; -FLUX_EXPORT inline constexpr auto count_eq = detail::count_eq_fn{}; -FLUX_EXPORT inline constexpr auto count_if = detail::count_if_fn{}; +FLUX_EXPORT inline constexpr count_eq_t count_eq{}; template constexpr auto inline_sequence_base::count() @@ -80,7 +70,7 @@ constexpr auto inline_sequence_base::count() template template - requires std::equality_comparable_with, Value const&> + requires std::equality_comparable_with, Value const&> constexpr auto inline_sequence_base::count_eq(Value const& value) { return flux::count_eq(derived(), value); @@ -88,7 +78,7 @@ constexpr auto inline_sequence_base::count_eq(Value const& value) template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::count_if(Pred pred) { return flux::count_if(derived(), std::move(pred)); diff --git a/include/flux/algorithm/detail/heap_ops.hpp b/include/flux/algorithm/detail/heap_ops.hpp index 43fcd0d4..e97de05a 100644 --- a/include/flux/algorithm/detail/heap_ops.hpp +++ b/include/flux/algorithm/detail/heap_ops.hpp @@ -33,7 +33,7 @@ namespace flux::detail { template -constexpr void sift_up_n(Seq& seq, distance_t n, Comp& comp) +constexpr void sift_up_n(Seq& seq, int_t n, Comp& comp) { cursor_t first = flux::first(seq); @@ -58,8 +58,7 @@ constexpr void sift_up_n(Seq& seq, distance_t n, Comp& comp) } template -constexpr void sift_down_n(Seq& seq, distance_t n, cursor_t start, - Comp& comp) +constexpr void sift_down_n(Seq& seq, int_t n, cursor_t start, Comp& comp) { cursor_t first = flux::first(seq); @@ -120,7 +119,7 @@ constexpr void sift_down_n(Seq& seq, distance_t n, cursor_t start, template constexpr void make_heap(Seq& seq, Comp& comp) { - distance_t n = flux::size(seq); + int_t n = flux::size(seq); auto first = flux::first(seq); if (n > 1) { @@ -131,7 +130,7 @@ constexpr void make_heap(Seq& seq, Comp& comp) } template -constexpr void pop_heap(Seq& seq, distance_t n, Comp& comp) +constexpr void pop_heap(Seq& seq, int_t n, Comp& comp) { auto first = flux::first(seq); if (n > 1) { diff --git a/include/flux/algorithm/detail/pdqsort.hpp b/include/flux/algorithm/detail/pdqsort.hpp index 31753f21..b60c9390 100644 --- a/include/flux/algorithm/detail/pdqsort.hpp +++ b/include/flux/algorithm/detail/pdqsort.hpp @@ -128,7 +128,7 @@ constexpr bool partial_insertion_sort(Seq& seq, Cur const begin, Cur const end, return true; } - distance_t limit = 0; + int_t limit = 0; for (auto cur = next(seq, begin); cur != end; inc(seq, cur)) { if (limit > pqdsort_partial_insertion_sort_limit) { @@ -322,9 +322,8 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) inc(seq, last, -pdqsort_block_size); } - distance_t l_size = 0, r_size = 0; - distance_t unknown_left = - distance(seq, first, last) - ((num_r || num_l) ? pdqsort_block_size : 0); + int_t l_size = 0, r_size = 0; + int_t unknown_left = distance(seq, first, last) - ((num_r || num_l) ? pdqsort_block_size : 0); if (num_r) { // Handle leftover block by assigning the unknown elements to the other // block. @@ -343,7 +342,7 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) if (unknown_left && !num_l) { start_l = 0; Cur cur = first; - for (unsigned char i = 0; static_cast(i) < l_size;) { + for (unsigned char i = 0; static_cast(i) < l_size;) { offsets_l[num_l] = i++; num_l += !comp(read_at(seq, cur), pivot); inc(seq, cur); @@ -352,7 +351,7 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) if (unknown_left && !num_r) { start_r = 0; Cur cur = last; - for (unsigned char i = 0; static_cast(i) < r_size;) { + for (unsigned char i = 0; static_cast(i) < r_size;) { offsets_r[num_r] = ++i; num_r += comp(read_at(seq, dec(seq, cur)), pivot); } @@ -496,7 +495,7 @@ template +#include #include #include +#include namespace flux { -namespace detail { - -struct ends_with_fn { -private: - template - static constexpr auto bidir_impl(H& h, N& n, auto& cmp) -> bool +FLUX_EXPORT +struct ends_with_t { + template + requires std::predicate, iterable_element_t> + && (multipass_sequence || sized_iterable) + && (multipass_sequence || sized_iterable) + [[nodiscard]] + constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(h) < flux::size(n)) { - return false; - } - } - - auto cur1 = flux::last(h); - auto cur2 = flux::last(n); - - auto const f1 = flux::first(h); - auto const f2 = flux::first(n); - - if (cur2 == f2) { - return true; - } else if (cur1 == f1) { - return false; - } - - while (true) { - flux::dec(h, cur1); - flux::dec(n, cur2); - - if (!std::invoke(cmp, flux::read_at(h, cur1), flux::read_at(n, cur2))) { - return false; - } - - if (cur2 == f2) { - return true; - } else if (cur1 == f1) { - return false; - } - } - } - -public: - template - requires std::predicate, element_t> && - (multipass_sequence || sized_sequence) && - (multipass_sequence || sized_sequence) - constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const - -> bool - { - if constexpr(bidirectional_sequence && - bounded_sequence && - bidirectional_sequence && - bounded_sequence) { - return bidir_impl(haystack, needle, cmp); + if constexpr (reverse_iterable && reverse_iterable) { + return starts_with(reverse(from_fwd_ref(haystack)), reverse(from_fwd_ref(needle)), + std::ref(cmp)); } else { - distance_t len1 = flux::count(haystack); - distance_t len2 = flux::count(needle); + int_t haystack_size = flux::count(haystack); + int_t needle_size = flux::count(needle); - if (len1 < len2) { + if (haystack_size < needle_size) { return false; } - auto cur1 = flux::first(haystack); - detail::advance(haystack, cur1, len1 - len2); - - return flux::equal(flux::slice(haystack, std::move(cur1), flux::last), - needle, std::move(cmp)); + return equal(drop(from_fwd_ref(haystack), num::sub(haystack_size, needle_size)), needle, + std::ref(cmp)); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto ends_with = detail::ends_with_fn{}; +FLUX_EXPORT inline constexpr auto ends_with = ends_with_t{}; template template - requires std::predicate, element_t> && - (multipass_sequence || sized_sequence) && - (multipass_sequence || sized_sequence) + requires std::predicate, iterable_element_t> + && (multipass_sequence || sized_sequence) + && (multipass_sequence || sized_sequence) constexpr auto inline_sequence_base::ends_with(Needle&& needle, Cmp cmp) -> bool { return flux::ends_with(derived(), FLUX_FWD(needle), std::move(cmp)); diff --git a/include/flux/algorithm/equal.hpp b/include/flux/algorithm/equal.hpp index 2ba36888..89370ce5 100644 --- a/include/flux/algorithm/equal.hpp +++ b/include/flux/algorithm/equal.hpp @@ -12,89 +12,97 @@ namespace flux { -namespace detail { - -struct equal_fn { +FLUX_EXPORT +struct equal_t { private: - template - static constexpr auto impl(Seq1& seq1, Seq2& seq2, Cmp cmp) + template + static constexpr auto impl(It1& it1, It2& it2, Cmp& cmp) -> bool { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + flux::optional opt1 = next_element(ctx1); + flux::optional opt2 = next_element(ctx2); - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - if (!std::invoke(cmp, flux::read_at(seq1, cur1), flux::read_at(seq2, cur2))) { + if (opt1.has_value() && opt2.has_value()) { + if (!std::invoke(cmp, opt1.value(), opt2.value())) { + return false; + } + } else if (opt1.has_value() || opt2.has_value()) { return false; + } else { + return true; } - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); } + } + + template + static constexpr auto memcmp_impl(Seq1& seq1, Seq2& seq2) -> bool + { + auto size = flux::usize(seq1); + if (size == 0) { + return true; + } + + auto data1 = flux::data(seq1); + auto data2 = flux::data(seq2); + FLUX_ASSERT(data1 != nullptr); + FLUX_ASSERT(data2 != nullptr); - return flux::is_last(seq1, cur1) == flux::is_last(seq2, cur2); + auto result = std::memcmp(data1, data2, size * sizeof(iterable_value_t)); + return result == 0; + } + + template + static consteval auto can_memcmp() -> bool + { + return std::same_as && contiguous_sequence + && contiguous_sequence && sized_sequence && sized_sequence + && std::same_as, iterable_value_t> + && (std::integral> || std::is_pointer_v>) + && std::has_unique_object_representations_v>; } public: - template - requires std::predicate, element_t> - constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const - -> bool + template + requires std::predicate, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2, Cmp cmp = {}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(seq1) != flux::size(seq2)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(it1) != flux::iterable_size(it2)) { return false; } } - constexpr bool can_memcmp = - std::same_as && - contiguous_sequence && contiguous_sequence && - sized_sequence && sized_sequence && - std::same_as, value_t> && - (std::integral> || std::is_pointer_v>) && - std::has_unique_object_representations_v>; - - if constexpr (can_memcmp) { + if constexpr (can_memcmp()) { if (std::is_constant_evaluated()) { - return impl(seq1, seq2, cmp); // LCOV_EXCL_LINE + return impl(it1, it2, cmp); // LCOV_EXCL_LINE } else { - auto size = flux::usize(seq1); - if(size == 0) { - return true; - } - - auto data1 = flux::data(seq1); - auto data2 = flux::data(seq2); - FLUX_ASSERT(data1 != nullptr); - FLUX_ASSERT(data2 != nullptr); - - auto result = std::memcmp(data1, data2, size * sizeof(value_t)); - return result == 0; + return memcmp_impl(it1, it2); } } else { - return impl(seq1, seq2, cmp); + return impl(it1, it2, cmp); } } - template - requires (sequence> && - sequence> && - !std::equality_comparable_with, element_t> && - std::is_invocable_v) - constexpr auto operator()(Seq1&& seq1, Seq2&& seq2) const -> bool + template + requires iterable> && iterable> + && (!std::equality_comparable_with, iterable_element_t> + && std::is_invocable_v) + constexpr auto operator()(It1&& it1, It2&& it2) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(seq1) != flux::size(seq2)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(it1) != flux::iterable_size(it2)) { return false; } } - return (*this)(seq1, seq2, *this); + return (*this)(it1, it2, *this); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto equal = detail::equal_fn{}; +FLUX_EXPORT inline constexpr equal_t equal{}; } // namespace flux diff --git a/include/flux/algorithm/fill.hpp b/include/flux/algorithm/fill.hpp index 71c57066..94c3c6c5 100644 --- a/include/flux/algorithm/fill.hpp +++ b/include/flux/algorithm/fill.hpp @@ -11,51 +11,49 @@ namespace flux { -namespace detail { - -struct fill_fn { +FLUX_EXPORT +struct fill_t { private: + template + static constexpr auto impl(It&& it, Value const& value) -> void + { + flux::for_each(it, [&value](auto&& elem) { FLUX_FWD(elem) = value; }); + } + template - static constexpr auto impl(Seq& seq, Value const& value) + static constexpr auto memset_impl(Seq& seq, Value const& value) -> void { - flux::for_each(seq, [&value](auto&& elem) { FLUX_FWD(elem) = value; }); + if (std::is_constant_evaluated()) { + impl(seq, value); // LCOV_EXCL_LINE + } else { + auto size = flux::usize(seq); + if (size == 0) { + return; + } + + FLUX_ASSERT(flux::data(seq) != nullptr); + + std::memset(flux::data(seq), value, size * sizeof(value_t)); + } } public: - template Seq> - constexpr void operator()(Seq&& seq, Value const& value) const + template + requires std::assignable_from, Value const&> + constexpr auto operator()(It&& it, Value const& value) const -> void { - constexpr bool can_memset = - contiguous_sequence && - sized_sequence && - std::same_as> && - // only allow memset on single byte types - sizeof(value_t) == 1 && - std::is_trivially_copyable_v>; - - if constexpr (can_memset) { - if (std::is_constant_evaluated()) { - impl(seq, value); // LCOV_EXCL_LINE - } else { - auto size = flux::usize(seq); - if(size == 0) { - return; - } - - FLUX_ASSERT(flux::data(seq) != nullptr); - - std::memset(flux::data(seq), value, - size * sizeof(value_t)); - } + if constexpr (contiguous_sequence && sized_sequence + && std::same_as> + // only allow memset on single byte types + && sizeof(value_t) == 1 && std::is_trivially_copyable_v>) { + memset_impl(it, value); } else { - impl(seq, value); + impl(it, value); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto fill = detail::fill_fn{}; +FLUX_EXPORT inline constexpr fill_t fill {}; template template diff --git a/include/flux/algorithm/find.hpp b/include/flux/algorithm/find.hpp index 06e0cfd9..d1795cea 100644 --- a/include/flux/algorithm/find.hpp +++ b/include/flux/algorithm/find.hpp @@ -20,9 +20,7 @@ struct find_fn { template static constexpr auto impl(Seq&& seq, Value const& value) -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return FLUX_FWD(elem) != value; - }); + return seq_for_each_while(seq, [&](auto&& elem) { return FLUX_FWD(elem) != value; }); } public: @@ -30,10 +28,10 @@ struct find_fn { requires std::equality_comparable_with, Value const&> constexpr auto operator()(Seq&& seq, Value const& value) const -> cursor_t { - constexpr auto can_memchr = - contiguous_sequence && sized_sequence && - std::same_as> && - flux::detail::any_of, char, signed char, unsigned char, char8_t, std::byte>; + constexpr auto can_memchr = contiguous_sequence && sized_sequence + && std::same_as> + && flux::detail::any_of, char, signed char, unsigned char, char8_t, + std::byte>; if constexpr (can_memchr) { if (std::is_constant_evaluated()) { @@ -65,9 +63,8 @@ struct find_if_fn { constexpr auto operator()(Seq&& seq, Pred pred) const -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - }); + return seq_for_each_while(seq, + [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }); } }; @@ -77,9 +74,8 @@ struct find_if_not_fn { constexpr auto operator()(Seq&& seq, Pred pred) const -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return std::invoke(pred, FLUX_FWD(elem)); - }); + return seq_for_each_while(seq, + [&](auto&& elem) { return std::invoke(pred, FLUX_FWD(elem)); }); } }; @@ -91,7 +87,7 @@ FLUX_EXPORT inline constexpr auto find_if_not = detail::find_if_not_fn{}; template template - requires std::equality_comparable_with, Value const&> + requires std::equality_comparable_with, Value const&> constexpr auto inline_sequence_base::find(Value const& val) { return flux::find(derived(), val); @@ -99,7 +95,7 @@ constexpr auto inline_sequence_base::find(Value const& val) template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::find_if(Pred pred) { return flux::find_if(derived(), std::ref(pred)); @@ -107,7 +103,7 @@ constexpr auto inline_sequence_base::find_if(Pred pred) template template - requires std::predicate> + requires std::predicate> constexpr auto inline_sequence_base::find_if_not(Pred pred) { return flux::find_if_not(derived(), std::ref(pred)); diff --git a/include/flux/algorithm/find_element.hpp b/include/flux/algorithm/find_element.hpp new file mode 100644 index 00000000..331e7d86 --- /dev/null +++ b/include/flux/algorithm/find_element.hpp @@ -0,0 +1,42 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED +#define FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED + +#include + +namespace flux { + +FLUX_EXPORT +struct find_element_if_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred pred) const + { + iterable auto filtered = filter(std::ref(it), std::ref(pred)); + iteration_context auto ctx = iterate(filtered); + return next_element(ctx); + } +}; + +FLUX_EXPORT inline constexpr find_element_if_t find_element_if{}; + +FLUX_EXPORT +struct find_element_t { + template + requires std::equality_comparable_with, Value const&> + [[nodiscard]] + constexpr auto operator()(It&& it, Value const& value) const + { + return find_element_if(it, [&value](auto&& elem) { return FLUX_FWD(elem) == value; }); + } +}; + +FLUX_EXPORT inline constexpr find_element_t find_element{}; + +} // namespace flux + +#endif // FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED diff --git a/include/flux/algorithm/fold.hpp b/include/flux/algorithm/fold.hpp index 8e01e75b..c237a63b 100644 --- a/include/flux/algorithm/fold.hpp +++ b/include/flux/algorithm/fold.hpp @@ -6,23 +6,22 @@ #ifndef FLUX_ALGORITHM_FOLD_HPP_INCLUDED #define FLUX_ALGORITHM_FOLD_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct fold_op { - template , - typename R = fold_result_t> - requires std::invocable> && - std::invocable> && - std::convertible_to && - std::assignable_from>> - constexpr auto operator()(Seq&& seq, Func func, Init init = Init{}) const -> R +FLUX_EXPORT +struct fold_t { + template , + typename R = fold_result_t> + requires std::invocable> + && std::invocable> && std::convertible_to + && std::assignable_from>> + [[nodiscard]] + constexpr auto operator()(It&& it, Func func, Init init = Init{}) const -> R { R init_ = R(std::move(init)); - flux::for_each_while(seq, [&func, &init_](auto&& elem) { + for_each_while(it, [&func, &init_](auto&& elem) { init_ = std::invoke(func, std::move(init_), FLUX_FWD(elem)); return true; }); @@ -30,31 +29,39 @@ struct fold_op { } }; -struct fold_first_op { - template > - requires std::invocable> && - std::assignable_from&, std::invoke_result_t>> +FLUX_EXPORT inline constexpr auto fold = fold_t{}; + +FLUX_EXPORT +struct fold_first_t { + template > + requires std::invocable> + && std::assignable_from&, + std::invoke_result_t>> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func) const -> flux::optional + constexpr auto operator()(It&& it, Func func) const -> flux::optional { - auto cur = flux::first(seq); + iteration_context auto ctx = iterate(it); - if (flux::is_last(seq, cur)) { - return std::nullopt; - } - - V init(flux::read_at(seq, cur)); - flux::inc(seq, cur); + auto opt = next_element(ctx); - while (!flux::is_last(seq, cur)) { - init = std::invoke(func, std::move(init), flux::read_at(seq, cur)); - flux::inc(seq, cur); + if (!opt.has_value()) { + return flux::nullopt; } + V init = std::move(opt).value(); + run_while(ctx, [&](auto&& elem) { + init = std::invoke(func, std::move(init), FLUX_FWD(elem)); + return loop_continue; + }); + return flux::optional(std::in_place, std::move(init)); } }; +FLUX_EXPORT inline constexpr fold_first_t fold_first{}; + +namespace detail { + // Workaround libc++18 invoke() bug: https://github.com/llvm/llvm-project/issues/106428 consteval bool libcpp_fold_invoke_workaround_required() { @@ -65,66 +72,68 @@ consteval bool libcpp_fold_invoke_workaround_required() #endif } -struct sum_op { - template - requires std::default_initializable> && - std::invocable> +} // namespace detail + +FLUX_EXPORT +struct sum_t { + template + requires std::invocable> && requires { iterable_value_t(0); } [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> value_t + constexpr auto operator()(It&& it) const -> iterable_value_t { - if constexpr (num::integral>) { - if constexpr (libcpp_fold_invoke_workaround_required()) { + if constexpr (num::integral>) { + if constexpr (detail::libcpp_fold_invoke_workaround_required()) { auto add = [](T lhs, T rhs) -> T { return num::add(lhs, rhs); }; - return fold_op{}(FLUX_FWD(seq), add, value_t(0)); + return fold(FLUX_FWD(it), add, iterable_value_t(0)); } else { - return fold_op{}(FLUX_FWD(seq), num::add, value_t(0)); + return fold(FLUX_FWD(it), num::add, iterable_value_t(0)); } } else { - return fold_op{}(FLUX_FWD(seq), std::plus<>{}, value_t(0)); + return fold(FLUX_FWD(it), std::plus<>{}, iterable_value_t(0)); } } }; -struct product_op { - template - requires std::invocable> && - requires { value_t(1); } +FLUX_EXPORT inline constexpr auto sum = sum_t{}; + +FLUX_EXPORT +struct product_t { + template + requires std::invocable> + && requires { iterable_value_t(1); } [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> value_t + constexpr auto operator()(It&& it) const -> iterable_value_t { - if constexpr (num::integral>) { - if constexpr (libcpp_fold_invoke_workaround_required()) { + if constexpr (num::integral>) { + if constexpr (detail::libcpp_fold_invoke_workaround_required()) { auto mul = [](T lhs, T rhs) -> T { return num::mul(lhs, rhs); }; - return fold_op{}(FLUX_FWD(seq), mul, value_t(1)); + return fold(FLUX_FWD(it), mul, iterable_value_t(1)); } else { - return fold_op{}(FLUX_FWD(seq), num::mul, value_t(1)); + return fold(FLUX_FWD(it), num::mul, iterable_value_t(1)); } } else { - return fold_op{}(FLUX_FWD(seq), std::multiplies<>{}, value_t(1)); + return fold(FLUX_FWD(it), std::multiplies<>{}, iterable_value_t(1)); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto fold = detail::fold_op{}; -FLUX_EXPORT inline constexpr auto fold_first = detail::fold_first_op{}; -FLUX_EXPORT inline constexpr auto sum = detail::sum_op{}; -FLUX_EXPORT inline constexpr auto product = detail::product_op{}; +FLUX_EXPORT inline constexpr auto product = product_t{}; template template requires foldable [[nodiscard]] -constexpr auto inline_sequence_base::fold(Func func, Init init) -> fold_result_t +constexpr auto inline_sequence_base::fold(Func func, Init init) + -> fold_result_t { return flux::fold(derived(), std::move(func), std::move(init)); } template template - requires std::invocable, element_t> && - std::assignable_from&, std::invoke_result_t, element_t>> + requires std::invocable, iterable_element_t> + && std::assignable_from&, + std::invoke_result_t, iterable_element_t>> constexpr auto inline_sequence_base::fold_first(Func func) { return flux::fold_first(derived(), std::move(func)); @@ -132,16 +141,16 @@ constexpr auto inline_sequence_base::fold_first(Func func) template constexpr auto inline_sequence_base::sum() - requires foldable, value_t> && - std::default_initializable> + requires foldable, iterable_value_t> + && std::default_initializable> { return flux::sum(derived()); } template constexpr auto inline_sequence_base::product() - requires foldable, value_t> && - requires { value_t(1); } + requires foldable, iterable_value_t> + && requires { iterable_value_t(1); } { return flux::product(derived()); } diff --git a/include/flux/algorithm/for_each.hpp b/include/flux/algorithm/for_each.hpp index 43a59917..a1dc7675 100644 --- a/include/flux/algorithm/for_each.hpp +++ b/include/flux/algorithm/for_each.hpp @@ -6,34 +6,29 @@ #ifndef FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED #define FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct for_each_fn { - - template - requires (std::invocable> && - !infinite_sequence) - constexpr auto operator()(Seq&& seq, Func func) const -> Func +FLUX_EXPORT +struct for_each_t { + template + requires std::invocable> + constexpr auto operator()(It&& it, Func func) const -> Func { - (void) flux::for_each_while(FLUX_FWD(seq), [&](auto&& elem) { - std::invoke(func, FLUX_FWD(elem)); - return true; + for_each_while(it, [&](auto&& elem) { + static_cast(std::invoke(func, FLUX_FWD(elem))); + return loop_continue; }); return func; } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto for_each = detail::for_each_fn{}; +FLUX_EXPORT inline constexpr for_each_t for_each {}; template template - requires std::invocable> + requires std::invocable> constexpr auto inline_sequence_base::for_each(Func func) -> Func { return flux::for_each(derived(), std::move(func)); diff --git a/include/flux/algorithm/for_each_while.hpp b/include/flux/algorithm/for_each_while.hpp new file mode 100644 index 00000000..b8d5ba94 --- /dev/null +++ b/include/flux/algorithm/for_each_while.hpp @@ -0,0 +1,26 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED +#define FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED + +#include + +namespace flux { + +FLUX_EXPORT +struct for_each_while_t { + template )> Pred> + constexpr auto operator()(It&& it, Pred&& pred) const -> iteration_result + { + iteration_context auto ctx = iterate(it); + return ctx.run_while(FLUX_FWD(pred)); + } +}; + +FLUX_EXPORT inline constexpr for_each_while_t for_each_while {}; + +} // namespace flux + +#endif // FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED \ No newline at end of file diff --git a/include/flux/algorithm/minmax.hpp b/include/flux/algorithm/minmax.hpp index 52018419..0f0829f7 100644 --- a/include/flux/algorithm/minmax.hpp +++ b/include/flux/algorithm/minmax.hpp @@ -19,17 +19,16 @@ struct minmax_result { T max; }; -namespace detail { - -struct min_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT +struct min_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional> { - return flux::fold_first(FLUX_FWD(seq), [&](auto min, auto&& elem) -> value_t { + return fold_first(FLUX_FWD(it), [&](auto min, auto&& elem) -> iterable_value_t { if (std::invoke(cmp, elem, min) < 0) { - return value_t(FLUX_FWD(elem)); + return iterable_value_t(FLUX_FWD(elem)); } else { return min; } @@ -37,15 +36,18 @@ struct min_op { } }; -struct max_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT inline constexpr min_t min{}; + +FLUX_EXPORT +struct max_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional> { - return flux::fold_first(FLUX_FWD(seq), [&](auto max, auto&& elem) -> value_t { + return fold_first(FLUX_FWD(it), [&](auto max, auto&& elem) -> iterable_value_t { if (!(std::invoke(cmp, elem, max) < 0)) { - return value_t(FLUX_FWD(elem)); + return iterable_value_t(FLUX_FWD(elem)); } else { return max; } @@ -53,42 +55,42 @@ struct max_op { } }; -struct minmax_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT inline constexpr max_t max{}; + +FLUX_EXPORT +struct minmax_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional>> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional>> { - using R = minmax_result>; + using R = minmax_result>; - auto cur = flux::first(seq); - if (flux::is_last(seq, cur)) { - return std::nullopt; + iteration_context auto ctx = iterate(it); + + auto opt = next_element(ctx); + if (!opt.has_value()) { + return flux::nullopt; } - R init = R{value_t(flux::read_at(seq, cur)), - value_t(flux::read_at(seq, cur))}; + auto min = iterable_value_t(opt.value()); + auto max = iterable_value_t(std::move(opt).value()); - auto fold_fn = [&](R mm, auto&& elem) -> R { - if (std::invoke(cmp, elem, mm.min) < 0) { - mm.min = value_t(elem); + run_while(ctx, [&](auto&& elem) { + if (std::invoke(cmp, elem, min) < 0) { + min = iterable_value_t(elem); } - if (!(std::invoke(cmp, elem, mm.max) < 0)) { - mm.max = value_t(FLUX_FWD(elem)); + if (!(std::invoke(cmp, elem, max) < 0)) { + max = iterable_value_t(FLUX_FWD(elem)); } - return mm; - }; + return loop_continue; + }); - return flux::optional(std::in_place, - flux::fold(flux::slice(seq, std::move(cur), flux::last), fold_fn, std::move(init))); + return flux::optional(R(std::move(min), std::move(max))); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto min = detail::min_op{}; -FLUX_EXPORT inline constexpr auto max = detail::max_op{}; -FLUX_EXPORT inline constexpr auto minmax = detail::minmax_op{}; +FLUX_EXPORT inline constexpr minmax_t minmax{}; template template diff --git a/include/flux/algorithm/output_to.hpp b/include/flux/algorithm/output_to.hpp index 3815df7f..ea439c42 100644 --- a/include/flux/algorithm/output_to.hpp +++ b/include/flux/algorithm/output_to.hpp @@ -13,43 +13,49 @@ namespace flux { -namespace detail { - -struct output_to_fn { +FLUX_EXPORT +struct output_to_t { private: template - static constexpr auto impl(Seq& seq, Iter& iter) -> Iter + static constexpr auto impl(Seq& seq, Iter iter) -> Iter { - flux::for_each(seq, [&iter](auto&& elem) { + for_each(seq, [&iter](auto&& elem) { *iter = FLUX_FWD(elem); ++iter; }); return iter; } + template + static consteval auto can_memcpy() -> bool + { + return contiguous_sequence && sized_sequence && std::contiguous_iterator + && std::is_trivially_copyable_v>; + } + + template + static constexpr auto memcpy_impl(Seq& seq, Iter iter) -> Iter + { + auto size = flux::usize(seq); + if (size == 0) { + return iter; + } + FLUX_ASSERT(flux::data(seq) != nullptr); + std::memmove(std::to_address(iter), flux::data(seq), size * sizeof(value_t)); + return iter + num::cast>(flux::size(seq)); + } + public: - template - requires std::indirectly_writable> + template + requires std::weakly_incrementable + && std::indirectly_writable> constexpr auto operator()(Seq&& seq, Iter iter) const -> Iter { - constexpr bool can_memcpy = - contiguous_sequence && - sized_sequence && - std::contiguous_iterator && - std::is_trivially_copyable_v>; - - if constexpr (can_memcpy) { + if constexpr (can_memcpy()) { if (std::is_constant_evaluated()) { return impl(seq, iter); // LCOV_EXCL_LINE } else { - auto size = flux::usize(seq); - if (size == 0) { - return iter; - } - FLUX_ASSERT(flux::data(seq) != nullptr); - std::memmove(std::to_address(iter), flux::data(seq), - size * sizeof(value_t)); - return iter + num::checked_cast>(flux::size(seq)); + return memcpy_impl(seq, iter); } } else { return impl(seq, iter); @@ -57,19 +63,16 @@ struct output_to_fn { } }; -} - -FLUX_EXPORT inline constexpr auto output_to = detail::output_to_fn{}; +FLUX_EXPORT inline constexpr output_to_t output_to{}; template template - requires std::weakly_incrementable && - std::indirectly_writable> + requires std::weakly_incrementable + && std::indirectly_writable> constexpr auto inline_sequence_base::output_to(Iter iter) -> Iter { return flux::output_to(derived(), std::move(iter)); } - } #endif // FLUX_ALGORITHM_OUTPUT_TO_HPP_INCLUDED diff --git a/include/flux/algorithm/starts_with.hpp b/include/flux/algorithm/starts_with.hpp index 5ebf88bd..1ab01fc1 100644 --- a/include/flux/algorithm/starts_with.hpp +++ b/include/flux/algorithm/starts_with.hpp @@ -10,41 +10,44 @@ namespace flux { -namespace detail { - -struct starts_with_fn { - template +FLUX_EXPORT +struct starts_with_t { + template requires std::predicate, element_t> + [[nodiscard]] constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(haystack) < flux::size(needle)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(haystack) < flux::iterable_size(needle)) { return false; } } - auto h = flux::first(haystack); - auto n = flux::first(needle); + iteration_context auto haystack_ctx = iterate(haystack); + iteration_context auto needle_ctx = iterate(needle); + + while (true) { + auto haystack_elem = next_element(haystack_ctx); + auto needle_elem = next_element(needle_ctx); - while (!flux::is_last(haystack, h) && !flux::is_last(needle, n)) { - if (!std::invoke(cmp, flux::read_at(haystack, h), flux::read_at(needle, n))) { + if (haystack_elem.has_value() && needle_elem.has_value()) { + if (!std::invoke(cmp, haystack_elem.value(), needle_elem.value())) { + return false; + } + } else if (needle_elem.has_value()) { return false; + } else { + return true; } - flux::inc(haystack, h); - flux::inc(needle, n); } - - return flux::is_last(needle, n); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto starts_with = detail::starts_with_fn{}; +FLUX_EXPORT inline constexpr starts_with_t starts_with{}; template template - requires std::predicate, element_t> + requires std::predicate, iterable_element_t> constexpr auto inline_sequence_base::starts_with(Needle&& needle, Cmp cmp) -> bool { return flux::starts_with(derived(), FLUX_FWD(needle), std::move(cmp)); diff --git a/include/flux/algorithm/swap_elements.hpp b/include/flux/algorithm/swap_elements.hpp index a0ad44a3..7d1e1b70 100644 --- a/include/flux/algorithm/swap_elements.hpp +++ b/include/flux/algorithm/swap_elements.hpp @@ -10,28 +10,29 @@ namespace flux { -namespace detail { - -struct swap_elements_fn { - template - requires element_swappable_with - constexpr void operator()(Seq1&& seq1, Seq2&& seq2) const +FLUX_EXPORT +struct swap_elements_t { + template + requires std::swappable_with, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2) const -> void { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); - - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - flux::swap_with(seq1, cur1, seq2, cur2); - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + auto opt1 = next_element(ctx1); + auto opt2 = next_element(ctx2); + + if (opt1.has_value() && opt2.has_value()) { + std::ranges::swap(*std::move(opt1), *std::move(opt2)); + } else { + break; + } } } }; -} - -FLUX_EXPORT inline constexpr auto swap_elements = detail::swap_elements_fn{}; - +FLUX_EXPORT inline constexpr swap_elements_t swap_elements{}; } #endif // FLUX_ALGORITHM_SWAP_ELEMENTS_HPP_INCLUDED diff --git a/include/flux/algorithm/to.hpp b/include/flux/algorithm/to.hpp index 6f3d06c9..6d4fd04c 100644 --- a/include/flux/algorithm/to.hpp +++ b/include/flux/algorithm/to.hpp @@ -10,51 +10,61 @@ #include #include +#if defined(__cpp_lib_ranges_to_container) && (__cpp_lib_ranges_to_container >= 202202L) +# define FLUX_HAVE_FROM_RANGE_CONSTRUCTORS +#endif + namespace flux { FLUX_EXPORT -struct from_sequence_t { - explicit from_sequence_t() = default; +struct from_iterable_t { + explicit from_iterable_t() = default; }; -FLUX_EXPORT inline constexpr auto from_sequence = from_sequence_t{}; +FLUX_EXPORT inline constexpr auto from_iterable = from_iterable_t{}; namespace detail { -template -concept direct_sequence_constructible = - std::constructible_from; +template +concept direct_iterable_constructible = std::constructible_from; + +template +concept from_iterable_constructible = std::constructible_from; -template -concept from_sequence_constructible = - std::constructible_from; +#ifdef FLUX_HAVE_FROM_RANGE_CONSTRUCTORS +template +concept from_range_constructible + = std::constructible_from())), + Args...>; +#else +template +concept from_range_constructible = false; +#endif template using container_value_t = typename C::value_type; // Let's just assume it exists -template -using common_iterator_t = - std::ranges::iterator_t; +template +using common_iterator_t + = std::ranges::iterator_t())))>; - -template -concept cpp17_range_constructible = - std::constructible_from, common_iterator_t, Args...>; +template +concept cpp17_range_constructible + = std::constructible_from, common_iterator_t, Args...>; template -concept container_insertable = - requires (C& c, Elem&& elem) { - requires (requires { c.push_back(FLUX_FWD(elem)); } || - requires { c.insert(c.end(), FLUX_FWD(elem)); }); - }; +concept container_appendable = requires(C& c, Elem&& elem) { + requires( + requires { c.emplace_back(FLUX_FWD(elem)); } || requires { c.push_back(FLUX_FWD(elem)); } + || requires { c.emplace(c.end(), FLUX_FWD(elem)); } + || requires { c.insert(c.end(), FLUX_FWD(elem)); }); +}; -template -concept container_convertible = - direct_sequence_constructible || - from_sequence_constructible || - cpp17_range_constructible || - ( std::constructible_from && - container_insertable>); +template +concept container_convertible + = direct_iterable_constructible || from_iterable_constructible + || from_range_constructible || cpp17_range_constructible + || (std::constructible_from && container_appendable>); template concept reservable_container = @@ -65,49 +75,75 @@ concept reservable_container = { c.capacity() } -> std::same_as>; }; -template -constexpr auto make_inserter(C& c) +template +constexpr auto container_appender(Container& c) { - if constexpr (requires { c.push_back(FLUX_DECLVAL(Elem)); }) { - return std::back_inserter(c); - } else { - return std::inserter(c, c.end()); - } + return [&c](auto&& elem) { + if constexpr (requires { c.emplace_back(FLUX_FWD(elem)); }) { + c.emplace_back(FLUX_FWD(elem)); + } else if constexpr (requires { c.push_back(FLUX_FWD(elem)); }) { + c.push_back(FLUX_FWD(elem)); + // } else if constexpr (requires { c.emplace(c.end(), FLUX_FWD(elem)); }) { + // c.emplace(c.end(), FLUX_FWD(elem)); + } else { + c.insert(c.end(), FLUX_FWD(elem)); + } + }; } template