Skip to content

Commit

Permalink
Fix indexing N-based system arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
BCSharp committed Dec 2, 2024
1 parent c1345b4 commit 06676cc
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 25 deletions.
84 changes: 72 additions & 12 deletions Src/IronPython/Runtime/Operations/ArrayOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,14 @@ public static Array Multiply(Array data, int count) {
if (data == null) throw PythonOps.TypeError("expected Array, got None");
if (data.Rank != 1) throw PythonOps.TypeError("bad dimensions for array, got {0} expected {1}", 1, data.Rank);

return data.GetValue(PythonOps.FixIndex(index, data.Length) + data.GetLowerBound(0));
return data.GetValue(FixIndex(data, index, 0));
}

[SpecialName]
public static object GetItem(Array data, Slice slice) {
if (data == null) throw PythonOps.TypeError("expected Array, got None");

return GetSlice(data, data.Length, slice);
return GetSlice(data, slice);
}

[SpecialName]
Expand All @@ -201,7 +201,6 @@ public static object GetItem(Array data, Slice slice) {
int[] iindices = TupleToIndices(data, indices);
if (data.Rank != indices.Length) throw PythonOps.TypeError("bad dimensions for array, got {0} expected {1}", indices.Length, data.Rank);

for (int i = 0; i < iindices.Length; i++) iindices[i] += data.GetLowerBound(i);
return data.GetValue(iindices);
}

Expand All @@ -210,7 +209,7 @@ public static void SetItem(Array data, int index, object value) {
if (data == null) throw PythonOps.TypeError("expected Array, got None");
if (data.Rank != 1) throw PythonOps.TypeError("bad dimensions for array, got {0} expected {1}", 1, data.Rank);

data.SetValue(Converter.Convert(value, data.GetType().GetElementType()), PythonOps.FixIndex(index, data.Length) + data.GetLowerBound(0));
data.SetValue(Converter.Convert(value, data.GetType().GetElementType()), FixIndex(data, index, 0));
}

[SpecialName]
Expand All @@ -231,8 +230,6 @@ public static void SetItem(Array a, params object[] indexAndValue) {

if (a.Rank != args.Length) throw PythonOps.TypeError("bad dimensions for array, got {0} expected {1}", args.Length, a.Rank);

for (int i = 0; i < indices.Length; i++) indices[i] += a.GetLowerBound(i);

Type elm = t.GetElementType()!;
a.SetValue(Converter.Convert(indexAndValue[indexAndValue.Length - 1], elm), indices);
}
Expand All @@ -243,9 +240,15 @@ public static void SetItem(Array a, Slice index, object? value) {

Type elm = a.GetType().GetElementType()!;

int lb = a.GetLowerBound(0);
if (lb != 0) {
FixSlice(index, a, out int start, out int stop, out int step);
index = new Slice(start - lb, stop - lb, step);
}

index.DoSliceAssign(
delegate (int idx, object? val) {
a.SetValue(Converter.Convert(val, elm), idx + a.GetLowerBound(0));
a.SetValue(Converter.Convert(val, elm), idx + lb);
},
a.Length,
value);
Expand Down Expand Up @@ -375,10 +378,10 @@ public static string __repr__(CodeContext/*!*/ context, [NotNone] Array/*!*/ sel
return GetSlice(data, start, stop, step);
}

internal static Array GetSlice(Array data, int size, Slice slice) {
private static Array GetSlice(Array data, Slice slice) {
if (data.Rank != 1) throw PythonOps.NotImplementedError("slice on multi-dimensional array");

slice.Indices(size, out int start, out int stop, out int step);
FixSlice(slice, data, out int start, out int stop, out int step);

if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
if (data.GetType().GetElementType() == typeof(object))
Expand All @@ -390,14 +393,14 @@ internal static Array GetSlice(Array data, int size, Slice slice) {
if (step == 1) {
int n = stop - start;
Array ret = Array.CreateInstance(data.GetType().GetElementType()!, n);
Array.Copy(data, start + data.GetLowerBound(0), ret, 0, n);
Array.Copy(data, start, ret, 0, n);
return ret;
} else {
int n = PythonOps.GetSliceCount(start, stop, step);
Array ret = Array.CreateInstance(data.GetType().GetElementType()!, n);
int ri = 0;
for (int i = 0, index = start; i < n; i++, index += step) {
ret.SetValue(data.GetValue(index + data.GetLowerBound(0)), ri++);
ret.SetValue(data.GetValue(index), ri++);
}
return ret;
}
Expand Down Expand Up @@ -427,11 +430,68 @@ private static int[] TupleToIndices(Array a, IList<object?> tuple) {
int[] indices = new int[tuple.Count];
for (int i = 0; i < indices.Length; i++) {
int iindex = Converter.ConvertToInt32(tuple[i]);
indices[i] = i < a.Rank ? PythonOps.FixIndex(iindex, a.GetLength(i)) : int.MinValue;
indices[i] = i < a.Rank ? FixIndex(a, iindex, i) : int.MinValue;
}
return indices;
}

private static int FixIndex(Array a, int v, int axis) {
int idx = v;
if (idx < 0) idx += a.GetUpperBound(axis) + 1;
if (idx < a.GetLowerBound(axis) || idx > a.GetUpperBound(axis)) {
throw PythonOps.IndexError("index out of range: {0}", v);
}
return idx;
}

private static void FixSlice(Slice slice, Array a, out int ostart, out int ostop, out int ostep) {
Debug.Assert(a.Rank == 1);

if (slice.step == null) {
ostep = 1;
} else {
ostep = Converter.ConvertToIndex(slice.step);
if (ostep == 0) {
throw PythonOps.ValueError("step cannot be zero");
}
}

int lb = a.GetLowerBound(0);
int ub = a.GetUpperBound(0);

if (slice.start == null) {
ostart = ostep > 0 ? lb : ub;
} else {
ostart = Converter.ConvertToIndex(slice.start);
if (ostart < lb) {
if (ostart < 0) {
ostart += ub + 1;
}
if (ostart < lb) {
ostart = ostep > 0 ? lb : lb - 1;
}
} else if (ostart > ub) {
ostart = ostep > 0 ? ub + 1 : ub;
}
}

if (slice.stop == null) {
ostop = ostep > 0 ? ub + 1 : lb - 1;
} else {
ostop = Converter.ConvertToIndex(slice.stop);
if (ostop < lb) {
if (ostop < 0) {
ostop += ub + 1;
}
if (ostop < 0) {
ostop = ostep > 0 ? lb : lb - 1;
}
} else if (ostop > ub) {
ostop = ostep > 0 ? ub + 1 : ub;
}
}
}

#endregion
}
}
75 changes: 62 additions & 13 deletions Tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,35 +173,55 @@ def test_constructor(self):

def test_nonzero_lowerbound(self):
a = System.Array.CreateInstance(int, (5,), (5,))
for i in range(5): a[i] = i
for i in range(5, 5 + a.Length): a[i] = i

self.assertEqual(a[:2], System.Array[int]((0,1)))
self.assertEqual(a[2:], System.Array[int]((2,3,4)))
self.assertEqual(a[2:4], System.Array[int]((2,3)))
self.assertEqual(a[-1], 4)
self.assertEqual(a[:7], System.Array[int]((5,6)))
self.assertEqual(a[7:], System.Array[int]((7,8,9)))
self.assertEqual(a[7:9], System.Array[int]((7,8)))
self.assertEqual(a[-1:-3:-1], System.Array[int]((9,8)))
self.assertEqual(a[-1], 9)

self.assertEqual(repr(a), 'Array[int]((0, 1, 2, 3, 4), base: 5)')
self.assertEqual(repr(a), 'Array[int]((5, 6, 7, 8, 9), base: 5)')

a = System.Array.CreateInstance(int, (5,), (15,))
b = System.Array.CreateInstance(int, (5,), (20,))
self.assertEqual(a.Length, b.Length)
for i in range(a.Length):
self.assertEqual(a[i], b[i])
self.assertEqual(a[i + 15], b[i + 20])

## 5-dimension
a0 = System.Array.CreateInstance(int, 5) # regular, 0-based
for i in range(5): a0[i] = i

a[17:19] = a0[2:4]
self.assertEqual(a[17:19], System.Array[int]((2, 3)))

self.assertEqual(a0[3:1:-1], System.Array[int]((3, 2)))
self.assertEqual(a[18:16:-1], System.Array[int]((3, 2)))

self.assertEqual(a0[-3:-1], System.Array[int]((2, 3)))
self.assertEqual(a[-3:-1], System.Array[int]((2, 3)))

a[18:16:-1] = a0[2:4]
self.assertEqual(a[17:19], System.Array[int]((3, 2)))

## 5-dimension, 2-length/dim, progressive lowerbound
a = System.Array.CreateInstance(int, (2,2,2,2,2), (1,2,3,4,5))
self.assertEqual(a[1,2,3,4,5], 0)

## 5-dimension, 2-length/dim, progressive lowerbound
a = System.Array.CreateInstance(int, (2,2,2,2,2), (1,2,3,4,5))
self.assertEqual(a[0,0,0,0,0], 0)
self.assertEqual(a[1,2,3,4,5], 0)

for i in range(5):
index = [0,0,0,0,0]
index[i] = 1
index = [1,2,3,4,5]
index[i] += 1

a[index[0], index[1], index[2], index[3], index[4]] = i
self.assertEqual(a[index[0], index[1], index[2], index[3], index[4]], i)

for i in range(5):
index = [0,0,0,0,0]
index[i] = 0
index = [2,3,4,5,6]
index[i] -= 1

a[index[0], index[1], index[2], index[3], index[4]] = i
self.assertEqual(a[index[0], index[1], index[2], index[3], index[4]], i)
Expand All @@ -218,6 +238,35 @@ def sliceArrayAssign(arr, index, val):
self.assertRaises(NotImplementedError, sliceArrayAssign, a, -200, 1)
self.assertRaises(NotImplementedError, sliceArrayAssign, a, 1, 1)

def test_base1(self):
# 1-based 2x2 matrix
arr = System.Array.CreateInstance(str, (2,2), (1,1))

self.assertEqual(arr.GetLowerBound(0), 1)
self.assertEqual(arr.GetLowerBound(1), 1)
self.assertEqual(arr.GetUpperBound(0), 2)
self.assertEqual(arr.GetUpperBound(1), 2)

arr.SetValue("a_1,1", System.Array[System.Int32]((1,1)))
arr.SetValue("a_1,2", System.Array[System.Int32]((1,2)))
arr.SetValue("a_2,1", System.Array[System.Int32]((2,1)))
arr.SetValue("a_2,2", System.Array[System.Int32]((2,2)))

self.assertEqual(arr[1, 1], "a_1,1")
self.assertEqual(arr[1, 2], "a_1,2")
self.assertEqual(arr[2, 1], "a_2,1")
self.assertEqual(arr[2, 2], "a_2,2")

arr[1, 1] = "b_1,1"
arr[1, 2] = "b_1,2"
arr[2, 1] = "b_2,1"
arr[2, 2] = "b_2,2"

self.assertEqual(arr.GetValue(System.Array[System.Int32]((1,1))), "b_1,1")
self.assertEqual(arr.GetValue(System.Array[System.Int32]((1,2))), "b_1,2")
self.assertEqual(arr.GetValue(System.Array[System.Int32]((2,1))), "b_2,1")
self.assertEqual(arr.GetValue(System.Array[System.Int32]((2,2))), "b_2,2")

def test_array_type(self):

def type_helper(array_type, instance):
Expand Down

0 comments on commit 06676cc

Please sign in to comment.