Skip to content

Commit 5891be6

Browse files
committed
add leastGreaterThanOrEqual(), leastGreaterThan()
1 parent 5d58707 commit 5891be6

File tree

3 files changed

+79
-55
lines changed

3 files changed

+79
-55
lines changed

src/main/scala/BPlusTree.scala

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,33 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
240240
protected def setValue[V1 >: V]( node: N, index: Int, v: V1 )
241241

242242

243+
/**
244+
* Returns the leaf position of the key that is least greater than or equal to `key`.
245+
**/
246+
protected def leastGTE( key: K ) =
247+
lookupGTE( key ) match {
248+
case (_, leaf, index) => (leaf, index)
249+
}
250+
251+
/**
252+
* Returns the leaf position of the key that is least greater than `key`.
253+
**/
254+
protected def leastGT( key: K ) =
255+
lookupGTE( key ) match {
256+
case (true, leaf, index) => nextPosition( leaf, index )
257+
case (false, leaf, index) => (leaf, index)
258+
}
259+
260+
/**
261+
* Returns the key/value pair whose key is least greater than or equal to `key`.
262+
*/
263+
def leastGreaterThanOrEqual( key: K ) = optionalKeyValue( leastGTE(key) )
264+
265+
/**
266+
* Returns the key/value pair whose key is least greater than `key`.
267+
*/
268+
def leastGreaterThan( key: K ) = optionalKeyValue( leastGT(key) )
269+
243270
/**
244271
* Returns a bounded iterator over a range of key/value pairs in the tree in ascending sorted key order. The range of key/value pairs in the iterator is specified by `bounds`. `bounds` must contain one or two pairs where the first element in the pair is a symbol corresponding to the type of bound (i.e. '<, '<=, '>, '>=) and the second element is a key value.
245272
*
@@ -251,20 +278,9 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
251278
* Returns a bounded iterator over a range of key positions (node/index pairs) in the tree in ascending sorted key order. The `bounds` parameter is the same as for [[boundedIterator]].
252279
*/
253280
protected def boundedPositionIterator( bounds: (Symbol, K)* ): Iterator[(N, Int)] = {
254-
def gte( key: K ) =
255-
lookupGTE( key ) match {
256-
case (_, leaf, index) => (leaf, index)
257-
}
258-
259-
def gt( key: K ) =
260-
lookupGTE( key ) match {
261-
case (true, leaf, index) => nextPosition( leaf, index )
262-
case (false, leaf, index) => (leaf, index)
263-
}
264-
265281
require( bounds.length == 1 || bounds.length == 2, "boundedIterator: one or two bounds" )
266282

267-
val symbols = ListMap[Symbol, K => (N, Int)]( '> -> gt, '>= -> gte, '< -> gte, '<= -> gt )
283+
val symbols = ListMap[Symbol, K => (N, Int)]( '> -> leastGT, '>= -> leastGTE, '< -> leastGTE, '<= -> leastGT )
268284

269285
require( bounds forall {case (s, _) => symbols contains s}, "boundedIterator: expected one of '<, '<=, '>, '>=" )
270286

@@ -395,6 +411,24 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
395411
*/
396412
def reverseKeysIterator = reversePositionIterator map {case (n, i) => getKey( n, i )}
397413

414+
protected def optionalKeyValue( pos: (N, Int) ) =
415+
pos match {
416+
case (leaf, index) =>
417+
if (index < nodeLength( leaf ))
418+
Some( getKeyValue(leaf, index ))
419+
else
420+
None
421+
}
422+
423+
protected def optionalKey( pos: (N, Int) ) =
424+
pos match {
425+
case (leaf, index) =>
426+
if (index < nodeLength( leaf ))
427+
Some( getKey(leaf, index ))
428+
else
429+
None
430+
}
431+
398432
/**
399433
* Returns the maximum key and it's associated value.
400434
*
@@ -404,7 +438,7 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
404438
if (isEmpty)
405439
None
406440
else
407-
Some( (getKey(last, lastlen - 1), getValue(last, lastlen - 1)) )
441+
Some( getKeyValue(last, lastlen - 1) )
408442

409443
/**
410444
* Returns the maximum key.
@@ -420,20 +454,12 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
420454
*
421455
* @return `Some( (key, value) )` where `key` is the minimum key and `value` is it's associated value if the tree is non-empty, or `None` if the tree is empty.
422456
*/
423-
def min =
424-
if (isEmpty)
425-
None
426-
else
427-
Some( (getKey(first, 0), getValue(first, 0)) )
457+
def min = optionalKeyValue( first, 0 )
428458

429459
/**
430460
* Returns the minimum key.
431461
*/
432-
def minKey =
433-
if (isEmpty)
434-
None
435-
else
436-
Some( getKey(first, 0) )
462+
def minKey = optionalKey( first, 0 )
437463

438464
/**
439465
* Inserts `key` with associated `value` into the tree. If `key` exists, then it's new associated value will be `value`.
@@ -559,12 +585,6 @@ abstract class BPlusTree[K <% Ordered[K], +V] {
559585
_lookup( root )
560586
}
561587

562-
/**
563-
* Returns the key/value pair at `index` in `leaf` as well as the leaf node and index where the next key (in sorted order) is located. This method assumes that `index` is the index of an existing key within `node`.
564-
*
565-
* @return a pair where the first element is the key/value pair, and the second element is a pair containing the node containing the next key in sorted order (or `null` if there is no next key), and the index of the next key (or 0 if there is no next key)
566-
*/
567-
protected def nextKeyValue( leaf: N, index: Int ) = (getKeyValue( leaf, index), nextPosition( leaf, index ))
568588

569589
/**
570590
* Returns the key/value pair at `index` within `leaf`.

src/main/scala/FileBPlusTree.scala

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -297,22 +297,10 @@ class FileBPlusTree[K <% Ordered[K], V]( protected val file: RandomAccessFile, p
297297
val dstKeys = savedKeys.view( begin*DATUM_SIZE, end*DATUM_SIZE ).toArray
298298
val dstBranches = savedBranches.view( begin + 1, end + 1 ).toArray
299299
val len = savedLength - (end - begin)
300-
301-
// dstKeys.insertAll( 0, savedKeys.view(begin, end) )
302-
// savedKeys.remove( begin, end - begin )
303-
// dstBranches.insertAll( 0, savedBranches.view(begin + 1, end + 1) )
304-
// savedBranches.remove( begin + 1, end - begin )
305-
306300
file seek (src + NODE_KEYS)
307301
file write (savedKeys, 0, len*DATUM_SIZE)
308302
file seek (dst + NODE_KEYS)
309303
file write dstKeys
310-
311-
// for ((k, i) <- savedKeys zipWithIndex)
312-
// setKey( src, i, k )
313-
//
314-
// for ((k, i) <- dstKeys zipWithIndex)
315-
// setKey( dst, i, k )
316304

317305
for (i <- 0 to begin)
318306
setBranch( src, i, savedBranches(i) )
@@ -346,22 +334,10 @@ class FileBPlusTree[K <% Ordered[K], V]( protected val file: RandomAccessFile, p
346334
file write (savedKeys, 0, len*DATUM_SIZE)
347335
file seek (src + LEAF_VALUES)
348336
file write (savedValues, 0, len*DATUM_SIZE)
349-
350-
// for (((k, v), i) <- savedKeys zip savedValues zipWithIndex) {
351-
// setKey( src, i, k )
352-
// setValue( src, i, v )
353-
// }
354-
355337
file seek (dst + NODE_KEYS)
356338
file write dstKeys
357339
file seek (dst + LEAF_VALUES)
358340
file write dstValues
359-
360-
// for (((k, v), i) <- dstKeys zip dstValues zipWithIndex) {
361-
// setKey( dst, i, k )
362-
// setValue( dst, i, v )
363-
// }
364-
365341
nodeLength( src, len )
366342
nodeLength( dst, dstlen )
367343
savedNode = NUL
@@ -419,8 +395,6 @@ class FileBPlusTree[K <% Ordered[K], V]( protected val file: RandomAccessFile, p
419395
if (node == savedNode) {
420396
Array.copy( savedKeys, (keyIndex + 1)*DATUM_SIZE, savedKeys, keyIndex*DATUM_SIZE, (savedLength - keyIndex - 1)*DATUM_SIZE )
421397
Array.copy( savedBranches, branchIndex + 1, savedBranches, branchIndex, savedLength - branchIndex )
422-
// savedKeys.remove( keyIndex, 1 )
423-
// savedBranches.remove( branchIndex, 1 )
424398
savedLength -= 1
425399
savedLength
426400
} else {

src/test/scala/MiscTests.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package xyz.hyperreal.btree
2+
3+
import org.scalatest._
4+
import prop.PropertyChecks
5+
import org.scalatest.prop.TableDrivenPropertyChecks._
6+
import org.scalatest.Assertions._
7+
8+
9+
class MiscTests extends FreeSpec with PropertyChecks with Matchers {
10+
11+
"max/min" in {
12+
val t = new MemoryBPlusTree[Int, Symbol]( 3 )
13+
14+
t.min shouldBe None
15+
t.max shouldBe None
16+
t.minKey shouldBe None
17+
t.maxKey shouldBe None
18+
t.insert( 5, 'five )
19+
t.min shouldBe Some((5, 'five))
20+
t.max shouldBe Some((5, 'five))
21+
t.minKey shouldBe Some(5)
22+
t.maxKey shouldBe Some(5)
23+
t.insert( 3, 'three )
24+
t.min shouldBe Some((3, 'three))
25+
t.max shouldBe Some((5, 'five))
26+
t.minKey shouldBe Some(3)
27+
t.maxKey shouldBe Some(5)
28+
}
29+
30+
}

0 commit comments

Comments
 (0)