xref: /aosp_15_r20/external/perfetto/ui/src/base/binary_search.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2018 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workertype Numeric = number | bigint;
16*6dbdd20aSAndroid Build Coastguard Workertype Range = [number, number];
17*6dbdd20aSAndroid Build Coastguard Worker
18*6dbdd20aSAndroid Build Coastguard Workerfunction searchImpl<T extends Numeric>(
19*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
20*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
21*6dbdd20aSAndroid Build Coastguard Worker  i: number,
22*6dbdd20aSAndroid Build Coastguard Worker  j: number,
23*6dbdd20aSAndroid Build Coastguard Worker): number {
24*6dbdd20aSAndroid Build Coastguard Worker  if (i === j) return -1;
25*6dbdd20aSAndroid Build Coastguard Worker  if (i + 1 === j) {
26*6dbdd20aSAndroid Build Coastguard Worker    return needle >= haystack[i] ? i : -1;
27*6dbdd20aSAndroid Build Coastguard Worker  }
28*6dbdd20aSAndroid Build Coastguard Worker
29*6dbdd20aSAndroid Build Coastguard Worker  const mid = Math.floor((j - i) / 2) + i;
30*6dbdd20aSAndroid Build Coastguard Worker  const midValue = haystack[mid];
31*6dbdd20aSAndroid Build Coastguard Worker  if (needle < midValue) {
32*6dbdd20aSAndroid Build Coastguard Worker    return searchImpl(haystack, needle, i, mid);
33*6dbdd20aSAndroid Build Coastguard Worker  } else {
34*6dbdd20aSAndroid Build Coastguard Worker    return searchImpl(haystack, needle, mid, j);
35*6dbdd20aSAndroid Build Coastguard Worker  }
36*6dbdd20aSAndroid Build Coastguard Worker}
37*6dbdd20aSAndroid Build Coastguard Worker
38*6dbdd20aSAndroid Build Coastguard Workerfunction searchRangeImpl<T extends Numeric>(
39*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
40*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
41*6dbdd20aSAndroid Build Coastguard Worker  i: number,
42*6dbdd20aSAndroid Build Coastguard Worker  j: number,
43*6dbdd20aSAndroid Build Coastguard Worker): Range {
44*6dbdd20aSAndroid Build Coastguard Worker  if (i === j) return [i, j];
45*6dbdd20aSAndroid Build Coastguard Worker  if (i + 1 === j) {
46*6dbdd20aSAndroid Build Coastguard Worker    if (haystack[i] <= needle) {
47*6dbdd20aSAndroid Build Coastguard Worker      return [i, j];
48*6dbdd20aSAndroid Build Coastguard Worker    } else {
49*6dbdd20aSAndroid Build Coastguard Worker      return [i, i];
50*6dbdd20aSAndroid Build Coastguard Worker    }
51*6dbdd20aSAndroid Build Coastguard Worker  }
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Worker  const mid = Math.floor((j - i) / 2) + i;
54*6dbdd20aSAndroid Build Coastguard Worker  const midValue = haystack[mid];
55*6dbdd20aSAndroid Build Coastguard Worker
56*6dbdd20aSAndroid Build Coastguard Worker  if (needle < midValue) {
57*6dbdd20aSAndroid Build Coastguard Worker    return searchRangeImpl(haystack, needle, i, mid);
58*6dbdd20aSAndroid Build Coastguard Worker  } else if (needle > midValue) {
59*6dbdd20aSAndroid Build Coastguard Worker    return searchRangeImpl(haystack, needle, mid, j);
60*6dbdd20aSAndroid Build Coastguard Worker  } else {
61*6dbdd20aSAndroid Build Coastguard Worker    while (haystack[i] !== needle) i++;
62*6dbdd20aSAndroid Build Coastguard Worker    while (haystack[j - 1] !== needle) j--;
63*6dbdd20aSAndroid Build Coastguard Worker    return [i, j];
64*6dbdd20aSAndroid Build Coastguard Worker  }
65*6dbdd20aSAndroid Build Coastguard Worker}
66*6dbdd20aSAndroid Build Coastguard Worker
67*6dbdd20aSAndroid Build Coastguard Worker// Given a sorted array of numeric values |haystack| and a |needle|, return the
68*6dbdd20aSAndroid Build Coastguard Worker// index of the last element of |haystack| which is less than or equal to
69*6dbdd20aSAndroid Build Coastguard Worker// |needle|, or -1 if all elements of |haystack| are greater than |needle|.
70*6dbdd20aSAndroid Build Coastguard Workerexport function search<T extends Numeric>(
71*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
72*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
73*6dbdd20aSAndroid Build Coastguard Worker): number {
74*6dbdd20aSAndroid Build Coastguard Worker  return searchImpl(haystack, needle, 0, haystack.length);
75*6dbdd20aSAndroid Build Coastguard Worker}
76*6dbdd20aSAndroid Build Coastguard Worker
77*6dbdd20aSAndroid Build Coastguard Worker// Given a sorted array of numeric values (|haystack|) return the half open
78*6dbdd20aSAndroid Build Coastguard Worker// range [i, j) of indices where |haystack| is equal to needle.
79*6dbdd20aSAndroid Build Coastguard Workerexport function searchEq<T extends Numeric>(
80*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
81*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
82*6dbdd20aSAndroid Build Coastguard Worker  optRange?: Range,
83*6dbdd20aSAndroid Build Coastguard Worker): Range {
84*6dbdd20aSAndroid Build Coastguard Worker  const range = searchRange(haystack, needle, optRange);
85*6dbdd20aSAndroid Build Coastguard Worker  const [i, j] = range;
86*6dbdd20aSAndroid Build Coastguard Worker  if (haystack[i] === needle) return range;
87*6dbdd20aSAndroid Build Coastguard Worker  return [j, j];
88*6dbdd20aSAndroid Build Coastguard Worker}
89*6dbdd20aSAndroid Build Coastguard Worker
90*6dbdd20aSAndroid Build Coastguard Worker// Given a sorted array of numeric values (|haystack|) and a |needle| return the
91*6dbdd20aSAndroid Build Coastguard Worker// smallest half open range [i, j) of indexes which contains |needle|.
92*6dbdd20aSAndroid Build Coastguard Workerexport function searchRange<T extends Numeric>(
93*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
94*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
95*6dbdd20aSAndroid Build Coastguard Worker  optRange?: Range,
96*6dbdd20aSAndroid Build Coastguard Worker): Range {
97*6dbdd20aSAndroid Build Coastguard Worker  const [left, right] = optRange ? optRange : [0, haystack.length];
98*6dbdd20aSAndroid Build Coastguard Worker  return searchRangeImpl(haystack, needle, left, right);
99*6dbdd20aSAndroid Build Coastguard Worker}
100*6dbdd20aSAndroid Build Coastguard Worker
101*6dbdd20aSAndroid Build Coastguard Worker// Given a sorted array of numeric values (|haystack|) and a |needle| return a
102*6dbdd20aSAndroid Build Coastguard Worker// pair of indexes [i, j] such that:
103*6dbdd20aSAndroid Build Coastguard Worker// If there is at least one element in |haystack| smaller than |needle|
104*6dbdd20aSAndroid Build Coastguard Worker// i is the index of the largest such number otherwise -1;
105*6dbdd20aSAndroid Build Coastguard Worker// If there is at least one element in |haystack| larger than |needle|
106*6dbdd20aSAndroid Build Coastguard Worker// j is the index of the smallest such element otherwise -1.
107*6dbdd20aSAndroid Build Coastguard Worker//
108*6dbdd20aSAndroid Build Coastguard Worker// So we try to get the indexes of the two data points around needle
109*6dbdd20aSAndroid Build Coastguard Worker// or -1 if there is no such datapoint.
110*6dbdd20aSAndroid Build Coastguard Workerexport function searchSegment<T extends Numeric>(
111*6dbdd20aSAndroid Build Coastguard Worker  haystack: ArrayLike<T>,
112*6dbdd20aSAndroid Build Coastguard Worker  needle: T,
113*6dbdd20aSAndroid Build Coastguard Worker): Range {
114*6dbdd20aSAndroid Build Coastguard Worker  if (!haystack.length) return [-1, -1];
115*6dbdd20aSAndroid Build Coastguard Worker
116*6dbdd20aSAndroid Build Coastguard Worker  const left = search(haystack, needle);
117*6dbdd20aSAndroid Build Coastguard Worker  if (left === -1) {
118*6dbdd20aSAndroid Build Coastguard Worker    return [left, 0];
119*6dbdd20aSAndroid Build Coastguard Worker  } else if (left + 1 === haystack.length) {
120*6dbdd20aSAndroid Build Coastguard Worker    return [left, -1];
121*6dbdd20aSAndroid Build Coastguard Worker  } else {
122*6dbdd20aSAndroid Build Coastguard Worker    return [left, left + 1];
123*6dbdd20aSAndroid Build Coastguard Worker  }
124*6dbdd20aSAndroid Build Coastguard Worker}
125