summary refs log blame commit diff stats
path: root/tests/accept/compile/tradix.nim
blob: e7ca210e4fccb803d6dd8f7e52e88bb9e1e0c2b7 (plain) (tree)
































                                                                  
                             


























                                                         
                             










































                                                                       
                


















                                                                              
                               














                                                                       
                
















































































































































































                                                                           
# 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