1 package org.ajmm.obsearch.index;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.BitSet;
6 import java.util.Iterator;
7
8 import org.ajmm.obsearch.Index;
9 import org.ajmm.obsearch.Result;
10 import org.ajmm.obsearch.exception.IllegalIdException;
11 import org.ajmm.obsearch.exception.NotFrozenException;
12 import org.ajmm.obsearch.exception.OBException;
13 import org.ajmm.obsearch.exception.OutOfRangeException;
14 import org.ajmm.obsearch.index.utils.MyTupleInput;
15 import org.ajmm.obsearch.ob.OBShort;
16 import org.ajmm.obsearch.result.OBPriorityQueueShort;
17 import org.ajmm.obsearch.result.OBResultShort;
18
19 import com.sleepycat.bind.tuple.IntegerBinding;
20 import com.sleepycat.bind.tuple.SortedFloatBinding;
21 import com.sleepycat.bind.tuple.TupleInput;
22 import com.sleepycat.bind.tuple.TupleOutput;
23 import com.sleepycat.je.Cursor;
24 import com.sleepycat.je.CursorConfig;
25 import com.sleepycat.je.DatabaseEntry;
26 import com.sleepycat.je.DatabaseException;
27 import com.sleepycat.je.LockMode;
28 import com.sleepycat.je.OperationStatus;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 public class ExtendedPyramidIndexShort < O extends OBShort >
58 extends AbstractExtendedPyramidIndex < O > implements IndexShort < O > {
59
60
61
62
63 private short minInput;
64
65
66
67
68 private short maxInput;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public ExtendedPyramidIndexShort(final File databaseDirectory,
86 final short pivots, PivotSelector < O > pivotSelector, Class<O> type)
87 throws DatabaseException, IOException {
88
89 this(databaseDirectory, pivots, (short) 0, Short.MAX_VALUE,
90 pivotSelector,type);
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public ExtendedPyramidIndexShort(final File databaseDirectory,
117 final short pivots, final short minInput, final short maxInput,
118 PivotSelector < O > pivotSelector,Class<O> type) throws DatabaseException,
119 IOException {
120 super(databaseDirectory, pivots, pivotSelector, type);
121 this.minInput = minInput;
122 this.maxInput = maxInput;
123 assert minInput < maxInput;
124 }
125
126 @Override
127 protected float[] extractTuple(final TupleInput in)
128 throws OutOfRangeException {
129 int i = 0;
130 float[] res = new float[pivotsCount];
131 while (i < pivotsCount) {
132 res[i] = normalize(in.readShort());
133 i++;
134 }
135 return res;
136 }
137
138 public boolean intersects(final O object, final short r, final int box)
139 throws NotFrozenException, DatabaseException,
140 InstantiationException, IllegalIdException, IllegalAccessException,
141 OutOfRangeException, OBException {
142 short[] t = new short[pivotsCount];
143 calculatePivotTuple(object, t);
144 float[][] q = new float[pivotsCount][2];
145 generateRectangle(t, r, q);
146 float[] lowHighResult = new float[2];
147 float[] minArray = generateMinArray(q);
148 return intersect(q, box, minArray, lowHighResult);
149 }
150
151 public int[] intersectingBoxes(final O object, final short r)
152 throws NotFrozenException, DatabaseException,
153 InstantiationException, IllegalIdException, IllegalAccessException,
154 OutOfRangeException, OBException {
155 BitSet result = new BitSet(super.totalBoxes());
156 short[] t = new short[pivotsCount];
157 calculatePivotTuple(object, t);
158 int pyramidCount = 2 * pivotsCount;
159 float[][] qorig = new float[pivotsCount][2];
160 float[][] q = new float[pivotsCount][2];
161 short myr = r;
162 float[] lowHighResult = new float[2];
163 generateRectangle(t, myr, qorig);
164 float[] minArray = generateMinArray(qorig);
165 int i = 0;
166 while (i < pyramidCount) {
167 copyQuery(qorig, q);
168 if (intersect(q, i, minArray, lowHighResult)) {
169 result.set(i);
170 }
171 i++;
172 }
173 int[] res = new int[result.cardinality()];
174 i = 0;
175 int index = 0;
176 while (i < res.length) {
177 index = result.nextSetBit(index);
178 res[i] = index;
179 index++;
180 i++;
181 }
182 return res;
183 }
184
185 public void searchOB(final O object, final short r,
186 final OBPriorityQueueShort < O > result) throws NotFrozenException,
187 DatabaseException, InstantiationException, IllegalIdException,
188 IllegalAccessException, OutOfRangeException, OBException {
189
190 assertFrozen();
191
192 short[] t = new short[pivotsCount];
193 calculatePivotTuple(object, t);
194
195
196 int pyramidCount = 2 * pivotsCount;
197 float[][] qorig = new float[pivotsCount][2];
198 float[][] q = new float[pivotsCount][2];
199 short myr = r;
200 generateRectangle(t, myr, qorig);
201 int i = 0;
202 float[] lowHighResult = new float[2];
203 float[] minArray = generateMinArray(qorig);
204 while (i < pyramidCount) {
205 copyQuery(qorig, q);
206
207 if (intersect(q, i, minArray, lowHighResult)) {
208 searchBTreeAndUpdate(object, t, myr, i + lowHighResult[HLOW], i
209 + lowHighResult[HHIGH], result);
210 short nr = result.updateRange(myr);
211 if (nr < myr) {
212 myr = nr;
213
214 generateRectangle(t, myr, qorig);
215 }
216 }
217 i++;
218 }
219 }
220
221 public void searchOB(final O object, final short r,
222 final OBPriorityQueueShort < O > result, final int[] boxes)
223 throws NotFrozenException, DatabaseException,
224 InstantiationException, IllegalIdException, IllegalAccessException,
225 OutOfRangeException, OBException {
226
227 assertFrozen();
228 BitSet boxesBit = new BitSet();
229 int i = 0;
230 while (i < boxes.length) {
231 boxesBit.set(boxes[i]);
232 i++;
233 }
234 short[] t = new short[pivotsCount];
235 calculatePivotTuple(object, t);
236
237
238 int pyramidCount = 2 * pivotsCount;
239 float[][] qorig = new float[pivotsCount][2];
240 float[][] q = new float[pivotsCount][2];
241 short myr = r;
242 generateRectangle(t, myr, qorig);
243 i = 0;
244 float[] lowHighResult = new float[2];
245
246 float[] minArray = generateMinArray(qorig);
247 while (i < pyramidCount) {
248 copyQuery(qorig, q);
249 if (intersect(q, i, minArray, lowHighResult) && boxesBit.get(i)) {
250 searchBTreeAndUpdate(object, t, myr, i + lowHighResult[HLOW], i
251 + lowHighResult[HHIGH], result);
252 short nr = result.updateRange(myr);
253 if (nr < myr) {
254 myr = nr;
255
256 generateRectangle(t, myr, qorig);
257 }
258 }
259 i++;
260 }
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 private void searchBTreeAndUpdate(final O object, final short[] tuple,
295 final short r, final float hlow, final float hhigh,
296 final OBPriorityQueueShort < O > result) throws DatabaseException,
297 IllegalAccessException, InstantiationException, IllegalIdException,
298 OBException {
299
300 Cursor cursor = null;
301
302 try {
303
304 DatabaseEntry keyEntry = new DatabaseEntry();
305 DatabaseEntry dataEntry = new DatabaseEntry();
306 cursor = cDB.openCursor(null, null);
307 SortedFloatBinding.floatToEntry(hlow, keyEntry);
308 OperationStatus retVal = cursor.getSearchKeyRange(keyEntry,
309 dataEntry, null);
310
311 if (retVal == OperationStatus.NOTFOUND) {
312 return;
313 }
314
315 if (cursor.count() > 0) {
316 float currentPyramidValue = SortedFloatBinding
317 .entryToFloat(keyEntry);
318 short max = Short.MIN_VALUE;
319 short realDistance = Short.MIN_VALUE;
320 while (retVal == OperationStatus.SUCCESS
321 && currentPyramidValue <= hhigh) {
322
323 TupleInput in = new TupleInput(dataEntry.getData());
324
325 int i = 0;
326 short t;
327 max = Short.MIN_VALUE;
328 while (i < tuple.length) {
329 t = (short) Math.abs(tuple[i] - in.readShort());
330 if (t > max) {
331 max = t;
332 if (t > r) {
333 break;
334
335
336 }
337 }
338 i++;
339 }
340 if (max <= r && result.isCandidate(max)) {
341
342 int id = in.readInt();
343 O toCompare = super.getObject(id);
344 realDistance = object.distance(toCompare);
345 if (realDistance <= r) {
346 result.add(id, toCompare, realDistance);
347 }
348 }
349
350
351 retVal = cursor.getNext(keyEntry, dataEntry, null);
352
353
354 currentPyramidValue = SortedFloatBinding
355 .entryToFloat(keyEntry);
356 }
357 }
358 } finally {
359 cursor.close();
360 }
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378 protected final void generateRectangle(final short[] t, final short r,
379 final float[][] q) throws OutOfRangeException {
380
381 int i = 0;
382 while (i < q.length) {
383 q[i][MIN] = extendedPyramidNormalization(normalize((short) Math
384 .max(t[i] - r, minInput)), i);
385 q[i][MAX] = extendedPyramidNormalization(normalize((short) Math
386 .min(t[i] + r, maxInput)), i);
387 i++;
388 }
389 centerQuery(q);
390 }
391
392
393
394
395
396
397
398
399
400
401 @Override
402 protected void insertFromBtoC() throws DatabaseException,
403 OutOfRangeException {
404
405 DatabaseEntry foundKey = new DatabaseEntry();
406 DatabaseEntry foundData = new DatabaseEntry();
407 Cursor cursor = null;
408 long count = super.bDB.count();
409 short[] t = new short[pivotsCount];
410 try {
411 int i = 0;
412 cursor = bDB.openCursor(null, null);
413 MyTupleInput in = new MyTupleInput();
414 while (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
415 assert i == IntegerBinding.entryToInt(foundKey);
416
417 in.setBuffer(foundData.getData());
418 int cx = 0;
419 while (cx < t.length) {
420 t[cx] = in.readShort();
421 cx++;
422 }
423 this.insertFrozenAux(t, i);
424 i++;
425 }
426
427
428 assert i == count;
429
430 } finally {
431 cursor.close();
432 }
433
434 }
435
436
437
438
439
440
441
442
443
444
445
446 protected final float[] extendedPyramidTransform(final short[] tuple)
447 throws OutOfRangeException {
448 int i = 0;
449 float[] result = new float[tuple.length];
450 assert tuple.length == result.length;
451 while (i < tuple.length) {
452 float norm = normalize(tuple[i]);
453 float norm2 = extendedPyramidNormalization(norm, i);
454 result[i] = norm2;
455 i++;
456 }
457 return result;
458 }
459
460
461
462
463
464
465
466
467
468
469 protected float normalize(final short x) throws OutOfRangeException {
470 if (x < minInput || x > maxInput) {
471 throw new OutOfRangeException(minInput + "", maxInput + "", "" + x);
472 }
473 return ((float) (x - minInput)) / ((float) (maxInput - minInput));
474 }
475
476 @Override
477 protected byte insertFrozen(final O object, final int id)
478 throws IllegalIdException, OBException, DatabaseException,
479 IllegalAccessException, InstantiationException {
480 short[] t = new short[pivotsCount];
481 calculatePivotTuple(object, t);
482
483 return insertFrozenAux(t, id);
484 }
485
486 public int getBox(final O object) throws OBException {
487 short[] t = new short[pivotsCount];
488 calculatePivotTuple(object, t);
489 float[] et = extendedPyramidTransform(t);
490 return super.pyramidOfPoint(et);
491 }
492
493 public Result exists(O object) throws DatabaseException, OBException,
494 IllegalAccessException, InstantiationException {
495 Result res = new Result(Result.Status.NOT_EXISTS);
496
497 short[] tuple = new short[pivotsCount];
498
499 calculatePivotTuple(object, tuple);
500
501 float[] et = extendedPyramidTransform(tuple);
502 float pyramidValue = pyramidValue(et);
503
504 Cursor cursor = null;
505
506 try {
507
508 DatabaseEntry keyEntry = new DatabaseEntry();
509 DatabaseEntry dataEntry = new DatabaseEntry();
510 cursor = cDB.openCursor(null, null);
511 SortedFloatBinding.floatToEntry(pyramidValue, keyEntry);
512 OperationStatus retVal = cursor.getSearchKeyRange(keyEntry,
513 dataEntry, null);
514
515 if (retVal == OperationStatus.NOTFOUND) {
516 return res;
517 }
518
519 if (cursor.count() > 0) {
520 float currentPyramidValue = SortedFloatBinding
521 .entryToFloat(keyEntry);
522 short max = Short.MIN_VALUE;
523 while (retVal == OperationStatus.SUCCESS
524 && currentPyramidValue == pyramidValue) {
525
526 TupleInput in = new TupleInput(dataEntry.getData());
527
528 int i = 0;
529 short t;
530 max = Short.MIN_VALUE;
531
532 while (i < tuple.length) {
533 t = (short) Math.abs(tuple[i] - in.readShort());
534 if (t > max) {
535 max = t;
536 if (t != 0) {
537 break;
538
539
540 }
541 }
542 i++;
543 }
544
545
546
547 if (max == 0) {
548
549 int id = in.readInt();
550 O toCompare = super.getObject(id);
551 if (object.equals(toCompare)) {
552 res = new Result(Result.Status.EXISTS);
553 res.setId(id);
554 break;
555 }
556
557 }
558
559 retVal = cursor.getNext(keyEntry, dataEntry, null);
560
561
562
563 currentPyramidValue = SortedFloatBinding
564 .entryToFloat(keyEntry);
565 }
566 }
567
568 } finally {
569 if (cursor != null) {
570 cursor.close();
571 }
572 }
573 return res;
574 }
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589 protected byte insertFrozenAux(final short[] t, final int id)
590 throws OutOfRangeException, DatabaseException {
591 float[] et = extendedPyramidTransform(t);
592 float pyramidValue = pyramidValue(et);
593
594 DatabaseEntry keyEntry = new DatabaseEntry();
595 DatabaseEntry dataEntry = new DatabaseEntry();
596 TupleOutput out = new TupleOutput();
597
598 for (short d : t) {
599 out.writeShort(d);
600 }
601
602 out.writeInt(id);
603
604 SortedFloatBinding.floatToEntry(pyramidValue, keyEntry);
605 dataEntry.setData(out.getBufferBytes());
606
607 if (cDB.put(null, keyEntry, dataEntry) != OperationStatus.SUCCESS) {
608 throw new DatabaseException();
609 }
610 return 1;
611 }
612
613 @Override
614 protected void insertInB(final int id, final O object) throws OBException,
615 DatabaseException {
616 DatabaseEntry keyEntry = new DatabaseEntry();
617 TupleOutput out = new TupleOutput();
618 short[] tuple = new short[pivotsCount];
619 calculatePivotTuple(object, tuple);
620
621 for (short d : tuple) {
622 out.writeShort(d);
623 }
624
625 IntegerBinding.intToEntry(id, keyEntry);
626 insertInDatabase(out, keyEntry, bDB);
627 }
628
629 @Override
630 protected Index returnSelf() {
631 return this;
632 }
633
634
635
636
637
638
639
640
641
642
643 protected final void calculatePivotTuple(final O obj, final short[] tuple)
644 throws OBException {
645 assert tuple.length == pivotsCount;
646 int i = 0;
647 while (i < tuple.length) {
648 tuple[i] = obj.distance(pivots[i]);
649 i++;
650 }
651 }
652
653
654
655
656
657
658
659
660
661
662
663
664 @Override
665 protected Result deleteAux(final O object) throws DatabaseException,
666 OBException, IllegalAccessException, InstantiationException {
667 int resId = -1;
668 Result res = new Result(Result.Status.NOT_EXISTS);
669 short[] tuple = new short[pivotsCount];
670
671 calculatePivotTuple(object, tuple);
672 float[] et = extendedPyramidTransform(tuple);
673 float pyramidValue = pyramidValue(et);
674
675 Cursor cursor = null;
676 try {
677
678 CursorConfig config = new CursorConfig();
679 config.setReadUncommitted(true);
680 DatabaseEntry keyEntry = new DatabaseEntry();
681 DatabaseEntry dataEntry = new DatabaseEntry();
682 cursor = cDB.openCursor(null, null);
683 SortedFloatBinding.floatToEntry(pyramidValue, keyEntry);
684 OperationStatus retVal = cursor.getSearchKeyRange(keyEntry,
685 dataEntry, null);
686
687 if (retVal == OperationStatus.NOTFOUND) {
688
689 } else if (cursor.count() > 0) {
690 float currentPyramidValue = SortedFloatBinding
691 .entryToFloat(keyEntry);
692 short max = Short.MIN_VALUE;
693 while (retVal == OperationStatus.SUCCESS
694 && currentPyramidValue == pyramidValue) {
695
696 TupleInput in = new TupleInput(dataEntry.getData());
697
698 int i = 0;
699 short t;
700 max = Short.MIN_VALUE;
701
702 while (i < tuple.length) {
703 t = (short) Math.abs(tuple[i] - in.readShort());
704 if (t > max) {
705 max = t;
706 if (t != 0) {
707 break;
708
709
710 }
711 }
712 i++;
713 }
714
715
716
717 if (max == 0) {
718
719 int id = in.readInt();
720 O toCompare = super.getObject(id);
721 if (object.equals(toCompare)) {
722 resId = id;
723 res = new Result(Result.Status.OK);
724 res.setId(resId);
725 retVal = cursor.delete();
726
727 if (retVal != OperationStatus.SUCCESS) {
728 throw new DatabaseException();
729 }
730 break;
731 }
732
733 }
734
735 retVal = cursor.getNext(keyEntry, dataEntry, null);
736
737
738
739 currentPyramidValue = SortedFloatBinding
740 .entryToFloat(keyEntry);
741 }
742
743 }
744
745 } finally {
746 if (cursor != null) {
747
748 cursor.close();
749 }
750 }
751 return res;
752 }
753
754 public float distance(O a, O b) throws OBException {
755 short result = ((OBShort) a).distance((OBShort) b);
756 return normalize(result);
757 }
758
759 }