range-tree.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { RangeCov } from "./types";
  2. export class RangeTree {
  3. start: number;
  4. end: number;
  5. delta: number;
  6. children: RangeTree[];
  7. constructor(
  8. start: number,
  9. end: number,
  10. delta: number,
  11. children: RangeTree[],
  12. ) {
  13. this.start = start;
  14. this.end = end;
  15. this.delta = delta;
  16. this.children = children;
  17. }
  18. /**
  19. * @precodition `ranges` are well-formed and pre-order sorted
  20. */
  21. static fromSortedRanges(ranges: ReadonlyArray<RangeCov>): RangeTree | undefined {
  22. let root: RangeTree | undefined;
  23. // Stack of parent trees and parent counts.
  24. const stack: [RangeTree, number][] = [];
  25. for (const range of ranges) {
  26. const node: RangeTree = new RangeTree(range.startOffset, range.endOffset, range.count, []);
  27. if (root === undefined) {
  28. root = node;
  29. stack.push([node, range.count]);
  30. continue;
  31. }
  32. let parent: RangeTree;
  33. let parentCount: number;
  34. while (true) {
  35. [parent, parentCount] = stack[stack.length - 1];
  36. // assert: `top !== undefined` (the ranges are sorted)
  37. if (range.startOffset < parent.end) {
  38. break;
  39. } else {
  40. stack.pop();
  41. }
  42. }
  43. node.delta -= parentCount;
  44. parent.children.push(node);
  45. stack.push([node, range.count]);
  46. }
  47. return root;
  48. }
  49. normalize(): void {
  50. const children: RangeTree[] = [];
  51. let curEnd: number;
  52. let head: RangeTree | undefined;
  53. const tail: RangeTree[] = [];
  54. for (const child of this.children) {
  55. if (head === undefined) {
  56. head = child;
  57. } else if (child.delta === head.delta && child.start === curEnd!) {
  58. tail.push(child);
  59. } else {
  60. endChain();
  61. head = child;
  62. }
  63. curEnd = child.end;
  64. }
  65. if (head !== undefined) {
  66. endChain();
  67. }
  68. if (children.length === 1) {
  69. const child: RangeTree = children[0];
  70. if (child.start === this.start && child.end === this.end) {
  71. this.delta += child.delta;
  72. this.children = child.children;
  73. // `.lazyCount` is zero for both (both are after normalization)
  74. return;
  75. }
  76. }
  77. this.children = children;
  78. function endChain(): void {
  79. if (tail.length !== 0) {
  80. head!.end = tail[tail.length - 1].end;
  81. for (const tailTree of tail) {
  82. for (const subChild of tailTree.children) {
  83. subChild.delta += tailTree.delta - head!.delta;
  84. head!.children.push(subChild);
  85. }
  86. }
  87. tail.length = 0;
  88. }
  89. head!.normalize();
  90. children.push(head!);
  91. }
  92. }
  93. /**
  94. * @precondition `tree.start < value && value < tree.end`
  95. * @return RangeTree Right part
  96. */
  97. split(value: number): RangeTree {
  98. let leftChildLen: number = this.children.length;
  99. let mid: RangeTree | undefined;
  100. // TODO(perf): Binary search (check overhead)
  101. for (let i: number = 0; i < this.children.length; i++) {
  102. const child: RangeTree = this.children[i];
  103. if (child.start < value && value < child.end) {
  104. mid = child.split(value);
  105. leftChildLen = i + 1;
  106. break;
  107. } else if (child.start >= value) {
  108. leftChildLen = i;
  109. break;
  110. }
  111. }
  112. const rightLen: number = this.children.length - leftChildLen;
  113. const rightChildren: RangeTree[] = this.children.splice(leftChildLen, rightLen);
  114. if (mid !== undefined) {
  115. rightChildren.unshift(mid);
  116. }
  117. const result: RangeTree = new RangeTree(
  118. value,
  119. this.end,
  120. this.delta,
  121. rightChildren,
  122. );
  123. this.end = value;
  124. return result;
  125. }
  126. /**
  127. * Get the range coverages corresponding to the tree.
  128. *
  129. * The ranges are pre-order sorted.
  130. */
  131. toRanges(): RangeCov[] {
  132. const ranges: RangeCov[] = [];
  133. // Stack of parent trees and counts.
  134. const stack: [RangeTree, number][] = [[this, 0]];
  135. while (stack.length > 0) {
  136. const [cur, parentCount]: [RangeTree, number] = stack.pop()!;
  137. const count: number = parentCount + cur.delta;
  138. ranges.push({startOffset: cur.start, endOffset: cur.end, count});
  139. for (let i: number = cur.children.length - 1; i >= 0; i--) {
  140. stack.push([cur.children[i], count]);
  141. }
  142. }
  143. return ranges;
  144. }
  145. }