View Javadoc

1   package net.obsearch.index.pptree.impl;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.nio.ByteBuffer;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.LinkedList;
9   import java.util.List;
10  
11  import net.obsearch.Index;
12  import net.obsearch.OperationStatus;
13  import net.obsearch.Status;
14  import net.obsearch.asserts.OBAsserts;
15  import net.obsearch.cache.OBCacheHandlerLong;
16  import net.obsearch.cache.OBCacheLong;
17  import net.obsearch.constants.OBSearchProperties;
18  import net.obsearch.exception.IllegalIdException;
19  import net.obsearch.exception.NotFrozenException;
20  import net.obsearch.exception.OBException;
21  import net.obsearch.exception.OBStorageException;
22  import net.obsearch.exception.OutOfRangeException;
23  import net.obsearch.filter.Filter;
24  import net.obsearch.index.pptree.AbstractPPTree;
25  import net.obsearch.index.pptree.SpaceTreeLeaf;
26  import net.obsearch.pivots.IncrementalPivotSelector;
27  import net.obsearch.storage.CloseIterator;
28  import net.obsearch.utils.bytes.ByteBufferFactoryConversion;
29  
30  import net.obsearch.index.IndexShort;
31  
32  import net.obsearch.ob.OBShort;
33  import net.obsearch.query.OBQueryShort;
34  import net.obsearch.result.OBPriorityQueueShort;
35  import net.obsearch.result.OBResultShort;
36  import net.obsearch.storage.OBStoreFactory;
37  import net.obsearch.storage.TupleDouble;
38  import org.apache.log4j.Logger;
39  
40  /*
41   OBSearch: a distributed similarity search engine
42   This project is to similarity search what 'bit-torrent' is to downloads.
43   Copyright (C)  2007 Arnoldo Jose Muller Molina
44  
45   This program is free software: you can redistribute it and/or modify
46   it under the terms of the GNU General Public License as published by
47   the Free Software Foundation, either version 3 of the License, or
48   (at your option) any later version.
49  
50   This program is distributed in the hope that it will be useful,
51   but WITHOUT ANY WARRANTY; without even the implied warranty of
52   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
53   GNU General Public License for more details.
54  
55   You should have received a copy of the GNU General Public License
56   along with this program.  If not, see <http://www.gnu.org/licenses/>.
57   */
58  /**
59   * PPTreeShort Implementation of a P+Tree that stores OB objects whose distance
60   * functions generate shorts. We take the burden of maintaining one class per
61   * datatype for efficiency reasons. This index will be deprecated soon because
62   * often floating point errors affect the results. Please use the idistance
63   * instead.
64   * 
65   * @author Arnoldo Jose Muller Molina
66   * @param <O>
67   *            The type of object to be stored in the Index.
68   * @since 0.7
69   */
70  
71  public class PPTreeShort<O extends OBShort> extends AbstractPPTree<O> implements
72  		IndexShort<O> {
73  
74  	/**
75  	 * Minimum value to be returned by the distance function.
76  	 */
77  	private short minInput;
78  
79  	/**
80  	 * Maximum value to be returned by the distance function.
81  	 */
82  	private short maxInput;
83  
84  	/**
85  	 * Optimization value for minInput and maxInput.
86  	 */
87  	private float opt;
88  
89  	/**
90  	 * Logger.
91  	 */
92  	private static final transient Logger logger = Logger
93  			.getLogger(PPTreeShort.class);
94  
95  	private OBCacheLong<double[]> bCache;
96  
97  	/**
98  	 * Constructor.
99  	 * 
100 	 * @param databaseDirectory
101 	 *            Directory were the index will be stored
102 	 * @param pivots
103 	 *            Numbe rof pivots to be used.
104 	 * @param od
105 	 *            Partitions for the space tree (please check the P+tree paper)
106 	 * @param pivotSelector
107 	 *            The pivot selector that will be used by this index.
108 	 * @param type
109 	 *            The class of the object O that will be used.
110 	 * @throws DatabaseException
111 	 *             If something goes wrong with the DB
112 	 * @throws IOException
113 	 *             If the databaseDirectory directory does not exist.
114 	 */
115 	public PPTreeShort(Class<O> type,
116 			IncrementalPivotSelector<O> pivotSelector, int pivotCount, int od)
117 			throws IOException, OBException {
118 		this(type, pivotSelector, pivotCount, od, Short.MIN_VALUE,
119 				Short.MAX_VALUE);
120 	}
121 
122 	/**
123 	 * Creates a new PPTreeShort. Ranges accepted by this index will be defined
124 	 * by the user. We recommend the use of this constructor. We believe it will
125 	 * give better resolution to the float transformation. The values returned
126 	 * by the distance function must be within [minInput, maxInput]. These two
127 	 * values can be over estimated but not under estimated.
128 	 * 
129 	 * @param databaseDirectory
130 	 *            Directory were the index will be stored
131 	 * @param pivots
132 	 *            Numbe rof pivots to be used.
133 	 * @param minInput
134 	 *            Minimum value to be returned by the distance function
135 	 * @param maxInput
136 	 *            Maximum value to be returned by the distance function
137 	 * @param cpus
138 	 *            Number of CPUS to use.
139 	 * @param pivotSelector
140 	 *            The pivot selector that will be used by this index.
141 	 * @param type
142 	 *            The class of the object O that will be used.
143 	 * @throws DatabaseException
144 	 *             If somehing goes wrong with the DB
145 	 * @throws IOException
146 	 *             If the databaseDirectory directory does not exist.
147 	 */
148 	public PPTreeShort(Class<O> type,
149 			IncrementalPivotSelector<O> pivotSelector, int pivotCount, int od,
150 			short minInput, short maxInput) throws IOException, OBException {
151 		super(type, pivotSelector, pivotCount, od);
152 		OBAsserts.chkAssert(minInput < maxInput,
153 				"minInput must be smaller than maxInput");
154 		this.minInput = minInput;
155 		this.maxInput = maxInput;
156 		// this optimization reduces the computations required
157 		// for the first level normalization.
158 		this.opt = 1 / ((float) (maxInput - minInput));
159 
160 	}
161 
162 	@Override
163 	protected double[] extractTuple(ByteBuffer in) throws OutOfRangeException {
164 		int i = 0;
165 		double[] res = new double[getPivotCount()];
166 		while (i < getPivotCount()) {
167 			res[i] = normalizeFirstPassAux(in.getShort());
168 			i++;
169 		}
170 		return res;
171 	}
172 
173 	@Override
174 	protected ByteBuffer objectToProjectionBytes(O object) throws OBException {
175 		short[] t = new short[getPivotCount()];
176 		calculatePivotTuple(object, t); // calculate the pivot for the given
177 		ByteBuffer out = ByteBufferFactoryConversion.createByteBuffer(0, super
178 				.getPivotCount(), 0, 0, 0, 0);
179 		for (short d : t) {
180 			out.putShort(d);
181 		}
182 		return out;
183 	}
184 
185 	/**
186 	 * Number of bytes that it takes to encode a short.
187 	 * 
188 	 * @return Number of bytes that it takes to encode a short.
189 	 */
190 	protected int distanceValueSizeInBytes() {
191 		return Short.SIZE / 8;
192 	}
193 
194 	/**
195 	 * Generates a query rectangle based on the given range and the given tuple.
196 	 * It normalizes the query first level only
197 	 * 
198 	 * @param t
199 	 *            the tuple to be processed
200 	 * @param r
201 	 *            the range
202 	 * @param q
203 	 *            resulting rectangle query
204 	 * @throws OutOfRangeException
205 	 *             If the distance of any object to any other object exceeds the
206 	 *             range defined by the user.
207 	 */
208 
209 	protected final void generateRectangleFirstPass(short[] t, short r,
210 			double[][] q) throws OutOfRangeException {
211 		// create a rectangle query
212 		int i = 0;
213 		while (i < q.length) { //
214 			q[i][MIN] = normalizeFirstPassAux((short) Math.max(t[i] - r,
215 					minInput));
216 			q[i][MAX] = normalizeFirstPassAux((short) Math.min(t[i] + r,
217 					maxInput));
218 			i++;
219 		}
220 	}
221 
222 	public boolean intersects(O object, short r, int box)
223 			throws NotFrozenException, InstantiationException,
224 			IllegalIdException, IllegalAccessException, OutOfRangeException,
225 			OBException {
226 		// calculate the vector for the object
227 		short[] t = new short[getPivotCount()];
228 		calculatePivotTuple(object, t);
229 		// calculate the rectangle
230 		double[][] qrect = new double[getPivotCount()][2];
231 		generateRectangleFirstPass(t, r, qrect);
232 
233 		return super.spaceTreeLeaves[box].intersects(qrect);
234 	}
235 
236 	public Iterator<Long> intersectingBoxes(O object, short r)
237 			throws NotFrozenException, InstantiationException,
238 			IllegalIdException, IllegalAccessException {
239 		throw new UnsupportedOperationException();
240 	}
241 
242 	public void searchOB(O object, short r, OBPriorityQueueShort<O> result)
243 			throws NotFrozenException, InstantiationException,
244 			IllegalIdException, IllegalAccessException, OutOfRangeException,
245 			OBException {
246 		OBAsserts.chkPositive(r);
247 		short[] t = new short[getPivotCount()];
248 		// calculate the pivot for the given object
249 		calculatePivotTuple(object, t);
250 		double[][] qrect = new double[getPivotCount()][2]; // rectangular query
251 		generateRectangleFirstPass(t, r, qrect);
252 		List<SpaceTreeLeaf> hyperRectangles = new LinkedList<SpaceTreeLeaf>();
253 		// obtain the hypercubes that have to be matched
254 		double[] center = normalizeFirstPass(t);
255 		spaceTree.searchRange(qrect, center, hyperRectangles);
256 		searchOBAux(object, r, result, qrect, t, hyperRectangles);
257 
258 		stats.incQueryCount();
259 		stats.incBucketsRead(hyperRectangles.size());
260 	}
261 
262 	public void searchOB(O object, short r, OBPriorityQueueShort<O> result,
263 			int[] boxes) throws NotFrozenException, InstantiationException,
264 			IllegalIdException, IllegalAccessException, OutOfRangeException,
265 			OBException {
266 		short[] t = new short[getPivotCount()];
267 		// calculate the pivot for the given object
268 		calculatePivotTuple(object, t);
269 		double[][] qrect = new double[getPivotCount()][2]; // rectangular query
270 		generateRectangleFirstPass(t, r, qrect);
271 		List<SpaceTreeLeaf> hyperRectangles = new LinkedList<SpaceTreeLeaf>();
272 		int i = 0;
273 		int max = boxes.length;
274 		while (i < max) {
275 			hyperRectangles.add(super.spaceTreeLeaves[boxes[i]]);
276 			i++;
277 		}
278 		searchOBAux(object, r, result, qrect, t, hyperRectangles);
279 	}
280 
281 	/**
282 	 * Helper function to search for objects.
283 	 * 
284 	 * @param object
285 	 *            The object that has to be searched
286 	 * @param r
287 	 *            The range to be used
288 	 * @param result
289 	 *            A priority queue that will hold the result
290 	 * @param qrect
291 	 *            Query rectangle
292 	 * @param t
293 	 *            Tuple in raw form (short)
294 	 * @param hyperRectangles
295 	 *            The space tree leaves that will be searched.
296 	 * @throws DatabaseException
297 	 *             If something goes wrong with the DB
298 	 * @throws OBException
299 	 *             User generated exception
300 	 * @throws IllegalAccessException
301 	 *             If there is a problem when instantiating objects O
302 	 * @throws InstantiationException
303 	 *             If there is a problem when instantiating objects O
304 	 * @throws OutOfRangeException
305 	 *             If the distance of any object to any other object exceeds the
306 	 *             range defined by the user.
307 	 * @throws NotFrozenException
308 	 *             if the index has not been frozen.
309 	 * @throws IllegalIdException
310 	 *             This exception is left as a Debug flag. If you receive this
311 	 *             exception please report the problem to:
312 	 *             http://code.google.com/p/obsearch/issues/list
313 	 */
314 	public void searchOBAux(O object, short r, OBPriorityQueueShort<O> result,
315 			double[][] qrect, short[] t, List<SpaceTreeLeaf> hyperRectangles)
316 			throws NotFrozenException, InstantiationException,
317 			IllegalIdException, IllegalAccessException, OutOfRangeException,
318 			OBException {
319 		// check if we are frozen
320 		assertFrozen();
321 
322 		// check if the result has been processed
323 		/*
324 		 * OBQueryShort cachedResult = this.resultCache.get(object);
325 		 * if(cachedResult != null && cachedResult.getDistance() == r){
326 		 * Iterator<OBResultShort<O>> it =cachedResult.getResult().iterator();
327 		 * while(it.hasNext()){ OBResultShort<O> element = it.next();
328 		 * result.add(element.getId(), element.getObject(),
329 		 * element.getDistance()); } return; }
330 		 */
331 		int pyramidCount = 2 * getPivotCount();
332 
333 		short myr = r;
334 
335 		double[] lowHighResult = new double[2];
336 
337 		Iterator<SpaceTreeLeaf> it = hyperRectangles.iterator();
338 		// this will hold the rectangle for the current hyperrectangle
339 		double[][] qw = new double[getPivotCount()][2];
340 		double[][] q = new double[getPivotCount()][2];
341 		while (it.hasNext()) {
342 			SpaceTreeLeaf space = it.next();
343 			if (!space.intersects(qrect)) {
344 				continue;
345 			}
346 
347 			// for each space there are 2d pyramids that have to be browsed
348 			int i = 0;
349 			// update the current rectangle, we also have to center it
350 			space.generateRectangle(qrect, qw);
351 			centerQuery(qw); // center the rectangle
352 			double[] minArray = generateMinArray(qw);
353 			while (i < pyramidCount) {
354 				// intersect destroys q, so we have to copy it
355 				copyQuery(qw, q);
356 				if (intersect(q, i, minArray, lowHighResult)) {
357 					int ri = (space.getSNo() * 2 * getPivotCount()) + i; // real
358 					// index
359 					stats.incBucketsRead();
360 					searchBTreeAndUpdate(object, t, myr, ri
361 							+ lowHighResult[HLOW], ri + lowHighResult[HHIGH],
362 							result);
363 
364 					short nr = result.updateRange(myr);
365 					// make the range shorter
366 					if (nr < myr) {
367 						myr = nr; // regenerate the query with a smaller
368 						// range
369 						generateRectangleFirstPass(t, myr, qrect);
370 						space.generateRectangle(qrect, qw);
371 						if (!space.intersects(qrect)) {
372 							break; // we have to skip the this space if
373 							// suddenly we are out of range...
374 							// otherwise we would end up
375 							// searching all the space for the
376 							// rest of the
377 							// pyramids!
378 						}
379 						centerQuery(qw); // center the rectangle
380 
381 					}
382 
383 				}
384 				i++;
385 			}
386 		}
387 
388 		// store the result in the cache
389 		// this.resultCache.put(object, new OBQueryShort<O>(object,r, result));
390 	}
391 
392 	/**
393 	 * This method reads from the B-tree appies l-infinite to discard false
394 	 * positives. This technique is called SMAP. Calculates the real distance
395 	 * and updates the result priority queue It is left public so that junit can
396 	 * perform validations on it Performance-wise this is one of the most
397 	 * important methods
398 	 * 
399 	 * @param object
400 	 *            object to search
401 	 * @param tuple
402 	 *            tuple of the object
403 	 * @param r
404 	 *            range
405 	 * @param hlow
406 	 *            lowest pyramid value
407 	 * @param hhigh
408 	 *            highest pyramid value
409 	 * @param result
410 	 *            result of the search operation
411 	 * @throws DatabaseException
412 	 *             If somehing goes wrong with the DB
413 	 * @throws OBException
414 	 *             User generated exception
415 	 * @throws IllegalAccessException
416 	 *             If there is a problem when instantiating objects O
417 	 * @throws InstantiationException
418 	 *             If there is a problem when instantiating objects O
419 	 * @throws IllegalIdException
420 	 *             This exception is left as a Debug flag. If you receive this
421 	 *             exception please report the problem to:
422 	 *             http://code.google.com/p/obsearch/issues/list
423 	 */
424 	private void searchBTreeAndUpdate(final O object, final short[] tuple,
425 			final short r, final double hlow, final double hhigh,
426 			final OBPriorityQueueShort<O> result)
427 			throws IllegalAccessException, InstantiationException,
428 			IllegalIdException, OBException {
429 
430 		CloseIterator<TupleDouble> it = C.processRange(hlow, hhigh);
431 		try {
432 			short max = Short.MIN_VALUE;
433 			short realDistance = Short.MIN_VALUE;
434 			while (it.hasNext()) {
435 				stats.incSmapCount();
436 				TupleDouble tup = it.next();
437 				ByteBuffer in = tup.getValue();
438 
439 				int i = 0;
440 				short t;
441 				max = Short.MIN_VALUE;
442 				while (i < tuple.length) {
443 					t = (short) Math.abs(tuple[i] - in.getShort());
444 					if (t > max) {
445 						max = t;
446 						if (t > r) {
447 							break; // finish this loop this slice won't be
448 							// matched
449 							// after all!
450 						}
451 					}
452 					i++;
453 				}
454 				if (max <= r && result.isCandidate(max)) {
455 					// there is a chance it is a possible match
456 					long id = in.getLong();
457 					O toCompare = getObject(id);
458 					realDistance = object.distance(toCompare);
459 					stats.incDistanceCount();
460 					if (realDistance <= r) {
461 						result.add(id, toCompare, realDistance);
462 					}
463 				}
464 			}
465 		} finally {
466 			it.closeCursor();
467 		}
468 
469 	}
470 
471 	protected net.obsearch.OperationStatus insertAux(long id, O object)
472 			throws OBStorageException, OBException, IllegalAccessException,
473 			InstantiationException {
474 		short[] t = new short[getPivotCount()];
475 		calculatePivotTuple(object, t); // calculate the tuple for the new //
476 		double[] first = normalizeFirstPass(t);
477 		double ppTreeValue = super.ppvalue(first);
478 		ByteBuffer out = ByteBufferFactoryConversion.createByteBuffer(0, super
479 				.getPivotCount(), 0, 1, 0, 0);
480 		// write the tuple
481 		for (short d : t) {
482 			out.putShort(d);
483 		}
484 		// write the object's id
485 		out.putLong(id);
486 		C.put(ppTreeValue, out);
487 		OperationStatus result = new OperationStatus();
488 		result.setStatus(Status.OK);
489 		result.setId(id);
490 		return result;
491 	}
492 
493 	protected net.obsearch.OperationStatus insertAuxBulk(long id, O object)
494 			throws OBStorageException, OBException, IllegalAccessException,
495 			InstantiationException {
496 		return insertAux(id, object);
497 	}
498 
499 	/**
500 	 * Normalizes the given t. The idea is to convert each of the values in t in
501 	 * the range [0,1].
502 	 * 
503 	 * @param t
504 	 * @return A double array of values in the range[0,1]
505 	 * @throws OutOfRangeException
506 	 *             If any of the values in t goes beyond the ranges defined at
507 	 *             construction time.
508 	 */
509 	protected double[] normalizeFirstPass(short[] t) throws OutOfRangeException {
510 		assert t.length == getPivotCount();
511 		double[] res = new double[getPivotCount()];
512 
513 		int i = 0;
514 		while (i < t.length) {
515 			res[i] = normalizeFirstPassAux(t[i]);
516 			i++;
517 		}
518 		return res;
519 	}
520 
521 	public void init(OBStoreFactory fact) throws OBStorageException,
522 			OBException, NotFrozenException, IllegalAccessException,
523 			InstantiationException, OBException {
524 		super.init(fact);
525 		bCache = new OBCacheLong<double[]>(new BLoader(), OBSearchProperties
526 				.getBCacheSize());
527 	}
528 
529 	private class BLoader implements OBCacheHandlerLong<double[]> {
530 
531 		public long getDBSize() throws OBStorageException {
532 			return B.size();
533 		}
534 
535 		public double[] loadObject(long id) throws OutOfRangeException,
536 				OBException, InstantiationException, IllegalAccessException {
537 			double[] tempTuple = new double[getPivotCount()];
538 			ByteBuffer in = B.getValue(id);
539 			int i = 0;
540 			while (i < getPivotCount()) {
541 				tempTuple[i] = normalizeFirstPassAux(in.getShort());
542 				i++;
543 			}
544 			return tempTuple;
545 		}
546 
547 		@Override
548 		public void store(long key, double[] object) throws OBException {
549 			// TODO Auto-generated method stub
550 		}
551 
552 	}
553 
554 	/**
555 	 * Read the given tuple from B database and load it into the given tuple in
556 	 * a normalized form.
557 	 * 
558 	 * @param id
559 	 *            local id of the tuple we want
560 	 * @param tuple
561 	 *            The tuple is loaded and stored here.
562 	 * @throws DatabaseException
563 	 *             If something goes wrong with the DB
564 	 * @throws OutOfRangeException
565 	 *             If the distance of any object to any other object exceeds the
566 	 *             range defined by the user.
567 	 */
568 	@Override
569 	protected final double[] readFromB(long id) throws OutOfRangeException,
570 			OBException {
571 		double[] res;
572 		try {
573 			res = this.bCache.get(id);
574 		} catch (Exception e) {
575 			throw new OBException(e);
576 		}
577 		return res;
578 	}
579 
580 	/**
581 	 * Normalize the given value This is a first pass normalization, any value
582 	 * to [0,1].
583 	 * 
584 	 * @param x
585 	 *            value to be normalized
586 	 * @return the normalized value
587 	 * @throws OutOfRangeException
588 	 *             If the distance of any object to any other object exceeds the
589 	 *             range defined by the user.
590 	 */
591 	protected double normalizeFirstPassAux(short x) throws OutOfRangeException {
592 		if (x < minInput || x > maxInput) {
593 			throw new OutOfRangeException(minInput + "", maxInput + "", "" + x);
594 		}
595 		return ((double) (x - minInput)) * opt;
596 	}
597 
598 	/**
599 	 * Calculates the tuple vector for the given object.
600 	 * 
601 	 * @param obj
602 	 *            object to be processed
603 	 * @param tuple
604 	 *            The resulting tuple will be stored here
605 	 */
606 	protected void calculatePivotTuple(final O obj, short[] tuple)
607 			throws OBException {
608 		assert tuple.length == this.getPivotCount();
609 		int i = 0;
610 		while (i < tuple.length) {
611 			tuple[i] = obj.distance(this.pivots[i]);
612 			i++;
613 		}
614 	}
615 
616 	/*
617 	 * private Object readResolve() throws DatabaseException,
618 	 * NotFrozenException, DatabaseException, IllegalAccessException,
619 	 * InstantiationException { super.initSpaceTreeLeaves(); resultCache = new
620 	 * HashMap < O, OBQueryShort < O >>(resultCacheSize); return this; }
621 	 */
622 
623 	/**
624 	 * Returns i >=0 if both tuples are the same, otherwise, it returns -1 the
625 	 * first non zero pair. This is used to find pairs of tuples that are likely
626 	 * to be similar The second tuple is made of shorts except its first item.
627 	 * Warning: the given tuple input will be modified. Only if all the 30
628 	 * pivots have the same value this method will return true. This also means
629 	 * that the next in.getInt() will correctly return the id of the object.
630 	 * Only in those cases in.getInt() will return something meaningful.
631 	 * 
632 	 * @param tuple
633 	 * @param tuple2
634 	 * @return true i >=0 if both tuples are the same, otherwise, it returns -1
635 	 */
636 	protected long equalTuples(short[] tuple, ByteBuffer in) {
637 
638 		int i = 0;
639 		while (i < tuple.length) {
640 			if (tuple[i] != in.getShort()) {
641 				return -1;
642 			}
643 			i++;
644 		}
645 		return in.getLong();
646 	}
647 
648 	public OperationStatus exists(O object) throws OBException,
649 			IllegalAccessException, InstantiationException {
650 		OperationStatus res = new OperationStatus(Status.NOT_EXISTS);
651 		OBPriorityQueueShort<O> result = new OBPriorityQueueShort<O>((byte) 1);
652 		searchOB(object, (short) 1, result);
653 
654 		if (result.getSize() == 1) {
655 			OBResultShort<O> r = result.iterator().next();
656 			if (r.getObject().equals(object)) {
657 				res.setStatus(Status.EXISTS);
658 				res.setId(r.getId());
659 			}
660 		}
661 		return res;
662 	}
663 
664 	@Override
665 	protected OperationStatus deleteAux(final O object) throws OBException,
666 			IllegalAccessException, InstantiationException {
667 		long resId = -1;
668 		OperationStatus res = new OperationStatus(Status.NOT_EXISTS);
669 		short[] tuple = new short[getPivotCount()];
670 		// calculate the pivot for the given object
671 		calculatePivotTuple(object, tuple);
672 
673 		double[] first = normalizeFirstPass(tuple);
674 		double ppTreeValue = super.ppvalue(first);
675 
676 		CloseIterator<TupleDouble> it = C
677 				.processRange(ppTreeValue, ppTreeValue);
678 		try {
679 			short max = Short.MIN_VALUE;
680 			while (it.hasNext()) {
681 				TupleDouble tup = it.next();
682 				ByteBuffer in = tup.getValue();
683 
684 				int i = 0;
685 				short t;
686 				max = Short.MIN_VALUE;
687 				// STATS
688 				while (i < tuple.length) {
689 					t = (short) Math.abs(tuple[i] - in.getShort());
690 					if (t > max) {
691 						max = t;
692 						if (t != 0) {
693 							break; // finish this loop this slice won't be
694 							// matched
695 							// after all!
696 						}
697 					}
698 					i++;
699 				}
700 
701 				if (max == 0) {
702 					// there is a chance it is a possible match
703 					long id = in.getLong();
704 					O toCompare = super.getObject(id);
705 					if (object.equals(toCompare)) {
706 						resId = id;
707 						res = new OperationStatus(Status.OK);
708 						res.setId(resId);
709 						it.remove();
710 						break;
711 					}
712 
713 				}
714 
715 			}
716 		} catch (Exception e) {
717 			throw new OBException(e);
718 		} finally {
719 			it.closeCursor();
720 		}
721 		return res;
722 	}
723 
724 	@Override
725 	public void searchOB(O object, short r, Filter<O> filter,
726 			OBPriorityQueueShort<O> result) throws NotFrozenException,
727 			InstantiationException, IllegalIdException, IllegalAccessException,
728 			OutOfRangeException, OBException {
729 
730 		throw new UnsupportedOperationException();
731 	}
732 
733 	public double distance(O a, O b) throws OBException {
734 		short result = ((OBShort) a).distance((OBShort) b);
735 		return normalizeFirstPassAux(result);
736 	}
737 
738 }