Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/reference/adaptors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,18 @@
:see also:
* :func:`flux::filter`

``permutations``
^^^^^^^^^^^^^^^^

.. function::

Check warning on line 1087 in docs/reference/adaptors.rst

View workflow job for this annotation

GitHub Actions / build

Error when parsing function declaration.

Check warning on line 1087 in docs/reference/adaptors.rst

View workflow job for this annotation

GitHub Actions / build

Error when parsing function declaration.
template<sequence Seq>
requires(not infinite_sequence<Seq>)
auto permutations(Seq seq) -> bidirectional_sequence auto

:param seq: A non-infinite sequence

:returns: A bidirectional sequence yielding permutations of :var:`seq`.

``pairwise``
^^^^^^^^^^^^

Expand Down Expand Up @@ -1997,4 +2009,4 @@
* - :concept:`read_only_sequence`
- All inputs are read-only
* - :concept:`const_iterable_sequence`
- All inputs are const-iterable
- All inputs are const-iterable
124 changes: 124 additions & 0 deletions include/flux/adaptor/permutations.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright stuff here? Not really sure on that as I've never done
// it

#ifndef FLUX_ADAPTOR_PERMUTATIONS_HPP_INCLUDED
#define FLUX_ADAPTOR_PERMUTATIONS_HPP_INCLUDED

#include "flux/core/concepts.hpp"
#include "flux/core/inline_sequence_base.hpp"
#include "flux/adaptor/permutations_base.hpp"
#include "flux/adaptor/cartesian_base.hpp" // tuple_repeated_t
#include <cstddef>
#include <flux/core.hpp>
#include <numeric>
#include <flux.hpp>

namespace flux {
namespace detail {

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

public:
constexpr permutations_adaptor(Base&& base) : base_(std::move(base)) { }

struct flux_sequence_traits : flux::default_sequence_traits {
private:
using self_t = permutations_adaptor;

struct cursor_type {
std::size_t permutation_index_;

[[nodiscard]] constexpr auto operator<=>(const cursor_type& other) const
{
return permutation_index_ <=> other.permutation_index_;
}
[[nodiscard]] constexpr bool operator==(const cursor_type& other) const
{
return permutation_index_ == other.permutation_index_;
}
};

public:
// TODO: remove vector type
using value_type = tuple_repeated_t<value_t<Base>, SubsequenceSize>;

inline static constexpr bool is_infinite = false;

static constexpr auto first(self_t& self) -> cursor_type
{
return {.permutation_index_ = 0};
}

static constexpr auto is_last(self_t& self, const cursor_type& cursor) -> bool
{
return false;
}

static constexpr auto inc(self_t& self, cursor_type& cursor) -> void
{
/*
const auto k = SubsequenceSize;
const auto n = self.cache_.size();

cursor.permutation_index_ += 1;

for (const auto i : flux::iota(std::size_t {0}, std::size_t {k}).reverse()) {
if (cursor.cycles_[i] == 0) {
cursor.cycles_[i] = n - i - 1;
std::rotate(cursor.indices_.begin() + i, cursor.indices_.begin() + i + 1,
cursor.indices_.end());
} else {
const auto swap_index = n - cursor.cycles_[i];
std::swap(cursor.indices_[i], cursor.indices_[swap_index]);
cursor.cycles_[i] -= 1;
return;
}
}
*/
}

static constexpr auto read_at(self_t& self, const cursor_type& cursor) -> value_type
{
return {};
}
};
};

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

} // namespace detail

FLUX_EXPORT
template <std::size_t SubsequenceSize>
inline constexpr auto permutations = detail::permutations_fn<SubsequenceSize> {};

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

#endif
43 changes: 43 additions & 0 deletions include/flux/adaptor/permutations_base.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include <concepts>
#include <vector>

namespace flux::detail {

// Calculates the factorial of `x`
[[nodiscard]] constexpr auto factorial(const std::integral auto x) -> decltype(x)
{
if (x <= 1) {
return 1;
}
return x * factorial(x - 1);
}

// TODO: this will need to change to support something other than vectors.
// 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>
{
std::vector<T> output;
output.reserve(input.size());

for (std::size_t i = 0; i < length; i++) {
output.push_back(input[indices[i]]);
}

return output;
}

// TODO: this will need to change to support something other than vectors.
// 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>
{
return reindex_vec(input, indices, input.size());
}
} // namespace flux::detail
6 changes: 6 additions & 0 deletions include/flux/core/inline_sequence_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#ifndef FLUX_CORE_INLINE_SEQUENCE_BASE_HPP_INCLUDED
#define FLUX_CORE_INLINE_SEQUENCE_BASE_HPP_INCLUDED

#include "flux/core/concepts.hpp"
#include <flux/core/sequence_access.hpp>
#include <flux/core/operation_requirements.hpp>

Expand Down Expand Up @@ -314,6 +315,11 @@ struct inline_sequence_base {
[[nodiscard]]
constexpr auto mask(Mask&& mask_) &&;

template<std::size_t Size>
requires(Size > 0)
[[nodiscard]]
constexpr auto permutations() && requires (not infinite_sequence<Derived>);

[[nodiscard]]
constexpr auto pairwise() && requires multipass_sequence<Derived>;

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ add_executable(test-flux
test_mask.cpp
test_minmax.cpp
test_output_to.cpp
test_permutations.cpp
test_range_iface.cpp
test_read_only.cpp
test_reverse.cpp
Expand Down
112 changes: 112 additions & 0 deletions test/test_permutations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include "flux/core/concepts.hpp"
#include "flux/core/ref.hpp"
#include "test_utils.hpp"
#include <algorithm>

#ifdef USE_MODULES
import flux;
#else
# include <flux/adaptor/permutations.hpp>
#endif

namespace {

// TODO: will need to add a test that tests permutations of a sequence of references

auto test_permutations_types() -> bool
{
auto arr = std::array {1, 2, 3};

auto seq = flux::mut_ref(arr).permutations<3>();

using SeqType = decltype(seq);
using CurType = flux::cursor_t<SeqType>;

// Sequence
static_assert(flux::sequence<SeqType>);
static_assert(flux::multipass_sequence<SeqType>);
// static_assert(flux::bounded_sequence<SeqType>);
// static_assert(flux::sized_sequence<SeqType>);
static_assert(not flux::infinite_sequence<SeqType>);
static_assert(not flux::random_access_sequence<SeqType>);
static_assert(not flux::contiguous_sequence<SeqType>);

// Cursors
static_assert(flux::regular_cursor<CurType>);
static_assert(flux::ordered_cursor<CurType>);

// Elements
// static_assert(std::same_as<flux::element_t<SeqType>, std::vector<int>>);
// static_assert(std::same_as<flux::value_t<SeqType>, std::vector<int>>);
// static_assert(flux::random_access_sequence<flux::element_t<SeqType>>);
// static_assert(flux::random_access_sequence<flux::value_t<SeqType>>);

return true;
}

constexpr auto test_permutations() -> bool
{
// Simple Array Comparison
{
auto arr = std::array {1, 2, 3};
auto seq = flux::mut_ref(arr).permutations<3>();

// Sizes
// STATIC_CHECK(seq.size() == 6);

auto cur = flux::first(seq);
auto test_comp = std::array {1, 2, 3};

// Forward Iteration Permutations
while (not flux::is_last(seq, cur)) {
// STATIC_CHECK(check_equal(flux::read_at(seq, cur), test_comp));

flux::inc(seq, cur);
std::ranges::next_permutation(test_comp);
}
}

return true;
}

constexpr auto compare_permutations_with_python_itertools() -> bool
{
// "flux" string permutations
{
/*
# Python code to generate comparison output
from itertools import permutations

perms = [''.join(p) for p in permutations("flux", 4)]
formatted = '{' + ', '.join(f'"{x}"' for x in perms) + '}'
print(formatted)
*/

auto reference = std::array<std::string, 24> {
"flux", "flxu", "fulx", "fuxl", "fxlu", "fxul", "lfux", "lfxu",
"lufx", "luxf", "lxfu", "lxuf", "uflx", "ufxl", "ulfx", "ulxf",
"uxfl", "uxlf", "xflu", "xful", "xlfu", "xluf", "xufl", "xulf"};

auto str = std::string("flux");
auto permutations = flux::permutations<4>(flux::ref(str));
auto first = flux::first(permutations);

for (auto i : flux::ints().take(24)) {
auto p = flux::read_at(permutations, first);
auto r = flux::ref(reference.at(static_cast<size_t>(i)));

// STATIC_CHECK(check_equal(p, r));

flux::inc(permutations, first);
}
}
return true;
}

} // namespace

TEST_CASE("permutations")
{
auto implementation = false;
REQUIRE(implementation);
}
Loading