diff --git a/include/flux/adaptor/permutations.hpp b/include/flux/adaptor/permutations.hpp index 93e6ce70..dcbe123b 100644 --- a/include/flux/adaptor/permutations.hpp +++ b/include/flux/adaptor/permutations.hpp @@ -22,16 +22,18 @@ class permutations_adaptor : public flux::inline_sequence_base; - std::vector cache_; // This caches the input sequence - + std::vector cache_; std::size_t size_; - [[nodiscard]] constexpr std::size_t number_permutations() + // Returns the total number of possible permutations of the input sequence. Requires that + // the input sequence is cached to do so. + [[nodiscard]] constexpr std::size_t count_permutations() { if (not is_cached()) { cache_base(); @@ -42,6 +44,7 @@ class permutations_adaptor : public flux::inline_sequence_base void { for (const auto elem : base_) { @@ -53,6 +56,9 @@ class permutations_adaptor : public flux::inline_sequence_base void requires flux::sized_sequence { @@ -73,17 +79,18 @@ class permutations_adaptor : public flux::inline_sequence_base indices_; - std::size_t index_ {0}; + std::size_t permutation_index_ {0}; [[nodiscard]] constexpr auto operator<=>(const cursor_type& other) const { - return index_ <=> other.index_; + return permutation_index_ <=> other.permutation_index_; } [[nodiscard]] constexpr bool operator==(const cursor_type& other) const { - return index_ == other.index_; + return permutation_index_ == other.permutation_index_; } }; @@ -98,7 +105,7 @@ class permutations_adaptor : public flux::inline_sequence_base indices(base_length); std::iota(indices.rbegin(), indices.rend(), 0); // return the cursor - return {.indices_ = indices, .index_ = self.number_permutations()}; + return {.indices_ = indices, .index_ = self.count_permutations()}; } static constexpr auto is_last([[maybe_unused]] self_t& self, const cursor_type& cursor) -> bool { - return cursor.index_ >= self.number_permutations(); + return cursor.permutation_index_ >= self.count_permutations(); } static constexpr auto inc([[maybe_unused]] self_t& self, cursor_type& cursor) -> void { std::next_permutation(cursor.indices_.begin(), cursor.indices_.end()); - cursor.index_ += 1; + cursor.permutation_index_ += 1; } static constexpr auto dec([[maybe_unused]] self_t& self, cursor_type& cursor) -> void { std::prev_permutation(cursor.indices_.begin(), cursor.indices_.end()); - cursor.index_ -= 1; + cursor.permutation_index_ -= 1; } static constexpr auto read_at(self_t& self, const cursor_type& cursor) -> value_type @@ -160,7 +167,7 @@ class permutations_adaptor : public flux::inline_sequence_base flux::distance_t { - return static_cast(self.number_permutations()); + return static_cast(self.count_permutations()); } }; }; @@ -179,10 +186,15 @@ struct permutations_fn { FLUX_EXPORT inline constexpr auto permutations = detail::permutations_fn {}; +// clang-format off FLUX_EXPORT template - constexpr auto inline_sequence_base::permutations() - && requires(not infinite_sequence) { return flux::permutations(std::move(derived())); } +constexpr auto inline_sequence_base::permutations() && + requires(not infinite_sequence) +{ + return flux::permutations(std::move(derived())); +} +// clang-format on } // namespace flux #endif diff --git a/include/flux/adaptor/permutations_base.hpp b/include/flux/adaptor/permutations_base.hpp index 6cae038c..1adcda04 100644 --- a/include/flux/adaptor/permutations_base.hpp +++ b/include/flux/adaptor/permutations_base.hpp @@ -5,6 +5,7 @@ namespace flux::detail { +// Calculates the factorial of `x` [[nodiscard]] constexpr auto factorial(const std::integral auto x) -> decltype(x) { if (x <= 1) { @@ -13,6 +14,8 @@ namespace flux::detail { return x * factorial(x - 1); } +// Given an input vector and a range of indices, return a new vector with the same values +// of `input`, ordered by `indices` up to the given `length`. template [[nodiscard]] constexpr auto reindex_vec(const std::vector& input, const auto& indices, const std::size_t length) -> std::vector @@ -27,6 +30,8 @@ template return output; } +// Given an input vector and a range of indices, return a new vector with the same values +// of `input`, ordered by `indices`. template [[nodiscard]] constexpr auto reindex_vec(const std::vector& input, const auto& indices) -> std::vector diff --git a/include/flux/adaptor/permutations_sized.hpp b/include/flux/adaptor/permutations_sized.hpp index 75c18a98..b6a0ecb5 100644 --- a/include/flux/adaptor/permutations_sized.hpp +++ b/include/flux/adaptor/permutations_sized.hpp @@ -15,10 +15,10 @@ namespace flux { namespace detail { -template - requires flux::bounded_sequence && (Size > 0) +template + requires flux::bounded_sequence && (SubsequenceSize > 0) struct permutations_sized_adaptor - : public flux::inline_sequence_base> { + : public flux::inline_sequence_base> { private: Base base_; @@ -28,13 +28,10 @@ struct permutations_sized_adaptor state_t state_ {state_t::Uninitialized}; using inner_value_t = flux::value_t; - std::vector cache_; // This caches the input sequence + std::vector cache_; + std::size_t size_; - [[nodiscard]] constexpr bool is_cached() const noexcept { return state_ == state_t::Cached; } - - std::size_t size_ {0}; - - [[nodiscard]] constexpr std::size_t number_permutations() + [[nodiscard]] constexpr std::size_t count_permutations() { if (not is_cached()) { cache_base(); @@ -43,14 +40,16 @@ struct permutations_sized_adaptor return size_; } + [[nodiscard]] constexpr bool is_cached() const noexcept { return state_ == state_t::Cached; } + constexpr auto cache_base() -> void { for (const auto elem : base_) { cache_.emplace_back(elem); } - if (Size <= cache_.size()) { - size_ = factorial(cache_.size()) / factorial(cache_.size() - Size); + if (SubsequenceSize <= cache_.size()) { + size_ = factorial(cache_.size()) / factorial(cache_.size() - SubsequenceSize); } else { size_ = 0; } @@ -65,8 +64,8 @@ struct permutations_sized_adaptor cache_.resize(size); std::ranges::copy(base_, cache_.begin()); - if (Size <= size) { - size_ = factorial(cache_.size()) / factorial(cache_.size() - Size); + if (SubsequenceSize <= size) { + size_ = factorial(cache_.size()) / factorial(cache_.size() - SubsequenceSize); } else { size_ = 0; } @@ -84,8 +83,7 @@ struct permutations_sized_adaptor struct cursor_type { std::vector indices_; std::vector cycles_; - - std::size_t index_ {0}; + std::size_t index_; [[nodiscard]] constexpr auto operator<=>(const cursor_type& other) const { @@ -96,8 +94,6 @@ struct permutations_sized_adaptor return index_ == other.index_; } }; - static_assert(flux::regular_cursor); - static_assert(flux::ordered_cursor); public: using value_type = std::vector; @@ -110,16 +106,16 @@ struct permutations_sized_adaptor self.cache_base(); } - // variable aliases for readability + // variable alias for readability const auto base_length = self.cache_.size(); - // fill up the indices from 0 to size-1 + // Set indices to range [0, 1, ..., size-1] std::vector indices(base_length); std::iota(indices.begin(), indices.end(), 0); - // fill up the cycles - std::vector cycles(Size); - std::iota(cycles.rbegin(), cycles.rend(), base_length - Size); + // Set cycles to [i, i+1, ..., size] where i = size - subsequence_size + std::vector cycles(SubsequenceSize); + std::iota(cycles.rbegin(), cycles.rend(), base_length - SubsequenceSize); // return the cursor return {.indices_ = indices, .cycles_ = cycles, .index_ = 0}; @@ -139,20 +135,20 @@ struct permutations_sized_adaptor std::iota(indices.rbegin(), indices.rend(), 0); // fill up the cycles with 0s because it's the end - std::vector cycles(Size, 0); + std::vector cycles(SubsequenceSize, 0); // return the cursor - return {.indices_ = indices, .cycles_ = cycles, .index_ = self.number_permutations()}; + return {.indices_ = indices, .cycles_ = cycles, .index_ = self.count_permutations()}; } static constexpr auto is_last(self_t& self, const cursor_type& cursor) -> bool { - return cursor.index_ >= self.number_permutations(); + return cursor.index_ >= self.count_permutations(); } static constexpr auto inc(self_t& self, cursor_type& cursor) -> void { - const auto k = Size; + const auto k = SubsequenceSize; const auto n = self.cache_.size(); cursor.index_ += 1; @@ -176,57 +172,53 @@ struct permutations_sized_adaptor if (not self.is_cached()) { self.cache_base(); } - return reindex_vec(self.cache_, cursor.indices_, Size); + return reindex_vec(self.cache_, cursor.indices_, SubsequenceSize); } static constexpr auto read_at_unchecked(self_t& self, const cursor_type& cursor) -> value_type { - return reindex_vec(self.cache_, cursor.indices_, Size); + return reindex_vec(self.cache_, cursor.indices_, SubsequenceSize); } static constexpr auto size(self_t& self) -> flux::distance_t requires flux::sized_sequence { - return self.number_permutations(); + return self.count_permutations(); } }; }; -static_assert(flux::sequence< - permutations_sized_adaptor>, 3>>); -static_assert(flux::multipass_sequence< - permutations_sized_adaptor>, 3>>); -static_assert(flux::bounded_sequence< - permutations_sized_adaptor>, 3>>); -static_assert(flux::sized_sequence< - permutations_sized_adaptor>, 3>>); - -template - requires(Size > 0) + +template + requires(SubsequenceSize > 0) struct permutations_sized_fn { template requires(not infinite_sequence) [[nodiscard]] constexpr auto operator()(Seq&& seq) const -> sized_sequence auto { - return permutations_sized_adaptor, Size>(FLUX_FWD(seq)); + return permutations_sized_adaptor, SubsequenceSize>(FLUX_FWD(seq)); } }; } // namespace detail FLUX_EXPORT -template -inline constexpr auto permutations_sized = detail::permutations_sized_fn {}; +template +inline constexpr auto permutations_sized = detail::permutations_sized_fn {}; +// clang-format off FLUX_EXPORT template -template - requires(Size > 0) -constexpr auto inline_sequence_base::permutations_sized() - && requires(not infinite_sequence) { - return flux::permutations_sized(std::move(derived())); - } +template + requires(SubsequenceSize > 0) +constexpr auto inline_sequence_base::permutations_sized() && + requires(not infinite_sequence) +{ + return flux::permutations_sized(std::move(derived())); +} +// clang-format on +// } // namespace flux #endif