diff --git a/single_include/flux.hpp b/single_include/flux.hpp index a9f4ff9c..38e09e32 100644 --- a/single_include/flux.hpp +++ b/single_include/flux.hpp @@ -505,6 +505,20 @@ inline constexpr auto checked_mul = } }; +FLUX_EXPORT +inline constexpr auto checked_pow = + [](T base, U exponent, + std::source_location loc = std::source_location::current()) + -> T +{ + T res{1}; + for(U i{0}; i < exponent; i++) { + res = checked_mul(res, base, loc); + } + return res; +}; + + inline constexpr auto checked_div = [](T lhs, T rhs, std::source_location loc = std::source_location::current()) @@ -2425,6 +2439,23 @@ concept foldable_ = } // namespace detail +namespace detail { + +template +struct repeated_invocable_helper { + template + using repeater = E; + + static inline constexpr bool value = [] (std::index_sequence) consteval { + return std::regular_invocable...>; + }(std::make_index_sequence{}); +}; + +template +concept repeated_invocable = repeated_invocable_helper::value; + +} // namespace detail + FLUX_EXPORT template concept foldable = @@ -3808,6 +3839,7 @@ constexpr auto inline_sequence_base::reverse() && #endif + // Copyright (c) 2022 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) @@ -4433,19 +4465,6 @@ struct adjacent_adaptor : inline_sequence_base> { }; }; -template -struct adjacent_invocable_helper { - template - using repeater = E; - - static inline constexpr bool value = [] (std::index_sequence) consteval { - return std::regular_invocable...>; - }(std::make_index_sequence{}); -}; - -template -concept adjacent_invocable = adjacent_invocable_helper::value; - template struct adjacent_map_adaptor : inline_sequence_base> { private: @@ -4464,7 +4483,7 @@ struct adjacent_map_adaptor : inline_sequence_base static constexpr auto read_at(Self& self, cursor_t const& cur) -> decltype(auto) - requires adjacent_invocable, N> + requires repeated_invocable, N> { return std::apply([&](auto const&... curs) { return std::invoke(self.func_, flux::read_at(self.base_, curs)...); @@ -4488,7 +4507,7 @@ template struct adjacent_map_fn { template requires multipass_sequence && - adjacent_invocable, N> + repeated_invocable, N> [[nodiscard]] constexpr auto operator()(Seq&& seq, Func func) const -> multipass_sequence auto { @@ -4893,68 +4912,72 @@ constexpr auto inline_sequence_base::cache_last() && // 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_OP_CARTESIAN_PRODUCT_HPP_INCLUDED -#define FLUX_OP_CARTESIAN_PRODUCT_HPP_INCLUDED - +#ifndef FLUX_CARTESIAN_BASE_HPP_INCLUDED +#define FLUX_CARTESIAN_BASE_HPP_INCLUDED +namespace flux::detail { +enum class cartesian_kind { product, power }; +enum class read_kind { tuple, map }; +template +inline constexpr bool cartesian_is_bounded = bounded_sequence; -#include +template +struct tuple_repeated { + template + using repeater = T; -namespace flux { + template + static auto make_tuple(std::index_sequence) -> std::tuple...>; -namespace detail { + using type = decltype(make_tuple(std::make_index_sequence{})); +}; -template -struct cartesian_product_traits_base; +template +using tuple_repeated_t = tuple_repeated::type; -template -struct cartesian_product_adaptor - : inline_sequence_base> { -private: - FLUX_NO_UNIQUE_ADDRESS std::tuple bases_; +template +struct cartesian_traits_types { +}; - friend struct cartesian_product_traits_base; - friend struct sequence_traits; +template +struct cartesian_traits_types { + using value_type = tuple_repeated_t, Arity>; +}; -public: - constexpr explicit cartesian_product_adaptor(decays_to auto&&... bases) - : bases_(FLUX_FWD(bases)...) - {} +template +struct cartesian_traits_types { + using value_type = std::tuple...>; }; -struct cartesian_product_fn { - template - requires (multipass_sequence && ...) - [[nodiscard]] - constexpr auto operator()(Seq0&& seq0, Seqs&&... seqs) const +template +struct cartesian_traits_base_impl { +private: + + template + static constexpr auto& get_base(Self& self) + requires (CartesianKind == cartesian_kind::power) { - return cartesian_product_adaptor, std::decay_t...>( - FLUX_FWD(seq0), FLUX_FWD(seqs)...); + return self.base_; } -}; -template -inline constexpr bool cartesian_product_is_bounded = bounded_sequence; + template + static constexpr auto& get_base(Self& self) + requires (CartesianKind == cartesian_kind::product) + { + return std::get(self.bases_); + } -template -struct cartesian_product_traits_base { -private: - template - using const_like_t = std::conditional_t, To const, To>; - - template - using cursor_type = std::tuple>...>; template - static constexpr auto inc_impl(Self& self, cursor_type& cur) -> cursor_type& + static constexpr auto inc_impl(Self& self, cursor_t& cur) -> cursor_t& { - flux::inc(std::get(self.bases_), std::get(cur)); + flux::inc(get_base(self), std::get(cur)); if constexpr (I > 0) { - if (flux::is_last(std::get(self.bases_), std::get(cur))) { - std::get(cur) = flux::first(std::get(self.bases_)); + if (flux::is_last(get_base(self), std::get(cur))) { + std::get(cur) = flux::first(get_base(self)); inc_impl(self, cur); } } @@ -4962,29 +4985,31 @@ struct cartesian_product_traits_base { return cur; } + template - static constexpr auto dec_impl(Self& self, cursor_type& cur) -> cursor_type& + static constexpr auto dec_impl(Self& self, cursor_t& cur) -> cursor_t& { - if (std::get(cur) == flux::first(std::get(self.bases_))) { - std::get(cur) = flux::last(std::get(self.bases_)); + if (std::get(cur) == flux::first(get_base(self))) { + std::get(cur) = flux::last(get_base(self)); if constexpr (I > 0) { dec_impl(self, cur); } } - flux::dec(std::get(self.bases_), std::get(cur)); + flux::dec(get_base(self), std::get(cur)); return cur; } template - static constexpr auto ra_inc_impl(Self& self, cursor_type& cur, distance_t offset) - -> cursor_type& + static constexpr auto ra_inc_impl(Self& self, cursor_t& cur, distance_t offset) + -> cursor_t& { - if (offset == 0) + if (offset == 0) { return cur; + } - auto& base = std::get(self.bases_); + auto& base = get_base(self); const auto this_index = flux::distance(base, flux::first(base), std::get(cur)); auto new_index = num::checked_add(this_index, offset); auto this_size = flux::size(base); @@ -5015,177 +5040,418 @@ struct cartesian_product_traits_base { template static constexpr auto distance_impl(Self& self, - cursor_type const& from, - cursor_type const& to) -> distance_t + cursor_t const& from, + cursor_t const& to) -> distance_t { if constexpr (I == 0) { - return flux::distance(std::get<0>(self.bases_), std::get<0>(from), std::get<0>(to)); + return flux::distance(get_base<0>(self), std::get<0>(from), std::get<0>(to)); } else { auto prev_dist = distance_impl(self, from, to); - auto our_sz = flux::size(std::get(self.bases_)); - auto our_dist = flux::distance(std::get(self.bases_), std::get(from), std::get(to)); + auto our_sz = flux::size(get_base(self)); + auto our_dist = flux::distance(get_base(self), std::get(from), std::get(to)); return prev_dist * our_sz + our_dist; } } + template + static constexpr auto read1_(Fn fn, Self& self, cursor_t const& cur) + -> decltype(auto) + { + return fn(get_base(self), std::get(cur)); + } + + template + static constexpr auto read_(Fn& fn, Self& self, cursor_t const& cur) + -> decltype(auto) + requires (ReadKind == read_kind::tuple) + { + return [&](std::index_sequence) { + return std::tuple(fn, self, cur))...>(read1_(fn, self, cur)...); + }(std::make_index_sequence{}); + } + + template + static constexpr void for_each_while_impl(Self& self, + bool& keep_going, + cursor_t& cur, + Function&& func, + PartialElements&&... partial_elements) + { + // 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; + }); + } 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; + }); + } + } + +protected: + using types = cartesian_traits_types; + public: + template - static constexpr auto first(Self& self) -> cursor_type + static constexpr auto first(Self& self) + requires (CartesianKind == cartesian_kind::product) { return std::apply([](auto&&... args) { - return cursor_type(flux::first(FLUX_FWD(args))...); + return std::tuple(flux::first(FLUX_FWD(args))...); }, self.bases_); } template - static constexpr auto is_last(Self& self, cursor_type const& cur) -> bool + static constexpr auto first(Self& self) + requires (CartesianKind == cartesian_kind::power) { - return [&](std::index_sequence) { - return (flux::is_last(std::get(self.bases_), std::get(cur)) || ...); - }(std::index_sequence_for{}); + auto base_cur = flux::first(self.base_); + return [&base_cur](std::index_sequence) { + static_assert(sizeof...(Bases) == 1); + std::array..., Arity> cur = {(static_cast(Is), base_cur)...}; + return cur; + }(std::make_index_sequence{}); } template - static constexpr auto inc(Self& self, cursor_type& cur) -> cursor_type& + static constexpr auto size(Self& self) -> distance_t + requires (CartesianKind == cartesian_kind::product + && (sized_sequence && ...)) { - return inc_impl(self, cur); + return std::apply([](auto&... base) { + return (flux::size(base) * ...); + }, self.bases_); } template - requires cartesian_product_is_bounded...> - static constexpr auto last(Self& self) -> cursor_type + static constexpr auto size(Self& self) -> distance_t + requires (CartesianKind == cartesian_kind::power + && (sized_sequence && ...)) { - auto cur = first(self); - std::get<0>(cur) = flux::last(std::get<0>(self.bases_)); - return cur; + return num::checked_pow(flux::size(self.base_), Arity); } template - requires (bidirectional_sequence> && ...) && - (bounded_sequence> && ...) - static constexpr auto dec(Self& self, cursor_type& cur) -> cursor_type& + static constexpr auto is_last(Self& self, cursor_t const& cur) -> bool { - return dec_impl(self, cur); + return [&](std::index_sequence) { + return (flux::is_last(get_base(self), std::get(cur)) || ...); + }(std::make_index_sequence{}); } template - requires (random_access_sequence> && ...) && - (sized_sequence> && ...) - static constexpr auto inc(Self& self, cursor_type& cur, distance_t offset) - -> cursor_type& + static constexpr auto dec(Self& self, cursor_t& cur) -> cursor_t& + requires ((bidirectional_sequence && ...) && + (bounded_sequence && ...)) { - return ra_inc_impl(self, cur, offset); + return dec_impl(self, cur); } template - requires (random_access_sequence> && ...) && - (sized_sequence> && ...) - static constexpr auto distance(Self& self, - cursor_type const& from, - cursor_type const& to) -> distance_t + static constexpr auto inc(Self& self, cursor_t& cur, distance_t offset) -> cursor_t& + requires ((random_access_sequence && ...) && + (sized_sequence && ...)) { - return distance_impl(self, from, to); + return ra_inc_impl(self, cur, offset); } template - requires (sized_sequence> && ...) - static constexpr auto size(Self& self) -> distance_t + static constexpr auto inc(Self& self, cursor_t& cur) -> cursor_t& { - return std::apply([](auto&... base) { - return (flux::size(base) * ...); - }, self.bases_); + return inc_impl(self, cur); } -}; - -} // end namespace detail -template -struct sequence_traits> - : detail::cartesian_product_traits_base -{ -private: - template - static constexpr auto read1_(Fn fn, Self& self, cursor_t const& cur) - -> decltype(auto) + template + static constexpr auto last(Self& self) -> cursor_t + requires cartesian_is_bounded { - return fn(std::get(self.bases_), std::get(cur)); + auto cur = first(self); + std::get<0>(cur) = flux::last(get_base<0>(self)); + return cur; } - template - static constexpr auto read_(Fn fn, Self& self, cursor_t const& cur) + template + static constexpr auto distance(Self& self, + cursor_t const& from, + cursor_t const& to) -> distance_t + requires ((random_access_sequence && ...) && + (sized_sequence && ...)) { - return [&](std::index_sequence) { - return std::tuple(fn, self, cur))...>(read1_(fn, self, cur)...); - }(std::index_sequence_for{}); + return distance_impl(self, from, to); } - template - static constexpr void for_each_while_impl(Self& self, - bool& keep_going, - cursor_t& cur, - Function&& func, - PartialElements&&... partial_elements) + template + static constexpr auto read_at(Self& self, cursor_t const& cur) + requires (ReadKind == read_kind::tuple) { - // We need to iterate right to left. - if constexpr (I == sizeof...(Bases) - 1) { - std::get(cur) = flux::for_each_while(std::get(self.bases_), - [&](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(std::get(self.bases_), - [&](auto&& elem) { - for_each_while_impl( - self, keep_going, cur, - func, FLUX_FWD(partial_elements)..., FLUX_FWD(elem)); - return keep_going; - }); - } + return read_(flux::read_at, self, cur); } -public: - using value_type = std::tuple...>; - template static constexpr auto read_at(Self& self, cursor_t const& cur) + -> decltype(auto) + requires (ReadKind == read_kind::map) { - return read_(flux::read_at, self, cur); + return [&](std::index_sequence) -> decltype(auto) { + return std::invoke(self.func_, flux::read_at(get_base(self), std::get(cur))...); + }(std::make_index_sequence{}); } template static constexpr auto move_at(Self& self, cursor_t const& cur) + requires (ReadKind == read_kind::tuple) { return read_(flux::move_at, self, cur); } template static constexpr auto read_at_unchecked(Self& self, cursor_t const& cur) + requires (ReadKind == read_kind::tuple) { return read_(flux::read_at_unchecked, self, cur); } template static constexpr auto move_at_unchecked(Self& self, cursor_t const& cur) + requires (ReadKind == read_kind::tuple) { return read_(flux::move_at_unchecked, self, cur); } template static constexpr auto for_each_while(Self& self, Function&& func) - -> cursor_t + -> cursor_t + requires (ReadKind == read_kind::tuple) { bool keep_going = true; cursor_t cur; for_each_while_impl<0>(self, keep_going, cur, FLUX_FWD(func)); return cur; } + +}; + +template +struct cartesian_traits_base : cartesian_traits_base_impl { + using impl = cartesian_traits_base_impl; +}; + +template +struct cartesian_traits_base : cartesian_traits_base_impl { + using impl = cartesian_traits_base_impl; + + using value_type = typename impl::types::value_type; +}; + +} + +#endif //FLUX_CARTESIAN_BASE_HPP_INCLUDED + + +// Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) +// Copyright (c) 2023 NVIDIA Corporation (reply-to: brycelelbach@gmail.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_OP_CARTESIAN_POWER_HPP_INCLUDED +#define FLUX_OP_CARTESIAN_POWER_HPP_INCLUDED + + + + + + +#include + +namespace flux { + +namespace detail { + +template +struct cartesian_power_adaptor + : inline_sequence_base> { +private: + FLUX_NO_UNIQUE_ADDRESS Base base_; + +public: + constexpr explicit cartesian_power_adaptor(Base&& base) + : base_(FLUX_FWD(base)) + {} + + using flux_sequence_traits = cartesian_traits_base< + PowN, + cartesian_kind::power, + read_kind::tuple, + Base + >; + friend flux_sequence_traits::impl; +}; + + + +template +struct cartesian_power_fn { + + template + requires multipass_sequence + [[nodiscard]] + constexpr auto operator()(Seq&& seq) const + { + if constexpr(PowN == 0) { + return empty>; + } else { + return cartesian_power_adaptor>( + FLUX_FWD(seq)); + } + } }; + +} // end namespace detail + +FLUX_EXPORT +template +inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; + +} // end namespace flux + +#endif + + + +// Copyright (c) 2022 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_OP_CARTESIAN_POWER_MAP_HPP_INCLUDED +#define FLUX_OP_CARTESIAN_POWER_MAP_HPP_INCLUDED + + + + +namespace flux { + +namespace detail { + +template +struct cartesian_power_map_adaptor + : inline_sequence_base> { +private: + FLUX_NO_UNIQUE_ADDRESS Base base_; + FLUX_NO_UNIQUE_ADDRESS Func func_; + +public: + constexpr explicit cartesian_power_map_adaptor(decays_to auto&& base, decays_to auto&& func) + : base_(FLUX_FWD(base)), + func_(FLUX_FWD(func)) + {} + + using flux_sequence_traits = cartesian_traits_base< + PowN, + cartesian_kind::power, + read_kind::map, + Base + >; + friend flux_sequence_traits::impl; +}; + +template +struct cartesian_power_map_fn +{ + template + requires multipass_sequence && + detail::repeated_invocable, PowN> + [[nodiscard]] + constexpr auto operator()(Seq&& seq, Func func) const + { + if constexpr(PowN == 0) { + return empty>; + } else { + return cartesian_power_map_adaptor, PowN, Func>( + FLUX_FWD(seq), std::move(func)); + } + } +}; + +} // namespace detail + +FLUX_EXPORT +template + requires (N >= 0) +inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn{}; + +} // namespace flux + +#endif + + +// Copyright (c) 2022 Tristan Brindle (tcbrindle at gmail dot com) +// Copyright (c) 2023 NVIDIA Corporation (reply-to: brycelelbach@gmail.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_OP_CARTESIAN_PRODUCT_HPP_INCLUDED +#define FLUX_OP_CARTESIAN_PRODUCT_HPP_INCLUDED + + + + + + +#include + +namespace flux { + +namespace detail { + +template +struct cartesian_product_adaptor + : inline_sequence_base> { +private: + FLUX_NO_UNIQUE_ADDRESS std::tuple bases_; + +public: + constexpr explicit cartesian_product_adaptor(decays_to auto&&... bases) + : bases_(FLUX_FWD(bases)...) + {} + + using flux_sequence_traits = cartesian_traits_base< + sizeof...(Bases), + cartesian_kind::product, + read_kind::tuple, + Bases... + >; + friend flux_sequence_traits::impl; +}; + +struct cartesian_product_fn { + template + requires (multipass_sequence && ...) + [[nodiscard]] + constexpr auto operator()(Seq0&& seq0, Seqs&&... seqs) const + { + return cartesian_product_adaptor, std::decay_t...>( + FLUX_FWD(seq0), FLUX_FWD(seqs)...); + } +}; + +} // end namespace detail + FLUX_EXPORT inline constexpr auto cartesian_product = detail::cartesian_product_fn{}; + } // end namespace flux #endif @@ -5196,8 +5462,8 @@ FLUX_EXPORT inline constexpr auto cartesian_product = detail::cartesian_product_ // 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_OP_CARTESIAN_PRODUCT_WITH_HPP_INCLUDED -#define FLUX_OP_CARTESIAN_PRODUCT_WITH_HPP_INCLUDED +#ifndef FLUX_OP_CARTESIAN_PRODUCT_MAP_HPP_INCLUDED +#define FLUX_OP_CARTESIAN_PRODUCT_MAP_HPP_INCLUDED @@ -5206,35 +5472,28 @@ namespace flux { namespace detail { template -struct cartesian_product_with_adaptor - : inline_sequence_base> { +struct cartesian_product_map_adaptor + : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS std::tuple bases_; FLUX_NO_UNIQUE_ADDRESS Func func_; - friend struct cartesian_product_traits_base; - friend struct sequence_traits; - public: - constexpr explicit cartesian_product_with_adaptor(decays_to auto&& func, decays_to auto&&... bases) + constexpr explicit cartesian_product_map_adaptor(decays_to auto&& func, decays_to auto&&... bases) : bases_(FLUX_FWD(bases)...), func_(FLUX_FWD(func)) {} - struct flux_sequence_traits : detail::cartesian_product_traits_base - { - template - static constexpr auto read_at(Self& self, cursor_t const& cur) - -> decltype(auto) - { - return [&](std::index_sequence) -> decltype(auto) { - return std::invoke(self.func_, flux::read_at(std::get(self.bases_), std::get(cur))...); - }(std::index_sequence_for{}); - } - }; + using flux_sequence_traits = cartesian_traits_base< + sizeof...(Bases), + cartesian_kind::product, + read_kind::map, + Bases... + >; + friend flux_sequence_traits::impl; }; -struct cartesian_product_with_fn +struct cartesian_product_map_fn { template requires (multipass_sequence && ...) && @@ -5242,7 +5501,7 @@ struct cartesian_product_with_fn [[nodiscard]] constexpr auto operator()(Func func, Seq0&& seq0, Seqs&&... seqs) const { - return cartesian_product_with_adaptor, std::decay_t...>( + return cartesian_product_map_adaptor, std::decay_t...>( std::move(func), FLUX_FWD(seq0), FLUX_FWD(seqs)...); } }; @@ -5251,7 +5510,7 @@ struct cartesian_product_with_fn } // namespace detail -FLUX_EXPORT inline constexpr auto cartesian_product_with = detail::cartesian_product_with_fn{}; +FLUX_EXPORT inline constexpr auto cartesian_product_map = detail::cartesian_product_map_fn{}; } // namespace flux