Skip to content
Merged
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
7 changes: 0 additions & 7 deletions cpp/include/coconext/types/array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,6 @@ class ArrayImpl {
constexpr auto rend() noexcept { return data_.rend(); }
constexpr auto rend() const noexcept { return data_.rend(); }

constexpr std::optional<index_type> index(value_type const& v) const {
return detail::index_in(*this, v);
}
constexpr std::optional<index_type> rindex(value_type const& v) const {
return detail::rindex_in(*this, v);
}

private:
template <typename Self>
static constexpr auto& access_(Self& self, index_type idx) {
Expand Down
34 changes: 9 additions & 25 deletions cpp/include/coconext/types/array_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,48 +80,48 @@ constexpr Range::value_type offset_to_hdl_coord(
return r.direction == Direction::TO ? r.left + off : r.left - off;
}

} // namespace detail

// First HDL coordinate (from the left in iteration order) whose element equals
// `v`, or nullopt if not found. Used by Array/Vector/slice members.
// `v`, or nullopt if not found.
template <RangedSequence S>
constexpr std::optional<Range::value_type> index_in(
constexpr std::optional<Range::value_type> index_of(
S const& s, std::ranges::range_value_t<S> const& v
) {
auto const it = std::ranges::find(s, v);
if (it == s.end()) {
return std::nullopt;
}
return offset_to_hdl_coord(
return detail::offset_to_hdl_coord(
s.range(), static_cast<size_t>(std::ranges::distance(s.begin(), it))
);
}

// First HDL coordinate from the right (i.e. the last matching element in
// iteration order), or nullopt if not found.
template <RangedSequence S>
constexpr std::optional<Range::value_type> rindex_in(
constexpr std::optional<Range::value_type> rindex_of(
S const& s, std::ranges::range_value_t<S> const& v
) {
auto const rit = std::find(s.rbegin(), s.rend(), v);
if (rit == s.rend()) {
return std::nullopt;
}
auto const off_from_end = static_cast<size_t>(std::distance(s.rbegin(), rit));
return offset_to_hdl_coord(s.range(), s.range().length() - 1 - off_from_end);
return detail::offset_to_hdl_coord(s.range(), s.range().length() - 1 - off_from_end);
}

} // namespace detail

template <typename ArrayT>
class ArraySlice;
template <typename ArrayT, Range R>
class StaticArraySlice;

namespace detail {

// We split out the implementations into a base class so that we can reuse then in the
// Logic/Bit-aware specializations in logic_array.hpp. LogicArray is just an Array with
// extra members. BitArray is too, for now...

namespace detail {

template <typename ArrayT>
class ArraySliceImpl {
public:
Expand Down Expand Up @@ -237,15 +237,6 @@ class ArraySliceImpl {
constexpr auto rbegin() const noexcept { return std::reverse_iterator(end()); }
constexpr auto rend() const noexcept { return std::reverse_iterator(begin()); }

// First HDL coordinate (from the left/right respectively) whose element
// equals `v`, or nullopt if not found.
constexpr std::optional<index_type> index(value_type const& v) const {
return detail::index_in(*this, v);
}
constexpr std::optional<index_type> rindex(value_type const& v) const {
return detail::rindex_in(*this, v);
}

private:
// Cache the begin pointer. This is just one stack pointer for a temporary object, and
// it saves recomputing the offset on every element access.
Expand Down Expand Up @@ -412,13 +403,6 @@ class StaticArraySliceImpl {
constexpr auto rbegin() const noexcept { return std::reverse_iterator(end()); }
constexpr auto rend() const noexcept { return std::reverse_iterator(begin()); }

constexpr std::optional<index_type> index(value_type const& v) const {
return detail::index_in(*this, v);
}
constexpr std::optional<index_type> rindex(value_type const& v) const {
return detail::rindex_in(*this, v);
}

private:
ArrayT* arr_;
};
Expand Down
7 changes: 0 additions & 7 deletions cpp/include/coconext/types/vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,6 @@ class VectorImpl {
return std::reverse_iterator(begin());
}

COCONEXT_VECTOR_CONSTEXPR std::optional<index_type> index(value_type const& v) const {
return detail::index_in(*this, v);
}
COCONEXT_VECTOR_CONSTEXPR std::optional<index_type> rindex(value_type const& v) const {
return detail::rindex_in(*this, v);
}

private:
template <typename Self>
static COCONEXT_VECTOR_CONSTEXPR auto& access_(Self& self, index_type idx) {
Expand Down
6 changes: 4 additions & 2 deletions nanobind/include/bind_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ void bind_array(nb::module_& m, char const* name) {

.def(
"__contains__",
[](VectorType const& a, ValueT const& val) { return a.index(val).has_value(); }
[](VectorType const& a, ValueT const& val) {
return index_of(a, val).has_value();
}
)

.def("index", [](VectorType const& a, ValueT const& val) {
auto idx = a.index(val);
auto idx = index_of(a, val);
if (!idx.has_value()) {
throw nb::value_error("Value not found in array");
}
Expand Down
32 changes: 16 additions & 16 deletions tests/cpp/test_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,78 +142,78 @@ TEST(TestVector, FindElementMissing) {

// -- index / rindex ---------------------------------------------------------
//
// index(v) returns the first HDL coordinate (from the left in iteration
// order) whose element equals v; rindex(v) is the same from the right (i.e.
// the last matching element). Both return nullopt when not found.
// index_of(seq, v) returns the first HDL coordinate (from the left in
// iteration order) whose element equals v; rindex_of is the same from the
// right (i.e. the last matching element). Both return nullopt when not found.

TEST(TestVector, IndexFoundTO) {
Vector<int> a(std::vector<int>{10, 20, 30, 20}, Range(0, Direction::TO, 3));
auto i = a.index(20);
auto i = index_of(a, 20);
ASSERT_TRUE(i.has_value());
EXPECT_EQ(*i, 1); // first 20 is at HDL coord 1
}

TEST(TestVector, IndexFoundDOWNTO) {
Vector<int> a({10, 20, 30}); // default DOWNTO {2..0}: a[2]=10, a[1]=20, a[0]=30
auto i = a.index(20);
auto i = index_of(a, 20);
ASSERT_TRUE(i.has_value());
EXPECT_EQ(*i, 1); // 20 is at HDL coord 1 (DOWNTO)
}

TEST(TestVector, IndexNotFound) {
Vector<int> a({10, 20, 30});
EXPECT_FALSE(a.index(99).has_value());
EXPECT_FALSE(index_of(a, 99).has_value());
}

TEST(TestVector, IndexEmpty) {
Vector<int> a({});
EXPECT_FALSE(a.index(0).has_value());
EXPECT_FALSE(index_of(a, 0).has_value());
}

TEST(TestVector, RindexFindsLastOccurrenceTO) {
Vector<int> a(std::vector<int>{10, 20, 30, 20}, Range(0, Direction::TO, 3));
auto i = a.rindex(20);
auto i = rindex_of(a, 20);
ASSERT_TRUE(i.has_value());
EXPECT_EQ(*i, 3); // last 20 is at HDL coord 3
}

TEST(TestVector, RindexFindsLastOccurrenceDOWNTO) {
Vector<int> a(std::vector<int>{10, 20, 30, 20}, Range(3, Direction::DOWNTO, 0));
// a[3]=10, a[2]=20, a[1]=30, a[0]=20. Last 20 in iteration is at HDL 0.
auto i = a.rindex(20);
auto i = rindex_of(a, 20);
ASSERT_TRUE(i.has_value());
EXPECT_EQ(*i, 0);
}

TEST(TestVector, RindexNotFound) {
Vector<int> a({10, 20, 30});
EXPECT_FALSE(a.rindex(99).has_value());
EXPECT_FALSE(rindex_of(a, 99).has_value());
}

TEST(TestVector, IndexOnSlice) {
// Generic Vector<int> defaults to TO {0..4}.
Vector<int> a({10, 20, 30, 40, 50});
auto s = a[{1, 3}]; // TO slice covering HDL coords 1, 2, 3 -> 20, 30, 40
auto i = s.index(30);
auto i = index_of(s, 30);
ASSERT_TRUE(i.has_value());
EXPECT_EQ(*i, 2);
}

TEST(TestStaticArray, IndexAndRindex) {
Array<int, Range{0, Direction::TO, 4}> a({10, 20, 30, 20, 50});
auto first = a.index(20);
auto last = a.rindex(20);
auto first = index_of(a, 20);
auto last = rindex_of(a, 20);
ASSERT_TRUE(first.has_value() && last.has_value());
EXPECT_EQ(*first, 1);
EXPECT_EQ(*last, 3);
EXPECT_FALSE(a.index(99).has_value());
EXPECT_FALSE(index_of(a, 99).has_value());
}

TEST(TestVectorStaticSlice, IndexAndRindex) {
Vector<int> a({10, 20, 30, 20, 50}, Range(0, Direction::TO, 4));
auto s = a.slice<Range{1, Direction::TO, 3}>(); // values 20, 30, 20
auto first = s.index(20);
auto last = s.rindex(20);
auto first = index_of(s, 20);
auto last = rindex_of(s, 20);
ASSERT_TRUE(first.has_value() && last.has_value());
EXPECT_EQ(*first, 1);
EXPECT_EQ(*last, 3);
Expand Down
16 changes: 8 additions & 8 deletions tests/cpp/test_logic_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,22 +437,22 @@ TEST(TestLogicArray, SubSlicePreservesMixin) {
EXPECT_FALSE(sub.is_resolvable());
}

// -- index / rindex on Logic/Bit arrays -----------------------------------
// -- index_of / rindex_of on Logic/Bit arrays --------------------------------

TEST(TestLogicArray, IndexInheritedOnLogicVector) {
TEST(TestLogicArray, IndexOfOnLogicVector) {
auto a = "10X10"_l; // DOWNTO {4..0}: a[4]=1, a[3]=0, a[2]=X, a[1]=1, a[0]=0
auto first_one = a.index('1'_l);
auto last_one = a.rindex('1'_l);
auto first_one = index_of(a, '1'_l);
auto last_one = rindex_of(a, '1'_l);
ASSERT_TRUE(first_one.has_value() && last_one.has_value());
EXPECT_EQ(*first_one, 4); // first '1' in iteration -> highest HDL coord
EXPECT_EQ(*last_one, 1);
EXPECT_FALSE(a.index('U'_l).has_value());
EXPECT_FALSE(index_of(a, 'U'_l).has_value());
}

TEST(TestBitArray, IndexInheritedOnStaticBitArray) {
TEST(TestBitArray, IndexOfOnStaticBitArray) {
BitArray<4> a({'1'_b, '0'_b, '1'_b, '0'_b});
EXPECT_EQ(*a.index('1'_b), 3); // DOWNTO {3..0}, first '1' is a[3]
EXPECT_EQ(*a.rindex('1'_b), 1); // last '1' is a[1]
EXPECT_EQ(*index_of(a, '1'_b), 3); // DOWNTO {3..0}, first '1' is a[3]
EXPECT_EQ(*rindex_of(a, '1'_b), 1); // last '1' is a[1]
}

// -- Reductions: and_reduce / or_reduce / xor_reduce -----------------------
Expand Down
Loading