Bug 1059179 - Add BinarySearchIf(). r=waldo

This commit is contained in:
Georg Fritzsche 2014-09-01 19:26:16 +02:00
parent 6d86e1e263
commit e1bff79b5b
2 changed files with 117 additions and 17 deletions

View File

@ -14,8 +14,9 @@
namespace mozilla {
/*
* The algorithm searches the given container |aContainer| over the sorted
* index range [aBegin, aEnd) for an index |i| where |aContainer[i] == aTarget|.
* The BinarySearch() algorithm searches the given container |aContainer| over
* the sorted index range [aBegin, aEnd) for an index |i| where
* |aContainer[i] == aTarget|.
* If such an index |i| is found, BinarySearch returns |true| and the index is
* returned via the outparam |aMatchOrInsertionPoint|. If no index is found,
* BinarySearch returns |false| and the outparam returns the first index in
@ -29,34 +30,61 @@ namespace mozilla {
* if (BinarySearch(sortedInts, 0, sortedInts.length(), 13, &match)) {
* printf("found 13 at %lu\n", match);
* }
*
* The BinarySearchIf() version behaves similar, but takes |aComparator|, a
* functor to compare the values with, instead of a value to find.
* That functor should take one argument - the value to compare - and return an
* |int| with the comparison result:
*
* * 0, if the argument is equal to,
* * less than 0, if the argument is greater than,
* * greater than 0, if the argument is less than
*
* the value.
*
* Example:
*
* struct Comparator {
* int operator()(int val) const {
* if (mTarget < val) return -1;
* if (mValue > val) return 1;
* return 0;
* }
* Comparator(int target) : mTarget(target) {}
const int mTarget;
* };
*
* Vector<int> sortedInts = ...
*
* size_t match;
* if (BinarySearchIf(sortedInts, 0, sortedInts.length(), Comparator(13), &match)) {
* printf("found 13 at %lu\n", match);
* }
*
*/
template <typename Container, typename T>
template<typename Container, typename Comparator>
bool
BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
T aTarget, size_t* aMatchOrInsertionPoint)
BinarySearchIf(const Container& aContainer, size_t aBegin, size_t aEnd,
const Comparator& aCompare, size_t* aMatchOrInsertionPoint)
{
MOZ_ASSERT(aBegin <= aEnd);
size_t low = aBegin;
size_t high = aEnd;
while (low != high) {
while (high != low) {
size_t middle = low + (high - low) / 2;
// Allow any intermediate type so long as it provides a suitable ordering
// relation.
const auto& middleValue = aContainer[middle];
const int result = aCompare(aContainer[middle]);
MOZ_ASSERT(aContainer[low] <= aContainer[middle]);
MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]);
MOZ_ASSERT(aContainer[low] <= aContainer[high - 1]);
if (aTarget == middleValue) {
if (result == 0) {
*aMatchOrInsertionPoint = middle;
return true;
}
if (aTarget < middleValue) {
if (result < 0) {
high = middle;
} else {
low = middle + 1;
@ -67,6 +95,45 @@ BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
return false;
}
namespace detail {
template<class T>
class BinarySearchDefaultComparator
{
public:
BinarySearchDefaultComparator(const T& aTarget)
: mTarget(aTarget)
{}
template <class U>
int operator()(const U& val) const {
if (mTarget == val) {
return 0;
}
if (mTarget < val) {
return -1;
}
return 1;
}
private:
const T& mTarget;
};
} // namespace detail
template <typename Container, typename T>
bool
BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
T aTarget, size_t* aMatchOrInsertionPoint)
{
return BinarySearchIf(aContainer, aBegin, aEnd,
detail::BinarySearchDefaultComparator<T>(aTarget),
aMatchOrInsertionPoint);
}
} // namespace mozilla
#endif // mozilla_BinarySearch_h

View File

@ -8,8 +8,12 @@
#include "mozilla/BinarySearch.h"
#include "mozilla/Vector.h"
using mozilla::Vector;
using mozilla::ArrayLength;
using mozilla::BinarySearch;
using mozilla::BinarySearchIf;
using mozilla::Vector;
#define A(a) MOZ_RELEASE_ASSERT(a)
struct Person
{
@ -25,8 +29,18 @@ struct GetAge
int operator[](size_t index) const { return mV[index].mAge; }
};
int
main()
struct RangeFinder {
const int mLower, mUpper;
RangeFinder(int lower, int upper) : mLower(lower), mUpper(upper) {}
int operator()(int val) const {
if (val >= mUpper) return -1;
if (val < mLower) return 1;
return 0;
}
};
static void
TestBinarySearch()
{
size_t m;
@ -68,7 +82,6 @@ main()
v3.append(Person(4, 13));
v3.append(Person(6, 360));
#define A(a) MOZ_RELEASE_ASSERT(a)
A(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0);
A( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0);
A(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1);
@ -76,5 +89,25 @@ main()
A(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2);
A( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2);
A(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3);
}
static void
TestBinarySearchIf()
{
const int v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
const size_t len = ArrayLength(v1);
size_t m;
A( BinarySearchIf(v1, 0, len, RangeFinder( 2, 3), &m) && m == 2);
A(!BinarySearchIf(v1, 0, len, RangeFinder(-5, -2), &m) && m == 0);
A( BinarySearchIf(v1, 0, len, RangeFinder( 3, 5), &m) && m >= 3 && m < 5);
A(!BinarySearchIf(v1, 0, len, RangeFinder(10, 12), &m) && m == 10);
}
int
main()
{
TestBinarySearch();
TestBinarySearchIf();
return 0;
}