diff --git a/cpp/include/coconext/types/array.hpp b/cpp/include/coconext/types/array.hpp index 50920e5..f7e7b70 100644 --- a/cpp/include/coconext/types/array.hpp +++ b/cpp/include/coconext/types/array.hpp @@ -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(value_type const& v) const { - return detail::index_in(*this, v); - } - constexpr std::optional rindex(value_type const& v) const { - return detail::rindex_in(*this, v); - } - private: template static constexpr auto& access_(Self& self, index_type idx) { diff --git a/cpp/include/coconext/types/array_base.hpp b/cpp/include/coconext/types/array_base.hpp index 484c303..b526f9f 100644 --- a/cpp/include/coconext/types/array_base.hpp +++ b/cpp/include/coconext/types/array_base.hpp @@ -80,17 +80,19 @@ 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 -constexpr std::optional index_in( +constexpr std::optional index_of( S const& s, std::ranges::range_value_t 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(std::ranges::distance(s.begin(), it)) ); } @@ -98,7 +100,7 @@ constexpr std::optional index_in( // First HDL coordinate from the right (i.e. the last matching element in // iteration order), or nullopt if not found. template -constexpr std::optional rindex_in( +constexpr std::optional rindex_of( S const& s, std::ranges::range_value_t const& v ) { auto const rit = std::find(s.rbegin(), s.rend(), v); @@ -106,22 +108,20 @@ constexpr std::optional rindex_in( return std::nullopt; } auto const off_from_end = static_cast(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 class ArraySlice; template 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 class ArraySliceImpl { public: @@ -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(value_type const& v) const { - return detail::index_in(*this, v); - } - constexpr std::optional 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. @@ -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(value_type const& v) const { - return detail::index_in(*this, v); - } - constexpr std::optional rindex(value_type const& v) const { - return detail::rindex_in(*this, v); - } - private: ArrayT* arr_; }; diff --git a/cpp/include/coconext/types/vector.hpp b/cpp/include/coconext/types/vector.hpp index 3dcd5f3..c9ebd53 100644 --- a/cpp/include/coconext/types/vector.hpp +++ b/cpp/include/coconext/types/vector.hpp @@ -211,13 +211,6 @@ class VectorImpl { return std::reverse_iterator(begin()); } - COCONEXT_VECTOR_CONSTEXPR std::optional index(value_type const& v) const { - return detail::index_in(*this, v); - } - COCONEXT_VECTOR_CONSTEXPR std::optional rindex(value_type const& v) const { - return detail::rindex_in(*this, v); - } - private: template static COCONEXT_VECTOR_CONSTEXPR auto& access_(Self& self, index_type idx) { diff --git a/nanobind/include/bind_vector.hpp b/nanobind/include/bind_vector.hpp index 7646521..42a78c2 100644 --- a/nanobind/include/bind_vector.hpp +++ b/nanobind/include/bind_vector.hpp @@ -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"); } diff --git a/tests/cpp/test_array.cpp b/tests/cpp/test_array.cpp index 5a190c0..05474d5 100644 --- a/tests/cpp/test_array.cpp +++ b/tests/cpp/test_array.cpp @@ -142,37 +142,37 @@ 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 a(std::vector{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 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 a({10, 20, 30}); - EXPECT_FALSE(a.index(99).has_value()); + EXPECT_FALSE(index_of(a, 99).has_value()); } TEST(TestVector, IndexEmpty) { Vector a({}); - EXPECT_FALSE(a.index(0).has_value()); + EXPECT_FALSE(index_of(a, 0).has_value()); } TEST(TestVector, RindexFindsLastOccurrenceTO) { Vector a(std::vector{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 } @@ -180,40 +180,40 @@ TEST(TestVector, RindexFindsLastOccurrenceTO) { TEST(TestVector, RindexFindsLastOccurrenceDOWNTO) { Vector a(std::vector{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 a({10, 20, 30}); - EXPECT_FALSE(a.rindex(99).has_value()); + EXPECT_FALSE(rindex_of(a, 99).has_value()); } TEST(TestVector, IndexOnSlice) { // Generic Vector defaults to TO {0..4}. Vector 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 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 a({10, 20, 30, 20, 50}, Range(0, Direction::TO, 4)); auto s = a.slice(); // 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); diff --git a/tests/cpp/test_logic_array.cpp b/tests/cpp/test_logic_array.cpp index 48d6aa7..eae3609 100644 --- a/tests/cpp/test_logic_array.cpp +++ b/tests/cpp/test_logic_array.cpp @@ -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 -----------------------