Repository: numba/numba

Issue:
Results change if I print them before returning
I have a macports version of python-2.7 with llvm-3.6 and the pip version of numba 0.23.1 (everything is up-to-date)

Here is the code I'm using:

```
import pandas as pd
from numba import jit
from numpy import nan, full

@jit
def movingAverage(adj_close, maxMA):
    ma = full([len(adj_close), maxMA], nan, dtype=float64)
    ind = range( 1, len(adj_close)+1 )
    for d in ind:
        m = max( 0, d-maxMA-1)
        adj = adj_close[d-1:m:-1] if (m or d==maxMA+1) else adj_close[d-1::-1]
        cs = adj.cumsum()
        for i in range( len(adj) ):
            ma[d-1][i] = ( cs[i] / (i+1) )
        print ma
    return ma
```

I'm calculating a rolling mean for the input `adj_close` for up to `maxMA` days.

`adj_close` is a array of values, one value per day

I started by creating ma, a holder for the values that are going to be calculated. And work out the vaules for each day individually (note that the first day can only have an average involving 1 day, the second, 2 and so on up to the `maxMA`)

If I input something like `adj_close = array(range(5), dtype=float64)` and `maxMA = 3` get the right answer as follows:

```
array([[  0.,  nan,  nan],
       [  1.,   0.5,  nan],
       [  2.,   1.5,   1.],
       [  3.,   2.5,   2.],
       [  4.,   3.5,   3.]])
```

However, If I take out the `print ma` line, just before the return of my function, it returns only part of the answer:

```
array([[ nan,  nan,  nan],
       [ nan,  nan,  nan],
       [ nan,  nan,  nan],
       [  3.,   2.5,   2.],
       [  4.,   3.5,   3.]])
```



Discussion:
Thank you for the report! This is an actual bug indeed.


Resolving patch:
diff --git a/numba/pythonapi.py b/numba/pythonapi.py
--- a/numba/pythonapi.py
+++ b/numba/pythonapi.py
@@ -663,16 +663,16 @@ def complex_imag_as_double(self, cobj):
     # Concrete slice API
     #
 
-    def slice_as_ints(self, obj, defaults):
+    def slice_as_ints(self, obj):
         """
         Read the members of a slice of integers.
+
         Returns a (ok, start, stop, step) tuple where ok is a boolean and
         the following members are pointer-sized ints.
         """
-        defaults = [ir.Constant(self.py_ssize_t, v) for v in defaults]
-        pstart = cgutils.alloca_once_value(self.builder, defaults[0])
-        pstop = cgutils.alloca_once_value(self.builder, defaults[1])
-        pstep = cgutils.alloca_once_value(self.builder, defaults[2])
+        pstart = cgutils.alloca_once(self.builder, self.py_ssize_t)
+        pstop = cgutils.alloca_once(self.builder, self.py_ssize_t)
+        pstep = cgutils.alloca_once(self.builder, self.py_ssize_t)
         fnty = Type.function(Type.int(),
                              [self.pyobj] + [self.py_ssize_t.as_pointer()] * 3)
         fn = self._get_function(fnty, name="numba_unpack_slice")
diff --git a/numba/targets/boxing.py b/numba/targets/boxing.py
--- a/numba/targets/boxing.py
+++ b/numba/targets/boxing.py
@@ -279,8 +279,7 @@ def unbox_slice(typ, obj, c):
     Convert object *obj* to a native slice structure.
     """
     from . import slicing
-    ok, start, stop, step = \
-        c.pyapi.slice_as_ints(obj, slicing.get_defaults(c.context))
+    ok, start, stop, step = c.pyapi.slice_as_ints(obj)
     sli = slicing.make_slice(c.context, c.builder, typ)
     sli.start = start
     sli.stop = stop
diff --git a/numba/targets/slicing.py b/numba/targets/slicing.py
--- a/numba/targets/slicing.py
+++ b/numba/targets/slicing.py
@@ -120,10 +120,11 @@ def guard_invalid_slice(context, builder, typ, slicestruct):
 
 def get_defaults(context):
     """
-    Get the default values for a slice's three members.
+    Get the default values for a slice's members:
+    (start, stop for positive step, stop for negative step, step)
     """
     maxint = (1 << (context.address_size - 1)) - 1
-    return (0, maxint, 1)
+    return (0, maxint, - maxint - 1, 1)
 
 
 #---------------------------------------------------------------------------
@@ -140,14 +141,32 @@ def make_slice(context, builder, typ, value=None):
 
 @lower_builtin(slice, types.VarArg(types.Any))
 def slice_constructor_impl(context, builder, sig, args):
-    slice_args = []
-    for ty, val, default in zip_longest(sig.args, args, get_defaults(context)):
-        if ty in (types.none, None):
-            # Omitted or None
-            slice_args.append(context.get_constant(types.intp, default))
+    default_start, default_stop_pos, default_stop_neg, default_step = \
+        [context.get_constant(types.intp, x) for x in get_defaults(context)]
+
+    # Fetch non-None arguments
+    slice_args = [None] * 3
+    for i, (ty, val) in enumerate(zip(sig.args, args)):
+        if ty is types.none:
+            slice_args[i] = None
         else:
-            slice_args.append(val)
-    start, stop, step = slice_args
+            slice_args[i] = val
+
+    # Fill omitted arguments
+    def get_arg_value(i, default):
+        val = slice_args[i]
+        if val is None:
+            return default
+        else:
+            return val
+
+    step = get_arg_value(2, default_step)
+    is_step_negative = builder.icmp_signed('<', step,
+                                           context.get_constant(types.intp, 0))
+    default_stop = builder.select(is_step_negative,
+                                  default_stop_neg, default_stop_pos)
+    stop = get_arg_value(1, default_stop)
+    start = get_arg_value(0, default_start)
 
     ty = sig.return_type
     sli = make_slice(context, builder, sig.return_type)


Test patch:
diff --git a/numba/tests/test_fancy_indexing.py b/numba/tests/test_fancy_indexing.py
--- a/numba/tests/test_fancy_indexing.py
+++ b/numba/tests/test_fancy_indexing.py
@@ -37,6 +37,7 @@ def generate_basic_index_tuples(self, N, maxdim, many=True):
             choices = [slice(None, None, None),
                        slice(1, N - 1, None),
                        slice(0, None, 2),
+                       slice(N - 1, None, -2),
                        slice(-N + 1, -1, None),
                        slice(-1, -N, -2),
                        ]
diff --git a/numba/tests/test_indexing.py b/numba/tests/test_indexing.py
--- a/numba/tests/test_indexing.py
+++ b/numba/tests/test_indexing.py
@@ -56,6 +56,15 @@ def slicing_1d_usecase6(a, stop):
         total += b[i] * (i + 1)
     return total
 
+def slicing_1d_usecase7(a, start):
+    # Omitted stop with negative step (issue #1690)
+    b = a[start::-2]
+    total = 0
+    for i in range(b.shape[0]):
+        total += b[i] * (i + 1)
+    return total
+
+
 def slicing_2d_usecase(a, start1, stop1, step1, start2, stop2, step2):
     # The index is a homogenous tuple of slices
     return a[start1:stop1:step1, start2:stop2:step2]
@@ -258,21 +267,18 @@ def test_1d_slicing4(self, flags=enable_pyobj_flags):
     def test_1d_slicing4_npm(self):
         self.test_1d_slicing4(flags=Noflags)
 
-    def test_1d_slicing5(self, flags=enable_pyobj_flags):
-        pyfunc = slicing_1d_usecase5
+    def check_1d_slicing_with_arg(self, pyfunc, flags):
+        args = list(range(-9, 10))
+
         arraytype = types.Array(types.int32, 1, 'C')
         argtys = (arraytype, types.int32)
         cr = compile_isolated(pyfunc, argtys, flags=flags)
         cfunc = cr.entry_point
 
         a = np.arange(10, dtype='i4')
-
-        args = [3, 2, 10, 0, 5]
-
         for arg in args:
             self.assertEqual(pyfunc(a, arg), cfunc(a, arg))
 
-
         # Any
         arraytype = types.Array(types.int32, 1, 'A')
         argtys = (arraytype, types.int32)
@@ -285,9 +291,27 @@ def test_1d_slicing5(self, flags=enable_pyobj_flags):
         for arg in args:
             self.assertEqual(pyfunc(a, arg), cfunc(a, arg))
 
+    def test_1d_slicing5(self, flags=enable_pyobj_flags):
+        pyfunc = slicing_1d_usecase5
+        self.check_1d_slicing_with_arg(pyfunc, flags)
+
     def test_1d_slicing5_npm(self):
         self.test_1d_slicing5(flags=Noflags)
 
+    def test_1d_slicing6(self, flags=enable_pyobj_flags):
+        pyfunc = slicing_1d_usecase6
+        self.check_1d_slicing_with_arg(pyfunc, flags)
+
+    def test_1d_slicing6_npm(self):
+        self.test_1d_slicing6(flags=Noflags)
+
+    def test_1d_slicing7(self, flags=enable_pyobj_flags):
+        pyfunc = slicing_1d_usecase7
+        self.check_1d_slicing_with_arg(pyfunc, flags)
+
+    def test_1d_slicing7_npm(self):
+        self.test_1d_slicing7(flags=Noflags)
+
     def test_2d_slicing(self, flags=enable_pyobj_flags):
         """
         arr_2d[a:b:c]
diff --git a/numba/tests/test_slices.py b/numba/tests/test_slices.py
--- a/numba/tests/test_slices.py
+++ b/numba/tests/test_slices.py
@@ -28,11 +28,23 @@ def check(a, b, c, d, e, f):
             got = cfunc(sl)
             self.assertPreciseEqual(got, (d, e, f))
 
-        maxint = sys.maxsize
+        maxposint = sys.maxsize
+        maxnegint = -maxposint - 1
         cfunc = jit(nopython=True)(slice_passing)
+
+        # Positive steps
+        start_cases = [(None, 0), (42, 42), (-1, -1)]
+        stop_cases = [(None, maxposint), (9, 9), (-11, -11)]
+        step_cases = [(None, 1), (12, 12)]
+        for (a, d), (b, e), (c, f) in itertools.product(start_cases,
+                                                        stop_cases,
+                                                        step_cases):
+            check(a, b, c, d, e, f)
+
+        # Negative steps
         start_cases = [(None, 0), (42, 42), (-1, -1)]
-        stop_cases = [(None, maxint), (9, 9), (-11, -11)]
-        step_cases = [(None, 1), (12, 12), (-33, -33)]
+        stop_cases = [(None, maxnegint), (9, 9), (-11, -11)]
+        step_cases = [(-1, -1), (-12, -12)]
         for (a, d), (b, e), (c, f) in itertools.product(start_cases,
                                                         stop_cases,
                                                         step_cases):