# implements and tests an efficient radix tree ## another method to store an efficient array of pointers: ## We use a radix tree with node compression. ## There are two node kinds: const bitsPerUnit = 8*sizeof(int) type TRadixNodeKind = enum rnLinear, rnFull, rnLeafBits, rnLeafLinear PRadixNode = ptr TRadixNode TRadixNode {.pure.} = object kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode len: byte keys: array [0..31, byte] vals: array [0..31, PRadixNode] TRadixNodeFull = object of TRadixNode b: array [0..255, PRadixNode] TRadixNodeLeafBits = object of TRadixNode b: array [0..7, int] TRadixNodeLeafLinear = object of TRadixNode len: byte keys: array [0..31, byte] var root: PRadixNode proc searchInner(r: PRadixNode, a: int): PRadixNode = case r.kind of rnLinear: var x = cast[ptr TRadixNodeLinear](r) for i in 0..ze(x.len)-1: if ze(x.keys[i]) == a: return x.vals[i] of rnFull: var x = cast[ptr TRadixNodeFull](r) return x.b[a] else: assert(false) proc testBit(w, i: int): bool {.inline.} = result = (w and (1 shl (i %% BitsPerUnit))) != 0 proc setBit(w: var int, i: int) {.inline.} = w = w or (1 shl (i %% bitsPerUnit)) proc resetBit(w: var int, i: int) {.inline.} = w = w and not (1 shl (i %% bitsPerUnit)) proc testOrSetBit(w: var int, i: int): bool {.inline.} = var x = (1 shl (i %% bitsPerUnit)) if (w and x) != 0: return true w = w or x proc searchLeaf(r: PRadixNode, a: int): bool = case r.kind of rnLeafBits: var x = cast[ptr TRadixNodeLeafBits](r) return testBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) for i in 0..ze(x.len)-1: if ze(x.keys[i]) == a: return true else: assert(false) proc exclLeaf(r: PRadixNode, a: int) = case r.kind of rnLeafBits: var x = cast[ptr TRadixNodeLeafBits](r) resetBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) var L = ze(x.len) for i in 0..L-1: if ze(x.keys[i]) == a: x.keys[i] = x.keys[L-1] dec(x.len) return else: assert(false) proc in_Operator*(r: PRadixNode, a: TAddress): bool = if r == nil: return false var x = searchInner(r, a shr 24 and 0xff) if x == nil: return false x = searchInner(x, a shr 16 and 0xff) if x == nil: return false x = searchInner(x, a shr 8 and 0xff) if x == nil: return false return searchLeaf(x, a and 0xff) proc excl*(r: PRadixNode, a: TAddress): bool = if r == nil: return false var x = searchInner(r, a shr 24 and 0xff) if x == nil: return false x = searchInner(x, a shr 16 and 0xff) if x == nil: return false x = searchInner(x, a shr 8 and 0xff) if x == nil: return false exclLeaf(x, a and 0xff) proc addLeaf(r: var PRadixNode, a: int): bool = if r == nil: # a linear node: var x = cast[ptr TRadixNodeLinear](alloc(sizeof(TRadixNodeLinear))) x.kind = rnLeafLinear x.len = 1'i8 x.keys[0] = toU8(a) r = x return false # not already in set case r.kind of rnLeafBits: var x = cast[ptr TRadixNodeLeafBits](r) return testOrSetBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) var L = ze(x.len) for i in 0..L-1: if ze(x.keys[i]) == a: return true if L <= high(x.keys): x.keys[L] = toU8(a) inc(x.len) else: # transform into a full node: var y = cast[ptr TRadixNodeLeafBits](alloc0(sizeof(TRadixNodeLeafBits))) y.kind = rnLeafBits for i in 0..ze(x.len)-1: var u = ze(x.keys[i]) setBit(y.b[u /% BitsPerUnit], u) setBit(y.b[a /% BitsPerUnit], a) dealloc(r) r = y else: assert(false) proc addInner(r: var PRadixNode, a: int, d: int): bool = if d == 0: return addLeaf(r, a and 0xff) var k = a shr d and 0xff if r == nil: # a linear node: var x = cast[ptr TRadixNodeLinear](alloc(sizeof(TRadixNodeLinear))) x.kind = rnLinear x.len = 1'i8 x.keys[0] = toU8(k) r = x return addInner(x.vals[0], a, d-8) case r.kind of rnLinear: var x = cast[ptr TRadixNodeLinear](r) var L = ze(x.len) for i in 0..L-1: if ze(x.keys[i]) == k: # already exists return addInner(x.vals[i], a, d-8) if L <= high(x.keys): x.keys[L] = toU8(k) inc(x.len) return addInner(x.vals[L], a, d-8) else: # transform into a full node: var y = cast[ptr TRadixNodeFull](alloc0(sizeof(TRadixNodeFull))) y.kind = rnFull for i in 0..L-1: y.b[ze(x.keys[i])] = x.vals[i] dealloc(r) r = y return addInner(y.b[k], a, d-8) of rnFull: var x = cast[ptr TRadixNodeFull](r) return addInner(x.b[k], a, d-8) else: assert(false) proc incl*(r: var PRadixNode, a: TAddress) {.inline.} = discard addInner(r, a, 24) proc testOrIncl*(r: var PRadixNode, a: TAddress): bool {.inline.} = return addInner(r, a, 24) iterator innerElements(r: PRadixNode): tuple[prefix: int, n: PRadixNode] = if r != nil: case r.kind of rnFull: var r = cast[ptr TRadixNodeFull](r) for i in 0..high(r.b): if r.b[i] != nil: yield (i, r.b[i]) of rnLinear: var r = cast[ptr TRadixNodeLinear](r) for i in 0..ze(r.len)-1: yield (ze(r.keys[i]), r.vals[i]) else: assert(false) iterator leafElements(r: PRadixNode): int = if r != nil: case r.kind of rnLeafBits: var r = cast[ptr TRadixNodeLeafBits](r) # iterate over any bit: for i in 0..high(r.b): if r.b[i] != 0: # test all bits for zero for j in 0..BitsPerUnit-1: if testBit(r.b[i], j): yield i*BitsPerUnit+j of rnLeafLinear: var r = cast[ptr TRadixNodeLeafLinear](r) for i in 0..ze(r.len)-1: yield ze(r.keys[i]) else: assert(false) iterator elements*(r: PRadixNode): TAddress {.inline.} = for p1, n1 in innerElements(r): for p2, n2 in innerElements(n1): for p3, n3 in innerElements(n2): for p4 in leafElements(n3): yield p1 shl 24 or p2 shl 16 or p3 shl 8 or p4 proc main() = const numbers = [128, 1, 2, 3, 4, 255, 17, -8, 45, 19_000] var r: PRadixNode = nil for x in items(numbers): echo testOrIncl(r, x) for x in elements(r): echo(x) main() when false: proc traverse(r: PRadixNode, prefix: int, d: int) = if r == nil: return case r.kind of rnLeafBits: assert(d == 0) var x = cast[ptr TRadixNodeLeafBits](r) # iterate over any bit: for i in 0..high(x.b): if x.b[i] != 0: # test all bits for zero for j in 0..BitsPerUnit-1: if testBit(x.b[i], j): visit(prefix or i*BitsPerUnit+j) of rnLeafLinear: assert(d == 0) var x = cast[ptr TRadixNodeLeafLinear](r) for i in 0..ze(x.len)-1: visit(prefix or ze(x.keys[i])) of rnFull: var x = cast[ptr TRadixNodeFull](r) for i in 0..high(r.b): if r.b[i] != nil: traverse(r.b[i], prefix or (i shl d), d-8) of rnLinear: var x = cast[ptr TRadixNodeLinear](r) for i in 0..ze(x.len)-1: traverse(x.vals[i], prefix or (ze(x.keys[i]) shl d), d-8) type TRadixIter {.final.} = object r: PRadixNode p: int x: int proc init(i: var TRadixIter, r: PRadixNode) = i.r = r i.x = 0 i.p = 0 proc nextr(i: var TRadixIter): PRadixNode = if i.r == nil: return nil case i.r.kind of rnFull: var r = cast[ptr TRadixNodeFull](i.r) while i.x <= high(r.b): if r.b[i.x] != nil: i.p = i.x return r.b[i.x] inc(i.x) of rnLinear: var r = cast[ptr TRadixNodeLinear](i.r) if i.x < ze(r.len): i.p = ze(r.keys[i.x]) result = r.vals[i.x] inc(i.x) else: assert(false) proc nexti(i: var TRadixIter): int = result = -1 case i.r.kind of rnLeafBits: var r = cast[ptr TRadixNodeLeafBits](i.r) # iterate over any bit: for i in 0..high(r.b): if x.b[i] != 0: # test all bits for zero for j in 0..BitsPerUnit-1: if testBit(x.b[i], j): visit(prefix or i*BitsPerUnit+j) of rnLeafLinear: var r = cast[ptr TRadixNodeLeafLinear](i.r) if i.x < ze(r.len): result = ze(r.keys[i.x]) inc(i.x) iterator elements(r: PRadixNode): TAddress {.inline.} = var a, b, c, d: TRadixIter init(a, r) while true: var x = nextr(a) if x != nil: init(b, x) while true: var y = nextr(b) if y != nil: init(c, y) while true: var z = nextr(c) if z != nil: init(d, z) while true: var q = nexti(d) if q != -1: yield a.p shl 24 or b.p shl 16 or c.p shl 8 or q