summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ecmas.html21
-rw-r--r--tests/ecmas.nim16
-rw-r--r--tests/gcbench.nim172
-rw-r--r--tests/hallo.nim34
-rw-r--r--tests/rectest.nim6
-rw-r--r--tests/tparscfg.nim25
-rw-r--r--tests/tparsopt.nim27
-rw-r--r--tests/tradix.nim319
-rw-r--r--tests/tstrset.nim76
-rw-r--r--tests/tstrtabs.nim12
10 files changed, 708 insertions, 0 deletions
diff --git a/tests/ecmas.html b/tests/ecmas.html
new file mode 100644
index 000000000..25eb93dc3
--- /dev/null
+++ b/tests/ecmas.html
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<!--  This has been written by hand. (c) 2008 Andreas Rumpf -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Nimrod ECMAScript Generator Test</title>
+<style type="text/css">
+span.DecNumber {color: blue}
+</style>
+<script src="rod_gen/ecmas.js" type="text/javascript"></script>
+</head>
+<body onload="OnLoad()">
+<form name="form1" action="ecmas.html">
+  <input type="text" name="input" size="3" />
+  <input type="button" value="Calculate square" onclick="OnButtonClick()" />
+</form>
+
+</body>
+</html>
diff --git a/tests/ecmas.nim b/tests/ecmas.nim
new file mode 100644
index 000000000..59e7ae1e8
--- /dev/null
+++ b/tests/ecmas.nim
@@ -0,0 +1,16 @@
+# This file tests the ECMAScript generator
+
+import
+  dom, strutils
+
+# We need to declare the used elements here. This is annoying but
+# prevents any kind of typo:
+var
+  inputElement {.importc: "document.form1.input", nodecl.}: ref TElement
+
+proc OnButtonClick() {.exportc.} =
+  var x: int = parseInt($inputElement.value)
+  echo($(x * x))
+
+proc OnLoad() {.exportc.} = 
+  echo("Welcome! Please take your time to fill in this formular!")
diff --git a/tests/gcbench.nim b/tests/gcbench.nim
new file mode 100644
index 000000000..4c94fe360
--- /dev/null
+++ b/tests/gcbench.nim
@@ -0,0 +1,172 @@
+# This is adapted from a benchmark written by John Ellis and Pete Kovac

+#   of Post Communications.

+#   It was modified by Hans Boehm of Silicon Graphics.

+#

+# 	This is no substitute for real applications. No actual application

+#	is likely to behave in exactly this way. However, this benchmark was

+#	designed to be more representative of real applications than other

+#	Java GC benchmarks of which we are aware.

+#	It attempts to model those properties of allocation requests that

+#	are important to current GC techniques.

+#	It is designed to be used either to obtain a single overall performance

+#	number, or to give a more detailed estimate of how collector

+#	performance varies with object lifetimes. It prints the time

+#	required to allocate and collect balanced binary trees of various

+#	sizes. Smaller trees result in shorter object lifetimes. Each cycle

+#	allocates roughly the same amount of memory.

+#	Two data structures are kept around during the entire process, so

+#	that the measured performance is representative of applications

+#	that maintain some live in-memory data. One of these is a tree

+#	containing many pointers. The other is a large array containing

+#	double precision floating point numbers. Both should be of comparable

+#	size.

+#

+#	The results are only really meaningful together with a specification

+#	of how much memory was used. It is possible to trade memory for

+#	better time performance. This benchmark should be run in a 32 MB

+#	heap, though we don't currently know how to enforce that uniformly.

+#

+#	Unlike the original Ellis and Kovac benchmark, we do not attempt

+# measure pause times. This facility should eventually be added back

+#	in. There are several reasons for omitting it for now.  The original

+#	implementation depended on assumptions about the thread scheduler

+#	that don't hold uniformly. The results really measure both the

+#	scheduler and GC. Pause time measurements tend to not fit well with

+#	current benchmark suites. As far as we know, none of the current

+#	commercial Java implementations seriously attempt to minimize GC pause

+#	times.

+#

+#	Known deficiencies:

+#		- No way to check on memory use

+#		- No cyclic data structures

+#		- No attempt to measure variation with object size

+#		- Results are sensitive to locking cost, but we dont

+#		  check for proper locking

+#

+

+import

+  strutils, times

+

+type

+  PNode = ref TNode

+  TNode {.final.} = object

+    left, right: PNode

+    i, j: int

+

+proc newNode(l, r: PNode): PNode =

+  new(result)

+  result.left = l

+  result.right = r

+

+const

+  kStretchTreeDepth = 18 # about 16Mb

+  kLongLivedTreeDepth = 16  # about 4Mb

+  kArraySize  = 500000  # about 4Mb

+  kMinTreeDepth = 4

+  kMaxTreeDepth = 16

+

+# Nodes used by a tree of a given size

+proc TreeSize(i: int): int = return ((1 shl (i + 1)) - 1)

+

+# Number of iterations to use for a given tree depth

+proc NumIters(i: int): int =

+  return 2 * TreeSize(kStretchTreeDepth) div TreeSize(i)

+

+# Build tree top down, assigning to older objects.

+proc Populate(iDepth: int, thisNode: PNode) =

+  if iDepth <= 0:

+    return

+  else:

+    new(thisNode.left)

+    new(thisNode.right)

+    Populate(iDepth-1, thisNode.left)

+    Populate(iDepth-1, thisNode.right)

+

+# Build tree bottom-up

+proc MakeTree(iDepth: int): PNode =

+  if iDepth <= 0:

+    new(result)

+  else:

+    return newNode(MakeTree(iDepth-1),

+                   MakeTree(iDepth-1))

+

+proc PrintDiagnostics() =

+  var

+    FreeMemory = getFreeMem()

+    TotalMemory = getTotalMem()

+

+  echo("Total memory available: " & $TotalMemory & " bytes")

+  echo("Free memory: " & $FreeMemory & " bytes")

+

+proc TimeConstruction(depth: int) =

+  var

+    root, tempTree: PNode

+    t: int

+    iNumIters: int

+

+  iNumIters = NumIters(depth)

+

+  echo("Creating " & $iNumIters & " trees of depth " & $depth)

+  t = getStartMilsecs()

+  for i in 0..iNumIters-1:

+    new(tempTree)

+    Populate(depth, tempTree)

+    tempTree = nil

+  echo("\tTop down construction took " &

+                     $(getStartMilsecs() - t) & "msecs")

+  t = getStartMilsecs()

+  for i in 0..iNumIters-1:

+    tempTree = MakeTree(depth)

+    tempTree = nil

+  echo("\tBottom up construction took " &

+                     $(getStartMilsecs() - t) & "msecs")

+

+type

+  tMyArray = seq[float]

+

+proc main() =

+  var

+    root, longLivedTree, tempTree: PNode

+    t: int

+    myarray: tMyArray

+

+  echo("Garbage Collector Test")

+  echo(" Stretching memory with a binary tree of depth " &

+       $kStretchTreeDepth)

+  PrintDiagnostics()

+  t = getStartMilsecs()

+

+  # Stretch the memory space quickly

+  tempTree = MakeTree(kStretchTreeDepth)

+  tempTree = nil

+

+  # Create a long lived object

+  echo(" Creating a long-lived binary tree of depth " &

+        $kLongLivedTreeDepth)

+  new(longLivedTree)

+  Populate(kLongLivedTreeDepth, longLivedTree)

+

+  # Create long-lived array, filling half of it

+  echo(" Creating a long-lived array of " & $kArraySize & " doubles")

+  myarray = []

+  setlength(myarray, kArraySize)

+  for i in 0..kArraySize div 2 -1:

+    myarray[i] = 1.0 / toFloat(i)

+

+  PrintDiagnostics()

+

+  var d = kMinTreeDepth

+  while d <= kMaxTreeDepth:

+    TimeConstruction(d)

+    inc(d, 2)

+

+  if longLivedTree == nil or myarray[1000] != 1.0/1000.0:

+    echo("Failed")

+    # fake reference to LongLivedTree

+    # and array to keep them from being optimized away

+

+  var elapsed = getStartMilsecs() - t

+  PrintDiagnostics()

+  echo("Completed in " & $elapsed & "ms.")

+

+main()

diff --git a/tests/hallo.nim b/tests/hallo.nim
new file mode 100644
index 000000000..070633793
--- /dev/null
+++ b/tests/hallo.nim
@@ -0,0 +1,34 @@
+# Hallo world program
+
+echo("Hi! What's your name?")
+var name = readLine(stdin)
+
+if name == "Andreas":
+  echo("What a nice name!")
+elif name == "":
+  echo("Don't you have a name?")
+else:
+  echo("Your name is not Andreas...")
+
+for i in 0..name.len-1:
+  if name[i] == 'm':
+    echo("hey, there is an *m* in your name!")
+
+echo("Please give your password: (12345)")
+var pw = readLine(stdin)
+
+while pw != "12345":
+  echo("Wrong password! Next try: ")
+  pw = readLine(stdin)
+
+echo("""Login complete!
+What do you want to do?
+delete-everything
+restart-computer
+go-for-a-walk""")
+
+case readline(stdin)
+of "delete-everything", "restart-computer":
+  echo("permission denied")
+of "go-for-a-walk":     echo("please yourself")
+else:                   echo("unknown command")
diff --git a/tests/rectest.nim b/tests/rectest.nim
new file mode 100644
index 000000000..f08306cfd
--- /dev/null
+++ b/tests/rectest.nim
@@ -0,0 +1,6 @@
+# Test the error message

+

+proc main() =

+  main()

+

+main()

diff --git a/tests/tparscfg.nim b/tests/tparscfg.nim
new file mode 100644
index 000000000..33347285c
--- /dev/null
+++ b/tests/tparscfg.nim
@@ -0,0 +1,25 @@
+
+import
+  os, parsecfg, strutils
+  
+var 
+  p: TCfgParser
+  
+if open(p, paramStr(1)): 
+  while true:
+    var e = next(p)
+    case e.kind
+    of cfgEof: 
+      echo("EOF!")
+      break
+    of cfgSectionStart:   ## a ``[section]`` has been parsed
+      echo("new section: " & e.section)
+    of cfgKeyValuePair:
+      echo("key-value-pair: " & e.key & ": " & e.value)
+    of cfgOption:
+      echo("command: " & e.key & ": " & e.value)
+    of cfgError:
+      echo(e.msg)
+  close(p)
+else:
+  echo("cannot open: " & paramStr(1))
diff --git a/tests/tparsopt.nim b/tests/tparsopt.nim
new file mode 100644
index 000000000..2b2da7e51
--- /dev/null
+++ b/tests/tparsopt.nim
@@ -0,0 +1,27 @@
+# Test the new parseopt module
+
+import
+  parseopt
+
+proc writeHelp() = 
+  writeln(stdout, "Usage: tparsopt [options] filename [options]")
+
+proc writeVersion() = 
+  writeln(stdout, "Version: 1.0.0")
+  
+var
+  filename = ""
+for kind, key, val in getopt():
+  case kind
+  of cmdArgument: 
+    filename = key
+  of cmdLongOption, cmdShortOption:
+    case key
+    of "help", "h": writeHelp()
+    of "version", "v": writeVersion()
+    else: 
+      writeln(stdout, "Unknown command line option: ", key, ": ", val)
+  of cmdEnd: assert(false) # cannot happen
+if filename == "":
+  # no filename has been given, so we show the help:
+  writeHelp()
diff --git a/tests/tradix.nim b/tests/tradix.nim
new file mode 100644
index 000000000..208f9ae9d
--- /dev/null
+++ b/tests/tradix.nim
@@ -0,0 +1,319 @@
+# 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..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..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
+    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..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
+    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
diff --git a/tests/tstrset.nim b/tests/tstrset.nim
new file mode 100644
index 000000000..76900bf63
--- /dev/null
+++ b/tests/tstrset.nim
@@ -0,0 +1,76 @@
+# test a simple yet highly efficient set of strings
+
+type
+  TRadixNodeKind = enum rnLinear, rnFull, rnLeaf
+  PRadixNode = ptr TRadixNode
+  TRadixNode = object
+    kind: TRadixNodeKind
+  TRadixNodeLinear = object of TRadixNode
+    len: byte
+    keys: array [0..31, char]
+    vals: array [0..31, PRadixNode]  
+  TRadixNodeFull = object of TRadixNode
+    b: array [char, PRadixNode]
+  TRadixNodeLeaf = object of TRadixNode
+    s: string
+  PRadixNodeLinear = ref TRadixNodeLinear
+  PRadixNodeFull = ref TRadixNodeFull
+  PRadixNodeLeaf = ref TRadixNodeLeaf
+    
+proc search(r: PRadixNode, s: string): PRadixNode =
+  var r = r
+  var i = 0
+  while r != nil:
+    case r.kind
+    of rnLinear:
+      var x = PRadixNodeLinear(r)
+      for j in 0..x.len-1:
+        if x.keys[j] == s[i]:
+          if s[i] == '\0': return r
+          r = x.vals[j]
+          inc(i)
+          break
+      break # character not found
+    of rnFull:
+      var x = PRadixNodeFull(r)
+      var y = x.b[s[i]]
+      if s[i] == '\0':
+        return if y != nil: r else: nil
+      r = y
+      inc(i)
+    of rnLeaf:
+      var x = PRadixNodeLeaf(r)
+      var j = 0
+      while true:
+        if x.s[j] != s[i]: return nil
+        if s[i] == '\0': return r
+        inc(j)
+        inc(i)
+
+proc in_Operator*(r: PRadixNode, s: string): bool =
+  return search(r, s) != nil
+
+proc testOrincl*(r: var PRadixNode, s: string): bool =
+  nil
+    
+proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s)
+
+proc excl*(r: var PRadixNode, s: string) =
+  x = search(r, s)
+  if x == nil: return
+  case x.kind
+  of rnLeaf: PRadixNodeLeaf(x).s = ""
+  of rnFull: PRadixNodeFull(x).b['\0'] = nil
+  of rnLinear:
+    var x = PRadixNodeLinear(x)
+    for i in 0..x.len-1:
+      if x.keys[i] == '\0':
+        swap(x.keys[i], x.keys[x.len-1])
+        dec(x.len)
+        break
+
+var
+  root: PRadixNode
+
+
+
diff --git a/tests/tstrtabs.nim b/tests/tstrtabs.nim
new file mode 100644
index 000000000..299db609d
--- /dev/null
+++ b/tests/tstrtabs.nim
@@ -0,0 +1,12 @@
+import strtabs
+
+var tab = newStringTable(["key1", "val1", "key2", "val2"], 
+                         modeStyleInsensitive)
+for i in 0..80:
+  tab["key_" & $i] = "value" & $i
+  
+for key, val in pairs(tab):
+  writeln(stdout, key, ": ", val)
+writeln(stdout, "length of table ", $tab.len)
+
+writeln(stdout, `%`("$key1 = $key2; ${PATH}", tab, {useEnvironment}))