12e5b6d6dSopenharmony_ci// © 2016 and later: Unicode, Inc. and others.
22e5b6d6dSopenharmony_ci// License & terms of use: http://www.unicode.org/copyright.html
32e5b6d6dSopenharmony_ci/*
42e5b6d6dSopenharmony_ci*******************************************************************************
52e5b6d6dSopenharmony_ci*
62e5b6d6dSopenharmony_ci*   Copyright (C) 2003-2013, International Business Machines
72e5b6d6dSopenharmony_ci*   Corporation and others.  All Rights Reserved.
82e5b6d6dSopenharmony_ci*
92e5b6d6dSopenharmony_ci*******************************************************************************
102e5b6d6dSopenharmony_ci*   file name:  uarrsort.c
112e5b6d6dSopenharmony_ci*   encoding:   UTF-8
122e5b6d6dSopenharmony_ci*   tab size:   8 (not used)
132e5b6d6dSopenharmony_ci*   indentation:4
142e5b6d6dSopenharmony_ci*
152e5b6d6dSopenharmony_ci*   created on: 2003aug04
162e5b6d6dSopenharmony_ci*   created by: Markus W. Scherer
172e5b6d6dSopenharmony_ci*
182e5b6d6dSopenharmony_ci*   Internal function for sorting arrays.
192e5b6d6dSopenharmony_ci*/
202e5b6d6dSopenharmony_ci
212e5b6d6dSopenharmony_ci#include <cstddef>
222e5b6d6dSopenharmony_ci
232e5b6d6dSopenharmony_ci#include "unicode/utypes.h"
242e5b6d6dSopenharmony_ci#include "cmemory.h"
252e5b6d6dSopenharmony_ci#include "uarrsort.h"
262e5b6d6dSopenharmony_ci
272e5b6d6dSopenharmony_cienum {
282e5b6d6dSopenharmony_ci    /**
292e5b6d6dSopenharmony_ci     * "from Knuth"
302e5b6d6dSopenharmony_ci     *
312e5b6d6dSopenharmony_ci     * A binary search over 8 items performs 4 comparisons:
322e5b6d6dSopenharmony_ci     * log2(8)=3 to subdivide, +1 to check for equality.
332e5b6d6dSopenharmony_ci     * A linear search over 8 items on average also performs 4 comparisons.
342e5b6d6dSopenharmony_ci     */
352e5b6d6dSopenharmony_ci    MIN_QSORT=9,
362e5b6d6dSopenharmony_ci    STACK_ITEM_SIZE=200
372e5b6d6dSopenharmony_ci};
382e5b6d6dSopenharmony_ci
392e5b6d6dSopenharmony_cistatic constexpr int32_t sizeInMaxAlignTs(int32_t sizeInBytes) {
402e5b6d6dSopenharmony_ci    return (sizeInBytes + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t);
412e5b6d6dSopenharmony_ci}
422e5b6d6dSopenharmony_ci
432e5b6d6dSopenharmony_ci/* UComparator convenience implementations ---------------------------------- */
442e5b6d6dSopenharmony_ci
452e5b6d6dSopenharmony_ciU_CAPI int32_t U_EXPORT2
462e5b6d6dSopenharmony_ciuprv_uint16Comparator(const void *context, const void *left, const void *right) {
472e5b6d6dSopenharmony_ci    (void)context;
482e5b6d6dSopenharmony_ci    return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right;
492e5b6d6dSopenharmony_ci}
502e5b6d6dSopenharmony_ci
512e5b6d6dSopenharmony_ciU_CAPI int32_t U_EXPORT2
522e5b6d6dSopenharmony_ciuprv_int32Comparator(const void *context, const void *left, const void *right) {
532e5b6d6dSopenharmony_ci    (void)context;
542e5b6d6dSopenharmony_ci    return *(const int32_t *)left - *(const int32_t *)right;
552e5b6d6dSopenharmony_ci}
562e5b6d6dSopenharmony_ci
572e5b6d6dSopenharmony_ciU_CAPI int32_t U_EXPORT2
582e5b6d6dSopenharmony_ciuprv_uint32Comparator(const void *context, const void *left, const void *right) {
592e5b6d6dSopenharmony_ci    (void)context;
602e5b6d6dSopenharmony_ci    uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right;
612e5b6d6dSopenharmony_ci
622e5b6d6dSopenharmony_ci    /* compare directly because (l-r) would overflow the int32_t result */
632e5b6d6dSopenharmony_ci    if(l<r) {
642e5b6d6dSopenharmony_ci        return -1;
652e5b6d6dSopenharmony_ci    } else if(l==r) {
662e5b6d6dSopenharmony_ci        return 0;
672e5b6d6dSopenharmony_ci    } else /* l>r */ {
682e5b6d6dSopenharmony_ci        return 1;
692e5b6d6dSopenharmony_ci    }
702e5b6d6dSopenharmony_ci}
712e5b6d6dSopenharmony_ci
722e5b6d6dSopenharmony_ci/* Insertion sort using binary search --------------------------------------- */
732e5b6d6dSopenharmony_ci
742e5b6d6dSopenharmony_ciU_CAPI int32_t U_EXPORT2
752e5b6d6dSopenharmony_ciuprv_stableBinarySearch(char *array, int32_t limit, void *item, int32_t itemSize,
762e5b6d6dSopenharmony_ci                        UComparator *cmp, const void *context) {
772e5b6d6dSopenharmony_ci    int32_t start=0;
782e5b6d6dSopenharmony_ci    UBool found=false;
792e5b6d6dSopenharmony_ci
802e5b6d6dSopenharmony_ci    /* Binary search until we get down to a tiny sub-array. */
812e5b6d6dSopenharmony_ci    while((limit-start)>=MIN_QSORT) {
822e5b6d6dSopenharmony_ci        int32_t i=(start+limit)/2;
832e5b6d6dSopenharmony_ci        int32_t diff=cmp(context, item, array+i*itemSize);
842e5b6d6dSopenharmony_ci        if(diff==0) {
852e5b6d6dSopenharmony_ci            /*
862e5b6d6dSopenharmony_ci             * Found the item. We look for the *last* occurrence of such
872e5b6d6dSopenharmony_ci             * an item, for stable sorting.
882e5b6d6dSopenharmony_ci             * If we knew that there will be only few equal items,
892e5b6d6dSopenharmony_ci             * we could break now and enter the linear search.
902e5b6d6dSopenharmony_ci             * However, if there are many equal items, then it should be
912e5b6d6dSopenharmony_ci             * faster to continue with the binary search.
922e5b6d6dSopenharmony_ci             * It seems likely that we either have all unique items
932e5b6d6dSopenharmony_ci             * (where found will never become true in the insertion sort)
942e5b6d6dSopenharmony_ci             * or potentially many duplicates.
952e5b6d6dSopenharmony_ci             */
962e5b6d6dSopenharmony_ci            found=true;
972e5b6d6dSopenharmony_ci            start=i+1;
982e5b6d6dSopenharmony_ci        } else if(diff<0) {
992e5b6d6dSopenharmony_ci            limit=i;
1002e5b6d6dSopenharmony_ci        } else {
1012e5b6d6dSopenharmony_ci            start=i;
1022e5b6d6dSopenharmony_ci        }
1032e5b6d6dSopenharmony_ci    }
1042e5b6d6dSopenharmony_ci
1052e5b6d6dSopenharmony_ci    /* Linear search over the remaining tiny sub-array. */
1062e5b6d6dSopenharmony_ci    while(start<limit) {
1072e5b6d6dSopenharmony_ci        int32_t diff=cmp(context, item, array+start*itemSize);
1082e5b6d6dSopenharmony_ci        if(diff==0) {
1092e5b6d6dSopenharmony_ci            found=true;
1102e5b6d6dSopenharmony_ci        } else if(diff<0) {
1112e5b6d6dSopenharmony_ci            break;
1122e5b6d6dSopenharmony_ci        }
1132e5b6d6dSopenharmony_ci        ++start;
1142e5b6d6dSopenharmony_ci    }
1152e5b6d6dSopenharmony_ci    return found ? (start-1) : ~start;
1162e5b6d6dSopenharmony_ci}
1172e5b6d6dSopenharmony_ci
1182e5b6d6dSopenharmony_cistatic void
1192e5b6d6dSopenharmony_cidoInsertionSort(char *array, int32_t length, int32_t itemSize,
1202e5b6d6dSopenharmony_ci                UComparator *cmp, const void *context, void *pv) {
1212e5b6d6dSopenharmony_ci    int32_t j;
1222e5b6d6dSopenharmony_ci
1232e5b6d6dSopenharmony_ci    for(j=1; j<length; ++j) {
1242e5b6d6dSopenharmony_ci        char *item=array+j*itemSize;
1252e5b6d6dSopenharmony_ci        int32_t insertionPoint=uprv_stableBinarySearch(array, j, item, itemSize, cmp, context);
1262e5b6d6dSopenharmony_ci        if(insertionPoint<0) {
1272e5b6d6dSopenharmony_ci            insertionPoint=~insertionPoint;
1282e5b6d6dSopenharmony_ci        } else {
1292e5b6d6dSopenharmony_ci            ++insertionPoint;  /* one past the last equal item */
1302e5b6d6dSopenharmony_ci        }
1312e5b6d6dSopenharmony_ci        if(insertionPoint<j) {
1322e5b6d6dSopenharmony_ci            char *dest=array+insertionPoint*itemSize;
1332e5b6d6dSopenharmony_ci            uprv_memcpy(pv, item, itemSize);  /* v=array[j] */
1342e5b6d6dSopenharmony_ci            uprv_memmove(dest+itemSize, dest, (j-insertionPoint)*(size_t)itemSize);
1352e5b6d6dSopenharmony_ci            uprv_memcpy(dest, pv, itemSize);  /* array[insertionPoint]=v */
1362e5b6d6dSopenharmony_ci        }
1372e5b6d6dSopenharmony_ci    }
1382e5b6d6dSopenharmony_ci}
1392e5b6d6dSopenharmony_ci
1402e5b6d6dSopenharmony_cistatic void
1412e5b6d6dSopenharmony_ciinsertionSort(char *array, int32_t length, int32_t itemSize,
1422e5b6d6dSopenharmony_ci              UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
1432e5b6d6dSopenharmony_ci
1442e5b6d6dSopenharmony_ci    icu::MaybeStackArray<std::max_align_t, sizeInMaxAlignTs(STACK_ITEM_SIZE)> v;
1452e5b6d6dSopenharmony_ci    if (sizeInMaxAlignTs(itemSize) > v.getCapacity() &&
1462e5b6d6dSopenharmony_ci            v.resize(sizeInMaxAlignTs(itemSize)) == nullptr) {
1472e5b6d6dSopenharmony_ci        *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
1482e5b6d6dSopenharmony_ci        return;
1492e5b6d6dSopenharmony_ci    }
1502e5b6d6dSopenharmony_ci
1512e5b6d6dSopenharmony_ci    doInsertionSort(array, length, itemSize, cmp, context, v.getAlias());
1522e5b6d6dSopenharmony_ci}
1532e5b6d6dSopenharmony_ci
1542e5b6d6dSopenharmony_ci/* QuickSort ---------------------------------------------------------------- */
1552e5b6d6dSopenharmony_ci
1562e5b6d6dSopenharmony_ci/*
1572e5b6d6dSopenharmony_ci * This implementation is semi-recursive:
1582e5b6d6dSopenharmony_ci * It recurses for the smaller sub-array to shorten the recursion depth,
1592e5b6d6dSopenharmony_ci * and loops for the larger sub-array.
1602e5b6d6dSopenharmony_ci *
1612e5b6d6dSopenharmony_ci * Loosely after QuickSort algorithms in
1622e5b6d6dSopenharmony_ci * Niklaus Wirth
1632e5b6d6dSopenharmony_ci * Algorithmen und Datenstrukturen mit Modula-2
1642e5b6d6dSopenharmony_ci * B.G. Teubner Stuttgart
1652e5b6d6dSopenharmony_ci * 4. Auflage 1986
1662e5b6d6dSopenharmony_ci * ISBN 3-519-02260-5
1672e5b6d6dSopenharmony_ci */
1682e5b6d6dSopenharmony_cistatic void
1692e5b6d6dSopenharmony_cisubQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize,
1702e5b6d6dSopenharmony_ci             UComparator *cmp, const void *context,
1712e5b6d6dSopenharmony_ci             void *px, void *pw) {
1722e5b6d6dSopenharmony_ci    int32_t left, right;
1732e5b6d6dSopenharmony_ci
1742e5b6d6dSopenharmony_ci    /* start and left are inclusive, limit and right are exclusive */
1752e5b6d6dSopenharmony_ci    do {
1762e5b6d6dSopenharmony_ci        if((start+MIN_QSORT)>=limit) {
1772e5b6d6dSopenharmony_ci            doInsertionSort(array+start*itemSize, limit-start, itemSize, cmp, context, px);
1782e5b6d6dSopenharmony_ci            break;
1792e5b6d6dSopenharmony_ci        }
1802e5b6d6dSopenharmony_ci
1812e5b6d6dSopenharmony_ci        left=start;
1822e5b6d6dSopenharmony_ci        right=limit;
1832e5b6d6dSopenharmony_ci
1842e5b6d6dSopenharmony_ci        /* x=array[middle] */
1852e5b6d6dSopenharmony_ci        uprv_memcpy(px, array+(size_t)((start+limit)/2)*itemSize, itemSize);
1862e5b6d6dSopenharmony_ci
1872e5b6d6dSopenharmony_ci        do {
1882e5b6d6dSopenharmony_ci            while(/* array[left]<x */
1892e5b6d6dSopenharmony_ci                  cmp(context, array+left*itemSize, px)<0
1902e5b6d6dSopenharmony_ci            ) {
1912e5b6d6dSopenharmony_ci                ++left;
1922e5b6d6dSopenharmony_ci            }
1932e5b6d6dSopenharmony_ci            while(/* x<array[right-1] */
1942e5b6d6dSopenharmony_ci                  cmp(context, px, array+(right-1)*itemSize)<0
1952e5b6d6dSopenharmony_ci            ) {
1962e5b6d6dSopenharmony_ci                --right;
1972e5b6d6dSopenharmony_ci            }
1982e5b6d6dSopenharmony_ci
1992e5b6d6dSopenharmony_ci            /* swap array[left] and array[right-1] via w; ++left; --right */
2002e5b6d6dSopenharmony_ci            if(left<right) {
2012e5b6d6dSopenharmony_ci                --right;
2022e5b6d6dSopenharmony_ci
2032e5b6d6dSopenharmony_ci                if(left<right) {
2042e5b6d6dSopenharmony_ci                    uprv_memcpy(pw, array+(size_t)left*itemSize, itemSize);
2052e5b6d6dSopenharmony_ci                    uprv_memcpy(array+(size_t)left*itemSize, array+(size_t)right*itemSize, itemSize);
2062e5b6d6dSopenharmony_ci                    uprv_memcpy(array+(size_t)right*itemSize, pw, itemSize);
2072e5b6d6dSopenharmony_ci                }
2082e5b6d6dSopenharmony_ci
2092e5b6d6dSopenharmony_ci                ++left;
2102e5b6d6dSopenharmony_ci            }
2112e5b6d6dSopenharmony_ci        } while(left<right);
2122e5b6d6dSopenharmony_ci
2132e5b6d6dSopenharmony_ci        /* sort sub-arrays */
2142e5b6d6dSopenharmony_ci        if((right-start)<(limit-left)) {
2152e5b6d6dSopenharmony_ci            /* sort [start..right[ */
2162e5b6d6dSopenharmony_ci            if(start<(right-1)) {
2172e5b6d6dSopenharmony_ci                subQuickSort(array, start, right, itemSize, cmp, context, px, pw);
2182e5b6d6dSopenharmony_ci            }
2192e5b6d6dSopenharmony_ci
2202e5b6d6dSopenharmony_ci            /* sort [left..limit[ */
2212e5b6d6dSopenharmony_ci            start=left;
2222e5b6d6dSopenharmony_ci        } else {
2232e5b6d6dSopenharmony_ci            /* sort [left..limit[ */
2242e5b6d6dSopenharmony_ci            if(left<(limit-1)) {
2252e5b6d6dSopenharmony_ci                subQuickSort(array, left, limit, itemSize, cmp, context, px, pw);
2262e5b6d6dSopenharmony_ci            }
2272e5b6d6dSopenharmony_ci
2282e5b6d6dSopenharmony_ci            /* sort [start..right[ */
2292e5b6d6dSopenharmony_ci            limit=right;
2302e5b6d6dSopenharmony_ci        }
2312e5b6d6dSopenharmony_ci    } while(start<(limit-1));
2322e5b6d6dSopenharmony_ci}
2332e5b6d6dSopenharmony_ci
2342e5b6d6dSopenharmony_cistatic void
2352e5b6d6dSopenharmony_ciquickSort(char *array, int32_t length, int32_t itemSize,
2362e5b6d6dSopenharmony_ci            UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
2372e5b6d6dSopenharmony_ci    /* allocate two intermediate item variables (x and w) */
2382e5b6d6dSopenharmony_ci    icu::MaybeStackArray<std::max_align_t, sizeInMaxAlignTs(STACK_ITEM_SIZE) * 2> xw;
2392e5b6d6dSopenharmony_ci    if(sizeInMaxAlignTs(itemSize)*2 > xw.getCapacity() &&
2402e5b6d6dSopenharmony_ci            xw.resize(sizeInMaxAlignTs(itemSize) * 2) == nullptr) {
2412e5b6d6dSopenharmony_ci        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
2422e5b6d6dSopenharmony_ci        return;
2432e5b6d6dSopenharmony_ci    }
2442e5b6d6dSopenharmony_ci
2452e5b6d6dSopenharmony_ci    subQuickSort(array, 0, length, itemSize, cmp, context,
2462e5b6d6dSopenharmony_ci                 xw.getAlias(), xw.getAlias() + sizeInMaxAlignTs(itemSize));
2472e5b6d6dSopenharmony_ci}
2482e5b6d6dSopenharmony_ci
2492e5b6d6dSopenharmony_ci/* uprv_sortArray() API ----------------------------------------------------- */
2502e5b6d6dSopenharmony_ci
2512e5b6d6dSopenharmony_ci/*
2522e5b6d6dSopenharmony_ci * Check arguments, select an appropriate implementation,
2532e5b6d6dSopenharmony_ci * cast the array to char * so that array+i*itemSize works.
2542e5b6d6dSopenharmony_ci */
2552e5b6d6dSopenharmony_ciU_CAPI void U_EXPORT2
2562e5b6d6dSopenharmony_ciuprv_sortArray(void *array, int32_t length, int32_t itemSize,
2572e5b6d6dSopenharmony_ci               UComparator *cmp, const void *context,
2582e5b6d6dSopenharmony_ci               UBool sortStable, UErrorCode *pErrorCode) {
2592e5b6d6dSopenharmony_ci    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
2602e5b6d6dSopenharmony_ci        return;
2612e5b6d6dSopenharmony_ci    }
2622e5b6d6dSopenharmony_ci    if((length>0 && array==NULL) || length<0 || itemSize<=0 || cmp==NULL) {
2632e5b6d6dSopenharmony_ci        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
2642e5b6d6dSopenharmony_ci        return;
2652e5b6d6dSopenharmony_ci    }
2662e5b6d6dSopenharmony_ci
2672e5b6d6dSopenharmony_ci    if(length<=1) {
2682e5b6d6dSopenharmony_ci        return;
2692e5b6d6dSopenharmony_ci    } else if(length<MIN_QSORT || sortStable) {
2702e5b6d6dSopenharmony_ci        insertionSort((char *)array, length, itemSize, cmp, context, pErrorCode);
2712e5b6d6dSopenharmony_ci    } else {
2722e5b6d6dSopenharmony_ci        quickSort((char *)array, length, itemSize, cmp, context, pErrorCode);
2732e5b6d6dSopenharmony_ci    }
2742e5b6d6dSopenharmony_ci}
275