Skip to content

Commit 06676cc

Browse files
committed
Fix indexing N-based system arrays
1 parent c1345b4 commit 06676cc

File tree

2 files changed

+134
-25
lines changed

2 files changed

+134
-25
lines changed

Src/IronPython/Runtime/Operations/ArrayOps.cs

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,14 @@ public static Array Multiply(Array data, int count) {
171171
if (data == null) throw PythonOps.TypeError("expected Array, got None");
172172
if (data.Rank != 1) throw PythonOps.TypeError("bad dimensions for array, got {0} expected {1}", 1, data.Rank);
173173

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

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

181-
return GetSlice(data, data.Length, slice);
181+
return GetSlice(data, slice);
182182
}
183183

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

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

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

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

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

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

234-
for (int i = 0; i < indices.Length; i++) indices[i] += a.GetLowerBound(i);
235-
236233
Type elm = t.GetElementType()!;
237234
a.SetValue(Converter.Convert(indexAndValue[indexAndValue.Length - 1], elm), indices);
238235
}
@@ -243,9 +240,15 @@ public static void SetItem(Array a, Slice index, object? value) {
243240

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

243+
int lb = a.GetLowerBound(0);
244+
if (lb != 0) {
245+
FixSlice(index, a, out int start, out int stop, out int step);
246+
index = new Slice(start - lb, stop - lb, step);
247+
}
248+
246249
index.DoSliceAssign(
247250
delegate (int idx, object? val) {
248-
a.SetValue(Converter.Convert(val, elm), idx + a.GetLowerBound(0));
251+
a.SetValue(Converter.Convert(val, elm), idx + lb);
249252
},
250253
a.Length,
251254
value);
@@ -375,10 +378,10 @@ public static string __repr__(CodeContext/*!*/ context, [NotNone] Array/*!*/ sel
375378
return GetSlice(data, start, stop, step);
376379
}
377380

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

381-
slice.Indices(size, out int start, out int stop, out int step);
384+
FixSlice(slice, data, out int start, out int stop, out int step);
382385

383386
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
384387
if (data.GetType().GetElementType() == typeof(object))
@@ -390,14 +393,14 @@ internal static Array GetSlice(Array data, int size, Slice slice) {
390393
if (step == 1) {
391394
int n = stop - start;
392395
Array ret = Array.CreateInstance(data.GetType().GetElementType()!, n);
393-
Array.Copy(data, start + data.GetLowerBound(0), ret, 0, n);
396+
Array.Copy(data, start, ret, 0, n);
394397
return ret;
395398
} else {
396399
int n = PythonOps.GetSliceCount(start, stop, step);
397400
Array ret = Array.CreateInstance(data.GetType().GetElementType()!, n);
398401
int ri = 0;
399402
for (int i = 0, index = start; i < n; i++, index += step) {
400-
ret.SetValue(data.GetValue(index + data.GetLowerBound(0)), ri++);
403+
ret.SetValue(data.GetValue(index), ri++);
401404
}
402405
return ret;
403406
}
@@ -427,11 +430,68 @@ private static int[] TupleToIndices(Array a, IList<object?> tuple) {
427430
int[] indices = new int[tuple.Count];
428431
for (int i = 0; i < indices.Length; i++) {
429432
int iindex = Converter.ConvertToInt32(tuple[i]);
430-
indices[i] = i < a.Rank ? PythonOps.FixIndex(iindex, a.GetLength(i)) : int.MinValue;
433+
indices[i] = i < a.Rank ? FixIndex(a, iindex, i) : int.MinValue;
431434
}
432435
return indices;
433436
}
434437

438+
private static int FixIndex(Array a, int v, int axis) {
439+
int idx = v;
440+
if (idx < 0) idx += a.GetUpperBound(axis) + 1;
441+
if (idx < a.GetLowerBound(axis) || idx > a.GetUpperBound(axis)) {
442+
throw PythonOps.IndexError("index out of range: {0}", v);
443+
}
444+
return idx;
445+
}
446+
447+
private static void FixSlice(Slice slice, Array a, out int ostart, out int ostop, out int ostep) {
448+
Debug.Assert(a.Rank == 1);
449+
450+
if (slice.step == null) {
451+
ostep = 1;
452+
} else {
453+
ostep = Converter.ConvertToIndex(slice.step);
454+
if (ostep == 0) {
455+
throw PythonOps.ValueError("step cannot be zero");
456+
}
457+
}
458+
459+
int lb = a.GetLowerBound(0);
460+
int ub = a.GetUpperBound(0);
461+
462+
if (slice.start == null) {
463+
ostart = ostep > 0 ? lb : ub;
464+
} else {
465+
ostart = Converter.ConvertToIndex(slice.start);
466+
if (ostart < lb) {
467+
if (ostart < 0) {
468+
ostart += ub + 1;
469+
}
470+
if (ostart < lb) {
471+
ostart = ostep > 0 ? lb : lb - 1;
472+
}
473+
} else if (ostart > ub) {
474+
ostart = ostep > 0 ? ub + 1 : ub;
475+
}
476+
}
477+
478+
if (slice.stop == null) {
479+
ostop = ostep > 0 ? ub + 1 : lb - 1;
480+
} else {
481+
ostop = Converter.ConvertToIndex(slice.stop);
482+
if (ostop < lb) {
483+
if (ostop < 0) {
484+
ostop += ub + 1;
485+
}
486+
if (ostop < 0) {
487+
ostop = ostep > 0 ? lb : lb - 1;
488+
}
489+
} else if (ostop > ub) {
490+
ostop = ostep > 0 ? ub + 1 : ub;
491+
}
492+
}
493+
}
494+
435495
#endregion
436496
}
437497
}

Tests/test_array.py

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -173,35 +173,55 @@ def test_constructor(self):
173173

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

178-
self.assertEqual(a[:2], System.Array[int]((0,1)))
179-
self.assertEqual(a[2:], System.Array[int]((2,3,4)))
180-
self.assertEqual(a[2:4], System.Array[int]((2,3)))
181-
self.assertEqual(a[-1], 4)
178+
self.assertEqual(a[:7], System.Array[int]((5,6)))
179+
self.assertEqual(a[7:], System.Array[int]((7,8,9)))
180+
self.assertEqual(a[7:9], System.Array[int]((7,8)))
181+
self.assertEqual(a[-1:-3:-1], System.Array[int]((9,8)))
182+
self.assertEqual(a[-1], 9)
182183

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

185186
a = System.Array.CreateInstance(int, (5,), (15,))
186187
b = System.Array.CreateInstance(int, (5,), (20,))
187188
self.assertEqual(a.Length, b.Length)
188189
for i in range(a.Length):
189-
self.assertEqual(a[i], b[i])
190+
self.assertEqual(a[i + 15], b[i + 20])
190191

191-
## 5-dimension
192+
a0 = System.Array.CreateInstance(int, 5) # regular, 0-based
193+
for i in range(5): a0[i] = i
194+
195+
a[17:19] = a0[2:4]
196+
self.assertEqual(a[17:19], System.Array[int]((2, 3)))
197+
198+
self.assertEqual(a0[3:1:-1], System.Array[int]((3, 2)))
199+
self.assertEqual(a[18:16:-1], System.Array[int]((3, 2)))
200+
201+
self.assertEqual(a0[-3:-1], System.Array[int]((2, 3)))
202+
self.assertEqual(a[-3:-1], System.Array[int]((2, 3)))
203+
204+
a[18:16:-1] = a0[2:4]
205+
self.assertEqual(a[17:19], System.Array[int]((3, 2)))
206+
207+
## 5-dimension, 2-length/dim, progressive lowerbound
208+
a = System.Array.CreateInstance(int, (2,2,2,2,2), (1,2,3,4,5))
209+
self.assertEqual(a[1,2,3,4,5], 0)
210+
211+
## 5-dimension, 2-length/dim, progressive lowerbound
192212
a = System.Array.CreateInstance(int, (2,2,2,2,2), (1,2,3,4,5))
193-
self.assertEqual(a[0,0,0,0,0], 0)
213+
self.assertEqual(a[1,2,3,4,5], 0)
194214

195215
for i in range(5):
196-
index = [0,0,0,0,0]
197-
index[i] = 1
216+
index = [1,2,3,4,5]
217+
index[i] += 1
198218

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

202222
for i in range(5):
203-
index = [0,0,0,0,0]
204-
index[i] = 0
223+
index = [2,3,4,5,6]
224+
index[i] -= 1
205225

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

241+
def test_base1(self):
242+
# 1-based 2x2 matrix
243+
arr = System.Array.CreateInstance(str, (2,2), (1,1))
244+
245+
self.assertEqual(arr.GetLowerBound(0), 1)
246+
self.assertEqual(arr.GetLowerBound(1), 1)
247+
self.assertEqual(arr.GetUpperBound(0), 2)
248+
self.assertEqual(arr.GetUpperBound(1), 2)
249+
250+
arr.SetValue("a_1,1", System.Array[System.Int32]((1,1)))
251+
arr.SetValue("a_1,2", System.Array[System.Int32]((1,2)))
252+
arr.SetValue("a_2,1", System.Array[System.Int32]((2,1)))
253+
arr.SetValue("a_2,2", System.Array[System.Int32]((2,2)))
254+
255+
self.assertEqual(arr[1, 1], "a_1,1")
256+
self.assertEqual(arr[1, 2], "a_1,2")
257+
self.assertEqual(arr[2, 1], "a_2,1")
258+
self.assertEqual(arr[2, 2], "a_2,2")
259+
260+
arr[1, 1] = "b_1,1"
261+
arr[1, 2] = "b_1,2"
262+
arr[2, 1] = "b_2,1"
263+
arr[2, 2] = "b_2,2"
264+
265+
self.assertEqual(arr.GetValue(System.Array[System.Int32]((1,1))), "b_1,1")
266+
self.assertEqual(arr.GetValue(System.Array[System.Int32]((1,2))), "b_1,2")
267+
self.assertEqual(arr.GetValue(System.Array[System.Int32]((2,1))), "b_2,1")
268+
self.assertEqual(arr.GetValue(System.Array[System.Int32]((2,2))), "b_2,2")
269+
221270
def test_array_type(self):
222271

223272
def type_helper(array_type, instance):

0 commit comments

Comments
 (0)