Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement equality comparisons on System.Array #1821

Merged
merged 2 commits into from
Dec 2, 2024
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
52 changes: 52 additions & 0 deletions Src/IronPython/Runtime/Operations/ArrayOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,58 @@ public static object __new__(CodeContext context, PythonType pythonType, object
return res;
}

[StaticExtensionMethod]
public static object __eq__(CodeContext context, Array self, [NotNone] Array other) {
if (self is null) throw PythonOps.TypeError("expected Array, got None");
if (other is null) throw PythonOps.TypeError("expected Array, got None");

if (self.GetType() != other.GetType()) return ScriptingRuntimeHelpers.False;
// same type implies: same rank, same element type
for (int d = 0; d < self.Rank; d++) {
if (self.GetLowerBound(d) != other.GetLowerBound(d)) return ScriptingRuntimeHelpers.False;
if (self.GetUpperBound(d) != other.GetUpperBound(d)) return ScriptingRuntimeHelpers.False;
}
if (self.Length == 0) return ScriptingRuntimeHelpers.True; // fast track

if (self.Rank == 1 && self.GetLowerBound(0) == 0 ) {
// IStructuralEquatable.Equals only works for 1-dim, 0-based arrays
return ScriptingRuntimeHelpers.BooleanToObject(
((IStructuralEquatable)self).Equals(other, context.LanguageContext.EqualityComparerNonGeneric)
);
} else {
int[] ix = new int[self.Rank];
for (int d = 0; d < self.Rank; d++) {
ix[d] = self.GetLowerBound(d);
}
for (int i = 0; i < self.Length; i++) {
if (!PythonOps.EqualRetBool(self.GetValue(ix), other.GetValue(ix))) {
return ScriptingRuntimeHelpers.False;
}
for (int d = self.Rank - 1; d >= 0; d--) {
if (ix[d] < self.GetUpperBound(d)) {
ix[d]++;
break;
} else {
ix[d] = self.GetLowerBound(d);
}
}
}
return ScriptingRuntimeHelpers.True;
}
}

[StaticExtensionMethod]
[return: MaybeNotImplemented]
public static object __eq__(CodeContext context, object self, object? other) => NotImplementedType.Value;

[StaticExtensionMethod]
public static object __ne__(CodeContext context, Array self, [NotNone] Array other)
=> ScriptingRuntimeHelpers.BooleanToObject(ReferenceEquals(__eq__(context, self, other), ScriptingRuntimeHelpers.False));

[StaticExtensionMethod]
[return: MaybeNotImplemented]
public static object __ne__(CodeContext context, object self, object? other) => NotImplementedType.Value;

/// <summary>
/// Multiply two object[] arrays - slow version, we need to get the type, etc...
/// </summary>
Expand Down
86 changes: 86 additions & 0 deletions Tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,90 @@ def test_tuple_indexer(self):
array1[0,0] = 5
self.assertEqual(array1[0,0], array1[(0,0)])

def test_equality(self):
a = System.Array.CreateInstance(int, 5)
a2 = System.Array.CreateInstance(int, 5) # same as a
b = System.Array.CreateInstance(int, 5, 6) # different rank
c = System.Array.CreateInstance(int, 6) # different length
d = System.Array.CreateInstance(int, (5,), (1,)) # different base
e = System.Array.CreateInstance(System.Int32, 5) # different element type
l = [0] * 5 # different type

self.assertTrue(a == a2)
self.assertTrue(a2 == a)
self.assertFalse(a != a2)
self.assertFalse(a2 != a)

self.assertFalse(a == b)
self.assertFalse(b == a)
self.assertTrue(a != b)
self.assertTrue(b != a)

self.assertFalse(a == c)
self.assertFalse(c == a)
self.assertTrue(a != c)
self.assertTrue(c != a)

self.assertFalse(a == d)
self.assertFalse(d == a)
self.assertTrue(a != d)
self.assertTrue(d != a)

self.assertFalse(a == e)
self.assertFalse(e == a)
self.assertTrue(a != e)
self.assertTrue(e != a)

self.assertFalse(a == l)
self.assertFalse(l == a)
self.assertTrue(a != l)
self.assertTrue(l != a)

def test_equality_base(self):
a = System.Array.CreateInstance(int, (5,), (5,))
a2 = System.Array.CreateInstance(int, (5,), (5,))
b = System.Array.CreateInstance(int, (6,), (5,))
c = System.Array.CreateInstance(int, (5,), (6,))
d = System.Array.CreateInstance(int, (6,), (6,))

self.assertTrue(a == a2)
self.assertFalse(a == b)
self.assertFalse(a == c)
self.assertFalse(a == d)

self.assertFalse(a != a2)
self.assertTrue(a != b)
self.assertTrue(a != c)
self.assertTrue(a != d)

def test_equality_rank(self):
a = System.Array.CreateInstance(int, 5, 6)
a2 = System.Array.CreateInstance(int, 5, 6)
b = System.Array.CreateInstance(int, 5, 6)
b[0, 0] = 1
c = System.Array.CreateInstance(int, (6, 5), (0, 0))
c[0, 0] = 1
d = System.Array.CreateInstance(int, (6, 5), (1, 1))
d[1, 1] = 1
d1 = System.Array.CreateInstance(int, (6, 5), (1, 1))
d1[1, 1] = 1

self.assertTrue(a == a2)
self.assertFalse(a == b) # different element
self.assertFalse(a == c) # different rank
self.assertFalse(a == d) # different rank
self.assertFalse(b == c) # different shape
self.assertFalse(b == d) # different shape & base
self.assertFalse(c == d) # different base
self.assertTrue(d == d1)

self.assertFalse(a != a2)
self.assertTrue(a != b)
self.assertTrue(a != c)
self.assertTrue(a != d)
self.assertTrue(b != c)
self.assertTrue(b != d)
self.assertTrue(c != d)
self.assertFalse(d != d1)

run_test(__name__)