View Javadoc

1   /*--------------------------------------------------------------------------
2    *  Copyright 2009 utgenome.org
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *--------------------------------------------------------------------------*/
16  //--------------------------------------
17  // utgb-core Project
18  //
19  // PrioritySearchTree.java
20  // Since: Dec 3, 2009
21  //
22  // $URL$ 
23  // $Author$
24  //--------------------------------------
25  package org.utgenome.gwt.utgb.client.canvas;
26  
27  import java.util.ArrayList;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Stack;
31  
32  /**
33   * Priority search tree for efficient 2D search
34   * 
35   * @author leo
36   * 
37   */
38  public class PrioritySearchTree<E> implements Iterable<E> {
39  
40  	public class Node {
41  		public E elem;
42  		public int x;
43  		public int y;
44  		public int splitX;
45  		public Node left;
46  		public Node right;
47  
48  		public Node(E node, int x, int y) {
49  			if (node == null)
50  				throw new NullPointerException("node cannot be null");
51  			this.elem = node;
52  			this.x = x;
53  			this.y = y;
54  		}
55  
56  		public void swap(Node n) {
57  			// swap x
58  			int tmpX = this.x;
59  			this.x = n.x;
60  			n.x = tmpX;
61  
62  			// swap y
63  			int tmpY = this.y;
64  			this.y = n.y;
65  			n.y = tmpY;
66  
67  			// swap node
68  			E tmpNode = this.elem;
69  			this.elem = n.elem;
70  			n.elem = tmpNode;
71  		}
72  
73  		public void replaceWith(Node n) {
74  			this.x = n.x;
75  			this.y = n.y;
76  			this.elem = n.elem;
77  		}
78  	}
79  
80  	private Node root = null;
81  	private int lowerBoundOfX = 0;
82  	private int upperBoundOfX = Integer.MAX_VALUE;
83  	private int lowerBoundOfY = 0;
84  	private int uppperBoundOfY = Integer.MAX_VALUE;
85  	private int nodeCount = 0;
86  
87  	public PrioritySearchTree() {
88  
89  	}
90  
91  	public PrioritySearchTree(int lowerBoundOfX, int upperBoundOfX, int lowerBoundOfY, int upperBoundOfY) {
92  		this.lowerBoundOfX = lowerBoundOfX;
93  		this.upperBoundOfX = upperBoundOfX;
94  		this.lowerBoundOfY = lowerBoundOfY;
95  		this.uppperBoundOfY = upperBoundOfY;
96  	}
97  
98  	public static interface Visitor<E> {
99  		public void visit(E visit);
100 	}
101 
102 	private class DFSIterator implements Iterator<E> {
103 
104 		private Stack<Node> nodeStack = new Stack<Node>();
105 
106 		public DFSIterator(Node root) {
107 			if (root != null)
108 				nodeStack.push(root);
109 		}
110 
111 		public boolean hasNext() {
112 			return !nodeStack.isEmpty();
113 		}
114 
115 		public E next() {
116 			if (nodeStack.isEmpty())
117 				return null;
118 
119 			Node nextNode = nodeStack.pop();
120 			if (nextNode.right != null)
121 				nodeStack.push(nextNode.right);
122 			if (nextNode.left != null)
123 				nodeStack.push(nextNode.left);
124 
125 			return nextNode.elem;
126 		}
127 
128 		public void remove() {
129 			throw new UnsupportedOperationException("remove");
130 		}
131 
132 	}
133 
134 	public Iterator<E> iterator() {
135 		return new DFSIterator(root);
136 	}
137 
138 	public void depthFirstSearch(Visitor<E> visitor) {
139 		dfs(root, visitor);
140 	}
141 
142 	private void dfs(Node current, Visitor<E> visitor) {
143 		if (current == null)
144 			return;
145 		visitor.visit(current.elem);
146 		dfs(current.left, visitor);
147 		dfs(current.right, visitor);
148 	}
149 
150 	private static class QueryBox {
151 		public final int x1;
152 		public final int x2;
153 		public final int upperY;
154 
155 		public QueryBox(int x1, int x2, int upperY) {
156 			this.x1 = x1;
157 			this.x2 = x2;
158 			this.upperY = upperY;
159 		}
160 
161 	}
162 
163 	public void clear() {
164 		root = null;
165 		nodeCount = 0;
166 	}
167 
168 	public int size() {
169 		return nodeCount;
170 	}
171 
172 	/**
173 	 * Retrieves elements contained in the specified range, (X:[x1, x2], Y:[ , upperY]). This query is useful for
174 	 * answering the interval intersection problem.
175 	 * 
176 	 * @param x1
177 	 * @param x2
178 	 * @param upperY
179 	 * @return elements contained in the range (X:[x1, x2], Y:[ , upperY])
180 	 */
181 	public List<E> rangeQuery(int x1, int x2, int upperY) {
182 		ArrayList<E> result = new ArrayList<E>();
183 		ResultCollector<E> resultCollector = new ResultCollector<E>();
184 		rangeQuery_internal(root, new QueryBox(x1, x2, upperY), x1, x2, resultCollector);
185 		return resultCollector.result;
186 	}
187 
188 	public void rangeQuery(int x1, int x2, int upperY, ResultHandler<E> handler) {
189 		rangeQuery_internal(root, new QueryBox(x1, x2, upperY), x1, x2, handler);
190 	}
191 
192 	public static interface ResultHandler<E> {
193 		public void handle(E elem);
194 
195 		public boolean toContinue();
196 	}
197 
198 	private static class ResultCollector<E> implements ResultHandler<E> {
199 
200 		public List<E> result = new ArrayList<E>();
201 
202 		public void handle(E elem) {
203 			result.add(elem);
204 		}
205 
206 		public boolean toContinue() {
207 			return true;
208 		}
209 
210 	}
211 
212 	boolean rangeQuery_internal(Node currentNode, QueryBox queryBox, int rangeX1, int rangeX2, ResultHandler<E> resultHandler) {
213 		boolean toContinue = resultHandler.toContinue();
214 		if (!toContinue)
215 			return false;
216 
217 		if (currentNode != null) {
218 			if (currentNode.y <= queryBox.upperY) {
219 				// the current node is within the y constraint
220 				if (queryBox.x1 <= currentNode.x && currentNode.x <= queryBox.x2) {
221 					// The current node is contained in the query box
222 					resultHandler.handle(currentNode.elem);
223 					toContinue = resultHandler.toContinue();
224 				}
225 
226 				// search the descendant nodes
227 				int middleX = currentNode.splitX;
228 
229 				// search the left tree
230 				if (toContinue && queryBox.x1 < middleX) {
231 					toContinue = rangeQuery_internal(currentNode.left, queryBox, rangeX1, middleX, resultHandler);
232 				}
233 
234 				// search the right tree
235 				if (toContinue && middleX <= queryBox.x2) {
236 					toContinue = rangeQuery_internal(currentNode.right, queryBox, middleX, rangeX2, resultHandler);
237 				}
238 			}
239 		}
240 
241 		return toContinue;
242 
243 	}
244 
245 	/**
246 	 * Insert a new node
247 	 * 
248 	 * @param elem
249 	 */
250 	public void insert(E elem, int x, int y) {
251 		root = insert_internal(root, new Node(elem, x, y), lowerBoundOfX, upperBoundOfX);
252 	}
253 
254 	/**
255 	 * Remove the specified node
256 	 * 
257 	 * @param elem
258 	 * @return true if the specified element exists in the tree
259 	 */
260 	public boolean remove(E elem, int x, int y) {
261 		int prevNumNodes = size();
262 		root = remove_internal(root, new Node(elem, x, y), lowerBoundOfX, upperBoundOfX);
263 		return prevNumNodes != size();
264 	}
265 
266 	Node insert_internal(Node currentNode, Node insertNode, int lowerRangeOfX, int upperRangeOfX) {
267 		if (currentNode == null) {
268 			// empty leaf is found. Insert the new node here
269 			currentNode = insertNode;
270 			currentNode.splitX = (lowerRangeOfX + upperRangeOfX) / 2;
271 			nodeCount++;
272 		}
273 		else {
274 			if (insertNode.y < currentNode.y) {
275 				currentNode.swap(insertNode);
276 			}
277 
278 			if (insertNode.x < currentNode.splitX)
279 				currentNode.left = insert_internal(currentNode.left, insertNode, lowerRangeOfX, currentNode.splitX);
280 			else
281 				currentNode.right = insert_internal(currentNode.right, insertNode, currentNode.splitX, upperRangeOfX);
282 		}
283 
284 		return currentNode;
285 	}
286 
287 	Node remove_internal(Node currentNode, Node removeTarget, int x_lower, int x_upper) {
288 		if (currentNode == null) {
289 			// no node to delete
290 			return currentNode;
291 		}
292 
293 		if (currentNode.elem.equals(removeTarget.elem)) {
294 			// current node is the deletion target
295 			if (currentNode.left != null) {
296 				if (currentNode.right != null) {
297 					if (currentNode.left.y < currentNode.right.y) {
298 						// left node has lower Y than the right one
299 						currentNode.replaceWith(currentNode.left);
300 						currentNode.left = remove_internal(currentNode.left, currentNode.left, x_lower, x_upper);
301 					}
302 					else {
303 						// right node has lower Y than the left one
304 						currentNode.replaceWith(currentNode.right);
305 						currentNode.right = remove_internal(currentNode.right, currentNode.right, x_lower, x_upper);
306 					}
307 				}
308 				else {
309 					// only the left subtree exists
310 					currentNode.replaceWith(currentNode.left);
311 					currentNode.left = remove_internal(currentNode.left, currentNode.left, x_lower, x_upper);
312 				}
313 
314 			}
315 			else {
316 				if (currentNode.right != null) {
317 					// only the right subtree exists
318 					currentNode.replaceWith(currentNode.right);
319 					currentNode.right = remove_internal(currentNode.right, currentNode.right, x_lower, x_upper);
320 				}
321 				else {
322 					// no subtree exists, so delete the currentNode by returning null
323 					nodeCount--;
324 					return null;
325 				}
326 			}
327 		}
328 		else {
329 			// node to be deleted exists in one of the subtrees
330 			if (removeTarget.x < currentNode.splitX)
331 				currentNode.left = remove_internal(currentNode.left, removeTarget, x_lower, currentNode.splitX);
332 			else
333 				currentNode.right = remove_internal(currentNode.right, removeTarget, currentNode.splitX, x_upper);
334 		}
335 
336 		return currentNode;
337 	}
338 
339 }