diff --git a/Src/IronPython/Runtime/Operations/ArrayOps.cs b/Src/IronPython/Runtime/Operations/ArrayOps.cs index 87c4bcb62..30dbc9b72 100644 --- a/Src/IronPython/Runtime/Operations/ArrayOps.cs +++ b/Src/IronPython/Runtime/Operations/ArrayOps.cs @@ -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] @@ -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); } @@ -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] @@ -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); } @@ -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); @@ -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)) @@ -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; } @@ -427,11 +430,68 @@ private static int[] TupleToIndices(Array a, IList 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 } } diff --git a/Tests/test_array.py b/Tests/test_array.py index 6287ff46a..94b4b4ac7 100644 --- a/Tests/test_array.py +++ b/Tests/test_array.py @@ -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) @@ -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):