Skip to content

Commit

Permalink
Renamed and refactored for first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
nwad123 committed Jan 10, 2025
1 parent 477d9b7 commit 62dfd56
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 65 deletions.
44 changes: 28 additions & 16 deletions include/flux/adaptor/permutations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap
Base base_;

// Uninitialized: Input sequence is not cached, output can't be generated
// Cached: Input sequence is cached (copied) into the cache_ member variable
// Cached: Input sequence is cached (copied) into the cache_ member variable and output
// can be generated when a valid cursor is provided
enum class state_t { Uninitialized, Cached };
state_t state_ {state_t::Uninitialized};

using inner_value_t = flux::value_t<Base>;
std::vector<inner_value_t> cache_; // This caches the input sequence

std::vector<inner_value_t> 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();
Expand All @@ -42,6 +44,7 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap

[[nodiscard]] constexpr bool is_cached() const noexcept { return state_ == state_t::Cached; }

// Caches the base sequence by copying it into the `cache_` member variable.
constexpr auto cache_base() -> void
{
for (const auto elem : base_) {
Expand All @@ -53,6 +56,9 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap
state_ = state_t::Cached;
}

// Caches the base sequence by copying it into the `cache_` member variable.
// Since this is a `sized_sequence` we can use pre-allocate the `cache_` and copy in the
// input using `std::copy`
constexpr auto cache_base() -> void
requires flux::sized_sequence<Base>
{
Expand All @@ -73,17 +79,18 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap
using self_t = permutations_adaptor;

struct cursor_type {
// `indices_` keeps track of the permuted indices of the input sequence
std::vector<std::size_t> 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_;
}
};

Expand All @@ -98,7 +105,7 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap
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
Expand All @@ -118,30 +125,30 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap
// variable aliases for readability
const auto base_length = self.cache_.size();

// fill up the indices from 0 to size-1 in reverse (as this is the last)
// fill up the indices from 0 to size-1 in reverse (as this is the last permutation)
std::vector<std::size_t> 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
Expand All @@ -160,7 +167,7 @@ class permutations_adaptor : public flux::inline_sequence_base<permutations_adap

static constexpr auto size(self_t& self) -> flux::distance_t
{
return static_cast<flux::distance_t>(self.number_permutations());
return static_cast<flux::distance_t>(self.count_permutations());
}
};
};
Expand All @@ -179,10 +186,15 @@ struct permutations_fn {

FLUX_EXPORT inline constexpr auto permutations = detail::permutations_fn {};

// clang-format off
FLUX_EXPORT
template <typename Derived>
constexpr auto inline_sequence_base<Derived>::permutations()
&& requires(not infinite_sequence<Derived>) { return flux::permutations(std::move(derived())); }
constexpr auto inline_sequence_base<Derived>::permutations() &&
requires(not infinite_sequence<Derived>)
{
return flux::permutations(std::move(derived()));
}
// clang-format on

} // namespace flux
#endif
5 changes: 5 additions & 0 deletions include/flux/adaptor/permutations_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 <typename T>
[[nodiscard]] constexpr auto reindex_vec(const std::vector<T>& input, const auto& indices,
const std::size_t length) -> std::vector<T>
Expand All @@ -27,6 +30,8 @@ template <typename T>
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 <typename T>
[[nodiscard]] constexpr auto reindex_vec(const std::vector<T>& input, const auto& indices)
-> std::vector<T>
Expand Down
90 changes: 41 additions & 49 deletions include/flux/adaptor/permutations_sized.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
namespace flux {
namespace detail {

template <flux::sequence Base, flux::distance_t Size>
requires flux::bounded_sequence<Base> && (Size > 0)
template <flux::sequence Base, flux::distance_t SubsequenceSize>
requires flux::bounded_sequence<Base> && (SubsequenceSize > 0)
struct permutations_sized_adaptor
: public flux::inline_sequence_base<permutations_sized_adaptor<Base, Size>> {
: public flux::inline_sequence_base<permutations_sized_adaptor<Base, SubsequenceSize>> {
private:
Base base_;

Expand All @@ -28,13 +28,10 @@ struct permutations_sized_adaptor
state_t state_ {state_t::Uninitialized};

using inner_value_t = flux::value_t<Base>;
std::vector<inner_value_t> cache_; // This caches the input sequence
std::vector<inner_value_t> 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();
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -84,8 +83,7 @@ struct permutations_sized_adaptor
struct cursor_type {
std::vector<std::size_t> indices_;
std::vector<std::size_t> cycles_;

std::size_t index_ {0};
std::size_t index_;

[[nodiscard]] constexpr auto operator<=>(const cursor_type& other) const
{
Expand All @@ -96,8 +94,6 @@ struct permutations_sized_adaptor
return index_ == other.index_;
}
};
static_assert(flux::regular_cursor<cursor_type>);
static_assert(flux::ordered_cursor<cursor_type>);

public:
using value_type = std::vector<inner_value_t>;
Expand All @@ -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<std::size_t> indices(base_length);
std::iota(indices.begin(), indices.end(), 0);

// fill up the cycles
std::vector<std::size_t> 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<std::size_t> cycles(SubsequenceSize);
std::iota(cycles.rbegin(), cycles.rend(), base_length - SubsequenceSize);

// return the cursor
return {.indices_ = indices, .cycles_ = cycles, .index_ = 0};
Expand All @@ -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<std::size_t> cycles(Size, 0);
std::vector<std::size_t> 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;
Expand All @@ -176,57 +172,53 @@ struct permutations_sized_adaptor
if (not self.is_cached()) {
self.cache_base();
}
return reindex_vec<inner_value_t>(self.cache_, cursor.indices_, Size);
return reindex_vec<inner_value_t>(self.cache_, cursor.indices_, SubsequenceSize);
}

static constexpr auto read_at_unchecked(self_t& self, const cursor_type& cursor)
-> value_type
{
return reindex_vec<inner_value_t>(self.cache_, cursor.indices_, Size);
return reindex_vec<inner_value_t>(self.cache_, cursor.indices_, SubsequenceSize);
}

static constexpr auto size(self_t& self) -> flux::distance_t
requires flux::sized_sequence<Base>
{
return self.number_permutations();
return self.count_permutations();
}
};
};
static_assert(flux::sequence<
permutations_sized_adaptor<flux::detail::ref_adaptor<const std::array<int, 3>>, 3>>);
static_assert(flux::multipass_sequence<
permutations_sized_adaptor<flux::detail::ref_adaptor<const std::array<int, 3>>, 3>>);
static_assert(flux::bounded_sequence<
permutations_sized_adaptor<flux::detail::ref_adaptor<const std::array<int, 3>>, 3>>);
static_assert(flux::sized_sequence<
permutations_sized_adaptor<flux::detail::ref_adaptor<const std::array<int, 3>>, 3>>);

template <std::size_t Size>
requires(Size > 0)

template <std::size_t SubsequenceSize>
requires(SubsequenceSize > 0)
struct permutations_sized_fn {
template <sequence Seq>
requires(not infinite_sequence<Seq>)
[[nodiscard]]
constexpr auto operator()(Seq&& seq) const -> sized_sequence auto
{
return permutations_sized_adaptor<std::decay_t<Seq>, Size>(FLUX_FWD(seq));
return permutations_sized_adaptor<std::decay_t<Seq>, SubsequenceSize>(FLUX_FWD(seq));
}
};

} // namespace detail

FLUX_EXPORT
template <std::size_t Size>
inline constexpr auto permutations_sized = detail::permutations_sized_fn<Size> {};
template <std::size_t SubsequenceSize>
inline constexpr auto permutations_sized = detail::permutations_sized_fn<SubsequenceSize> {};

// clang-format off
FLUX_EXPORT
template <typename Derived>
template <std::size_t Size>
requires(Size > 0)
constexpr auto inline_sequence_base<Derived>::permutations_sized()
&& requires(not infinite_sequence<Derived>) {
return flux::permutations_sized<Size>(std::move(derived()));
}
template <std::size_t SubsequenceSize>
requires(SubsequenceSize > 0)
constexpr auto inline_sequence_base<Derived>::permutations_sized() &&
requires(not infinite_sequence<Derived>)
{
return flux::permutations_sized<SubsequenceSize>(std::move(derived()));
}
// clang-format on
//
} // namespace flux

#endif

0 comments on commit 62dfd56

Please sign in to comment.