#line 1 "numpy/core/src/npysort/mergesort.c.src"

/*
 *****************************************************************************
 **       This file was autogenerated from a template  DO NOT EDIT!!!!      **
 **       Changes should be made to the original source (.src) file         **
 *****************************************************************************
 */

#line 1
/* -*- 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.
 */

#define NPY_NO_DEPRECATED_API NPY_API_VERSION

#include "npy_sort.h"
#include "npysort_common.h"
#include <stdlib.h>

#define NOT_USED NPY_UNUSED(unused)
#define PYA_QS_STACK 100
#define SMALL_QUICKSORT 15
#define SMALL_MERGESORT 20
#define SMALL_STRING 16


/*
 *****************************************************************************
 **                            NUMERIC SORTS                                **
 *****************************************************************************
 */


#line 62

static void
mergesort0_bool(npy_bool *pl, npy_bool *pr, npy_bool *pw)
{
    npy_bool vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_bool(pl, pm, pw);
        mergesort0_bool(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (BOOL_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && BOOL_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_bool(void *start, npy_intp num, void *NOT_USED)
{
    npy_bool *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_bool));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_bool(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_bool(npy_intp *pl, npy_intp *pr, npy_bool *v, npy_intp *pw)
{
    npy_bool vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_bool(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_bool(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_byte(npy_byte *pl, npy_byte *pr, npy_byte *pw)
{
    npy_byte vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_byte(pl, pm, pw);
        mergesort0_byte(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (BYTE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && BYTE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_byte(void *start, npy_intp num, void *NOT_USED)
{
    npy_byte *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_byte));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_byte(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_byte(npy_intp *pl, npy_intp *pr, npy_byte *v, npy_intp *pw)
{
    npy_byte vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_byte(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_byte(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_ubyte(npy_ubyte *pl, npy_ubyte *pr, npy_ubyte *pw)
{
    npy_ubyte vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_ubyte(pl, pm, pw);
        mergesort0_ubyte(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (UBYTE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && UBYTE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_ubyte(void *start, npy_intp num, void *NOT_USED)
{
    npy_ubyte *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_ubyte));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_ubyte(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_ubyte(npy_intp *pl, npy_intp *pr, npy_ubyte *v, npy_intp *pw)
{
    npy_ubyte vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_ubyte(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_ubyte(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_short(npy_short *pl, npy_short *pr, npy_short *pw)
{
    npy_short vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_short(pl, pm, pw);
        mergesort0_short(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (SHORT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && SHORT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_short(void *start, npy_intp num, void *NOT_USED)
{
    npy_short *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_short));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_short(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_short(npy_intp *pl, npy_intp *pr, npy_short *v, npy_intp *pw)
{
    npy_short vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_short(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_short(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_ushort(npy_ushort *pl, npy_ushort *pr, npy_ushort *pw)
{
    npy_ushort vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_ushort(pl, pm, pw);
        mergesort0_ushort(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (USHORT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && USHORT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_ushort(void *start, npy_intp num, void *NOT_USED)
{
    npy_ushort *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_ushort));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_ushort(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_ushort(npy_intp *pl, npy_intp *pr, npy_ushort *v, npy_intp *pw)
{
    npy_ushort vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_ushort(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_ushort(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_int(npy_int *pl, npy_int *pr, npy_int *pw)
{
    npy_int vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_int(pl, pm, pw);
        mergesort0_int(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (INT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && INT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_int(void *start, npy_intp num, void *NOT_USED)
{
    npy_int *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_int));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_int(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_int(npy_intp *pl, npy_intp *pr, npy_int *v, npy_intp *pw)
{
    npy_int vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_int(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_int(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_uint(npy_uint *pl, npy_uint *pr, npy_uint *pw)
{
    npy_uint vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_uint(pl, pm, pw);
        mergesort0_uint(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (UINT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && UINT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_uint(void *start, npy_intp num, void *NOT_USED)
{
    npy_uint *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_uint));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_uint(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_uint(npy_intp *pl, npy_intp *pr, npy_uint *v, npy_intp *pw)
{
    npy_uint vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_uint(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_uint(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_long(npy_long *pl, npy_long *pr, npy_long *pw)
{
    npy_long vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_long(pl, pm, pw);
        mergesort0_long(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (LONG_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && LONG_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_long(void *start, npy_intp num, void *NOT_USED)
{
    npy_long *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_long));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_long(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_long(npy_intp *pl, npy_intp *pr, npy_long *v, npy_intp *pw)
{
    npy_long vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_long(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_long(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_ulong(npy_ulong *pl, npy_ulong *pr, npy_ulong *pw)
{
    npy_ulong vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_ulong(pl, pm, pw);
        mergesort0_ulong(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (ULONG_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && ULONG_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_ulong(void *start, npy_intp num, void *NOT_USED)
{
    npy_ulong *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_ulong));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_ulong(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_ulong(npy_intp *pl, npy_intp *pr, npy_ulong *v, npy_intp *pw)
{
    npy_ulong vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_ulong(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_ulong(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_longlong(npy_longlong *pl, npy_longlong *pr, npy_longlong *pw)
{
    npy_longlong vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_longlong(pl, pm, pw);
        mergesort0_longlong(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (LONGLONG_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && LONGLONG_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_longlong(void *start, npy_intp num, void *NOT_USED)
{
    npy_longlong *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_longlong));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_longlong(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_longlong(npy_intp *pl, npy_intp *pr, npy_longlong *v, npy_intp *pw)
{
    npy_longlong vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_longlong(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_longlong(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_ulonglong(npy_ulonglong *pl, npy_ulonglong *pr, npy_ulonglong *pw)
{
    npy_ulonglong vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_ulonglong(pl, pm, pw);
        mergesort0_ulonglong(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (ULONGLONG_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && ULONGLONG_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_ulonglong(void *start, npy_intp num, void *NOT_USED)
{
    npy_ulonglong *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_ulonglong));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_ulonglong(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_ulonglong(npy_intp *pl, npy_intp *pr, npy_ulonglong *v, npy_intp *pw)
{
    npy_ulonglong vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_ulonglong(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_ulonglong(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_half(npy_ushort *pl, npy_ushort *pr, npy_ushort *pw)
{
    npy_ushort vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_half(pl, pm, pw);
        mergesort0_half(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (HALF_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && HALF_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_half(void *start, npy_intp num, void *NOT_USED)
{
    npy_ushort *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_ushort));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_half(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_half(npy_intp *pl, npy_intp *pr, npy_ushort *v, npy_intp *pw)
{
    npy_ushort vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_half(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_half(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_float(npy_float *pl, npy_float *pr, npy_float *pw)
{
    npy_float vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_float(pl, pm, pw);
        mergesort0_float(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (FLOAT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && FLOAT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_float(void *start, npy_intp num, void *NOT_USED)
{
    npy_float *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_float));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_float(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_float(npy_intp *pl, npy_intp *pr, npy_float *v, npy_intp *pw)
{
    npy_float vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_float(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_float(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_double(npy_double *pl, npy_double *pr, npy_double *pw)
{
    npy_double vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_double(pl, pm, pw);
        mergesort0_double(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (DOUBLE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && DOUBLE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_double(void *start, npy_intp num, void *NOT_USED)
{
    npy_double *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_double));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_double(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_double(npy_intp *pl, npy_intp *pr, npy_double *v, npy_intp *pw)
{
    npy_double vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_double(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_double(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_longdouble(npy_longdouble *pl, npy_longdouble *pr, npy_longdouble *pw)
{
    npy_longdouble vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_longdouble(pl, pm, pw);
        mergesort0_longdouble(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (LONGDOUBLE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && LONGDOUBLE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_longdouble(void *start, npy_intp num, void *NOT_USED)
{
    npy_longdouble *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_longdouble));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_longdouble(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_longdouble(npy_intp *pl, npy_intp *pr, npy_longdouble *v, npy_intp *pw)
{
    npy_longdouble vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_longdouble(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_longdouble(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_cfloat(npy_cfloat *pl, npy_cfloat *pr, npy_cfloat *pw)
{
    npy_cfloat vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_cfloat(pl, pm, pw);
        mergesort0_cfloat(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (CFLOAT_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && CFLOAT_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_cfloat(void *start, npy_intp num, void *NOT_USED)
{
    npy_cfloat *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_cfloat));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_cfloat(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_cfloat(npy_intp *pl, npy_intp *pr, npy_cfloat *v, npy_intp *pw)
{
    npy_cfloat vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_cfloat(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_cfloat(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_cdouble(npy_cdouble *pl, npy_cdouble *pr, npy_cdouble *pw)
{
    npy_cdouble vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_cdouble(pl, pm, pw);
        mergesort0_cdouble(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (CDOUBLE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && CDOUBLE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_cdouble(void *start, npy_intp num, void *NOT_USED)
{
    npy_cdouble *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_cdouble));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_cdouble(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_cdouble(npy_intp *pl, npy_intp *pr, npy_cdouble *v, npy_intp *pw)
{
    npy_cdouble vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_cdouble(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_cdouble(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_clongdouble(npy_clongdouble *pl, npy_clongdouble *pr, npy_clongdouble *pw)
{
    npy_clongdouble vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_clongdouble(pl, pm, pw);
        mergesort0_clongdouble(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (CLONGDOUBLE_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && CLONGDOUBLE_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_clongdouble(void *start, npy_intp num, void *NOT_USED)
{
    npy_clongdouble *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_clongdouble));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_clongdouble(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_clongdouble(npy_intp *pl, npy_intp *pr, npy_clongdouble *v, npy_intp *pw)
{
    npy_clongdouble vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_clongdouble(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_clongdouble(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_datetime(npy_datetime *pl, npy_datetime *pr, npy_datetime *pw)
{
    npy_datetime vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_datetime(pl, pm, pw);
        mergesort0_datetime(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (DATETIME_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && DATETIME_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_datetime(void *start, npy_intp num, void *NOT_USED)
{
    npy_datetime *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_datetime));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_datetime(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_datetime(npy_intp *pl, npy_intp *pr, npy_datetime *v, npy_intp *pw)
{
    npy_datetime vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_datetime(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_datetime(pl, pr, v, pw);
    free(pw);

    return 0;
}


#line 62

static void
mergesort0_timedelta(npy_timedelta *pl, npy_timedelta *pr, npy_timedelta *pw)
{
    npy_timedelta vp, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        mergesort0_timedelta(pl, pm, pw);
        mergesort0_timedelta(pm, pr, pw);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (TIMEDELTA_LT(*pm, *pj)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && TIMEDELTA_LT(vp, *pk)) {
                *pj-- = *pk--;
            }
            *pj = vp;
        }
    }
}


NPY_NO_EXPORT int
mergesort_timedelta(void *start, npy_intp num, void *NOT_USED)
{
    npy_timedelta *pl, *pr, *pw;

    pl = start;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_timedelta));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    mergesort0_timedelta(pl, pr, pw);

    free(pw);
    return 0;
}


static void
amergesort0_timedelta(npy_intp *pl, npy_intp *pr, npy_timedelta *v, npy_intp *pw)
{
    npy_timedelta vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

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


NPY_NO_EXPORT int
amergesort_timedelta(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED)
{
    npy_intp *pl, *pr, *pw;

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_timedelta(pl, pr, v, pw);
    free(pw);

    return 0;
}




/*
 *****************************************************************************
 **                             STRING SORTS                                **
 *****************************************************************************
 */


#line 202

static void
mergesort0_string(npy_char *pl, npy_char *pr, npy_char *pw, npy_char *vp, size_t len)
{
    npy_char *pi, *pj, *pk, *pm;

    if ((size_t)(pr - pl) > SMALL_MERGESORT*len) {
        /* merge sort */
        pm = pl + (((pr - pl)/len) >> 1)*len;
        mergesort0_string(pl, pm, pw, vp, len);
        mergesort0_string(pm, pr, pw, vp, len);
        STRING_COPY(pw, pl, pm - pl);
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (STRING_LT(pm, pj, len)) {
                STRING_COPY(pk, pm, len);
                pm += len;
                pk += len;
            }
            else {
                STRING_COPY(pk, pj, len);
                pj += len;
                pk += len;
            }
        }
        STRING_COPY(pk, pj, pi - pj);
    }
    else {
        /* insertion sort */
        for (pi = pl + len; pi < pr; pi += len) {
            STRING_COPY(vp, pi, len);
            pj = pi;
            pk = pi - len;
            while (pj > pl && STRING_LT(vp, pk, len)) {
                STRING_COPY(pj, pk, len);
                pj -= len;
                pk -= len;
            }
            STRING_COPY(pj, vp, len);
        }
    }
}


NPY_NO_EXPORT int
mergesort_string(void *start, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    size_t elsize = PyArray_ITEMSIZE(arr);
    size_t len = elsize / sizeof(npy_char);
    npy_char *pl, *pr, *pw, *vp;
    int err = 0;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pl = start;
    pr = pl + num*len;
    pw = malloc((num/2) * elsize);
    if (pw == NULL) {
        err = -NPY_ENOMEM;
        goto fail_0;
    }
    vp = malloc(elsize);
    if (vp == NULL) {
        err = -NPY_ENOMEM;
        goto fail_1;
    }
    mergesort0_string(pl, pr, pw, vp, len);

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


static void
amergesort0_string(npy_intp *pl, npy_intp *pr, npy_char *v, npy_intp *pw, size_t len)
{
    npy_char *vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        amergesort0_string(pl, pm, v, pw, len);
        amergesort0_string(pm, pr, v, pw, len);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (STRING_LT(v + (*pm)*len, v + (*pj)*len, len)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && STRING_LT(vp, v + (*pk)*len, len)) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
    }
}


NPY_NO_EXPORT int
amergesort_string(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    size_t elsize = PyArray_ITEMSIZE(arr);
    size_t len = elsize / sizeof(npy_char);
    npy_intp *pl, *pr, *pw;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_string(pl, pr, v, pw, len);
    free(pw);

    return 0;
}


#line 202

static void
mergesort0_unicode(npy_ucs4 *pl, npy_ucs4 *pr, npy_ucs4 *pw, npy_ucs4 *vp, size_t len)
{
    npy_ucs4 *pi, *pj, *pk, *pm;

    if ((size_t)(pr - pl) > SMALL_MERGESORT*len) {
        /* merge sort */
        pm = pl + (((pr - pl)/len) >> 1)*len;
        mergesort0_unicode(pl, pm, pw, vp, len);
        mergesort0_unicode(pm, pr, pw, vp, len);
        UNICODE_COPY(pw, pl, pm - pl);
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (UNICODE_LT(pm, pj, len)) {
                UNICODE_COPY(pk, pm, len);
                pm += len;
                pk += len;
            }
            else {
                UNICODE_COPY(pk, pj, len);
                pj += len;
                pk += len;
            }
        }
        UNICODE_COPY(pk, pj, pi - pj);
    }
    else {
        /* insertion sort */
        for (pi = pl + len; pi < pr; pi += len) {
            UNICODE_COPY(vp, pi, len);
            pj = pi;
            pk = pi - len;
            while (pj > pl && UNICODE_LT(vp, pk, len)) {
                UNICODE_COPY(pj, pk, len);
                pj -= len;
                pk -= len;
            }
            UNICODE_COPY(pj, vp, len);
        }
    }
}


NPY_NO_EXPORT int
mergesort_unicode(void *start, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    size_t elsize = PyArray_ITEMSIZE(arr);
    size_t len = elsize / sizeof(npy_ucs4);
    npy_ucs4 *pl, *pr, *pw, *vp;
    int err = 0;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pl = start;
    pr = pl + num*len;
    pw = malloc((num/2) * elsize);
    if (pw == NULL) {
        err = -NPY_ENOMEM;
        goto fail_0;
    }
    vp = malloc(elsize);
    if (vp == NULL) {
        err = -NPY_ENOMEM;
        goto fail_1;
    }
    mergesort0_unicode(pl, pr, pw, vp, len);

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


static void
amergesort0_unicode(npy_intp *pl, npy_intp *pr, npy_ucs4 *v, npy_intp *pw, size_t len)
{
    npy_ucs4 *vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        amergesort0_unicode(pl, pm, v, pw, len);
        amergesort0_unicode(pm, pr, v, pw, len);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (UNICODE_LT(v + (*pm)*len, v + (*pj)*len, len)) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        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 && UNICODE_LT(vp, v + (*pk)*len, len)) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
    }
}


NPY_NO_EXPORT int
amergesort_unicode(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    size_t elsize = PyArray_ITEMSIZE(arr);
    size_t len = elsize / sizeof(npy_ucs4);
    npy_intp *pl, *pr, *pw;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pl = tosort;
    pr = pl + num;
    pw = malloc((num/2) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    amergesort0_unicode(pl, pr, v, pw, len);
    free(pw);

    return 0;
}




/*
 *****************************************************************************
 **                             GENERIC SORT                                **
 *****************************************************************************
 */


static void
npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize,
               PyArray_CompareFunc *cmp, PyArrayObject *arr)
{
    char *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT*elsize) {
        /* merge sort */
        pm = pl + (((pr - pl)/elsize) >> 1)*elsize;
        npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr);
        npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr);
        GENERIC_COPY(pw, pl, pm - pl);
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (cmp(pm, pj, arr) < 0) {
                GENERIC_COPY(pk, pm, elsize);
                pm += elsize;
                pk += elsize;
            }
            else {
                GENERIC_COPY(pk, pj, elsize);
                pj += elsize;
                pk += elsize;
            }
        }
        GENERIC_COPY(pk, pj, pi - pj);
    }
    else {
        /* insertion sort */
        for (pi = pl + elsize; pi < pr; pi += elsize) {
            GENERIC_COPY(vp, pi, elsize);
            pj = pi;
            pk = pi - elsize;
            while (pj > pl && cmp(vp, pk, arr) < 0) {
                GENERIC_COPY(pj, pk, elsize);
                pj -= elsize;
                pk -= elsize;
            }
            GENERIC_COPY(pj, vp, elsize);
        }
    }
}


NPY_NO_EXPORT int
npy_mergesort(void *start, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    npy_intp elsize = PyArray_ITEMSIZE(arr);
    PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
    char *pl = start;
    char *pr = pl + num*elsize;
    char *pw;
    char *vp;
    int err = -NPY_ENOMEM;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pw = malloc((num >> 1) *elsize);
    vp = malloc(elsize);

    if (pw != NULL && vp != NULL) {
        npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr);
        err = 0;
    }

    free(vp);
    free(pw);

    return err;
}


static void
npy_amergesort0(npy_intp *pl, npy_intp *pr, char *v, npy_intp *pw,
                npy_intp elsize, PyArray_CompareFunc *cmp, PyArrayObject *arr)
{
    char *vp;
    npy_intp vi, *pi, *pj, *pk, *pm;

    if (pr - pl > SMALL_MERGESORT) {
        /* merge sort */
        pm = pl + ((pr - pl) >> 1);
        npy_amergesort0(pl, pm, v, pw, elsize, cmp, arr);
        npy_amergesort0(pm, pr, v, pw, elsize, cmp, arr);
        for (pi = pw, pj = pl; pj < pm;) {
            *pi++ = *pj++;
        }
        pi = pw + (pm - pl);
        pj = pw;
        pk = pl;
        while (pj < pi && pm < pr) {
            if (cmp(v + (*pm)*elsize, v + (*pj)*elsize, arr) < 0) {
                *pk++ = *pm++;
            }
            else {
                *pk++ = *pj++;
            }
        }
        while (pj < pi) {
            *pk++ = *pj++;
        }
    }
    else {
        /* insertion sort */
        for (pi = pl + 1; pi < pr; ++pi) {
            vi = *pi;
            vp = v + vi*elsize;
            pj = pi;
            pk = pi - 1;
            while (pj > pl && cmp(vp, v + (*pk)*elsize, arr) < 0) {
                *pj-- = *pk--;
            }
            *pj = vi;
        }
    }
}


NPY_NO_EXPORT int
npy_amergesort(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
    PyArrayObject *arr = varr;
    npy_intp elsize = PyArray_ITEMSIZE(arr);
    PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
    npy_intp *pl, *pr, *pw;

    /* Items that have zero size don't make sense to sort */
    if (elsize == 0) {
        return 0;
    }

    pl = tosort;
    pr = pl + num;
    pw = malloc((num >> 1) * sizeof(npy_intp));
    if (pw == NULL) {
        return -NPY_ENOMEM;
    }
    npy_amergesort0(pl, pr, v, pw, elsize, cmp, arr);
    free(pw);

    return 0;
}

