/* -*- c -*- */

/* The purpose of this module is to add faster sort functions
   that are type-specific.  This is done by altering the
   function table for the builtin descriptors.

   These sorting functions are copied almost directly from numarray
   with a few modifications (complex comparisons compare the imaginary
   part if the real parts are equal, for example), and the names
   are changed.

   The original sorting code is due to Charles R. Harris who wrote
   it for numarray.
*/

/* Quick sort is usually the fastest, but the worst case scenario can
   be slower than the merge and heap sorts.  The merge sort requires
   extra memory and so for large arrays may not be useful.

   The merge sort is *stable*, meaning that equal components
   are unmoved from their entry versions, so it can be used to
   implement lexigraphic sorting on multiple keys.

   The heap sort is included for completeness.
*/


#include "Python.h"
#include "numpy/noprefix.h"

#define PYA_QS_STACK 100
#define SMALL_QUICKSORT 15
#define SMALL_MERGESORT 20
#define SMALL_STRING 16
#define SWAP(a,b) {SWAP_temp = (b); (b)=(a); (a) = SWAP_temp;}
#define STDC_LT(a,b) ((a) < (b))
#define STDC_LE(a,b) ((a) <= (b))
#define STDC_EQ(a,b) ((a) == (b))
#define NUMC_LT(p,q) ((((p).real==(q).real) ? ((p).imag < (q).imag): ((p).real < (q).real)))
#define NUMC_LE(p,q) ((((p).real==(q).real) ? ((p).imag <= (q).imag): ((p).real <= (q).real)))
#define NUMC_EQ(p,q) (((p).real==(q).real) && ((p).imag == (q).imag))
#define STRING_LT(pa, pb, len) (compare_string(pa, pb, len) < 0)
#define STRING_LE(pa, pb, len) (compare_string(pa, pb, len) <= 0)
#define STRING_EQ(pa, pb, len) (compare_string(pa, pb, len) == 0)
#define UNICODE_LT(pa, pb, len) (compare_ucs4(pa, pb, len) < 0)
#define UNICODE_LE(pa, pb, len) (compare_ucs4(pa, pb, len) <= 0)
#define UNICODE_EQ(pa, pb, len) (compare_ucs4(pa, pb, len) == 0)


/**begin repeat
   #TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE#
   #type=Bool,byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,cfloat,cdouble,clongdouble#
   #lessthan=STDC_LT*14,NUMC_LT*3#
   #lessequal=STDC_LE*14,NUMC_LE*3#
**/
static int
@TYPE@_quicksort(@type@ *start, intp num, void *unused)
{
    @type@ *pl = start;
    @type@ *pr = start + num - 1;
    @type@ vp, SWAP_temp;
    @type@ *stack[PYA_QS_STACK], **sptr = stack, *pm, *pi, *pj, *pk;

    for(;;) {
        while ((pr - pl) > SMALL_QUICKSORT) {
            /* quicksort partition */
            pm = pl + ((pr - pl) >> 1);
            if (@lessthan@(*pm, *pl)) SWAP(*pm, *pl);
            if (@lessthan@(*pr, *pm)) SWAP(*pr, *pm);
            if (@lessthan@(*pm, *pl)) SWAP(*pm, *pl);
            vp = *pm;
            pi = pl;
            pj = pr - 1;
            SWAP(*pm, *pj);
            for(;;) {
                do ++pi; while (@lessthan@(*pi, vp));
                do --pj; while (@lessthan@(vp, *pj));
                if (pi >= pj)  break;
                SWAP(*pi,*pj);
            }
            pk = pr - 1;
            SWAP(*pi, *pk);
            /* push largest partition on stack */
            if (pi - pl < pr - pi) {
                *sptr++ = pi + 1;
                *sptr++ = pr;
                pr = pi - 1;
            }
            else {
                *sptr++ = pl;
                *sptr++ = pi - 1;
                pl = pi + 1;
            }
        }

        /* insertion sort */
        for(pi = pl + 1; pi <= pr; ++pi) {
            vp = *pi;
            pj = pi;
            pk = pi - 1;
            while (pj > pl && @lessthan@(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
        if (sptr == stack) break;
        pr = *(--sptr);
        pl = *(--sptr);
    }

    return 0;
}

static int
@TYPE@_aquicksort(@type@ *v, intp* tosort, intp num, void *unused)
{
    @type@ vp;
    intp *pl, *pr, SWAP_temp;
    intp *stack[PYA_QS_STACK], **sptr=stack, *pm, *pi, *pj, *pk, vi;

    pl = tosort;
    pr = tosort + num - 1;

    for(;;) {
        while ((pr - pl) > SMALL_QUICKSORT) {
            /* quicksort partition */
            pm = pl + ((pr - pl) >> 1);
            if (@lessthan@(v[*pm],v[*pl])) SWAP(*pm,*pl);
            if (@lessthan@(v[*pr],v[*pm])) SWAP(*pr,*pm);
            if (@lessthan@(v[*pm],v[*pl])) SWAP(*pm,*pl);
            vp = v[*pm];
            pi = pl;
            pj = pr - 1;
            SWAP(*pm,*pj);
            for(;;) {
                do ++pi; while (@lessthan@(v[*pi],vp));
                do --pj; while (@lessthan@(vp,v[*pj]));
                if (pi >= pj)  break;
                SWAP(*pi,*pj);
            }
            pk = pr - 1; 
            SWAP(*pi,*pk);
            /* push largest partition on stack */
            if (pi - pl < pr - pi) {
                *sptr++ = pi + 1;
                *sptr++ = pr;
                pr = pi - 1;
            }
            else {
                *sptr++ = pl;
                *sptr++ = pi - 1;
                pl = pi + 1;
            }
        }

        /* insertion sort */
        for(pi = pl + 1; pi <= pr; ++pi) {
            vi = *pi;
            vp = v[vi];
            pj = pi;
            pk = pi - 1;
            while (pj > pl && @lessthan@(vp, v[*pk])) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
        if (sptr == stack) break;
        pr = *(--sptr);
        pl = *(--sptr);
    }

    return 0;
}


static int
@TYPE@_heapsort(@type@ *start, intp n, void *unused)
{
    @type@ tmp, *a;
    intp i,j,l;

    /* The array needs to be offset by one for heapsort indexing */
    a = start - 1;

    for (l = n>>1; l > 0; --l) {
        tmp = a[l];
        for (i = l, j = l<<1; j <= n;) {
            if (j < n && @lessthan@(a[j], a[j+1]))
                j += 1;
            if (@lessthan@(tmp, a[j])) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    for (; n > 1;) {
        tmp = a[n];
        a[n] = a[1];
        n -= 1;
        for (i = 1, j = 2; j <= n;) {
            if (j < n && @lessthan@(a[j], a[j+1]))
                j++;
            if (@lessthan@(tmp, a[j])) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    return 0;
}

static int
@TYPE@_aheapsort(@type@ *v, intp *tosort, intp n, void *unused)
{
    intp *a, i,j,l, tmp;
    /* The arrays need to be offset by one for heapsort indexing */
    a = tosort - 1;

    for (l = n>>1; l > 0; --l) {
        tmp = a[l];
        for (i = l, j = l<<1; j <= n;) {
            if (j < n && @lessthan@(v[a[j]], v[a[j+1]]))
                j += 1;
            if (@lessthan@(v[tmp], v[a[j]])) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    for (; n > 1;) {
        tmp = a[n];
        a[n] = a[1];
        n -= 1;
        for (i = 1, j = 2; j <= n;) {
            if (j < n && @lessthan@(v[a[j]], v[a[j+1]]))
                j++;
            if (@lessthan@(v[tmp], v[a[j]])) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    return 0;
}

static void
@TYPE@_mergesort0(@type@ *pl, @type@ *pr, @type@ *pw)
{
    @type@ vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        @TYPE@_mergesort0(pl, pm, pw);
        @TYPE@_mergesort0(pm, pr, pw);
        for(pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (@lessequal@(*pj,*pm)) {
                *pk = *pj++;
            }
            else {
                *pk = *pm++;
            }
            pk++;
        }
        while(pj < pi) {
            *pk++ = *pj++;
        }
    }
    else {
        /* insertion sort */
        for(pi = pl + 1; pi < pr; ++pi) {
            vp = *pi;
            pj = pi;
            pk = pi -1;
            while (pj > pl && @lessthan@(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}

static int
@TYPE@_mergesort(@type@ *start, intp num, void *unused)
{
    @type@ *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = (@type@ *) PyDataMem_NEW((num/2)*sizeof(@type@));
    if (!pw) {
        PyErr_NoMemory();
        return -1;
    }
    @TYPE@_mergesort0(pl, pr, pw);

    PyDataMem_FREE(pw);
    return 0;
}

static void
@TYPE@_amergesort0(intp *pl, intp *pr, @type@ *v, intp *pw)
{
    @type@ vp;
    intp vi, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl + 1)>>1);
        @TYPE@_amergesort0(pl,pm-1,v,pw);
        @TYPE@_amergesort0(pm,pr,v,pw);
        for(pi = pw, pj = pl; pj < pm; ++pi, ++pj) {
            *pi = *pj;
        }
        for(pk = pw, pm = pl; pk < pi && pj <= pr; ++pm) {
            if (@lessequal@(v[*pk],v[*pj])) {
                *pm = *pk;
                ++pk;
            }
            else {
                *pm = *pj;
                ++pj;
            }
        }
        for(; pk < pi; ++pm, ++pk) {
            *pm = *pk;
        }
    }
    else {
        /* insertion sort */
        for(pi = pl + 1; pi <= pr; ++pi) {
            vi = *pi;
            vp = v[vi];
            for(pj = pi, pk = pi - 1; pj > pl && @lessthan@(vp, v[*pk]); --pj, --pk) {
                *pj = *pk;
            }
            *pj = vi;
        }
    }
}

static int
@TYPE@_amergesort(@type@ *v, intp *tosort, intp num, void *unused)
{
    intp *pl, *pr, *pw;

    pl = tosort; pr = pl + num - 1;
    pw = PyDimMem_NEW((1+num/2));

    if (!pw) {
        PyErr_NoMemory();
        return -1;
    }

    @TYPE@_amergesort0(pl, pr, v, pw);
    PyDimMem_FREE(pw);

    return 0;
}
/**end repeat**/

/*
 * Subroutines that will hopefully be inlined when the code
 * for strings and unicode is compiled with proper flags.
 */

static int
compare_string(char *s1, char *s2, size_t len)
{
    const unsigned char *c1 = (unsigned char *)s1;
    const unsigned char *c2 = (unsigned char *)s2;
    size_t i;

    for(i = 0; i < len; ++i) {
        if (c1[i] != c2[i]) {
            return (c1[i] > c2[i]) ? 1 : -1;
        }
    }
    return 0;
}

static void
copy_string(char *s1, char *s2, size_t len)
{
    if (len < SMALL_STRING) {
        while(len--) {
            *s1++ = *s2++;
        }
    }
    else {
        memcpy(s1, s2, len);
    }
}

static void
swap_string(char *s1, char *s2, size_t len)
{
    while(len--) {
        const char t = *s1;
        *s1++ = *s2;
        *s2++ = t;
    }
}


static int
compare_ucs4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
{
    size_t i;

    for(i = 0; i < len; ++i) {
        if (s1[i] != s2[i]) {
            return (s1[i] > s2[i]) ? 1 : -1;
        }
    }
    return 0;
}


static void
copy_ucs4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
{
    while(len--) {
        *s1++ = *s2++;
    }
}


static void
swap_ucs4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len)
{
    while(len--) {
        const npy_ucs4 t = *s1;
        *s1++ = *s2;
        *s2++ = t;
    }
}

/**begin repeat
   #TYPE=STRING, UNICODE#
   #type=char, PyArray_UCS4#
   #lessthan=STRING_LT, UNICODE_LT#
   #lessequal=STRING_LE, UNICODE_LE#
   #swap=swap_string, swap_ucs4#
   #copy=copy_string, copy_ucs4#
**/

static void
@TYPE@_mergesort0(@type@ *pl, @type@ *pr, @type@ *pw, @type@ *vp, size_t len)
{
    @type@ *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT*len) {
        /* merge sort */
        pm = pl + (((pr - pl)/len) >> 1)*len;
        @TYPE@_mergesort0(pl, pm, pw, vp, len);
        @TYPE@_mergesort0(pm, pr, pw, vp, len);
        @copy@(pw, pl, pm - pl);
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (@lessequal@(pj, pm, len)) {
                @copy@(pk, pj, len);
                pj += len;
            }
            else {
                @copy@(pk, pm, len);
                pm += len;
            }
            pk += len;
        }
        @copy@(pk, pj, pi - pj);
    }
    else {
        /* insertion sort */
        for(pi = pl + len; pi < pr; pi += len) {
            @copy@(vp, pi, len);
            pj = pi;
            pk = pi - len;
            while (pj > pl && @lessthan@(vp, pk, len)) {
                @copy@(pj, pk, len);
                pj -= len;
                pk -= len;
            }
            @copy@(pj, vp, len);
        }
    }
}

static int
@TYPE@_mergesort(@type@ *start, intp num, PyArrayObject *arr)
{
    const size_t elsize = arr->descr->elsize;
    const size_t len = elsize / sizeof(@type@);
    @type@ *pl, *pr, *pw, *vp;
    int err = 0;

    pl = start;
    pr = pl + num*len;
    pw = (@type@ *) PyDataMem_NEW((num/2)*elsize);
    if (!pw) {
        PyErr_NoMemory();
        err = -1;
        goto fail_0;
    }
    vp = (@type@ *) PyDataMem_NEW(elsize);
    if (!vp) {
        PyErr_NoMemory();
        err = -1;
        goto fail_1;
    }
    @TYPE@_mergesort0(pl, pr, pw, vp, len);

    PyDataMem_FREE(vp);
fail_1:
    PyDataMem_FREE(pw);
fail_0:
    return err;
}

static int
@TYPE@_quicksort(@type@ *start, intp num, PyArrayObject *arr)
{
    const size_t len = arr->descr->elsize/sizeof(@type@);
    @type@ *vp = malloc(arr->descr->elsize);
    @type@ *pl = start;
    @type@ *pr = start + (num - 1)*len;
    @type@ *stack[PYA_QS_STACK], **sptr = stack, *pm, *pi, *pj, *pk;

    for(;;) {
        while ((pr - pl) > SMALL_QUICKSORT*len) {
            /* quicksort partition */
            pm = pl + (((pr - pl)/len) >> 1)*len;
            if (@lessthan@(pm, pl, len)) @swap@(pm, pl, len);
            if (@lessthan@(pr, pm, len)) @swap@(pr, pm, len);
            if (@lessthan@(pm, pl, len)) @swap@(pm, pl, len);
            @copy@(vp, pm, len);
            pi = pl;
            pj = pr - len;
            @swap@(pm, pj, len);
            for(;;) {
                do pi += len; while (@lessthan@(pi, vp, len));
                do pj -= len; while (@lessthan@(vp, pj, len));
                if (pi >= pj)  break;
                @swap@(pi, pj, len);
            }
            pk = pr - len;
            @swap@(pi, pk, len);
            /* push largest partition on stack */
            if (pi - pl < pr - pi) {
                *sptr++ = pi + len;
                *sptr++ = pr;
                pr = pi - len;
            }
            else {
                *sptr++ = pl;
                *sptr++ = pi - len;
                pl = pi + len;
            }
        }

        /* insertion sort */
        for(pi = pl + len; pi <= pr; pi += len) {
            @copy@(vp, pi, len);
            pj = pi;
            pk = pi - len;
            while (pj > pl && @lessthan@(vp, pk, len)) {
                @copy@(pj, pk, len);
                pj -= len;
                pk -= len;
            }
            @copy@(pj, vp, len);
        }
        if (sptr == stack) break;
        pr = *(--sptr);
        pl = *(--sptr);
    }

    free(vp);
    return 0;
}


static int
@TYPE@_heapsort(@type@ *start, intp n, PyArrayObject *arr)
{
    size_t len = arr->descr->elsize/sizeof(@type@);
    @type@ *tmp = malloc(arr->descr->elsize);
    @type@ *a = start - len;
    intp i,j,l;

    for (l = n>>1; l > 0; --l) {
        @copy@(tmp, a + l*len, len);
        for (i = l, j = l<<1; j <= n;) {
            if (j < n && @lessthan@(a + j*len, a + (j+1)*len, len))
                j += 1;
            if (@lessthan@(tmp, a + j*len, len)) {
                @copy@(a + i*len, a + j*len, len);
                i = j;
                j += j;
            }
            else
                break;
        }
        @copy@(a + i*len, tmp, len);
    }

    for (; n > 1;) {
        @copy@(tmp, a + n*len, len);
        @copy@(a + n*len, a + len, len);
        n -= 1;
        for (i = 1, j = 2; j <= n;) {
            if (j < n && @lessthan@(a + j*len, a + (j+1)*len, len))
                j++;
            if (@lessthan@(tmp, a + j*len, len)) {
                @copy@(a + i*len, a + j*len, len);
                i = j;
                j += j;
            }
            else
                break;
        }
        @copy@(a + i*len, tmp, len);
    }

    free(tmp);
    return 0;
}


static int
@TYPE@_aheapsort(@type@ *v, intp *tosort, intp n, PyArrayObject *arr)
{
    size_t len = arr->descr->elsize/sizeof(@type@);
    intp *a, i,j,l, tmp;

    /* The array needs to be offset by one for heapsort indexing */
    a = tosort - 1;

    for (l = n>>1; l > 0; --l) {
        tmp = a[l];
        for (i = l, j = l<<1; j <= n;) {
            if (j < n && @lessthan@(v + a[j]*len, v + a[j+1]*len, len))
                j += 1;
            if (@lessthan@(v + tmp*len, v + a[j]*len, len)) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    for (; n > 1;) {
        tmp = a[n];
        a[n] = a[1];
        n -= 1;
        for (i = 1, j = 2; j <= n;) {
            if (j < n && @lessthan@(v + a[j]*len, v + a[j+1]*len, len))
                j++;
            if (@lessthan@(v + tmp*len, v + a[j]*len, len)) {
                a[i] = a[j];
                i = j;
                j += j;
            }
            else
                break;
        }
        a[i] = tmp;
    }

    return 0;
}


static int
@TYPE@_aquicksort(@type@ *v, intp* tosort, intp num, PyArrayObject *arr)
{
    size_t len = arr->descr->elsize/sizeof(@type@);
    @type@ *vp;
    intp *pl = tosort;
    intp *pr = tosort + num - 1;
    intp *stack[PYA_QS_STACK];
    intp **sptr=stack;
    intp *pm, *pi, *pj, *pk, vi, SWAP_temp;

    for(;;) {
        while ((pr - pl) > SMALL_QUICKSORT) {
            /* quicksort partition */
            pm = pl + ((pr - pl) >> 1);
            if (@lessthan@(v + (*pm)*len, v + (*pl)*len, len)) SWAP(*pm, *pl);
            if (@lessthan@(v + (*pr)*len, v + (*pm)*len, len)) SWAP(*pr, *pm);
            if (@lessthan@(v + (*pm)*len, v + (*pl)*len, len)) SWAP(*pm, *pl);
            vp = v + (*pm)*len;
            pi = pl;
            pj = pr - 1;
            SWAP(*pm,*pj);
            for(;;) {
                do ++pi; while (@lessthan@(v + (*pi)*len, vp, len));
                do --pj; while (@lessthan@(vp, v + (*pj)*len, len));
                if (pi >= pj)  break;
                SWAP(*pi,*pj);
            }
            pk = pr - 1;
            SWAP(*pi,*pk);
            /* push largest partition on stack */
            if (pi - pl < pr - pi) {
                *sptr++ = pi + 1;
                *sptr++ = pr;
                pr = pi - 1;
            }
            else {
                *sptr++ = pl;
                *sptr++ = pi - 1;
                pl = pi + 1;
            }
        }

        /* insertion sort */
        for(pi = pl + 1; pi <= pr; ++pi) {
            vi = *pi;
            vp = v + vi*len;
            pj = pi;
            pk = pi - 1;
            while (pj > pl && @lessthan@(vp, v + (*pk)*len, len)) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
        if (sptr == stack) break;
        pr = *(--sptr);
        pl = *(--sptr);
    }

    return 0;
}


static void
@TYPE@_amergesort0(intp *pl, intp *pr, @type@ *v, intp *pw, int len)
{
    @type@ *vp;
    intp vi, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        @TYPE@_amergesort0(pl,pm,v,pw,len);
        @TYPE@_amergesort0(pm,pr,v,pw,len);
        for(pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (@lessequal@(v + (*pj)*len, v + (*pm)*len, len)) {
                *pk = *pj++;
            } else {
                *pk = *pm++;
            }
            pk++;
        }
        while (pj < pi) {
            *pk++ = *pj++;
        }
    } else {
        /* insertion sort */
        for(pi = pl + 1; pi < pr; ++pi) {
            vi = *pi;
            vp = v + vi*len;
            pj = pi;
            pk = pi -1;
            while (pj > pl && @lessthan@(vp, v + (*pk)*len, len)) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
    }
}


static int
@TYPE@_amergesort(@type@ *v, intp *tosort, intp num, PyArrayObject *arr)
{
    const size_t elsize = arr->descr->elsize;
    const size_t len = elsize / sizeof(@type@);
    intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = PyDimMem_NEW(num/2);
    if (!pw) {
        PyErr_NoMemory();
        return -1;
    }
    @TYPE@_amergesort0(pl, pr, v, pw, len);

    PyDimMem_FREE(pw);
    return 0;
}
/**end repeat**/

static void
add_sortfuncs(void)
{
    PyArray_Descr *descr;

    /**begin repeat
       #TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,STRING,UNICODE#
    **/
    descr = PyArray_DescrFromType(PyArray_@TYPE@);
    descr->f->sort[PyArray_QUICKSORT] = \
        (PyArray_SortFunc *)@TYPE@_quicksort;
    descr->f->argsort[PyArray_QUICKSORT] = \
        (PyArray_ArgSortFunc *)@TYPE@_aquicksort;
    descr->f->sort[PyArray_HEAPSORT] = \
        (PyArray_SortFunc *)@TYPE@_heapsort;
    descr->f->argsort[PyArray_HEAPSORT] = \
        (PyArray_ArgSortFunc *)@TYPE@_aheapsort;
    descr->f->sort[PyArray_MERGESORT] = \
        (PyArray_SortFunc *)@TYPE@_mergesort;
    descr->f->argsort[PyArray_MERGESORT] = \
        (PyArray_ArgSortFunc *)@TYPE@_amergesort;
    /**end repeat**/

}

static struct PyMethodDef methods[] = {
    {NULL, NULL, 0}
};

PyMODINIT_FUNC
init_sort(void) {

    Py_InitModule("_sort", methods);

    import_array();
    add_sortfuncs();
}
