taxonium-component
Version:
React component for exploring large phylogenetic trees in the browser
393 lines (392 loc) • 15.8 kB
JavaScript
const c = class u {
/**
* Accept two comparable values and creates new instance of interval
* Predicate Interval.comparable_less(low, high) supposed to return true on these values
* @param low
* @param high
*/
constructor(t, e) {
this.low = t, this.high = e;
}
/**
* Clone interval
* @returns {Interval}
*/
clone() {
return new u(this.low, this.high);
}
/**
* Propery max returns clone of this interval
* @returns {Interval}
*/
get max() {
return this.clone();
}
/**
* Predicate returns true is this interval less than other interval
* @param other_interval
* @returns {boolean}
*/
less_than(t) {
return this.low < t.low || this.low === t.low && this.high < t.high;
}
/**
* Predicate returns true is this interval equals to other interval
* @param other_interval
* @returns {boolean}
*/
equal_to(t) {
return this.low === t.low && this.high === t.high;
}
/**
* Predicate returns true if this interval intersects other interval
* @param other_interval
* @returns {boolean}
*/
intersect(t) {
return !this.not_intersect(t);
}
/**
* Predicate returns true if this interval does not intersect other interval
* @param other_interval
* @returns {boolean}
*/
not_intersect(t) {
return this.high < t.low || t.high < this.low;
}
/**
* Returns new interval merged with other interval
* @param {Interval} other_interval - Other interval to merge with
* @returns {Interval}
*/
merge(t) {
return new u(
this.low === void 0 ? t.low : this.low < t.low ? this.low : t.low,
this.high === void 0 ? t.high : this.high > t.high ? this.high : t.high
);
}
/**
* Returns how key should return
*/
output() {
return [this.low, this.high];
}
/**
* Function returns maximum between two comparable values
* @param interval1
* @param interval2
* @returns {Interval}
*/
static comparable_max(t, e) {
return t.merge(e);
}
/**
* Predicate returns true if first value less than second value
* @param val1
* @param val2
* @returns {boolean}
*/
static comparable_less_than(t, e) {
return t < e;
}
}, s = 0, l = 1;
class h {
constructor(t = void 0, e = void 0, i = null, r = null, a = null, _ = l) {
if (this.left = i, this.right = r, this.parent = a, this.color = _, this.item = { key: t, value: e }, t && t instanceof Array && t.length === 2 && !Number.isNaN(t[0]) && !Number.isNaN(t[1])) {
let [n, o] = t;
n > o && ([n, o] = [o, n]), this.item.key = new c(n, o);
}
this.max = this.item.key ? this.item.key.max : void 0;
}
isNil() {
return this.item.key === void 0 && this.item.value === void 0 && this.left === null && this.right === null && this.color === l;
}
_value_less_than(t) {
return this.item.value && t.item.value && this.item.value.less_than ? this.item.value.less_than(t.item.value) : this.item.value < t.item.value;
}
less_than(t) {
return this.item.value === this.item.key && t.item.value === t.item.key ? this.item.key.less_than(t.item.key) : this.item.key.less_than(t.item.key) || this.item.key.equal_to(t.item.key) && this._value_less_than(t);
}
_value_equal(t) {
return this.item.value && t.item.value && this.item.value.equal_to ? this.item.value.equal_to(t.item.value) : this.item.value === t.item.value;
}
equal_to(t) {
return this.item.value === this.item.key && t.item.value === t.item.key ? this.item.key.equal_to(t.item.key) : this.item.key.equal_to(t.item.key) && this._value_equal(t);
}
intersect(t) {
return this.item.key.intersect(t.item.key);
}
copy_data(t) {
this.item.key = t.item.key, this.item.value = t.item.value;
}
update_max() {
if (this.max = this.item.key ? this.item.key.max : void 0, this.right && this.right.max) {
const t = this.item.key.constructor.comparable_max;
this.max = t(this.max, this.right.max);
}
if (this.left && this.left.max) {
const t = this.item.key.constructor.comparable_max;
this.max = t(this.max, this.left.max);
}
}
// Other_node does not intersect any node of left subtree, if this.left.max < other_node.item.key.low
not_intersect_left_subtree(t) {
const e = this.item.key.constructor.comparable_less_than;
let i = this.left.max.high !== void 0 ? this.left.max.high : this.left.max;
return e(i, t.item.key.low);
}
// Other_node does not intersect right subtree if other_node.item.key.high < this.right.key.low
not_intersect_right_subtree(t) {
const e = this.item.key.constructor.comparable_less_than;
let i = this.right.max.low !== void 0 ? this.right.max.low : this.right.item.key.low;
return e(t.item.key.high, i);
}
}
class f {
/**
* Construct new empty instance of IntervalTree
*/
constructor() {
this.root = null, this.nil_node = new h();
}
/**
* Returns number of items stored in the interval tree
* @returns {number}
*/
get size() {
let t = 0;
return this.tree_walk(this.root, () => t++), t;
}
/**
* Returns array of sorted keys in the ascending order
* @returns {Array}
*/
get keys() {
let t = [];
return this.tree_walk(this.root, (e) => t.push(
e.item.key.output ? e.item.key.output() : e.item.key
)), t;
}
/**
* Return array of values in the ascending keys order
* @returns {Array}
*/
get values() {
let t = [];
return this.tree_walk(this.root, (e) => t.push(e.item.value)), t;
}
/**
* Returns array of items (<key,value> pairs) in the ascended keys order
* @returns {Array}
*/
get items() {
let t = [];
return this.tree_walk(this.root, (e) => t.push({
key: e.item.key.output ? e.item.key.output() : e.item.key,
value: e.item.value
})), t;
}
/**
* Returns true if tree is empty
* @returns {boolean}
*/
isEmpty() {
return this.root == null || this.root === this.nil_node;
}
/**
* Clear tree
*/
clear() {
this.root = null;
}
/**
* Insert new item into interval tree
* @param {Interval} key - interval object or array of two numbers [low, high]
* @param {any} value - value representing any object (optional)
* @returns {Node} returns reference to inserted node as an object {key:interval, value: value}
*/
insert(t, e = t) {
if (t === void 0) return;
let i = new h(t, e, this.nil_node, this.nil_node, null, s);
return this.tree_insert(i), this.recalc_max(i), i;
}
/**
* Returns true if item {key,value} exist in the tree
* @param {Interval} key - interval correspondent to keys stored in the tree
* @param {any} value - value object to be checked
* @returns {boolean} true if item {key, value} exist in the tree, false otherwise
*/
exist(t, e = t) {
let i = new h(t, e);
return !!this.tree_search(this.root, i);
}
/**
* Remove entry {key, value} from the tree
* @param {Interval} key - interval correspondent to keys stored in the tree
* @param {any} value - value object
* @returns {boolean} true if item {key, value} deleted, false if not found
*/
remove(t, e = t) {
let i = new h(t, e), r = this.tree_search(this.root, i);
return r && this.tree_delete(r), r;
}
/**
* Returns array of entry values which keys intersect with given interval <br/>
* If no values stored in the tree, returns array of keys which intersect given interval
* @param {Interval} interval - search interval, or tuple [low, high]
* @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output
* @returns {Array}
*/
search(t, e = (i, r) => i === r ? r.output() : i) {
let i = new h(t), r = [];
return this.tree_search_interval(this.root, i, r), r.map((a) => e(a.item.value, a.item.key));
}
/**
* Returns true if intersection between given and any interval stored in the tree found
* @param {Interval} interval - search interval or tuple [low, high]
* @returns {boolean}
*/
intersect_any(t) {
let e = new h(t);
return this.tree_find_any_interval(this.root, e);
}
/**
* Tree visitor. For each node implement a callback function. <br/>
* Method calls a callback function with two parameters (key, value)
* @param visitor(key,value) - function to be called for each tree item
*/
forEach(t) {
this.tree_walk(this.root, (e) => t(e.item.key, e.item.value));
}
/**
* Value Mapper. Walk through every node and map node value to another value
* @param callback(value,key) - function to be called for each tree item
*/
map(t) {
const e = new f();
return this.tree_walk(this.root, (i) => e.insert(i.item.key, t(i.item.value, i.item.key))), e;
}
/**
* @param {Interval} interval - optional if the iterator is intended to start from the beginning
* @param outputMapperFn(value,key) - optional function that maps (value, key) to custom output
* @returns {Iterator}
*/
*iterate(t, e = (i, r) => i === r ? r.output() : i) {
let i;
for (t ? i = this.tree_search_nearest_forward(this.root, new h(t)) : this.root && (i = this.local_minimum(this.root)); i; )
yield e(i.item.value, i.item.key), i = this.tree_successor(i);
}
recalc_max(t) {
let e = t;
for (; e.parent != null; )
e.parent.update_max(), e = e.parent;
}
tree_insert(t) {
let e = this.root, i = null;
if (this.root == null || this.root === this.nil_node)
this.root = t;
else {
for (; e !== this.nil_node; )
i = e, t.less_than(e) ? e = e.left : e = e.right;
t.parent = i, t.less_than(i) ? i.left = t : i.right = t;
}
this.insert_fixup(t);
}
// After insertion insert_node may have red-colored parent, and this is a single possible violation
// Go upwords to the root and re-color until violation will be resolved
insert_fixup(t) {
let e, i;
for (e = t; e !== this.root && e.parent.color === s; )
e.parent === e.parent.parent.left ? (i = e.parent.parent.right, i.color === s ? (e.parent.color = l, i.color = l, e.parent.parent.color = s, e = e.parent.parent) : (e === e.parent.right && (e = e.parent, this.rotate_left(e)), e.parent.color = l, e.parent.parent.color = s, this.rotate_right(e.parent.parent))) : (i = e.parent.parent.left, i.color === s ? (e.parent.color = l, i.color = l, e.parent.parent.color = s, e = e.parent.parent) : (e === e.parent.left && (e = e.parent, this.rotate_right(e)), e.parent.color = l, e.parent.parent.color = s, this.rotate_left(e.parent.parent)));
this.root.color = l;
}
tree_delete(t) {
let e, i;
t.left === this.nil_node || t.right === this.nil_node ? e = t : e = this.tree_successor(t), e.left !== this.nil_node ? i = e.left : i = e.right, i.parent = e.parent, e === this.root ? this.root = i : (e === e.parent.left ? e.parent.left = i : e.parent.right = i, e.parent.update_max()), this.recalc_max(i), e !== t && (t.copy_data(e), t.update_max(), this.recalc_max(t)), /*fix_node != this.nil_node && */
e.color === l && this.delete_fixup(i);
}
delete_fixup(t) {
let e = t, i;
for (; e !== this.root && e.parent != null && e.color === l; )
e === e.parent.left ? (i = e.parent.right, i.color === s && (i.color = l, e.parent.color = s, this.rotate_left(e.parent), i = e.parent.right), i.left.color === l && i.right.color === l ? (i.color = s, e = e.parent) : (i.right.color === l && (i.color = s, i.left.color = l, this.rotate_right(i), i = e.parent.right), i.color = e.parent.color, e.parent.color = l, i.right.color = l, this.rotate_left(e.parent), e = this.root)) : (i = e.parent.left, i.color === s && (i.color = l, e.parent.color = s, this.rotate_right(e.parent), i = e.parent.left), i.left.color === l && i.right.color === l ? (i.color = s, e = e.parent) : (i.left.color === l && (i.color = s, i.right.color = l, this.rotate_left(i), i = e.parent.left), i.color = e.parent.color, e.parent.color = l, i.left.color = l, this.rotate_right(e.parent), e = this.root));
e.color = l;
}
tree_search(t, e) {
if (!(t == null || t === this.nil_node))
return e.equal_to(t) ? t : e.less_than(t) ? this.tree_search(t.left, e) : this.tree_search(t.right, e);
}
tree_search_nearest_forward(t, e) {
let i, r = t;
for (; r && r !== this.nil_node; )
r.less_than(e) ? r.intersect(e) ? (i = r, r = r.left) : r = r.right : ((!i || r.less_than(i)) && (i = r), r = r.left);
return i || null;
}
// Original search_interval method; container res support push() insertion
// Search all intervals intersecting given one
tree_search_interval(t, e, i) {
t != null && t !== this.nil_node && (t.left !== this.nil_node && !t.not_intersect_left_subtree(e) && this.tree_search_interval(t.left, e, i), t.intersect(e) && i.push(t), t.right !== this.nil_node && !t.not_intersect_right_subtree(e) && this.tree_search_interval(t.right, e, i));
}
tree_find_any_interval(t, e) {
let i = !1;
return t != null && t !== this.nil_node && (t.left !== this.nil_node && !t.not_intersect_left_subtree(e) && (i = this.tree_find_any_interval(t.left, e)), i || (i = t.intersect(e)), !i && t.right !== this.nil_node && !t.not_intersect_right_subtree(e) && (i = this.tree_find_any_interval(t.right, e))), i;
}
local_minimum(t) {
let e = t;
for (; e.left != null && e.left !== this.nil_node; )
e = e.left;
return e;
}
// not in use
local_maximum(t) {
let e = t;
for (; e.right != null && e.right !== this.nil_node; )
e = e.right;
return e;
}
tree_successor(t) {
let e, i, r;
if (t.right !== this.nil_node)
e = this.local_minimum(t.right);
else {
for (i = t, r = t.parent; r != null && r.right === i; )
i = r, r = r.parent;
e = r;
}
return e;
}
// | right-rotate(T,y) |
// y ---------------. x
// / \ / \
// x c left-rotate(T,x) a y
// / \ <--------------- / \
// a b b c
rotate_left(t) {
let e = t.right;
t.right = e.left, e.left !== this.nil_node && (e.left.parent = t), e.parent = t.parent, t === this.root ? this.root = e : t === t.parent.left ? t.parent.left = e : t.parent.right = e, e.left = t, t.parent = e, t != null && t !== this.nil_node && t.update_max(), e = t.parent, e != null && e !== this.nil_node && e.update_max();
}
rotate_right(t) {
let e = t.left;
t.left = e.right, e.right !== this.nil_node && (e.right.parent = t), e.parent = t.parent, t === this.root ? this.root = e : t === t.parent.left ? t.parent.left = e : t.parent.right = e, e.right = t, t.parent = e, t !== null && t !== this.nil_node && t.update_max(), e = t.parent, e != null && e !== this.nil_node && e.update_max();
}
tree_walk(t, e) {
t != null && t !== this.nil_node && (this.tree_walk(t.left, e), e(t), this.tree_walk(t.right, e));
}
/* Return true if all red nodes have exactly two black child nodes */
testRedBlackProperty() {
let t = !0;
return this.tree_walk(this.root, function(e) {
e.color === s && (e.left.color === l && e.right.color === l || (t = !1));
}), t;
}
/* Throw error if not every path from root to bottom has same black height */
testBlackHeightProperty(t) {
let e = 0, i = 0, r = 0;
if (t.color === l && e++, t.left !== this.nil_node ? i = this.testBlackHeightProperty(t.left) : i = 1, t.right !== this.nil_node ? r = this.testBlackHeightProperty(t.right) : r = 1, i !== r)
throw new Error("Red-black height property violated");
return e += i, e;
}
}
export {
f as I
};
//# sourceMappingURL=main-D1zX1xg0.js.map