/* * Copyright (C)2005-2019 Haxe Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package haxe.ds; /** BalancedTree allows key-value mapping with arbitrary keys, as long as they can be ordered. By default, `Reflect.compare` is used in the `compare` method, which can be overridden in subclasses. Operations have a logarithmic average and worst-case cost. Iteration over keys and values, using `keys` and `iterator` respectively, are in-order. **/ class BalancedTree implements haxe.Constraints.IMap { var root:TreeNode; /** Creates a new BalancedTree, which is initially empty. **/ public function new() {} /** Binds `key` to `value`. If `key` is already bound to a value, that binding disappears. If `key` is null, the result is unspecified. **/ public function set(key:K, value:V) { root = setLoop(key, value, root); } /** Returns the value `key` is bound to. If `key` is not bound to any value, `null` is returned. If `key` is null, the result is unspecified. **/ public function get(key:K):Null { var node = root; while (node != null) { var c = compare(key, node.key); if (c == 0) return node.value; if (c < 0) node = node.left; else node = node.right; } return null; } /** Removes the current binding of `key`. If `key` has no binding, `this` BalancedTree is unchanged and false is returned. Otherwise the binding of `key` is removed and true is returned. If `key` is null, the result is unspecified. **/ public function remove(key:K) { try { root = removeLoop(key, root); return true; } catch (e:String) { return false; } } /** Tells if `key` is bound to a value. This method returns true even if `key` is bound to null. If `key` is null, the result is unspecified. **/ public function exists(key:K) { var node = root; while (node != null) { var c = compare(key, node.key); if (c == 0) return true; else if (c < 0) node = node.left; else node = node.right; } return false; } /** Iterates over the bound values of `this` BalancedTree. This operation is performed in-order. **/ public function iterator():Iterator { var ret = []; iteratorLoop(root, ret); return ret.iterator(); } /** See `Map.keyValueIterator` **/ @:runtime public inline function keyValueIterator():KeyValueIterator { return new haxe.iterators.MapKeyValueIterator(this); } /** Iterates over the keys of `this` BalancedTree. This operation is performed in-order. **/ public function keys():Iterator { var ret = []; keysLoop(root, ret); return ret.iterator(); } public function copy():BalancedTree { var copied = new BalancedTree(); copied.root = root; return copied; } function setLoop(k:K, v:V, node:TreeNode) { if (node == null) return new TreeNode(null, k, v, null); var c = compare(k, node.key); return if (c == 0) new TreeNode(node.left, k, v, node.right, node.get_height()); else if (c < 0) { var nl = setLoop(k, v, node.left); balance(nl, node.key, node.value, node.right); } else { var nr = setLoop(k, v, node.right); balance(node.left, node.key, node.value, nr); } } function removeLoop(k:K, node:TreeNode) { if (node == null) throw "Not_found"; var c = compare(k, node.key); return if (c == 0) merge(node.left, node.right); else if (c < 0) balance(removeLoop(k, node.left), node.key, node.value, node.right); else balance(node.left, node.key, node.value, removeLoop(k, node.right)); } static function iteratorLoop(node:TreeNode, acc:Array) { if (node != null) { iteratorLoop(node.left, acc); acc.push(node.value); iteratorLoop(node.right, acc); } } function keysLoop(node:TreeNode, acc:Array) { if (node != null) { keysLoop(node.left, acc); acc.push(node.key); keysLoop(node.right, acc); } } function merge(t1, t2) { if (t1 == null) return t2; if (t2 == null) return t1; var t = minBinding(t2); return balance(t1, t.key, t.value, removeMinBinding(t2)); } function minBinding(t:TreeNode) { return if (t == null) throw "Not_found"; else if (t.left == null) t; else minBinding(t.left); } function removeMinBinding(t:TreeNode) { return if (t.left == null) t.right; else balance(removeMinBinding(t.left), t.key, t.value, t.right); } function balance(l:TreeNode, k:K, v:V, r:TreeNode):TreeNode { var hl = l.get_height(); var hr = r.get_height(); return if (hl > hr + 2) { if (l.left.get_height() >= l.right.get_height()) new TreeNode(l.left, l.key, l.value, new TreeNode(l.right, k, v, r)); else new TreeNode(new TreeNode(l.left, l.key, l.value, l.right.left), l.right.key, l.right.value, new TreeNode(l.right.right, k, v, r)); } else if (hr > hl + 2) { if (r.right.get_height() > r.left.get_height()) new TreeNode(new TreeNode(l, k, v, r.left), r.key, r.value, r.right); else new TreeNode(new TreeNode(l, k, v, r.left.left), r.left.key, r.left.value, new TreeNode(r.left.right, r.key, r.value, r.right)); } else { new TreeNode(l, k, v, r, (hl > hr ? hl : hr) + 1); } } function compare(k1:K, k2:K) { return Reflect.compare(k1, k2); } public function toString() { return root == null ? '{}' : '{${root.toString()}}'; } /** Removes all keys from `this` BalancedTree. **/ public function clear():Void { root = null; } } /** A tree node of `haxe.ds.BalancedTree`. **/ class TreeNode { public var left:TreeNode; public var right:TreeNode; public var key:K; public var value:V; var _height:Int; public function new(l, k, v, r, h = -1) { left = l; key = k; value = v; right = r; if (h == -1) _height = (left.get_height() > right.get_height() ? left.get_height() : right.get_height()) + 1; else _height = h; } extern public inline function get_height() return this == null ? 0 : _height; public function toString() { return (left == null ? "" : left.toString() + ", ") + '$key=$value' + (right == null ? "" : ", " + right.toString()); } }