Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 

431 wiersze
8.9 KiB

'use strict';
import { Iterables } from './iterable';
import { Strings } from './string';
// Code stolen from https://github.com/Microsoft/vscode/blob/b3e6d5bb039a4a9362b52a2c8726267ca68cf64e/src/vs/base/common/map.ts#L352
const FIN = { done: true, value: undefined };
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
export interface IKeyIterator {
reset(key: string): this;
next(): this;
hasNext(): boolean;
cmp(a: string): number;
value(): string;
}
export class StringIterator implements IKeyIterator {
private _value: string = '';
private _pos: number = 0;
reset(key: string): this {
this._value = key;
this._pos = 0;
return this;
}
next(): this {
this._pos += 1;
return this;
}
hasNext(): boolean {
return this._pos < this._value.length - 1;
}
cmp(a: string): number {
const aCode = a.charCodeAt(0);
const thisCode = this._value.charCodeAt(this._pos);
return aCode - thisCode;
}
value(): string {
return this._value[this._pos];
}
}
export class PathIterator implements IKeyIterator {
private _value!: string;
private _from!: number;
private _to!: number;
reset(key: string): this {
this._value = key.replace(/\\$|\/$/, '');
this._from = 0;
this._to = 0;
return this.next();
}
hasNext(): boolean {
return this._to < this._value.length;
}
next(): this {
// this._data = key.split(/[\\/]/).filter(s => !!s);
this._from = this._to;
let justSeps = true;
for (; this._to < this._value.length; this._to++) {
const ch = this._value.charCodeAt(this._to);
if (ch === Strings.CharCode.Slash || ch === Strings.CharCode.Backslash) {
if (justSeps) {
this._from++;
} else {
break;
}
} else {
justSeps = false;
}
}
return this;
}
cmp(a: string): number {
let aPos = 0;
const aLen = a.length;
let thisPos = this._from;
while (aPos < aLen && thisPos < this._to) {
const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
if (cmp !== 0) {
return cmp;
}
aPos += 1;
thisPos += 1;
}
if (aLen === this._to - this._from) {
return 0;
}
if (aPos < aLen) {
return -1;
}
return 1;
}
value(): string {
return this._value.substring(this._from, this._to);
}
}
class TernarySearchTreeNode<E> {
segment!: string;
value: E | undefined;
key!: string;
left: TernarySearchTreeNode<E> | undefined;
mid: TernarySearchTreeNode<E> | undefined;
right: TernarySearchTreeNode<E> | undefined;
isEmpty(): boolean {
return !this.left && !this.mid && !this.right && !this.value;
}
}
export class TernarySearchTree<E> {
static forPaths<E>(): TernarySearchTree<E> {
return new TernarySearchTree<E>(new PathIterator());
}
static forStrings<E>(): TernarySearchTree<E> {
return new TernarySearchTree<E>(new StringIterator());
}
private _iter: IKeyIterator;
private _root: TernarySearchTreeNode<E> | undefined;
constructor(segments: IKeyIterator) {
this._iter = segments;
}
clear(): void {
this._root = undefined;
}
set(key: string, element: E): E | undefined {
const iter = this._iter.reset(key);
let node: TernarySearchTreeNode<E>;
if (!this._root) {
this._root = new TernarySearchTreeNode<E>();
this._root.segment = iter.value();
}
node = this._root;
while (true) {
const val = iter.cmp(node.segment);
if (val > 0) {
// left
if (!node.left) {
node.left = new TernarySearchTreeNode<E>();
node.left.segment = iter.value();
}
node = node.left;
} else if (val < 0) {
// right
if (!node.right) {
node.right = new TernarySearchTreeNode<E>();
node.right.segment = iter.value();
}
node = node.right;
} else if (iter.hasNext()) {
// mid
iter.next();
if (!node.mid) {
node.mid = new TernarySearchTreeNode<E>();
node.mid.segment = iter.value();
}
node = node.mid;
} else {
break;
}
}
const oldElement = node.value;
node.value = element;
node.key = key;
return oldElement;
}
get(key: string): E | undefined {
const iter = this._iter.reset(key);
let node = this._root;
while (node) {
const val = iter.cmp(node.segment);
if (val > 0) {
// left
node = node.left;
} else if (val < 0) {
// right
node = node.right;
} else if (iter.hasNext()) {
// mid
iter.next();
node = node.mid;
} else {
break;
}
}
return node ? node.value : undefined;
}
delete(key: string): void {
const iter = this._iter.reset(key);
const stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
let node = this._root;
// find and unset node
while (node) {
const val = iter.cmp(node.segment);
if (val > 0) {
// left
stack.push([1, node]);
node = node.left;
} else if (val < 0) {
// right
stack.push([-1, node]);
node = node.right;
} else if (iter.hasNext()) {
// mid
iter.next();
stack.push([0, node]);
node = node.mid;
} else {
// remove element
node.value = undefined;
// clean up empty nodes
while (stack.length > 0 && node.isEmpty()) {
const [dir, parent] = stack.pop()!;
switch (dir) {
case 1:
parent.left = undefined;
break;
case 0:
parent.mid = undefined;
break;
case -1:
parent.right = undefined;
break;
}
node = parent;
}
break;
}
}
}
findSubstr(key: string): E | undefined {
const iter = this._iter.reset(key);
let node = this._root;
let candidate: E | undefined;
while (node) {
const val = iter.cmp(node.segment);
if (val > 0) {
// left
node = node.left;
} else if (val < 0) {
// right
node = node.right;
} else if (iter.hasNext()) {
// mid
iter.next();
candidate = node.value || candidate;
node = node.mid;
} else {
break;
}
}
return (node && node.value) || candidate;
}
findSuperstr(key: string, limit: boolean = false): Iterable<E> | undefined {
const iter = this._iter.reset(key);
let node = this._root;
while (node) {
const val = iter.cmp(node.segment);
if (val > 0) {
// left
node = node.left;
} else if (val < 0) {
// right
node = node.right;
} else if (iter.hasNext()) {
// mid
iter.next();
node = node.mid;
} else {
// collect
if (!node.mid) {
return undefined;
}
node = node.mid;
return {
// eslint-disable-next-line no-loop-func
[Symbol.iterator]: () => this._nodeIterator(node!, limit)
};
}
}
return undefined;
}
private _nodeIterator(node: TernarySearchTreeNode<E>, limit: boolean = false): Iterator<E> {
let res: { done: false; value: E };
let idx: number;
let data: E[];
const next = (): IteratorResult<E> => {
if (!data) {
// lazy till first invocation
data = [];
idx = 0;
this._forEach(node, value => data.push(value), limit);
}
if (idx >= data.length) {
return (FIN as unknown) as IteratorResult<E>;
}
if (!res) {
res = { done: false, value: data[idx++] };
} else {
res.value = data[idx++];
}
return res;
};
return { next: next };
}
forEach(callback: (value: E, index: string) => any) {
this._forEach(this._root, callback);
}
private _forEach(
node: TernarySearchTreeNode<E> | undefined,
callback: (value: E, index: string) => any,
limit: boolean = false
) {
if (node === undefined) return;
// left
this._forEach(node.left, callback, limit);
// node
if (node.value) {
callback(node.value, node.key);
}
if (!limit) {
// mid
this._forEach(node.mid, callback, limit);
}
// right
this._forEach(node.right, callback, limit);
}
any(): boolean {
return this._root !== undefined && !this._root.isEmpty();
}
count(predicate?: (entry: E) => boolean): number {
if (this._root === undefined || this._root.isEmpty()) return 0;
return Iterables.count(this.entries(), predicate === undefined ? undefined : ([e]) => predicate(e));
}
entries(): Iterable<[E, string]> {
return this._iterator(this._root);
}
values(): Iterable<E> {
return Iterables.map(this.entries(), ([e]) => e);
}
highlander(): [E, string] | undefined {
if (this._root === undefined || this._root.isEmpty()) return undefined;
const entries = this.entries() as IterableIterator<[E, string]>;
let count = 0;
let next: IteratorResult<[E, string]>;
let value: [E, string] | undefined;
while (true) {
next = entries.next();
if (next.done) break;
value = next.value;
count++;
if (count > 1) return undefined;
}
return value;
}
some(predicate: (entry: E) => boolean): boolean {
if (this._root === undefined || this._root.isEmpty()) return false;
return Iterables.some(this.entries(), ([e]) => predicate(e));
}
private *_iterator(node: TernarySearchTreeNode<E> | undefined): IterableIterator<[E, string]> {
if (node !== undefined) {
// left
yield* this._iterator(node.left);
// node
if (node.value) {
yield [node.value, node.key];
}
// mid
yield* this._iterator(node.mid);
// right
yield* this._iterator(node.right);
}
}
}