Files
railwaysystem/RuoYi-Vue/ruoyi-ui/node_modules2/hyperformula/es/interpreter/binarySearch.js
T

111 lines
4.2 KiB
JavaScript
Raw Normal View History

2026-09-10 15:40:10 +08:00
/**
* @license
* Copyright (c) 2024 Handsoncode. All rights reserved.
*/
import { CellError, simpleCellAddress } from '../Cell';
import { EmptyValue, getRawValue } from './InterpreterValue';
const NOT_FOUND = -1;
/*
* Searches for the searchKey in a sorted 1-D range.
*
* Options:
* - searchCoordinate - must be set to either 'row' or 'col' to indicate the dimension of the search,
* - orderingDirection - must be set to either 'asc' or 'desc' to indicate the ordering direction for the search range,
* - matchExactly - when set to false, searches for the lower/upper bound.
*
* Semantics:
* - If orderingDirection === 'asc', searches for the lower bound for the searchKey value (unless marchExactly === true).
* - If orderingDirection === 'desc', searches for the upper bound for the searchKey value (unless marchExactly === true).
* - If the search range contains duplicates, returns the last matching value.
* - If no value in the range satisfies the above, returns -1.
*
* Note: this function does not normalize input strings.
*/
export function findLastOccurrenceInOrderedRange(searchKey, range, {
searchCoordinate,
orderingDirection,
matchExactly
}, dependencyGraph) {
const start = range.start[searchCoordinate];
const end = searchCoordinate === 'col' ? range.effectiveEndColumn(dependencyGraph) : range.effectiveEndRow(dependencyGraph);
const getValueFromIndexFn = searchCoordinate === 'col' ? index => getRawValue(dependencyGraph.getCellValue(simpleCellAddress(range.sheet, index, range.start.row))) : index => getRawValue(dependencyGraph.getCellValue(simpleCellAddress(range.sheet, range.start.col, index)));
const compareFn = orderingDirection === 'asc' ? (left, right) => compare(left, right) : (left, right) => -compare(left, right);
const foundIndex = findLastMatchingIndex(index => compareFn(searchKey, getValueFromIndexFn(index)) >= 0, start, end);
const foundValue = getValueFromIndexFn(foundIndex);
if (foundIndex === NOT_FOUND || typeof foundValue !== typeof searchKey) {
return NOT_FOUND;
}
if (matchExactly && foundValue !== searchKey) {
return NOT_FOUND;
}
return foundIndex - start;
}
/*
* Searches for the searchKey in a sorted array.
* Param orderingDirection must be set to either 'asc' or 'desc' to indicate the ordering direction of the array.
*
* Semantics:
* - If orderingDirection === 'asc', searches for the lower bound for the searchKey value.
* - If orderingDirection === 'desc', searches for the upper bound for the searchKey value.
* - If the array contains duplicates, returns the last matching value.
* - If no value in the range satisfies the above, returns -1.
*/
export function findLastOccurrenceInOrderedArray(searchKey, array, orderingDirection = 'asc') {
const predicate = orderingDirection === 'asc' ? index => compare(searchKey, array[index]) >= 0 : index => -compare(searchKey, array[index]) >= 0;
return findLastMatchingIndex(predicate, 0, array.length - 1);
}
/*
* Returns:
* - the last element in the range for which predicate === true or,
* - value -1 if predicate === false for all elements.
* Assumption: All elements for which predicate === true are before the elements for which predicate === false.
*/
export function findLastMatchingIndex(predicate, startRange, endRange) {
let start = startRange;
let end = endRange;
while (start < end) {
const pivot = Math.ceil((start + end) / 2);
if (predicate(pivot)) {
start = pivot;
} else {
end = pivot - 1;
}
}
if (start === end && predicate(start)) {
return start;
}
return NOT_FOUND;
}
/*
* numbers < strings < false < true
*/
export function compare(left, right) {
if (typeof left === typeof right) {
if (left === EmptyValue) {
return 0;
}
return left < right ? -1 : left > right ? 1 : 0;
}
if (left === EmptyValue) {
return -1;
}
if (right === EmptyValue) {
return 1;
}
if (right instanceof CellError) {
return -1;
}
if (typeof left === 'number' && typeof right === 'string') {
return -1;
}
if (typeof left === 'number' && typeof right === 'boolean') {
return -1;
}
if (typeof left === 'string' && typeof right === 'number') {
return 1;
}
if (typeof left === 'string' && typeof right === 'boolean') {
return -1;
}
return 1;
}