summary refs log blame commit diff stats
path: root/tests/generics/tgeneric3.nim
blob: 963d0ccfb0277342bffd8e8480ed6b4beeb669c7 (plain) (tree)

































                                                        
                                                              
































                                                                                       
                                                     



                         
                                         

























                                                                     
                 




































































































































                                                                                  
               














































































































































































































































                                                                                                          
import strutils

type
  PNode[T,D] = ref TNode[T,D]
  TItem {.acyclic, pure, final, shallow.} [T,D] = object
        key: T
        value: D
        node: PNode[T,D]
        when not (D is string):
          val_set: Bool

  TItems[T,D] = seq[ref TItem[T,D]]
  TNode {.acyclic, pure, final, shallow.} [T,D] = object
        slots: TItems[T,D]
        left: PNode[T,D]
        count: Int32


  RPath[T,D] = tuple[
    Xi: Int,
    Nd: PNode[T,D] ]

const
  cLen1 = 2
  cLen2 = 16
  cLen3 = 32
  cLenCenter = 80
  clen4 = 96
  cLenMax = 128
  cCenter = cLenMax div 2

proc len[T,D] (n:PNode[T,D]): Int {.inline.} =
  return n.Count

proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard

proc clean[T: string|seq](o: var T) {.inline.} =
  o = nil

proc clean[T,D] (o: ref TItem[T,D]) {.inline.} = 
  when (D is string) :
    o.Value = nil
  else :
    o.val_set = false

proc isClean[T,D] (it: ref TItem[T,D]): Bool {.inline.} = 
  when (D is string) :
    return it.Value == nil
  else :
    return not it.val_set

proc isClean[T,D] (n: PNode[T,D], x: Int): Bool {.inline.} = 
  when (D is string) :
    return n.slots[x].Value == nil
  else :
    return not n.slots[x].val_set

proc setItem[T,D] (AKey: T, AValue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inline.} = 
  new(Result)
  Result.Key = AKey
  Result.Value = AValue
  Result.Node = ANode
  when not (D is string) :
    Result.val_set = true

proc cmp[T:Int8|Int16|Int32|Int64|Int] (a,b: T): T {.inline.} =
  return a-b

template binSearchImpl *(docmp: expr) {.immediate.} =
  var bFound = false
  result = 0
  var H = haystack.len -1
  while result <= H :
    var I {.inject.} = (result + H) shr 1
    var SW = docmp 
    if SW < 0: result = I + 1 
    else:
      H = I - 1
      if SW == 0 : bFound = True
  if bFound: inc(result)
  else: result = - result

proc bSearch[T,D] (haystack: PNode[T,D], needle:T): Int {.inline.} =
  binSearchImpl(haystack.slots[I].key.cmp(needle))

proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} =
  var w = n.slots[x]
  if w.Node != nil : 
    clean(w)
    return n
  dec(n.Count)
  if n.Count > 0 :
    for i in countup(x, n.Count -1) : n.slots[i] = n.slots[i + 1]
    n.slots[n.Count] = nil
    case n.Count 
    of cLen1 : setLen(n.slots, cLen1)
    of cLen2 : setLen(n.slots, cLen2)
    of cLen3 : setLen(n.slots, cLen3)
    of cLenCenter : setLen(n.slots, cLenCenter)
    of cLen4 : setLen(n.slots, cLen4)
    else: discard
    Result = n

  else :
    Result = n.Left
    n.slots = nil
    n.Left = nil

proc InternalDelete[T,D] (ANode: PNode[T,D], key: T, AValue: var D): PNode[T,D] = 
  var Path: array[0..20, RPath[T,D]]
  var n = ANode
  Result = n
  clean(AValue)
  var h = 0
  while n != nil:
    var x = bSearch(n, key)
    if x <= 0 :
      Path[h].Nd = n
      Path[h].Xi = - x
      inc(h)
      if x == 0 :
        n = n.Left
      else :
        x = (-x) -1
        if x < n.Count :
          n = n.slots[x].Node
        else :
          n = nil
    else : 
      dec(x)
      if isClean(n, x) : return
      AValue = n.slots[x].Value
      var n2 = DeleteItem(n, x)
      dec(h)
      while (n2 != n) and (h >=0) :
        n = n2 
        var w = addr Path[h]
        x  = w.Xi -1
        if x >= 0 :
          if (n == nil) and isClean(w.Nd, x) :
            n = w.Nd
            n.slots[x].Node = nil 
            n2 = DeleteItem(n, x)
          else :
            w.Nd.slots[x].Node = n
            return
        else :
          w.Nd.Left = n
          return
        dec(h)
      if h < 0:
        Result = n2
      return

proc InternalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} =
  var wn = n
  while wn != nil :
    var x = bSearch(wn, key)
    if x <= 0 :
      if x == 0 :
        wn = wn.Left
      else :
        x = (-x) -1
        if x < wn.Count : 
          wn = wn.slots[x].Node
        else :
          return nil

    else :
      return wn.slots[x - 1]
  return nil

proc traceTree[T,D](root: PNode[T,D]) =
  proc traceX(x: Int) = 
    write stdout, "("
    write stdout, x
    write stdout, ") "

  proc traceEl(el: ref TItem[T,D]) =
    write stdout, " key: "
    write stdout, el.Key
    write stdout, " value: "
    write stdout, el.Value


  proc traceln(space: string) =
    writeln stdout, ""
    write stdout, space

  proc doTrace(n: PNode[T,D], level: Int) =
    var space = repeatChar(2 * level)
    traceln(space)
    write stdout, "node: "
    if n == nil:
      writeln stdout, "is empty"
      return
    write stdout, n.Count
    write stdout, " elements: "
    if n.Left != nil:
      traceln(space)
      write stdout, "left: "
      doTrace(n.left, level +1)
    for i, el in n.slots :
      if el != nil and not isClean(el):
        traceln(space)
        traceX(i)
        if i >= n.Count: 
          write stdout, "error "
        else:
          traceEl(el)
          if el.Node != nil: doTrace(el.Node, level +1)
          else : write stdout, " empty "
      elif i < n.Count :
        traceln(space)
        traceX(i)
        write stdout, "clean: "
        when T is string :
          if el.Key != nil: write stdout, el.Key
        else : write stdout, el.Key
        if el.Node != nil: doTrace(el.Node, level +1)
        else : write stdout, " empty "
    writeln stdout,""

  doTrace(root, 0)

proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) =
  var x = - APath.Xi
  inc(APath.Nd.Count)
  case APath.Nd.Count 
  of cLen1: setLen(APath.Nd.slots, cLen2)
  of cLen2: setLen(APath.Nd.slots, cLen3)
  of cLen3: setLen(APath.Nd.slots, cLenCenter)
  of cLenCenter: setLen(APath.Nd.slots, cLen4)
  of cLen4: setLen(APath.Nd.slots, cLenMax)
  else: discard
  for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
  APath.Nd.slots[x] = setItem(AKey, AValue, ANode)


proc SplitPage[T,D](n, left: PNode[T,D], xi: Int, AKey:var T, AValue:var D): PNode[T,D] =
  var x = -Xi
  var it1: TItems[T,D]
  it1.newSeq(cLenCenter)
  new(Result)
  Result.slots.newSeq(cLenCenter)
  Result.Count = cCenter
  if x == cCenter:
    for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i])
    for i in 0..cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i])
    Result.Left = n
  else :
    if x < cCenter :
      for i in 0..x-1: shallowCopy(it1[i], left.slots[i])
      it1[x] = setItem(AKey, AValue, n)
      for i in x+1 .. cCenter -1: shallowCopy(it1[i], left.slots[i-1])
      var w = left.slots[cCenter -1]
      AKey = w.Key
      AValue = w.Value
      Result.Left = w.Node
      for i in 0..cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i])
    else :
      for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i])
      x = x - (cCenter + 1)
      for i in 0..x-1: shallowCopy(Result.slots[i], left.slots[cCenter + i + 1])
      Result.slots[x] = setItem(AKey, AValue, n)
      for i in x+1 .. cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i])
      var w = left.slots[cCenter]
      AKey = w.Key
      AValue = w.Value
      Result.Left = w.Node
  left.Count = cCenter
  shallowCopy(left.slots, it1)


proc InternalPut[T,D](ANode: ref TNode[T,D], AKey: T, AValue: D, OldValue: var D): ref TNode[T,D] =
  var h: Int
  var Path: array[0..30, RPath[T,D]]
  var left: PNode[T,D]
  var n = ANode


  Result = ANode
  h = 0
  while n != nil:
    var x = bSearch[T,D](n, AKey)
    if x <= 0 :
      Path[h].Nd = n
      Path[h].Xi = x
      inc(h) 
      if x == 0 :
        n = n.Left
      else :
        x = (-x) -1
        if x < n.Count :
          n = n.slots[x].Node
        else :
          n = nil
    else :
      var w = n.slots[x - 1]
      OldValue = w.Value
      w.Value = AValue
      return

  dec(h)
  left = nil
  var lKey = AKey
  var lValue = AValue
  while h >= 0 :
    if Path[h].Nd.Count < cLenMax :
      InsertItem(Path[h], n, lKey, lValue)
      return
    else :
      left = Path[h].Nd
      n = SplitPage(n, left, Path[h].Xi, lKey, lValue)
    dec(h)

  new(Result)
  Result.slots.newSeq(cLen1)
  Result.Count = 1
  Result.Left = left
  Result.slots[0] = setItem(lKey, lValue, n)


proc CleanTree[T,D](n: PNode[T,D]): PNode[T,D] =
  if n.Left != nil :
    n.Left = CleanTree(n.Left)
  for i in 0 .. n.Count - 1 :
    var w = n.slots[i]
    if w.Node != nil :
        w.Node = CleanTree(w.Node)
    clean(w.Value)
    clean(w.Key)
  n.slots = nil
  return nil


proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]): PNode[T,D] {.closure.} ): PNode[T,D] =
  if n != nil :
    if n.Left != nil :
      n.Left = VisitAllNodes(n.Left, visit)    
    for i in 0 .. n.Count - 1 :
      var w = n.slots[i]
      if w.Node != nil :
        w.Node = VisitAllNodes(w.Node, visit)    
    return visit(n)
  return nil

proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]) {.closure.} ) =
  if n != nil:
    if n.Left != nil :
      VisitAllNodes(n.Left, visit)    
    for i in 0 .. n.Count - 1 :
      var w = n.slots[i]
      if w.Node != nil :
        VisitAllNodes(w.Node, visit)    
    visit(n)

proc VisitAll[T,D](n: PNode[T,D], visit: proc(AKey: T, AValue: D) {.closure.} ) =
  if n != nil:
    if n.Left != nil :
      VisitAll(n.Left, visit) 
    for i in 0 .. n.Count - 1 :
      var w = n.slots[i]
      if not w.isClean :
        visit(w.Key, w.Value)   
      if w.Node != nil :
        VisitAll(w.Node, visit)    

proc VisitAll[T,D](n: PNode[T,D], visit: proc(AKey: T, AValue: var D):Bool {.closure.} ): PNode[T,D] =
  if n != nil:
    var n1 = n.Left
    if n1 != nil :
      var n2 = VisitAll(n1, visit) 
      if n1 != n2 :
        n.Left = n2
    var i = 0
    while i < n.Count :
      var w = n.slots[i]
      if not w.isClean :
        if visit(w.Key, w.Value) :
          Result = DeleteItem(n, i)
          if Result == nil : return
          dec(i)
      n1 = w.Node
      if n1 != nil :
        var n2 = VisitAll(n1, visit)
        if n1 != n2 :
          w.Node = n2
      inc(i)
  return n

iterator keys* [T,D] (n: PNode[T,D]): T =
  if n != nil :
    var Path: array[0..20, RPath[T,D]]
    var level = 0
    var nd = n
    var i = -1
    while true : 
      if i < nd.Count :
        Path[level].Nd = nd
        Path[level].Xi = i
        if i < 0 :
          if nd.Left != nil :
            nd = nd.Left
            inc(level)
          else : inc(i)
        else :
          var w = nd.slots[i]
          if not w.isClean() :
            yield w.Key
          if w.Node != nil :
            nd = w.Node
            i = -1
            inc(level)
          else : inc(i)
      else :
        dec(level)
        if level < 0 : break
        nd = Path[level].Nd
        i = Path[level].Xi
        inc(i)


when isMainModule:

  proc test() =
    var oldValue: Int
    var root = InternalPut[int, int](nil, 312, 312, oldValue)
    var someOtherRoot = InternalPut[string, int](nil, "312", 312, oldValue)
    var it1 = InternalFind(root, 312)
    echo it1.Value

    for i in 1..1_000_000:
      root = InternalPut(root, i, i, oldValue)

    var cnt = 0
    oldValue = -1
    when true : # code compiles, when this or the other when is switched to false
      for k in root.keys :
        if k <= oldValue :
          echo k
        oldValue = k
        inc(cnt)
      echo cnt
    when true :
      cnt = 0
      VisitAll(root, proc(key, val: int) = inc(cnt))
      echo cnt
      when true :
        root = VisitAll(root, proc(key: int, value: var int): bool =
          return key mod 2 == 0 )
      cnt = 0
      oldValue = -1
      VisitAll(root, proc(key: int, value: int) {.closure.} =
        if key <= oldValue :
          echo key
        oldValue = key
        inc(cnt) )
      echo cnt
      root = VisitAll(root, proc(key: int, value: var int): bool =
        return key mod 2 != 0 )
      cnt = 0
      oldValue = -1
      VisitAll(root, proc(key: int, value: int) {.closure.} =
        if key <= oldValue :
          echo "error ", key
        oldValue = key
        inc(cnt) )
      echo cnt
      #traceTree(root)



  test()