Skip to content

Fix VecXX implementation#1851

Merged
shimat merged 6 commits into
mainfrom
fix_vec
Mar 31, 2026
Merged

Fix VecXX implementation#1851
shimat merged 6 commits into
mainfrom
fix_vec

Conversation

@shimat
Copy link
Copy Markdown
Owner

@shimat shimat commented Mar 31, 2026

This pull request introduces several improvements and API changes to the Vec struct family in OpenCvSharp, focusing on interface consistency, new utility methods, and operator enhancements. The changes make the Vec types more flexible and efficient, especially for modern .NET versions, and improve clarity and maintainability.

Key changes include:

Interface and API Consistency:

  • Removed the out variance from the TElem type parameter in IVec<TSelf, TElem>, and made the indexer read-write instead of read-only, allowing both getting and setting elements. [1] [2]

.NET API Enhancements:

  • Added AsSpan() methods to all Vec structs (e.g., Vec2b, Vec2d, Vec2f, Vec2i, Vec2s, Vec2w, Vec3b, Vec3d, Vec3f) for .NET Standard 2.1+ to enable efficient, allocation-free access to the underlying data. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Operator Improvements:

  • Added unary negation operators (e.g., -a) to all 2- and 3-element Vec structs, enabling concise element-wise negation. [1] [2] [3] [4] [5] [6]
  • Updated equality (==) and inequality (!=) operators for Vec2d, Vec2f, Vec3d, and Vec3f to compare elements directly instead of relying on Equals, ensuring more predictable behavior. [1] [2] [3] [4]

Code Cleanup:

  • Removed unnecessary [Serializable] and [SuppressMessage] attributes, and suppressed CA1051 warnings via pragmas, simplifying the codebase and reducing clutter. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

These changes modernize the Vec types, improve their usability in high-performance scenarios, and bring the codebase in line with current .NET best practices.

Summary by CodeRabbit

  • New Features

    • Added AsSpan() on many vector types (span-based access on supported targets)
    • Added unary negation operator for many vector types
  • Refactor

    • Vector indexers now support assignment (mutable indexer)
    • Equality/inequality operators use component-wise semantics
    • Serialization metadata attributes removed from vector types; analyzer suppression consolidated
  • Tests

    • Expanded vector tests (arithmetic, equality, indexer bounds, ToString, AsSpan, conversions, unary minus)

@shimat shimat self-assigned this Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 670cceb5-3317-46b0-83f6-7dc7c05e2ec3

📥 Commits

Reviewing files that changed from the base of the PR and between df47d6f and aca15a7.

📒 Files selected for processing (1)
  • test/OpenCvSharp.Tests/core/VecTest.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/OpenCvSharp.Tests/core/VecTest.cs

📝 Walkthrough

Walkthrough

Removed covariance from IVec<TSelf, TElem> and made its indexer read/write. Many Vec2/3/4/6 structs removed [Serializable] and CA1051 suppression attributes, added unary negation operators, added conditional AsSpan() methods, changed equality/inequality to component-wise checks, and expanded Vec tests.

Changes

Cohort / File(s) Summary
Interface variance & mutability
src/OpenCvSharp/Modules/core/Struct/Vec/IVec.cs
Removed out variance on TElem; changed indexer from read-only to read/write (TElem this[int i] { get; set; }). Implementers must provide setter and review variance usage.
Vec2 types
src/OpenCvSharp/Modules/core/Struct/Vec/Vec2b.cs, .../Vec2d.cs, .../Vec2f.cs, .../Vec2i.cs, .../Vec2s.cs, .../Vec2w.cs
Removed [Serializable]/per-member CA1051 suppressions (file-level pragma used), added unary - operators, added conditional AsSpan() (#if !NETSTANDARD2_0), and adjusted equality/inequality to component-wise logic.
Vec3 types
src/OpenCvSharp/Modules/core/Struct/Vec/Vec3b.cs, .../Vec3d.cs, .../Vec3f.cs, .../Vec3i.cs, .../Vec3s.cs, .../Vec3w.cs
Same pattern as Vec2: attribute removals/pragmas, unary negation operators, conditional AsSpan(), and component-wise ==/!= changes.
Vec4 types
src/OpenCvSharp/Modules/core/Struct/Vec/Vec4b.cs, .../Vec4d.cs, .../Vec4f.cs, .../Vec4i.cs, .../Vec4s.cs, .../Vec4w.cs
Removed attributes/replaced with file-level pragma, added unary - operators, added conditional AsSpan(), and updated equality/inequality to direct field comparisons.
Vec6 types
src/OpenCvSharp/Modules/core/Struct/Vec/Vec6b.cs, .../Vec6d.cs, .../Vec6f.cs, .../Vec6i.cs, .../Vec6s.cs, .../Vec6w.cs
Same changes for 6-element vectors: attribute removals/pragmas, unary negation operators, conditional AsSpan(), and component-wise equality/inequality updates.
Tests
test/OpenCvSharp.Tests/core/VecTest.cs
Added many xUnit tests covering arithmetic (including saturating semantics), equality/hashcode, deconstruction, indexer bounds and setter behavior, ToString, conversions, unary minus edge cases, NaN equality differences, and AsSpan() mutation tests (guarded by framework macros).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "Fix VecXX implementation" is overly vague and does not clearly convey the scope of substantial API and structural changes made across 24 Vec struct files and their interface. Consider using a more specific title that reflects the main changes, such as "Add AsSpan, unary minus operators, and modernize Vec structs" or "Refactor Vec types: add AsSpan, operators, and remove serialization attributes".
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 85.06% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix_vec

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs`:
- Around line 196-203: The == and != operators on Vec6d currently compare
doubles with raw ==/!= which disagrees with Vec6d.Equals that uses double.Equals
(so NaN components compare differently); update Vec6d.operator == and
Vec6d.operator != to use double.Equals for each Item0..Item5 component (matching
the logic in Equals) so that NaN is treated consistently, and ensure operator !=
is the logical negation of the new == implementation.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68486a4a-506a-4a50-b222-12948708d696

📥 Commits

Reviewing files that changed from the base of the PR and between 8c1cd34 and fd9bee7.

📒 Files selected for processing (25)
  • src/OpenCvSharp/Modules/core/Struct/Vec/IVec.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6w.cs

Comment thread src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 31, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs

Commit: e5b38c69141ed8df1df0604d34f901530579968c

The changes have been pushed to the fix_vec branch.

Time taken: 2m 0s

coderabbitai Bot and others added 2 commits March 31, 2026 13:39
Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <[email protected]>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
test/OpenCvSharp.Tests/core/VecTest.cs (2)

242-247: Specify the string comparison mode in these assertions.

These Assert.Contains calls are triggering CA1307 in CI. Passing StringComparison.Ordinal keeps the tests explicit and removes the analyzer noise.

🩹 Suggested cleanup
-        Assert.Contains("Vec2b", new Vec2b(1, 2).ToString());
-        Assert.Contains("1", new Vec2b(1, 2).ToString());
+        Assert.Contains("Vec2b", new Vec2b(1, 2).ToString(), StringComparison.Ordinal);
+        Assert.Contains("1", new Vec2b(1, 2).ToString(), StringComparison.Ordinal);

-        Assert.Contains("Vec3f", new Vec3f(1f, 2f, 3f).ToString());
-        Assert.Contains("Vec6i", new Vec6i(1, 2, 3, 4, 5, 6).ToString());
-        Assert.Contains("Vec4d", new Vec4d(1.0, 2.0, 3.0, 4.0).ToString());
+        Assert.Contains("Vec3f", new Vec3f(1f, 2f, 3f).ToString(), StringComparison.Ordinal);
+        Assert.Contains("Vec6i", new Vec6i(1, 2, 3, 4, 5, 6).ToString(), StringComparison.Ordinal);
+        Assert.Contains("Vec4d", new Vec4d(1.0, 2.0, 3.0, 4.0).ToString(), StringComparison.Ordinal);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/OpenCvSharp.Tests/core/VecTest.cs` around lines 242 - 247, The
Assert.Contains calls for Vec2b/Vec3f/Vec6i/Vec4d ToString() are triggering
CA1307; update each Assert.Contains invocation to use the overload that accepts
a StringComparison and pass StringComparison.Ordinal (e.g.,
Assert.Contains("Vec2b", new Vec2b(...).ToString(), StringComparison.Ordinal)),
referencing the Assert.Contains method and the ToString() results on Vec2b,
Vec3f, Vec6i, and Vec4d.

309-326: Avoid x == x / x != x in these operator tests.

The semantics being tested are good, but the self-comparisons generate CS1718 on every target. Compare against an equivalent copy/value instead so the NaN behavior stays covered without compiler noise.

🩹 Suggested cleanup
         var vfNaN = new Vec2f(float.NaN, 1f);
+        var vfNaNCopy = new Vec2f(float.NaN, 1f);
         Assert.True(vfNaN.Equals(vfNaN));   // IEquatable contract: reflexive
-        Assert.False(vfNaN == vfNaN);        // IEEE 754: NaN != NaN
-        Assert.True(vfNaN != vfNaN);
+        Assert.False(vfNaN == vfNaNCopy);   // IEEE 754: NaN != NaN
+        Assert.True(vfNaN != vfNaNCopy);

         var vfNormal = new Vec2f(1f, 2f);
-        Assert.True(vfNormal == vfNormal);
-        Assert.False(vfNormal != vfNormal);
+        var vfNormalCopy = new Vec2f(1f, 2f);
+        Assert.True(vfNormal == vfNormalCopy);
+        Assert.False(vfNormal != vfNormalCopy);

         // double
         var vdNaN = new Vec3d(double.NaN, 1.0, 2.0);
+        var vdNaNCopy = new Vec3d(double.NaN, 1.0, 2.0);
         Assert.True(vdNaN.Equals(vdNaN));
-        Assert.False(vdNaN == vdNaN);
-        Assert.True(vdNaN != vdNaN);
+        Assert.False(vdNaN == vdNaNCopy);
+        Assert.True(vdNaN != vdNaNCopy);

         var vdNormal = new Vec3d(1.0, 2.0, 3.0);
-        Assert.True(vdNormal == vdNormal);
-        Assert.False(vdNormal != vdNormal);
+        var vdNormalCopy = new Vec3d(1.0, 2.0, 3.0);
+        Assert.True(vdNormal == vdNormalCopy);
+        Assert.False(vdNormal != vdNormalCopy);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/OpenCvSharp.Tests/core/VecTest.cs` around lines 309 - 326, Replace the
self-comparisons that trigger CS1718 by creating equivalent copy/value instances
and using those for operator tests: instead of asserting vfNaN == vfNaN or vfNaN
!= vfNaN, construct a same-valued copy (e.g., vfNaNCopy) and assert vfNaN ==
vfNaNCopy and vfNaN != vfNaNCopy as appropriate; do the same for vfNormal, vdNaN
and vdNormal with Vec2f/Vec3d so the IEquatable.Equals reflexive checks remain
but operator ==/!= tests compare distinct but equal values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/OpenCvSharp.Tests/core/VecTest.cs`:
- Line 132: The test assertion for Vec4b division is wrong: update the expected
value in the Assert.Equal for Vec4b division in VecTest.cs so it matches the
midpoint-rounding behavior used by Vec3b (e.g., 1/2 -> 1, 3/2 -> 2); replace the
current expected new Vec4b(0, 1, 1, 2) with new Vec4b(1, 1, 2, 2) to reflect
correct rounding when dividing new Vec4b(1, 2, 3, 4) by 2.
- Around line 265-268: The test expects Vec2f.ToVec2i() to round components
rather than truncate; update the Vec2f.ToVec2i() implementation (in class Vec2f,
method ToVec2i) to convert each component using rounding instead of a direct
cast—e.g., replace (int)Item0/(int)Item1 truncation with
(int)System.Math.Round(Item0) and (int)System.Math.Round(Item1) (apply the same
change for both components) so Vec2f(1.5f,2.5f).ToVec2i() yields Vec2i(2,2).

---

Nitpick comments:
In `@test/OpenCvSharp.Tests/core/VecTest.cs`:
- Around line 242-247: The Assert.Contains calls for Vec2b/Vec3f/Vec6i/Vec4d
ToString() are triggering CA1307; update each Assert.Contains invocation to use
the overload that accepts a StringComparison and pass StringComparison.Ordinal
(e.g., Assert.Contains("Vec2b", new Vec2b(...).ToString(),
StringComparison.Ordinal)), referencing the Assert.Contains method and the
ToString() results on Vec2b, Vec3f, Vec6i, and Vec4d.
- Around line 309-326: Replace the self-comparisons that trigger CS1718 by
creating equivalent copy/value instances and using those for operator tests:
instead of asserting vfNaN == vfNaN or vfNaN != vfNaN, construct a same-valued
copy (e.g., vfNaNCopy) and assert vfNaN == vfNaNCopy and vfNaN != vfNaNCopy as
appropriate; do the same for vfNormal, vdNaN and vdNormal with Vec2f/Vec3d so
the IEquatable.Equals reflexive checks remain but operator ==/!= tests compare
distinct but equal values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed863f4b-e3fe-4d1b-b10d-5c241a7e6820

📥 Commits

Reviewing files that changed from the base of the PR and between fd9bee7 and 4c125ca.

📒 Files selected for processing (1)
  • test/OpenCvSharp.Tests/core/VecTest.cs

Comment thread test/OpenCvSharp.Tests/core/VecTest.cs Outdated
Comment thread test/OpenCvSharp.Tests/core/VecTest.cs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs (1)

197-204: ⚠️ Potential issue | 🔴 Critical

Vec6d operators violate the documented IEEE 754 NaN semantics contract.

At lines 197 and 204, Vec6d uses double.Equals() for equality comparisons, causing NaN == NaN to return true. This violates the explicitly documented contract in the test file (VecTest.cs line 302–304) which requires operator == to follow IEEE 754 semantics where NaN == NaN is false. All other floating Vec types (Vec2d, Vec4d, Vec2f, Vec4f) correctly use raw ==/!= operators. Additionally, Vec6d is not covered by the FloatingPointNaNEquality test, leaving this bug undetected.

Proposed fix
-    public static bool operator ==(Vec6d a, Vec6d b) => a.Item0.Equals(b.Item0) && a.Item1.Equals(b.Item1) && a.Item2.Equals(b.Item2) && a.Item3.Equals(b.Item3) && a.Item4.Equals(b.Item4) && a.Item5.Equals(b.Item5);
+    public static bool operator ==(Vec6d a, Vec6d b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2 && a.Item3 == b.Item3 && a.Item4 == b.Item4 && a.Item5 == b.Item5;

-    public static bool operator !=(Vec6d a, Vec6d b) => !a.Item0.Equals(b.Item0) || !a.Item1.Equals(b.Item1) || !a.Item2.Equals(b.Item2) || !a.Item3.Equals(b.Item3) || !a.Item4.Equals(b.Item4) || !a.Item5.Equals(b.Item5);
+    public static bool operator !=(Vec6d a, Vec6d b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2 || a.Item3 != b.Item3 || a.Item4 != b.Item4 || a.Item5 != b.Item5;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs` around lines 197 - 204, The
Vec6d equality operators currently use double.Equals (in operator == and
operator !=) which makes NaNs compare equal; change Vec6d.operator== to use the
raw double == for each component (e.g., a.Item0 == b.Item0 && ... for all six
items) and implement operator!= as the logical negation of operator== (e.g.,
return !(a == b)) so IEEE 754 NaN semantics are preserved; update the
implementations for the methods named operator== and operator!= in the Vec6d
struct accordingly.
🧹 Nitpick comments (1)
test/OpenCvSharp.Tests/core/VecTest.cs (1)

306-331: Consider extending NaN/AsSpan tests to newly touched floating Vec types.

Great coverage bump overall. Since this PR touches Vec4d, Vec6f, and Vec6d, adding targeted assertions for those in FloatingPointNaNEquality/AsSpan would better lock the intended behavior and prevent regressions.

Also applies to: 333-362

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/OpenCvSharp.Tests/core/VecTest.cs` around lines 306 - 331, Extend the
existing FloatingPointNaNEquality and AsSpan tests to cover the newly touched
vector types Vec4d, Vec6f, and Vec6d: for FloatingPointNaNEquality create one
NaN-containing instance per type (e.g., Vec4d with double.NaN in one element,
Vec6f/Vec6d with float/double.NaN) and one normal instance, then assert
.Equals(self) is true while == returns false and != returns true for the NaN
instance and that normal instances compare equal; for AsSpan create instances
for Vec4d, Vec6f, Vec6d and assert their AsSpan() length and element values (or
sequence equality against an expected array) match expected contents to lock in
span behavior. Ensure you reference the test method names
FloatingPointNaNEquality and AsSpan and the types Vec4d, Vec6f, Vec6d when
adding these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec2f.cs`:
- Around line 142-149: The == and != operators in Vec2f must be made consistent
with the existing Equals(Vec2f) behavior (which treats NaN pairs as equal);
change operator == to return a.Equals(b) and operator != to return !a.Equals(b)
so both operator overloads delegate to the Vec2f.Equals(Vec2f) implementation.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs`:
- Line 86: The Add and Subtract methods in Vec2i perform arithmetic in int
context which allows overflow before passing results to SaturateCast; to match
the unary operator- behavior you must widen both operands to long inside Add and
Subtract (e.g., cast Item0/Item1 to long, perform the + or - on longs) and then
call SaturateCast.ToInt32 on the long result so boundary cases (like
int.MinValue negation) saturate correctly; update Vec2i.Add, Vec2i.Subtract (and
ensure consistency with operator - and SaturateCast usage) to use long
arithmetic.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs`:
- Around line 149-156: The operator implementations for Vec3d are inconsistent
with the Equals method: replace the raw component comparisons in the operator ==
and operator != overloads by using Double.Equals for each component so NaN
values are treated consistently (i.e., implement operator == to return
Double.Equals(a.Item0,b.Item0) && Double.Equals(a.Item1,b.Item1) &&
Double.Equals(a.Item2,b.Item2) and operator != as the negation of that),
ensuring behavior matches the Equals(Vec3d) implementation.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec4f.cs`:
- Around line 175-182: The equality operators on Vec4f are inconsistent with
Equals(Vec4f) for NaN-containing components; update operator== and operator!= to
delegate to the existing Equals(Vec4f) logic so both semantics match (i.e., have
operator== call a.Equals(b) and operator!= return !a.Equals(b)); reference the
Vec4f type and its operator==, operator!= and Equals(Vec4f) methods when making
the change.

---

Duplicate comments:
In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs`:
- Around line 197-204: The Vec6d equality operators currently use double.Equals
(in operator == and operator !=) which makes NaNs compare equal; change
Vec6d.operator== to use the raw double == for each component (e.g., a.Item0 ==
b.Item0 && ... for all six items) and implement operator!= as the logical
negation of operator== (e.g., return !(a == b)) so IEEE 754 NaN semantics are
preserved; update the implementations for the methods named operator== and
operator!= in the Vec6d struct accordingly.

---

Nitpick comments:
In `@test/OpenCvSharp.Tests/core/VecTest.cs`:
- Around line 306-331: Extend the existing FloatingPointNaNEquality and AsSpan
tests to cover the newly touched vector types Vec4d, Vec6f, and Vec6d: for
FloatingPointNaNEquality create one NaN-containing instance per type (e.g.,
Vec4d with double.NaN in one element, Vec6f/Vec6d with float/double.NaN) and one
normal instance, then assert .Equals(self) is true while == returns false and !=
returns true for the NaN instance and that normal instances compare equal; for
AsSpan create instances for Vec4d, Vec6f, Vec6d and assert their AsSpan() length
and element values (or sequence equality against an expected array) match
expected contents to lock in span behavior. Ensure you reference the test method
names FloatingPointNaNEquality and AsSpan and the types Vec4d, Vec6f, Vec6d when
adding these assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 698cc083-647b-451c-a619-50491cc92e65

📥 Commits

Reviewing files that changed from the base of the PR and between b685318 and df47d6f.

📒 Files selected for processing (25)
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6w.cs
  • test/OpenCvSharp.Tests/core/VecTest.cs
🚧 Files skipped from review as they are similar to previous changes (13)
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2b.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3f.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec6i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2w.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec2d.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3s.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec4i.cs
  • src/OpenCvSharp/Modules/core/Struct/Vec/Vec3i.cs

Comment on lines +142 to +149
public static bool operator ==(Vec2f a, Vec2f b) => a.Item0 == b.Item0 && a.Item1 == b.Item1;

/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec2f a, Vec2f b) => !(a == b);
public static bool operator !=(Vec2f a, Vec2f b) => a.Item0 != b.Item0 || a.Item1 != b.Item1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In .NET/C#, does float.NaN.Equals(float.NaN)returntruewhilefloat.NaN == float.NaNreturnsfalse?

💡 Result:

Yes, in .NET/C#, float.NaN.Equals(float.NaN) returns true while float.NaN == float.NaN returns false. This behavior follows the IEEE 754 floating-point standard for the == operator, which treats NaN as not equal to anything, including itself. However, the Equals method on float (Single) is overridden to return true for two NaNs to satisfy the Object.Equals contract requirements (reflexivity: x.Equals(x) must be true, and consistency for use in collections like dictionaries). Official Microsoft documentation for Single.NaN explicitly demonstrates this: Console.WriteLine("NaN == NaN: {0}", Single.NaN == Single.NaN); // False Console.WriteLine("NaN.Equals(NaN): {0}", Single.NaN.Equals(Single.NaN)); // True The same applies to double.NaN. Use float.IsNaN to detect NaN values, as direct comparisons always fail.

Citations:


Align ==/!= operators with Equals method to handle NaN consistently.

The operators at lines 142–149 use raw float comparisons (==/!=), which follow IEEE 754 semantics where NaN == NaN is false. However, Equals(Vec2f) at line 128 uses Single.Equals, which returns true for two NaN values. This creates an inconsistency: (NaN, 1).Equals((NaN, 1)) returns true, but (NaN, 1) == (NaN, 1) returns false.

Either delegate the operators to Equals:

-    public static bool operator ==(Vec2f a, Vec2f b) => a.Item0 == b.Item0 && a.Item1 == b.Item1;
-    public static bool operator !=(Vec2f a, Vec2f b) => a.Item0 != b.Item0 || a.Item1 != b.Item1;
+    public static bool operator ==(Vec2f a, Vec2f b) => a.Equals(b);
+    public static bool operator !=(Vec2f a, Vec2f b) => !a.Equals(b);

Or update Equals to use raw comparisons. Choose one approach to ensure consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec2f.cs` around lines 142 - 149, The
== and != operators in Vec2f must be made consistent with the existing
Equals(Vec2f) behavior (which treats NaN pairs as equal); change operator == to
return a.Equals(b) and operator != to return !a.Equals(b) so both operator
overloads delegate to the Vec2f.Equals(Vec2f) implementation.

public static Vec2i operator -(Vec2i a, Vec2i b) => a.Subtract(b);
public static Vec2i operator *(Vec2i a, double alpha) => a.Multiply(alpha);
public static Vec2i operator /(Vec2i a, double alpha) => a.Divide(alpha);
public static Vec2i operator -(Vec2i a) => new(SaturateCast.ToInt32(-(long)a.Item0), SaturateCast.ToInt32(-(long)a.Item1));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In C#, are intadditions and subtractions evaluated in uncheckedintby default, so0 - int.MinValue wraps before the value is passed to another method unless one operand is widened first?

💡 Result:

Yes, in C#, additions and subtractions on int operands are evaluated in the default unchecked context, causing wraparound on overflow. Specifically, 0 - int.MinValue computes to int.MaxValue due to two's complement wraparound (-(-2147483648) = 2147483647, which fits in int), with no overflow occurring. This wrapped int result is then passed to another method unchanged unless widening (e.g., to long) occurs first. There is no automatic widening of both operands to a larger type like long for binary int operations; binary numeric promotions only apply when mixing certain smaller types or with floating-point, but two ints stay as int. The default is unchecked unless the /checked+ compiler option is set or explicit checked blocks are used. Constant expressions are checked at compile-time, but runtime non-constant int arithmetic defaults to unchecked wraparound.

Citations:


🏁 Script executed:

# First, check if the file exists and get its basic info
ls -la src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs
wc -l src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs

Repository: shimat/opencvsharp

Length of output: 212


🏁 Script executed:

# Read the file to see line 86 and surrounding context
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | sed -n '80,95p'

Repository: shimat/opencvsharp

Length of output: 803


🏁 Script executed:

# Find the Add and Subtract methods in the file
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | grep -A 2 "public readonly Vec2i Add\|public readonly Vec2i Subtract"

Repository: shimat/opencvsharp

Length of output: 419


🏁 Script executed:

# Get a broader view to understand the structure
cat -n src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs | head -170

Repository: shimat/opencvsharp

Length of output: 6027


Match the new unary saturation behavior in the binary +/- paths.

Line 86 widens to long before negation, so -new Vec2i(int.MinValue, 0) saturates correctly. The Add and Subtract methods at lines 50–52 and 59–61 perform arithmetic in int context, causing overflow to wrap before SaturateCast runs, producing inconsistent boundary results.

Widen both operands to long in Add and Subtract to match the unary negation behavior:

Minimal fix
 public readonly Vec2i Add(Vec2i other) => new(
-    SaturateCast.ToInt32(Item0 + other.Item0),
-    SaturateCast.ToInt32(Item1 + other.Item1));
+    SaturateCast.ToInt32((long)Item0 + other.Item0),
+    SaturateCast.ToInt32((long)Item1 + other.Item1));

 public readonly Vec2i Subtract(Vec2i other) => new(
-    SaturateCast.ToInt32(Item0 - other.Item0),
-    SaturateCast.ToInt32(Item1 - other.Item1));
+    SaturateCast.ToInt32((long)Item0 - other.Item0),
+    SaturateCast.ToInt32((long)Item1 - other.Item1));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec2i.cs` at line 86, The Add and
Subtract methods in Vec2i perform arithmetic in int context which allows
overflow before passing results to SaturateCast; to match the unary operator-
behavior you must widen both operands to long inside Add and Subtract (e.g.,
cast Item0/Item1 to long, perform the + or - on longs) and then call
SaturateCast.ToInt32 on the long result so boundary cases (like int.MinValue
negation) saturate correctly; update Vec2i.Add, Vec2i.Subtract (and ensure
consistency with operator - and SaturateCast usage) to use long arithmetic.

Comment on lines +149 to +156
public static bool operator ==(Vec3d a, Vec3d b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2;

/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec3d a, Vec3d b) => !(a == b);
public static bool operator !=(Vec3d a, Vec3d b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In .NET/C#, does double.NaN.Equals(double.NaN)returntruewhiledouble.NaN == double.NaNreturnsfalse?

💡 Result:

Yes, in .NET/C#, double.NaN.Equals(double.NaN) returns true while double.NaN == double.NaN returns false. This behavior follows the IEEE 754 standard for the == operator, which treats NaN as unordered and not equal to itself or any value. The Equals method, however, is overridden to return true for two NaN values to provide value equality semantics consistent with object.Equals. Official Microsoft documentation confirms this explicitly: - Double.NaN page example output: "NaN == NaN: False" and "NaN.Equals(NaN): True" - Double.Equality operator remarks: "If two Double.NaN values are tested for equality by using the equality operator, the result is false; two Double.NaN values are not considered equal. If they are tested for equality by calling the Equals method, the result is true." This has been consistent across .NET versions, including .NET 9 and .NET 10 previews as of the latest docs.

Citations:


🏁 Script executed:

# First, check if the file exists and read the relevant sections
if [ -f "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs" ]; then
  echo "=== File exists, checking encoding ==="
  file "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs"
  
  echo -e "\n=== Checking BOM marker ==="
  xxd -l 16 "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs"
  
  echo -e "\n=== Lines around 135 (Equals method) ==="
  sed -n '130,140p' "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs" | cat -n
  
  echo -e "\n=== Lines 149-156 (equality operators) ==="
  sed -n '149,156p' "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs" | cat -n
else
  echo "File not found"
fi

Repository: shimat/opencvsharp

Length of output: 1281


🏁 Script executed:

python3 << 'EOF'
import os

file_path = "src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs"

# Read the first 3 bytes to check for UTF-8 BOM
with open(file_path, 'rb') as f:
    bom_bytes = f.read(3)
    
utf8_bom = b'\xef\xbb\xbf'
has_bom = bom_bytes == utf8_bom

print(f"File: {file_path}")
print(f"First 3 bytes (hex): {bom_bytes.hex()}")
print(f"UTF-8 BOM expected: {utf8_bom.hex()}")
print(f"Has UTF-8 BOM: {has_bom}")
EOF

Repository: shimat/opencvsharp

Length of output: 191


Align operator equality with the Equals method to maintain consistent NaN handling.

The Equals method at line 135 uses Double.Equals (which treats matching NaN values as equal), but the == and != operators at lines 149–156 use raw double comparison (which treats NaN as unequal to itself per IEEE 754). This inconsistency breaks the value equality contract: two vectors containing matching NaNs will compare equal via Equals but unequal via ==.

Suggested fix
-    public static bool operator ==(Vec3d a, Vec3d b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2;
+    public static bool operator ==(Vec3d a, Vec3d b) => a.Equals(b);
...
-    public static bool operator !=(Vec3d a, Vec3d b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2;
+    public static bool operator !=(Vec3d a, Vec3d b) => !a.Equals(b);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static bool operator ==(Vec3d a, Vec3d b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2;
/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec3d a, Vec3d b) => !(a == b);
public static bool operator !=(Vec3d a, Vec3d b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2;
public static bool operator ==(Vec3d a, Vec3d b) => a.Equals(b);
/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec3d a, Vec3d b) => !a.Equals(b);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec3d.cs` around lines 149 - 156, The
operator implementations for Vec3d are inconsistent with the Equals method:
replace the raw component comparisons in the operator == and operator !=
overloads by using Double.Equals for each component so NaN values are treated
consistently (i.e., implement operator == to return
Double.Equals(a.Item0,b.Item0) && Double.Equals(a.Item1,b.Item1) &&
Double.Equals(a.Item2,b.Item2) and operator != as the negation of that),
ensuring behavior matches the Equals(Vec3d) implementation.

Comment on lines +175 to +182
public static bool operator ==(Vec4f a, Vec4f b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2 && a.Item3 == b.Item3;

/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec4f a, Vec4f b) => !(a == b);
public static bool operator !=(Vec4f a, Vec4f b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2 || a.Item3 != b.Item3;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In .NET/C#, does float.NaN.Equals(float.NaN)returntruewhilefloat.NaN == float.NaNreturnsfalse?

💡 Result:

Yes, in .NET/C#, float.NaN.Equals(float.NaN) returns true while float.NaN == float.NaN returns false. This behavior follows IEEE 754 standards for the == operator, which requires NaN != NaN, but the Equals method on System.Single (float) is overridden to return true when both values are NaN to satisfy the Object.Equals contract (reflexivity: x.Equals(x) must be true). Official Microsoft documentation confirms this with example code: Console.WriteLine("NaN == NaN: {0}", Single.NaN == Single.NaN); // False Console.WriteLine("NaN.Equals(NaN): {0}", Single.NaN.Equals(Single.NaN)); // True The C# equality operator (==) follows IEEE 754 semantics where any comparison involving NaN returns false. This has been consistent across .NET versions up to .NET 9/10 (as of 2026). Note: A .NET 7 breaking change affected custom types using NaN in fields (their Equals changed from true to false), but primitive float behavior remains unchanged.

Citations:


🏁 Script executed:

sed -n '157,182p' src/OpenCvSharp/Modules/core/Struct/Vec/Vec4f.cs

Repository: shimat/opencvsharp

Length of output: 979


Don't split Vec4f equality semantics across Equals and operators.

Equals(Vec4f) at lines 157–161 uses Item.Equals() on each component, which returns true for NaN.Equals(NaN), but operator== and operator!= at lines 175–182 use the ==/!= operators, which return false for NaN == NaN per IEEE 754. This causes a.Equals(b) and a == b to disagree when both vectors contain matching NaN values.

Delegate the operators to Equals:

Fix
-    public static bool operator ==(Vec4f a, Vec4f b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2 && a.Item3 == b.Item3;
+    public static bool operator ==(Vec4f a, Vec4f b) => a.Equals(b);
...
-    public static bool operator !=(Vec4f a, Vec4f b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2 || a.Item3 != b.Item3;
+    public static bool operator !=(Vec4f a, Vec4f b) => !a.Equals(b);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static bool operator ==(Vec4f a, Vec4f b) => a.Item0 == b.Item0 && a.Item1 == b.Item1 && a.Item2 == b.Item2 && a.Item3 == b.Item3;
/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec4f a, Vec4f b) => !(a == b);
public static bool operator !=(Vec4f a, Vec4f b) => a.Item0 != b.Item0 || a.Item1 != b.Item1 || a.Item2 != b.Item2 || a.Item3 != b.Item3;
public static bool operator ==(Vec4f a, Vec4f b) => a.Equals(b);
/// <summary>
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Vec4f a, Vec4f b) => !a.Equals(b);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/OpenCvSharp/Modules/core/Struct/Vec/Vec4f.cs` around lines 175 - 182, The
equality operators on Vec4f are inconsistent with Equals(Vec4f) for
NaN-containing components; update operator== and operator!= to delegate to the
existing Equals(Vec4f) logic so both semantics match (i.e., have operator== call
a.Equals(b) and operator!= return !a.Equals(b)); reference the Vec4f type and
its operator==, operator!= and Equals(Vec4f) methods when making the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant