From a2dff8e0a0ecf705b264062656b3ddc1c9bd4569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Sat, 10 Sep 2022 08:34:59 -0700 Subject: [PATCH] Added bx::unique. --- include/bx/inline/sort.inl | 48 ++++++++++++------- include/bx/sort.h | 96 ++++++++++++++++++++++++++++---------- src/sort.cpp | 28 ++++++++++- tests/sort_test.cpp | 31 ++++++++++-- 4 files changed, 155 insertions(+), 48 deletions(-) diff --git a/include/bx/inline/sort.inl b/include/bx/inline/sort.inl index 9fccb8c..ed72c3a 100644 --- a/include/bx/inline/sort.inl +++ b/include/bx/inline/sort.inl @@ -36,63 +36,77 @@ namespace bx } template - void quickSort(Ty* _data, uint32_t _num, const ComparisonFn _fn) + inline void quickSort(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { - BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Sort element type must be trivially move assignable"); - quickSort( (void*)_data, _num, sizeof(Ty), _fn); - } - - template - void quickSort(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) - { - BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Sort element type must be trivially move assignable"); + BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Element type must be trivially move assignable"); quickSort(_data, _num, _stride, _fn); } template - uint32_t lowerBound(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) + inline void quickSort(Ty* _data, uint32_t _num, const ComparisonFn _fn) + { + BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Element type must be trivially move assignable"); + quickSort( (void*)_data, _num, sizeof(Ty), _fn); + } + + template + inline uint32_t unique(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + { + BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Element type must be trivially move assignable"); + return unique(_data, _num, _stride, _fn); + } + + template + inline uint32_t unique(Ty* _data, uint32_t _num, const ComparisonFn _fn) + { + BX_STATIC_ASSERT(isTriviallyMoveAssignable(), "Element type must be trivially move assignable"); + return unique( (void*)_data, _num, sizeof(Ty), _fn); + } + + template + inline uint32_t lowerBound(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) { return lowerBound( (const void*)&_key, _data, _num, sizeof(Ty), _fn); } template - uint32_t lowerBound(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + inline uint32_t lowerBound(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { return lowerBound( (const void*)&_key, _data, _num, _stride, _fn); } template - uint32_t upperBound(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) + inline uint32_t upperBound(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) { return upperBound( (const void*)&_key, _data, _num, sizeof(Ty), _fn); } template - uint32_t upperBound(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + inline uint32_t upperBound(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { return upperBound( (const void*)&_key, _data, _num, _stride, _fn); } template - bool isSorted(const Ty* _data, uint32_t _num, const ComparisonFn _fn) + inline bool isSorted(const Ty* _data, uint32_t _num, const ComparisonFn _fn) { return isSorted(_data, _num, sizeof(Ty), _fn); } template - bool isSorted(const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + inline bool isSorted(const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { return isSorted(_data, _num, _stride, _fn); } template - int32_t binarySearch(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) + inline int32_t binarySearch(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) { return binarySearch( (const void*)&_key, _data, _num, sizeof(Ty), _fn); } template - int32_t binarySearch(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + inline int32_t binarySearch(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { return binarySearch( (const void*)&_key, _data, _num, _stride, _fn); } diff --git a/include/bx/sort.h b/include/bx/sort.h index a6f1fd0..cee84bf 100644 --- a/include/bx/sort.h +++ b/include/bx/sort.h @@ -43,7 +43,7 @@ namespace bx /// Performs sort (Quick Sort algorithm). /// - /// @param _data Pointer to sorted array data. + /// @param _data Pointer to array data. /// @param _num Number of elements. /// @param _stride Element stride in bytes. /// @param _fn Comparison function. @@ -57,16 +57,7 @@ namespace bx /// Performs sort (Quick Sort algorithm). /// - /// @param _data Pointer to sorted array data. - /// @param _num Number of elements. - /// @param _fn Comparison function. - /// - template - void quickSort(Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); - - /// Performs sort (Quick Sort algorithm). - /// - /// @param _data Pointer to sorted array data. + /// @param _data Pointer to array data. /// @param _num Number of elements. /// @param _stride Element stride in bytes. /// @param _fn Comparison function. @@ -74,6 +65,58 @@ namespace bx template void quickSort(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn = compareAscending); + /// Performs sort (Quick Sort algorithm). + /// + /// @param _data Pointer to array data. + /// @param _num Number of elements. + /// @param _fn Comparison function. + /// + template + void quickSort(Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); + + /// Performs reordering of duplicate elements in the array in the way that unique elements + /// are sorted to the front of array, and duplicates are after the return value index. + /// + /// @param _data Pointer to sorted array data. + /// @param _num Number of elements. + /// @param _stride Element stride in bytes. + /// @param _fn Comparison function. + /// + /// @remarks Array must be sorted! + /// + /// @returns Returns the count of unique elements. + /// + uint32_t unique(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn); + + /// Performs reordering of duplicate elements in the array in the way that unique elements + /// are sorted to the front of array, and duplicates are after the return value index. + /// + /// @param _data Pointer to sorted array data. + /// @param _num Number of elements. + /// @param _stride Element stride in bytes. + /// @param _fn Comparison function. + /// + /// @remarks Array must be sorted! + /// + /// @returns Returns the count of unique elements. + /// + template + uint32_t unique(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn = compareAscending); + + /// Performs reordering of duplicate elements in the array in the way that unique elements + /// are sorted to the front of array, and duplicates are after the return value index. + /// + /// @param _data Pointer to sorted array data. + /// @param _num Number of elements. + /// @param _fn Comparison function. + /// + /// @remarks Array must be sorted! + /// + /// @returns Returns the count of unique elements. + /// + template + uint32_t unique(Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); + /// Performs check if array is sorted. /// /// @param _data Pointer to sorted array data. @@ -90,17 +133,6 @@ namespace bx , const ComparisonFn _fn ); - /// Performs check if array is sorted. - /// - /// @param _data Pointer to sorted array data. - /// @param _num Number of elements. - /// @param _fn Comparison function. - /// - /// @returns Returns `true` if array is sorted, otherwise returns `false`. - /// - template - bool isSorted(const Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); - /// Performs check if array is sorted. /// /// @param _data Pointer to sorted array data. @@ -113,6 +145,17 @@ namespace bx template bool isSorted(const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn = compareAscending); + /// Performs check if array is sorted. + /// + /// @param _data Pointer to sorted array data. + /// @param _num Number of elements. + /// @param _fn Comparison function. + /// + /// @returns Returns `true` if array is sorted, otherwise returns `false`. + /// + template + bool isSorted(const Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); + /// Returns an index to the first element greater or equal than the `_key` value. /// /// @param _key Pointer to the key to search for. @@ -209,7 +252,8 @@ namespace bx /// /// @remarks Array must be sorted! /// - /// @returns Returns index of element or -1 if the key is not found in sorted array. + /// @returns Returns positive value index of element if found, or negative number that is bitwise + /// complement (~) of the index of the next element that's larger than item. /// int32_t binarySearch( const void* _key @@ -228,7 +272,8 @@ namespace bx /// /// @remarks Array must be sorted! /// - /// @returns Returns index of element or -1 if the key is not found in sorted array. + /// @returns Returns positive value index of element if found, or negative number that is bitwise + /// complement (~) of the index of the next element that's larger than item. /// template int32_t binarySearch(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); @@ -243,7 +288,8 @@ namespace bx /// /// @remarks Array must be sorted! /// - /// @returns Returns index of element or -1 if the key is not found in sorted array. + /// @returns Returns positive value index of element if found, or negative number that is bitwise + /// complement (~) of the index of the next element that's larger than item. /// template int32_t binarySearch(const Ty& _key, const void* _data, uint32_t _num, uint32_t _stride = sizeof(Ty), const ComparisonFn _fn = compareAscending); diff --git a/src/sort.cpp b/src/sort.cpp index caba004..31ad8c7 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -68,6 +68,32 @@ namespace bx return true; } + uint32_t unique(void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) + { + if (0 == _num) + { + return 0; + } + + uint8_t* data = (uint8_t*)_data; + + uint32_t last = 0; + + for (uint32_t ii = 1; ii < _num; ++ii) + { + int32_t result = _fn(&data[last*_stride], &data[ii*_stride]); + BX_ASSERT(0 >= result, "Performing unique on non-sorted array (ii %d, last %d)!", ii, last); + + if (0 > result) + { + last++; + swap(&data[last*_stride], &data[ii*_stride], _stride); + } + } + + return last+1; + } + uint32_t lowerBound(const void* _key, const void* _data, uint32_t _num, uint32_t _stride, const ComparisonFn _fn) { uint32_t offset = 0; @@ -141,7 +167,7 @@ namespace bx } } - return -1; + return ~offset; } } // namespace bx diff --git a/tests/sort_test.cpp b/tests/sort_test.cpp index 93c5bc7..4abb76d 100644 --- a/tests/sort_test.cpp +++ b/tests/sort_test.cpp @@ -9,7 +9,7 @@ #include #include -TEST_CASE("quickSort", "") +TEST_CASE("sort-quickSort", "") { const char* str[] = { @@ -37,12 +37,12 @@ TEST_CASE("quickSort", "") return bx::strCmp(lhs, rhs); }; - REQUIRE(-1 == bx::binarySearch("sljiva", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); + REQUIRE(~4 == bx::binarySearch("sljiva", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 0 == bx::binarySearch("jabuka", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 1 == bx::binarySearch("jagoda", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 2 == bx::binarySearch("kruska", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 3 == bx::binarySearch("malina", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); - REQUIRE(-1 == bx::binarySearch("kupina", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); + REQUIRE(~3 == bx::binarySearch("kupina", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 0 == bx::lowerBound("jabuka", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); REQUIRE( 1 == bx::upperBound("jabuka", str, BX_COUNTOF(str), sizeof(str[0]), bsearchStrCmpFn) ); @@ -75,7 +75,28 @@ TEST_CASE("quickSort", "") REQUIRE(bx::isSorted(byte, BX_COUNTOF(byte) ) ); } -TEST_CASE("lower/upperBound int32_t", "") +TEST_CASE("sort-unique", "") +{ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 | 14 + int32_t test[] = { 100, 101, 101, 101, 103, 104, 105, 105, 105, 106, 106, 107, 108, 109 }; + REQUIRE(bx::isSorted(test, BX_COUNTOF(test) ) ); + + REQUIRE(0 == bx::unique(test, 0) ); + REQUIRE(1 == bx::unique(test, 1) ); + + REQUIRE(2 == bx::unique(test, 4) ); + bx::quickSort(test, BX_COUNTOF(test) ); + + REQUIRE(3 == bx::unique(test, 5) ); + bx::quickSort(test, BX_COUNTOF(test) ); + + uint32_t last = bx::unique(test, BX_COUNTOF(test) ); + REQUIRE(9 == last); + + REQUIRE(9 == bx::unique(test, last) ); +} + +TEST_CASE("sort-lower/upperBound int32_t", "") { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 | 14 const int32_t test[] = { 100, 101, 101, 101, 103, 104, 105, 105, 105, 106, 106, 107, 108, 109 }; @@ -129,7 +150,7 @@ void compareTest(const Ty& _min, const Ty& _max) REQUIRE(-1 == compareDescendingTest(_max, _min) ); } -TEST_CASE("ComparisonFn", "") +TEST_CASE("sort-ComparisonFn", "") { compareTest< int8_t>( -13, 89); compareTest(-1389, 1389);