#line 1 "numpy/core/src/npysort/binsearch.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 -*- */
#define NPY_NO_DEPRECATED_API NPY_API_VERSION

#include "npy_sort.h"
#include "npysort_common.h"
#include "npy_binsearch.h"

#define NOT_USED NPY_UNUSED(unused)

/*
 *****************************************************************************
 **                            NUMERIC SEARCHES                             **
 *****************************************************************************
 */

#line 29

#define BOOL_LTE(a, b) (!BOOL_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_bool(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_bool last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_bool *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_bool key_val = *(const npy_bool *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BOOL_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_bool mid_val = *(const npy_bool *)(arr + mid_idx*arr_str);
            if (BOOL_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_bool(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_bool last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_bool *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_bool key_val = *(const npy_bool *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BOOL_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_bool mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_bool *)(arr + sort_idx*arr_str);

            if (BOOL_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_bool(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_bool last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_bool *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_bool key_val = *(const npy_bool *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BOOL_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_bool mid_val = *(const npy_bool *)(arr + mid_idx*arr_str);
            if (BOOL_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_bool(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_bool last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_bool *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_bool key_val = *(const npy_bool *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BOOL_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_bool mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_bool *)(arr + sort_idx*arr_str);

            if (BOOL_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define BYTE_LTE(a, b) (!BYTE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_byte(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_byte last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_byte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_byte key_val = *(const npy_byte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_byte mid_val = *(const npy_byte *)(arr + mid_idx*arr_str);
            if (BYTE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_byte(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_byte last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_byte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_byte key_val = *(const npy_byte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_byte mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_byte *)(arr + sort_idx*arr_str);

            if (BYTE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_byte(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_byte last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_byte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_byte key_val = *(const npy_byte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_byte mid_val = *(const npy_byte *)(arr + mid_idx*arr_str);
            if (BYTE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_byte(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_byte last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_byte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_byte key_val = *(const npy_byte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (BYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_byte mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_byte *)(arr + sort_idx*arr_str);

            if (BYTE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define UBYTE_LTE(a, b) (!UBYTE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_ubyte(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ubyte last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ubyte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ubyte key_val = *(const npy_ubyte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UBYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ubyte mid_val = *(const npy_ubyte *)(arr + mid_idx*arr_str);
            if (UBYTE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_ubyte(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ubyte last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ubyte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ubyte key_val = *(const npy_ubyte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UBYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ubyte mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ubyte *)(arr + sort_idx*arr_str);

            if (UBYTE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_ubyte(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ubyte last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ubyte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ubyte key_val = *(const npy_ubyte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UBYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ubyte mid_val = *(const npy_ubyte *)(arr + mid_idx*arr_str);
            if (UBYTE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_ubyte(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ubyte last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ubyte *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ubyte key_val = *(const npy_ubyte *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UBYTE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ubyte mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ubyte *)(arr + sort_idx*arr_str);

            if (UBYTE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define SHORT_LTE(a, b) (!SHORT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_short(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_short last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_short *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_short key_val = *(const npy_short *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (SHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_short mid_val = *(const npy_short *)(arr + mid_idx*arr_str);
            if (SHORT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_short(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_short last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_short *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_short key_val = *(const npy_short *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (SHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_short mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_short *)(arr + sort_idx*arr_str);

            if (SHORT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_short(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_short last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_short *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_short key_val = *(const npy_short *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (SHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_short mid_val = *(const npy_short *)(arr + mid_idx*arr_str);
            if (SHORT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_short(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_short last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_short *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_short key_val = *(const npy_short *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (SHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_short mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_short *)(arr + sort_idx*arr_str);

            if (SHORT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define USHORT_LTE(a, b) (!USHORT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_ushort(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (USHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ushort mid_val = *(const npy_ushort *)(arr + mid_idx*arr_str);
            if (USHORT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_ushort(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (USHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ushort mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ushort *)(arr + sort_idx*arr_str);

            if (USHORT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_ushort(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (USHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ushort mid_val = *(const npy_ushort *)(arr + mid_idx*arr_str);
            if (USHORT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_ushort(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (USHORT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ushort mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ushort *)(arr + sort_idx*arr_str);

            if (USHORT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define INT_LTE(a, b) (!INT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_int(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_int last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_int *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_int key_val = *(const npy_int *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (INT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_int mid_val = *(const npy_int *)(arr + mid_idx*arr_str);
            if (INT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_int(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_int last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_int *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_int key_val = *(const npy_int *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (INT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_int mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_int *)(arr + sort_idx*arr_str);

            if (INT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_int(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_int last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_int *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_int key_val = *(const npy_int *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (INT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_int mid_val = *(const npy_int *)(arr + mid_idx*arr_str);
            if (INT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_int(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_int last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_int *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_int key_val = *(const npy_int *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (INT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_int mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_int *)(arr + sort_idx*arr_str);

            if (INT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define UINT_LTE(a, b) (!UINT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_uint(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_uint last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_uint *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_uint key_val = *(const npy_uint *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UINT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_uint mid_val = *(const npy_uint *)(arr + mid_idx*arr_str);
            if (UINT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_uint(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_uint last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_uint *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_uint key_val = *(const npy_uint *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UINT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_uint mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_uint *)(arr + sort_idx*arr_str);

            if (UINT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_uint(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_uint last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_uint *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_uint key_val = *(const npy_uint *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UINT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_uint mid_val = *(const npy_uint *)(arr + mid_idx*arr_str);
            if (UINT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_uint(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_uint last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_uint *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_uint key_val = *(const npy_uint *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (UINT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_uint mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_uint *)(arr + sort_idx*arr_str);

            if (UINT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define LONG_LTE(a, b) (!LONG_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_long(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_long last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_long *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_long key_val = *(const npy_long *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_long mid_val = *(const npy_long *)(arr + mid_idx*arr_str);
            if (LONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_long(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_long last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_long *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_long key_val = *(const npy_long *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_long mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_long *)(arr + sort_idx*arr_str);

            if (LONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_long(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_long last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_long *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_long key_val = *(const npy_long *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_long mid_val = *(const npy_long *)(arr + mid_idx*arr_str);
            if (LONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_long(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_long last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_long *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_long key_val = *(const npy_long *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_long mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_long *)(arr + sort_idx*arr_str);

            if (LONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define ULONG_LTE(a, b) (!ULONG_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_ulong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ulong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulong key_val = *(const npy_ulong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ulong mid_val = *(const npy_ulong *)(arr + mid_idx*arr_str);
            if (ULONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_ulong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ulong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulong key_val = *(const npy_ulong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ulong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ulong *)(arr + sort_idx*arr_str);

            if (ULONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_ulong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ulong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulong key_val = *(const npy_ulong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ulong mid_val = *(const npy_ulong *)(arr + mid_idx*arr_str);
            if (ULONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_ulong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ulong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulong key_val = *(const npy_ulong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ulong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ulong *)(arr + sort_idx*arr_str);

            if (ULONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define LONGLONG_LTE(a, b) (!LONGLONG_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_longlong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longlong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_longlong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longlong key_val = *(const npy_longlong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_longlong mid_val = *(const npy_longlong *)(arr + mid_idx*arr_str);
            if (LONGLONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_longlong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longlong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_longlong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longlong key_val = *(const npy_longlong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_longlong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_longlong *)(arr + sort_idx*arr_str);

            if (LONGLONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_longlong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longlong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_longlong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longlong key_val = *(const npy_longlong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_longlong mid_val = *(const npy_longlong *)(arr + mid_idx*arr_str);
            if (LONGLONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_longlong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longlong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_longlong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longlong key_val = *(const npy_longlong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_longlong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_longlong *)(arr + sort_idx*arr_str);

            if (LONGLONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define ULONGLONG_LTE(a, b) (!ULONGLONG_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_ulonglong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulonglong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ulonglong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulonglong key_val = *(const npy_ulonglong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ulonglong mid_val = *(const npy_ulonglong *)(arr + mid_idx*arr_str);
            if (ULONGLONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_ulonglong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulonglong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ulonglong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulonglong key_val = *(const npy_ulonglong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ulonglong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ulonglong *)(arr + sort_idx*arr_str);

            if (ULONGLONG_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_ulonglong(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulonglong last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ulonglong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulonglong key_val = *(const npy_ulonglong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ulonglong mid_val = *(const npy_ulonglong *)(arr + mid_idx*arr_str);
            if (ULONGLONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_ulonglong(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ulonglong last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ulonglong *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ulonglong key_val = *(const npy_ulonglong *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (ULONGLONG_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ulonglong mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ulonglong *)(arr + sort_idx*arr_str);

            if (ULONGLONG_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define HALF_LTE(a, b) (!HALF_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_half(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (HALF_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ushort mid_val = *(const npy_ushort *)(arr + mid_idx*arr_str);
            if (HALF_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_half(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (HALF_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ushort mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ushort *)(arr + sort_idx*arr_str);

            if (HALF_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_half(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (HALF_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_ushort mid_val = *(const npy_ushort *)(arr + mid_idx*arr_str);
            if (HALF_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_half(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_ushort last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_ushort *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_ushort key_val = *(const npy_ushort *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (HALF_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_ushort mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_ushort *)(arr + sort_idx*arr_str);

            if (HALF_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define FLOAT_LTE(a, b) (!FLOAT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_float(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_float last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_float *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_float key_val = *(const npy_float *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (FLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_float mid_val = *(const npy_float *)(arr + mid_idx*arr_str);
            if (FLOAT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_float(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_float last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_float *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_float key_val = *(const npy_float *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (FLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_float mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_float *)(arr + sort_idx*arr_str);

            if (FLOAT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_float(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_float last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_float *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_float key_val = *(const npy_float *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (FLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_float mid_val = *(const npy_float *)(arr + mid_idx*arr_str);
            if (FLOAT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_float(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_float last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_float *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_float key_val = *(const npy_float *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (FLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_float mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_float *)(arr + sort_idx*arr_str);

            if (FLOAT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define DOUBLE_LTE(a, b) (!DOUBLE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_double(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_double last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_double *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_double key_val = *(const npy_double *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_double mid_val = *(const npy_double *)(arr + mid_idx*arr_str);
            if (DOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_double(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_double last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_double *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_double key_val = *(const npy_double *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_double mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_double *)(arr + sort_idx*arr_str);

            if (DOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_double(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_double last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_double *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_double key_val = *(const npy_double *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_double mid_val = *(const npy_double *)(arr + mid_idx*arr_str);
            if (DOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_double(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_double last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_double *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_double key_val = *(const npy_double *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_double mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_double *)(arr + sort_idx*arr_str);

            if (DOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define LONGDOUBLE_LTE(a, b) (!LONGDOUBLE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_longdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_longdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longdouble key_val = *(const npy_longdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_longdouble mid_val = *(const npy_longdouble *)(arr + mid_idx*arr_str);
            if (LONGDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_longdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_longdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longdouble key_val = *(const npy_longdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_longdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_longdouble *)(arr + sort_idx*arr_str);

            if (LONGDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_longdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_longdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longdouble key_val = *(const npy_longdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_longdouble mid_val = *(const npy_longdouble *)(arr + mid_idx*arr_str);
            if (LONGDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_longdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_longdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_longdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_longdouble key_val = *(const npy_longdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (LONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_longdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_longdouble *)(arr + sort_idx*arr_str);

            if (LONGDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define CFLOAT_LTE(a, b) (!CFLOAT_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_cfloat(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cfloat last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_cfloat *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cfloat key_val = *(const npy_cfloat *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CFLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_cfloat mid_val = *(const npy_cfloat *)(arr + mid_idx*arr_str);
            if (CFLOAT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_cfloat(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cfloat last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_cfloat *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cfloat key_val = *(const npy_cfloat *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CFLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_cfloat mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_cfloat *)(arr + sort_idx*arr_str);

            if (CFLOAT_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_cfloat(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cfloat last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_cfloat *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cfloat key_val = *(const npy_cfloat *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CFLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_cfloat mid_val = *(const npy_cfloat *)(arr + mid_idx*arr_str);
            if (CFLOAT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_cfloat(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cfloat last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_cfloat *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cfloat key_val = *(const npy_cfloat *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CFLOAT_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_cfloat mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_cfloat *)(arr + sort_idx*arr_str);

            if (CFLOAT_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define CDOUBLE_LTE(a, b) (!CDOUBLE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_cdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_cdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cdouble key_val = *(const npy_cdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_cdouble mid_val = *(const npy_cdouble *)(arr + mid_idx*arr_str);
            if (CDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_cdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_cdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cdouble key_val = *(const npy_cdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_cdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_cdouble *)(arr + sort_idx*arr_str);

            if (CDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_cdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_cdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cdouble key_val = *(const npy_cdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_cdouble mid_val = *(const npy_cdouble *)(arr + mid_idx*arr_str);
            if (CDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_cdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_cdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_cdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_cdouble key_val = *(const npy_cdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_cdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_cdouble *)(arr + sort_idx*arr_str);

            if (CDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define CLONGDOUBLE_LTE(a, b) (!CLONGDOUBLE_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_clongdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_clongdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_clongdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_clongdouble key_val = *(const npy_clongdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CLONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_clongdouble mid_val = *(const npy_clongdouble *)(arr + mid_idx*arr_str);
            if (CLONGDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_clongdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_clongdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_clongdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_clongdouble key_val = *(const npy_clongdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CLONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_clongdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_clongdouble *)(arr + sort_idx*arr_str);

            if (CLONGDOUBLE_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_clongdouble(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_clongdouble last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_clongdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_clongdouble key_val = *(const npy_clongdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CLONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_clongdouble mid_val = *(const npy_clongdouble *)(arr + mid_idx*arr_str);
            if (CLONGDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_clongdouble(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_clongdouble last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_clongdouble *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_clongdouble key_val = *(const npy_clongdouble *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (CLONGDOUBLE_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_clongdouble mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_clongdouble *)(arr + sort_idx*arr_str);

            if (CLONGDOUBLE_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define DATETIME_LTE(a, b) (!DATETIME_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_datetime(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_datetime last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_datetime *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_datetime key_val = *(const npy_datetime *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DATETIME_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_datetime mid_val = *(const npy_datetime *)(arr + mid_idx*arr_str);
            if (DATETIME_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_datetime(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_datetime last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_datetime *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_datetime key_val = *(const npy_datetime *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DATETIME_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_datetime mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_datetime *)(arr + sort_idx*arr_str);

            if (DATETIME_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_datetime(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_datetime last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_datetime *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_datetime key_val = *(const npy_datetime *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DATETIME_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_datetime mid_val = *(const npy_datetime *)(arr + mid_idx*arr_str);
            if (DATETIME_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_datetime(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_datetime last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_datetime *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_datetime key_val = *(const npy_datetime *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (DATETIME_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_datetime mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_datetime *)(arr + sort_idx*arr_str);

            if (DATETIME_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



#line 29

#define TIMEDELTA_LTE(a, b) (!TIMEDELTA_LT((b), (a)))

#line 37

NPY_NO_EXPORT void
binsearch_left_timedelta(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_timedelta last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_timedelta *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_timedelta key_val = *(const npy_timedelta *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (TIMEDELTA_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_timedelta mid_val = *(const npy_timedelta *)(arr + mid_idx*arr_str);
            if (TIMEDELTA_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_left_timedelta(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_timedelta last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_timedelta *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_timedelta key_val = *(const npy_timedelta *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (TIMEDELTA_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_timedelta mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_timedelta *)(arr + sort_idx*arr_str);

            if (TIMEDELTA_LT(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 37

NPY_NO_EXPORT void
binsearch_right_timedelta(const char *arr, const char *key, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                        PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_timedelta last_key_val;

    if (key_len == 0) {
        return;
    }
    last_key_val = *(const npy_timedelta *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_timedelta key_val = *(const npy_timedelta *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (TIMEDELTA_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_timedelta mid_val = *(const npy_timedelta *)(arr + mid_idx*arr_str);
            if (TIMEDELTA_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
argbinsearch_right_timedelta(const char *arr, const char *key,
                           const char *sort, char *ret,
                           npy_intp arr_len, npy_intp key_len,
                           npy_intp arr_str, npy_intp key_str,
                           npy_intp sort_str, npy_intp ret_str,
                           PyArrayObject *NOT_USED)
{
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    npy_timedelta last_key_val;

    if (key_len == 0) {
        return 0;
    }
    last_key_val = *(const npy_timedelta *)key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        const npy_timedelta key_val = *(const npy_timedelta *)key;
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (TIMEDELTA_LT(last_key_val, key_val)) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key_val = key_val;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            npy_timedelta mid_val;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            mid_val = *(const npy_timedelta *)(arr + sort_idx*arr_str);

            if (TIMEDELTA_LTE(mid_val, key_val)) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}




/*
 *****************************************************************************
 **                             GENERIC SEARCH                              **
 *****************************************************************************
 */

 #line 155

NPY_NO_EXPORT void
npy_binsearch_left(const char *arr, const char *key, char *ret,
                     npy_intp arr_len, npy_intp key_len,
                     npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                     PyArrayObject *cmp)
{
    PyArray_CompareFunc *compare = PyArray_DESCR(cmp)->f->compare;
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    const char *last_key = key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (compare(last_key, key, cmp) < 0) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key = key;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const char *arr_ptr = arr + mid_idx*arr_str;

            if (compare(arr_ptr, key, cmp) < 0) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
npy_argbinsearch_left(const char *arr, const char *key,
                        const char *sort, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str,
                        npy_intp sort_str, npy_intp ret_str,
                        PyArrayObject *cmp)
{
    PyArray_CompareFunc *compare = PyArray_DESCR(cmp)->f->compare;
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    const char *last_key = key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (compare(last_key, key, cmp) < 0) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key = key;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            const char *arr_ptr;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            arr_ptr = arr + sort_idx*arr_str;

            if (compare(arr_ptr, key, cmp) < 0) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}


#line 155

NPY_NO_EXPORT void
npy_binsearch_right(const char *arr, const char *key, char *ret,
                     npy_intp arr_len, npy_intp key_len,
                     npy_intp arr_str, npy_intp key_str, npy_intp ret_str,
                     PyArrayObject *cmp)
{
    PyArray_CompareFunc *compare = PyArray_DESCR(cmp)->f->compare;
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    const char *last_key = key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (compare(last_key, key, cmp) <= 0) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key = key;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const char *arr_ptr = arr + mid_idx*arr_str;

            if (compare(arr_ptr, key, cmp) <= 0) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
}

NPY_NO_EXPORT int
npy_argbinsearch_right(const char *arr, const char *key,
                        const char *sort, char *ret,
                        npy_intp arr_len, npy_intp key_len,
                        npy_intp arr_str, npy_intp key_str,
                        npy_intp sort_str, npy_intp ret_str,
                        PyArrayObject *cmp)
{
    PyArray_CompareFunc *compare = PyArray_DESCR(cmp)->f->compare;
    npy_intp min_idx = 0;
    npy_intp max_idx = arr_len;
    const char *last_key = key;

    for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
        /*
         * Updating only one of the indices based on the previous key
         * gives the search a big boost when keys are sorted, but slightly
         * slows down things for purely random ones.
         */
        if (compare(last_key, key, cmp) <= 0) {
            max_idx = arr_len;
        }
        else {
            min_idx = 0;
            max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
        }

        last_key = key;

        while (min_idx < max_idx) {
            const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
            const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx*sort_str);
            const char *arr_ptr;

            if (sort_idx < 0 || sort_idx >= arr_len) {
                return -1;
            }

            arr_ptr = arr + sort_idx*arr_str;

            if (compare(arr_ptr, key, cmp) <= 0) {
                min_idx = mid_idx + 1;
            }
            else {
                max_idx = mid_idx;
            }
        }
        *(npy_intp *)ret = min_idx;
    }
    return 0;
}



