node-red-contrib-tak-registration
Version:
A Node-RED node to register to TAK and to help wrap files as datapackages to send to TAK
680 lines (554 loc) • 15.6 kB
text/typescript
import Node from './node';
import { Key, Value } from './types';
export type Comparator<Key> = (a:Key, b:Key) => number;
export type Visitor<Key, Value> = (node:Node<Key, Value>) => void;
export type NodePrinter<Key, Value> = (node:Node<Key, Value>) => string;
/* follows "An implementation of top-down splaying"
* by D. Sleator <sleator@cs.cmu.edu> March 1992
*/
function DEFAULT_COMPARE (a:Key, b:Key) : number {
return a > b ? 1 : a < b ? -1 : 0;
}
type TreeNodeList<Key, Value> = { head:Node<Key, Value>|null };
/**
* Simple top down splay, not requiring i to be in the tree t.
*/
function splay (i:Key, t:Node<Key, Value>|null, comparator:Comparator<Key>) : Node<Key, Value> {
const N = new Node(null, null);
let l = N;
let r = N;
while (true) {
const cmp = comparator(i, t.key);
//if (i < t.key) {
if (cmp < 0) {
if (t.left === null) break;
//if (i < t.left.key) {
if (comparator(i, t.left.key) < 0) {
const y = t.left; /* rotate right */
t.left = y.right;
y.right = t;
t = y;
if (t.left === null) break;
}
r.left = t; /* link right */
r = t;
t = t.left;
//} else if (i > t.key) {
} else if (cmp > 0) {
if (t.right === null) break;
//if (i > t.right.key) {
if (comparator(i, t.right.key) > 0) {
const y = t.right; /* rotate left */
t.right = y.left;
y.left = t;
t = y;
if (t.right === null) break;
}
l.right = t; /* link left */
l = t;
t = t.right;
} else break;
}
/* assemble */
l.right = t.left;
r.left = t.right;
t.left = N.right;
t.right = N.left;
return t;
}
function insert (
i:Key, data:Value,
t:Node<Key, Value>,
comparator:Comparator<Key>,
) : Node<Key, Value> {
const node = new Node(i, data);
if (t === null) {
node.left = node.right = null;
return node;
}
t = splay(i, t, comparator);
const cmp = comparator(i, t.key);
if (cmp < 0) {
node.left = t.left;
node.right = t;
t.left = null;
} else if (cmp >= 0) {
node.right = t.right;
node.left = t;
t.right = null;
}
return node;
}
function split (key:Key, v:Node<Key, Value>, comparator:Comparator<Key>) : {
left:Node<Key, Value>|null,
right:Node<Key, Value>|null,
} {
let left = null;
let right = null;
if (v) {
v = splay(key, v, comparator);
const cmp = comparator(v.key, key);
if (cmp === 0) {
left = v.left;
right = v.right;
} else if (cmp < 0) {
right = v.right;
v.right = null;
left = v;
} else {
left = v.left;
v.left = null;
right = v;
}
}
return { left, right };
}
function merge (
left:Node<Key, Value>|null,
right:Node<Key, Value>|null,
comparator:Comparator<Key>,
) {
if (right === null) return left;
if (left === null) return right;
right = splay(left.key, right, comparator);
right.left = left;
return right;
}
type StringCollector = (s:string) => void;
/**
* Prints level of the tree
*/
function printRow (
root:Node<Key, Value>,
prefix:string,
isTail:boolean,
out:StringCollector,
printNode:NodePrinter<Key, Value>,
) {
if (root) {
out(`${ prefix }${ isTail ? '└── ' : '├── ' }${ printNode(root) }\n`);
const indent = prefix + (isTail ? ' ' : '│ ');
if (root.left) printRow(root.left, indent, false, out, printNode);
if (root.right) printRow(root.right, indent, true, out, printNode);
}
}
export default class Tree<Key=number, Value=any> {
private _comparator:Comparator<Key>;
private _root:Node<Key, Value>|null = null;
private _size:number = 0;
constructor (comparator = DEFAULT_COMPARE) {
this._comparator = comparator;
}
/**
* Inserts a key, allows duplicates
*/
public insert (key:Key, data?:Value) : Node<Key, Value> {
this._size++;
return this._root = insert(key, data, this._root, this._comparator);
}
/**
* Adds a key, if it is not present in the tree
*/
public add (key:Key, data?:Value) : Node<Key, Value> {
const node = new Node(key, data);
if (this._root === null) {
node.left = node.right = null;
this._size++;
this._root = node;
}
const comparator = this._comparator;
const t = splay(key, this._root, comparator);
const cmp = comparator(key, t.key);
if (cmp === 0) this._root = t;
else {
if (cmp < 0) {
node.left = t.left;
node.right = t;
t.left = null;
} else if (cmp > 0) {
node.right = t.right;
node.left = t;
t.right = null;
}
this._size++;
this._root = node;
}
return this._root;
}
/**
* @param {Key} key
* @return {Node|null}
*/
public remove (key:Key) : void {
this._root = this._remove(key, this._root, this._comparator);
}
/**
* Deletes i from the tree if it's there
*/
private _remove (
i:Key, t:Node<Key, Value>,
comparator:Comparator<Key>) : Node<Key, Value> {
let x;
if (t === null) return null;
t = splay(i, t, comparator);
const cmp = comparator(i, t.key);
if (cmp === 0) { /* found it */
if (t.left === null) {
x = t.right;
} else {
x = splay(i, t.left, comparator);
x.right = t.right;
}
this._size--;
return x;
}
return t; /* It wasn't there */
}
/**
* Removes and returns the node with smallest key
*/
public pop () : { key:Key, data:Value }|null {
let node = this._root;
if (node) {
while (node.left) node = node.left;
this._root = splay(node.key, this._root, this._comparator);
this._root = this._remove(node.key, this._root, this._comparator);
return { key: node.key, data: node.data };
}
return null;
}
/**
* Find without splaying
*/
public findStatic (key:Key) : Node<Key, Value>|null {
let current = this._root;
const compare = this._comparator;
while (current) {
const cmp = compare(key, current.key);
if (cmp === 0) return current;
else if (cmp < 0) current = current.left;
else current = current.right;
}
return null;
}
public find (key:Key) : Node<Key, Value>|null {
if (this._root) {
this._root = splay(key, this._root, this._comparator);
if (this._comparator(key, this._root.key) !== 0) return null;
}
return this._root;
}
public contains (key:Key) : boolean {
let current = this._root;
const compare = this._comparator;
while (current) {
const cmp = compare(key, current.key);
if (cmp === 0) return true;
else if (cmp < 0) current = current.left;
else current = current.right;
}
return false;
}
public forEach (visitor:Visitor<Key, Value>, ctx?:any) : Tree<Key, Value> {
let current = this._root;
const Q = []; /* Initialize stack s */
let done = false;
while (!done) {
if (current !== null) {
Q.push(current);
current = current.left;
} else {
if (Q.length !== 0) {
current = Q.pop();
visitor.call(ctx, current);
current = current.right;
} else done = true;
}
}
return this;
}
/**
* Walk key range from `low` to `high`. Stops if `fn` returns a value.
*/
public range (low:Key, high:Key, fn:Visitor<Key, Value>, ctx?:any) : Tree<Key, Value> {
const Q = [];
const compare = this._comparator;
let node = this._root;
let cmp;
while (Q.length !== 0 || node) {
if (node) {
Q.push(node);
node = node.left;
} else {
node = Q.pop();
cmp = compare(node.key, high);
if (cmp > 0) {
break;
} else if (compare(node.key, low) >= 0) {
if (fn.call(ctx, node)) return this; // stop if smth is returned
}
node = node.right;
}
}
return this;
}
/**
* Returns array of keys
*/
public keys () : Key[] {
const keys:Key[] = [];
this.forEach(({ key }) => keys.push(key));
return keys;
}
/**
* Returns array of all the data in the nodes
*/
public values () : Value[] {
const values:Value[] = [];
this.forEach(({ data }) => values.push(data));
return values;
}
public min() : Key|null {
if (this._root) return this.minNode(this._root).key;
return null;
}
public max() : Key|null {
if (this._root) return this.maxNode(this._root).key;
return null;
}
public minNode(t = this._root) : Node<Key, Value> | null {
if (t) while (t.left) t = t.left;
return t;
}
public maxNode(t = this._root) : Node<Key, Value> | null {
if (t) while (t.right) t = t.right;
return t;
}
/**
* Returns node at given index
*/
public at (index:number) : Node<Key, Value>|null {
let current = this._root;
let done = false;
let i = 0;
const Q = [];
while (!done) {
if (current) {
Q.push(current);
current = current.left;
} else {
if (Q.length > 0) {
current = Q.pop();
if (i === index) return current;
i++;
current = current.right;
} else done = true;
}
}
return null;
}
public next (d:Node<Key, Value>) : Node<Key, Value>|null {
let root = this._root;
let successor = null;
if (d.right) {
successor = d.right;
while (successor.left) successor = successor.left;
return successor;
}
const comparator = this._comparator;
while (root) {
const cmp = comparator(d.key, root.key);
if (cmp === 0) break;
else if (cmp < 0) {
successor = root;
root = root.left;
} else root = root.right;
}
return successor;
}
public prev (d:Node<Key, Value>) : Node<Key, Value>|null {
let root = this._root;
let predecessor = null;
if (d.left !== null) {
predecessor = d.left;
while (predecessor.right) predecessor = predecessor.right;
return predecessor;
}
const comparator = this._comparator;
while (root) {
const cmp = comparator(d.key, root.key);
if (cmp === 0) break;
else if (cmp < 0) root = root.left;
else {
predecessor = root;
root = root.right;
}
}
return predecessor;
}
public clear () : Tree<Key, Value> {
this._root = null;
this._size = 0;
return this;
}
public toList() {
return toList(this._root);
}
/**
* Bulk-load items. Both array have to be same size
*/
public load (keys:Key[], values:Value[] = [], presort:boolean = false) {
let size = keys.length;
const comparator = this._comparator;
// sort if needed
if (presort) sort(keys, values, 0, size - 1, comparator);
if (this._root === null) { // empty tree
this._root = loadRecursive(keys, values, 0, size);
this._size = size;
} else { // that re-builds the whole tree from two in-order traversals
const mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
size = this._size + size;
this._root = sortedListToBST({ head: mergedList }, 0, size);
}
return this;
}
public isEmpty() : boolean { return this._root === null; }
get size () : number { return this._size; }
get root () : Node<Key, Value>|null { return this._root; }
public toString (printNode:NodePrinter<Key, Value> = (n) => String(n.key)) : string {
const out:string[] = [];
printRow(this._root, '', true, (v) => out.push(v), printNode);
return out.join('');
}
public update (key:Key, newKey:Key, newData?:Value) : void {
const comparator = this._comparator;
let { left, right } = split(key, this._root, comparator);
if (comparator(key, newKey) < 0) {
right = insert(newKey, newData, right, comparator);
} else {
left = insert(newKey, newData, left, comparator);
}
this._root = merge(left, right, comparator);
}
public split(key:Key) {
return split(key, this._root, this._comparator);
}
*[Symbol.iterator]() {
let current = this._root;
const Q: Node<Key, Value>[] = []; /* Initialize stack s */
let done = false;
while (!done) {
if (current !== null) {
Q.push(current);
current = current.left;
} else {
if (Q.length !== 0) {
current = Q.pop()!;
yield current;
current = current.right;
} else done = true;
}
}
}
}
function loadRecursive (keys:Key[], values:Value[], start:number, end:number) : Node<Key, Value>|null {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const key = keys[middle];
const data = values[middle];
const node = new Node(key, data);
node.left = loadRecursive(keys, values, start, middle);
node.right = loadRecursive(keys, values, middle + 1, end);
return node;
}
return null;
}
function createList(keys:Key[], values:Value[]) : Node<Key, Value> {
const head = new Node<Key, Value>(null, null);
let p:Node<Key, Value> = head;
for (let i = 0; i < keys.length; i++) {
p = p.next = new Node(keys[i], values[i]);
}
p.next = null;
return head.next;
}
function toList (root:Node<Key, Value>) : Node<Key, Value> {
let current = root;
const Q = [];
let done = false;
const head = new Node<Key, Value>(null, null);
let p = head;
while (!done) {
if (current) {
Q.push(current);
current = current.left;
} else {
if (Q.length > 0) {
current = p = p.next = Q.pop();
current = current.right;
} else done = true;
}
}
p.next = null; // that'll work even if the tree was empty
return head.next;
}
function sortedListToBST(list:TreeNodeList<Key, Value>, start:number, end:number) : Node<Key, Value> {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const left = sortedListToBST(list, start, middle);
const root = list.head;
root.left = left;
list.head = list.head.next;
root.right = sortedListToBST(list, middle + 1, end);
return root;
}
return null;
}
function mergeLists<Key, Value> (
l1:Node<Key, Value>, l2:Node<Key, Value>,
compare:Comparator<Key>) : Node<Key, Value> {
const head:Node<Key, Value> = new Node<Key, Value>(null, null); // dummy
let p = head;
let p1:Node<Key, Value> = l1;
let p2:Node<Key, Value> = l2;
while (p1 !== null && p2 !== null) {
if (compare(p1.key, p2.key) < 0) {
p.next = p1;
p1 = p1.next;
} else {
p.next = p2;
p2 = p2.next;
}
p = p.next;
}
if (p1 !== null) {
p.next = p1;
} else if (p2 !== null) {
p.next = p2;
}
return head.next;
}
function sort(
keys:Key[], values:Value[],
left:number, right:number, compare:Comparator<Key>,
) {
if (left >= right) return;
const pivot = keys[(left + right) >> 1];
let i = left - 1;
let j = right + 1;
while (true) {
do i++; while (compare(keys[i], pivot) < 0);
do j--; while (compare(keys[j], pivot) > 0);
if (i >= j) break;
let tmp = keys[i];
keys[i] = keys[j];
keys[j] = tmp;
tmp = values[i];
values[i] = values[j];
values[j] = tmp;
}
sort(keys, values, left, j, compare);
sort(keys, values, j + 1, right, compare);
}