Repository: pyccel/pyccel (python)

Issue:
basic indexing : creating a view with Integers + slice objects
in basic indexing, python has a specific behaviour when mixing slice objects and Integers.

**To Reproduce**
```python
a = numpy.array([[1, 2, 3], [4, 5, 6]])
x = a[1, 1:]
y = a[1:, 1:]

x >>> [5, 6]
y >>> [[5, 6]]
```

Here is the behaviour of python for the **basic indexing**:

- An integer `i` returns the same values as the slice `i:i+1`, except the dimensionality of the returned array view is reduced by 1. In particular, a selection tuple with the p-th element an integer (and all other entries being slices `:`) returns the corresponding sub-array with dimension N - 1. If N = 1 then the returned object is an array scalar. These objects are explained in Scalars.

- If the selection tuple has all entries being simple slices `:` except the p-th entry which is a slice object `i:j:k`, then the returned array has dimension N formed by concatenating the sub-arrays returned by integer indexing of elements i, i+k, …, i + (m - 1) k < j,

- Basic slicing with more than one non-: entry in the slicing tuple, acts like a repeated application of slicing using a single non-`:` entry, where the non-`:` entries are successively taken (with all other non-`:` entries replaced by `:`). Thus, x[ind1,...,ind2,:] acts like x[ind1][...,ind2,:] under basic slicing.


Pull request description:
Better handling for array slicing with literal indexes
This fixes #566:

- Pass the rank value to the `array_slicing` function to make sure we got the correct one;
- Handle literals indexes.

Patch:
diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index 2ae6fadd..46586c45 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -677,8 +677,10 @@ class CCodePrinter(CodePrinter):
                     if isinstance(ind, Slice):
                         inds[i] = self._new_slice_with_processed_arguments(ind, PyccelArraySize(base, i),
                             allow_negative_indexes)
+                    else:
+                        inds[i] = Slice(ind, PyccelAdd(ind, LiteralInteger(1)), LiteralInteger(1))
                 inds = [self._print(i) for i in inds]
-                return "array_slicing(%s, %s)" % (base_name, ", ".join(inds))
+                return "array_slicing(%s, %s, %s)" % (base_name, expr.rank, ", ".join(inds))
             inds = [self._print(i) for i in inds]
         else:
             raise NotImplementedError(expr)
diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c
index 39f1bf7a..2b478019 100644
--- a/pyccel/stdlib/ndarrays/ndarrays.c
+++ b/pyccel/stdlib/ndarrays/ndarrays.c
@@ -189,21 +189,21 @@ t_slice new_slice(int32_t start, int32_t end, int32_t step)
     return (slice);
 }
 
-t_ndarray array_slicing(t_ndarray arr, ...)
+t_ndarray array_slicing(t_ndarray arr, int n, ...)
 {
     t_ndarray view;
     va_list  va;
     t_slice slice;
     int32_t start = 0;
 
-    view.nd = arr.nd;
+    view.nd = n;
     view.type = arr.type;
     view.type_size = arr.type_size;
     view.shape = malloc(sizeof(int32_t) * arr.nd);
     view.strides = malloc(sizeof(int32_t) * arr.nd);
     memcpy(view.strides, arr.strides, sizeof(int32_t) * arr.nd);
     view.is_view = true;
-    va_start(va, arr);
+    va_start(va, n);
     for (int32_t i = 0; i < arr.nd ; i++)
     {
         slice = va_arg(va, t_slice);
@@ -212,6 +212,22 @@ t_ndarray array_slicing(t_ndarray arr, ...)
         view.strides[i] *= slice.step;
     }
     va_end(va);
+    int32_t j = arr.nd - view.nd;
+    if (j)
+    {
+        int32_t *tmp_strides = malloc(sizeof(int32_t) * view.nd);
+        int32_t *tmp_shape = malloc(sizeof(int32_t) * view.nd);
+        for (int32_t i = 0; i < view.nd; i++)
+        {
+            tmp_strides[i] = view.strides[j];
+            tmp_shape[i] = view.shape[j];
+            j++;
+        }
+        free(view.shape);
+        free(view.strides);
+        view.strides = tmp_strides;
+        view.shape = tmp_shape;
+    }
     view.raw_data = arr.raw_data + start * arr.type_size;
     view.length = 1;
     for (int32_t i = 0; i < view.nd; i++)
diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h
index b42c100a..3f96ed83 100644
--- a/pyccel/stdlib/ndarrays/ndarrays.h
+++ b/pyccel/stdlib/ndarrays/ndarrays.h
@@ -96,7 +96,7 @@ void        _array_fill_cdouble(double complex c, t_ndarray arr);
                 /* creating a Slice object */
 t_slice     new_slice(int32_t start, int32_t end, int32_t step);
                 /* creating an array view */
-t_ndarray   array_slicing(t_ndarray p, ...);
+t_ndarray   array_slicing(t_ndarray arr, int n, ...);
 
 /* assigns */
 void        alias_assign(t_ndarray *dest, t_ndarray src);


Test patch:
diff --git a/tests/ndarrays/test_ndarrays.c b/tests/ndarrays/test_ndarrays.c
index 412e8bc2..a267c75b 100644
--- a/tests/ndarrays/test_ndarrays.c
+++ b/tests/ndarrays/test_ndarrays.c
@@ -368,7 +368,7 @@ int32_t test_slicing_int64(void)
 
     x = array_create(2, m_1_shape, nd_int64);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
@@ -409,7 +409,7 @@ int32_t test_slicing_int32(void)
 
     x = array_create(2, m_1_shape, nd_int32);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
@@ -449,7 +449,7 @@ int32_t test_slicing_int16(void)
 
     x = array_create(2, m_1_shape, nd_int16);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
@@ -490,7 +490,7 @@ int32_t test_slicing_int8(void)
 
     x = array_create(2, m_1_shape, nd_int8);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
@@ -531,7 +531,7 @@ int32_t test_slicing_double(void)
 
     x = array_create(2, m_1_shape, nd_double);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
@@ -568,7 +568,7 @@ int32_t test_slicing_cdouble(void)
 
     x = array_create(2, m_1_shape, nd_cdouble);
     memcpy(x.raw_data, m_1, x.buffer_size);
-    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    xview = array_slicing(x, 2, new_slice(1, 2, 1), new_slice(0, 5, 2));
     c_index = 5;
     for (int32_t i = 0; i < xview.shape[0]; i++)
     {
diff --git a/tests/pyccel/scripts/arrays_view.py b/tests/pyccel/scripts/arrays_view.py
index 8967663a..798c5433 100644
--- a/tests/pyccel/scripts/arrays_view.py
+++ b/tests/pyccel/scripts/arrays_view.py
@@ -39,6 +39,28 @@ def array_view_negative_variable_step():
         for j in range(5):
             print(b[i][j])
 
+@allow_negative_index('a')
+def array_view_negative_var():
+    a = np.array([[1, 2, 3], [4, 5, 6], [7, 9, 5]])
+    v = -1
+    x = a[v,1:]
+    for i in range(2):
+        print(x[i])
+
+@allow_negative_index('a')
+def array_view_negative_literal():
+    a = np.array([[1, 2, 3], [4, 5, 6], [7, 9, 5]])
+    x = a[-1,1:]
+    for i in range(2):
+        print(x[i])
+
+def array_view_positive_literal():
+    a = np.array([[1, 2, 3], [4, 5, 6], [7, 9, 5]])
+    y = a[1:, 1:3]
+    for i in range(2):
+        for j in range(2):
+            print(y[i][j])
+
 def array_view_2():
     a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
     b = a[1:-1]
@@ -61,3 +83,6 @@ array_view_negative_literal_step()
 array_view_negative_literal_step__2()
 array_view_negative_literal_step__3()
 array_view_negative_variable_step()
+array_view_negative_var()
+array_view_positive_literal()
+array_view_negative_literal()