prefixSumComputer.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. import { arrayInsert } from '../../../base/common/arrays.js';
  6. import { toUint32 } from '../../../base/common/uint.js';
  7. export class PrefixSumComputer {
  8. constructor(values) {
  9. this.values = values;
  10. this.prefixSum = new Uint32Array(values.length);
  11. this.prefixSumValidIndex = new Int32Array(1);
  12. this.prefixSumValidIndex[0] = -1;
  13. }
  14. insertValues(insertIndex, insertValues) {
  15. insertIndex = toUint32(insertIndex);
  16. const oldValues = this.values;
  17. const oldPrefixSum = this.prefixSum;
  18. const insertValuesLen = insertValues.length;
  19. if (insertValuesLen === 0) {
  20. return false;
  21. }
  22. this.values = new Uint32Array(oldValues.length + insertValuesLen);
  23. this.values.set(oldValues.subarray(0, insertIndex), 0);
  24. this.values.set(oldValues.subarray(insertIndex), insertIndex + insertValuesLen);
  25. this.values.set(insertValues, insertIndex);
  26. if (insertIndex - 1 < this.prefixSumValidIndex[0]) {
  27. this.prefixSumValidIndex[0] = insertIndex - 1;
  28. }
  29. this.prefixSum = new Uint32Array(this.values.length);
  30. if (this.prefixSumValidIndex[0] >= 0) {
  31. this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1));
  32. }
  33. return true;
  34. }
  35. setValue(index, value) {
  36. index = toUint32(index);
  37. value = toUint32(value);
  38. if (this.values[index] === value) {
  39. return false;
  40. }
  41. this.values[index] = value;
  42. if (index - 1 < this.prefixSumValidIndex[0]) {
  43. this.prefixSumValidIndex[0] = index - 1;
  44. }
  45. return true;
  46. }
  47. removeValues(startIndex, count) {
  48. startIndex = toUint32(startIndex);
  49. count = toUint32(count);
  50. const oldValues = this.values;
  51. const oldPrefixSum = this.prefixSum;
  52. if (startIndex >= oldValues.length) {
  53. return false;
  54. }
  55. let maxCount = oldValues.length - startIndex;
  56. if (count >= maxCount) {
  57. count = maxCount;
  58. }
  59. if (count === 0) {
  60. return false;
  61. }
  62. this.values = new Uint32Array(oldValues.length - count);
  63. this.values.set(oldValues.subarray(0, startIndex), 0);
  64. this.values.set(oldValues.subarray(startIndex + count), startIndex);
  65. this.prefixSum = new Uint32Array(this.values.length);
  66. if (startIndex - 1 < this.prefixSumValidIndex[0]) {
  67. this.prefixSumValidIndex[0] = startIndex - 1;
  68. }
  69. if (this.prefixSumValidIndex[0] >= 0) {
  70. this.prefixSum.set(oldPrefixSum.subarray(0, this.prefixSumValidIndex[0] + 1));
  71. }
  72. return true;
  73. }
  74. getTotalSum() {
  75. if (this.values.length === 0) {
  76. return 0;
  77. }
  78. return this._getPrefixSum(this.values.length - 1);
  79. }
  80. /**
  81. * Returns the sum of the first `index + 1` many items.
  82. * @returns `SUM(0 <= j <= index, values[j])`.
  83. */
  84. getPrefixSum(index) {
  85. if (index < 0) {
  86. return 0;
  87. }
  88. index = toUint32(index);
  89. return this._getPrefixSum(index);
  90. }
  91. _getPrefixSum(index) {
  92. if (index <= this.prefixSumValidIndex[0]) {
  93. return this.prefixSum[index];
  94. }
  95. let startIndex = this.prefixSumValidIndex[0] + 1;
  96. if (startIndex === 0) {
  97. this.prefixSum[0] = this.values[0];
  98. startIndex++;
  99. }
  100. if (index >= this.values.length) {
  101. index = this.values.length - 1;
  102. }
  103. for (let i = startIndex; i <= index; i++) {
  104. this.prefixSum[i] = this.prefixSum[i - 1] + this.values[i];
  105. }
  106. this.prefixSumValidIndex[0] = Math.max(this.prefixSumValidIndex[0], index);
  107. return this.prefixSum[index];
  108. }
  109. getIndexOf(sum) {
  110. sum = Math.floor(sum);
  111. // Compute all sums (to get a fully valid prefixSum)
  112. this.getTotalSum();
  113. let low = 0;
  114. let high = this.values.length - 1;
  115. let mid = 0;
  116. let midStop = 0;
  117. let midStart = 0;
  118. while (low <= high) {
  119. mid = low + ((high - low) / 2) | 0;
  120. midStop = this.prefixSum[mid];
  121. midStart = midStop - this.values[mid];
  122. if (sum < midStart) {
  123. high = mid - 1;
  124. }
  125. else if (sum >= midStop) {
  126. low = mid + 1;
  127. }
  128. else {
  129. break;
  130. }
  131. }
  132. return new PrefixSumIndexOfResult(mid, sum - midStart);
  133. }
  134. }
  135. /**
  136. * {@link getIndexOf} has an amortized runtime complexity of O(1).
  137. *
  138. * ({@link PrefixSumComputer.getIndexOf} is just O(log n))
  139. */
  140. export class ConstantTimePrefixSumComputer {
  141. constructor(values) {
  142. this._values = values;
  143. this._isValid = false;
  144. this._validEndIndex = -1;
  145. this._prefixSum = [];
  146. this._indexBySum = [];
  147. }
  148. /**
  149. * @returns SUM(0 <= j < values.length, values[j])
  150. */
  151. getTotalSum() {
  152. this._ensureValid();
  153. return this._indexBySum.length;
  154. }
  155. /**
  156. * Returns the sum of the first `count` many items.
  157. * @returns `SUM(0 <= j < count, values[j])`.
  158. */
  159. getPrefixSum(count) {
  160. this._ensureValid();
  161. if (count === 0) {
  162. return 0;
  163. }
  164. return this._prefixSum[count - 1];
  165. }
  166. /**
  167. * @returns `result`, such that `getPrefixSum(result.index) + result.remainder = sum`
  168. */
  169. getIndexOf(sum) {
  170. this._ensureValid();
  171. const idx = this._indexBySum[sum];
  172. const viewLinesAbove = idx > 0 ? this._prefixSum[idx - 1] : 0;
  173. return new PrefixSumIndexOfResult(idx, sum - viewLinesAbove);
  174. }
  175. removeValues(start, deleteCount) {
  176. this._values.splice(start, deleteCount);
  177. this._invalidate(start);
  178. }
  179. insertValues(insertIndex, insertArr) {
  180. this._values = arrayInsert(this._values, insertIndex, insertArr);
  181. this._invalidate(insertIndex);
  182. }
  183. _invalidate(index) {
  184. this._isValid = false;
  185. this._validEndIndex = Math.min(this._validEndIndex, index - 1);
  186. }
  187. _ensureValid() {
  188. if (this._isValid) {
  189. return;
  190. }
  191. for (let i = this._validEndIndex + 1, len = this._values.length; i < len; i++) {
  192. const value = this._values[i];
  193. const sumAbove = i > 0 ? this._prefixSum[i - 1] : 0;
  194. this._prefixSum[i] = sumAbove + value;
  195. for (let j = 0; j < value; j++) {
  196. this._indexBySum[sumAbove + j] = i;
  197. }
  198. }
  199. // trim things
  200. this._prefixSum.length = this._values.length;
  201. this._indexBySum.length = this._prefixSum[this._prefixSum.length - 1];
  202. // mark as valid
  203. this._isValid = true;
  204. this._validEndIndex = this._values.length - 1;
  205. }
  206. setValue(index, value) {
  207. if (this._values[index] === value) {
  208. // no change
  209. return;
  210. }
  211. this._values[index] = value;
  212. this._invalidate(index);
  213. }
  214. }
  215. export class PrefixSumIndexOfResult {
  216. constructor(index, remainder) {
  217. this.index = index;
  218. this.remainder = remainder;
  219. this._prefixSumIndexOfResultBrand = undefined;
  220. this.index = index;
  221. this.remainder = remainder;
  222. }
  223. }