Repository: pyccel/pyccel (2021-01-15 ~ 2021-03-10)

problem_statement: 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.

date: 2021-01-15
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()


----

problem_statement: Builtin abs function not working in c language
**Describe the bug**
Builtin abs function not implemented in c language

**To Reproduce**
Provide code to reproduce the behavior:
```python
@types('int')
def f(x):
    return abs(x)
```

**Error : **
ERROR at code generation stage
pyccel:
 |fatal [codegen]: first.py [3,11]| Pyccel has encountered syntax that has not been implemented yet. (<pyccel.ast.builtins.PythonAbs object at 0x7fa62ca792b0>)


**Language**
c

date: 2021-03-04
patch: diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index 46586c45..0d9b342b 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -276,6 +276,17 @@ class CCodePrinter(CodePrinter):
 
     # ============ Elements ============ #
 
+    def _print_PythonAbs(self, expr):
+        if expr.arg.dtype is NativeReal():
+            self._additional_imports.add("math")
+            func = "fabs"
+        elif expr.arg.dtype is NativeComplex():
+            self._additional_imports.add("complex")
+            func = "cabs"
+        else:
+            func = "abs"
+        return "{}({})".format(func, self._print(expr.arg))
+
     def _print_PythonFloat(self, expr):
         value = self._print(expr.arg)
         type_name = self.find_in_dtype_registry('real', default_precision['real'])

test_patch: diff --git a/tests/epyccel/test_builtins.py b/tests/epyccel/test_builtins.py
new file mode 100644
index 00000000..2b597fb1
--- /dev/null
+++ b/tests/epyccel/test_builtins.py
@@ -0,0 +1,41 @@
+# pylint: disable=missing-function-docstring, missing-module-docstring/
+
+from pyccel.epyccel import epyccel
+from pyccel.decorators import types
+
+def test_abs_i(language):
+    @types('int')
+    def f1(x):
+        return abs(x)
+
+    f2 = epyccel(f1, language=language)
+
+    assert f1(0) == f2(0)
+    assert f1(-5) == f2(-5)
+    assert f1(11) == f2(11)
+
+def test_abs_r(language):
+    @types('real')
+    def f1(x):
+        return abs(x)
+
+    f2 = epyccel(f1, language=language)
+
+    assert f1(0.00000) == f2(0.00000)
+    assert f1(-3.1415) == f2(-3.1415)
+    assert f1(2.71828) == f2(2.71828)
+
+
+
+def test_abs_c(language):
+    @types('complex')
+    def f1(x):
+        return abs(x)
+
+    f2 = epyccel(f1, language=language)
+
+    assert f1(3j + 4) == f2(3j + 4)
+    assert f1(3j - 4) == f2(3j - 4)
+    assert f1(5j + 0) == f2(5j + 0)
+    assert f1(0j + 5) == f2(0j + 5)
+    assert f1(0j + 0) == f2(0j + 0)


----

problem_statement: For Loop for loop descending order are not working in C  and Fortran
**Describe the bug**
For Loop for loop descending order are not working in C and have some bugs in Fortran

**To Reproduce**
```python
for i in range(10, 1, -1):
    print(i)
```
```Python
>> 10 9 8 7 6 5 4 3 2
```

```C
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main()
{
    int64_t i;

    for (i = 10; i < 1; i += -1)
    {
        printf("%ld\n", i);
    }
    return 0;
}
```
here the for loop condition should be changed. 
```Fortran
program prog_test

use, intrinsic :: ISO_C_BINDING

implicit none

integer(C_INT64_T) :: i

do i = 10_C_INT64_T, 1_C_INT64_T-1_C_INT64_T, -1_C_INT64_T
  print *, i
end do

end program prog_test
```
```python
>> 10 9 8 7 6 5 4 3 2 1 0
```
here the end of the for loop is wrong

date: 2021-03-10
patch: diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index 0d9b342b..1e4a1aef 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -1158,15 +1158,30 @@ class CCodePrinter(CodePrinter):
         return '{} = {};'.format(lhs, rhs)
 
     def _print_For(self, expr):
-        target = self._print(expr.target)
+        counter = self._print(expr.target)
         body  = self._print(expr.body)
         if isinstance(expr.iterable, PythonRange):
-            start, stop, step = [self._print(e) for e in expr.iterable.args]
+            start = self._print(expr.iterable.start)
+            stop  = self._print(expr.iterable.stop)
+            step  = self._print(expr.iterable.step )
         else:
             raise NotImplementedError("Only iterable currently supported is Range")
-        return ('for ({target} = {start}; {target} < {stop}; {target} += '
-                '{step})\n{{\n{body}\n}}').format(target=target, start=start,
-                stop=stop, step=step, body=body)
+
+        test_step = expr.iterable.step
+        if isinstance(test_step, PyccelUnarySub):
+            test_step = expr.iterable.step.args[0]
+
+        # testing if the step is a value or an expression
+        if isinstance(test_step, Literal):
+            op = '>' if isinstance(expr.iterable.step, PyccelUnarySub) else '<'
+            return ('for ({counter} = {start}; {counter} {op} {stop}; {counter} += '
+                        '{step})\n{{\n{body}\n}}').format(counter=counter, start=start, op=op,
+                                                          stop=stop, step=step, body=body)
+        else:
+            return (
+                'for ({counter} = {start}; ({step} > 0) ? ({counter} < {stop}) : ({counter} > {stop}); {counter} += '
+                '{step})\n{{\n{body}\n}}').format(counter=counter, start=start,
+                                                  stop=stop, step=step, body=body)
 
     def _print_CodeBlock(self, expr):
         body = []
diff --git a/pyccel/codegen/printing/fcode.py b/pyccel/codegen/printing/fcode.py
index a5f913be..ea9f9fb4 100644
--- a/pyccel/codegen/printing/fcode.py
+++ b/pyccel/codegen/printing/fcode.py
@@ -57,7 +57,7 @@ from pyccel.ast.datatypes import CustomDataType
 
 from pyccel.ast.internals import Slice
 
-from pyccel.ast.literals  import LiteralInteger, LiteralFloat
+from pyccel.ast.literals  import LiteralInteger, LiteralFloat, Literal
 from pyccel.ast.literals  import LiteralTrue
 from pyccel.ast.literals  import Nil
 
@@ -1777,8 +1777,24 @@ class FCodePrinter(CodePrinter):
 
     def _print_PythonRange(self, expr):
         start = self._print(expr.start)
-        stop  = self._print(expr.stop) + '-' + self._print(LiteralInteger(1))
         step  = self._print(expr.step)
+
+        test_step = expr.step
+        if isinstance(test_step, PyccelUnarySub):
+            test_step = expr.step.args[0]
+
+        # testing if the step is a value or an expression
+        if isinstance(test_step, Literal):
+            if isinstance(expr.step, PyccelUnarySub):
+                stop = PyccelAdd(expr.stop, LiteralInteger(1))
+            else:
+                stop = PyccelMinus(expr.stop, LiteralInteger(1))
+        else:
+            stop = IfTernaryOperator(PyccelGt(expr.step, LiteralInteger(0)),
+                                     PyccelMinus(expr.stop, LiteralInteger(1)),
+                                     PyccelAdd(expr.stop, LiteralInteger(1)))
+
+        stop = self._print(stop)
         return '{0}, {1}, {2}'.format(start, stop, step)
 
     def _print_Tile(self, expr):

test_patch: diff --git a/tests/epyccel/modules/loops.py b/tests/epyccel/modules/loops.py
index 9ee805c1..8ee87c54 100644
--- a/tests/epyccel/modules/loops.py
+++ b/tests/epyccel/modules/loops.py
@@ -215,3 +215,22 @@ def while_not_0( n ):
     while n:
         n -= 1
     return n
+
+@types( int, int, int )
+def for_loop1(start, stop, step):
+    x = 0
+    for i in range(start, stop, step):
+        x += i
+    return x
+
+def for_loop2():
+    x = 0
+    for i in range(1, 10, 1):
+        x += i
+    return x
+
+def for_loop3():
+    x = 0
+    for i in range(10, 1, -2):
+        x += i
+    return x
diff --git a/tests/epyccel/test_loops.py b/tests/epyccel/test_loops.py
index ef5db8b4..4956af53 100644
--- a/tests/epyccel/test_loops.py
+++ b/tests/epyccel/test_loops.py
@@ -184,6 +184,21 @@ def test_loop_on_real_array(language):
 
     assert np.array_equal( out1, out2 )
 
+def test_for_loops(language):
+    f1 = loops.for_loop1
+    g1 = epyccel(f1, language=language)
+    f2 = loops.for_loop2
+    g2 = epyccel(f2, language=language)
+    f3 = loops.for_loop2
+    g3 = epyccel(f3, language=language)
+
+    assert (f1(1,10,1) == g1(1,10,1))
+    assert (f1(10,1,-1) == g1(10,1,-1))
+    assert (f1(1, 10, 2) == g1(1, 10, 2))
+    assert (f1(10, 1, -3) == g1(10, 1, -3))
+    assert (f2() == g2())
+    assert (f3() == g3())
+
 def test_breaks(language):
     f1 = loops.fizzbuzz_search_with_breaks
     f2 = epyccel( f1, language = language )