implemented cursor into BTree
This commit is contained in:
parent
5b436bad5a
commit
80ef044771
1 changed files with 117 additions and 49 deletions
|
|
@ -41,10 +41,12 @@ type
|
||||||
n: int ## number of key-value pairs
|
n: int ## number of key-value pairs
|
||||||
CmpKind {.pure.} = enum
|
CmpKind {.pure.} = enum
|
||||||
eq, le, lt, ge, gt, neq
|
eq, le, lt, ge, gt, neq
|
||||||
|
CursorState = enum stPop, stLeaf, stEnd
|
||||||
Cursor = object
|
Cursor = object
|
||||||
n: Node
|
n: Node
|
||||||
i, dir: int
|
i: int
|
||||||
up: seq[(Node, int)]
|
stack: seq[Node]
|
||||||
|
state: CursorState
|
||||||
|
|
||||||
proc newBTree(): BTree = BTree(root: Node(m: 0, isInternal: false))
|
proc newBTree(): BTree = BTree(root: Node(m: 0, isInternal: false))
|
||||||
|
|
||||||
|
|
@ -73,28 +75,6 @@ proc `=~`(i: int; k: CmpKind): bool =
|
||||||
of CmpKind.gt: i > 0
|
of CmpKind.gt: i > 0
|
||||||
of CmpKind.neq: i != 0
|
of CmpKind.neq: i != 0
|
||||||
|
|
||||||
proc follow(i: int; k: CmpKind): bool =
|
|
||||||
case k
|
|
||||||
of CmpKind.eq:
|
|
||||||
# we demand equality so if less than, follow it
|
|
||||||
i < 0
|
|
||||||
of CmpKind.le:
|
|
||||||
i <= 0
|
|
||||||
of CmpKind.lt: i <= 0
|
|
||||||
of CmpKind.ge: i >= 0
|
|
||||||
of CmpKind.gt: i >= 0
|
|
||||||
of CmpKind.neq: true
|
|
||||||
|
|
||||||
proc startingPoint(x: Node; key: Key; kind: CmpKind): Cursor =
|
|
||||||
if x.isInternal:
|
|
||||||
for j in 0 ..< x.m:
|
|
||||||
if j+1 == x.m or cmp(key, x.keys[j+1]) =~ kind:
|
|
||||||
return startingPoint(x.links[j], key, kind)
|
|
||||||
else:
|
|
||||||
for j in 0 ..< x.m:
|
|
||||||
let cmpRes = cmp(key, x.keys[j])
|
|
||||||
if cmpRes =~ kind: return Cursor(n: x, i: j)
|
|
||||||
|
|
||||||
proc dos(x: Node; kind: CmpKind; key: Key; withKey: proc(k: Key; v: Val)) =
|
proc dos(x: Node; kind: CmpKind; key: Key; withKey: proc(k: Key; v: Val)) =
|
||||||
if not x.isInternal:
|
if not x.isInternal:
|
||||||
for j in 0 ..< x.m:
|
for j in 0 ..< x.m:
|
||||||
|
|
@ -246,36 +226,114 @@ proc don(x: Node; kind: CmpKind; key: Key; withKey: proc(k: Key; v: Val)) =
|
||||||
for i in countdown(followB, followA):
|
for i in countdown(followB, followA):
|
||||||
stack.add(x.links[i])
|
stack.add(x.links[i])
|
||||||
|
|
||||||
proc init(x: Node): Cursor =
|
proc initCursor(x: Node): Cursor =
|
||||||
result.up = @[]
|
result.stack = @[x]
|
||||||
result.i = 0
|
result.i = 0
|
||||||
var it = x
|
result.n = nil
|
||||||
while it.isInternal:
|
result.state = stPop
|
||||||
result.up.add((it, 0))
|
|
||||||
it = it.links[0]
|
|
||||||
result.n = it
|
|
||||||
|
|
||||||
proc next(c: var Cursor) =
|
proc next(c: var Cursor; kind: CmpKind; key: Key) =
|
||||||
assert c.n != nil
|
case c.state
|
||||||
if c.i >= c.n.m:
|
of stEnd: discard
|
||||||
# current leaf exhausted, pick the next one:
|
of stLeaf:
|
||||||
if c.up.len > 0:
|
let x = c.n
|
||||||
(c.n, c.i) = c.up.pop()
|
for j in c.i+1 ..< x.m:
|
||||||
|
if cmp(x.keys[j], key) =~ kind:
|
||||||
inc c.i
|
c.state = stLeaf
|
||||||
var u = 1
|
c.i = j
|
||||||
while c.i > c.n.m or c.n.isInternal:
|
|
||||||
if c.up.len > 0:
|
|
||||||
(c.n, c.i) = c.up[c.up.len - u]
|
|
||||||
else:
|
|
||||||
c.n = nil
|
|
||||||
return
|
return
|
||||||
assert(c.n == nil or not c.n.isInternal)
|
c.state = stPop
|
||||||
|
next(c, kind, key)
|
||||||
|
of stPop:
|
||||||
|
if c.stack.len == 0:
|
||||||
|
c.state = stEnd
|
||||||
|
return
|
||||||
|
let x = c.stack.pop()
|
||||||
|
if not x.isInternal:
|
||||||
|
c.i = -1
|
||||||
|
c.n = x
|
||||||
|
c.state = stLeaf
|
||||||
|
next(c, kind, key)
|
||||||
|
else:
|
||||||
|
# we compute the range of links to follow first, before
|
||||||
|
# recursing:
|
||||||
|
var followA = 0
|
||||||
|
var followB = -1
|
||||||
|
case kind
|
||||||
|
of CmpKind.eq:
|
||||||
|
# want: key == 10
|
||||||
|
# keys: 0 3 4 5 10 20
|
||||||
|
# keys: 20 30 40
|
||||||
|
for j in 1..x.m:
|
||||||
|
if j == x.m or cmp(key, x.keys[j]) < 0:
|
||||||
|
followA = j-1
|
||||||
|
followB = j-1
|
||||||
|
break
|
||||||
|
of CmpKind.le, CmpKind.lt:
|
||||||
|
# want: key <= 10 or key < 10
|
||||||
|
# keys: 0 3 4 5 10 20
|
||||||
|
# keys: 20 30 40
|
||||||
|
|
||||||
proc atEnd(c: Cursor): bool = c.n == nil
|
# Case A: all keys are bigger:
|
||||||
|
if cmp(key, x.keys[1]) < 0:
|
||||||
|
# --> use the very first branch
|
||||||
|
followA = 0
|
||||||
|
followB = 0
|
||||||
|
else:
|
||||||
|
# Case B: all keys are smaller --> use all branches is covered too
|
||||||
|
# by this loop.
|
||||||
|
for j in 1..<x.m:
|
||||||
|
let cmpRes = cmp(key, x.keys[j])
|
||||||
|
if cmpRes >= 0:
|
||||||
|
if followB < 0: followA = j-1
|
||||||
|
# if the keys are identical and we require 'lt', we know
|
||||||
|
# only the left branch is required:
|
||||||
|
followB = j - ord(kind == CmpKind.lt and cmpRes == 0)
|
||||||
|
else:
|
||||||
|
# it's already greater, all others are greater too:
|
||||||
|
break
|
||||||
|
of CmpKind.ge, CmpKind.gt:
|
||||||
|
# want: key >= 10 or key > 10
|
||||||
|
# keys: 0 3 4 5 10 20
|
||||||
|
# keys: 20 30 40
|
||||||
|
|
||||||
proc getKey(c: Cursor): Key = discard
|
# Case A: all keys are smaller:
|
||||||
proc getVal(c: Cursor): Val = discard
|
if cmp(key, x.keys[x.m-1]) >= 0:
|
||||||
|
# --> use the very last branch
|
||||||
|
followA = x.m-1
|
||||||
|
followB = x.m-1
|
||||||
|
else:
|
||||||
|
# also covers case B: all keys are bigger --> use all branches
|
||||||
|
# we find the key that is bigger or equal to ours and from
|
||||||
|
# then on, follow every branch:
|
||||||
|
for j in 1..<x.m:
|
||||||
|
let cmpRes = cmp(key, x.keys[j])
|
||||||
|
if cmpRes <= 0:
|
||||||
|
# if the keys are identical and we need 'ge', we don't have
|
||||||
|
# to consider the 'j-1' branch:
|
||||||
|
followA = j - ord(kind != CmpKind.ge or cmpRes != 0)
|
||||||
|
# we know everything else is even bigger:
|
||||||
|
followB = x.m-1
|
||||||
|
break
|
||||||
|
of CmpKind.neq:
|
||||||
|
# neq: just follow all for now:
|
||||||
|
followA = 0
|
||||||
|
followB = x.m-1
|
||||||
|
# now recurse into the branches that hold candidates we're interested in:
|
||||||
|
for i in countdown(followB, followA):
|
||||||
|
c.stack.add(x.links[i])
|
||||||
|
# state stays stPop, but go on:
|
||||||
|
next(c, kind, key)
|
||||||
|
|
||||||
|
proc atEnd(c: Cursor): bool = c.state == stEnd
|
||||||
|
|
||||||
|
proc getKey(c: Cursor): Key =
|
||||||
|
assert c.state == stLeaf
|
||||||
|
result = c.n.keys[c.i]
|
||||||
|
|
||||||
|
proc getVal(c: Cursor): Val =
|
||||||
|
assert c.state == stLeaf
|
||||||
|
result = c.n.vals[c.i]
|
||||||
|
|
||||||
proc get(t: BTree; key: Key): Val = search(t.root, key, t.height)
|
proc get(t: BTree; key: Key): Val = search(t.root, key, t.height)
|
||||||
|
|
||||||
|
|
@ -491,4 +549,14 @@ when isMainModule:
|
||||||
echo " > 5"
|
echo " > 5"
|
||||||
don(b1.root, CmpKind.gt, "5", proc(k: Key; v: Val) = echo("k ", k, " = ", v))
|
don(b1.root, CmpKind.gt, "5", proc(k: Key; v: Val) = echo("k ", k, " = ", v))
|
||||||
|
|
||||||
|
echo "======================================================================"
|
||||||
|
var c = initCursor(b1.root)
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
next(c, CmpKind.le, "9")
|
||||||
|
if atEnd(c): break
|
||||||
|
echo "key ", getKey(c), " ", getVal(c)
|
||||||
|
if i > 30: break
|
||||||
|
inc i
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue