From b8cead88af414f0025cd03db3616289df5241e3e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 20 Jun 2026 22:29:56 -0400 Subject: [PATCH] Make resolve and reduce methods free functions on LogicArray concept This reduces repetition, not only in code, but also in documentation. This follows the index_of and rindex_of becoming free functions on all RangedSequence. --- cpp/include/coconext/types/logic_array.hpp | 161 ++++++------------- docs/dev/type_system.md | 2 +- python/_pycoconext.pyi | 6 - tests/cpp/test_logic.cpp | 4 +- tests/cpp/test_logic_array.cpp | 175 ++++++++++++--------- 5 files changed, 148 insertions(+), 200 deletions(-) diff --git a/cpp/include/coconext/types/logic_array.hpp b/cpp/include/coconext/types/logic_array.hpp index 8e22122..5b50faa 100644 --- a/cpp/include/coconext/types/logic_array.hpp +++ b/cpp/include/coconext/types/logic_array.hpp @@ -82,88 +82,7 @@ constexpr Range make_logic_static_range() { } // namespace detail template <> -class Vector; - -namespace detail { - -// CRTP mixin providing the Logic/Bit-specific resolve member. Inherited by -// the Logic/Bit specializations of Array, Vector, StaticArraySlice, and -// ArraySlice below. The non-Logic/Bit primaries do NOT inherit this, so -// `Array::resolve(method)` and friends don't exist. -template -struct LogicArrayMixin { - - auto resolve(ResolveMethod method) const; - // Default to WEAK. - auto resolve() const { return resolve(ResolveMethod::WEAK); } - - // Reductions: fold over the array with the corresponding bitwise op. - // Empty arrays return the operation's identity (1 for AND, 0 for OR/XOR), - // matching the standard math definition and VHDL std_logic_1164's - // and_reduce/or_reduce/xor_reduce. - auto and_reduce() const { - auto const& self = *static_cast(this); - using Elem = std::ranges::range_value_t; - Elem result{Elem::_1}; - for (auto const& v : self) { - result = result & v; - } - return result; - } - - auto or_reduce() const { - auto const& self = *static_cast(this); - using Elem = std::ranges::range_value_t; - Elem result{Elem::_0}; - for (auto const& v : self) { - result = result | v; - } - return result; - } - - auto xor_reduce() const { - auto const& self = *static_cast(this); - using Elem = std::ranges::range_value_t; - Elem result{Elem::_0}; - for (auto const& v : self) { - result = result ^ v; - } - return result; - } -}; - -} // namespace detail - -// -- Logic/Bit specializations --------------------------------------------- -// -// These specializations make `Array`, `Array`, -// `Vector`, `Vector`, and slices over Logic/Bit owners -// inherit `LogicArrayMixin`, gaining `resolve(method)` as a member. The -// primary templates remain unchanged for non-Logic element types -- e.g., -// `Array` has no `resolve(method)`. - -namespace detail { - -template -class Array : public ArrayImpl, - public LogicArrayMixin> { - public: - using ArrayImpl::ArrayImpl; - using ArrayImpl::operator=; -}; - -template -class Array : public ArrayImpl, public LogicArrayMixin> { - public: - using ArrayImpl::ArrayImpl; - using ArrayImpl::operator=; -}; - -} // namespace detail - -template <> -class Vector : public detail::VectorImpl, - public detail::LogicArrayMixin> { +class Vector : public detail::VectorImpl { public: using detail::VectorImpl::VectorImpl; using detail::VectorImpl::operator=; @@ -188,8 +107,7 @@ class Vector : public detail::VectorImpl, }; template <> -class Vector : public detail::VectorImpl, - public detail::LogicArrayMixin> { +class Vector : public detail::VectorImpl { public: using detail::VectorImpl::VectorImpl; using detail::VectorImpl::operator=; @@ -208,35 +126,11 @@ class Vector : public detail::VectorImpl, } }; -// Constrained partial specs that pick up any slice whose owner's element -// type is Logic or Bit, regardless of whether the owner is Array<...>, -// Vector<...>, or const-qualified. -template - requires LogicType> -class ArraySlice : public detail::ArraySliceImpl, - public detail::LogicArrayMixin> { - public: - using detail::ArraySliceImpl::ArraySliceImpl; - using detail::ArraySliceImpl::operator=; -}; - -template - requires LogicType> -class StaticArraySlice - : public detail::StaticArraySliceImpl, - public detail::LogicArrayMixin> { - public: - using detail::StaticArraySliceImpl::StaticArraySliceImpl; - using detail::StaticArraySliceImpl::operator=; -}; - -namespace detail { - -template -auto LogicArrayMixin::resolve(ResolveMethod method) const { - auto const& self = *static_cast(this); - if constexpr (StaticRangedSequence) { - std::optional<::coconext::types::detail::Array> result{ +template + requires LogicType> +auto resolve(T const& self, ResolveMethod method) { + if constexpr (StaticRangedSequence) { + std::optional::static_range>> result{ std::in_place }; auto out = result->begin(); @@ -249,7 +143,7 @@ auto LogicArrayMixin::resolve(ResolveMethod method) const { } return result; } else { - std::optional<::coconext::types::Vector> result{std::in_place, self.range()}; + std::optional> result{std::in_place, self.range()}; auto out = result->begin(); for (auto const& v : self) { auto r = v.resolve(method); @@ -262,7 +156,44 @@ auto LogicArrayMixin::resolve(ResolveMethod method) const { } } -} // namespace detail +template + requires LogicType> +auto resolve(T const& self) { + return resolve(self, ResolveMethod::WEAK); +} + +template + requires LogicType> +auto and_reduce(T const& self) { + using Elem = std::ranges::range_value_t; + Elem result{Elem::_1}; + for (auto const& v : self) { + result = result & v; + } + return result; +} + +template + requires LogicType> +auto or_reduce(T const& self) { + using Elem = std::ranges::range_value_t; + Elem result{Elem::_0}; + for (auto const& v : self) { + result = result | v; + } + return result; +} + +template + requires LogicType> +auto xor_reduce(T const& self) { + using Elem = std::ranges::range_value_t; + Elem result{Elem::_0}; + for (auto const& v : self) { + result = result ^ v; + } + return result; +} using LogicVector = Vector; using BitVector = Vector; diff --git a/docs/dev/type_system.md b/docs/dev/type_system.md index 8fa5b1f..d98c2bf 100644 --- a/docs/dev/type_system.md +++ b/docs/dev/type_system.md @@ -561,7 +561,7 @@ which is used for compile-time bounds checks and constant folding. Same as `ArraySlice` and `StaticArraySlice`, but support logical operators like `LogicArray`. * `&`, `|`, `^`, `~` -* `and_reduce`, `or_reduce`, `xor_reduce` +* Free functions on any `RangedSequence` of `Logic`/`Bit`: `resolve(arr, method)` / `resolve(arr)`, `and_reduce(arr)`, `or_reduce(arr)`, `xor_reduce(arr)` ## `BitArray` diff --git a/python/_pycoconext.pyi b/python/_pycoconext.pyi index 5dc0274..7403586 100644 --- a/python/_pycoconext.pyi +++ b/python/_pycoconext.pyi @@ -50,10 +50,7 @@ class Logic: def __invert__(self) -> Logic: ... @property def is_resolvable(self) -> bool: ... - @overload def resolve(self, arg: str, /) -> Logic: ... - @overload - def resolve(self, arg: ResolveMethod, /) -> Logic: ... def __copy__(self) -> Logic: ... def __deepcopy__(self, arg: dict, /) -> Logic: ... @@ -94,10 +91,7 @@ class Bit: def __invert__(self) -> Bit: ... @property def is_resolvable(self) -> bool: ... - @overload def resolve(self, arg: str, /) -> Bit: ... - @overload - def resolve(self, arg: ResolveMethod, /) -> Bit: ... def __copy__(self) -> Bit: ... def __deepcopy__(self, arg: dict, /) -> Bit: ... diff --git a/tests/cpp/test_logic.cpp b/tests/cpp/test_logic.cpp index a2e3bcb..8913beb 100644 --- a/tests/cpp/test_logic.cpp +++ b/tests/cpp/test_logic.cpp @@ -356,9 +356,7 @@ TEST(TestBit, BitResolve) { EXPECT_EQ('1'_b.resolve(static_cast(99)), '1'_b); } -// No-arg resolve() defaults to WEAK -- mirroring the LogicArrayMixin shortcut -// so user code can write `r.resolve()` for the common synthesizable-values -// case. +// No-arg resolve() defaults to WEAK -- mirroring the array-form shortcut. TEST(TestLogic, LogicResolveNoArgDefaultsToWeak) { EXPECT_EQ('0'_l.resolve(), '0'_b); EXPECT_EQ('1'_l.resolve(), '1'_b); diff --git a/tests/cpp/test_logic_array.cpp b/tests/cpp/test_logic_array.cpp index dca03df..cd1f90a 100644 --- a/tests/cpp/test_logic_array.cpp +++ b/tests/cpp/test_logic_array.cpp @@ -307,33 +307,33 @@ TEST(TestLogicArray, ToStringEmpty) { TEST(TestLogicArray, ResolveEngagedOnResolvable) { auto a = to_logic_array("01LH"); - EXPECT_TRUE(a.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_TRUE(resolve(a, ResolveMethod::WEAK).has_value()); } TEST(TestLogicArray, ResolveNulloptOnMetavalue) { - EXPECT_FALSE(to_logic_array("01X0").resolve(ResolveMethod::WEAK).has_value()); - EXPECT_FALSE(to_logic_array("Z").resolve(ResolveMethod::WEAK).has_value()); - EXPECT_FALSE(to_logic_array("U").resolve(ResolveMethod::WEAK).has_value()); - EXPECT_FALSE(to_logic_array("W").resolve(ResolveMethod::WEAK).has_value()); - EXPECT_FALSE(to_logic_array("-").resolve(ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(to_logic_array("01X0"), ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(to_logic_array("Z"), ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(to_logic_array("U"), ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(to_logic_array("W"), ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(to_logic_array("-"), ResolveMethod::WEAK).has_value()); } TEST(TestLogicArray, ResolveEngagedOnEmpty) { auto a = to_logic_array(""); - EXPECT_TRUE(a.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_TRUE(resolve(a, ResolveMethod::WEAK).has_value()); } // -- resolve on arrays ------------------------------------------------------ TEST(TestLogicArray, ResolveZeros) { auto a = to_logic_array("01XZULWH-"); - auto b = a.resolve(ResolveMethod::ZEROS); + auto b = resolve(a, ResolveMethod::ZEROS); EXPECT_EQ(to_string(*b), "010000010"); } TEST(TestLogicArray, ResolveOnes) { auto a = to_logic_array("01XZULWH-"); - auto b = a.resolve(ResolveMethod::ONES); + auto b = resolve(a, ResolveMethod::ONES); EXPECT_EQ(to_string(*b), "011110111"); } @@ -342,7 +342,7 @@ TEST(TestLogicArray, ResolveWeakAcceptsResolvable) { // input must contain only resolvable-under-WEAK values for the result to // be engaged. auto a = to_logic_array("01LH"); - auto b = a.resolve(ResolveMethod::WEAK); + auto b = resolve(a, ResolveMethod::WEAK); EXPECT_EQ(to_string(*b), "0101"); } @@ -350,27 +350,26 @@ TEST(TestLogicArray, ResolveWeakReturnsNulloptOnMetavalue) { // Even a single non-resolvable value makes the whole array's resolve // return nullopt (build-then-drop). auto a = to_logic_array("01X"); - EXPECT_FALSE(a.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(a, ResolveMethod::WEAK).has_value()); } TEST(TestLogicArray, ResolveError) { auto a = to_logic_array("01X"); - EXPECT_FALSE(a.resolve(ResolveMethod::ERROR).has_value()); + EXPECT_FALSE(resolve(a, ResolveMethod::ERROR).has_value()); } TEST(TestLogicArray, ResolveErrorPass) { auto a = to_logic_array("01"); - auto b = a.resolve(ResolveMethod::ERROR); + auto b = resolve(a, ResolveMethod::ERROR); EXPECT_EQ(to_string(*b), "01"); } TEST(TestLogicArray, ResolveStaticReturnsStaticArray) { auto a = "01XZ"_l; // static LogicArray - auto b = a.resolve(ResolveMethod::ZEROS); - // Static-bound input -> static-bound output of matching range. This is the - // payoff of memberizing resolve on the specializations: the result type - // preserves Self's static range when available, and resolve always returns - // an optional Bit-valued container. + auto b = resolve(a, ResolveMethod::ZEROS); + // Static-bound input -> static-bound output of matching range. The + // StaticRangedSequence branch of the free resolve preserves T's static + // range, and resolve always returns an optional Bit-valued container. static_assert( std::is_same_v>> ); @@ -381,37 +380,38 @@ TEST(TestLogicArray, ResolveStaticReturnsStaticArray) { // matching the scalar Logic::resolve() shortcut. TEST(TestLogicArray, ResolveNoArgDefaultsToWeak) { auto resolvable = to_logic_array("01LH"); - auto a = resolvable.resolve(); + auto a = resolve(resolvable); ASSERT_TRUE(a.has_value()); EXPECT_EQ(to_string(*a), "0101"); auto mixed = to_logic_array("01X"); - EXPECT_FALSE(mixed.resolve().has_value()); + EXPECT_FALSE(resolve(mixed).has_value()); } // -- Slice resolvability --------------------------------------------------- // -// The constrained partial specs of StaticArraySlice and ArraySlice inherit the -// LogicArrayMixin too, so slices of LogicArray/BitArray/LogicVector/etc -// have resolve(method) members returning std::optional. Sub-slicing preserves -// the mixin via outer-name resolution in the slice impl. +// resolve() is a free function template constrained on +// `RangedSequence` with a `LogicType` element type, so it applies to +// slices over LogicArray/BitArray/LogicVector/etc out of the box -- and to +// sub-slices the same way, since ArraySlice/StaticArraySlice are themselves +// RangedSequence. TEST(TestLogicArray, DynSliceIsResolvable) { // to_logic_array parses MSB-first into a DOWNTO range, so "01X" has // a[2]='0', a[1]='1', a[0]='X'. auto a = to_logic_array("01X"); auto s_full = a[{2, 0}]; - EXPECT_FALSE(s_full.resolve(ResolveMethod::WEAK).has_value()); // covers the X at a[0] + EXPECT_FALSE(resolve(s_full, ResolveMethod::WEAK).has_value()); // covers the X at a[0] auto s_excl_x = a[{2, 1}]; EXPECT_TRUE( - s_excl_x.resolve(ResolveMethod::WEAK).has_value() + resolve(s_excl_x, ResolveMethod::WEAK).has_value() ); // covers '0' and '1' only } TEST(TestLogicArray, DynSliceResolveReturnsVector) { auto a = to_logic_array("01XZ"); auto s = a[{3, 0}]; - auto r = s.resolve(ResolveMethod::ZEROS); + auto r = resolve(s, ResolveMethod::ZEROS); static_assert(std::is_same_v>); EXPECT_EQ(to_string(*r), "0100"); } @@ -419,7 +419,7 @@ TEST(TestLogicArray, DynSliceResolveReturnsVector) { TEST(TestLogicArray, StaticSliceResolveReturnsStaticArray) { auto a = "01XZ"_l; // LogicArray auto s = a.slice(); - auto r = s.resolve(ResolveMethod::ZEROS); + auto r = resolve(s, ResolveMethod::ZEROS); static_assert( std::is_same_v>> ); @@ -427,46 +427,71 @@ TEST(TestLogicArray, StaticSliceResolveReturnsStaticArray) { } TEST(TestLogicArray, StaticSliceIsResolvable) { - // Exercises StaticArraySlice, R2>::resolve(method) -- the - // constrained partial spec from logic_array.hpp. Without this test a - // regression that drops the mixin from the static slice spec would only be - // caught when user code calls .resolve(method).has_value() on a sliced - // LogicArray and fails to compile. + // Exercises resolve() on StaticArraySlice, R2> -- a regression + // that drops StaticArraySlice from the RangedSequence-with-LogicType set + // would only be caught when user code calls resolve(...).has_value() on a + // sliced LogicArray and fails to compile. auto a = "01XZ"_l; // a[3]='0', a[2]='1', a[1]='X', a[0]='Z' auto s_with_x = a.slice(); EXPECT_FALSE( - s_with_x.resolve(ResolveMethod::WEAK).has_value() + resolve(s_with_x, ResolveMethod::WEAK).has_value() ); // contains '1' and 'X' auto s_clean = a.slice(); - EXPECT_TRUE(s_clean.resolve(ResolveMethod::WEAK).has_value()); // '0' and '1' + EXPECT_TRUE(resolve(s_clean, ResolveMethod::WEAK).has_value()); // '0' and '1' } -TEST(TestLogicArray, ConstOwnerDynSliceHasMixin) { - // Exercises ArraySlice> -- the constrained partial - // spec must trigger for const-qualified Logic/Bit owners too. Without this - // test a regression where range_value_t> doesn't reduce - // to Logic would leave const slices on the non-Logic primary shell and lose - // the mixin. The check is structural (the call has to compile and return a - // bool), not the value -- a const slice over a resolvable Logic array. +TEST(TestLogicArray, ConstOwnerDynSliceResolves) { + // Exercises resolve() on ArraySlice> -- a regression + // where range_value_t> doesn't reduce to Logic would + // make the constraint fail to match. The check is structural (the call has + // to compile and return a bool), not the value -- a const slice over a + // resolvable Logic array. Vector const a = to_logic_array("01LH"); auto s = a[{3, 0}]; static_assert( std::same_as const>>, "const Logic owner must produce ArraySlice>" ); - EXPECT_TRUE(s.resolve(ResolveMethod::WEAK).has_value()); // all elements are 0/1/L/H - auto r = s.resolve(ResolveMethod::WEAK); + EXPECT_TRUE(resolve(s, ResolveMethod::WEAK).has_value()); // all elements are 0/1/L/H + auto r = resolve(s, ResolveMethod::WEAK); EXPECT_EQ(to_string(*r), "0101"); // L->0, H->1 } -TEST(TestLogicArray, SubSlicePreservesMixin) { +TEST(TestLogicArray, SubSliceResolves) { auto a = to_logic_array("01XZ"); auto s = a[{3, 0}]; auto sub = s[{2, 1}]; // sub-slice via ArraySliceImpl::operator[] - // The sub-slice still has resolve(method) -- the impl returns - // ArraySlice by outer name, which resolves to the constrained - // partial spec when the element type is Logic. - EXPECT_FALSE(sub.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_FALSE(resolve(sub, ResolveMethod::WEAK).has_value()); +} + +namespace { + +struct LogicSpanAdapter { + std::vector data; + Range range_; + using value_type = Logic; + auto begin() const { return data.begin(); } + auto end() const { return data.end(); } + auto begin() { return data.begin(); } + auto end() { return data.end(); } + Range range() const noexcept { return range_; } +}; +static_assert(RangedSequence); +static_assert(!StaticRangedSequence); + +} // namespace + +TEST(TestLogicArray, ResolveOnForeignRangedSequence) { + LogicSpanAdapter a{ + {'0'_l, '1'_l, 'L'_l, 'H'_l}, + Range{3, Direction::DOWNTO, 0} + }; + auto r = resolve(a, ResolveMethod::WEAK); + ASSERT_TRUE(r.has_value()); + EXPECT_EQ(to_string(*r), "0101"); + EXPECT_EQ(and_reduce(a), '0'_l); + EXPECT_EQ(or_reduce(a), '1'_l); + EXPECT_EQ(xor_reduce(a), '0'_l); } // -- index_of / rindex_of on Logic/Bit arrays -------------------------------- @@ -491,85 +516,85 @@ TEST(TestBitArray, IndexOfOnStaticBitArray) { TEST(TestLogicArray, AndReduceAllOnes) { auto a = "1111"_l; - EXPECT_EQ(a.and_reduce(), '1'_l); + EXPECT_EQ(and_reduce(a), '1'_l); } TEST(TestLogicArray, AndReduceWithZero) { auto a = "1101"_l; - EXPECT_EQ(a.and_reduce(), '0'_l); + EXPECT_EQ(and_reduce(a), '0'_l); } TEST(TestLogicArray, AndReduceWithX) { auto a = "11X1"_l; - EXPECT_EQ(a.and_reduce(), 'X'_l); + EXPECT_EQ(and_reduce(a), 'X'_l); } TEST(TestLogicArray, AndReduceEmpty) { Vector a({}); - EXPECT_EQ(a.and_reduce(), '1'_l); // identity for AND + EXPECT_EQ(and_reduce(a), '1'_l); // identity for AND } TEST(TestLogicArray, OrReduceAllZeros) { auto a = "0000"_l; - EXPECT_EQ(a.or_reduce(), '0'_l); + EXPECT_EQ(or_reduce(a), '0'_l); } TEST(TestLogicArray, OrReduceWithOne) { auto a = "0010"_l; - EXPECT_EQ(a.or_reduce(), '1'_l); + EXPECT_EQ(or_reduce(a), '1'_l); } TEST(TestLogicArray, OrReduceWithX) { auto a = "00X0"_l; - EXPECT_EQ(a.or_reduce(), 'X'_l); + EXPECT_EQ(or_reduce(a), 'X'_l); } TEST(TestLogicArray, OrReduceEmpty) { Vector a({}); - EXPECT_EQ(a.or_reduce(), '0'_l); // identity for OR + EXPECT_EQ(or_reduce(a), '0'_l); // identity for OR } TEST(TestLogicArray, XorReduceEvenOnes) { auto a = "1100"_l; - EXPECT_EQ(a.xor_reduce(), '0'_l); + EXPECT_EQ(xor_reduce(a), '0'_l); } TEST(TestLogicArray, XorReduceOddOnes) { auto a = "1110"_l; - EXPECT_EQ(a.xor_reduce(), '1'_l); + EXPECT_EQ(xor_reduce(a), '1'_l); } TEST(TestLogicArray, XorReduceWithX) { auto a = "1X1"_l; - EXPECT_EQ(a.xor_reduce(), 'X'_l); + EXPECT_EQ(xor_reduce(a), 'X'_l); } TEST(TestLogicArray, XorReduceEmpty) { Vector a({}); - EXPECT_EQ(a.xor_reduce(), '0'_l); // identity for XOR + EXPECT_EQ(xor_reduce(a), '0'_l); // identity for XOR } TEST(TestBitArray, ReductionsReturnBit) { auto a = "1010"_b; - static_assert(std::is_same_v); - EXPECT_EQ(a.and_reduce(), '0'_b); - EXPECT_EQ(a.or_reduce(), '1'_b); - EXPECT_EQ(a.xor_reduce(), '0'_b); + static_assert(std::is_same_v); + EXPECT_EQ(and_reduce(a), '0'_b); + EXPECT_EQ(or_reduce(a), '1'_b); + EXPECT_EQ(xor_reduce(a), '0'_b); } TEST(TestLogicArray, ReductionsOnVector) { Vector a({'1'_l, '0'_l, '1'_l}); - EXPECT_EQ(a.and_reduce(), '0'_l); - EXPECT_EQ(a.or_reduce(), '1'_l); - EXPECT_EQ(a.xor_reduce(), '0'_l); + EXPECT_EQ(and_reduce(a), '0'_l); + EXPECT_EQ(or_reduce(a), '1'_l); + EXPECT_EQ(xor_reduce(a), '0'_l); } TEST(TestLogicArray, ReductionsOnSlice) { auto a = "1110"_l; auto s = a[{3, 1}]; // "111" - EXPECT_EQ(s.and_reduce(), '1'_l); - EXPECT_EQ(s.or_reduce(), '1'_l); - EXPECT_EQ(s.xor_reduce(), '1'_l); + EXPECT_EQ(and_reduce(s), '1'_l); + EXPECT_EQ(or_reduce(s), '1'_l); + EXPECT_EQ(xor_reduce(s), '1'_l); } // -- Compound bitwise assignment ------------------------------------------- @@ -1194,9 +1219,9 @@ TEST(TestBitArray, ToStringStaticBit) { TEST(TestBitArray, ResolveAlwaysEngaged) { auto a = to_bit_array("0110"); - EXPECT_TRUE(a.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_TRUE(resolve(a, ResolveMethod::WEAK).has_value()); auto empty = to_bit_array(""); - EXPECT_TRUE(empty.resolve(ResolveMethod::WEAK).has_value()); + EXPECT_TRUE(resolve(empty, ResolveMethod::WEAK).has_value()); } // -- resolve on BitArray --------------------------------------------------- @@ -1204,7 +1229,7 @@ TEST(TestBitArray, ResolveAlwaysEngaged) { TEST(TestBitArray, ResolveBitIsIdentity) { auto a = to_bit_array("0110"); static_assert( - std::is_same_v> + std::is_same_v> ); for (auto method : {ResolveMethod::ZEROS, @@ -1213,7 +1238,7 @@ TEST(TestBitArray, ResolveBitIsIdentity) { ResolveMethod::ERROR, ResolveMethod::RANDOM}) { - EXPECT_EQ(to_string(*a.resolve(method)), "0110"); + EXPECT_EQ(to_string(*resolve(a, method)), "0110"); } }