1 module PixelPerfectEngine.system.binarySearchTree;
2 
3 /*
4  * Copyright (C) 2015-2018, by Laszlo Szeremi under the Boost license.
5  *
6  * Pixel Perfect Engine, system.transformFunctions module
7  */
8 
9 import core.stdc.stdlib;
10 
11 /**
12  * Implements a mostly @nogc-able container format, with relatively fast access times.
13  * Mostly based upon the AVT tree algorithm with small modifications, with the intent to replace D's default associative arrays for GC-free operations.
14  * TODO: Implement ordered tree traversal.
15  */
16 public struct BinarySearchTree(K, E){
17 	private BSTNode!(K, E)* root;
18 	//private sizediff_t bal;
19 	private size_t nOfElements;
20 	
21 	/*~this(){
22 		if(root !is null){
23 			BSTNode!(K, E)*[] nodes = collectNodes(root);
24 			foreach(n ; nodes){
25 				free(n);
26 			}
27 		}
28 	}*/
29 	/**
30 	 * Tree traversal for quick access.
31 	 * TODO: Make it NOGC
32 	 */
33 	public int opApply(scope int delegate(ref E) dg){
34 		int result;
35 		result = treeTraversalByDepth(dg, root);
36 		return result;
37 	}
38 	protected int treeTraversalByDepth(scope int delegate(ref E) dg, BSTNode!(K, E)* root){
39 		int result = dg(root.elem);
40 		if(result)
41 			return result;
42 		else{
43 			if(root.left !is null){
44 				result = treeTraversalByDepth(dg, root.left);
45 				if(result)
46 					return result;
47 			}
48 			if(root.right !is null){
49 				result = treeTraversalByDepth(dg, root.right);
50 				if(result)
51 					return result;
52 			}
53 		}
54 		return result;
55 	}
56 	/**
57 	 * Gets an element without allocation. Returns E.init if key not found.
58 	 */
59 	public @nogc E opIndex(K key){
60 		bool found;
61 		BSTNode!(K, E)* crnt = root;
62 		E result;
63 		do{
64 			if(crnt is null){
65 				found = true;
66 			}else if(crnt.key == key){
67 				found = true;
68 				result = crnt.elem;
69 			}else if(crnt.key > key){
70 				crnt = crnt.left;
71 			}else{
72 				crnt = crnt.right;
73 			}
74 		}while(!found);
75 		return result;
76 	}
77 	
78 	/**
79 	 * Inserts a new element with some automatic optimization.
80 	 * TODO: Make the optimization better.
81 	 */
82 	public @nogc E opIndexAssign(E value, K i){
83 		if(root is null){
84 			root = cast(BSTNode!(K, E)*)malloc(BSTNode!(K, E).sizeof);
85 			*root = BSTNode!(K, E)(i,value);
86 			nOfElements++;
87 			return value;
88 		}
89 		if(insertAt(&root, i, value)){
90 			rebalanceTree(&root);
91 		}
92 		nOfElements++;
93 		return value;
94 	}
95 	/**
96 	 * Rebalances a tree.
97 	 */
98 	public @nogc void rebalanceTree(){
99 		rebalanceTree(&root);
100 	}
101 	protected @nogc void rebalanceTree(BSTNode!(K, E)** node){
102 		if((*node).height > 1){
103 			if((*node).bal > 1 || (*node).bal < -1){
104 				optimize(node);
105 			}
106 			if((*node).left !is null){
107 				rebalanceTree(&(*node).left);
108 			}
109 			if((*node).right !is null){
110 				rebalanceTree(&(*node).right);
111 			}
112 		}
113 	}
114 	/**
115 	 * Returns the number of elements in the tree.
116 	 */
117 	public @nogc @property size_t length(){
118 		return nOfElements;
119 	}
120 	/**
121 	 * Inserts an item at the given point. Returns true if the height of the tree have been raised.
122 	 */
123 	protected @nogc bool insertAt(BSTNode!(K, E)** node, K key, E val){
124 		if(key == (*node).key){
125 			(*node).elem = val;
126 			return false;
127 		}else if(key < (*node).key){
128 			if((*node).left is null){
129 				(*node).left = cast(BSTNode!(K, E)*)malloc(BSTNode!(K, E).sizeof);
130 				*(*node).left = BSTNode!(K, E)(key,val);
131 				if((*node).right is null){
132 					return true;
133 				}else{
134 					return false;
135 				}
136 			}else{
137 				return insertAt(&((*node).left), key, val);
138 			}
139 		}else{
140 			if((*node).right is null){
141 				(*node).right = cast(BSTNode!(K, E)*)malloc(BSTNode!(K, E).sizeof);
142 				*(*node).right = BSTNode!(K, E)(key,val);
143 				if((*node).left is null){
144 					return true;
145 				}else{
146 					return false;
147 				}
148 			}else{
149 				return insertAt(&((*node).right), key, val);
150 			}
151 		}
152 	}
153 	/**
154 	 * Removes an element by key.
155 	 */
156 	public @nogc void remove(K key){
157 		nOfElements--;
158 		bool handside;
159 		BSTNode!(K, E)* crnt = root;
160 		BSTNode!(K, E)* prev;
161 		while(crnt !is null){
162 			if(crnt.key == key){
163 				if(crnt.left is null && crnt.right is null){
164 					free(crnt);
165 					if(prev !is null){
166 						if(handside)
167 							prev.right = null;
168 						else
169 							prev.left = null;
170 					}
171 					crnt = null;
172 				}else if(crnt.left is null){
173 					if(prev is null){
174 						root = crnt.right;
175 					}else{
176 						if(handside)
177 							prev.right = crnt.right;
178 						else
179 							prev.left = crnt.right;
180 					}
181 					free(crnt);
182 					crnt = null;
183 				}else if(crnt.right is null){
184 					if(prev is null){
185 						root = crnt.left;
186 					}else{
187 						if(handside)
188 							prev.right = crnt.left;
189 						else
190 							prev.left = crnt.left;
191 					}
192 					free(crnt);
193 					crnt = null;
194 				}else{
195 					BSTNode!(K, E)* temp;
196 					if(handside){
197 						temp = findMin(prev.right);
198 					}else{
199 						temp = findMax(prev.right);
200 					}
201 					K tempKey = temp.key;
202 					E tempVal = temp.elem;
203 					BSTNode!(K, E)* tempLHS = temp.left;
204 					BSTNode!(K, E)* tempRHS = temp.right;
205 					remove(tempKey);
206 					crnt.key = tempKey;
207 					crnt.elem = tempVal;
208 					crnt.left = tempLHS;
209 					crnt.right = tempRHS;
210 					return;
211 				}
212 			}else if(crnt.key > key){
213 				prev = crnt;
214 				handside = false;
215 				crnt = crnt.left;
216 			}else{
217 				prev = crnt;
218 				handside = true;
219 				crnt = crnt.right;
220 			}
221 		}
222 	}
223 	/**
224 	 * Rotates the subtree to the left by one.
225 	 */
226 	protected @nogc void rotateLeft(BSTNode!(K, E)** node){
227 		/*BSTNode!(K, E)* temp = (*node).right;
228 		(*node).right = temp.left;
229 		temp.left = *node;
230 		*node = temp;
231 		(*node).bal = 0;
232 		temp.bal = 0;*/
233 		BSTNode!(K, E)* temp = *node;	//save current node
234 		*node = temp.right;				//assign the first RHS to the root
235 		temp.right = (*node).left;		//assign the new root's LHS to the saved node's RHS
236 		(*node).left = temp;			//assign the saved node to the new root's LHS
237 		//(*node).bal = 0;				//reset new root's balance
238 		//temp.bal = 0;					//reset the new LHS's balance
239 	}
240 	/**
241 	 * Rotates the subtree to the right by one.
242 	 */
243 	protected @nogc void rotateRight(BSTNode!(K, E)** node){
244 		/*BSTNode!(K, E)* temp = (*node).left;
245 		(*node).left = temp.right;
246 		temp.right = *node;
247 		*node = temp;
248 		(*node).bal = 0;
249 		temp.bal = 0;*/
250 		BSTNode!(K, E)* temp = *node;	//save current node
251 		*node = temp.left;				//assign the first LHS to the root
252 		temp.left = (*node).right;		//assign the new root's RHS to the saved node's LHS
253 		(*node).right = temp;			//assign the saved node to the new root's RHS
254 		//(*node).bal = 0;				//reset new root's balance
255 		//temp.bal = 0;					//reset the new RHS's balance
256 	}
257 	/**
258 	 * Rotates the subtree to the right then to the left.
259 	 */
260 	protected @nogc void rotateRightLeft(BSTNode!(K, E)** node){
261 		BSTNode!(K, E)* temp = (*node).right.left;	//save root.RHS.LHS
262 		(*node).right.left = temp.right;			//assign temp.RHS to root.RHS.LHS
263 		temp.right = (*node).right;					//assign root.RHS to temp.RHS
264 		(*node).right = temp;						//assign temp to root.RHS
265 		//temp.right.bal = 0;							//reset balance of temp.RHS
266 		rotateLeft(node);							//rotate the root to the left
267 	}
268 	/**
269 	 * Rotates the subtree to the right then to the left.
270 	 */
271 	protected @nogc void rotateLeftRight(BSTNode!(K, E)** node){
272 		BSTNode!(K, E)* temp = (*node).left.right;	//save root.LHS.RHS
273 		(*node).left.right = temp.left;				//assign temp.LHS to root.LHS.RHS
274 		temp.left = (*node).left;					//assign root.LHS to temp.LHS
275 		(*node).left = temp;						//assign temp to root.LHS
276 		//temp.left.bal = 0;							//reset balance of temp.LHS
277 		rotateRight(node);							//rotate the root to the right
278 	}
279 	/**
280 	 * Optimizes a BinarySearchTree by distributing nodes evenly.
281 	 */
282 	protected @nogc void optimize(BSTNode!(K, E)** node){
283 		if((*node).bal >= 2){
284 			if((*node).right.bal >= 1){
285 				rotateLeft(node);
286 			}else if((*node).right.bal <= -1){
287 				rotateRightLeft(node);
288 			}
289 		}else if((*node).bal <= -2){
290 			if((*node).left.bal >= 1){
291 				rotateLeftRight(node);
292 			}else if((*node).left.bal <= -1){
293 				rotateRight(node);
294 			}
295 		}
296 		
297 	}
298 
299 	public BSTNode!(K, E)*[] collectNodes(BSTNode!(K, E)* source){
300 		BSTNode!(K, E)*[] result;
301 		if(source !is null){
302 			result ~= source;
303 			if(source.left !is null)
304 				result ~= collectNodes(source.left);
305 			if(source.right !is null)
306 				result ~= collectNodes(source.right);
307 		}
308 		return result;
309 	}
310 
311 	public @nogc BSTNode!(K, E)* findMin(){
312 		bool found;
313 		BSTNode!(K, E)* result = root;
314 		do{
315 			if(result.left is null)
316 				found = true;
317 			else
318 				result = result.left;
319 		}while(!found);
320 		return result;
321 	}
322 
323 	public @nogc BSTNode!(K, E)* findMin(BSTNode!(K, E)* from){
324 		bool found;
325 		BSTNode!(K, E)* result = from;
326 		do{
327 			if(result.left is null)
328 				found = true;
329 			else
330 				result = result.left;
331 		}while(!found);
332 		return result;
333 	}
334 
335 	public @nogc BSTNode!(K, E)* findMax(){
336 		bool found;
337 		BSTNode!(K, E)* result = root;
338 		do{
339 			if(result.right is null)
340 				found = true;
341 			else
342 				result = result.right;
343 		}while(!found);
344 		return result;
345 	}
346 
347 	public @nogc BSTNode!(K, E)* findMax(BSTNode!(K, E)* from){
348 		bool found;
349 		BSTNode!(K, E)* result = from;
350 		do{
351 			if(result.right is null)
352 				found = true;
353 			else
354 				result = result.right;
355 		}while(!found);
356 		return result;
357 	}
358 
359 	public string toString(){
360 		import std.conv;
361 		string result = "Key: " ~ K.stringof ~ " : Elem: " ~ E.stringof;
362 		if(root !is null)
363 			result ~= root.toString();
364 		return result;
365 	}
366 }
367 
368 public struct BSTNode(K, E){
369 	K key;
370 	E elem;
371 	BSTNode!(K, E)* left;		//lesser elements
372 	BSTNode!(K, E)* right;		//greater elements
373 
374 	@nogc this(K key, E elem, BSTNode!(K, E)* left = null, BSTNode!(K, E)* right = null){
375 		this.key = key;
376 		this.elem = elem;
377 		this.left = left;
378 		this.right = right;
379 	}
380 
381 	@nogc int opCmp(ref BSTNode!(K, E) rhs){
382 		return key - rhs.key;
383 	}
384 	/**
385 	 * Returns the total height.
386 	 */
387 	@nogc @property size_t height(){
388 		if(left is null && right is null){
389 			return 1;
390 		}else{
391 			size_t l = 1, r = 1;
392 			if(left !is null){
393 				l += left.height;
394 			}
395 			if(right !is null){
396 				r += right.height;
397 			}
398 			if(l >= r){
399 				return l;
400 			}else{
401 				return r;
402 			}
403 		}
404 	}
405 	/**
406 	 * Returns the balance of the node. Minus if LHS is heavier, plus if RHS is heavier.
407 	 */
408 	@nogc @property sizediff_t bal(){
409 		if(left is null && right is null){
410 			return 0;
411 		}else{
412 			size_t l, r;
413 			if(left !is null){
414 				l = left.height;
415 			}
416 			if(right !is null){
417 				r = right.height;
418 			}
419 			return r - l;
420 		}
421 	}
422 	/**
423 	 * String representation for debugging.
424 	 */
425 	public string toString(){
426 		import std.conv;
427 		string result = "[" ~ to!string(key) ~ ":" ~ to!string(elem) ~ ";" ~ to!string(bal) ~ ";";
428 		if(left is null){
429 			result ~= "lhs: null ";
430 		}else{
431 			result ~= " lhs: " ~ left.toString;
432 		}
433 		if(right is null){
434 			result ~= "rhs: null ";
435 		}else{
436 			result ~= " rhs: " ~ right.toString;
437 		}
438 		return result ~ "]";
439 	}
440 }