# cython: infer_types=True

import numpy as np

cimport cython

from numpy import nan

{# cdef float NAN = float("NaN") #}

from libc.math cimport copysign, isfinite, INFINITY, NAN

cdef float inf = INFINITY
{# cdef float  = INFINITY #}

{% for config_add_sub in configs_add_sub %}

# s/boundscheck(True/boundscheck(False
# s/boundscheck(False/boundscheck(True

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef {{config_add_sub.operation}}_rles(long [::1] runs1, {{config_add_sub.value_dtype}} [::1] values1, long [::1] runs2, {{config_add_sub.value_dtype}} [::1] values2):

    cdef int x1 = 0
    cdef int x2 = 0
    cdef int xn = 0
    cdef int nr = 0
    cdef {{config_add_sub.value_dtype}} nv = 0
    cdef {{config_add_sub.value_dtype}} diff = 0
    cdef int l1 = len(runs1)
    cdef int l2 = len(runs2)
    cdef {{config_add_sub.run_dtype}} r1 = runs1[x1]
    cdef {{config_add_sub.run_dtype}} r2 = runs2[x2]
    nrs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.{{config_add_sub.run_dtype}})
    nvs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.{{config_add_sub.value_dtype}})

    cdef {{config_add_sub.run_dtype}}[::1] nrs
    cdef {{config_add_sub.value_dtype}}[::1] nvs

    nrs = nrs_arr
    nvs = nvs_arr

    while(x1 < l1 and x2 < l2):

        diff = r1 - r2
        nv = values1[x1] {{config_add_sub.op}} values2[x2]
        if diff < 0:
            nr = r1
            r2 = r2 - r1
            x1 += 1
            if x1 < l1:
                r1 = runs1[x1]
        elif diff > 0:
            nr = r2
            r1 = r1 - r2
            x2 += 1
            if x2 < l2:
                r2 = runs2[x2]
        else:
            nr = r2
            x1 += 1
            x2 += 1
            if x1 < l1:
                r1 = runs1[x1]
            if x2 < l2:
                r2 = runs2[x2]

        # if the new value is the same as the old, merge the runs
        if xn > 0 and nv == nvs[xn - 1]:
            nrs[xn - 1] += nr
        else:
            nrs[xn] = nr
            nvs[xn] = nv
            xn += 1

    # Must use resize because initial guess for array was likely way too large
    nrs_arr.resize(xn, refcheck=False)
    nvs_arr.resize(xn, refcheck=False)

    return nrs_arr, nvs_arr

{% endfor %}


{% for config_div in configs_div %}

@cython.cdivision(True)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef div_rles_{{config_div.name}}(long [::1] runs1, double [::1] values1, long [::1] runs2, double [::1] values2):

    cdef int x1 = 0
    cdef int x2 = 0
    cdef int xn = 0
    cdef int nr = 0
    cdef double nv = 0
    cdef double diff = 0
    cdef double sign = 0
    cdef int l1 = len(runs1)
    cdef int l2 = len(runs2)
    cdef long r1 = runs1[x1]
    cdef long r2 = runs2[x2]
    nrs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.int)
    nvs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.double)

    cdef long[::1] nrs
    cdef double[::1] nvs

    nrs = nrs_arr
    nvs = nvs_arr

    while(x1 < l1 and x2 < l2):

        diff = r1 - r2
        {{config_div.nv}}
        if diff < 0:
            nr = r1
            r2 = r2 - r1
            x1 += 1
            if x1 < l1:
                r1 = runs1[x1]
        elif diff > 0:
            nr = r2
            r1 = r1 - r2
            x2 += 1
            if x2 < l2:
                r2 = runs2[x2]
        else:
            nr = r2
            x1 += 1
            x2 += 1
            if x1 < l1:
                r1 = runs1[x1]
            if x2 < l2:
                r2 = runs2[x2]

        # if the new value is the same as the old, merge the runs
        if xn > 0 and nv == nvs[xn - 1]:
            nrs[xn - 1] += nr
        else:
            nrs[xn] = nr
            nvs[xn] = nv
            xn += 1

    # Must use resize because initial guess for array was likely way too large
    nrs_arr.resize(xn, refcheck=False)
    nvs_arr.resize(xn, refcheck=False)

    return nrs_arr, nvs_arr

{% endfor %}



@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef mul_rles(long [::1] runs1, double [::1] values1, long [::1] runs2, double [::1] values2):
    cdef int x1 = 0
    cdef int x2 = 0
    cdef int xn = 0
    cdef int nr = 0
    cdef double nv = 0
    cdef double diff = 0
    cdef int l1 = len(runs1)
    cdef int l2 = len(runs2)
    cdef long r1 = runs1[x1]
    cdef long r2 = runs2[x2]
    nrs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.int)
    nvs_arr = np.zeros(len(runs1) + len(runs2), dtype=np.double)

    cdef long[::1] nrs
    cdef double[::1] nvs

    nrs = nrs_arr
    nvs = nvs_arr

    while(x1 < l1 and x2 < l2):

        diff = r1 - r2
        nv = values1[x1] * values2[x2]
        if diff < 0:
            nr = r1
            r2 = r2 - r1
            x1 += 1
            if x1 < l1:
                r1 = runs1[x1]
        elif diff > 0:
            nr = r2
            r1 = r1 - r2
            x2 += 1
            if x2 < l2:
                r2 = runs2[x2]
        else:
            nr = r2
            x1 += 1
            x2 += 1
            if x1 < l1:
                r1 = runs1[x1]
            if x2 < l2:
                r2 = runs2[x2]

        # if the new value is the same as the old, merge the runs
        if xn > 0 and nv == nvs[xn - 1]:
            nrs[xn - 1] += nr
        else:
            nrs[xn] = nr
            nvs[xn] = nv
            xn += 1


    return nrs_arr, nvs_arr
