nim-stew/stew/sorted_set/rbtree_find.nim

234 lines
6.6 KiB
Nim

# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
{.push raises: [].}
import
results,
./rbtree_desc
# ----------------------------------------------------------------------- ------
# Public
# ------------------------------------------------------------------------------
func rbTreeFindEq*[C,K](rbt: RbTreeRef[C,K]; key: K): RbResult[C] =
## Generic red-black tree function. Search for a data container `casket` of
## type `C` in the red black tree which matches the argument `key`,
## i.e. `rbt.cmp(casket,key) == 0`. If found, this data container `casket` is
## returned, otherwise an error code is returned.
##
## :Ackn:
## `jsw_rbfind()` from jsw_rbtree.c from captured C library
## `jsw_rbtree.zip <https://web.archive.org/web/20160428112900/http://eternallyconfuzzled.com/libs/jsw_rbtree.zip>`_.
##
if rbt.root.isNil:
return err(rbEmptyTree)
if not rbt.cache.isNil:
if rbt.cmp(rbt.cache.casket,key) == 0:
return ok(rbt.cache.casket)
var
q = rbt.root
while not q.isNil:
let diff = rbt.cmp(q.casket,key)
if diff == 0:
return ok(q.casket)
# FIXME: If the tree supports duplicates, they should be
# chained to the right subtree for this to work
let dir2 = (diff < 0).toDir
q = q.link[dir2]
return err(rbNotFound)
func rbTreeFindGe*[C,K](rbt: RbTreeRef[C,K]; key: K): RbResult[C] =
## Generic red-black tree function. Search for the *smallest* data container
## `casket` of type `C` which is *greater or equal* to the specified argument
## `key` in the red black-tree. If such a data container is found in the
## tree it is returned, otherwise an error code is returned.
##
## If found in the tree, this data container `casket` satisfies
## `0 <= cmp(casket,key)` and there is no *smaller* data container relative
## to `casket` in the red-black tree satisfying this condition.
##
##
## For a more formal reasoning of *smaller* and *greater*, consider the
## injection `mkc:K -> C` which is used to create data containers of type `C`
## from a key of type `K`. Let `mkc':K -> C'` be the map onto the
## equivalence class `C'` where `x` is in a class `mkc'(key)` if
## `cmp(x,key) == 0` (i.e. `x` is a later modification of a data container
## originally created as `mkc(key)`.)
##
## Then `mkc'` is an isomorphism.and there is the natural order relation on
## `C'` which is extended from the order relation on `K`. So the returned
## result is `min(v' of C': key <= v')` which is `mkc(key)` apart from
## data container modifications.
##
if rbt.root.isNil:
return err(rbEmptyTree)
# Always: not itemOk or key <= item
var
itemOk = false
item: C
q = rbt.root
while true:
var
nxt: RbNodeRef[C]
diff = rbt.cmp(q.casket,key)
if 0 < diff: # key < q.casket
itemOk = true # remember item
item = q.casket # now: key < item
nxt = q.linkLeft # move left => get strictly smaller items
elif diff < 0:
nxt = q.linkRight # move right => see strictly larger items
else:
return ok(q.casket)
if nxt.isNil:
if itemOk:
return ok(item)
break
q = nxt
# End while
return err(rbNotFound)
func rbTreeFindGt*[C,K](rbt: RbTreeRef[C,K]; key: K): RbResult[C] =
## Generic red-black tree function. Search for the *smallest* data container
## of type `C` which is *strictly greater* than the specified argument `key`
## in the red-black tree.
##
## See comments on `rbTreeFindGe()` for a formal definition of how to apply
## an order relation on `C`.
if rbt.root.isNil:
return err(rbEmptyTree)
# Always: not itemOk or key < item
var
itemOk = false
item: C
q = rbt.root
while true:
var
nxt: RbNodeRef[C]
diff = rbt.cmp(q.casket,key)
if 0 < diff: # key < q.casket
itemOk = true # remember item
item = q.casket # now: key < item
nxt = q.linkLeft # move left => get strictly smaller items
else:
nxt = q.linkRight # move right => see probably larger items
if nxt.isNil:
if itemOk:
return ok(item)
break
q = nxt
# End while
return err(rbNotFound)
func rbTreeFindLe*[C,K](rbt: RbTreeRef[C,K]; key: K): RbResult[C] =
## Generic red-black tree function. Search for the *greatest* data container
## of type `C` which is *less than or equal* to the specified argument
## `key` in the red-black tree.
##
## See comments on `rbTreeFindGe()` for a formal definition of how to apply
## an order relation on `C`.
if rbt.root.isNil:
return err(rbEmptyTree)
# Always: not itemOk or item < key
var
itemOk = false
item: C
q = rbt.root
while true:
var
nxt: RbNodeRef[C]
diff = rbt.cmp(q.casket,key)
if diff < 0: # q.casket < key
itemOk = true # remember item
item = q.casket # now: item < key
nxt = q.linkRight # move right => get strictly larger items
elif 0 < diff:
nxt = q.linkLeft # move left => see strictly smaller items
else:
return ok(q.casket)
if nxt.isNil:
if itemOk:
return ok(item)
break
q = nxt
# End while
return err(rbNotFound)
func rbTreeFindLt*[C,K](rbt: RbTreeRef[C,K]; key: K): RbResult[C] =
## Generic red-black tree function. Search for the *greatest* data container
## of type `C` which is *strictly less* than the specified argument `key` in
## the red-black tree.
##
## See comments on `rbTreeFindGe()` for a formal definition of how to apply
## an order relation on `C`.
if rbt.root.isNil:
return err(rbEmptyTree)
# Always: not itemOk or item < key
var
itemOk = false
item: C
q = rbt.root
while true:
var
nxt: RbNodeRef[C]
diff = rbt.cmp(q.casket,key)
if diff < 0: # q.casket < key
itemOk = true # remember item
item = q.casket # now: item < key
nxt = q.linkRight # move right => get larger items
else:
nxt = q.linkLeft # move left => see probably smaller items
if nxt.isNil:
if itemOk:
return ok(item)
break
q = nxt
# End while
return err(rbNotFound)
# ----------------------------------------------------------------------- ------
# End
# ------------------------------------------------------------------------------