Extend ArrayUtils

Provide only two binary search functions:
- Add binarySearchFirstGreaterOrEqual()
- Add binarySearchFirstGreater()

Note: other binary search variations can be built later on top of these functions.

Bug: b/256564627
Test: npm run test:unit
Change-Id: Icbd308923fe4b79dae29fb69176fa7276f5dd7cd
This commit is contained in:
Kean Mariotti
2023-02-03 09:52:52 +00:00
parent c2fc0c3daf
commit a644699eb5
2 changed files with 89 additions and 27 deletions

View File

@@ -61,7 +61,10 @@ class ArrayUtils {
return undefined;
}
static binarySearchLowerOrEqual<T>(values: T[] | TypedArray, target: T): number | undefined {
static binarySearchFirstGreaterOrEqual<T>(
values: T[] | TypedArray,
target: T
): number | undefined {
if (values.length === 0) {
return undefined;
}
@@ -75,11 +78,11 @@ class ArrayUtils {
const mid = (low + high) >> 1;
if (values[mid] < target) {
if (result === undefined || result < mid) {
result = mid;
}
low = mid + 1;
} else if (values[mid] > target) {
if (result === undefined || result > mid) {
result = mid;
}
high = mid - 1;
} else {
result = mid;
@@ -90,6 +93,34 @@ class ArrayUtils {
return result;
}
static binarySearchFirstGreater<T>(values: T[] | TypedArray, target: T): number | undefined {
if (values.length === 0) {
return undefined;
}
let low = 0;
let high = values.length - 1;
let result: number | undefined = undefined;
while (low <= high) {
const mid = (low + high) >> 1;
if (values[mid] < target) {
low = mid + 1;
} else if (values[mid] > target) {
if (result === undefined || result > mid) {
result = mid;
}
high = mid - 1;
} else {
low = mid + 1;
}
}
return result;
}
static toUintLittleEndian(buffer: Uint8Array, start: number, end: number): bigint {
let result = 0n;
for (let i = end - 1; i >= start; --i) {

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ArrayUtils} from './array_utils';
describe('ArrayUtils', () => {
@@ -55,35 +56,65 @@ describe('ArrayUtils', () => {
expect(ArrayUtils.searchSubarray([0, 1, 2], [2, 3])).toEqual(undefined);
});
it('binarySearchLowerOrEqual', () => {
it('binarySearchFirstGreaterOrEqual', () => {
// no match
expect(ArrayUtils.binarySearchLowerOrEqual([], 5)).toBeUndefined();
expect(ArrayUtils.binarySearchLowerOrEqual([6], 5)).toBeUndefined();
expect(ArrayUtils.binarySearchLowerOrEqual([6, 7], 5)).toBeUndefined();
expect(ArrayUtils.binarySearchLowerOrEqual([6, 7, 8], 5)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([8], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([7, 8], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6, 7, 8], 9)).toBeUndefined();
// match (lower)
expect(ArrayUtils.binarySearchLowerOrEqual([4], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4], 5)).toEqual(1);
expect(ArrayUtils.binarySearchLowerOrEqual([2, 3, 4], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([2, 3, 4, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([2, 3, 4, 6, 7], 5)).toEqual(2);
// match (greater)
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([6, 7], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 6], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 6, 7, 8], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 6, 7, 8], 5)).toEqual(2);
// match (equal)
expect(ArrayUtils.binarySearchLowerOrEqual([5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([5, 6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([4, 5], 5)).toEqual(1);
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6, 7], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 5], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([3, 4, 5, 6, 7], 5)).toEqual(2);
// match (equal with repeated values)
expect(ArrayUtils.binarySearchLowerOrEqual([5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([5, 5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([5, 5, 5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchLowerOrEqual([4, 5, 5, 6], 5)).toEqual(1);
expect(ArrayUtils.binarySearchLowerOrEqual([4, 4, 5, 5, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([4, 4, 4, 5, 5, 5, 5, 6], 5)).toEqual(3);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([5, 5, 5, 5], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 5, 5, 6], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 4, 5, 5, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreaterOrEqual([4, 4, 4, 5, 5, 5, 5, 6], 5)).toEqual(3);
});
it('binarySearchFirstGreater', () => {
// no match
expect(ArrayUtils.binarySearchFirstGreater([], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreater([8], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreater([7, 8], 9)).toBeUndefined();
expect(ArrayUtils.binarySearchFirstGreater([6, 7, 8], 9)).toBeUndefined();
// match
expect(ArrayUtils.binarySearchFirstGreater([6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreater([6, 7], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreater([4, 6], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreater([4, 6, 7, 8], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreater([3, 4, 6, 7, 8], 5)).toEqual(2);
// match (ignore equal)
expect(ArrayUtils.binarySearchFirstGreater([5], 5)).toEqual(undefined);
expect(ArrayUtils.binarySearchFirstGreater([5, 6], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreater([4, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreater([3, 4, 5, 6], 5)).toEqual(3);
expect(ArrayUtils.binarySearchFirstGreater([3, 4, 5, 6, 7], 5)).toEqual(3);
// match (with repeated values)
expect(ArrayUtils.binarySearchFirstGreater([6, 6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreater([6, 6, 6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreater([6, 6, 6, 6], 5)).toEqual(0);
expect(ArrayUtils.binarySearchFirstGreater([5, 6, 6, 7], 5)).toEqual(1);
expect(ArrayUtils.binarySearchFirstGreater([5, 5, 6, 6, 6, 7], 5)).toEqual(2);
expect(ArrayUtils.binarySearchFirstGreater([5, 5, 5, 6, 6, 6, 6, 7], 5)).toEqual(3);
});
it('toUintLittleEndian', () => {