Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/model/prefixSumComputer.ts
3294 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { arrayInsert } from '../../../base/common/arrays.js';
7
import { toUint32 } from '../../../base/common/uint.js';
8
9
export class PrefixSumComputer {
10
11
/**
12
* values[i] is the value at index i
13
*/
14
private values: Uint32Array;
15
16
/**
17
* prefixSum[i] = SUM(heights[j]), 0 <= j <= i
18
*/
19
private prefixSum: Uint32Array;
20
21
/**
22
* prefixSum[i], 0 <= i <= prefixSumValidIndex can be trusted
23
*/
24
private readonly prefixSumValidIndex: Int32Array;
25
26
constructor(values: Uint32Array) {
27
this.values = values;
28
this.prefixSum = new Uint32Array(values.length);
29
this.prefixSumValidIndex = new Int32Array(1);
30
this.prefixSumValidIndex[0] = -1;
31
}
32
33
public getCount(): number {
34
return this.values.length;
35
}
36
37
public insertValues(insertIndex: number, insertValues: Uint32Array): boolean {
38
insertIndex = toUint32(insertIndex);
39
const oldValues = this.values;
40
const oldPrefixSum = this.prefixSum;
41
const insertValuesLen = insertValues.length;
42
43
if (insertValuesLen === 0) {
44
return false;
45
}
46
47
this.values = new Uint32Array(oldValues.length + insertValuesLen);
48
this.values.set(oldValues.subarray(0, insertIndex), 0);
49
this.values.set(oldValues.subarray(insertIndex), insertIndex + insertValuesLen);
50
this.values.set(insertValues, insertIndex);
51
52
if (insertIndex - 1 < this.prefixSumValidIndex[0]) {
53
this.prefixSumValidIndex[0] = insertIndex - 1;
54
}
55
56
this.prefixSum = new Uint32Array(this.values.length);
57
if (this.prefixSumValidIndex[0] >= 0) {
58
this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1));
59
}
60
return true;
61
}
62
63
public setValue(index: number, value: number): boolean {
64
index = toUint32(index);
65
value = toUint32(value);
66
67
if (this.values[index] === value) {
68
return false;
69
}
70
this.values[index] = value;
71
if (index - 1 < this.prefixSumValidIndex[0]) {
72
this.prefixSumValidIndex[0] = index - 1;
73
}
74
return true;
75
}
76
77
public removeValues(startIndex: number, count: number): boolean {
78
startIndex = toUint32(startIndex);
79
count = toUint32(count);
80
81
const oldValues = this.values;
82
const oldPrefixSum = this.prefixSum;
83
84
if (startIndex >= oldValues.length) {
85
return false;
86
}
87
88
const maxCount = oldValues.length - startIndex;
89
if (count >= maxCount) {
90
count = maxCount;
91
}
92
93
if (count === 0) {
94
return false;
95
}
96
97
this.values = new Uint32Array(oldValues.length - count);
98
this.values.set(oldValues.subarray(0, startIndex), 0);
99
this.values.set(oldValues.subarray(startIndex + count), startIndex);
100
101
this.prefixSum = new Uint32Array(this.values.length);
102
if (startIndex - 1 < this.prefixSumValidIndex[0]) {
103
this.prefixSumValidIndex[0] = startIndex - 1;
104
}
105
if (this.prefixSumValidIndex[0] >= 0) {
106
this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1));
107
}
108
return true;
109
}
110
111
public getTotalSum(): number {
112
if (this.values.length === 0) {
113
return 0;
114
}
115
return this._getPrefixSum(this.values.length - 1);
116
}
117
118
/**
119
* Returns the sum of the first `index + 1` many items.
120
* @returns `SUM(0 <= j <= index, values[j])`.
121
*/
122
public getPrefixSum(index: number): number {
123
if (index < 0) {
124
return 0;
125
}
126
127
index = toUint32(index);
128
return this._getPrefixSum(index);
129
}
130
131
private _getPrefixSum(index: number): number {
132
if (index <= this.prefixSumValidIndex[0]) {
133
return this.prefixSum[index];
134
}
135
136
let startIndex = this.prefixSumValidIndex[0] + 1;
137
if (startIndex === 0) {
138
this.prefixSum[0] = this.values[0];
139
startIndex++;
140
}
141
142
if (index >= this.values.length) {
143
index = this.values.length - 1;
144
}
145
146
for (let i = startIndex; i <= index; i++) {
147
this.prefixSum[i] = this.prefixSum[i - 1] + this.values[i];
148
}
149
this.prefixSumValidIndex[0] = Math.max(this.prefixSumValidIndex[0], index);
150
return this.prefixSum[index];
151
}
152
153
public getIndexOf(sum: number): PrefixSumIndexOfResult {
154
sum = Math.floor(sum);
155
156
// Compute all sums (to get a fully valid prefixSum)
157
this.getTotalSum();
158
159
let low = 0;
160
let high = this.values.length - 1;
161
let mid = 0;
162
let midStop = 0;
163
let midStart = 0;
164
165
while (low <= high) {
166
mid = low + ((high - low) / 2) | 0;
167
168
midStop = this.prefixSum[mid];
169
midStart = midStop - this.values[mid];
170
171
if (sum < midStart) {
172
high = mid - 1;
173
} else if (sum >= midStop) {
174
low = mid + 1;
175
} else {
176
break;
177
}
178
}
179
180
return new PrefixSumIndexOfResult(mid, sum - midStart);
181
}
182
}
183
184
/**
185
* {@link getIndexOf} has an amortized runtime complexity of O(1).
186
*
187
* ({@link PrefixSumComputer.getIndexOf} is just O(log n))
188
*/
189
export class ConstantTimePrefixSumComputer {
190
private _values: number[];
191
private _isValid: boolean;
192
private _validEndIndex: number;
193
194
/**
195
* _prefixSum[i] = SUM(values[j]), 0 <= j <= i
196
*/
197
private _prefixSum: number[];
198
199
/**
200
* _indexBySum[sum] = idx => _prefixSum[idx - 1] <= sum < _prefixSum[idx]
201
*/
202
private _indexBySum: number[];
203
204
constructor(values: number[]) {
205
this._values = values;
206
this._isValid = false;
207
this._validEndIndex = -1;
208
this._prefixSum = [];
209
this._indexBySum = [];
210
}
211
212
/**
213
* @returns SUM(0 <= j < values.length, values[j])
214
*/
215
public getTotalSum(): number {
216
this._ensureValid();
217
return this._indexBySum.length;
218
}
219
220
/**
221
* Returns the sum of the first `count` many items.
222
* @returns `SUM(0 <= j < count, values[j])`.
223
*/
224
public getPrefixSum(count: number): number {
225
this._ensureValid();
226
if (count === 0) {
227
return 0;
228
}
229
return this._prefixSum[count - 1];
230
}
231
232
/**
233
* @returns `result`, such that `getPrefixSum(result.index) + result.remainder = sum`
234
*/
235
public getIndexOf(sum: number): PrefixSumIndexOfResult {
236
this._ensureValid();
237
const idx = this._indexBySum[sum];
238
const viewLinesAbove = idx > 0 ? this._prefixSum[idx - 1] : 0;
239
return new PrefixSumIndexOfResult(idx, sum - viewLinesAbove);
240
}
241
242
public removeValues(start: number, deleteCount: number): void {
243
this._values.splice(start, deleteCount);
244
this._invalidate(start);
245
}
246
247
public insertValues(insertIndex: number, insertArr: number[]): void {
248
this._values = arrayInsert(this._values, insertIndex, insertArr);
249
this._invalidate(insertIndex);
250
}
251
252
private _invalidate(index: number): void {
253
this._isValid = false;
254
this._validEndIndex = Math.min(this._validEndIndex, index - 1);
255
}
256
257
private _ensureValid(): void {
258
if (this._isValid) {
259
return;
260
}
261
262
for (let i = this._validEndIndex + 1, len = this._values.length; i < len; i++) {
263
const value = this._values[i];
264
const sumAbove = i > 0 ? this._prefixSum[i - 1] : 0;
265
266
this._prefixSum[i] = sumAbove + value;
267
for (let j = 0; j < value; j++) {
268
this._indexBySum[sumAbove + j] = i;
269
}
270
}
271
272
// trim things
273
this._prefixSum.length = this._values.length;
274
this._indexBySum.length = this._prefixSum[this._prefixSum.length - 1];
275
276
// mark as valid
277
this._isValid = true;
278
this._validEndIndex = this._values.length - 1;
279
}
280
281
public setValue(index: number, value: number): void {
282
if (this._values[index] === value) {
283
// no change
284
return;
285
}
286
this._values[index] = value;
287
this._invalidate(index);
288
}
289
}
290
291
292
export class PrefixSumIndexOfResult {
293
_prefixSumIndexOfResultBrand: void = undefined;
294
295
constructor(
296
public readonly index: number,
297
public readonly remainder: number
298
) {
299
this.index = index;
300
this.remainder = remainder;
301
}
302
}
303
304