/**
 * Searches the index of the specified `value` inside an ordered array of `items` using the binary search algorithm.
 *
 * @param items - the array to be searched
 * @param value - the value to be searched for
 * @returns integer number between 0 and `items.length`. This value is the index of the search value,
 * if it is contained in the array, or the index at which the value should be inserted to keep the array ordered.
 */
export function binarySearch(items: number[], value: number): number {
  let startIndex = 0;
  let stopIndex = items.length - 1;
  let middle = Math.floor((stopIndex + startIndex) / 2);
  let minIndex = startIndex;
  let maxIndex = stopIndex;

  while (items[middle] !== value && startIndex < stopIndex) {
    // adjust search area
    if (value < items[middle]) {
      stopIndex = middle - 1;
    } else if (value > items[middle]) {
      startIndex = middle + 1;
    }

    // recalculate middle
    middle = Math.floor((stopIndex + startIndex) / 2);
  }

  // correct if middle is out of range
  if (middle < minIndex) {
    middle = minIndex;
  } else if (middle > maxIndex) {
    middle = maxIndex;
  }

  // we want to always return based on strict minor comparation
  if (value < items[middle] && middle > minIndex) {
    return middle - 1;
  }

  return middle;
}
