Repository: pyccel/pyccel (python)

Issue:
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


Pull request:
Allow negative step in for loops
Allow for loops of descending order (with negative step). This fixes #709:

- Fix method `_print_PythonRange` of Fortran printer;
- Fix method `_print_For` of C printer;
- Add tests.

Patch:
diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index 9a5f4601..6128e2c6 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -1317,7 +1317,7 @@ 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 = self._print(expr.iterable.start)
@@ -1325,9 +1325,22 @@ class CCodePrinter(CodePrinter):
             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_exprs, new_vars = expand_to_loops(expr, self._parser.get_new_variable, language_has_vectors = False)
diff --git a/pyccel/codegen/printing/fcode.py b/pyccel/codegen/printing/fcode.py
index 65e0a74b..a60de6c0 100644
--- a/pyccel/codegen/printing/fcode.py
+++ b/pyccel/codegen/printing/fcode.py
@@ -55,7 +55,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
 
@@ -1756,8 +1756,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_FunctionalFor(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 )