summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2011-06-07 03:37:36 +0200
committerAraq <rumpf_a@web.de>2011-06-07 03:37:36 +0200
commit3bc821aa5c676869d65f1448a005dfd47f197f61 (patch)
tree16b757f5f8bcab86e46b677b8b8becd63ff04bed
parent42eb21be7b422b47732a56a1b8420b6f1b63f2c6 (diff)
downloadNim-3bc821aa5c676869d65f1448a005dfd47f197f61.tar.gz
basic generic collections implemented and tested
-rwxr-xr-xcompiler/semexprs.nim8
-rwxr-xr-xlib/pure/collections/lists.nim55
-rw-r--r--lib/pure/collections/sets.nim231
-rw-r--r--lib/pure/collections/tables.nim81
-rw-r--r--tests/accept/run/tlists.nim66
-rw-r--r--tests/accept/run/tsets2.nim62
-rw-r--r--tests/accept/run/ttables.nim82
-rwxr-xr-xtodo.txt18
-rwxr-xr-xweb/news.txt3
-rwxr-xr-xweb/nimrod.ini1
10 files changed, 512 insertions, 95 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 25272b37d..9c1b89fe9 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -83,11 +83,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     markUsed(n, s)
     result = newSymNode(s, n.info)
 
-proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) = 
+proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) =
   var diff = inheritanceDiff(castDest, src)
-  if diff == high(int): 
-    GlobalError(info, errGenerated, `%`(MsgKindToString(errIllegalConvFromXtoY), [
-        typeToString(src), typeToString(castDest)]))
+  if diff == high(int):
+    GlobalError(info, errGenerated, MsgKindToString(errIllegalConvFromXtoY) % [
+        src.typeToString, castDest.typeToString])
   
 proc checkConvertible(info: TLineInfo, castDest, src: PType) = 
   const 
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 10caac336..55c3d6ad9 100755
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -12,28 +12,28 @@
 ## be manipulated directly for efficiency.
 
 type
-  TDoublyLinkedNode*[T] {.pure, 
-      final.} = object ## a node a doubly linked list consists of
+  TDoublyLinkedNode* {.pure, 
+      final.}[T] = object ## a node a doubly linked list consists of
     next*, prev*: ref TDoublyLinkedNode[T]
     value*: T
   PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T]
 
-  TSinglyLinkedNode*[T] {.pure, 
-      final.} = object ## a node a singly linked list consists of
+  TSinglyLinkedNode* {.pure, 
+      final.}[T] = object ## a node a singly linked list consists of
     next*: ref TSinglyLinkedNode[T]
     value*: T
   PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T]
 
-  TSinglyLinkedList*[T] {.pure, final.} = object ## a singly linked list
+  TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list
     head*, tail*: PSinglyLinkedNode[T]
   
-  TDoublyLinkedList*[T] {.pure, final.} = object ## a doubly linked list
+  TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list
     head*, tail*: PDoublyLinkedNode[T]
 
-  TSinglyLinkedRing*[T] {.pure, final.} = object ## a singly linked ring
+  TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring
     head*: PSinglyLinkedNode[T]
   
-  TDoublyLinkedRing*[T] {.pure, final.} = object ## a doubly linked ring
+  TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring
     head*: PDoublyLinkedNode[T]
     
 proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] =
@@ -240,17 +240,15 @@ proc prepend*[T](L: var TSinglyLinkedRing[T], value: T) =
 
 proc append*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
   ## appends a node `n` to `L`. Efficiency: O(1).
-  if L.tail != nil: 
-    L.tail.next = n
-    n.prev = L.tail
+  if L.head != nil:
     n.next = L.head
+    n.prev = L.head.prev
+    L.head.prev.next = n
+    L.head.prev = n
   else:
-    # both head and tail are nil:
-    assert L.head == nil
-    L.head = n
     n.prev = n
     n.next = n
-  L.tail = n
+    L.head = n
 
 proc append*[T](L: var TDoublyLinkedRing[T], value: T) = 
   ## appends a value to `L`. Efficiency: O(1).
@@ -259,13 +257,11 @@ proc append*[T](L: var TDoublyLinkedRing[T], value: T) =
 proc prepend*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
   ## prepends a node `n` to `L`. Efficiency: O(1).
   if L.head != nil: 
-    L.head.prev = n
-    n.prev = L.tail
     n.next = L.head
+    n.prev = L.head.prev
+    L.head.prev.next = n
+    L.head.prev = n
   else:
-    # both head and tail are nil:
-    assert L.tail == nil
-    L.tail = n
     n.prev = n
     n.next = n
   L.head = n
@@ -276,19 +272,14 @@ proc prepend*[T](L: var TDoublyLinkedRing[T], value: T) =
   
 proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = 
   ## removes `n` from `L`. Efficiency: O(1).
-  if n == L.tail: 
-    if n == L.head:
-      # only element:
-      L.tail = nil
-      L.head = nil
-    else:
-      L.tail = n.prev
-  elif n == L.head: 
-    L.head = n.next
   n.next.prev = n.prev
   n.prev.next = n.next
-  # break cycles for the GC; not necessary, but might help:
-  n.next = nil
-  n.prev = nil
+  if n == L.head: 
+    var p = L.head.prev
+    if p == L.head: 
+      # only one element left:
+      L.head = nil
+    else:
+      L.head = L.head.prev
 
 
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
new file mode 100644
index 000000000..331881d88
--- /dev/null
+++ b/lib/pure/collections/sets.nim
@@ -0,0 +1,231 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## The ``sets`` module implements an efficient hash set and ordered hash set.
+##
+## Note: The data types declared here have *value semantics*: This means that
+## ``=`` performs a copy of the hash table. If you are overly concerned with
+## efficiency and know what you do (!), you can define the symbol
+## ``shallowADT`` to compile a version that uses shallow copies instead.
+
+import
+  os, hashes, math
+
+when defined(shallowADT):
+  {.pragma: myShallow, shallow.}
+else:
+  {.pragma: myShallow.}
+
+type
+  TSlotEnum = enum seEmpty, seFilled, seDeleted
+  TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A]
+  TKeyValuePairSeq[A] = seq[TKeyValuePair[A]]
+  TSet* {.final, myShallow.}[A] = object
+    data: TKeyValuePairSeq[A]
+    counter: int
+
+proc len*[A](s: TSet[A]): int =
+  ## returns the number of keys in `s`.
+  result = s.counter
+
+proc card*[A](s: TSet[A]): int =
+  ## alias for `len`.
+  result = s.counter
+
+iterator items*[A](s: TSet[A]): A =
+  ## iterates over any key in the table `t`.
+  for h in 0..high(s.data):
+    if s.data[h].slot == seFilled: yield s.data[h].key
+
+const
+  growthFactor = 2
+
+proc mustRehash(length, counter: int): bool {.inline.} =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+proc nextTry(h, maxHash: THash): THash {.inline.} =
+  result = ((5 * h) + 1) and maxHash
+
+template rawGetImpl() =
+  var h: THash = hash(key) and high(s.data) # start with real hash value
+  while s.data[h].slot != seEmpty:
+    if s.data[h].key == key and s.data[h].slot == seFilled:
+      return h
+    h = nextTry(h, high(s.data))
+  result = -1
+
+template rawInsertImpl() =
+  var h: THash = hash(key) and high(data)
+  while data[h].slot == seFilled:
+    h = nextTry(h, high(data))
+  data[h].key = key
+  data[h].slot = seFilled
+
+proc RawGet[A](s: TSet[A], key: A): int =
+  rawGetImpl()
+
+proc contains*[A](s: TSet[A], key: A): bool =
+  ## returns true iff `key` is in `s`.
+  var index = RawGet(t, key)
+  result = index >= 0
+
+proc RawInsert[A](s: var TSet[A], data: var TKeyValuePairSeq[A], key: A) =
+  rawInsertImpl()
+
+proc Enlarge[A](s: var TSet[A]) =
+  var n: TKeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  for i in countup(0, high(s.data)):
+    if s.data[i].slot == seFilled: RawInsert(s, n, s.data[i].key)
+  swap(s.data, n)
+
+template inclImpl() =
+  var index = RawGet(s, key)
+  if index < 0:
+    if mustRehash(len(s.data), s.counter): Enlarge(s)
+    RawInsert(s, s.data, key)
+    inc(s.counter)
+
+template containsOrInclImpl() =
+  var index = RawGet(s, key)
+  if index >= 0:
+    result = true
+  else:
+    if mustRehash(len(s.data), s.counter): Enlarge(s)
+    RawInsert(s, s.data, key)
+    inc(s.counter)
+
+proc incl*[A](s: var TSet[A], key: A) =
+  ## includes an element `key` in `s`.
+  inclImpl()
+
+proc excl*[A](s: var TSet[A], key: A) =
+  ## excludes `key` from the set `s`.
+  var index = RawGet(t, key)
+  if index >= 0:
+    s.data[index].slot = seDeleted
+    dec(s.counter)
+
+proc containsOrIncl*[A](s: var TSet[A], key: A): bool =
+  ## returns true if `s` contains `key`, otherwise `key` is included in `s`
+  ## and false is returned.
+  containsOrInclImpl()
+
+proc initSet*[A](initialSize=64): TSet[A] =
+  ## creates a new hash set that is empty. `initialSize` needs to be
+  ## a power of two.
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  newSeq(result.data, initialSize)
+
+proc toSet*[A](keys: openarray[A]): TSet[A] =
+  ## creates a new hash set that contains the given `keys`.
+  result = initSet[A](nextPowerOfTwo(keys.len+10))
+  for key in items(keys): result.incl(key)
+
+template dollarImpl(): stmt =
+  result = "{"
+  for key in items(s):
+    if result.len > 1: result.add(", ")
+    result.add($key)
+  result.add("}")
+
+proc `$`*[A](s: TSet[A]): string =
+  ## The `$` operator for hash sets.
+  dollarImpl()
+
+# ------------------------------ ordered table ------------------------------
+
+type
+  TOrderedKeyValuePair[A] = tuple[
+    slot: TSlotEnum, next: int, key: A]
+  TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]]
+  TOrderedSet* {.
+      final, myShallow.}[A] = object ## set that remembers insertion order
+    data: TOrderedKeyValuePairSeq[A]
+    counter, first, last: int
+
+proc len*[A](s: TOrderedSet[A]): int {.inline.} =
+  ## returns the number of keys in `s`.
+  result = t.counter
+
+proc card*[A](s: TOrderedSet[A]): int {.inline.} =
+  ## alias for `len`.
+  result = t.counter
+
+template forAllOrderedPairs(yieldStmt: stmt) =
+  var h = s.first
+  while h >= 0:
+    var nxt = s.data[h].next
+    if s.data[h].slot == seFilled: yieldStmt
+    h = nxt
+
+iterator items*[A](s: TOrderedSet[A]): A =
+  ## iterates over any key in the set `s` in insertion order.
+  forAllOrderedPairs:
+    yield s.data[h].key
+
+proc RawGet[A](s: TOrderedSet[A], key: A): int =
+  rawGetImpl()
+
+proc contains*[A](s: TOrderedSet[A], key: A): bool =
+  ## returns true iff `key` is in `s`.
+  var index = RawGet(s, key)
+  result = index >= 0
+
+proc RawInsert[A](s: var TOrderedSet[A], 
+                  data: var TOrderedKeyValuePairSeq[A], key: A) =
+  rawInsertImpl()
+  data[h].next = -1
+  if s.first < 0: s.first = h
+  if s.last >= 0: data[s.last].next = h
+  s.last = h
+
+proc Enlarge[A](s: var TOrderedSet[A]) =
+  var n: TOrderedKeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  var h = s.first
+  s.first = -1
+  s.last = -1
+  while h >= 0:
+    var nxt = s.data[h].next
+    if s.data[h].slot == seFilled: 
+      RawInsert(s, n, s.data[h].key)
+    h = nxt
+  swap(s.data, n)
+
+proc incl*[A](s: var TOrderedSet[A], key: A) =
+  ## includes an element `key` in `s`.
+  inclImpl()
+
+proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool =
+  ## returns true if `s` contains `key`, otherwise `key` is included in `s`
+  ## and false is returned.
+  containsOrInclImpl()
+
+proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] =
+  ## creates a new ordered hash set that is empty. `initialSize` needs to be
+  ## a power of two.
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  result.first = -1
+  result.last = -1
+  newSeq(result.data, initialSize)
+
+proc toOrderedSet*[A](keys: openarray[A]): TOrderedSet[A] =
+  ## creates a new ordered hash set that contains the given `keys`.
+  result = initOrderedSet[A](nextPowerOfTwo(keys.len+10))
+  for key in items(keys): result.incl(key)
+
+proc `$`*[A](s: TOrderedSet[A]): string =
+  ## The `$` operator for ordered hash sets.
+  dollarImpl()
+
+
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index f132051da..f817ec800 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2011 Andreas Rumpf, Dominik Picheta
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -12,7 +12,7 @@
 ##
 ## Note: The data types declared here have *value semantics*: This means that
 ## ``=`` performs a copy of the hash table. If you are overly concerned with
-## efficiency and don't need this behaviour, you can define the symbol
+## efficiency and know what you do (!), you can define the symbol
 ## ``shallowADT`` to compile a version that uses shallow copies instead.
 
 import
@@ -123,7 +123,7 @@ proc del*[A, B](t: var TTable[A, B], key: A) =
     dec(t.counter)
 
 proc initTable*[A, B](initialSize=64): TTable[A, B] =
-  ## creates a new hash table table that is empty. `initialSize` needs to be
+  ## creates a new hash table that is empty. `initialSize` needs to be
   ## a power of two.
   assert isPowerOfTwo(initialSize)
   result.counter = 0
@@ -132,7 +132,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] =
 proc toTable*[A, B](pairs: openarray[tuple[key: A, 
                     val: B]]): TTable[A, B] =
   ## creates a new hash table that contains the given `pairs`.
-  result = initTable[A](nextPowerOfTwo(pairs.len+10))
+  result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
   for key, val in items(pairs): result[key] = val
 
 template dollarImpl(): stmt =
@@ -148,7 +148,7 @@ template dollarImpl(): stmt =
     result.add("}")
 
 proc `$`*[A, B](t: TTable[A, B]): string =
-  ## The `$` operator for string tables.
+  ## The `$` operator for hash tables.
   dollarImpl()
 
 # ------------------------------ ordered table ------------------------------
@@ -167,11 +167,11 @@ proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
   result = t.counter
 
 template forAllOrderedPairs(yieldStmt: stmt) =
-  var i = t.first
-  while i >= 0:
-    var nxt = t.data[i].next
+  var h = t.first
+  while h >= 0:
+    var nxt = t.data[h].next
     if t.data[h].slot == seFilled: yieldStmt
-    i = nxt
+    h = nxt
 
 iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: B] =
   ## iterates over any (key, value) pair in the table `t` in insertion
@@ -204,23 +204,29 @@ proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool =
   ## returns true iff `key` is in the table `t`.
   result = rawGet(t, key) >= 0
 
-proc RawInsert[A, B](t: TOrderedTable[A, B], 
+proc RawInsert[A, B](t: var TOrderedTable[A, B], 
                      data: var TOrderedKeyValuePairSeq[A, B],
                      key: A, val: B) =
   rawInsertImpl()
   data[h].next = -1
-  if first < 0: first = h
-  if last >= 0: data[last].next = h
-  lastEntry = h
+  if t.first < 0: t.first = h
+  if t.last >= 0: data[t.last].next = h
+  t.last = h
 
-proc Enlarge[A, B](t: TOrderedTable[A, B]) =
+proc Enlarge[A, B](t: var TOrderedTable[A, B]) =
   var n: TOrderedKeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
-  forAllOrderedPairs:
-    RawInsert(t, n, t.data[h].key, t.data[h].val)
+  var h = t.first
+  t.first = -1
+  t.last = -1
+  while h >= 0:
+    var nxt = t.data[h].next
+    if t.data[h].slot == seFilled: 
+      RawInsert(t, n, t.data[h].key, t.data[h].val)
+    h = nxt
   swap(t.data, n)
 
-proc `[]=`*[A, B](t: TOrderedTable[A, B], key: A, val: B) =
+proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
   ## puts a (key, value)-pair into `t`.
   putImpl()
 
@@ -240,7 +246,7 @@ proc toOrderedTable*[A, B](pairs: openarray[tuple[key: A,
   for key, val in items(pairs): result[key] = val
 
 proc `$`*[A, B](t: TOrderedTable[A, B]): string =
-  ## The `$` operator for hash tables.
+  ## The `$` operator for ordered hash tables.
   dollarImpl()
 
 # ------------------------------ count tables -------------------------------
@@ -295,14 +301,14 @@ proc RawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
   data[h].key = key
   data[h].val = val
 
-proc Enlarge[A](t: TCountTable[A]) =
+proc Enlarge[A](t: var TCountTable[A]) =
   var n: seq[tuple[key: A, val: int]]
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
     if t.data[i].val != 0: RawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
-proc `[]=`*[A](t: TCountTable[A], key: A, val: int) =
+proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) =
   ## puts a (key, value)-pair into `t`. `val` has to be positive.
   assert val > 0
   PutImpl()
@@ -323,7 +329,7 @@ proc `$`*[A](t: TCountTable[A]): string =
   ## The `$` operator for count tables.
   dollarImpl()
 
-proc inc*[A](t: TCountTable[A], key: A, val = 1) = 
+proc inc*[A](t: var TCountTable[A], key: A, val = 1) = 
   ## increments `t[key]` by `val`.
   var index = RawGet(t, key)
   if index >= 0:
@@ -351,8 +357,8 @@ proc Largest*[A](t: TCountTable[A]): tuple[key: A, val: int] =
   result.key = t.data[maxIdx].key
   result.val = t.data[maxIdx].val
 
-proc sort*[A](t: var TCountTable[A]) = 
-  ## sorts the count table so that the entry with the highest counter comes 
+proc sort*[A](t: var TCountTable[A]) =
+  ## sorts the count table so that the entry with the highest counter comes
   ## first. This is destructive! You must not modify `t` afterwards!
   ## You can use the iterators `pairs`,  `keys`, and `values` to iterate over
   ## `t` in the sorted order.
@@ -361,35 +367,14 @@ proc sort*[A](t: var TCountTable[A]) =
   var h = 1
   while true:
     h = 3 * h + 1
-    if h >= t.data.high: break
-  while true: 
+    if h >= high(t.data): break
+  while true:
     h = h div 3
-    for i in countup(h, t.data.high):
+    for i in countup(h, high(t.data)):
       var j = i
-      while t.data[j-h].val < t.data[j].val:
+      while t.data[j-h].val <= t.data[j].val:
         swap(t.data[j], t.data[j-h])
         j = j-h
         if j < h: break
     if h == 1: break
 
-when isMainModule:
-  var table = initHashTable[string, float]()
-  table["test"] = 1.2345
-  table["111"] = 1.000043
-  echo table
-  table.del("111")
-  echo table
-  #echo repr(table["111"])
-  #echo(repr(table["1212"]))
-  table["111"] = 1.5
-  table["011"] = 67.9
-  echo table
-  table.del("test")
-  table.del("111")
-  echo table
-
-
-  echo hash("test")
-  echo hash("test")
-
-
diff --git a/tests/accept/run/tlists.nim b/tests/accept/run/tlists.nim
index e69de29bb..7d5379945 100644
--- a/tests/accept/run/tlists.nim
+++ b/tests/accept/run/tlists.nim
@@ -0,0 +1,66 @@
+discard """
+  output: '''true'''
+"""
+
+import lists
+
+const
+  data = [1, 2, 3, 4, 5, 6]
+
+block SinglyLinkedListTest1:
+  var L: TSinglyLinkedList[int]
+  for d in items(data): L.prepend(d)
+  assert($L == "[6, 5, 4, 3, 2, 1]")
+  
+  assert(4 in L)
+
+block SinglyLinkedListTest2:
+  var L: TSinglyLinkedList[string]
+  for d in items(data): L.prepend($d)
+  assert($L == "[6, 5, 4, 3, 2, 1]")
+  
+  assert("4" in L)
+
+
+block DoublyLinkedListTest1:
+  var L: TDoublyLinkedList[int]
+  for d in items(data): L.prepend(d)
+  for d in items(data): L.append(d)
+  L.remove(L.find(1))
+  assert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]")
+  
+  assert(4 in L)
+
+block SinglyLinkedRingTest1:
+  var L: TSinglyLinkedRing[int]
+  L.prepend(4)
+  assert($L == "[4]")
+  L.prepend(4)
+
+  assert($L == "[4, 4]")
+  assert(4 in L)
+  
+
+block DoublyLinkedRingTest1:
+  var L: TDoublyLinkedRing[int]
+  L.prepend(4)
+  assert($L == "[4]")
+  L.prepend(4)
+
+  assert($L == "[4, 4]")
+  assert(4 in L)
+  
+  L.append(3)
+  L.append(5)
+  assert($L == "[4, 4, 3, 5]")
+
+  L.remove(L.find(3))
+  L.remove(L.find(5))
+  L.remove(L.find(4))
+  L.remove(L.find(4))
+  assert($L == "[]")
+  assert(4 notin L)
+  
+
+echo "true"
+
diff --git a/tests/accept/run/tsets2.nim b/tests/accept/run/tsets2.nim
new file mode 100644
index 000000000..89935072f
--- /dev/null
+++ b/tests/accept/run/tsets2.nim
@@ -0,0 +1,62 @@
+discard """
+  output: '''true'''
+  cmd: "nimrod cc --gc:none --hints:on $# $#"
+"""
+
+import hashes, sets
+
+const
+  data = [
+    "34", "12",
+    "90", "0",
+    "1", "2",
+    "3", "4",
+    "5", "6",
+    "7", "8",
+    "9", "---00",
+    "10", "11", "19",
+    "20", "30", "40",
+    "50", "60", "70",
+    "80"]
+
+block tableTest1:
+  var t = initSet[tuple[x, y: int]]()
+  t.incl((0,0))
+  t.incl((1,0))
+  assert(not t.containsOrIncl((0,1)))
+  t.incl((1,1))
+
+  for x in 0..1:
+    for y in 0..1:
+      assert((x,y) in t)
+  #assert($t == 
+  #  "{(x: 0, y: 0), (x: 0, y: 1), (x: 1, y: 0), (x: 1, y: 1)}")
+
+block setTest2:
+  var t = initSet[string]()
+  t.incl("test")
+  t.incl("111")
+  t.incl("123")
+  t.excl("111")
+  
+  t.incl("012")
+  t.incl("123") # test duplicates
+  
+  assert "123" in t
+  assert "111" notin t # deleted
+  
+  for key in items(data): t.incl(key)
+  for key in items(data): assert key in t
+  
+
+block orderedSetTest1:
+  var t = data.toOrderedSet
+  for key in items(data): assert key in t
+  var i = 0
+  # `items` needs to yield in insertion order:
+  for key in items(t):
+    assert key == data[i]
+    inc(i)
+
+echo "true"
+
diff --git a/tests/accept/run/ttables.nim b/tests/accept/run/ttables.nim
index c7033bf70..72799414e 100644
--- a/tests/accept/run/ttables.nim
+++ b/tests/accept/run/ttables.nim
@@ -1,18 +1,84 @@
 discard """
   output: '''true'''
+  cmd: "nimrod cc --gc:none --hints:on $# $#"
 """
 
 import hashes, tables
 
-var t = initTable[tuple[x, y: int], string]()
-t[(0,0)] = "00"
-t[(1,0)] = "10"
-t[(0,1)] = "01"
-t[(1,1)] = "11"
+const
+  data = {
+    "34": 123456, "12": 789,
+    "90": 343, "0": 34404,
+    "1": 344004, "2": 344774,
+    "3": 342244, "4": 3412344,
+    "5": 341232144, "6": 34214544,
+    "7": 3434544, "8": 344544,
+    "9": 34435644, "---00": 346677844,
+    "10": 34484, "11": 34474, "19": 34464,
+    "20": 34454, "30": 34141244, "40": 344114,
+    "50": 344490, "60": 344491, "70": 344492,
+    "80": 344497}
 
-for x in 0..1:
-  for y in 0..1:
-    assert t[(x,y)] == $x & $y
+block tableTest1:
+  var t = initTable[tuple[x, y: int], string]()
+  t[(0,0)] = "00"
+  t[(1,0)] = "10"
+  t[(0,1)] = "01"
+  t[(1,1)] = "11"
+  for x in 0..1:
+    for y in 0..1:
+      assert t[(x,y)] == $x & $y
+  assert($t == 
+    "{(x: 0, y: 0): 00, (x: 0, y: 1): 01, (x: 1, y: 0): 10, (x: 1, y: 1): 11}")
+
+block tableTest2:
+  var t = initTable[string, float]()
+  t["test"] = 1.2345
+  t["111"] = 1.000043
+  t["123"] = 1.23
+  t.del("111")
+  
+  t["012"] = 67.9
+  t["123"] = 1.5 # test overwriting
+  
+  assert t["123"] == 1.5
+  assert t["111"] == 0.0 # deleted
+  assert(not hasKey(t, "111"))
+  
+  for key, val in items(data): t[key] = val.toFloat
+  for key, val in items(data): assert t[key] == val.toFloat
+  
+
+block orderedTableTest1:
+  var t = initOrderedTable[string, int](2)
+  for key, val in items(data): t[key] = val
+  for key, val in items(data): assert t[key] == val
+  var i = 0
+  # `pairs` needs to yield in insertion order:
+  for key, val in pairs(t):
+    assert key == data[i][0]
+    assert val == data[i][1]
+    inc(i)
+
+block countTableTest1:
+  var s = data.toTable
+  var t = initCountTable[string]()
+  for k in s.Keys: t.inc(k)
+  for k in t.keys: assert t[k] == 1
+  t.inc("90", 3)
+  t.inc("12", 2)
+  t.inc("34", 1)
+  assert t.largest()[0] == "90"
+  
+  t.sort()
+  var i = 0
+  for k, v in t.pairs:
+    case i
+    of 0: assert k == "90" and v == 4
+    of 1: assert k == "12" and v == 3
+    of 2: assert k == "34" and v == 2
+    else: break
+    inc i
 
 echo "true"
 
diff --git a/todo.txt b/todo.txt
index d060a17e9..06e46092c 100755
--- a/todo.txt
+++ b/todo.txt
@@ -4,12 +4,13 @@
     from different heaps:  n.next = otherHeapPtr
 
 * add --deadlock_prevention:on|off switch? timeout for locks?
-  
+* test the sort implementation again
 
 
 High priority (version 0.9.0)
 =============================
 
+- iterators should not always be destructive!
 - warning for implicit openArray -> varargs convention
 - implement explicit varargs
 - tests: run modules that contain "#RUN_ME", compile the other
@@ -19,7 +20,6 @@ High priority (version 0.9.0)
 - fix overloading resolution
 - wrong co-/contravariance
 - make ^ available as operator
-- iterators should not always be destructive
 
 Bugs
 ----
@@ -30,12 +30,23 @@ Bugs
 - BUG: generic assign still buggy
   - Optimization: If we use a temporary for the result anyway the code gen
     should make use of this fact to generate better code...
+- sorting with leads to a strange memory corruption!
+  --> system.swap or genericAssign is broken! And indeed, if reference counts
+      are not modified and the GC is triggered in between a swap, bad things
+      may happen!
+  
+  proc sort*[A](t: var TCountTable[A]) =
+    for i in 0 .. high(t.data)-1:
+      var maxIdx = i
+      for j in i+1 .. high(t.data):
+        if t.data[j].val == 3: echo "touched! ", t.data[j].key
+        if t.data[j].val > t.data[maxIdx].val: maxIdx = j
+      swap(t.data[maxIdx], t.data[i])
 
 
 To implement
 ------------
 
-* hash tables and sets; count tables; ordered dicts
 * distinct types for array/seq indexes
 * implement closures for the C code generator
 * GC: marker procs for native Nimrod GC and Boehm GC
@@ -68,6 +79,7 @@ Low priority
 Library
 -------
 
+- specialized bit sets; specialized string sets
 - locale support
 - conversion between character sets
 - bignums
diff --git a/web/news.txt b/web/news.txt
index c1477abbe..f048ae541 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -48,6 +48,9 @@ Changes affecting backwards compatibility
 Additions
 ---------
 
+- Added ``lists`` module which contains generic linked lists.
+- Added ``sets`` module which contains generic hash sets.
+- Added ``tables`` module which contains generic hash tables.
 - Added ``scgi`` module.
 - Added ``smtp`` module.
 - Added ``re.findAll``, ``pegs.findAll``.
diff --git a/web/nimrod.ini b/web/nimrod.ini
index be0c67690..a7bee7492 100755
--- a/web/nimrod.ini
+++ b/web/nimrod.ini
@@ -38,6 +38,7 @@ srcdoc: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser"
 srcdoc: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors"
 srcdoc: "pure/json;pure/base64;pure/scgi;pure/redis;impure/graphics"
 srcdoc: "impure/rdstdin;wrappers/zmq"
+srcdoc: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
 
 webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
 webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"