| import { RangeCov } from "./types"; |
|
|
| export class RangeTree { |
| start: number; |
| end: number; |
| delta: number; |
| children: RangeTree[]; |
|
|
| constructor( |
| start: number, |
| end: number, |
| delta: number, |
| children: RangeTree[], |
| ) { |
| this.start = start; |
| this.end = end; |
| this.delta = delta; |
| this.children = children; |
| } |
|
|
| |
| |
| |
| static fromSortedRanges(ranges: ReadonlyArray<RangeCov>): RangeTree | undefined { |
| let root: RangeTree | undefined; |
| |
| const stack: [RangeTree, number][] = []; |
| for (const range of ranges) { |
| const node: RangeTree = new RangeTree(range.startOffset, range.endOffset, range.count, []); |
| if (root === undefined) { |
| root = node; |
| stack.push([node, range.count]); |
| continue; |
| } |
| let parent: RangeTree; |
| let parentCount: number; |
| while (true) { |
| [parent, parentCount] = stack[stack.length - 1]; |
| |
| if (range.startOffset < parent.end) { |
| break; |
| } else { |
| stack.pop(); |
| } |
| } |
| node.delta -= parentCount; |
| parent.children.push(node); |
| stack.push([node, range.count]); |
| } |
| return root; |
| } |
|
|
| normalize(): void { |
| const children: RangeTree[] = []; |
| let curEnd: number; |
| let head: RangeTree | undefined; |
| const tail: RangeTree[] = []; |
| for (const child of this.children) { |
| if (head === undefined) { |
| head = child; |
| } else if (child.delta === head.delta && child.start === curEnd!) { |
| tail.push(child); |
| } else { |
| endChain(); |
| head = child; |
| } |
| curEnd = child.end; |
| } |
| if (head !== undefined) { |
| endChain(); |
| } |
|
|
| if (children.length === 1) { |
| const child: RangeTree = children[0]; |
| if (child.start === this.start && child.end === this.end) { |
| this.delta += child.delta; |
| this.children = child.children; |
| |
| return; |
| } |
| } |
|
|
| this.children = children; |
|
|
| function endChain(): void { |
| if (tail.length !== 0) { |
| head!.end = tail[tail.length - 1].end; |
| for (const tailTree of tail) { |
| for (const subChild of tailTree.children) { |
| subChild.delta += tailTree.delta - head!.delta; |
| head!.children.push(subChild); |
| } |
| } |
| tail.length = 0; |
| } |
| head!.normalize(); |
| children.push(head!); |
| } |
| } |
|
|
| |
| |
| |
| |
| split(value: number): RangeTree { |
| let leftChildLen: number = this.children.length; |
| let mid: RangeTree | undefined; |
|
|
| |
| for (let i: number = 0; i < this.children.length; i++) { |
| const child: RangeTree = this.children[i]; |
| if (child.start < value && value < child.end) { |
| mid = child.split(value); |
| leftChildLen = i + 1; |
| break; |
| } else if (child.start >= value) { |
| leftChildLen = i; |
| break; |
| } |
| } |
|
|
| const rightLen: number = this.children.length - leftChildLen; |
| const rightChildren: RangeTree[] = this.children.splice(leftChildLen, rightLen); |
| if (mid !== undefined) { |
| rightChildren.unshift(mid); |
| } |
| const result: RangeTree = new RangeTree( |
| value, |
| this.end, |
| this.delta, |
| rightChildren, |
| ); |
| this.end = value; |
| return result; |
| } |
|
|
| |
| |
| |
| |
| |
| toRanges(): RangeCov[] { |
| const ranges: RangeCov[] = []; |
| |
| const stack: [RangeTree, number][] = [[this, 0]]; |
| while (stack.length > 0) { |
| const [cur, parentCount]: [RangeTree, number] = stack.pop()!; |
| const count: number = parentCount + cur.delta; |
| ranges.push({startOffset: cur.start, endOffset: cur.end, count}); |
| for (let i: number = cur.children.length - 1; i >= 0; i--) { |
| stack.push([cur.children[i], count]); |
| } |
| } |
| return ranges; |
| } |
| } |
|
|