#if !defined(__BSEARCH__)
#define __BSEARCH__

// stealed from bsearch.c

#if !defined(FASTAPICALLCONVENTION)
  #if defined(_WINDOWS)
    #ifdef WIN32
      #define FASTAPICALLCONVENTION __fastcall
    #else // !WIN32
      #define FASTAPICALLCONVENTION PASCAL
    #endif // WIN32
  #else // !defined(_WINDOWS)
    #define FASTAPICALLCONVENTION
  #endif // defined(_WINDOWS)
#endif // !defined(TSORTCALL)

#define TBSEARCH_SAMEVALUE(TYPE,key,ptr,count) TBSEARCH<TYPE, TYPE, TBsearchSameDiff<TYPE,TYPE> >::bsearch(key,ptr,count)
#define TBSEARCH_SAMEVALUEUNSIGNED(TYPE,key,ptr,count) TBSEARCH<TYPE, TYPE, TBsearchSameDiffUnsigned<TYPE,TYPE> >::bsearch(key,ptr,count)
#define TBSEARCH_SAMEMEM(TYPE,pKey,ptr,count) TBSEARCH<const TYPE*, TYPE, TBsearchSameMEM<const TYPE*,TYPE> >::bsearch(pKey,ptr,count)
#define TBSEARCH_SAMEPTR(TYPE,pKey,ptr,count) TBSEARCH<const TYPE*, TYPE, TBsearchSameDiffUnsigned<const TYPE,TYPE> >::bsearch(pKey,ptr,count)

#define TBRANGE_SAMEVALUE(TYPE,key,ptr,count,bRange) TBRANGE<TYPE, TYPE, TBsearchSameDiff<TYPE,TYPE> >::GetIndexViaKey(key,ptr,count,bRange)
#define TBRANGE_SAMEVALUEUNSIGNED(TYPE,key,ptr,count,bRange) TBRANGE<TYPE, TYPE, TBsearchSameDiffUnsigned<TYPE,TYPE> >::GetIndexViaKey(key,ptr,count,bRange)
#define TBRANGE_SAMEMEM(TYPE,pKey,ptr,count,bRange) TBRANGE<const TYPE*, TYPE, TBsearchSameMEM<const TYPE*,TYPE> >::GetIndexViaKey(pKey,ptr,count,bRange)
#define TBRANGE_SAMEPTR(TYPE,pKey,ptr,count,bRange) TBRANGE<const TYPE*, TYPE, TBsearchSameDiffUnsigned<const TYPE,TYPE> >::GetIndexViaKey(pKey,ptr,count,bRange)

template <class KEY, class TYPE>
struct TBsearchSameDiff
{
    static int FASTAPICALLCONVENTION Compare(KEY key, const TYPE* pb)
    {
        return key - *pb;
    }
};

template <class KEY, class TYPE>
struct TBsearchSameDiffUnsigned
{
    static int FASTAPICALLCONVENTION Compare(KEY key, const TYPE* pb)
    {
        TYPE tmp = *pb;

        if (key != tmp) {
            if (key > tmp)
                return +1;
            return -1;
        }

        return 0;
    }
};

template <class KEY, class TYPE>
struct TBsearchSameMEM
{
    static int FASTAPICALLCONVENTION Compare(KEY key, const TYPE* pb)
    {
        return memcmp(key, pb, sizeof(TYPE));
    }
};

template <class KEY, class TYPE, class COMP>
struct TBSEARCH
{
public:
public:
    static TYPE* FASTAPICALLCONVENTION bsearch(KEY key, const TYPE* base, size_t num)
    {
    /***
    *TYPE *bsearch() - do a binary search on an array
    *
    *Purpose:
    *       Does a binary search of a sorted array for a key.
    *
    *Entry:
    *       const char *key    - key to search for
    *       const char *base   - base of sorted array to search
    *       unsigned int num   - number of elements in array
    *
    *Exit:
    *       if key is found:
    *               returns pointer to occurrence of key in array
    *       if key is not found:
    *               returns NULL
    *
    *Exceptions:
    *
    *******************************************************************************/

        BYTE *lo = (BYTE *)base;
        BYTE *hi = (BYTE *)base + (num - 1) * sizeof(TYPE);
        BYTE *mid;
        size_t half;
        int result;

        while(lo <= hi) {
            if ((half = num / 2) != 0) {
                mid = lo + (num & 1 ? half : (half - 1)) * sizeof(TYPE);
                if ((result = COMP::Compare(key, (const TYPE*)mid)) == 0)
                    return (TYPE*)mid;
                else if (result < 0) {
                    hi = mid - sizeof(TYPE);
                    num = num & 1 ? half : half-1;
                } else {
                    lo = mid + sizeof(TYPE);
                    num = half;
                }
            } else if (num)
                return (TYPE*)(COMP::Compare(key, (const TYPE*)lo) ? NULL : lo);
            else
                break;
        }

        return NULL;
    }
};

template <class KEY, class TYPE, class COMP>
struct TBRANGE
{
public:
public:
    static int FASTAPICALLCONVENTION GetIndexViaKey(KEY key, const TYPE* base, int nSize, BOOL bRange = FALSE)
    {
        if (nSize > 0) {
            int nNum = nSize;
            const TYPE* pStart = &base[0];
            const TYPE* pLo = pStart;
            const TYPE* pHi = &pStart[nNum - 1];
            const TYPE* pMd;
            int nHalf;
            int nIndex;
            int nDiffer;

            while(pLo <= pHi) {
                nHalf = nNum / 2;
                if (nHalf) {
                    int nNum2 = nNum & 1 ? nHalf : (nHalf - 1);
                    pMd = &pLo[nNum2];
                    nDiffer = COMP::Compare(key, pMd);
                    if (nDiffer) {
                        if (nDiffer > 0) {
                            pLo = &pMd[1];
                            nNum = nHalf;
                        } else {
                            nNum = nNum2;
                            if (!nNum) {
                                if (bRange) {
							        nIndex = pLo - pStart;
                                    goto check_1below;
						        }
                                break;
                            }
                            pHi = &pMd[-1];
                        }
                    } else {
                        nIndex = pMd - pStart;
                        pLo = pMd;
                        goto check_before;
                    }
                } else {
                    ASSERT(nNum == 1);
                    nDiffer = COMP::Compare(key, pLo);
                    nIndex = pLo - pStart;
                    if (bRange) {
                        if (nDiffer < 0) {
check_1below:
                            if (0 < nIndex) {
                                pLo--;
                                if (COMP::Compare(key, pLo) >= 0) {
                                    ASSERT(COMP::Compare(key, pLo) != 0);
                                    nIndex--;
                                    goto check_before;
                                }
                            }
                            break;
                        }
                    } else {
                        if (nDiffer != 0)
                            break;
                    }
check_before:
                    return nIndex;
                }
            }
        }

        return -1;
    }
};

#endif // !defined(__BSEARCH__)
