summary refs log tree commit diff stats
path: root/tests/generics/tgeneric3.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/generics/tgeneric3.nim')
-rw-r--r--tests/generics/tgeneric3.nim487
1 files changed, 487 insertions, 0 deletions
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
new file mode 100644
index 000000000..29a73afc6
--- /dev/null
+++ b/tests/generics/tgeneric3.nim
@@ -0,0 +1,487 @@
+discard """
+output: '''
+312
+1000
+1000
+500
+0
+'''
+"""
+
+import strutils
+
+type
+  PNode[T,D] = ref TNode[T,D]
+  TItem[T,D] {.acyclic, pure, final, shallow.} = 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[T,D] {.acyclic, pure, final, shallow.} = 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: SomeOrdinal|SomeNumber](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: untyped) =
+  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 = @[]
+    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) =
+    writeLine stdout, ""
+    write stdout, space
+
+  proc doTrace(n: PNode[T,D], level: int) =
+    var space = spaces(2 * level)
+    traceln(space)
+    write stdout, "node: "
+    if n == nil:
+      writeLine 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 "
+    writeLine 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): APath.Nd.slots[i] = move 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: 
+      it1[i] = move left.slots[i]
+    for i in 0..cCenter-1:
+      result.slots[i] = move left.slots[cCenter + i]
+    result.left = n
+  else :
+    if x < cCenter :
+      for i in 0..x-1:
+        it1[i] = move left.slots[i]
+      it1[x] = setItem(Akey, Avalue, n)
+      for i in x+1 .. cCenter-1:
+        it1[i] = move 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:
+        result.slots[i] = move left.slots[cCenter + i]
+    else :
+      for i in 0..cCenter-1:
+        it1[i] = move left.slots[i]
+      x = x - (cCenter + 1)
+      for i in 0..x-1:
+        result.slots[i] = move left.slots[cCenter + i + 1]
+      result.slots[x] = setItem(Akey, Avalue, n)
+      for i in x+1 .. cCenter-1:
+        result.slots[i] = move left.slots[cCenter + i]
+      var w = left.slots[cCenter]
+      Akey = w.key
+      Avalue = w.value
+      result.left = w.node
+  left.count = cCenter
+  left.slots = move 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)
+
+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:
+    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()