270 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			270 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | /* | ||
|  |  * 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<K, V> implements haxe.Constraints.IMap<K, V> { | ||
|  | 	var root:TreeNode<K, V>; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		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<V> { | ||
|  | 		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<V> { | ||
|  | 		var ret = []; | ||
|  | 		iteratorLoop(root, ret); | ||
|  | 		return ret.iterator(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		See `Map.keyValueIterator` | ||
|  | 	**/ | ||
|  | 	@:runtime public inline function keyValueIterator():KeyValueIterator<K, V> { | ||
|  | 		return new haxe.iterators.MapKeyValueIterator(this); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Iterates over the keys of `this` BalancedTree. | ||
|  | 
 | ||
|  | 		This operation is performed in-order. | ||
|  | 	**/ | ||
|  | 	public function keys():Iterator<K> { | ||
|  | 		var ret = []; | ||
|  | 		keysLoop(root, ret); | ||
|  | 		return ret.iterator(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public function copy():BalancedTree<K, V> { | ||
|  | 		var copied = new BalancedTree<K, V>(); | ||
|  | 		copied.root = root; | ||
|  | 		return copied; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function setLoop(k:K, v:V, node:TreeNode<K, V>) { | ||
|  | 		if (node == null) | ||
|  | 			return new TreeNode<K, V>(null, k, v, null); | ||
|  | 		var c = compare(k, node.key); | ||
|  | 		return if (c == 0) new TreeNode<K, V>(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<K, V>) { | ||
|  | 		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<K,V>(node:TreeNode<K, V>, acc:Array<V>) { | ||
|  | 		if (node != null) { | ||
|  | 			iteratorLoop(node.left, acc); | ||
|  | 			acc.push(node.value); | ||
|  | 			iteratorLoop(node.right, acc); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function keysLoop(node:TreeNode<K, V>, acc:Array<K>) { | ||
|  | 		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<K, V>) { | ||
|  | 		return if (t == null) throw "Not_found"; else if (t.left == null) t; else minBinding(t.left); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function removeMinBinding(t:TreeNode<K, V>) { | ||
|  | 		return if (t.left == null) t.right; else balance(removeMinBinding(t.left), t.key, t.value, t.right); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function balance(l:TreeNode<K, V>, k:K, v:V, r:TreeNode<K, V>):TreeNode<K, V> { | ||
|  | 		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<K, V>(l.left, l.key, l.value, new TreeNode<K, V>(l.right, k, v, r)); | ||
|  | 			else | ||
|  | 				new TreeNode<K, V>(new TreeNode<K, V>(l.left, l.key, l.value, l.right.left), l.right.key, l.right.value, | ||
|  | 					new TreeNode<K, V>(l.right.right, k, v, r)); | ||
|  | 		} else if (hr > hl + 2) { | ||
|  | 			if (r.right.get_height() > r.left.get_height()) | ||
|  | 				new TreeNode<K, V>(new TreeNode<K, V>(l, k, v, r.left), r.key, r.value, r.right); | ||
|  | 			else | ||
|  | 				new TreeNode<K, V>(new TreeNode<K, V>(l, k, v, r.left.left), r.left.key, r.left.value, | ||
|  | 					new TreeNode<K, V>(r.left.right, r.key, r.value, r.right)); | ||
|  | 		} else { | ||
|  | 			new TreeNode<K, V>(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<K, V> { | ||
|  | 	public var left:TreeNode<K, V>; | ||
|  | 	public var right:TreeNode<K, V>; | ||
|  | 	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()); | ||
|  | 	} | ||
|  | } |