summary refs log tree commit diff stats
path: root/lib/pure/collections
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/collections')
-rw-r--r--lib/pure/collections/critbits.nim378
-rw-r--r--lib/pure/collections/deques.nim625
-rw-r--r--lib/pure/collections/hashcommon.nim29
-rw-r--r--lib/pure/collections/heapqueue.nim305
-rw-r--r--lib/pure/collections/intsets.nim675
-rw-r--r--lib/pure/collections/lists.nim795
-rw-r--r--lib/pure/collections/sequtils.nim541
-rw-r--r--lib/pure/collections/setimpl.nim11
-rw-r--r--lib/pure/collections/sets.nim448
-rw-r--r--lib/pure/collections/sharedlist.nim22
-rw-r--r--lib/pure/collections/sharedtables.nim137
-rw-r--r--lib/pure/collections/tableimpl.nim116
-rw-r--r--lib/pure/collections/tables.nim1454
13 files changed, 2322 insertions, 3214 deletions
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 91c113988..24257dacb 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -8,12 +8,37 @@
 #
 
 ## This module implements a `crit bit tree`:idx: which is an efficient
-## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper
-## by Adam Langley.
+## container for a sorted set of strings, or for a sorted mapping of strings. Based on the
+## [excellent paper by Adam Langley](https://www.imperialviolet.org/binary/critbit.pdf).
 ## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.)
 
+runnableExamples:
+  from std/sequtils import toSeq
+
+  var critbitAsSet: CritBitTree[void] = ["kitten", "puppy"].toCritBitTree
+  doAssert critbitAsSet.len == 2
+  critbitAsSet.incl("")
+  doAssert "" in critbitAsSet
+  critbitAsSet.excl("")
+  doAssert "" notin critbitAsSet
+  doAssert toSeq(critbitAsSet.items) == @["kitten", "puppy"]
+  let same = ["puppy", "kitten", "puppy"].toCritBitTree
+  doAssert toSeq(same.keys) == toSeq(critbitAsSet.keys)
+
+  var critbitAsDict: CritBitTree[int] = {"key1": 42}.toCritBitTree
+  doAssert critbitAsDict.len == 1
+  critbitAsDict["key2"] = 0
+  doAssert "key2" in critbitAsDict
+  doAssert critbitAsDict["key2"] == 0
+  critbitAsDict.excl("key1")
+  doAssert "key1" notin critbitAsDict
+  doAssert toSeq(critbitAsDict.pairs) == @[("key2", 0)]
+
 import std/private/since
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type
   NodeObj[T] {.acyclic.} = object
     byte: int ## byte index of the difference
@@ -28,17 +53,15 @@ type
   Node[T] = ref NodeObj[T]
   CritBitTree*[T] = object ## The crit bit tree can either be used
                            ## as a mapping from strings to
-                           ## some type ``T`` or as a set of
-                           ## strings if ``T`` is void.
+                           ## some type `T` or as a set of
+                           ## strings if `T` is `void`.
     root: Node[T]
     count: int
 
-proc len*[T](c: CritBitTree[T]): int =
+func len*[T](c: CritBitTree[T]): int {.inline.} =
   ## Returns the number of elements in `c` in O(1).
   runnableExamples:
-    var c: CritBitTree[void]
-    incl(c, "key1")
-    incl(c, "key2")
+    let c = ["key1", "key2"].toCritBitTree
     doAssert c.len == 2
 
   result = c.count
@@ -53,7 +76,7 @@ proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
     else:
       return if it.key == key: it else: nil
 
-proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
+func contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
   ## Returns true if `c` contains the given `key`.
   runnableExamples:
     var c: CritBitTree[void]
@@ -62,7 +85,7 @@ proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
 
   result = rawGet(c, key) != nil
 
-proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
+func hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
   ## Alias for `contains <#contains,CritBitTree[T],string>`_.
   result = rawGet(c, key) != nil
 
@@ -116,7 +139,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
     wherep[] = inner
   inc c.count
 
-proc exclImpl[T](c: var CritBitTree[T], key: string): int =
+func exclImpl[T](c: var CritBitTree[T], key: string): int =
   var p = c.root
   var wherep = addr(c.root)
   var whereq: ptr Node[T] = nil
@@ -144,7 +167,7 @@ proc excl*[T](c: var CritBitTree[T], key: string) =
   ## Removes `key` (and its associated value) from the set `c`.
   ## If the `key` does not exist, nothing happens.
   ##
-  ## See also:
+  ## **See also:**
   ## * `incl proc <#incl,CritBitTree[void],string>`_
   ## * `incl proc <#incl,CritBitTree[T],string,T>`_
   runnableExamples:
@@ -157,9 +180,9 @@ proc excl*[T](c: var CritBitTree[T], key: string) =
 
 proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
   ## Returns true if `c` does not contain the given `key`. If the key
-  ## does exist, c.excl(key) is performed.
+  ## does exist, `c.excl(key)` is performed.
   ##
-  ## See also:
+  ## **See also:**
   ## * `excl proc <#excl,CritBitTree[T],string>`_
   ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_
   ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_
@@ -177,11 +200,11 @@ proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
   discard exclImpl(c, key)
   result = c.count == oldCount
 
-proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
-  ## Returns true if `c` contains the given `key`. If the key does not exist
-  ## ``c[key] = val`` is performed.
+proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: sink T): bool =
+  ## Returns true if `c` contains the given `key`. If the key does not exist,
+  ## `c[key] = val` is performed.
   ##
-  ## See also:
+  ## **See also:**
   ## * `incl proc <#incl,CritBitTree[void],string>`_
   ## * `incl proc <#incl,CritBitTree[T],string,T>`_
   ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_
@@ -204,10 +227,10 @@ proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
     if not result: n.val = val
 
 proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
-  ## Returns true if `c` contains the given `key`. If the key does not exist
+  ## Returns true if `c` contains the given `key`. If the key does not exist,
   ## it is inserted into `c`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `incl proc <#incl,CritBitTree[void],string>`_
   ## * `incl proc <#incl,CritBitTree[T],string,T>`_
   ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_
@@ -240,7 +263,7 @@ proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) =
 proc incl*(c: var CritBitTree[void], key: string) =
   ## Includes `key` in `c`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `excl proc <#excl,CritBitTree[T],string>`_
   ## * `incl proc <#incl,CritBitTree[T],string,T>`_
   runnableExamples:
@@ -250,10 +273,10 @@ proc incl*(c: var CritBitTree[void], key: string) =
 
   discard rawInsert(c, key)
 
-proc incl*[T](c: var CritBitTree[T], key: string, val: T) =
+proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) =
   ## Inserts `key` with value `val` into `c`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `excl proc <#excl,CritBitTree[T],string>`_
   ## * `incl proc <#incl,CritBitTree[void],string>`_
   runnableExamples:
@@ -264,45 +287,37 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: T) =
   var n = rawInsert(c, key)
   n.val = val
 
-proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
-  ## Puts a (key, value)-pair into `t`.
+proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) =
+  ## Alias for `incl <#incl,CritBitTree[T],string,T>`_.
   ##
-  ## See also:
+  ## **See also:**
   ## * `[] proc <#[],CritBitTree[T],string>`_
   ## * `[] proc <#[],CritBitTree[T],string_2>`_
-  runnableExamples:
-    var c: CritBitTree[int]
-    c["key"] = 42
-    doAssert c["key"] == 42
-
   var n = rawInsert(c, key)
   n.val = val
 
 template get[T](c: CritBitTree[T], key: string): T =
   let n = rawGet(c, key)
   if n == nil:
-    when compiles($key):
-      raise newException(KeyError, "key not found: " & $key)
-    else:
-      raise newException(KeyError, "key not found")
+    raise newException(KeyError, "key not found: " & key)
 
   n.val
 
-proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} =
-  ## Retrieves the value at ``c[key]``. If `key` is not in `t`, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
+func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} =
+  ## Retrieves the value at `c[key]`. If `key` is not in `t`, the
+  ## `KeyError` exception is raised. One can check with `hasKey` whether
   ## the key exists.
   ##
-  ## See also:
+  ## **See also:**
   ## * `[] proc <#[],CritBitTree[T],string_2>`_
   ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_
   get(c, key)
 
-proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
-  ## Retrieves the value at ``c[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
+func `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
+  ## Retrieves the value at `c[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   ##
-  ## See also:
+  ## **See also:**
   ## * `[] proc <#[],CritBitTree[T],string>`_
   ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_
   get(c, key)
@@ -323,27 +338,24 @@ iterator leaves[T](n: Node[T]): Node[T] =
 iterator keys*[T](c: CritBitTree[T]): string =
   ## Yields all keys in lexicographical order.
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 1
-    c["key2"] = 2
-    var keys: seq[string]
-    for key in c.keys:
-      keys.add(key)
-    doAssert keys == @["key1", "key2"]
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.keys) == @["key1", "key2"]
 
   for x in leaves(c.root): yield x.key
 
-iterator values*[T](c: CritBitTree[T]): T =
+iterator values*[T](c: CritBitTree[T]): lent T =
   ## Yields all values of `c` in the lexicographical order of the
   ## corresponding keys.
+  ##
+  ## **See also:**
+  ## * `mvalues iterator <#mvalues.i,CritBitTree[T]>`_
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 1
-    c["key2"] = 2
-    var vals: seq[int]
-    for val in c.values:
-      vals.add(val)
-    doAssert vals == @[1, 2]
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.values) == @[1, 2]
 
   for x in leaves(c.root): yield x.val
 
@@ -351,45 +363,37 @@ iterator mvalues*[T](c: var CritBitTree[T]): var T =
   ## Yields all values of `c` in the lexicographical order of the
   ## corresponding keys. The values can be modified.
   ##
-  ## See also:
+  ## **See also:**
   ## * `values iterator <#values.i,CritBitTree[T]>`_
   for x in leaves(c.root): yield x.val
 
 iterator items*[T](c: CritBitTree[T]): string =
-  ## Yields all keys in lexicographical order.
-  runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 1
-    c["key2"] = 2
-    var keys: seq[string]
-    for key in c.items:
-      keys.add(key)
-    doAssert keys == @["key1", "key2"]
-
+  ## Alias for `keys <#keys.i,CritBitTree[T]>`_.
   for x in leaves(c.root): yield x.key
 
 iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
-  ## Yields all (key, value)-pairs of `c`.
+  ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the
+  ## corresponding keys.
+  ##
+  ## **See also:**
+  ## * `mpairs iterator <#mpairs.i,CritBitTree[T]>`_
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 1
-    c["key2"] = 2
-    var ps: seq[tuple[key: string, val: int]]
-    for p in c.pairs:
-      ps.add(p)
-    doAssert ps == @[(key: "key1", val: 1), (key: "key2", val: 2)]
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.pairs) == @[(key: "key1", val: 1), (key: "key2", val: 2)]
 
   for x in leaves(c.root): yield (x.key, x.val)
 
 iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
-  ## Yields all (key, value)-pairs of `c`. The yielded values can be modified.
+  ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the
+  ## corresponding keys. The yielded values can be modified.
   ##
-  ## See also:
+  ## **See also:**
   ## * `pairs iterator <#pairs.i,CritBitTree[T]>`_
   for x in leaves(c.root): yield (x.key, x.val)
 
-proc allprefixedAux[T](c: CritBitTree[T], key: string;
-                       longestMatch: bool): Node[T] =
+proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
   var p = c.root
   var top = p
   if p != nil:
@@ -399,100 +403,83 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string;
       let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
       p = p.child[dir]
       if q.byte < key.len: top = p
-    if not longestMatch:
-      for i in 0 ..< key.len:
-        if i >= p.key.len or p.key[i] != key[i]: return
+    for i in 0 ..< key.len:
+      if i >= p.key.len or p.key[i] != key[i]: return
     result = top
 
-iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                             longestMatch = false): string =
-  ## Yields all keys starting with `prefix`. If `longestMatch` is true,
-  ## the longest match is returned, it doesn't have to be a complete match then.
-  runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 42
-    c["key2"] = 43
-    var keys: seq[string]
-    for key in c.itemsWithPrefix("key"):
-      keys.add(key)
-    doAssert keys == @["key1", "key2"]
-
-  let top = allprefixedAux(c, prefix, longestMatch)
-  for x in leaves(top): yield x.key
-
-iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                            longestMatch = false): string =
+iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
   ## Yields all keys starting with `prefix`.
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 42
-    c["key2"] = 43
-    var keys: seq[string]
-    for key in c.keysWithPrefix("key"):
-      keys.add(key)
-    doAssert keys == @["key1", "key2"]
-
-  let top = allprefixedAux(c, prefix, longestMatch)
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.keysWithPrefix("key")) == @["key1", "key2"]
+
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.key
 
-iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                              longestMatch = false): T =
+iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T =
   ## Yields all values of `c` starting with `prefix` of the
   ## corresponding keys.
+  ##
+  ## **See also:**
+  ## * `mvaluesWithPrefix iterator <#mvaluesWithPrefix.i,CritBitTree[T],string>`_
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 42
-    c["key2"] = 43
-    var vals: seq[int]
-    for val in c.valuesWithPrefix("key"):
-      vals.add(val)
-    doAssert vals == @[42, 43]
-
-  let top = allprefixedAux(c, prefix, longestMatch)
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.valuesWithPrefix("key")) == @[42, 43]
+
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.val
 
-iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string;
-                               longestMatch = false): var T =
+iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
   ## Yields all values of `c` starting with `prefix` of the
   ## corresponding keys. The values can be modified.
   ##
-  ## See also:
+  ## **See also:**
   ## * `valuesWithPrefix iterator <#valuesWithPrefix.i,CritBitTree[T],string>`_
-  let top = allprefixedAux(c, prefix, longestMatch)
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.val
 
+iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
+  ## Alias for `keysWithPrefix <#keysWithPrefix.i,CritBitTree[T],string>`_.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.key
+
 iterator pairsWithPrefix*[T](c: CritBitTree[T],
-                             prefix: string;
-                             longestMatch = false): tuple[key: string, val: T] =
+                             prefix: string): tuple[key: string, val: T] =
   ## Yields all (key, value)-pairs of `c` starting with `prefix`.
+  ##
+  ## **See also:**
+  ## * `mpairsWithPrefix iterator <#mpairsWithPrefix.i,CritBitTree[T],string>`_
   runnableExamples:
-    var c: CritBitTree[int]
-    c["key1"] = 42
-    c["key2"] = 43
-    var ps: seq[tuple[key: string, val: int]]
-    for p in c.pairsWithPrefix("key"):
-      ps.add(p)
-    doAssert ps == @[(key: "key1", val: 42), (key: "key2", val: 43)]
-
-  let top = allprefixedAux(c, prefix, longestMatch)
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.pairsWithPrefix("key")) == @[(key: "key1", val: 42), (key: "key2", val: 43)]
+
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield (x.key, x.val)
 
 iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
-                              prefix: string;
-                             longestMatch = false): tuple[key: string, val: var T] =
+                              prefix: string): tuple[key: string, val: var T] =
   ## Yields all (key, value)-pairs of `c` starting with `prefix`.
   ## The yielded values can be modified.
   ##
-  ## See also:
+  ## **See also:**
   ## * `pairsWithPrefix iterator <#pairsWithPrefix.i,CritBitTree[T],string>`_
-  let top = allprefixedAux(c, prefix, longestMatch)
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield (x.key, x.val)
 
-proc `$`*[T](c: CritBitTree[T]): string =
-  ## Turns `c` into a string representation. Example outputs:
-  ## ``{keyA: value, keyB: value}``, ``{:}``
-  ## If `T` is void the outputs look like:
-  ## ``{keyA, keyB}``, ``{}``.
+func `$`*[T](c: CritBitTree[T]): string =
+  ## Turns `c` into a string representation.
+  runnableExamples:
+    doAssert $CritBitTree[int].default == "{:}"
+    doAssert $toCritBitTree({"key1": 1, "key2": 2}) == """{"key1": 1, "key2": 2}"""
+    doAssert $CritBitTree[void].default == "{}"
+    doAssert $toCritBitTree(["key1", "key2"]) == """{"key1", "key2"}"""
+
   if c.len == 0:
     when T is void:
       result = "{}"
@@ -518,8 +505,8 @@ proc `$`*[T](c: CritBitTree[T]): string =
         result.addQuoted(val)
     result.add("}")
 
-proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} =
-  ## Returns longest common prefix length of all keys of `c`.
+func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} =
+  ## Returns the length of the longest common prefix of all keys in `c`.
   ## If `c` is empty, returns 0.
   runnableExamples:
     var c: CritBitTree[void]
@@ -534,90 +521,17 @@ proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} =
     else: c.root.byte
   else: 0
 
+proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} =
+  ## Creates a new `CritBitTree` that contains the given `pairs`.
+  runnableExamples:
+    doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string]
+    doAssert {"a": 0, "b": 1, "c": 2}.toCritBitTree is CritBitTree[int]
 
-runnableExamples:
-  static:
-    block:
-      var critbitAsSet: CritBitTree[void]
-      doAssert critbitAsSet.len == 0
-      incl critbitAsSet, "kitten"
-      doAssert critbitAsSet.len == 1
-      incl critbitAsSet, "puppy"
-      doAssert critbitAsSet.len == 2
-      incl critbitAsSet, "kitten"
-      doAssert critbitAsSet.len == 2
-      incl critbitAsSet, ""
-      doAssert critbitAsSet.len == 3
-  block:
-    var critbitAsDict: CritBitTree[int]
-    critbitAsDict["key"] = 42
-    doAssert critbitAsDict["key"] == 42
-    critbitAsDict["key"] = 0
-    doAssert critbitAsDict["key"] == 0
-    critbitAsDict["key"] = -int.high
-    doAssert critbitAsDict["key"] == -int.high
-    critbitAsDict["key"] = int.high
-    doAssert critbitAsDict["key"] == int.high
-
-
-when isMainModule:
-  import sequtils
-
-  var r: CritBitTree[void]
-  r.incl "abc"
-  r.incl "xyz"
-  r.incl "def"
-  r.incl "definition"
-  r.incl "prefix"
-  r.incl "foo"
-
-  doAssert r.contains"def"
-
-  r.excl "def"
-  assert r.missingOrExcl("foo") == false
-  assert "foo" notin toSeq(r.items)
-
-  assert r.missingOrExcl("foo") == true
-
-  assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
-
-  assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
-  var c = CritBitTree[int]()
-
-  c.inc("a")
-  assert c["a"] == 1
-
-  c.inc("a", 4)
-  assert c["a"] == 5
-
-  c.inc("a", -5)
-  assert c["a"] == 0
-
-  c.inc("b", 2)
-  assert c["b"] == 2
-
-  c.inc("c", 3)
-  assert c["c"] == 3
-
-  c.inc("a", 1)
-  assert c["a"] == 1
-
-  var cf = CritBitTree[float]()
-
-  cf.incl("a", 1.0)
-  assert cf["a"] == 1.0
-
-  cf.incl("b", 2.0)
-  assert cf["b"] == 2.0
-
-  cf.incl("c", 3.0)
-  assert cf["c"] == 3.0
+  for item in pairs: result.incl item[0], item[1]
 
-  assert cf.len == 3
-  cf.excl("c")
-  assert cf.len == 2
+proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} =
+  ## Creates a new `CritBitTree` that contains the given `items`.
+  runnableExamples:
+    doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void]
 
-  var cb: CritBitTree[string]
-  cb.incl("help", "help")
-  for k in cb.keysWithPrefix("helpp"):
-    doAssert false, "there is no prefix helpp"
+  for item in items: result.incl item
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index d096874a3..d2b0099f2 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -7,157 +7,153 @@
 #    distribution, for details about the copyright.
 #
 
-## Implementation of a `deque`:idx: (double-ended queue).
-## The underlying implementation uses a ``seq``.
+## An implementation of a `deque`:idx: (double-ended queue).
+## The underlying implementation uses a `seq`.
 ##
-## None of the procs that get an individual value from the deque can be used
-## on an empty deque.
-## If compiled with `boundChecks` option, those procs will raise an `IndexDefect`
-## on such access. This should not be relied upon, as `-d:release` will
-## disable those checks and may return garbage or crash the program.
+## .. note:: None of the procs that get an individual value from the deque should be used
+##   on an empty deque.
+##
+## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect`
+## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will
+## disable those checks and then the procs may return garbage or crash the program.
 ##
 ## As such, a check to see if the deque is empty is needed before any
 ## access, unless your program logic guarantees it indirectly.
-##
-## .. code-block:: Nim
-##   import deques
-##
-##   var a = initDeque[int]()
-##
-##   doAssertRaises(IndexDefect, echo a[0])
-##
-##   for i in 1 .. 5:
-##     a.addLast(10*i)
-##   assert $a == "[10, 20, 30, 40, 50]"
-##
-##   assert a.peekFirst == 10
-##   assert a.peekLast == 50
-##   assert len(a) == 5
-##
-##   assert a.popFirst == 10
-##   assert a.popLast == 50
-##   assert len(a) == 3
-##
-##   a.addFirst(11)
-##   a.addFirst(22)
-##   a.addFirst(33)
-##   assert $a == "[33, 22, 11, 20, 30, 40]"
-##
-##   a.shrink(fromFirst = 1, fromLast = 2)
-##   assert $a == "[22, 11, 20]"
-##
-##
-## **See also:**
+
+runnableExamples:
+  var a = [10, 20, 30, 40].toDeque
+
+  doAssertRaises(IndexDefect, echo a[4])
+
+  a.addLast(50)
+  assert $a == "[10, 20, 30, 40, 50]"
+
+  assert a.peekFirst == 10
+  assert a.peekLast == 50
+  assert len(a) == 5
+
+  assert a.popFirst == 10
+  assert a.popLast == 50
+  assert len(a) == 3
+
+  a.addFirst(11)
+  a.addFirst(22)
+  a.addFirst(33)
+  assert $a == "[33, 22, 11, 20, 30, 40]"
+
+  a.shrink(fromFirst = 1, fromLast = 2)
+  assert $a == "[22, 11, 20]"
+
+## See also
+## ========
 ## * `lists module <lists.html>`_ for singly and doubly linked lists and rings
-## * `channels module <channels.html>`_ for inter-thread communication
 
 import std/private/since
 
-import math
+import std/[assertions, hashes, math]
 
 type
   Deque*[T] = object
-    ## A double-ended queue backed with a ringed seq buffer.
+    ## A double-ended queue backed with a ringed `seq` buffer.
     ##
-    ## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
+    ## To initialize an empty deque,
+    ## use the `initDeque proc <#initDeque,int>`_.
     data: seq[T]
-    head, tail, count, mask: int
+
+    # `head` and `tail` are masked only when accessing an element of `data`
+    # so that `tail - head == data.len` when the deque is full.
+    # They are uint so that incrementing/decrementing them doesn't cause
+    # over/underflow. You can get a number of items with `tail - head`
+    # even if `tail` or `head` is wraps around and `tail < head`, because
+    # `tail - head == (uint.high + 1 + tail) - head` when `tail < head`.
+    head, tail: uint
 
 const
   defaultInitialSize* = 4
 
 template initImpl(result: typed, initialSize: int) =
-  assert isPowerOfTwo(initialSize)
-  result.mask = initialSize-1
-  newSeq(result.data, initialSize)
+  let correctSize = nextPowerOfTwo(initialSize)
+  newSeq(result.data, correctSize)
 
 template checkIfInitialized(deq: typed) =
-  when compiles(defaultInitialSize):
-    if deq.mask == 0:
-      initImpl(deq, defaultInitialSize)
+  if deq.data.len == 0:
+    initImpl(deq, defaultInitialSize)
+
+func mask[T](deq: Deque[T]): uint {.inline.} =
+  uint(deq.data.len) - 1
 
-proc initDeque*[T](initialSize: int = 4): Deque[T] =
-  ## Create a new empty deque.
+proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] =
+  ## Creates a new empty deque.
   ##
   ## Optionally, the initial capacity can be reserved via `initialSize`
-  ## as a performance optimization.
+  ## as a performance optimization
+  ## (default: `defaultInitialSize <#defaultInitialSize>`_).
   ## The length of a newly created deque will still be 0.
   ##
-  ## ``initialSize`` must be a power of two (default: 4).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_.
+  ## **See also:**
+  ## * `toDeque proc <#toDeque,openArray[T]>`_
   result.initImpl(initialSize)
 
-proc len*[T](deq: Deque[T]): int {.inline.} =
-  ## Return the number of elements of `deq`.
-  result = deq.count
+func len*[T](deq: Deque[T]): int {.inline.} =
+  ## Returns the number of elements of `deq`.
+  int(deq.tail - deq.head)
 
 template emptyCheck(deq) =
   # Bounds check for the regular deque access.
   when compileOption("boundChecks"):
-    if unlikely(deq.count < 1):
+    if unlikely(deq.len < 1):
       raise newException(IndexDefect, "Empty deque.")
 
 template xBoundsCheck(deq, i) =
   # Bounds check for the array like accesses.
-  when compileOption("boundChecks"): # d:release should disable this.
-    if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
+  when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this.
+    if unlikely(i >= deq.len): # x < deq.low is taken care by the Natural parameter
       raise newException(IndexDefect,
-                         "Out of bounds: " & $i & " > " & $(deq.count - 1))
+                         "Out of bounds: " & $i & " > " & $(deq.len - 1))
     if unlikely(i < 0): # when used with BackwardsIndex
       raise newException(IndexDefect,
                          "Out of bounds: " & $i & " < 0")
 
-proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} =
-  ## Access the i-th element of `deq`.
+proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} =
+  ## Accesses the `i`-th element of `deq`.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    let a = [10, 20, 30, 40, 50].toDeque
     assert a[0] == 10
     assert a[3] == 40
     doAssertRaises(IndexDefect, echo a[8])
 
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) and deq.mask]
 
 proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
-  ## Access the i-th element of `deq` and return a mutable
+  ## Accesses the `i`-th element of `deq` and returns a mutable
   ## reference to it.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
-    assert a[0] == 10
-    assert a[3] == 40
-    doAssertRaises(IndexDefect, echo a[8])
+    var a = [10, 20, 30, 40, 50].toDeque
+    inc(a[0])
+    assert a[0] == 11
 
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) and deq.mask]
 
-proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} =
-  ## Change the i-th element of `deq`.
+proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} =
+  ## Sets the `i`-th element of `deq` to `val`.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    var a = [10, 20, 30, 40, 50].toDeque
     a[0] = 99
     a[3] = 66
     assert $a == "[99, 20, 30, 66, 50]"
 
   checkIfInitialized(deq)
   xBoundsCheck(deq, i)
-  deq.data[(deq.head + i) and deq.mask] = val
+  deq.data[(deq.head + i.uint) and deq.mask] = val
 
-proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
-  ## Access the backwards indexed i-th element.
+proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} =
+  ## Accesses the backwards indexed `i`-th element.
   ##
   ## `deq[^1]` is the last element.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    let a = [10, 20, 30, 40, 50].toDeque
     assert a[^1] == 50
     assert a[^4] == 20
     doAssertRaises(IndexDefect, echo a[^9])
@@ -166,28 +162,24 @@ proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
   return deq[deq.len - int(i)]
 
 proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} =
-  ## Access the backwards indexed i-th element.
+  ## Accesses the backwards indexed `i`-th element and returns a mutable
+  ## reference to it.
   ##
   ## `deq[^1]` is the last element.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
-    assert a[^1] == 50
-    assert a[^4] == 20
-    doAssertRaises(IndexDefect, echo a[^9])
+    var a = [10, 20, 30, 40, 50].toDeque
+    inc(a[^1])
+    assert a[^1] == 51
 
   xBoundsCheck(deq, deq.len - int(i))
   return deq[deq.len - int(i)]
 
-proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} =
-  ## Change the backwards indexed i-th element.
+proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} =
+  ## Sets the backwards indexed `i`-th element of `deq` to `x`.
   ##
   ## `deq[^1]` is the last element.
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    var a = [10, 20, 30, 40, 50].toDeque
     a[^1] = 99
     a[^3] = 77
     assert $a == "[10, 20, 77, 40, 99]"
@@ -196,167 +188,143 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} =
   xBoundsCheck(deq, deq.len - int(i))
   deq[deq.len - int(i)] = x
 
-iterator items*[T](deq: Deque[T]): T =
-  ## Yield every element of `deq`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block::
-  ##   var a = initDeque[int]()
-  ##   for i in 1 .. 3:
-  ##     a.addLast(10*i)
-  ##
-  ##   for x in a:  # the same as: for x in items(a):
-  ##     echo x
-  ##
-  ##   # 10
-  ##   # 20
-  ##   # 30
+iterator items*[T](deq: Deque[T]): lent T =
+  ## Yields every element of `deq`.
   ##
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+  ## **See also:**
+  ## * `mitems iterator <#mitems.i,Deque[T]>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let a = [10, 20, 30, 40, 50].toDeque
+    assert toSeq(a.items) == @[10, 20, 30, 40, 50]
+
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator mitems*[T](deq: var Deque[T]): var T =
-  ## Yield every element of `deq`, which can be modified.
+  ## Yields every element of `deq`, which can be modified.
+  ##
+  ## **See also:**
+  ## * `items iterator <#items.i,Deque[T]>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    var a = [10, 20, 30, 40, 50].toDeque
     assert $a == "[10, 20, 30, 40, 50]"
     for x in mitems(a):
-      x = 5*x - 1
+      x = 5 * x - 1
     assert $a == "[49, 99, 149, 199, 249]"
 
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
-  ## Yield every (position, value) of `deq`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block::
-  ##   var a = initDeque[int]()
-  ##   for i in 1 .. 3:
-  ##     a.addLast(10*i)
-  ##
-  ##   for k, v in pairs(a):
-  ##     echo "key: ", k, ", value: ", v
-  ##
-  ##   # key: 0, value: 10
-  ##   # key: 1, value: 20
-  ##   # key: 2, value: 30
-  ##
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield (c, deq.data[i])
-    i = (i + 1) and deq.mask
+  ## Yields every `(position, value)`-pair of `deq`.
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let a = [10, 20, 30].toDeque
+    assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)]
+
+  for c in 0 ..< deq.len:
+    yield (c, deq.data[(deq.head + c.uint) and deq.mask])
 
 proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
-  ## Return true if `item` is in `deq` or false if not found.
-  ##
-  ## Usually used via the ``in`` operator.
-  ## It is the equivalent of ``deq.find(item) >= 0``.
+  ## Returns true if `item` is in `deq` or false if not found.
   ##
-  ## .. code-block:: Nim
-  ##   if x in q:
-  ##     assert q.contains(x)
+  ## Usually used via the `in` operator.
+  ## It is the equivalent of `deq.find(item) >= 0`.
+  runnableExamples:
+    let q = [7, 9].toDeque
+    assert 7 in q
+    assert q.contains(7)
+    assert 8 notin q
+
   for e in deq:
     if e == item: return true
   return false
 
 proc expandIfNeeded[T](deq: var Deque[T]) =
   checkIfInitialized(deq)
-  var cap = deq.mask + 1
-  if unlikely(deq.count >= cap):
+  let cap = deq.data.len
+  assert deq.len <= cap
+  if unlikely(deq.len == cap):
     var n = newSeq[T](cap * 2)
     var i = 0
     for x in mitems(deq):
-      when nimVM: n[i] = x # workaround for VM bug
+      when nimvm: n[i] = x # workaround for VM bug
       else: n[i] = move(x)
       inc i
     deq.data = move(n)
-    deq.mask = cap * 2 - 1
-    deq.tail = deq.count
+    deq.tail = cap.uint
     deq.head = 0
 
-proc addFirst*[T](deq: var Deque[T], item: T) =
-  ## Add an `item` to the beginning of the `deq`.
+proc addFirst*[T](deq: var Deque[T], item: sink T) =
+  ## Adds an `item` to the beginning of `deq`.
   ##
-  ## See also:
-  ## * `addLast proc <#addLast,Deque[T],T>`_
-  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `peekLast proc <#peekLast,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## **See also:**
+  ## * `addLast proc <#addLast,Deque[T],sinkT>`_
   runnableExamples:
     var a = initDeque[int]()
     for i in 1 .. 5:
-      a.addFirst(10*i)
+      a.addFirst(10 * i)
     assert $a == "[50, 40, 30, 20, 10]"
 
   expandIfNeeded(deq)
-  inc deq.count
-  deq.head = (deq.head - 1) and deq.mask
-  deq.data[deq.head] = item
+  dec deq.head
+  deq.data[deq.head and deq.mask] = item
 
-proc addLast*[T](deq: var Deque[T], item: T) =
-  ## Add an `item` to the end of the `deq`.
+proc addLast*[T](deq: var Deque[T], item: sink T) =
+  ## Adds an `item` to the end of `deq`.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `peekLast proc <#peekLast,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## **See also:**
+  ## * `addFirst proc <#addFirst,Deque[T],sinkT>`_
   runnableExamples:
     var a = initDeque[int]()
     for i in 1 .. 5:
-      a.addLast(10*i)
+      a.addLast(10 * i)
     assert $a == "[10, 20, 30, 40, 50]"
 
   expandIfNeeded(deq)
-  inc deq.count
-  deq.data[deq.tail] = item
-  deq.tail = (deq.tail + 1) and deq.mask
+  deq.data[deq.tail and deq.mask] = item
+  inc deq.tail
+
+proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} =
+  ## Creates a new deque that contains the elements of `x` (in the same order).
+  ##
+  ## **See also:**
+  ## * `initDeque proc <#initDeque,int>`_
+  runnableExamples:
+    let a = toDeque([7, 8, 9])
+    assert len(a) == 3
+    assert $a == "[7, 8, 9]"
 
-proc peekFirst*[T](deq: Deque[T]): T {.inline.} =
+  result.initImpl(x.len)
+  for item in items(x):
+    result.addLast(item)
+
+proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} =
   ## Returns the first element of `deq`, but does not remove it from the deque.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ which returns a mutable reference
   ## * `peekLast proc <#peekLast,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    let a = [10, 20, 30, 40, 50].toDeque
     assert $a == "[10, 20, 30, 40, 50]"
     assert a.peekFirst == 10
     assert len(a) == 5
 
   emptyCheck(deq)
-  result = deq.data[deq.head]
+  result = deq.data[deq.head and deq.mask]
 
-proc peekLast*[T](deq: Deque[T]): T {.inline.} =
+proc peekLast*[T](deq: Deque[T]): lent T {.inline.} =
   ## Returns the last element of `deq`, but does not remove it from the deque.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## **See also:**
+  ## * `peekLast proc <#peekLast,Deque[T]_2>`_ which returns a mutable reference
   ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    let a = [10, 20, 30, 40, 50].toDeque
     assert $a == "[10, 20, 30, 40, 50]"
     assert a.peekLast == 50
     assert len(a) == 5
@@ -365,41 +333,31 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
   result = deq.data[(deq.tail - 1) and deq.mask]
 
 proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
-  ## Returns the first element of `deq`, but does not remove it from the deque.
+  ## Returns a mutable reference to the first element of `deq`,
+  ## but does not remove it from the deque.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
-  ## * `peekLast proc <#peekLast,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]_2>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
-    assert $a == "[10, 20, 30, 40, 50]"
-    assert a.peekFirst == 10
-    assert len(a) == 5
+    var a = [10, 20, 30, 40, 50].toDeque
+    a.peekFirst() = 99
+    assert $a == "[99, 20, 30, 40, 50]"
 
   emptyCheck(deq)
-  result = deq.data[deq.head]
+  result = deq.data[deq.head and deq.mask]
 
 proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
-  ## Returns the last element of `deq`, but does not remove it from the deque.
+  ## Returns a mutable reference to the last element of `deq`,
+  ## but does not remove it from the deque.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
-  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
-    assert $a == "[10, 20, 30, 40, 50]"
-    assert a.peekLast == 50
-    assert len(a) == 5
+    var a = [10, 20, 30, 40, 50].toDeque
+    a.peekLast() = 99
+    assert $a == "[10, 20, 30, 40, 99]"
 
   emptyCheck(deq)
   result = deq.data[(deq.tail - 1) and deq.mask]
@@ -408,218 +366,115 @@ template destroy(x: untyped) =
   reset(x)
 
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
-  ## Remove and returns the first element of the `deq`.
+  ## Removes and returns the first element of the `deq`.
   ##
   ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
-  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `peekLast proc <#peekLast,Deque[T]>`_
   ## * `popLast proc <#popLast,Deque[T]>`_
-  ## * `clear proc <#clear,Deque[T]>`_
   ## * `shrink proc <#shrink,Deque[T],int,int>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    var a = [10, 20, 30, 40, 50].toDeque
     assert $a == "[10, 20, 30, 40, 50]"
     assert a.popFirst == 10
     assert $a == "[20, 30, 40, 50]"
 
   emptyCheck(deq)
-  dec deq.count
-  result = deq.data[deq.head]
-  destroy(deq.data[deq.head])
-  deq.head = (deq.head + 1) and deq.mask
+  result = move deq.data[deq.head and deq.mask]
+  inc deq.head
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
-  ## Remove and returns the last element of the `deq`.
+  ## Removes and returns the last element of the `deq`.
   ##
-  ## See also:
-  ## * `addFirst proc <#addFirst,Deque[T],T>`_
-  ## * `addLast proc <#addLast,Deque[T],T>`_
-  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
-  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## **See also:**
   ## * `popFirst proc <#popFirst,Deque[T]>`_
-  ## * `clear proc <#clear,Deque[T]>`_
   ## * `shrink proc <#shrink,Deque[T],int,int>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addLast(10*i)
+    var a = [10, 20, 30, 40, 50].toDeque
     assert $a == "[10, 20, 30, 40, 50]"
     assert a.popLast == 50
     assert $a == "[10, 20, 30, 40]"
 
   emptyCheck(deq)
-  dec deq.count
-  deq.tail = (deq.tail - 1) and deq.mask
-  result = deq.data[deq.tail]
-  destroy(deq.data[deq.tail])
+  dec deq.tail
+  result = move deq.data[deq.tail and deq.mask]
 
 proc clear*[T](deq: var Deque[T]) {.inline.} =
   ## Resets the deque so that it is empty.
   ##
-  ## See also:
-  ## * `clear proc <#clear,Deque[T]>`_
+  ## **See also:**
   ## * `shrink proc <#shrink,Deque[T],int,int>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addFirst(10*i)
-    assert $a == "[50, 40, 30, 20, 10]"
+    var a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
     clear(a)
     assert len(a) == 0
 
   for el in mitems(deq): destroy(el)
-  deq.count = 0
   deq.tail = deq.head
 
 proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
-  ## Remove `fromFirst` elements from the front of the deque and
+  ## Removes `fromFirst` elements from the front of the deque and
   ## `fromLast` elements from the back.
   ##
   ## If the supplied number of elements exceeds the total number of elements
   ## in the deque, the deque will remain empty.
   ##
-  ## See also:
+  ## **See also:**
   ## * `clear proc <#clear,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
   runnableExamples:
-    var a = initDeque[int]()
-    for i in 1 .. 5:
-      a.addFirst(10*i)
-    assert $a == "[50, 40, 30, 20, 10]"
+    var a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
     a.shrink(fromFirst = 2, fromLast = 1)
-    assert $a == "[30, 20]"
+    assert $a == "[30, 40]"
 
-  if fromFirst + fromLast > deq.count:
+  if fromFirst + fromLast > deq.len:
     clear(deq)
     return
 
   for i in 0 ..< fromFirst:
-    destroy(deq.data[deq.head])
-    deq.head = (deq.head + 1) and deq.mask
+    destroy(deq.data[deq.head and deq.mask])
+    inc deq.head
 
   for i in 0 ..< fromLast:
-    destroy(deq.data[deq.tail])
-    deq.tail = (deq.tail - 1) and deq.mask
-
-  dec deq.count, fromFirst + fromLast
+    destroy(deq.data[(deq.tail - 1) and deq.mask])
+    dec deq.tail
 
 proc `$`*[T](deq: Deque[T]): string =
-  ## Turn a deque into its string representation.
+  ## Turns a deque into its string representation.
+  runnableExamples:
+    let a = [10, 20, 30].toDeque
+    assert $a == "[10, 20, 30]"
+
   result = "["
   for x in deq:
     if result.len > 1: result.add(", ")
     result.addQuoted(x)
   result.add("]")
 
+func `==`*[T](deq1, deq2: Deque[T]): bool =
+  ## The `==` operator for Deque.
+  ## Returns `true` if both deques contains the same values in the same order.
+  runnableExamples:
+    var a, b = initDeque[int]()
+    a.addFirst(2)
+    a.addFirst(1)
+    b.addLast(1)
+    b.addLast(2)
+    doAssert a == b
 
+  if deq1.len != deq2.len:
+    return false
 
-when isMainModule:
-  var deq = initDeque[int](1)
-  deq.addLast(4)
-  deq.addFirst(9)
-  deq.addFirst(123)
-  var first = deq.popFirst()
-  deq.addLast(56)
-  assert(deq.peekLast() == 56)
-  deq.addLast(6)
-  assert(deq.peekLast() == 6)
-  var second = deq.popFirst()
-  deq.addLast(789)
-  assert(deq.peekLast() == 789)
-
-  assert first == 123
-  assert second == 9
-  assert($deq == "[4, 56, 6, 789]")
-
-  assert deq[0] == deq.peekFirst and deq.peekFirst == 4
-  #assert deq[^1] == deq.peekLast and deq.peekLast == 789
-  deq[0] = 42
-  deq[deq.len - 1] = 7
-
-  assert 6 in deq and 789 notin deq
-  assert deq.find(6) >= 0
-  assert deq.find(789) < 0
-
-  block:
-    var d = initDeque[int](1)
-    d.addLast 7
-    d.addLast 8
-    d.addLast 10
-    d.addFirst 5
-    d.addFirst 2
-    d.addFirst 1
-    d.addLast 20
-    d.shrink(fromLast = 2)
-    doAssert($d == "[1, 2, 5, 7, 8]")
-    d.shrink(2, 1)
-    doAssert($d == "[5, 7]")
-    d.shrink(2, 2)
-    doAssert d.len == 0
-
-  for i in -2 .. 10:
-    if i in deq:
-      assert deq.contains(i) and deq.find(i) >= 0
-    else:
-      assert(not deq.contains(i) and deq.find(i) < 0)
+  for i in 0 ..< deq1.len:
+    if deq1.data[(deq1.head + i.uint) and deq1.mask] != deq2.data[(deq2.head + i.uint) and deq2.mask]:
+      return false
 
-  when compileOption("boundChecks"):
-    try:
-      echo deq[99]
-      assert false
-    except IndexDefect:
-      discard
-
-    try:
-      assert deq.len == 4
-      for i in 0 ..< 5: deq.popFirst()
-      assert false
-    except IndexDefect:
-      discard
-
-  # grabs some types of resize error.
-  deq = initDeque[int]()
-  for i in 1 .. 4: deq.addLast i
-  deq.popFirst()
-  deq.popLast()
-  for i in 5 .. 8: deq.addFirst i
-  assert $deq == "[8, 7, 6, 5, 2, 3]"
-
-  # Similar to proc from the documentation example
-  proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
-    var deq = initDeque[int]()
-    assert deq.len == 0
-    for i in 1 .. a: deq.addLast i
-
-    if b < deq.len: # checking before indexed access.
-      assert deq[b] == b + 1
-
-    # The following two lines don't need any checking on access due to the logic
-    # of the program, but that would not be the case if `a` could be 0.
-    assert deq.peekFirst == 1
-    assert deq.peekLast == a
-
-    while deq.len > 0: # checking if the deque is empty
-      assert deq.popFirst() > 0
-
-  #foo(0,0)
-  foo(8, 5)
-  foo(10, 9)
-  foo(1, 1)
-  foo(2, 1)
-  foo(1, 5)
-  foo(3, 2)
-
-  import sets
-  block t13310:
-    proc main() =
-      var q = initDeque[HashSet[int16]](2)
-      q.addFirst([1'i16].toHashSet)
-      q.addFirst([2'i16].toHashSet)
-      q.addFirst([3'i16].toHashSet)
-      assert $q == "[{3}, {2}, {1}]"
-
-    static:
-      main()
+  true
+
+func hash*[T](deq: Deque[T]): Hash =
+  ## Hashing of Deque.
+  var h: Hash = 0
+  for x in deq:
+    h = h !& hash(x)
+  !$h
diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim
index e998145e7..17785c8c7 100644
--- a/lib/pure/collections/hashcommon.nim
+++ b/lib/pure/collections/hashcommon.nim
@@ -7,17 +7,17 @@
 #    distribution, for details about the copyright.
 #
 
-# An ``include`` file which contains common code for
+# An `include` file which contains common code for
 # hash sets and tables.
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std / outparams
+
 const
   growthFactor = 2
 
-when not defined(nimHasDefault):
-  template default[T](t: typedesc[T]): T =
-    var v: T
-    v
-
 # hcode for real keys cannot be zero.  hcode==0 signifies an empty slot.  These
 # two procs retain clarity of that encoding without the space cost of an enum.
 proc isEmpty(hcode: Hash): bool {.inline.} =
@@ -30,16 +30,12 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = (h + 1) and maxHash
 
 proc mustRehash[T](t: T): bool {.inline.} =
+  # If this is changed, make sure to synchronize it with `slotsNeeded` below
   assert(t.dataLen > t.counter)
   result = (t.dataLen * 2 < t.counter * 3) or (t.dataLen - t.counter < 4)
 
-proc rightSize*(count: Natural): int {.inline.} =
-  ## Return the value of ``initialSize`` to support ``count`` items.
-  ##
-  ## If more items are expected to be added, simply add that
-  ## expected extra amount to the parameter before calling this.
-  #
-  # Make sure to synchronize with `mustRehash`
+proc slotsNeeded(count: Natural): int {.inline.} =
+  # Make sure to synchronize with `mustRehash` above
   result = nextPowerOfTwo(count * 3 div 2 + 4)
 
 template rawGetKnownHCImpl() {.dirty.} =
@@ -62,7 +58,10 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
 template genHashImpl(key, hc: typed) =
   hc = hash(key)
   if hc == 0: # This almost never taken branch should be very predictable.
-    hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
+    when sizeof(int) < 4:
+      hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit.
+    else:
+      hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
 
 template genHash(key: typed): Hash =
   var res: Hash
@@ -73,5 +72,5 @@ template rawGetImpl() {.dirty.} =
   genHashImpl(key, hc)
   rawGetKnownHCImpl()
 
-proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
   rawGetImpl()
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index 608fd2920..96f9b4430 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -6,77 +6,91 @@
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 
-##[
-  The `heapqueue` module implements a
-  `heap data structure<https://en.wikipedia.org/wiki/Heap_(data_structure)>`_
-  that can be used as a
-  `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_.
-  Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for
-  all `k`, counting elements from 0. The interesting property of a heap is that
-  `a[0]` is always its smallest element.
-
-  Basic usage
-  -----------
-  .. code-block:: Nim
-    import heapqueue
-
-    var heap = initHeapQueue[int]()
-    heap.push(8)
-    heap.push(2)
-    heap.push(5)
-    # The first element is the lowest element
-    assert heap[0] == 2
-    # Remove and return the lowest element
-    assert heap.pop() == 2
-    # The lowest element remaining is 5
-    assert heap[0] == 5
-
-  Usage with custom object
-  ------------------------
-  To use a `HeapQueue` with a custom object, the `<` operator must be
-  implemented.
-
-  .. code-block:: Nim
-    import heapqueue
-
-    type Job = object
-      priority: int
 
-    proc `<`(a, b: Job): bool = a.priority < b.priority
+## The `heapqueue` module implements a
+## `binary heap data structure<https://en.wikipedia.org/wiki/Binary_heap>`_
+## that can be used as a `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_.
+## They are represented as arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]`
+## for all indices `k` (counting elements from 0). The interesting property of a heap is that
+## `a[0]` is always its smallest element.
+##
+## Basic usage
+## -----------
+##
+runnableExamples:
+  var heap = [8, 2].toHeapQueue
+  heap.push(5)
+  # the first element is the lowest element
+  assert heap[0] == 2
+  # remove and return the lowest element
+  assert heap.pop() == 2
+  # the lowest element remaining is 5
+  assert heap[0] == 5
+
+## Usage with custom objects
+## -------------------------
+## To use a `HeapQueue` with a custom object, the `<` operator must be
+## implemented.
+
+runnableExamples:
+  type Job = object
+    priority: int
+
+  proc `<`(a, b: Job): bool = a.priority < b.priority
+
+  var jobs = initHeapQueue[Job]()
+  jobs.push(Job(priority: 1))
+  jobs.push(Job(priority: 2))
+
+  assert jobs[0].priority == 1
 
-    var jobs = initHeapQueue[Job]()
-    jobs.push(Job(priority: 1))
-    jobs.push(Job(priority: 2))
 
-    assert jobs[0].priority == 1
-]##
 import std/private/since
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type HeapQueue*[T] = object
   ## A heap queue, commonly known as a priority queue.
   data: seq[T]
 
 proc initHeapQueue*[T](): HeapQueue[T] =
-  ## Create a new empty heap.
-  discard
+  ## Creates a new empty heap.
+  ##
+  ## Heaps are initialized by default, so it is not necessary to call
+  ## this function explicitly.
+  ##
+  ## **See also:**
+  ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_
+  result = default(HeapQueue[T])
 
 proc len*[T](heap: HeapQueue[T]): int {.inline.} =
-  ## Return the number of elements of `heap`.
+  ## Returns the number of elements of `heap`.
+  runnableExamples:
+    let heap = [9, 5, 8].toHeapQueue
+    assert heap.len == 3
+
   heap.data.len
 
-proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} =
-  ## Access the i-th element of `heap`.
+proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} =
+  ## Accesses the i-th element of `heap`.
   heap.data[i]
 
-proc heapCmp[T](x, y: T): bool {.inline.} =
-  return (x < y)
+iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} =
+  ## Iterates over each item of `heap`.
+  let L = len(heap)
+  for i in 0 .. high(heap.data):
+    yield heap.data[i]
+    assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it")
+
+proc heapCmp[T](x, y: T): bool {.inline.} = x < y
 
-proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
-  ## 'heap' is a heap at all indices >= startpos, except possibly for pos.  pos
-  ## is the index of a leaf with a possibly out-of-order value.  Restore the
+proc siftup[T](heap: var HeapQueue[T], startpos, p: int) =
+  ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p`
+  ## is the index of a leaf with a possibly out-of-order value. Restores the
   ## heap invariant.
   var pos = p
-  var newitem = heap[pos]
+  let newitem = heap[pos]
   # Follow the path to the root, moving parents down until finding a place
   # newitem fits.
   while pos > startpos:
@@ -89,13 +103,14 @@ proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
       break
   heap.data[pos] = newitem
 
-proc siftup[T](heap: var HeapQueue[T], p: int) =
+proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) =
+  # This is faster when the element should be close to the bottom.
   let endpos = len(heap)
   var pos = p
   let startpos = pos
   let newitem = heap[pos]
   # Bubble up the smaller child until hitting a leaf.
-  var childpos = 2*pos + 1 # leftmost child position
+  var childpos = 2 * pos + 1 # leftmost child position
   while childpos < endpos:
     # Set childpos to index of smaller child.
     let rightpos = childpos + 1
@@ -104,132 +119,148 @@ proc siftup[T](heap: var HeapQueue[T], p: int) =
     # Move the smaller child up.
     heap.data[pos] = heap[childpos]
     pos = childpos
-    childpos = 2*pos + 1
-  # The leaf at pos is empty now.  Put newitem there, and bubble it up
+    childpos = 2 * pos + 1
+  # The leaf at pos is empty now. Put newitem there, and bubble it up
   # to its final resting place (by sifting its parents down).
   heap.data[pos] = newitem
-  siftdown(heap, startpos, pos)
+  siftup(heap, startpos, pos)
 
-proc push*[T](heap: var HeapQueue[T], item: T) =
-  ## Push `item` onto heap, maintaining the heap invariant.
+proc siftdown[T](heap: var HeapQueue[T], p: int) =
+  let endpos = len(heap)
+  var pos = p
+  let newitem = heap[pos]
+  var childpos = 2 * pos + 1
+  while childpos < endpos:
+    let rightpos = childpos + 1
+    if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
+      childpos = rightpos
+    if not heapCmp(heap[childpos], newitem):
+      break
+    heap.data[pos] = heap[childpos]
+    pos = childpos
+    childpos = 2 * pos + 1
+  heap.data[pos] = newitem
+
+proc push*[T](heap: var HeapQueue[T], item: sink T) =
+  ## Pushes `item` onto `heap`, maintaining the heap invariant.
   heap.data.add(item)
-  siftdown(heap, 0, len(heap)-1)
+  siftup(heap, 0, len(heap) - 1)
+
+proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} =
+  ## Creates a new HeapQueue that contains the elements of `x`.
+  ##
+  ## **See also:**
+  ## * `initHeapQueue proc <#initHeapQueue>`_
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    assert heap.pop() == 5
+    assert heap[0] == 8
+
+  # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
+  result.data = @x
+  for i in countdown(x.len div 2 - 1, 0):
+    siftdown(result, i)
 
 proc pop*[T](heap: var HeapQueue[T]): T =
-  ## Pop and return the smallest item from `heap`,
+  ## Pops and returns the smallest item from `heap`,
   ## maintaining the heap invariant.
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    assert heap.pop() == 5
+
   let lastelt = heap.data.pop()
   if heap.len > 0:
     result = heap[0]
     heap.data[0] = lastelt
-    siftup(heap, 0)
+    siftdownToBottom(heap, 0)
   else:
     result = lastelt
 
 proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} =
-  ## Linear scan to find index of item ``x`` or -1 if not found.
+  ## Linear scan to find the index of the item `x` or -1 if not found.
+  runnableExamples:
+    let heap = [9, 5, 8].toHeapQueue
+    assert heap.find(5) == 0
+    assert heap.find(9) == 1
+    assert heap.find(777) == -1
+
   result = -1
   for i in 0 ..< heap.len:
     if heap[i] == x: return i
 
+proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} =
+  ## Returns true if `x` is in `heap` or false if not found. This is a shortcut
+  ## for `find(heap, x) >= 0`.
+  result = find(heap, x) >= 0
+
 proc del*[T](heap: var HeapQueue[T], index: Natural) =
   ## Removes the element at `index` from `heap`, maintaining the heap invariant.
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    heap.del(1)
+    assert heap[0] == 5
+    assert heap[1] == 8
+
   swap(heap.data[^1], heap.data[index])
   let newLen = heap.len - 1
   heap.data.setLen(newLen)
   if index < newLen:
-    heap.siftup(index)
+    siftdownToBottom(heap, index)
 
-proc replace*[T](heap: var HeapQueue[T], item: T): T =
-  ## Pop and return the current smallest value, and add the new item.
-  ## This is more efficient than pop() followed by push(), and can be
+proc replace*[T](heap: var HeapQueue[T], item: sink T): T =
+  ## Pops and returns the current smallest value, and add the new item.
+  ## This is more efficient than `pop()` followed by `push()`, and can be
   ## more appropriate when using a fixed-size heap. Note that the value
-  ## returned may be larger than item! That constrains reasonable uses of
-  ## this routine unless written as part of a conditional replacement:
+  ## returned may be larger than `item`! That constrains reasonable uses of
+  ## this routine unless written as part of a conditional replacement.
   ##
-  ## .. code-block:: nim
-  ##    if item > heap[0]:
-  ##        item = replace(heap, item)
+  ## **See also:**
+  ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_
+  runnableExamples:
+    var heap = [5, 12].toHeapQueue
+    assert heap.replace(6) == 5
+    assert heap.len == 2
+    assert heap[0] == 6
+    assert heap.replace(4) == 6
+
   result = heap[0]
   heap.data[0] = item
-  siftup(heap, 0)
+  siftdown(heap, 0)
 
-proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
-  ## Fast version of a push followed by a pop.
-  if heap.len > 0 and heapCmp(heap[0], item):
-    swap(item, heap[0])
-    siftup(heap, 0)
-  return item
+proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T =
+  ## Fast version of a `push()` followed by a `pop()`.
+  ##
+  ## **See also:**
+  ## * `replace proc <#replace,HeapQueue[T],sinkT>`_
+  runnableExamples:
+    var heap = [5, 12].toHeapQueue
+    assert heap.pushpop(6) == 5
+    assert heap.len == 2
+    assert heap[0] == 6
+    assert heap.pushpop(4) == 4
+
+  result = item
+  if heap.len > 0 and heapCmp(heap.data[0], result):
+    swap(result, heap.data[0])
+    siftdown(heap, 0)
 
 proc clear*[T](heap: var HeapQueue[T]) =
-  ## Remove all elements from `heap`, making it empty.
+  ## Removes all elements from `heap`, making it empty.
   runnableExamples:
-    var heap = initHeapQueue[int]()
-    heap.push(1)
+    var heap = [9, 5, 8].toHeapQueue
     heap.clear()
     assert heap.len == 0
+
   heap.data.setLen(0)
 
 proc `$`*[T](heap: HeapQueue[T]): string =
-  ## Turn a heap into its string representation.
+  ## Turns a heap into its string representation.
   runnableExamples:
-    var heap = initHeapQueue[int]()
-    heap.push(1)
-    heap.push(2)
+    let heap = [1, 2].toHeapQueue
     assert $heap == "[1, 2]"
+
   result = "["
   for x in heap.data:
     if result.len > 1: result.add(", ")
     result.addQuoted(x)
   result.add("]")
-
-when isMainModule:
-  proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
-    var tmp = h
-    result = @[]
-    while tmp.len > 0:
-      result.add(pop(tmp))
-
-  block: # Simple sanity test
-    var heap = initHeapQueue[int]()
-    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
-    for item in data:
-      push(heap, item)
-    doAssert(heap[0] == 0)
-    doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
-
-  block: # Test del
-    var heap = initHeapQueue[int]()
-    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
-    for item in data: push(heap, item)
-
-    heap.del(0)
-    doAssert(heap[0] == 1)
-
-    heap.del(heap.find(7))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
-
-    heap.del(heap.find(5))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
-
-    heap.del(heap.find(6))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
-
-    heap.del(heap.find(2))
-    doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
-
-    doAssert(heap.find(2) == -1)
-
-  block: # Test del last
-    var heap = initHeapQueue[int]()
-    let data = [1, 2, 3]
-    for item in data: push(heap, item)
-
-    heap.del(2)
-    doAssert(heap.toSortedSeq == @[1, 2])
-
-    heap.del(1)
-    doAssert(heap.toSortedSeq == @[1])
-
-    heap.del(0)
-    doAssert(heap.toSortedSeq == @[])
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index eae3fa447..765a23e97 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -7,676 +7,17 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``intsets`` module implements an efficient `int` set implemented as a
-## `sparse bit set`:idx:.
-##
-## **Note**: Currently the assignment operator ``=`` for ``IntSet``
-## performs some rather meaningless shallow copy. Since Nim currently does
-## not allow the assignment operator to be overloaded, use `assign proc
-## <#assign,IntSet,IntSet>`_ to get a deep copy.
-##
-## **See also:**
-## * `sets module <sets.html>`_ for more general hash sets
+## Specialization of the generic `packedsets module <packedsets.html>`_
+## (see its documentation for more examples) for ordinal sparse sets.
 
-
-import
-  hashes
-
-type
-  BitScalar = uint
-
-const
-  InitIntSetSize = 8              # must be a power of two!
-  TrunkShift = 9
-  BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
-                                  # divisible by 64
-  TrunkMask = BitsPerTrunk - 1
-  IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
-  IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
-  IntMask = 1 shl IntShift - 1
+import std/private/since
+import std/packedsets
+export packedsets
 
 type
-  PTrunk = ref Trunk
-  Trunk = object
-    next: PTrunk                                # all nodes are connected with this pointer
-    key: int                                    # start address at bit 0
-    bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
-
-  TrunkSeq = seq[PTrunk]
-  IntSet* = object       ## An efficient set of `int` implemented as a sparse bit set.
-    elems: int           # only valid for small numbers
-    counter, max: int
-    head: PTrunk
-    data: TrunkSeq
-    a: array[0..33, int] # profiling shows that 34 elements are enough
-
-proc mustRehash[T](t: T): bool {.inline.} =
-  let length = t.max + 1
-  assert length > t.counter
-  result = (length * 2 < t.counter * 3) or (length - t.counter < 4)
-
-proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} =
-  const PERTURB_SHIFT = 5
-  var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT
-  perturb = cast[Hash](perturb2)
-  result = ((5*h) + 1 + perturb) and maxHash
-
-proc intSetGet(t: IntSet, key: int): PTrunk =
-  var h = key and t.max
-  var perturb = key
-  while t.data[h] != nil:
-    if t.data[h].key == key:
-      return t.data[h]
-    h = nextTry(h, t.max, perturb)
-  result = nil
-
-proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
-  var h = desc.key and t.max
-  var perturb = desc.key
-  while data[h] != nil:
-    assert(data[h] != desc)
-    h = nextTry(h, t.max, perturb)
-  assert(data[h] == nil)
-  data[h] = desc
-
-proc intSetEnlarge(t: var IntSet) =
-  var n: TrunkSeq
-  var oldMax = t.max
-  t.max = ((t.max + 1) * 2) - 1
-  newSeq(n, t.max + 1)
-  for i in countup(0, oldMax):
-    if t.data[i] != nil: intSetRawInsert(t, n, t.data[i])
-  swap(t.data, n)
-
-proc intSetPut(t: var IntSet, key: int): PTrunk =
-  var h = key and t.max
-  var perturb = key
-  while t.data[h] != nil:
-    if t.data[h].key == key:
-      return t.data[h]
-    h = nextTry(h, t.max, perturb)
-  if mustRehash(t): intSetEnlarge(t)
-  inc(t.counter)
-  h = key and t.max
-  perturb = key
-  while t.data[h] != nil: h = nextTry(h, t.max, perturb)
-  assert(t.data[h] == nil)
-  new(result)
-  result.next = t.head
-  result.key = key
-  t.head = result
-  t.data[h] = result
-
-proc bitincl(s: var IntSet, key: int) {.inline.} =
-  var ret: PTrunk
-  var t = intSetPut(s, `shr`(key, TrunkShift))
-  var u = key and TrunkMask
-  t.bits[u shr IntShift] = t.bits[u shr IntShift] or
-      (BitScalar(1) shl (u and IntMask))
-
-proc exclImpl(s: var IntSet, key: int) =
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key:
-        s.a[i] = s.a[s.elems-1]
-        dec s.elems
-        return
-  else:
-    var t = intSetGet(s, key shr TrunkShift)
-    if t != nil:
-      var u = key and TrunkMask
-      t.bits[u shr IntShift] = t.bits[u shr IntShift] and
-          not(BitScalar(1) shl (u and IntMask))
-
-template dollarImpl(): untyped =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.add($key)
-  result.add("}")
-
-
-iterator items*(s: IntSet): int {.inline.} =
-  ## Iterates over any included element of `s`.
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      yield s.a[i]
-  else:
-    var r = s.head
-    while r != nil:
-      var i = 0
-      while i <= high(r.bits):
-        var w: uint = r.bits[i]
-        # taking a copy of r.bits[i] here is correct, because
-        # modifying operations are not allowed during traversation
-        var j = 0
-        while w != 0: # test all remaining bits for zero
-          if (w and 1) != 0: # the bit is set!
-            yield (r.key shl TrunkShift) or (i shl IntShift +% j)
-          inc(j)
-          w = w shr 1
-        inc(i)
-      r = r.next
-
-
-proc initIntSet*: IntSet =
-  ## Returns an empty IntSet.
-  runnableExamples:
-    var a = initIntSet()
-    assert len(a) == 0
-
-  # newSeq(result.data, InitIntSetSize)
-  # result.max = InitIntSetSize-1
-  result = IntSet(
-    elems: 0,
-    counter: 0,
-    max: 0,
-    head: nil,
-    data: when defined(nimNoNilSeqs): @[] else: nil)
-  #  a: array[0..33, int] # profiling shows that 34 elements are enough
-
-proc contains*(s: IntSet, key: int): bool =
-  ## Returns true if `key` is in `s`.
-  ##
-  ## This allows the usage of `in` operator.
-  runnableExamples:
-    var a = initIntSet()
-    for x in [1, 3, 5]:
-      a.incl(x)
-    assert a.contains(3)
-    assert 3 in a
-    assert(not a.contains(8))
-    assert 8 notin a
-
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key: return true
-  else:
-    var t = intSetGet(s, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      result = (t.bits[u shr IntShift] and
-                (BitScalar(1) shl (u and IntMask))) != 0
-    else:
-      result = false
-
-proc incl*(s: var IntSet, key: int) =
-  ## Includes an element `key` in `s`.
-  ##
-  ## This doesn't do anything if `key` is already in `s`.
-  ##
-  ## See also:
-  ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
-  ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set
-  ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
-  runnableExamples:
-    var a = initIntSet()
-    a.incl(3)
-    a.incl(3)
-    assert len(a) == 1
-
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key: return
-    if s.elems < s.a.len:
-      s.a[s.elems] = key
-      inc s.elems
-      return
-    newSeq(s.data, InitIntSetSize)
-    s.max = InitIntSetSize-1
-    for i in 0..<s.elems:
-      bitincl(s, s.a[i])
-    s.elems = s.a.len + 1
-    # fall through:
-  bitincl(s, key)
-
-proc incl*(s: var IntSet, other: IntSet) =
-  ## Includes all elements from `other` into `s`.
-  ##
-  ## This is the in-place version of `s + other <#+,IntSet,IntSet>`_.
-  ##
-  ## See also:
-  ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
-  ## * `incl proc <#incl,IntSet,int>`_ for including an element
-  ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1)
-    b.incl(5)
-    a.incl(b)
-    assert len(a) == 2
-    assert 5 in a
-
-  for item in other: incl(s, item)
-
-proc containsOrIncl*(s: var IntSet, key: int): bool =
-  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
-  ##
-  ## The difference with regards to the `incl proc <#incl,IntSet,int>`_ is
-  ## that this proc returns `true` if `s` already contained `key`. The
-  ## proc will return `false` if `key` was added as a new value to `s` during
-  ## this call.
-  ##
-  ## See also:
-  ## * `incl proc <#incl,IntSet,int>`_ for including an element
-  ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
-  runnableExamples:
-    var a = initIntSet()
-    assert a.containsOrIncl(3) == false
-    assert a.containsOrIncl(3) == true
-    assert a.containsOrIncl(4) == false
-
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key:
-        return true
-    incl(s, key)
-    result = false
-  else:
-    var t = intSetGet(s, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0
-      if not result:
-        t.bits[u shr IntShift] = t.bits[u shr IntShift] or
-            (BitScalar(1) shl (u and IntMask))
-    else:
-      incl(s, key)
-      result = false
-
-proc excl*(s: var IntSet, key: int) =
-  ## Excludes `key` from the set `s`.
-  ##
-  ## This doesn't do anything if `key` is not found in `s`.
-  ##
-  ## See also:
-  ## * `incl proc <#incl,IntSet,int>`_ for including an element
-  ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
-  ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
-  runnableExamples:
-    var a = initIntSet()
-    a.incl(3)
-    a.excl(3)
-    a.excl(3)
-    a.excl(99)
-    assert len(a) == 0
-  exclImpl(s, key)
-
-proc excl*(s: var IntSet, other: IntSet) =
-  ## Excludes all elements from `other` from `s`.
-  ##
-  ## This is the in-place version of `s - other <#-,IntSet,IntSet>`_.
-  ##
-  ## See also:
-  ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set
-  ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
-  ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1)
-    a.incl(5)
-    b.incl(5)
-    a.excl(b)
-    assert len(a) == 1
-    assert 5 notin a
-
-  for item in other: excl(s, item)
-
-proc len*(s: IntSet): int {.inline.} =
-  ## Returns the number of elements in `s`.
-  if s.elems < s.a.len:
-    result = s.elems
-  else:
-    result = 0
-    for _ in s:
-      inc(result)
-
-proc missingOrExcl*(s: var IntSet, key: int): bool =
-  ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`.
-  ##
-  ## The difference with regards to the `excl proc <#excl,IntSet,int>`_ is
-  ## that this proc returns `true` if `key` was missing from `s`.
-  ## The proc will return `false` if `key` was in `s` and it was removed
-  ## during this call.
-  ##
-  ## See also:
-  ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
-  ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
-  ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
-  runnableExamples:
-    var a = initIntSet()
-    a.incl(5)
-    assert a.missingOrExcl(5) == false
-    assert a.missingOrExcl(5) == true
-
-  var count = s.len
-  exclImpl(s, key)
-  result = count == s.len
-
-proc clear*(result: var IntSet) =
-  ## Clears the IntSet back to an empty state.
-  runnableExamples:
-    var a = initIntSet()
-    a.incl(5)
-    a.incl(7)
-    clear(a)
-    assert len(a) == 0
-
-  # setLen(result.data, InitIntSetSize)
-  # for i in 0..InitIntSetSize-1: result.data[i] = nil
-  # result.max = InitIntSetSize-1
-  when defined(nimNoNilSeqs):
-    result.data = @[]
-  else:
-    result.data = nil
-  result.max = 0
-  result.counter = 0
-  result.head = nil
-  result.elems = 0
-
-proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0
-
-proc assign*(dest: var IntSet, src: IntSet) =
-  ## Copies `src` to `dest`.
-  ## `dest` does not need to be initialized by `initIntSet proc <#initIntSet>`_.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    b.incl(5)
-    b.incl(7)
-    a.assign(b)
-    assert len(a) == 2
-
-  if src.elems <= src.a.len:
-    when defined(nimNoNilSeqs):
-      dest.data = @[]
-    else:
-      dest.data = nil
-    dest.max = 0
-    dest.counter = src.counter
-    dest.head = nil
-    dest.elems = src.elems
-    dest.a = src.a
-  else:
-    dest.counter = src.counter
-    dest.max = src.max
-    dest.elems = src.elems
-    newSeq(dest.data, src.data.len)
-
-    var it = src.head
-    while it != nil:
-      var h = it.key and dest.max
-      var perturb = it.key
-      while dest.data[h] != nil: h = nextTry(h, dest.max, perturb)
-      assert(dest.data[h] == nil)
-      var n: PTrunk
-      new(n)
-      n.next = dest.head
-      n.key = it.key
-      n.bits = it.bits
-      dest.head = n
-      dest.data[h] = n
-      it = it.next
-
-proc union*(s1, s2: IntSet): IntSet =
-  ## Returns the union of the sets `s1` and `s2`.
-  ##
-  ## The same as `s1 + s2 <#+,IntSet,IntSet>`_.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1); a.incl(2); a.incl(3)
-    b.incl(3); b.incl(4); b.incl(5)
-    assert union(a, b).len == 5
-    ## {1, 2, 3, 4, 5}
-
-  result.assign(s1)
-  incl(result, s2)
-
-proc intersection*(s1, s2: IntSet): IntSet =
-  ## Returns the intersection of the sets `s1` and `s2`.
-  ##
-  ## The same as `s1 * s2 <#*,IntSet,IntSet>`_.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1); a.incl(2); a.incl(3)
-    b.incl(3); b.incl(4); b.incl(5)
-    assert intersection(a, b).len == 1
-    ## {3}
-
-  result = initIntSet()
-  for item in s1:
-    if contains(s2, item):
-      incl(result, item)
-
-proc difference*(s1, s2: IntSet): IntSet =
-  ## Returns the difference of the sets `s1` and `s2`.
-  ##
-  ## The same as `s1 - s2 <#-,IntSet,IntSet>`_.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1); a.incl(2); a.incl(3)
-    b.incl(3); b.incl(4); b.incl(5)
-    assert difference(a, b).len == 2
-    ## {1, 2}
-
-  result = initIntSet()
-  for item in s1:
-    if not contains(s2, item):
-      incl(result, item)
-
-proc symmetricDifference*(s1, s2: IntSet): IntSet =
-  ## Returns the symmetric difference of the sets `s1` and `s2`.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1); a.incl(2); a.incl(3)
-    b.incl(3); b.incl(4); b.incl(5)
-    assert symmetricDifference(a, b).len == 4
-    ## {1, 2, 4, 5}
-
-  result.assign(s1)
-  for item in s2:
-    if containsOrIncl(result, item): excl(result, item)
-
-proc `+`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `union(s1, s2) <#union,IntSet,IntSet>`_.
-  result = union(s1, s2)
-
-proc `*`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `intersection(s1, s2) <#intersection,IntSet,IntSet>`_.
-  result = intersection(s1, s2)
-
-proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `difference(s1, s2) <#difference,IntSet,IntSet>`_.
-  result = difference(s1, s2)
-
-proc disjoint*(s1, s2: IntSet): bool =
-  ## Returns true if the sets `s1` and `s2` have no items in common.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1); a.incl(2)
-    b.incl(2); b.incl(3)
-    assert disjoint(a, b) == false
-    b.excl(2)
-    assert disjoint(a, b) == true
-
-  for item in s1:
-    if contains(s2, item):
-      return false
-  return true
-
-proc card*(s: IntSet): int {.inline.} =
-  ## Alias for `len() <#len,IntSet>`_.
-  result = s.len()
-
-proc `<=`*(s1, s2: IntSet): bool =
-  ## Returns true if `s1` is subset of `s2`.
-  ##
-  ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily
-  ## have more elements than `s1`. That is, `s1` can be equal to `s2`.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1)
-    b.incl(1); b.incl(2)
-    assert a <= b
-    a.incl(2)
-    assert a <= b
-    a.incl(3)
-    assert(not (a <= b))
-
-  for item in s1:
-    if not s2.contains(item):
-      return false
-  return true
-
-proc `<`*(s1, s2: IntSet): bool =
-  ## Returns true if `s1` is proper subset of `s2`.
-  ##
-  ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has
-  ## more elements than `s1`.
-  runnableExamples:
-    var
-      a = initIntSet()
-      b = initIntSet()
-    a.incl(1)
-    b.incl(1); b.incl(2)
-    assert a < b
-    a.incl(2)
-    assert(not (a < b))
-  return s1 <= s2 and not (s2 <= s1)
-
-proc `==`*(s1, s2: IntSet): bool =
-  ## Returns true if both `s1` and `s2` have the same elements and set size.
-  return s1 <= s2 and s2 <= s1
-
-proc `$`*(s: IntSet): string =
-  ## The `$` operator for int sets.
-  ##
-  ## Converts the set `s` to a string, mostly for logging and printing purposes.
-  dollarImpl()
-
-
-
-when isMainModule:
-  import sequtils, algorithm
-
-  var x = initIntSet()
-  x.incl(1)
-  x.incl(2)
-  x.incl(7)
-  x.incl(1056)
-
-  x.incl(1044)
-  x.excl(1044)
-
-  assert x.containsOrIncl(888) == false
-  assert 888 in x
-  assert x.containsOrIncl(888) == true
-
-  assert x.missingOrExcl(888) == false
-  assert 888 notin x
-  assert x.missingOrExcl(888) == true
-
-  var xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 2, 7, 1056]
-
-  var y: IntSet
-  assign(y, x)
-  var ys = toSeq(items(y))
-  ys.sort(cmp[int])
-  assert ys == @[1, 2, 7, 1056]
-
-  assert x == y
-
-  var z: IntSet
-  for i in 0..1000:
-    incl z, i
-    assert z.len() == i+1
-  for i in 0..1000:
-    assert z.contains(i)
-
-  var w = initIntSet()
-  w.incl(1)
-  w.incl(4)
-  w.incl(50)
-  w.incl(1001)
-  w.incl(1056)
-
-  var xuw = x.union(w)
-  var xuws = toSeq(items(xuw))
-  xuws.sort(cmp[int])
-  assert xuws == @[1, 2, 4, 7, 50, 1001, 1056]
-
-  var xiw = x.intersection(w)
-  var xiws = toSeq(items(xiw))
-  xiws.sort(cmp[int])
-  assert xiws == @[1, 1056]
-
-  var xdw = x.difference(w)
-  var xdws = toSeq(items(xdw))
-  xdws.sort(cmp[int])
-  assert xdws == @[2, 7]
-
-  var xsw = x.symmetricDifference(w)
-  var xsws = toSeq(items(xsw))
-  xsws.sort(cmp[int])
-  assert xsws == @[2, 4, 7, 50, 1001]
-
-  x.incl(w)
-  xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 2, 4, 7, 50, 1001, 1056]
-
-  assert w <= x
-
-  assert w < x
-
-  assert(not disjoint(w, x))
-
-  var u = initIntSet()
-  u.incl(3)
-  u.incl(5)
-  u.incl(500)
-  assert disjoint(u, x)
-
-  var v = initIntSet()
-  v.incl(2)
-  v.incl(50)
-
-  x.excl(v)
-  xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 4, 7, 1001, 1056]
-
-  proc bug12366 =
-    var
-      x = initIntSet()
-      y = initIntSet()
-      n = 3584
+  IntSet* = PackedSet[int]
 
-    for i in 0..n:
-      x.incl(i)
-      y.incl(i)
+proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3), inline.} = toPackedSet[int](x)
 
-    let z = symmetricDifference(x, y)
-    doAssert z.len == 0
-    doAssert $z == "{}"
+proc initIntSet*(): IntSet {.inline.} = initPackedSet[int]()
 
-  bug12366()
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index bd22949bf..6b88747ef 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -13,119 +13,90 @@
 ## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists)
 ## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists)
 ##
-##
-## Basic Usage
-## ===========
-##
+## # Basic Usage
 ## Because it makes no sense to do otherwise, the `next` and `prev` pointers
 ## are not hidden from you and can be manipulated directly for efficiency.
 ##
-## Lists
-## -----
-##
-## .. code-block::
-##   import lists
-##
-##   var
-##     l = initDoublyLinkedList[int]()
-##     a = newDoublyLinkedNode[int](3)
-##     b = newDoublyLinkedNode[int](7)
-##     c = newDoublyLinkedNode[int](9)
-##
-##   l.append(a)
-##   l.append(b)
-##   l.prepend(c)
-##
-##   assert a.next == b
-##   assert a.prev == c
-##   assert c.next == a
-##   assert c.next.next == b
-##   assert c.prev == nil
-##   assert b.next == nil
-##
-##
-## Rings
-## -----
-##
-## .. code-block::
-##   import lists
-##
-##   var
-##     l = initSinglyLinkedRing[int]()
-##     a = newSinglyLinkedNode[int](3)
-##     b = newSinglyLinkedNode[int](7)
-##     c = newSinglyLinkedNode[int](9)
-##
-##   l.append(a)
-##   l.append(b)
-##   l.prepend(c)
-##
-##   assert c.next == a
-##   assert a.next == b
-##   assert c.next.next == b
-##   assert b.next == c
-##   assert c.next.next.next == c
-##
-## See also
-## ========
-##
+## ## Lists
+runnableExamples:
+  var list = initDoublyLinkedList[int]()
+  let
+    a = newDoublyLinkedNode[int](3)
+    b = newDoublyLinkedNode[int](7)
+    c = newDoublyLinkedNode[int](9)
+
+  list.add(a)
+  list.add(b)
+  list.prepend(c)
+
+  assert a.next == b
+  assert a.prev == c
+  assert c.next == a
+  assert c.next.next == b
+  assert c.prev == nil
+  assert b.next == nil
+
+## ## Rings
+runnableExamples:
+  var ring = initSinglyLinkedRing[int]()
+  let
+    a = newSinglyLinkedNode[int](3)
+    b = newSinglyLinkedNode[int](7)
+    c = newSinglyLinkedNode[int](9)
+
+  ring.add(a)
+  ring.add(b)
+  ring.prepend(c)
+
+  assert c.next == a
+  assert a.next == b
+  assert c.next.next == b
+  assert b.next == c
+  assert c.next.next.next == c
+
+## # See also
 ## * `deques module <deques.html>`_ for double-ended queues
-## * `sharedlist module <sharedlist.html>`_ for shared singly-linked lists
-
 
-when not defined(nimhygiene):
-  {.pragma: dirty.}
+import std/private/since
 
-when not defined(nimHasCursor):
-  {.pragma: cursor.}
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
-  DoublyLinkedNodeObj*[T] = object ## \
-    ## A node a doubly linked list consists of.
+  DoublyLinkedNodeObj*[T] = object
+    ## A node of a doubly linked list.
     ##
     ## It consists of a `value` field, and pointers to `next` and `prev`.
-    next*: <//>(ref DoublyLinkedNodeObj[T])
-    prev* {.cursor.}: ref DoublyLinkedNodeObj[T]
+    next*: DoublyLinkedNode[T]
+    prev* {.cursor.}: DoublyLinkedNode[T]
     value*: T
   DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
 
-  SinglyLinkedNodeObj*[T] = object ## \
-    ## A node a singly linked list consists of.
+  SinglyLinkedNodeObj*[T] = object
+    ## A node of a singly linked list.
     ##
     ## It consists of a `value` field, and a pointer to `next`.
-    next*: <//>(ref SinglyLinkedNodeObj[T])
+    next*: SinglyLinkedNode[T]
     value*: T
   SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
 
-  SinglyLinkedList*[T] = object ## \
+  SinglyLinkedList*[T] = object
     ## A singly linked list.
-    ##
-    ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
-    ## a new empty list.
-    head*: <//>(SinglyLinkedNode[T])
+    head*: SinglyLinkedNode[T]
     tail* {.cursor.}: SinglyLinkedNode[T]
 
-  DoublyLinkedList*[T] = object ## \
+  DoublyLinkedList*[T] = object
     ## A doubly linked list.
-    ##
-    ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
-    ## a new empty list.
-    head*: <//>(DoublyLinkedNode[T])
+    head*: DoublyLinkedNode[T]
     tail* {.cursor.}: DoublyLinkedNode[T]
 
-  SinglyLinkedRing*[T] = object ## \
+  SinglyLinkedRing*[T] = object
     ## A singly linked ring.
-    ##
-    ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
-    ## a new empty ring.
-    head*: <//>(SinglyLinkedNode[T])
+    head*: SinglyLinkedNode[T]
     tail* {.cursor.}: SinglyLinkedNode[T]
 
-  DoublyLinkedRing*[T] = object ## \
+  DoublyLinkedRing*[T] = object
     ## A doubly linked ring.
-    ##
-    ## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing>`_ to create
-    ## a new empty ring.
     head*: DoublyLinkedNode[T]
 
   SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
@@ -138,54 +109,70 @@ type
 
 proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
   ## Creates a new singly linked list that is empty.
+  ##
+  ## Singly linked lists are initialized by default, so it is not necessary to
+  ## call this function explicitly.
   runnableExamples:
-    var a = initSinglyLinkedList[int]()
+    let a = initSinglyLinkedList[int]()
+
   discard
 
 proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
   ## Creates a new doubly linked list that is empty.
+  ##
+  ## Doubly linked lists are initialized by default, so it is not necessary to
+  ## call this function explicitly.
   runnableExamples:
-    var a = initDoublyLinkedList[int]()
+    let a = initDoublyLinkedList[int]()
+
   discard
 
 proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
   ## Creates a new singly linked ring that is empty.
+  ##
+  ## Singly linked rings are initialized by default, so it is not necessary to
+  ## call this function explicitly.
   runnableExamples:
-    var a = initSinglyLinkedRing[int]()
+    let a = initSinglyLinkedRing[int]()
+
   discard
 
 proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
   ## Creates a new doubly linked ring that is empty.
+  ##
+  ## Doubly linked rings are initialized by default, so it is not necessary to
+  ## call this function explicitly.
   runnableExamples:
-    var a = initDoublyLinkedRing[int]()
+    let a = initDoublyLinkedRing[int]()
+
   discard
 
-proc newDoublyLinkedNode*[T](value: T): <//>(DoublyLinkedNode[T]) =
+proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
   ## Creates a new doubly linked node with the given `value`.
   runnableExamples:
-    var n = newDoublyLinkedNode[int](5)
+    let n = newDoublyLinkedNode[int](5)
     assert n.value == 5
 
   new(result)
   result.value = value
 
-proc newSinglyLinkedNode*[T](value: T): <//>(SinglyLinkedNode[T]) =
+proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
   ## Creates a new singly linked node with the given `value`.
   runnableExamples:
-    var n = newSinglyLinkedNode[int](5)
+    let n = newSinglyLinkedNode[int](5)
     assert n.value == 5
 
   new(result)
   result.value = value
 
 template itemsListImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   while it != nil:
     yield it.value
     it = it.next
 
 template itemsRingImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   if it != nil:
     while true:
       yield it.value
@@ -195,101 +182,91 @@ template itemsRingImpl() {.dirty.} =
 iterator items*[T](L: SomeLinkedList[T]): T =
   ## Yields every value of `L`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
   ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block::
-  ##   var a = initSinglyLinkedList[int]()
-  ##   for i in 1 .. 3:
-  ##     a.append(10*i)
-  ##
-  ##   for x in a:  # the same as: for x in items(a):
-  ##     echo x
-  ##
-  ##   # 10
-  ##   # 20
-  ##   # 30
+  runnableExamples:
+    from std/sugar import collect
+    from std/sequtils import toSeq
+    let a = collect(initSinglyLinkedList):
+      for i in 1..3: 10 * i
+    assert toSeq(items(a)) == toSeq(a)
+    assert toSeq(a) == @[10, 20, 30]
+
   itemsListImpl()
 
 iterator items*[T](L: SomeLinkedRing[T]): T =
   ## Yields every value of `L`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
   ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block::
-  ##   var a = initSinglyLinkedRing[int]()
-  ##   for i in 1 .. 3:
-  ##     a.append(10*i)
-  ##
-  ##   for x in a:  # the same as: for x in items(a):
-  ##     echo x
-  ##
-  ##   # 10
-  ##   # 20
-  ##   # 30
+  runnableExamples:
+    from std/sugar import collect
+    from std/sequtils import toSeq
+    let a = collect(initSinglyLinkedRing):
+      for i in 1..3: 10 * i
+    assert toSeq(items(a)) == toSeq(a)
+    assert toSeq(a) == @[10, 20, 30]
+
   itemsRingImpl()
 
 iterator mitems*[T](L: var SomeLinkedList[T]): var T =
   ## Yields every value of `L` so that you can modify it.
   ##
-  ## See also:
+  ## **See also:**
   ## * `items iterator <#items.i,SomeLinkedList[T]>`_
   ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
   runnableExamples:
     var a = initSinglyLinkedList[int]()
-    for i in 1 .. 5:
-      a.append(10*i)
+    for i in 1..5:
+      a.add(10 * i)
     assert $a == "[10, 20, 30, 40, 50]"
     for x in mitems(a):
-      x = 5*x - 1
+      x = 5 * x - 1
     assert $a == "[49, 99, 149, 199, 249]"
+
   itemsListImpl()
 
 iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
   ## Yields every value of `L` so that you can modify it.
   ##
-  ## See also:
+  ## **See also:**
   ## * `items iterator <#items.i,SomeLinkedRing[T]>`_
   ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
   runnableExamples:
     var a = initSinglyLinkedRing[int]()
-    for i in 1 .. 5:
-      a.append(10*i)
+    for i in 1..5:
+      a.add(10 * i)
     assert $a == "[10, 20, 30, 40, 50]"
     for x in mitems(a):
-      x = 5*x - 1
+      x = 5 * x - 1
     assert $a == "[49, 99, 149, 199, 249]"
+
   itemsRingImpl()
 
 iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
   ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
   ##
-  ## See also:
+  ## **See also:**
   ## * `items iterator <#items.i,SomeLinkedList[T]>`_
   ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
   runnableExamples:
     var a = initDoublyLinkedList[int]()
-    for i in 1 .. 5:
-      a.append(10*i)
+    for i in 1..5:
+      a.add(10 * i)
     assert $a == "[10, 20, 30, 40, 50]"
     for x in nodes(a):
       if x.value == 30:
         a.remove(x)
       else:
-        x.value = 5*x.value - 1
+        x.value = 5 * x.value - 1
     assert $a == "[49, 99, 199, 249]"
 
-  var it = L.head
+  var it {.cursor.} = L.head
   while it != nil:
-    var nxt = it.next
+    let nxt = it.next
     yield it
     it = nxt
 
@@ -297,31 +274,35 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
   ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
   ##
-  ## See also:
+  ## **See also:**
   ## * `items iterator <#items.i,SomeLinkedRing[T]>`_
   ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
   runnableExamples:
     var a = initDoublyLinkedRing[int]()
-    for i in 1 .. 5:
-      a.append(10*i)
+    for i in 1..5:
+      a.add(10 * i)
     assert $a == "[10, 20, 30, 40, 50]"
     for x in nodes(a):
       if x.value == 30:
         a.remove(x)
       else:
-        x.value = 5*x.value - 1
+        x.value = 5 * x.value - 1
     assert $a == "[49, 99, 199, 249]"
 
-  var it = L.head
+  var it {.cursor.} = L.head
   if it != nil:
     while true:
-      var nxt = it.next
+      let nxt = it.next
       yield it
       it = nxt
       if it == L.head: break
 
 proc `$`*[T](L: SomeLinkedCollection[T]): string =
   ## Turns a list into its string representation for logging and printing.
+  runnableExamples:
+    let a = [1, 2, 3, 4].toSinglyLinkedList
+    assert $a == "[1, 2, 3, 4]"
+
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
@@ -332,12 +313,10 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
   ## Searches in the list for a value. Returns `nil` if the value does not
   ## exist.
   ##
-  ## See also:
+  ## **See also:**
   ## * `contains proc <#contains,SomeLinkedCollection[T],T>`_
   runnableExamples:
-    var a = initSinglyLinkedList[int]()
-    a.append(9)
-    a.append(8)
+    let a = [9, 8].toSinglyLinkedList
     assert a.find(9).value == 9
     assert a.find(1) == nil
 
@@ -346,14 +325,13 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
 
 proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
   ## Searches in the list for a value. Returns `false` if the value does not
-  ## exist, `true` otherwise.
+  ## exist, `true` otherwise. This allows the usage of the `in` and `notin`
+  ## operators.
   ##
-  ## See also:
+  ## **See also:**
   ## * `find proc <#find,SomeLinkedCollection[T],T>`_
   runnableExamples:
-    var a = initSinglyLinkedList[int]()
-    a.append(9)
-    a.append(8)
+    let a = [9, 8].toSinglyLinkedList
     assert a.contains(9)
     assert 8 in a
     assert(not a.contains(1))
@@ -361,20 +339,65 @@ proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
 
   result = find(L, value) != nil
 
-proc append*[T](L: var SinglyLinkedList[T],
-                n: SinglyLinkedNode[T]) {.inline.} =
+proc prepend*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} =
+  ## Prepends a shallow copy of `b` to the beginning of `a`.
+  ##
+  ## **See also:**
+  ## * `prependMoved proc <#prependMoved,T,T>`_
+  ##   for moving the second list instead of copying
+  runnableExamples:
+    from std/sequtils import toSeq
+    var a = [4, 5].toSinglyLinkedList
+    let b = [1, 2, 3].toSinglyLinkedList
+    a.prepend(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == [1, 2, 3]
+    a.prepend(a)
+    assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+
+  var tmp = b.copy
+  tmp.addMoved(a)
+  a = tmp
+
+proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
+  ## Moves `b` before the head of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-prepending results in a cycle.
+  ##
+  ## **See also:**
+  ## * `prepend proc <#prepend,T,T>`_
+  ##   for prepending a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [4, 5].toSinglyLinkedList
+      b = [1, 2, 3].toSinglyLinkedList
+      c = [0, 1].toSinglyLinkedList
+    a.prependMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.prependMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  b.addMoved(a)
+  swap a, b
+
+proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} =
   ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
   runnableExamples:
-    var
-      a = initSinglyLinkedList[int]()
-      n = newSinglyLinkedNode[int](9)
-    a.append(n)
+    var a = initSinglyLinkedList[int]()
+    let n = newSinglyLinkedNode[int](9)
+    a.add(n)
     assert a.contains(9)
 
   n.next = nil
@@ -384,34 +407,34 @@ proc append*[T](L: var SinglyLinkedList[T],
   L.tail = n
   if L.head == nil: L.head = n
 
-proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
+proc add*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
   ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
   runnableExamples:
     var a = initSinglyLinkedList[int]()
-    a.append(9)
-    a.append(8)
+    a.add(9)
+    a.add(8)
     assert a.contains(9)
-  append(L, newSinglyLinkedNode(value))
+
+  add(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedList[T],
                  n: SinglyLinkedNode[T]) {.inline.} =
   ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
   runnableExamples:
-    var
-      a = initSinglyLinkedList[int]()
-      n = newSinglyLinkedNode[int](9)
+    var a = initSinglyLinkedList[int]()
+    let n = newSinglyLinkedNode[int](9)
     a.prepend(n)
     assert a.contains(9)
 
@@ -422,10 +445,10 @@ proc prepend*[T](L: var SinglyLinkedList[T],
 proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
   ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   runnableExamples:
@@ -433,25 +456,80 @@ proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
     a.prepend(9)
     a.prepend(8)
     assert a.contains(9)
-  prepend(L, newSinglyLinkedNode(value))
 
+  prepend(L, newSinglyLinkedNode(value))
 
+func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a shallow copy of `a`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    type Foo = ref object
+      x: int
+    var
+      f = Foo(x: 1)
+      a = [f].toSinglyLinkedList
+    let b = a.copy
+    a.add([f].toSinglyLinkedList)
+    assert a.toSeq == [f, f]
+    assert b.toSeq == [f] # b isn't modified...
+    f.x = 42
+    assert a.head.value.x == 42
+    assert b.head.value.x == 42 # ... but the elements are not deep copied
+
+    let c = [1, 2, 3].toSinglyLinkedList
+    assert $c == $c.copy
+
+  result = initSinglyLinkedList[T]()
+  for x in a.items:
+    result.add(x)
+
+proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} =
+  ## Moves `b` to the end of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-adding results in a cycle.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,T,T>`_ for adding a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [1, 2, 3].toSinglyLinkedList
+      b = [4, 5].toSinglyLinkedList
+      c = [0, 1].toSinglyLinkedList
+    a.addMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.addMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  if b.head != nil:
+    if a.head == nil:
+      a.head = b.head
+    else:
+      a.tail.next = b.head
+    a.tail = b.tail
+  if a.addr != b.addr:
+    b.head = nil
+    b.tail = nil
 
-proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
+proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
   ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for removing a node
   runnableExamples:
-    var
-      a = initDoublyLinkedList[int]()
-      n = newDoublyLinkedNode[int](9)
-    a.append(n)
+    var a = initDoublyLinkedList[int]()
+    let n = newDoublyLinkedNode[int](9)
+    a.add(n)
     assert a.contains(9)
 
   n.next = nil
@@ -462,11 +540,11 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   L.tail = n
   if L.head == nil: L.head = n
 
-proc append*[T](L: var DoublyLinkedList[T], value: T) =
+proc add*[T](L: var DoublyLinkedList[T], value: T) =
   ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
   ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
@@ -475,25 +553,25 @@ proc append*[T](L: var DoublyLinkedList[T], value: T) =
   ##   for removing a node
   runnableExamples:
     var a = initDoublyLinkedList[int]()
-    a.append(9)
-    a.append(8)
+    a.add(9)
+    a.add(8)
     assert a.contains(9)
-  append(L, newDoublyLinkedNode(value))
+
+  add(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
   ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for removing a node
   runnableExamples:
-    var
-      a = initDoublyLinkedList[int]()
-      n = newDoublyLinkedNode[int](9)
+    var a = initDoublyLinkedList[int]()
+    let n = newDoublyLinkedNode[int](9)
     a.prepend(n)
     assert a.contains(9)
 
@@ -508,10 +586,10 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
 proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
   ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
@@ -521,18 +599,147 @@ proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
     a.prepend(9)
     a.prepend(8)
     assert a.contains(9)
+
   prepend(L, newDoublyLinkedNode(value))
 
+func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a shallow copy of `a`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    type Foo = ref object
+      x: int
+    var
+      f = Foo(x: 1)
+      a = [f].toDoublyLinkedList
+    let b = a.copy
+    a.add([f].toDoublyLinkedList)
+    assert a.toSeq == [f, f]
+    assert b.toSeq == [f] # b isn't modified...
+    f.x = 42
+    assert a.head.value.x == 42
+    assert b.head.value.x == 42 # ... but the elements are not deep copied
+
+    let c = [1, 2, 3].toDoublyLinkedList
+    assert $c == $c.copy
+
+  result = initDoublyLinkedList[T]()
+  for x in a.items:
+    result.add(x)
+
+proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} =
+  ## Moves `b` to the end of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-adding results in a cycle.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,T,T>`_
+  ##   for adding a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [1, 2, 3].toDoublyLinkedList
+      b = [4, 5].toDoublyLinkedList
+      c = [0, 1].toDoublyLinkedList
+    a.addMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.addMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  if b.head != nil:
+    if a.head == nil:
+      a.head = b.head
+    else:
+      b.head.prev = a.tail
+      a.tail.next = b.head
+    a.tail = b.tail
+  if a.addr != b.addr:
+    b.head = nil
+    b.tail = nil
+
+proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} =
+  ## Appends a shallow copy of `b` to the end of `a`.
+  ##
+  ## **See also:**
+  ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
+  ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
+  ##   for moving the second list instead of copying
+  runnableExamples:
+    from std/sequtils import toSeq
+    var a = [1, 2, 3].toSinglyLinkedList
+    let b = [4, 5].toSinglyLinkedList
+    a.add(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == [4, 5]
+    a.add(a)
+    assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+
+  var tmp = b.copy
+  a.addMoved(tmp)
+
+proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} =
+  ## Removes a node `n` from `L`.
+  ## Returns `true` if `n` was found in `L`.
+  ## Efficiency: O(n); the list is traversed until `n` is found.
+  ## Attempting to remove an element not contained in the list is a no-op.
+  ## When the list is cyclic, the cycle is preserved after removal.
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var a = [0, 1, 2].toSinglyLinkedList
+    let n = a.head.next
+    assert n.value == 1
+    assert a.remove(n) == true
+    assert a.toSeq == [0, 2]
+    assert a.remove(n) == false
+    assert a.toSeq == [0, 2]
+    a.addMoved(a) # cycle: [0, 2, 0, 2, ...]
+    a.remove(a.head)
+    let s = collect:
+      for i, ai in enumerate(a):
+        if i == 4: break
+        ai
+    assert s == [2, 2, 2, 2]
+
+  if n == L.head:
+    L.head = n.next
+    if L.tail.next == n:
+      L.tail.next = L.head # restore cycle
+  else:
+    var prev {.cursor.} = L.head
+    while prev.next != n and prev.next != nil:
+      prev = prev.next
+    if prev.next == nil:
+      return false
+    prev.next = n.next
+    if L.tail == n:
+      L.tail = prev # update tail if we removed the last node
+  true
+
 proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   ## Removes a node `n` from `L`. Efficiency: O(1).
+  ## This function assumes, for the sake of efficiency, that `n` is contained in `L`,
+  ## otherwise the effects are undefined.
+  ## When the list is cyclic, the cycle is preserved after removal.
   runnableExamples:
-    var
-      a = initDoublyLinkedList[int]()
-      n = newDoublyLinkedNode[int](5)
-    a.append(n)
-    assert 5 in a
+    import std/[sequtils, enumerate, sugar]
+    var a = [0, 1, 2].toSinglyLinkedList
+    let n = a.head.next
+    assert n.value == 1
     a.remove(n)
-    assert 5 notin a
+    assert a.toSeq == [0, 2]
+    a.remove(n)
+    assert a.toSeq == [0, 2]
+    a.addMoved(a) # cycle: [0, 2, 0, 2, ...]
+    a.remove(a.head)
+    let s = collect:
+      for i, ai in enumerate(a):
+        if i == 4: break
+        ai
+    assert s == [2, 2, 2, 2]
 
   if n == L.tail: L.tail = n.prev
   if n == L.head: L.head = n.next
@@ -541,59 +748,57 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
 
 
 
-proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
+proc add*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
   runnableExamples:
-    var
-      a = initSinglyLinkedRing[int]()
-      n = newSinglyLinkedNode[int](9)
-    a.append(n)
+    var a = initSinglyLinkedRing[int]()
+    let n = newSinglyLinkedNode[int](9)
+    a.add(n)
     assert a.contains(9)
 
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
     L.tail.next = n
-    L.tail = n
   else:
     n.next = n
     L.head = n
-    L.tail = n
+  L.tail = n
 
-proc append*[T](L: var SinglyLinkedRing[T], value: T) =
+proc add*[T](L: var SinglyLinkedRing[T], value: T) =
   ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for appending a node
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
   runnableExamples:
     var a = initSinglyLinkedRing[int]()
-    a.append(9)
-    a.append(8)
+    a.add(9)
+    a.add(8)
     assert a.contains(9)
-  append(L, newSinglyLinkedNode(value))
+
+  add(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
   runnableExamples:
-    var
-      a = initSinglyLinkedRing[int]()
-      n = newSinglyLinkedNode[int](9)
+    var a = initSinglyLinkedRing[int]()
+    let n = newSinglyLinkedNode[int](9)
     a.prepend(n)
     assert a.contains(9)
 
@@ -609,10 +814,10 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
 proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
   ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
   ##   for prepending a node
   runnableExamples:
@@ -620,25 +825,25 @@ proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
     a.prepend(9)
     a.prepend(8)
     assert a.contains(9)
+
   prepend(L, newSinglyLinkedNode(value))
 
 
 
-proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
+proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
   ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for removing a node
   runnableExamples:
-    var
-      a = initDoublyLinkedRing[int]()
-      n = newDoublyLinkedNode[int](9)
-    a.append(n)
+    var a = initDoublyLinkedRing[int]()
+    let n = newDoublyLinkedNode[int](9)
+    a.add(n)
     assert a.contains(9)
 
   if L.head != nil:
@@ -651,11 +856,11 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
     n.next = n
     L.head = n
 
-proc append*[T](L: var DoublyLinkedRing[T], value: T) =
+proc add*[T](L: var DoublyLinkedRing[T], value: T) =
   ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
   ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
@@ -664,25 +869,25 @@ proc append*[T](L: var DoublyLinkedRing[T], value: T) =
   ##   for removing a node
   runnableExamples:
     var a = initDoublyLinkedRing[int]()
-    a.append(9)
-    a.append(8)
+    a.add(9)
+    a.add(8)
     assert a.contains(9)
-  append(L, newDoublyLinkedNode(value))
+
+  add(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
   ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for removing a node
   runnableExamples:
-    var
-      a = initDoublyLinkedRing[int]()
-      n = newDoublyLinkedNode[int](9)
+    var a = initDoublyLinkedRing[int]()
+    let n = newDoublyLinkedNode[int](9)
     a.prepend(n)
     assert a.contains(9)
 
@@ -699,10 +904,10 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
 proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
   ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
   ##
-  ## See also:
-  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for appending a node
-  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value
   ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
   ##   for prepending a node
   ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
@@ -712,15 +917,17 @@ proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
     a.prepend(9)
     a.prepend(8)
     assert a.contains(9)
+
   prepend(L, newDoublyLinkedNode(value))
 
 proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   ## Removes `n` from `L`. Efficiency: O(1).
+  ## This function assumes, for the sake of efficiency, that `n` is contained in `L`,
+  ## otherwise the effects are undefined.
   runnableExamples:
-    var
-      a = initDoublyLinkedRing[int]()
-      n = newDoublyLinkedNode[int](5)
-    a.append(n)
+    var a = initDoublyLinkedRing[int]()
+    let n = newDoublyLinkedNode[int](5)
+    a.add(n)
     assert 5 in a
     a.remove(n)
     assert 5 notin a
@@ -728,9 +935,81 @@ proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   n.next.prev = n.prev
   n.prev.next = n.next
   if n == L.head:
-    var p = L.head.prev
+    let p = L.head.prev
     if p == L.head:
       # only one element left:
       L.head = nil
     else:
-      L.head = L.head.prev
+      L.head = p
+
+proc append*[T](a: var (SinglyLinkedList[T] | SinglyLinkedRing[T]),
+                b: SinglyLinkedList[T] | SinglyLinkedNode[T] | T) =
+  ## Alias for `a.add(b)`.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_
+  ## * `add proc <#add,T,T>`_
+  a.add(b)
+
+proc append*[T](a: var (DoublyLinkedList[T] | DoublyLinkedRing[T]),
+                b: DoublyLinkedList[T] | DoublyLinkedNode[T] | T) =
+  ## Alias for `a.add(b)`.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ## * `add proc <#add,DoublyLinkedList[T],T>`_
+  ## * `add proc <#add,T,T>`_
+  a.add(b)
+
+proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
+  ## Alias for `a.addMoved(b)`.
+  ##
+  ## **See also:**
+  ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
+  ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
+  a.addMoved(b)
+
+func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `SinglyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toSinglyLinkedRing*[T](elems: openArray[T]): SinglyLinkedRing[T] =
+  ## Creates a new `SinglyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `DoublyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedRing*[T](elems: openArray[T]): DoublyLinkedRing[T] =
+  ## Creates a new `DoublyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index f101d508e..3c0d8dc0e 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -7,19 +7,19 @@
 #    distribution, for details about the copyright.
 #
 
-## Although this module has ``seq`` in its name, it implements operations
-## not only for `seq`:idx: type, but for three built-in container types under
-## the ``openArray`` umbrella:
+## Although this module has `seq` in its name, it implements operations
+## not only for the `seq`:idx: type, but for three built-in container types
+## under the `openArray` umbrella:
 ## * sequences
 ## * strings
 ## * array
 ##
-## The system module defines several common functions, such as:
-## * ``newSeq[T]`` for creating new sequences of type ``T``
-## * ``@`` for converting arrays and strings to sequences
-## * ``add`` for adding new elements to strings and sequences
-## * ``&`` for string and seq concatenation
-## * ``in`` (alias for ``contains``) and ``notin`` for checking if an item is
+## The `system` module defines several common functions, such as:
+## * `newSeq[T]` for creating new sequences of type `T`
+## * `@` for converting arrays and strings to sequences
+## * `add` for adding new elements to strings and sequences
+## * `&` for string and seq concatenation
+## * `in` (alias for `contains`) and `notin` for checking if an item is
 ##   in a container
 ##
 ## This module builds upon that, providing additional functionality in form of
@@ -27,45 +27,52 @@
 ## languages.
 ##
 ## For functional style programming you have different options at your disposal:
-## * pass `anonymous proc<manual.html#procedures-anonymous-procs>`_
-## * import `sugar module<sugar.html>`_  and use
-##   `=> macro<sugar.html#%3D>.m,untyped,untyped>`_
+## * the `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+## * pass an `anonymous proc<manual.html#procedures-anonymous-procs>`_
+## * import the `sugar module<sugar.html>`_  and use
+##   the `=> macro<sugar.html#%3D>.m,untyped,untyped>`_
 ## * use `...It templates<#18>`_
 ##   (`mapIt<#mapIt.t,typed,untyped>`_,
 ##   `filterIt<#filterIt.t,untyped,untyped>`_, etc.)
 ##
-## The chaining of functions is possible thanks to the
+## Chaining of functions is possible thanks to the
 ## `method call syntax<manual.html#procedures-method-call-syntax>`_.
-##
-## .. code-block::
-##   import sequtils, sugar
-##
-##   # Creating a sequence from 1 to 10, multiplying each member by 2,
-##   # keeping only the members which are not divisible by 6.
-##   let
-##     foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0)
-##     bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0)
-##
-##   doAssert foo == bar
-##   echo foo                  # @[2, 4, 8, 10, 14, 16, 20]
-##
-##   echo foo.any(x => x > 17) # true
-##   echo bar.allIt(it < 20)   # false
-##   echo foo.foldl(a + b)     # 74; sum of all members
-##
-## .. code-block::
-##   import sequtils
-##   from strutils import join
-##
-##   let
-##     vowels = @"aeiou" # creates a sequence @['a', 'e', 'i', 'o', 'u']
-##     foo = "sequtils is an awesome module"
-##
-##   echo foo.filterIt(it notin vowels).join # "sqtls s n wsm mdl"
-##
-## ----
-##
-## **See also**:
+
+runnableExamples:
+  import std/sugar
+
+  # Creating a sequence from 1 to 10, multiplying each member by 2,
+  # keeping only the members which are not divisible by 6.
+  let
+    foo = toSeq(1..10).map(x => x * 2).filter(x => x mod 6 != 0)
+    bar = toSeq(1..10).mapIt(it * 2).filterIt(it mod 6 != 0)
+    baz = collect:
+      for i in 1..10:
+        let j = 2 * i
+        if j mod 6 != 0:
+          j
+
+  doAssert foo == bar
+  doAssert foo == baz
+  doAssert foo == @[2, 4, 8, 10, 14, 16, 20]
+
+  doAssert foo.any(x => x > 17)
+  doAssert not bar.allIt(it < 20)
+  doAssert foo.foldl(a + b) == 74 # sum of all members
+
+
+runnableExamples:
+  from std/strutils import join
+
+  let
+    vowels = @"aeiou"
+    foo = "sequtils is an awesome module"
+
+  doAssert (vowels is seq[char]) and (vowels == @['a', 'e', 'i', 'o', 'u'])
+  doAssert foo.filterIt(it notin vowels).join == "sqtls s n wsm mdl"
+
+## See also
+## ========
 ## * `strutils module<strutils.html>`_ for common string functions
 ## * `sugar module<sugar.html>`_ for syntactic sugar macros
 ## * `algorithm module<algorithm.html>`_ for common generic algorithms
@@ -75,20 +82,26 @@
 
 import std/private/since
 
-import macros
+import std/macros
+from std/typetraits import supportsCopyMem
 
-when not defined(nimhygiene):
-  {.pragma: dirty.}
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
+
 macro evalOnceAs(expAlias, exp: untyped,
                  letAssigneable: static[bool]): untyped =
-  ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple
-  ##  substitution in macro arguments such as
-  ## https://github.com/nim-lang/Nim/issues/7187
-  ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp``
-  ## except when ``letAssigneable`` is false (e.g. to handle openArray) where
-  ## it just forwards ``exp`` unchanged
+  ## Injects `expAlias` in caller scope, to avoid bugs involving multiple
+  ## substitution in macro arguments such as
+  ## https://github.com/nim-lang/Nim/issues/7187.
+  ## `evalOnceAs(myAlias, myExp)` will behave as `let myAlias = myExp`
+  ## except when `letAssigneable` is false (e.g. to handle openArray) where
+  ## it just forwards `exp` unchanged.
   expectKind(expAlias, nnkIdent)
   var val = exp
 
@@ -103,12 +116,12 @@ macro evalOnceAs(expAlias, exp: untyped,
     newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
       body = val, procType = nnkTemplateDef))
 
-proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
+func concat*[T](seqs: varargs[seq[T]]): seq[T] =
   ## Takes several sequences' items and returns them inside a new sequence.
   ## All sequences must be of the same type.
   ##
-  ## See also:
-  ## * `distribute proc<#distribute,seq[T],Positive>`_ for a reverse
+  ## **See also:**
+  ## * `distribute func<#distribute,seq[T],Positive>`_ for a reverse
   ##   operation
   ##
   runnableExamples:
@@ -128,7 +141,23 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
       result[i] = itm
       inc(i)
 
-proc count*[T](s: openArray[T], x: T): int =
+func addUnique*[T](s: var seq[T], x: sink T) =
+  ## Adds `x` to the container `s` if it is not already present. 
+  ## Uses `==` to check if the item is already present.
+  runnableExamples:
+    var a = @[1, 2, 3]
+    a.addUnique(4)
+    a.addUnique(4)
+    assert a == @[1, 2, 3, 4]
+
+  for i in 0..high(s):
+    if s[i] == x: return
+  when declared(ensureMove):
+    s.add ensureMove(x)
+  else:
+    s.add x
+
+func count*[T](s: openArray[T], x: T): int =
   ## Returns the number of occurrences of the item `x` in the container `s`.
   ##
   runnableExamples:
@@ -143,7 +172,7 @@ proc count*[T](s: openArray[T], x: T): int =
     if itm == x:
       inc result
 
-proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
+func cycle*[T](s: openArray[T], n: Natural): seq[T] =
   ## Returns a new sequence with the items of the container `s` repeated
   ## `n` times.
   ## `n` must be a non-negative number (zero or more).
@@ -174,10 +203,10 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
   for i in 0 ..< n:
     result[i] = x
 
-proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
+func deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
   ## Returns a new sequence without duplicates.
   ##
-  ## Setting the optional argument ``isSorted`` to ``true`` (default: false)
+  ## Setting the optional argument `isSorted` to true (default: false)
   ## uses a faster algorithm for deduplication.
   ##
   runnableExamples:
@@ -202,9 +231,9 @@ proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
       for itm in items(s):
         if not result.contains(itm): result.add(itm)
 
-proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
+func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
   ## Returns the index of the minimum value of `s`.
-  ## ``T`` needs to have a ``<`` operator.
+  ## `T` needs to have a `<` operator.
   runnableExamples:
     let
       a = @[1, 2, 3, 4]
@@ -219,9 +248,9 @@ proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
   for i in 1..high(s):
     if s[i] < s[result]: result = i
 
-proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
+func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
   ## Returns the index of the maximum value of `s`.
-  ## ``T`` needs to have a ``<`` operator.
+  ## `T` needs to have a `<` operator.
   runnableExamples:
     let
       a = @[1, 2, 3, 4]
@@ -236,6 +265,15 @@ proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
   for i in 1..high(s):
     if s[i] > s[result]: result = i
 
+func minmax*[T](x: openArray[T]): (T, T) =
+  ## The minimum and maximum values of `x`. `T` needs to have a `<` operator.
+  var l = x[0]
+  var h = x[0]
+  for i in 1..high(x):
+    if x[i] < l: l = x[i]
+    if h < x[i]: h = x[i]
+  result = (l, h)
+
 
 template zipImpl(s1, s2, retType: untyped): untyped =
   proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType =
@@ -245,9 +283,9 @@ template zipImpl(s1, s2, retType: untyped): untyped =
     ## If one container is shorter, the remaining items in the longer container
     ## are discarded.
     ##
-    ## **Note**: For Nim 1.0.x and older version, ``zip`` returned a seq of
-    ## named tuple with fields ``a`` and ``b``. For Nim versions 1.1.x and newer,
-    ## ``zip`` returns a seq of unnamed tuples.
+    ## **Note**: For Nim 1.0.x and older version, `zip` returned a seq of
+    ## named tuples with fields `a` and `b`. For Nim versions 1.1.x and newer,
+    ## `zip` returns a seq of unnamed tuples.
     runnableExamples:
       let
         short = @[1, 2, 3]
@@ -290,25 +328,24 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} =
       unzipped2 = @['a', 'b', 'c']
     assert zipped.unzip() == (unzipped1, unzipped2)
     assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2)
-  result[0] = newSeq[S](s.len)
-  result[1] = newSeq[T](s.len)
+  result = (newSeq[S](s.len), newSeq[T](s.len))
   for i in 0..<s.len:
     result[0][i] = s[i][0]
     result[1][i] = s[i][1]
 
-proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   ## Splits and distributes a sequence `s` into `num` sub-sequences.
   ##
   ## Returns a sequence of `num` sequences. For *some* input values this is the
-  ## inverse of the `concat <#concat,varargs[seq[T]]>`_ proc.
+  ## inverse of the `concat <#concat,varargs[seq[T]]>`_ func.
   ## The input sequence `s` can be empty, which will produce
   ## `num` empty sequences.
   ##
   ## If `spread` is false and the length of `s` is not a multiple of `num`, the
-  ## proc will max out the first sub-sequence with ``1 + len(s) div num``
+  ## func will max out the first sub-sequence with `1 + len(s) div num`
   ## entries, leaving the remainder of elements to the last sequence.
   ##
-  ## On the other hand, if `spread` is true, the proc will distribute evenly
+  ## On the other hand, if `spread` is true, the func will distribute evenly
   ## the remainder of the division across all sequences, which makes the result
   ## more suited to multithreading where you are passing equal sized work units
   ## to a thread pool and want to maximize core usage.
@@ -323,7 +360,6 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   if num < 2:
     result = @[s]
     return
-  let num = int(num) # XXX probably only needed because of .. bug
 
   # Create the result and calculate the stride size and the remainder if any.
   result = newSeq[seq[T]](num)
@@ -354,14 +390,18 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
       first = last
 
 proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}):
-                                                            seq[S]{.inline.} =
-  ## Returns a new sequence with the results of `op` proc applied to every
+                                                            seq[S] {.inline, effectsOf: op.} =
+  ## Returns a new sequence with the results of the `op` proc applied to every
   ## item in the container `s`.
   ##
-  ## Since the input is not modified you can use it to
+  ## Since the input is not modified, you can use it to
   ## transform the type of the elements in the input container.
   ##
-  ## See also:
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
+  ##
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
   ## * `mapIt template<#mapIt.t,typed,untyped>`_
   ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version
   ##
@@ -376,15 +416,14 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}):
     result[i] = op(s[i])
 
 proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
-                                                              {.inline.} =
-  ## Applies `op` to every item in `s` modifying it directly.
+                                                              {.inline, effectsOf: op.} =
+  ## Applies `op` to every item in `s`, modifying it directly.
   ##
-  ## Note that container `s` must be declared as a ``var``
-  ## and it is required for your input and output types to
-  ## be the same, since `s` is modified in-place.
-  ## The parameter function takes a ``var T`` type parameter.
+  ## Note that the container `s` must be declared as a `var`,
+  ## since `s` is modified in-place.
+  ## The parameter function takes a `var T` type parameter.
   ##
-  ## See also:
+  ## **See also:**
   ## * `applyIt template<#applyIt.t,untyped,untyped>`_
   ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
@@ -396,15 +435,15 @@ proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
   for i in 0 ..< s.len: op(s[i])
 
 proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
-                                                              {.inline.} =
+                                                              {.inline, effectsOf: op.} =
   ## Applies `op` to every item in `s` modifying it directly.
   ##
-  ## Note that container `s` must be declared as a ``var``
+  ## Note that the container `s` must be declared as a `var`
   ## and it is required for your input and output types to
   ## be the same, since `s` is modified in-place.
-  ## The parameter function takes and returns a ``T`` type variable.
+  ## The parameter function takes and returns a `T` type variable.
   ##
-  ## See also:
+  ## **See also:**
   ## * `applyIt template<#applyIt.t,untyped,untyped>`_
   ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
@@ -415,12 +454,25 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
 
   for i in 0 ..< s.len: s[i] = op(s[i])
 
-iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
+proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3), effectsOf: op.} =
+  ## Same as `apply` but for a proc that does not return anything
+  ## and does not mutate `s` directly.
+  runnableExamples:
+    var message: string
+    apply([0, 1, 2, 3, 4], proc(item: int) = message.addInt item)
+    assert message == "01234"
+  for i in 0 ..< s.len: op(s[i])
+
+iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T {.effectsOf: pred.} =
   ## Iterates through a container `s` and yields every item that fulfills the
-  ## predicate `pred` (function that returns a `bool`).
+  ## predicate `pred` (a function that returns a `bool`).
+  ##
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
   ##
-  ## See also:
-  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
   ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
   runnableExamples:
@@ -435,11 +487,15 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
       yield s[i]
 
 proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
-                                                                  {.inline.} =
-  ## Returns a new sequence with all the items of `s` that fulfilled the
-  ## predicate `pred` (function that returns a `bool`).
+                                                                  {.inline, effectsOf: pred.} =
+  ## Returns a new sequence with all the items of `s` that fulfill the
+  ## predicate `pred` (a function that returns a `bool`).
+  ##
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
   ##
-  ## See also:
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
   ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
   ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version
@@ -458,16 +514,16 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
       result.add(s[i])
 
 proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
-                                                                {.inline.} =
-  ## Keeps the items in the passed sequence `s` if they fulfilled the
-  ## predicate `pred` (function that returns a `bool`).
+                                                                {.inline, effectsOf: pred.} =
+  ## Keeps the items in the passed sequence `s` if they fulfill the
+  ## predicate `pred` (a function that returns a `bool`).
   ##
-  ## Note that `s` must be declared as a ``var``.
+  ## Note that `s` must be declared as a `var`.
   ##
   ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_,
   ## but modifies the sequence directly.
   ##
-  ## See also:
+  ## **See also:**
   ## * `keepItIf template<#keepItIf.t,seq,untyped>`_
   ## * `filter proc<#filter,openArray[T],proc(T)>`_
   ##
@@ -487,12 +543,51 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
       inc(pos)
   setLen(s, pos)
 
-proc delete*[T](s: var seq[T]; first, last: Natural) =
-  ## Deletes in the items of a sequence `s` at positions ``first..last``
-  ## (including both ends of a range).
-  ## This modifies `s` itself, it does not return a copy.
+func delete*[T](s: var seq[T]; slice: Slice[int]) =
+  ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains
+  ## elements out of range.
   ##
+  ## This operation moves all elements after `s[slice]` in linear time.
   runnableExamples:
+    var a = @[10, 11, 12, 13, 14]
+    doAssertRaises(IndexDefect): a.delete(4..5)
+    assert a == @[10, 11, 12, 13, 14]
+    a.delete(4..4)
+    assert a == @[10, 11, 12, 13]
+    a.delete(1..2)
+    assert a == @[10, 13]
+    a.delete(1..<1) # empty slice
+    assert a == @[10, 13]
+  when compileOption("boundChecks"):
+    if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len):
+      raise newException(IndexDefect, $(slice: slice, len: s.len))
+  if slice.b >= slice.a:
+    template defaultImpl =
+      var i = slice.a
+      var j = slice.b + 1
+      var newLen = s.len - j + i
+      while i < newLen:
+        when defined(gcDestructors):
+          s[i] = move(s[j])
+        else:
+          s[i].shallowCopy(s[j])
+        inc(i)
+        inc(j)
+      setLen(s, newLen)
+    when nimvm: defaultImpl()
+    else:
+      when defined(js):
+        let n = slice.b - slice.a + 1
+        let first = slice.a
+        {.emit: "`s`.splice(`first`, `n`);".}
+      else:
+        defaultImpl()
+
+func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(s, first..last)`".} =
+  ## Deletes the items of a sequence `s` at positions `first..last`
+  ## (including both ends of the range).
+  ## This modifies `s` itself, it does not return a copy.
+  runnableExamples("--warning:deprecated:off"):
     let outcome = @[1, 1, 1, 1, 1, 1, 1, 1]
     var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1]
     dest.delete(3, 8)
@@ -501,8 +596,8 @@ proc delete*[T](s: var seq[T]; first, last: Natural) =
   if first >= s.len:
     return
   var i = first
-  var j = min(len(s), last+1)
-  var newLen = len(s)-j+i
+  var j = min(len(s), last + 1)
+  var newLen = len(s) - j + i
   while i < newLen:
     when defined(gcDestructors):
       s[i] = move(s[j])
@@ -512,11 +607,11 @@ proc delete*[T](s: var seq[T]; first, last: Natural) =
     inc(j)
   setLen(s, newLen)
 
-proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
+func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
   ## Inserts items from `src` into `dest` at position `pos`. This modifies
   ## `dest` itself, it does not return a copy.
   ##
-  ## Notice that `src` and `dest` must be of the same type.
+  ## Note that the elements of `src` and `dest` must be of the same type.
   ##
   runnableExamples:
     var dest = @[1, 1, 1, 1, 1, 1, 1, 1]
@@ -527,7 +622,8 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
     assert dest == outcome
 
   var j = len(dest) - 1
-  var i = len(dest) + len(src) - 1
+  var i = j + len(src)
+  if i == j: return
   dest.setLen(i + 1)
 
   # Move items after `pos` to the end of the sequence.
@@ -546,16 +642,20 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
 
 
 template filterIt*(s, pred: untyped): untyped =
-  ## Returns a new sequence with all the items of `s` that fulfilled the
+  ## Returns a new sequence with all the items of `s` that fulfill the
   ## predicate `pred`.
   ##
   ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and
   ## `filter iterator<#filter.i,openArray[T],proc(T)>`_,
-  ## the predicate needs to be an expression using the ``it`` variable
-  ## for testing, like: ``filterIt("abcxyz", it == 'x')``.
+  ## the predicate needs to be an expression using the `it` variable
+  ## for testing, like: `filterIt("abcxyz", it == 'x')`.
   ##
-  ## See also:
-  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro
+  ## from the `sugar` module.
+  ##
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
   ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
   ##
   runnableExamples:
@@ -572,14 +672,14 @@ template filterIt*(s, pred: untyped): untyped =
   result
 
 template keepItIf*(varSeq: seq, pred: untyped) =
-  ## Keeps the items in the passed sequence (must be declared as a ``var``)
-  ## if they fulfilled the predicate.
+  ## Keeps the items in the passed sequence (must be declared as a `var`)
+  ## if they fulfill the predicate.
   ##
   ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_,
   ## the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
+  ## the `it` variable for testing, like: `keepItIf("abcxyz", it == 'x')`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_
   ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
@@ -602,10 +702,10 @@ template keepItIf*(varSeq: seq, pred: untyped) =
 
 since (1, 1):
   template countIt*(s, pred: untyped): int =
-    ## Returns a count of all the items that fulfilled the predicate.
+    ## Returns a count of all the items that fulfill the predicate.
     ##
     ## The predicate needs to be an expression using
-    ## the ``it`` variable for testing, like: ``countIt(@[1, 2, 3], it > 2)``.
+    ## the `it` variable for testing, like: `countIt(@[1, 2, 3], it > 2)`.
     ##
     runnableExamples:
       let numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
@@ -619,23 +719,23 @@ since (1, 1):
       if pred: result += 1
     result
 
-proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
+proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} =
   ## Iterates through a container and checks if every item fulfills the
   ## predicate.
   ##
-  ## See also:
+  ## **See also:**
   ## * `allIt template<#allIt.t,untyped,untyped>`_
   ## * `any proc<#any,openArray[T],proc(T)>`_
   ##
   runnableExamples:
     let numbers = @[1, 4, 5, 8, 9, 7, 4]
-    assert all(numbers, proc (x: int): bool = return x < 10) == true
-    assert all(numbers, proc (x: int): bool = return x < 9) == false
+    assert all(numbers, proc (x: int): bool = x < 10) == true
+    assert all(numbers, proc (x: int): bool = x < 9) == false
 
   for i in s:
     if not pred(i):
       return false
-  return true
+  true
 
 template allIt*(s, pred: untyped): bool =
   ## Iterates through a container and checks if every item fulfills the
@@ -643,9 +743,9 @@ template allIt*(s, pred: untyped): bool =
   ##
   ## Unlike the `all proc<#all,openArray[T],proc(T)>`_,
   ## the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``allIt("abba", it == 'a')``.
+  ## the `it` variable for testing, like: `allIt("abba", it == 'a')`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `all proc<#all,openArray[T],proc(T)>`_
   ## * `anyIt template<#anyIt.t,untyped,untyped>`_
   ##
@@ -661,33 +761,33 @@ template allIt*(s, pred: untyped): bool =
       break
   result
 
-proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
-  ## Iterates through a container and checks if some item fulfills the
-  ## predicate.
+proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} =
+  ## Iterates through a container and checks if at least one item
+  ## fulfills the predicate.
   ##
-  ## See also:
+  ## **See also:**
   ## * `anyIt template<#anyIt.t,untyped,untyped>`_
   ## * `all proc<#all,openArray[T],proc(T)>`_
   ##
   runnableExamples:
     let numbers = @[1, 4, 5, 8, 9, 7, 4]
-    assert any(numbers, proc (x: int): bool = return x > 8) == true
-    assert any(numbers, proc (x: int): bool = return x > 9) == false
+    assert any(numbers, proc (x: int): bool = x > 8) == true
+    assert any(numbers, proc (x: int): bool = x > 9) == false
 
   for i in s:
     if pred(i):
       return true
-  return false
+  false
 
 template anyIt*(s, pred: untyped): bool =
-  ## Iterates through a container and checks if some item fulfills the
-  ## predicate.
+  ## Iterates through a container and checks if at least one item
+  ## fulfills the predicate.
   ##
   ## Unlike the `any proc<#any,openArray[T],proc(T)>`_,
   ## the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``anyIt("abba", it == 'a')``.
+  ## the `it` variable for testing, like: `anyIt("abba", it == 'a')`.
   ##
-  ## See also:
+  ## **See also:**
   ## * `any proc<#any,openArray[T],proc(T)>`_
   ## * `allIt template<#allIt.t,untyped,untyped>`_
   ##
@@ -716,7 +816,7 @@ template toSeq1(s: not iterator): untyped =
         i += 1
       result
   else:
-    var result: seq[OutType] = @[]
+    var result: seq[OutType]# = @[]
     for it in s:
       result.add(it)
     result
@@ -733,7 +833,7 @@ template toSeq2(iter: iterator): untyped =
     result
   else:
     type OutType = typeof(iter2())
-    var result: seq[OutType] = @[]
+    var result: seq[OutType]# = @[]
     when compiles(iter2()):
       evalOnceAs(iter4, iter, false)
       let iter3 = iter4()
@@ -788,15 +888,15 @@ template foldl*(sequence, operation: untyped): untyped =
   ## The sequence is required to have at least a single element. Debug versions
   ## of your program will assert in this situation but release versions will
   ## happily go ahead. If the sequence has a single element it will be returned
-  ## without applying ``operation``.
+  ## without applying `operation`.
   ##
-  ## The ``operation`` parameter should be an expression which uses the
-  ## variables ``a`` and ``b`` for each step of the fold. Since this is a left
+  ## The `operation` parameter should be an expression which uses the
+  ## variables `a` and `b` for each step of the fold. Since this is a left
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
   ## 3).
   ##
-  ## See also:
+  ## **See also:**
   ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
   ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
@@ -808,10 +908,17 @@ template foldl*(sequence, operation: untyped): untyped =
       multiplication = foldl(numbers, a * b)
       words = @["nim", "is", "cool"]
       concatenation = foldl(words, a & b)
+      procs = @["proc", "Is", "Also", "Fine"]
+
+
+    func foo(acc, cur: string): string =
+      result = acc & cur
+
     assert addition == 25, "Addition is (((5)+9)+11)"
     assert subtraction == -15, "Subtraction is (((5)-9)-11)"
     assert multiplication == 495, "Multiplication is (((5)*9)*11)"
     assert concatenation == "nimiscool"
+    assert foldl(procs, foo(a, b)) == "procIsAlsoFine"
 
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
@@ -827,14 +934,14 @@ template foldl*(sequence, operation: untyped): untyped =
 template foldl*(sequence, operation, first): untyped =
   ## Template to fold a sequence from left to right, returning the accumulation.
   ##
-  ## This version of ``foldl`` gets a **starting parameter**. This makes it possible
+  ## This version of `foldl` gets a **starting parameter**. This makes it possible
   ## to accumulate the sequence into a different type than the sequence elements.
   ##
-  ## The ``operation`` parameter should be an expression which uses the variables
-  ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the
-  ## start value (the first ``a``) and therefor defines the type of the result.
+  ## The `operation` parameter should be an expression which uses the variables
+  ## `a` and `b` for each step of the fold. The `first` parameter is the
+  ## start value (the first `a`) and therefore defines the type of the result.
   ##
-  ## See also:
+  ## **See also:**
   ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
   runnableExamples:
@@ -857,15 +964,15 @@ template foldr*(sequence, operation: untyped): untyped =
   ## The sequence is required to have at least a single element. Debug versions
   ## of your program will assert in this situation but release versions will
   ## happily go ahead. If the sequence has a single element it will be returned
-  ## without applying ``operation``.
+  ## without applying `operation`.
   ##
-  ## The ``operation`` parameter should be an expression which uses the
-  ## variables ``a`` and ``b`` for each step of the fold. Since this is a right
+  ## The `operation` parameter should be an expression which uses the
+  ## variables `a` and `b` for each step of the fold. Since this is a right
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
   ## (3))).
   ##
-  ## See also:
+  ## **See also:**
   ## * `foldl template<#foldl.t,untyped,untyped>`_
   ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
   ##
@@ -894,16 +1001,20 @@ template foldr*(sequence, operation: untyped): untyped =
   result
 
 template mapIt*(s: typed, op: untyped): untyped =
-  ## Returns a new sequence with the results of `op` proc applied to every
+  ## Returns a new sequence with the results of the `op` proc applied to every
   ## item in the container `s`.
   ##
   ## Since the input is not modified you can use it to
   ## transform the type of the elements in the input container.
   ##
-  ## The template injects the ``it`` variable which you can use directly in an
+  ## The template injects the `it` variable which you can use directly in an
   ## expression.
   ##
-  ## See also:
+  ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro
+  ## from the `sugar` module.
+  ##
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
   ## * `map proc<#map,openArray[T],proc(T)>`_
   ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version
   ##
@@ -913,16 +1024,10 @@ template mapIt*(s: typed, op: untyped): untyped =
       strings = nums.mapIt($(4 * it))
     assert strings == @["4", "8", "12", "16"]
 
-  when defined(nimHasTypeof):
-    type OutType = typeof((
-      block:
-        var it{.inject.}: typeof(items(s), typeOfIter);
-        op), typeOfProc)
-  else:
-    type OutType = typeof((
-      block:
-        var it{.inject.}: typeof(items(s));
-        op))
+  type OutType = typeof((
+    block:
+      var it{.inject.}: typeof(items(s), typeOfIter);
+      op), typeOfProc)
   when OutType is not (proc):
     # Here, we avoid to create closures in loops.
     # This avoids https://github.com/nim-lang/Nim/issues/12625
@@ -940,7 +1045,7 @@ template mapIt*(s: typed, op: untyped): untyped =
           i += 1
         result
     else:
-      var result: seq[OutType] = @[]
+      var result: seq[OutType]# = @[]
       # use `items` to avoid https://github.com/nim-lang/Nim/issues/12639
       for it {.inject.} in items(s):
         result.add(op)
@@ -953,11 +1058,7 @@ template mapIt*(s: typed, op: untyped): untyped =
     # With this fallback, above code can be simplified to:
     #   [1, 2].mapIt((x: int) => it + x)
     # In this case, `mapIt` is just syntax sugar for `map`.
-
-    when defined(nimHasTypeof):
-      type InType = typeof(items(s), typeOfIter)
-    else:
-      type InType = typeof(items(s))
+    type InType = typeof(items(s), typeOfIter)
     # Use a help proc `f` to create closures for each element in `s`
     let f = proc (x: InType): OutType =
               let it {.inject.} = x
@@ -965,13 +1066,13 @@ template mapIt*(s: typed, op: untyped): untyped =
     map(s, f)
 
 template applyIt*(varSeq, op: untyped) =
-  ## Convenience template around the mutable ``apply`` proc to reduce typing.
+  ## Convenience template around the mutable `apply` proc to reduce typing.
   ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. The expression has to return the same type as the sequence you
-  ## are mutating.
+  ## The template injects the `it` variable which you can use directly in an
+  ## expression. The expression has to return the same type as the elements
+  ## of the sequence you are mutating.
   ##
-  ## See also:
+  ## **See also:**
   ## * `apply proc<#apply,openArray[T],proc(T)_2>`_
   ## * `mapIt template<#mapIt.t,typed,untyped>`_
   ##
@@ -986,29 +1087,33 @@ template applyIt*(varSeq, op: untyped) =
 
 
 template newSeqWith*(len: int, init: untyped): untyped =
-  ## Creates a new sequence of length `len`, calling `init` to initialize
-  ## each value of the sequence.
-  ##
-  ## Useful for creating "2D" sequences - sequences containing other sequences
-  ## or to populate fields of the created sequence.
+  ## Creates a new `seq` of length `len`, calling `init` to initialize
+  ## each value of the seq.
   ##
+  ## Useful for creating "2D" seqs - seqs containing other seqs
+  ## or to populate fields of the created seq.
   runnableExamples:
-    ## Creates a sequence containing 5 bool sequences, each of length of 3.
+    ## Creates a seq containing 5 bool seqs, each of length of 3.
     var seq2D = newSeqWith(5, newSeq[bool](3))
     assert seq2D.len == 5
     assert seq2D[0].len == 3
     assert seq2D[4][2] == false
 
-    ## Creates a sequence of 20 random numbers from 1 to 10
-    import random
-    var seqRand = newSeqWith(20, rand(10))
-
-  var result = newSeq[typeof(init)](len)
-  for i in 0 ..< len:
+    ## Creates a seq with random numbers
+    import std/random
+    var seqRand = newSeqWith(20, rand(1.0))
+    assert seqRand[0] != seqRand[1]
+  type T = typeof(init)
+  let newLen = len
+  when supportsCopyMem(T) and declared(newSeqUninit):
+    var result = newSeqUninit[T](newLen)
+  else: # TODO: use `newSeqUnsafe` when that's available
+    var result = newSeq[T](newLen)
+  for i in 0 ..< newLen:
     result[i] = init
-  result
+  move(result) # refs bug #7295
 
-proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
+func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
                  filter = nnkLiterals): NimNode =
   if constructor.kind in filter:
     result = newNimNode(nnkCall, lineInfoFrom = constructor)
@@ -1024,44 +1129,34 @@ proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
 
 macro mapLiterals*(constructor, op: untyped;
                    nested = true): untyped =
-  ## Applies ``op`` to each of the **atomic** literals like ``3``
-  ## or ``"abc"`` in the specified ``constructor`` AST. This can
+  ## Applies `op` to each of the **atomic** literals like `3`
+  ## or `"abc"` in the specified `constructor` AST. This can
   ## be used to map every array element to some target type:
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
-  ##   doAssert x is array[4, int]
-  ##
-  ## Short notation for:
-  ##
-  ## .. code-block::
-  ##   let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
-  ##
-  ## If ``nested`` is true (which is the default), the literals are replaced
-  ## everywhere in the ``constructor`` AST, otherwise only the first level
+  runnableExamples:
+    let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
+    doAssert x is array[4, int]
+    doAssert x == [int(0.1), int(1.2), int(2.3), int(3.4)]
+  ## If `nested` is true (which is the default), the literals are replaced
+  ## everywhere in the `constructor` AST, otherwise only the first level
   ## is considered:
-  ##
-  ## .. code-block::
-  ##   let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int)
-  ##   let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false)
-  ##   assert a == (1, (2, 3), 4)
-  ##   assert b == (1, (2.3, 3.4), 4)
-  ##
-  ##   let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`)
-  ##   let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false)
-  ##   assert c == ("1", ("2", "3"), "4", ("5", "6"))
-  ##   assert d == ("1", (2, 3), "4", (5, 6))
-  ##
-  ## There are no constraints for the ``constructor`` AST, it
+  runnableExamples:
+    let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int)
+    let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false)
+    assert a == (1, (2, 3), 4)
+    assert b == (1, (2.3, 3.4), 4)
+
+    let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`)
+    let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false)
+    assert c == ("1", ("2", "3"), "4", ("5", "6"))
+    assert d == ("1", (2, 3), "4", (5, 6))
+  ## There are no constraints for the `constructor` AST, it
   ## works for nested tuples of arrays of sets etc.
   result = mapLitsImpl(constructor, op, nested.boolVal)
 
 iterator items*[T](xs: iterator: T): T =
-  ## iterates over each element yielded by a closure iterator. This may
+  ## Iterates over each element yielded by a closure iterator. This may
   ## not seem particularly useful on its own, but this allows closure
-  ## iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc.
+  ## iterators to be used by the mapIt, filterIt, allIt, anyIt, etc.
   ## templates.
   for x in xs():
     yield x
diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim
index d798cbcb3..360a075d6 100644
--- a/lib/pure/collections/setimpl.nim
+++ b/lib/pure/collections/setimpl.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-# An ``include`` file for the different hash set implementations.
+# An `include` file for the different hash set implementations.
 
 
 template maxHash(t): untyped = high(t.data)
@@ -16,12 +16,12 @@ template dataLen(t): untyped = len(t.data)
 include hashcommon
 
 template initImpl(s: typed, size: int) =
-  assert isPowerOfTwo(size)
+  let correctSize = slotsNeeded(size)
   when s is OrderedSet:
     s.first = -1
     s.last = -1
   s.counter = 0
-  newSeq(s.data, size)
+  newSeq(s.data, correctSize)
 
 template rawInsertImpl() {.dirty.} =
   if data.len == 0:
@@ -62,6 +62,7 @@ template containsOrInclImpl() {.dirty.} =
   if index >= 0:
     result = true
   else:
+    result = false
     if mustRehash(s):
       enlarge(s)
       index = rawGetKnownHC(s, key, hc)
@@ -86,7 +87,9 @@ proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} =
       var j = i # The correctness of this depends on (h+1) in nextTry,
       var r = j # though may be adaptable to other simple sequences.
       s.data[i].hcode = 0 # mark current EMPTY
-      s.data[i].key = default(type(s.data[i].key))
+      {.push warning[UnsafeDefault]:off.}
+      reset(s.data[i].key)
+      {.pop.}
       doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
         i = (i + 1) and msk # increment mod table size
         if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 2b270c2cb..af13135aa 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``sets`` module implements an efficient `hash set`:idx: and
+## The `sets` module implements an efficient `hash set`:idx: and
 ## ordered hash set.
 ##
 ## Hash sets are different from the `built in set type
@@ -16,7 +16,7 @@
 ##
 ## Common usages of sets:
 ## * removing duplicates from a container by converting it with `toHashSet proc
-##   <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate proc
+##   <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate func
 ##   <sequtils.html#deduplicate,openArray[T],bool>`_)
 ## * membership testing
 ## * mathematical operations on two sets, such as
@@ -25,7 +25,9 @@
 ##   `difference <#difference,HashSet[A],HashSet[A]>`_, and
 ##   `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
 ##
-## .. code-block::
+## **Examples:**
+##
+##   ```Nim
 ##   echo toHashSet([9, 5, 1])     # {9, 1, 5}
 ##   echo toOrderedSet([9, 5, 1])  # {9, 5, 1}
 ##
@@ -37,10 +39,10 @@
 ##   echo s1 - s2    # {1, 9}
 ##   echo s1 * s2    # {5}
 ##   echo s1 -+- s2  # {9, 1, 3, 7}
-##
+##   ```
 ##
 ## Note: The data types declared here have *value semantics*: This means
-## that ``=`` performs a copy of the set.
+## that `=` performs a copy of the set.
 ##
 ## **See also:**
 ## * `intsets module <intsets.html>`_ for efficient int sets
@@ -48,12 +50,12 @@
 
 
 import
-  hashes, math
+  std/[hashes, math]
 
-{.pragma: myShallow.}
-when not defined(nimhygiene):
-  {.pragma: dirty.}
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
+{.pragma: myShallow.}
 # For "integer-like A" that are too big for intsets/bit-vectors to be practical,
 # it would be best to shrink hcode to the same size as the integer.  Larger
 # codes should never be needed, and this can pack more entries per cache-line.
@@ -64,7 +66,7 @@ type
   HashSet*[A] {.myShallow.} = object ## \
     ## A generic hash set.
     ##
-    ## Use `init proc <#init,HashSet[A],int>`_ or `initHashSet proc <#initHashSet,int>`_
+    ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_
     ## before calling other procs on it.
     data: KeyValuePairSeq[A]
     counter: int
@@ -76,10 +78,12 @@ type
   OrderedSet*[A] {.myShallow.} = object ## \
     ## A generic hash set that remembers insertion order.
     ##
-    ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
-    ## <#initOrderedSet,int>`_ before calling other procs on it.
+    ## Use `init proc <#init,OrderedSet[A]>`_ or `initOrderedSet proc
+    ## <#initOrderedSet>`_ before calling other procs on it.
     data: OrderedKeyValuePairSeq[A]
     counter, first, last: int
+  SomeSet*[A] = HashSet[A] | OrderedSet[A]
+    ## Type union representing `HashSet` or `OrderedSet`.
 
 const
   defaultInitialSize* = 64
@@ -94,11 +98,6 @@ include setimpl
 proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
   ## Initializes a hash set.
   ##
-  ## The `initialSize` parameter needs to be a power of two (default: 64).
-  ## If you need to accept runtime values for this, you can use
-  ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or
-  ## `rightSize proc <#rightSize,Natural>`_ from this module.
-  ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -107,7 +106,7 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
   ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
   ##
   ## See also:
-  ## * `initHashSet proc <#initHashSet,int>`_
+  ## * `initHashSet proc <#initHashSet>`_
   ## * `toHashSet proc <#toHashSet,openArray[A]>`_
   runnableExamples:
     var a: HashSet[int]
@@ -116,10 +115,10 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
   initImpl(s, initialSize)
 
 proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
-  ## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of
+  ## Wrapper around `init proc <#init,HashSet[A]>`_ for initialization of
   ## hash sets.
   ##
-  ## Returns an empty hash set you can assign directly in ``var`` blocks in a
+  ## Returns an empty hash set you can assign directly in `var` blocks in a
   ## single line.
   ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
@@ -131,12 +130,12 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
     var a = initHashSet[int]()
     a.incl(3)
     assert len(a) == 1
-
+  result = default(HashSet[A])
   result.init(initialSize)
 
 proc `[]`*[A](s: var HashSet[A], key: A): var A =
   ## Returns the element that is actually stored in `s` which has the same
-  ## value as `key` or raises the ``KeyError`` exception.
+  ## value as `key` or raises the `KeyError` exception.
   ##
   ## This is useful when one overloaded `hash` and `==` but still needs
   ## reference semantics for sharing.
@@ -170,6 +169,27 @@ proc contains*[A](s: HashSet[A], key: A): bool =
   var index = rawGet(s, key, hc)
   result = index >= 0
 
+proc len*[A](s: HashSet[A]): int =
+  ## Returns the number of elements in `s`.
+  ##
+  ## Due to an implementation detail you can call this proc on variables which
+  ## have not been initialized yet. The proc will return zero as the length
+  ## then.
+  runnableExamples:
+    var a: HashSet[string]
+    assert len(a) == 0
+    let s = toHashSet([3, 5, 7])
+    assert len(s) == 3
+
+  result = s.counter
+
+proc card*[A](s: HashSet[A]): int =
+  ## Alias for `len() <#len,HashSet[A]>`_.
+  ##
+  ## Card stands for the `cardinality
+  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
+  result = s.counter
+
 proc incl*[A](s: var HashSet[A], key: A) =
   ## Includes an element `key` in `s`.
   ##
@@ -212,7 +232,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
   ## Duplicates are removed.
   ##
   ## See also:
-  ## * `initHashSet proc <#initHashSet,int>`_
+  ## * `initHashSet proc <#initHashSet>`_
   runnableExamples:
     let
       a = toHashSet([5, 3, 2])
@@ -222,7 +242,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
     assert len(b) == 5
     ## b == {'a', 'b', 'c', 'd', 'r'}
 
-  result = initHashSet[A](rightSize(keys.len))
+  result = initHashSet[A](keys.len)
   for key in items(keys): result.incl(key)
 
 iterator items*[A](s: HashSet[A]): A =
@@ -231,7 +251,7 @@ iterator items*[A](s: HashSet[A]): A =
   ## If you need a sequence with the elements you can use `sequtils.toSeq
   ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   type
   ##     pair = tuple[a, b: int]
   ##   var
@@ -244,8 +264,12 @@ iterator items*[A](s: HashSet[A]): A =
   ##   assert a.len == 2
   ##   echo b
   ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
+  ##   ```
+  let length = s.len
   for h in 0 .. high(s.data):
-    if isFilled(s.data[h].hcode): yield s.data[h].key
+    if isFilled(s.data[h].hcode):
+      yield s.data[h].key
+      assert(len(s) == length, "the length of the HashSet changed while iterating over it")
 
 proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
   ## Includes `key` in the set `s` and tells if `key` was already in `s`.
@@ -324,9 +348,9 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
   exclImpl(s, key)
 
 proc pop*[A](s: var HashSet[A]): A =
-  ## Remove and return an arbitrary element from the set `s`.
+  ## Removes and returns an arbitrary element from the set `s`.
   ##
-  ## Raises KeyError if the set `s` is empty.
+  ## Raises `KeyError` if the set `s` is empty.
   ##
   ## See also:
   ## * `clear proc <#clear,HashSet[A]>`_
@@ -358,28 +382,9 @@ proc clear*[A](s: var HashSet[A]) =
   s.counter = 0
   for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
-    s.data[i].key = default(type(s.data[i].key))
-
-proc len*[A](s: HashSet[A]): int =
-  ## Returns the number of elements in `s`.
-  ##
-  ## Due to an implementation detail you can call this proc on variables which
-  ## have not been initialized yet. The proc will return zero as the length
-  ## then.
-  runnableExamples:
-    var a: HashSet[string]
-    assert len(a) == 0
-    let s = toHashSet([3, 5, 7])
-    assert len(s) == 3
-
-  result = s.counter
-
-proc card*[A](s: HashSet[A]): int =
-  ## Alias for `len() <#len,HashSet[A]>`_.
-  ##
-  ## Card stands for the `cardinality
-  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
-  result = s.counter
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
 
 proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
@@ -425,8 +430,15 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
     assert c == toHashSet(["b"])
 
   result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
-  for item in s1:
-    if item in s2: incl(result, item)
+
+  # iterate over the elements of the smaller set
+  if s1.data.len < s2.data.len:
+    for item in s1:
+      if item in s2: incl(result, item)
+  else:
+    for item in s2:
+      if item in s1: incl(result, item)
+
 
 proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the difference of the sets `s1` and `s2`.
@@ -552,7 +564,7 @@ proc `==`*[A](s, t: HashSet[A]): bool =
 
   s.counter == t.counter and s <= t
 
-proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
+proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] {.effectsOf: op.} =
   ## Returns a new set after applying `op` proc on each of the elements of
   ##`data` set.
   ##
@@ -579,12 +591,12 @@ proc `$`*[A](s: HashSet[A]): string =
   ## any moment and values are not escaped.
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   echo toHashSet([2, 4, 5])
   ##   # --> {2, 4, 5}
   ##   echo toHashSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
+  ##   ```
   dollarImpl()
 
 
@@ -597,14 +609,12 @@ proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
 proc isValid*[A](s: HashSet[A]): bool {.deprecated:
      "Deprecated since v0.20; sets are initialized by default".} =
   ## Returns `true` if the set has been initialized (with `initHashSet proc
-  ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
+  ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_).
   ##
-  ## **Examples:**
-  ##
-  ## .. code-block ::
-  ##   proc savePreferences(options: HashSet[string]) =
-  ##     assert options.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
+  runnableExamples:
+    proc savePreferences(options: HashSet[string]) =
+      assert options.isValid, "Pass an initialized set!"
+      # Do stuff here, may crash in release builds!
   result = s.data.len > 0
 
 
@@ -628,11 +638,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
 proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
   ## Initializes an ordered hash set.
   ##
-  ## The `initialSize` parameter needs to be a power of two (default: 64).
-  ## If you need to accept runtime values for this, you can use
-  ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or
-  ## `rightSize proc <#rightSize,Natural>`_ from this module.
-  ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -641,7 +646,7 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
   ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
   ##
   ## See also:
-  ## * `initOrderedSet proc <#initOrderedSet,int>`_
+  ## * `initOrderedSet proc <#initOrderedSet>`_
   ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
   runnableExamples:
     var a: OrderedSet[int]
@@ -650,10 +655,10 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
   initImpl(s, initialSize)
 
 proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
-  ## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of
+  ## Wrapper around `init proc <#init,OrderedSet[A]>`_ for initialization of
   ## ordered hash sets.
   ##
-  ## Returns an empty ordered hash set you can assign directly in ``var`` blocks
+  ## Returns an empty ordered hash set you can assign directly in `var` blocks
   ## in a single line.
   ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
@@ -675,7 +680,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
   ## Duplicates are removed.
   ##
   ## See also:
-  ## * `initOrderedSet proc <#initOrderedSet,int>`_
+  ## * `initOrderedSet proc <#initOrderedSet>`_
   runnableExamples:
     let
       a = toOrderedSet([5, 3, 2])
@@ -685,7 +690,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
     assert len(b) == 5
     ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet
 
-  result = initOrderedSet[A](rightSize(keys.len))
+  result = initOrderedSet[A](keys.len)
   for key in items(keys): result.incl(key)
 
 proc contains*[A](s: OrderedSet[A], key: A): bool =
@@ -813,7 +818,9 @@ proc clear*[A](s: var OrderedSet[A]) =
   for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
     s.data[i].next = 0
-    s.data[i].key = default(type(s.data[i].key))
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
 proc len*[A](s: OrderedSet[A]): int {.inline.} =
   ## Returns the number of elements in `s`.
@@ -874,12 +881,12 @@ proc `$`*[A](s: OrderedSet[A]): string =
   ## any moment and values are not escaped.
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   echo toOrderedSet([2, 4, 5])
   ##   # --> {2, 4, 5}
   ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
+  ##   ```
   dollarImpl()
 
 
@@ -890,7 +897,7 @@ iterator items*[A](s: OrderedSet[A]): A =
   ## If you need a sequence with the elements you can use `sequtils.toSeq
   ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var a = initOrderedSet[int]()
   ##   for value in [9, 2, 1, 5, 1, 8, 4, 2]:
   ##     a.incl(value)
@@ -902,8 +909,11 @@ iterator items*[A](s: OrderedSet[A]): A =
   ##   # --> Got 5
   ##   # --> Got 8
   ##   # --> Got 4
+  ##   ```
+  let length = s.len
   forAllOrderedPairs:
     yield s.data[h].key
+    assert(len(s) == length, "the length of the OrderedSet changed while iterating over it")
 
 iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
   ## Iterates through (position, value) tuples of OrderedSet `s`.
@@ -914,289 +924,7 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
       p.add(x)
     assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
 
+  let length = s.len
   forAllOrderedPairs:
     yield (idx, s.data[h].key)
-
-
-
-# -----------------------------------------------------------------------
-
-
-
-when isMainModule and not defined(release):
-  proc testModule() =
-    ## Internal micro test to validate docstrings and such.
-    block lenTest:
-      var values: HashSet[int]
-      assert values.len == 0
-      assert values.card == 0
-
-    block setIterator:
-      type pair = tuple[a, b: int]
-      var a, b = initHashSet[pair]()
-      a.incl((2, 3))
-      a.incl((3, 2))
-      a.incl((2, 3))
-      for x, y in a.items:
-        b.incl((x - 2, y + 1))
-      assert a.len == b.card
-      assert a.len == 2
-      #echo b
-
-    block setContains:
-      var values = initHashSet[int]()
-      assert(not values.contains(2))
-      values.incl(2)
-      assert values.contains(2)
-      values.excl(2)
-      assert(not values.contains(2))
-
-      values.incl(4)
-      var others = toHashSet([6, 7])
-      values.incl(others)
-      assert values.len == 3
-
-      values.init
-      assert values.containsOrIncl(2) == false
-      assert values.containsOrIncl(2) == true
-      var
-        a = toHashSet([1, 2])
-        b = toHashSet([1])
-      b.incl(2)
-      assert a == b
-
-    block exclusions:
-      var s = toHashSet([2, 3, 6, 7])
-      s.excl(2)
-      s.excl(2)
-      assert s.len == 3
-
-      var
-        numbers = toHashSet([1, 2, 3, 4, 5])
-        even = toHashSet([2, 4, 6, 8])
-      numbers.excl(even)
-      #echo numbers
-      # --> {1, 3, 5}
-
-    block toSeqAndString:
-      var a = toHashSet([2, 7, 5])
-      var b = initHashSet[int](rightSize(a.len))
-      for x in [2, 7, 5]: b.incl(x)
-      assert($a == $b)
-      #echo a
-      #echo toHashSet(["no", "esc'aping", "is \" provided"])
-
-    #block orderedToSeqAndString:
-    #  echo toOrderedSet([2, 4, 5])
-    #  echo toOrderedSet(["no", "esc'aping", "is \" provided"])
-
-    block setOperations:
-      var
-        a = toHashSet(["a", "b"])
-        b = toHashSet(["b", "c"])
-        c = union(a, b)
-      assert c == toHashSet(["a", "b", "c"])
-      var d = intersection(a, b)
-      assert d == toHashSet(["b"])
-      var e = difference(a, b)
-      assert e == toHashSet(["a"])
-      var f = symmetricDifference(a, b)
-      assert f == toHashSet(["a", "c"])
-      assert d < a and d < b
-      assert((a < a) == false)
-      assert d <= a and d <= b
-      assert((a <= a))
-      # Alias test.
-      assert a + b == toHashSet(["a", "b", "c"])
-      assert a * b == toHashSet(["b"])
-      assert a - b == toHashSet(["a"])
-      assert a -+- b == toHashSet(["a", "c"])
-      assert disjoint(a, b) == false
-      assert disjoint(a, b - a) == true
-
-    block mapSet:
-      var a = toHashSet([1, 2, 3])
-      var b = a.map(proc (x: int): string = $x)
-      assert b == toHashSet(["1", "2", "3"])
-
-    block lenTest:
-      var values: OrderedSet[int]
-      assert values.len == 0
-      assert values.card == 0
-
-    block setIterator:
-      type pair = tuple[a, b: int]
-      var a, b = initOrderedSet[pair]()
-      a.incl((2, 3))
-      a.incl((3, 2))
-      a.incl((2, 3))
-      for x, y in a.items:
-        b.incl((x - 2, y + 1))
-      assert a.len == b.card
-      assert a.len == 2
-
-    block setPairsIterator:
-      var s = toOrderedSet([1, 3, 5, 7])
-      var items = newSeq[tuple[a: int, b: int]]()
-      for idx, item in s: items.add((idx, item))
-      assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
-
-    block exclusions:
-      var s = toOrderedSet([1, 2, 3, 6, 7, 4])
-
-      s.excl(3)
-      s.excl(3)
-      s.excl(1)
-      s.excl(4)
-
-      var items = newSeq[int]()
-      for item in s: items.add item
-      assert items == @[2, 6, 7]
-
-    block: #9005
-      var s = initOrderedSet[(int, int)]()
-      for i in 0 .. 30: incl(s, (i, 0))
-      for i in 0 .. 30: excl(s, (i, 0))
-      doAssert s.len == 0
-
-    #block orderedSetIterator:
-    #  var a = initOrderedSet[int]()
-    #  for value in [9, 2, 1, 5, 1, 8, 4, 2]:
-    #    a.incl(value)
-    #  for value in a.items:
-    #    echo "Got ", value
-
-    block setContains:
-      var values = initOrderedSet[int]()
-      assert(not values.contains(2))
-      values.incl(2)
-      assert values.contains(2)
-
-    block toSeqAndString:
-      var a = toOrderedSet([2, 4, 5])
-      var b = initOrderedSet[int]()
-      for x in [2, 4, 5]: b.incl(x)
-      assert($a == $b)
-      assert(a == b) # https://github.com/Araq/Nim/issues/1413
-
-    block initBlocks:
-      var a: OrderedSet[int]
-      a.init(4)
-      a.incl(2)
-      a.init
-      assert a.len == 0
-      a = initOrderedSet[int](4)
-      a.incl(2)
-      assert a.len == 1
-
-      var b: HashSet[int]
-      b.init(4)
-      b.incl(2)
-      b.init
-      assert b.len == 0
-      b = initHashSet[int](4)
-      b.incl(2)
-      assert b.len == 1
-
-    block:
-      type FakeTable = object
-        dataLen: int
-        counter: int
-        countDeleted: int
-
-      var t: FakeTable
-      for i in 0 .. 32:
-        var s = rightSize(i)
-        t.dataLen = s
-        t.counter = i
-        doAssert s > i and not mustRehash(t),
-          "performance issue: rightSize() will not elide enlarge() at: " & $i
-
-    block missingOrExcl:
-      var s = toOrderedSet([2, 3, 6, 7])
-      assert s.missingOrExcl(4) == true
-      assert s.missingOrExcl(6) == false
-
-    block orderedSetEquality:
-      type pair = tuple[a, b: int]
-
-      var aa = initOrderedSet[pair]()
-      var bb = initOrderedSet[pair]()
-
-      var x = (a: 1, b: 2)
-      var y = (a: 3, b: 4)
-
-      aa.incl(x)
-      aa.incl(y)
-
-      bb.incl(x)
-      bb.incl(y)
-      assert aa == bb
-
-    block setsWithoutInit:
-      var
-        a: HashSet[int]
-        b: HashSet[int]
-        c: HashSet[int]
-        d: HashSet[int]
-        e: HashSet[int]
-
-      doAssert a.containsOrIncl(3) == false
-      doAssert a.contains(3)
-      doAssert a.len == 1
-      doAssert a.containsOrIncl(3)
-      a.incl(3)
-      doAssert a.len == 1
-      a.incl(6)
-      doAssert a.len == 2
-
-      b.incl(5)
-      doAssert b.len == 1
-      b.excl(5)
-      b.excl(c)
-      doAssert b.missingOrExcl(5)
-      doAssert b.disjoint(c)
-
-      d = b + c
-      doAssert d.len == 0
-      d = b * c
-      doAssert d.len == 0
-      d = b - c
-      doAssert d.len == 0
-      d = b -+- c
-      doAssert d.len == 0
-
-      doAssert (d < e) == false
-      doAssert d <= e
-      doAssert d == e
-
-    block setsWithoutInit:
-      var
-        a: OrderedSet[int]
-        b: OrderedSet[int]
-        c: OrderedSet[int]
-        d: HashSet[int]
-
-
-      doAssert a.containsOrIncl(3) == false
-      doAssert a.contains(3)
-      doAssert a.len == 1
-      doAssert a.containsOrIncl(3)
-      a.incl(3)
-      doAssert a.len == 1
-      a.incl(6)
-      doAssert a.len == 2
-
-      b.incl(5)
-      doAssert b.len == 1
-      doAssert b.missingOrExcl(5) == false
-      doAssert b.missingOrExcl(5)
-
-      doAssert c.missingOrExcl(9)
-      d.incl(c)
-      doAssert d.len == 0
-
-    when not defined(testing):
-      echo "Micro tests run successfully."
-
-  testModule()
+    assert(len(s) == length, "the length of the OrderedSet changed while iterating over it")
diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim
index 790529b79..ec8f1cd86 100644
--- a/lib/pure/collections/sharedlist.nim
+++ b/lib/pure/collections/sharedlist.nim
@@ -11,10 +11,12 @@
 ##
 ## Unstable API.
 
+{.deprecated.}
+
 {.push stackTrace: off.}
 
 import
-  locks
+  std/locks
 
 const
   ElemsPerNode = 100
@@ -35,8 +37,10 @@ template withLock(t, x: untyped) =
   release(t.lock)
 
 proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) =
-  ## iterates over the list. If 'action' returns true, the
+  ## Iterates over the list. If `action` returns true, the
   ## current item is removed from the list.
+  ##
+  ## .. warning:: It may not preserve the element order after some modifications.
   withLock(x):
     var n = x.head
     while n != nil:
@@ -47,8 +51,9 @@ proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) =
         if action(n.d[i]):
           acquire(x.lock)
           let t = x.tail
+          dec t.dataLen # TODO considering t.dataLen == 0,
+                        # probably the module should be refactored using doubly linked lists
           n.d[i] = t.d[t.dataLen]
-          dec t.dataLen
         else:
           acquire(x.lock)
           inc i
@@ -65,11 +70,14 @@ iterator items*[A](x: var SharedList[A]): A =
 proc add*[A](x: var SharedList[A]; y: A) =
   withLock(x):
     var node: SharedListNode[A]
-    if x.tail == nil or x.tail.dataLen == ElemsPerNode:
-      node = cast[type node](allocShared0(sizeof(node[])))
-      node.next = x.tail
+    if x.tail == nil:
+      node = cast[typeof node](allocShared0(sizeof(node[])))
+      x.tail = node
+      x.head = node
+    elif x.tail.dataLen == ElemsPerNode:
+      node = cast[typeof node](allocShared0(sizeof(node[])))
+      x.tail.next = node
       x.tail = node
-      if x.head == nil: x.head = node
     else:
       node = x.tail
     node.d[node.dataLen] = y
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index abef89507..b474ecd31 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -14,8 +14,10 @@
 ##
 ## Unstable API.
 
+{.deprecated.}
+
 import
-  hashes, math, locks
+  std/[hashes, math, locks]
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
@@ -60,17 +62,27 @@ template withLock(t, x: untyped) =
 
 template withValue*[A, B](t: var SharedTable[A, B], key: A,
                           value, body: untyped) =
-  ## retrieves the value at ``t[key]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     # value is threadsafe in block
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##
+  ## Retrieves the value at `t[key]`.
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    var table: SharedTable[string, string]
+    init(table)
+
+    table["a"] = "x"
+    table["b"] = "y"
+    table["c"] = "z"
+
+    table.withValue("a", value):
+      assert value[] == "x"
+
+    table.withValue("b", value):
+      value[] = "modified"
+
+    table.withValue("b", value):
+      assert value[] == "modified"
+
+    table.withValue("nonexistent", value):
+      assert false # not called
   acquire(t.lock)
   try:
     var hc: Hash
@@ -84,20 +96,33 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
 
 template withValue*[A, B](t: var SharedTable[A, B], key: A,
                           value, body1, body2: untyped) =
-  ## retrieves the value at ``t[key]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     # value is threadsafe in block
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##   do:
-  ##     # block is executed when ``key`` not in ``t``
-  ##     raise newException(KeyError, "Key not found")
-  ##
+  ## Retrieves the value at `t[key]`.
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    var table: SharedTable[string, string]
+    init(table)
+
+    table["a"] = "x"
+    table["b"] = "y"
+    table["c"] = "z"
+
+
+    table.withValue("a", value):
+      value[] = "m"
+
+    var flag = false
+    table.withValue("d", value):
+      discard value
+      doAssert false
+    do: # if "d" notin table
+      flag = true
+
+    if flag:
+      table["d"] = "n"
+
+    assert table.mget("a") == "m"
+    assert table.mget("d") == "n"
+
   acquire(t.lock)
   try:
     var hc: Hash
@@ -112,8 +137,8 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
     release(t.lock)
 
 proc mget*[A, B](t: var SharedTable[A, B], key: A): var B =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
+  ## Retrieves the value at `t[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   withLock t:
     var hc: Hash
     var index = rawGet(t, key, hc)
@@ -126,44 +151,47 @@ proc mget*[A, B](t: var SharedTable[A, B], key: A): var B =
       raise newException(KeyError, "key not found")
 
 proc mgetOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at `t[key]` or puts `val` if not present, either way
   ## returning a value which can be modified. **Note**: This is inherently
   ## unsafe in the context of multi-threading since it returns a pointer
-  ## to ``B``.
+  ## to `B`.
   withLock t:
     mgetOrPutImpl(enlarge)
 
 proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool =
-  ## returns true if `key` is in the table, otherwise inserts `value`.
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   withLock t:
     hasKeyOrPutImpl(enlarge)
 
+template tabMakeEmpty(i) = t.data[i].hcode = 0
+template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
+template tabCellHash(i)  = t.data[i].hcode
+
 proc withKey*[A, B](t: var SharedTable[A, B], key: A,
                     mapper: proc(key: A, val: var B, pairExists: var bool)) =
-  ## Computes a new mapping for the ``key`` with the specified ``mapper``
+  ## Computes a new mapping for the `key` with the specified `mapper`
   ## procedure.
   ##
-  ## The ``mapper`` takes 3 arguments:
+  ## The `mapper` takes 3 arguments:
   ##
-  ## 1. ``key`` - the current key, if it exists, or the key passed to
-  ##    ``withKey`` otherwise;
-  ## 2. ``val`` - the current value, if the key exists, or default value
+  ## 1. `key` - the current key, if it exists, or the key passed to
+  ##    `withKey` otherwise;
+  ## 2. `val` - the current value, if the key exists, or default value
   ##    of the type otherwise;
-  ## 3. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
+  ## 3. `pairExists` - `true` if the key exists, `false` otherwise.
   ##
-  ## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change
+  ## The `mapper` can can modify `val` and `pairExists` values to change
   ## the mapping of the key or delete it from the table.
-  ## When adding a value, make sure to set ``pairExists`` to ``true`` along
-  ## with modifying the ``val``.
+  ## When adding a value, make sure to set `pairExists` to `true` along
+  ## with modifying the `val`.
   ##
   ## The operation is performed atomically and other operations on the table
-  ## will be blocked while the ``mapper`` is invoked, so it should be short and
+  ## will be blocked while the `mapper` is invoked, so it should be short and
   ## simple.
   ##
   ## Example usage:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   # If value exists, decrement it.
   ##   # If it becomes zero or less, delete the key
   ##   t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool):
@@ -171,6 +199,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
   ##       dec v
   ##       if v <= 0:
   ##         pairExists = false
+  ##   ```
   withLock t:
     var hc: Hash
     var index = rawGet(t, key, hc)
@@ -179,7 +208,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
     if pairExists:
       mapper(t.data[index].key, t.data[index].val, pairExists)
       if not pairExists:
-        delImplIdx(t, index)
+        delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
     else:
       var val: B
       mapper(key, val, pairExists)
@@ -187,35 +216,31 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
         st_maybeRehashPutImpl(enlarge)
 
 proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
-  ## puts a (key, value)-pair into `t`.
+  ## Puts a (key, value)-pair into `t`.
   withLock t:
     putImpl(enlarge)
 
 proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) =
-  ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## Puts a new (key, value)-pair into `t` even if `t[key]` already exists.
   ## This can introduce duplicate keys into the table!
   withLock t:
     addImpl(enlarge)
 
 proc del*[A, B](t: var SharedTable[A, B], key: A) =
-  ## deletes `key` from hash table `t`.
+  ## Deletes `key` from hash table `t`.
   withLock t:
-    delImpl()
+    delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash)
 
 proc len*[A, B](t: var SharedTable[A, B]): int =
-  ## number of elements in `t`
+  ## Number of elements in `t`.
   withLock t:
     result = t.counter
 
-proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) =
-  ## creates a new hash table that is empty.
+proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) =
+  ## Creates a new hash table that is empty.
   ##
   ## This proc must be called before any other usage of `t`.
-  ##
-  ## `initialSize` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
-  assert isPowerOfTwo(initialSize)
+  let initialSize = slotsNeeded(initialSize)
   t.counter = 0
   t.dataLen = initialSize
   t.data = cast[KeyValuePairSeq[A, B]](allocShared0(
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 47c14af93..3542741fa 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -7,10 +7,13 @@
 #    distribution, for details about the copyright.
 #
 
-# An ``include`` file for the different table implementations.
+# An `include` file for the different table implementations.
 
 include hashcommon
 
+const
+  defaultInitialSize* = 32
+
 template rawGetDeepImpl() {.dirty.} =   # Search algo for unconditional add
   genHashImpl(key, hc)
   var h: Hash = hc and maxHash(t)
@@ -23,17 +26,16 @@ template rawInsertImpl() {.dirty.} =
   data[h].val = val
   data[h].hcode = hc
 
-proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
   rawGetDeepImpl()
 
 proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
-                     key: A, val: B, hc: Hash, h: Hash) =
+                     key: A, val: sink B, hc: Hash, h: Hash) =
   rawInsertImpl()
 
 template checkIfInitialized() =
-  when compiles(defaultInitialSize):
-    if t.dataLen == 0:
-      initImpl(t, defaultInitialSize)
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
 
 template addImpl(enlarge) {.dirty.} =
   checkIfInitialized()
@@ -43,7 +45,7 @@ template addImpl(enlarge) {.dirty.} =
   rawInsert(t, t.data, key, val, hc, j)
   inc(t.counter)
 
-template maybeRehashPutImpl(enlarge) {.dirty.} =
+template maybeRehashPutImpl(enlarge, val) {.dirty.} =
   checkIfInitialized()
   if mustRehash(t):
     enlarge(t)
@@ -54,46 +56,91 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
 
 template putImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index >= 0: t.data[index].val = val
-  else: maybeRehashPutImpl(enlarge)
+  else: maybeRehashPutImpl(enlarge, val)
 
 template mgetOrPutImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     # not present: insert (flipping index)
-    maybeRehashPutImpl(enlarge)
+    when declared(val):
+      maybeRehashPutImpl(enlarge, val)
+    else:
+      maybeRehashPutImpl(enlarge, default(B))
   # either way return modifiable val
   result = t.data[index].val
 
+# template mgetOrPutDefaultImpl(enlarge) {.dirty.} =
+#   checkIfInitialized()
+#   var hc: Hash = default(Hash)
+#   var index = rawGet(t, key, hc)
+#   if index < 0:
+#     # not present: insert (flipping index)
+#     maybeRehashPutImpl(enlarge, default(B))
+#   # either way return modifiable val
+#   result = t.data[index].val
+
 template hasKeyOrPutImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     result = false
-    maybeRehashPutImpl(enlarge)
+    maybeRehashPutImpl(enlarge, val)
   else: result = true
 
-template delImplIdx(t, i) =
+# delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to
+# be called "back shift delete".  It shifts elements in the collision cluster of
+# a victim backward to make things as-if the victim were never inserted in the
+# first place.  This is desirable to keep things "ageless" after many deletes.
+# It is trickier than you might guess since initial probe (aka "home") locations
+# of keys in a cluster may collide and since table addresses wrap around.
+#
+# A before-after diagram might look like ('.' means empty):
+#   slot:   0   1   2   3   4   5   6   7
+# before(1)
+#   hash1:  6   7   .   3   .   5   5   6  ; Really hash() and msk
+#   data1:  E   F   .   A   .   B   C   D  ; About to delete C @index 6
+# after(2)
+#   hash2:  7   .   .   3   .   5   6   6  ; Really hash() and msk
+#   data2:  F   .   .   A   .   B   D   E  ; After deletion of C
+#
+# This lowers total search depth over the whole table from 1+1+2+2+2+2=10 to 7.
+# Had the victim been B@5, C would need back shifting to slot 5.  Total depth is
+# always lowered by at least 1, e.g. victim A@3.  This is all quite fast when
+# empty slots are frequent (also needed to keep insert/miss searches fast) and
+# hash() is either fast or avoided (via `.hcode`).  It need not compare keys.
+#
+# delImplIdx realizes the above transformation, but only works for dense Linear
+# Probing, nextTry(h)=h+1.  This is not an important limitation since that's the
+# fastest sequence on any CPU made since the 1980s. { Performance analysis often
+# overweights "key cmp" neglecting cache behavior, giving bad ideas how big/slow
+# tables behave (when perf matters most!).  Comparing hcode first means usually
+# only 1 key cmp is needed for *any* seq.  Timing only predictable activity,
+# small tables, and/or integer keys often perpetuates such bad ideas. }
+
+template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) =
   let msk = maxHash(t)
   if i >= 0:
     dec(t.counter)
     block outer:
       while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
-        var j = i         # The correctness of this depends on (h+1) in nextTry,
+        var j = i         # The correctness of this depends on (h+1) in nextTry
         var r = j         # though may be adaptable to other simple sequences.
-        t.data[i].hcode = 0              # mark current EMPTY
-        t.data[i].key = default(type(t.data[i].key))
-        t.data[i].val = default(type(t.data[i].val))
+        makeEmpty(i)                     # mark current EMPTY
+        {.push warning[UnsafeDefault]:off.}
+        reset(t.data[i].key)
+        reset(t.data[i].val)
+        {.pop.}
         while true:
           i = (i + 1) and msk            # increment mod table size
-          if isEmpty(t.data[i].hcode):   # end of collision cluster; So all done
+          if cellEmpty(i):               # end of collision cluster; So all done
             break outer
-          r = t.data[i].hcode and msk    # "home" location of key@i
+          r = cellHash(i) and msk        # initial probe index for key@slot i
           if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
             break
         when defined(js):
@@ -101,17 +148,28 @@ template delImplIdx(t, i) =
         else:
           t.data[j] = move(t.data[i]) # data[j] will be marked EMPTY next loop
 
-template delImpl() {.dirty.} =
+template delImpl(makeEmpty, cellEmpty, cellHash) {.dirty.} =
   var hc: Hash
   var i = rawGet(t, key, hc)
-  delImplIdx(t, i)
+  delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
+
+template delImplNoHCode(makeEmpty, cellEmpty, cellHash) {.dirty.} =
+  if t.dataLen > 0:
+    var i: Hash = hash(key) and maxHash(t)
+    while not cellEmpty(i):
+      if t.data[i].key == key:
+        delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
+        break
+      i = nextTry(i, maxHash(t))
 
 template clearImpl() {.dirty.} =
   for i in 0 ..< t.dataLen:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
-    t.data[i].key = default(type(t.data[i].key))
-    t.data[i].val = default(type(t.data[i].val))
+    {.push warning[UnsafeDefault]:off.}
+    reset(t.data[i].key)
+    reset(t.data[i].val)
+    {.pop.}
   t.counter = 0
 
 template ctAnd(a, b): bool =
@@ -121,12 +179,12 @@ template ctAnd(a, b): bool =
   else: false
 
 template initImpl(result: typed, size: int) =
-  when ctAnd(declared(SharedTable), type(result) is SharedTable):
-    init(result, size)
+  let correctSize = slotsNeeded(size)
+  when ctAnd(declared(SharedTable), typeof(result) is SharedTable):
+    init(result, correctSize)
   else:
-    assert isPowerOfTwo(size)
     result.counter = 0
-    newSeq(result.data, size)
+    newSeq(result.data, correctSize)
     when compiles(result.first):
       result.first = -1
       result.last = -1
@@ -169,3 +227,5 @@ template equalsImpl(s, t: typed) =
       if not t.hasKey(key): return false
       if t.getOrDefault(key) != val: return false
     return true
+  else:
+    return false
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index a969a4c5d..d414caeed 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -7,219 +7,198 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``tables`` module implements variants of an efficient `hash table`:idx:
+## The `tables` module implements variants of an efficient `hash table`:idx:
 ## (also often named `dictionary`:idx: in other programming languages) that is
 ## a mapping from keys to values.
 ##
 ## There are several different types of hash tables available:
 ## * `Table<#Table>`_ is the usual hash table,
-## * `OrderedTable<#OrderedTable>`_ is like ``Table`` but remembers insertion order,
+## * `OrderedTable<#OrderedTable>`_ is like `Table` but remembers insertion order,
 ## * `CountTable<#CountTable>`_ is a mapping from a key to its number of occurrences
 ##
 ## For consistency with every other data type in Nim these have **value**
-## semantics, this means that ``=`` performs a copy of the hash table.
+## semantics, this means that `=` performs a copy of the hash table.
 ##
 ## For `ref semantics<manual.html#types-reference-and-pointer-types>`_
-## use their ``Ref`` variants: `TableRef<#TableRef>`_,
+## use their `Ref` variants: `TableRef<#TableRef>`_,
 ## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
 ##
-## To give an example, when ``a`` is a ``Table``, then ``var b = a`` gives ``b``
-## as a new independent table. ``b`` is initialised with the contents of ``a``.
-## Changing ``b`` does not affect ``a`` and vice versa:
-##
-## .. code-block::
-##   import tables
-##
-##   var
-##     a = {1: "one", 2: "two"}.toTable  # creates a Table
-##     b = a
-##
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two}
-##
-##   b[3] = "three"
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two, 3: three}
-##   echo a == b  # output: false
-##
-## On the other hand, when ``a`` is a ``TableRef`` instead, then changes to ``b``
-## also affect ``a``. Both ``a`` and ``b`` **ref** the same data structure:
-##
-## .. code-block::
-##   import tables
-##
-##   var
-##     a = {1: "one", 2: "two"}.newTable  # creates a TableRef
-##     b = a
-##
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two}
-##
-##   b[3] = "three"
-##   echo a, b  # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three}
-##   echo a == b  # output: true
+## To give an example, when `a` is a `Table`, then `var b = a` gives `b`
+## as a new independent table. `b` is initialised with the contents of `a`.
+## Changing `b` does not affect `a` and vice versa:
+
+runnableExamples:
+  var
+    a = {1: "one", 2: "two"}.toTable  # creates a Table
+    b = a
+
+  assert a == b
+
+  b[3] = "three"
+  assert 3 notin a
+  assert 3 in b
+  assert a != b
+
+## On the other hand, when `a` is a `TableRef` instead, then changes to `b`
+## also affect `a`. Both `a` and `b` **ref** the same data structure:
+
+runnableExamples:
+  var
+    a = {1: "one", 2: "two"}.newTable  # creates a TableRef
+    b = a
+
+  assert a == b
+
+  b[3] = "three"
+
+  assert 3 in a
+  assert 3 in b
+  assert a == b
+
 ##
 ## ----
 ##
-## Basic usage
-## ===========
-##
-## Table
-## -----
-##
-## .. code-block::
-##   import tables
-##   from sequtils import zip
-##
-##   let
-##     names = ["John", "Paul", "George", "Ringo"]
-##     years = [1940, 1942, 1943, 1940]
-##
-##   var beatles = initTable[string, int]()
-##
-##   for pairs in zip(names, years):
-##     let (name, birthYear) = pairs
-##     beatles[name] = birthYear
-##
-##   echo beatles
-##   # {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}
-##
-##
-##   var beatlesByYear = initTable[int, seq[string]]()
-##
-##   for pairs in zip(years, names):
-##     let (birthYear, name) = pairs
-##     if not beatlesByYear.hasKey(birthYear):
-##       # if a key doesn't exist, we create one with an empty sequence
-##       # before we can add elements to it
-##       beatlesByYear[birthYear] = @[]
-##     beatlesByYear[birthYear].add(name)
-##
-##   echo beatlesByYear
-##   # {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}
-##
-##
-##
-## OrderedTable
-## ------------
-##
+
+## # Basic usage
+
+
+## ## Table
+runnableExamples:
+  from std/sequtils import zip
+
+  let
+    names = ["John", "Paul", "George", "Ringo"]
+    years = [1940, 1942, 1943, 1940]
+
+  var beatles = initTable[string, int]()
+
+  for pairs in zip(names, years):
+    let (name, birthYear) = pairs
+    beatles[name] = birthYear
+
+  assert beatles == {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}.toTable
+
+
+  var beatlesByYear = initTable[int, seq[string]]()
+
+  for pairs in zip(years, names):
+    let (birthYear, name) = pairs
+    if not beatlesByYear.hasKey(birthYear):
+      # if a key doesn't exist, we create one with an empty sequence
+      # before we can add elements to it
+      beatlesByYear[birthYear] = @[]
+    beatlesByYear[birthYear].add(name)
+
+  assert beatlesByYear == {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}.toTable
+
+## ## OrderedTable
 ## `OrderedTable<#OrderedTable>`_ is used when it is important to preserve
 ## the insertion order of keys.
-##
-## .. code-block::
-##   import tables
-##
-##   let
-##     a = [('z', 1), ('y', 2), ('x', 3)]
-##     t = a.toTable          # regular table
-##     ot = a.toOrderedTable  # ordered tables
-##
-##   echo t   # {'x': 3, 'y': 2, 'z': 1}
-##   echo ot  # {'z': 1, 'y': 2, 'x': 3}
-##
-##
-##
-## CountTable
-## ----------
-##
+
+runnableExamples:
+  let
+    a = [('z', 1), ('y', 2), ('x', 3)]
+    ot = a.toOrderedTable  # ordered tables
+
+  assert $ot == """{'z': 1, 'y': 2, 'x': 3}"""
+
+## ## CountTable
 ## `CountTable<#CountTable>`_ is useful for counting number of items of some
 ## container (e.g. string, sequence or array), as it is a mapping where the
 ## items are the keys, and their number of occurrences are the values.
 ## For that purpose `toCountTable proc<#toCountTable,openArray[A]>`_
 ## comes handy:
-##
-## .. code-block::
-##   import tables
-##
-##   let myString = "abracadabra"
-##   let letterFrequencies = toCountTable(myString)
-##   echo letterFrequencies
-##   # 'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
-##
+
+runnableExamples:
+  let myString = "abracadabra"
+  let letterFrequencies = toCountTable(myString)
+  assert $letterFrequencies == "{'a': 5, 'd': 1, 'b': 2, 'r': 2, 'c': 1}"
+
 ## The same could have been achieved by manually iterating over a container
 ## and increasing each key's value with `inc proc
-## <#inc,CountTable[A],A,Positive>`_:
-##
-## .. code-block::
-##   import tables
-##
-##   let myString = "abracadabra"
-##   var letterFrequencies = initCountTable[char]()
-##   for c in myString:
-##     letterFrequencies.inc(c)
-##   echo letterFrequencies
-##   # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
+## <#inc,CountTable[A],A,int>`_:
+
+runnableExamples:
+  let myString = "abracadabra"
+  var letterFrequencies = initCountTable[char]()
+  for c in myString:
+    letterFrequencies.inc(c)
+  assert $letterFrequencies == "{'d': 1, 'r': 2, 'c': 1, 'a': 5, 'b': 2}"
+
 ##
 ## ----
 ##
+
+## ## Hashing
 ##
-##
-## Hashing
-## -------
-##
-## If you are using simple standard types like ``int`` or ``string`` for the
+## If you are using simple standard types like `int` or `string` for the
 ## keys of the table you won't have any problems, but as soon as you try to use
 ## a more complex object as a key you will be greeted by a strange compiler
 ## error:
 ##
-##   Error: type mismatch: got (Person)
-##   but expected one of:
-##   hashes.hash(x: openArray[A]): Hash
-##   hashes.hash(x: int): Hash
-##   hashes.hash(x: float): Hash
-##   …
+##     Error: type mismatch: got (Person)
+##     but expected one of:
+##     hashes.hash(x: openArray[A]): Hash
+##     hashes.hash(x: int): Hash
+##     hashes.hash(x: float): Hash
 ##
 ## What is happening here is that the types used for table keys require to have
-## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
+## a `hash()` proc which will convert them to a `Hash <hashes.html#Hash>`_
 ## value, and the compiler is listing all the hash functions it knows.
-## Additionally there has to be a ``==`` operator that provides the same
-## semantics as its corresponding ``hash`` proc.
+## Additionally there has to be a `==` operator that provides the same
+## semantics as its corresponding `hash` proc.
 ##
-## After you add ``hash`` and ``==`` for your custom type everything will work.
-## Currently, however, ``hash`` for objects is not defined, whereas
-## ``system.==`` for objects does exist and performs a "deep" comparison (every
+## After you add `hash` and `==` for your custom type everything will work.
+## Currently, however, `hash` for objects is not defined, whereas
+## `system.==` for objects does exist and performs a "deep" comparison (every
 ## field is compared) which is usually what you want. So in the following
-## example implementing only ``hash`` suffices:
-##
-## .. code-block::
-##   import tables, hashes
-##
-##   type
-##     Person = object
-##       firstName, lastName: string
-##
-##   proc hash(x: Person): Hash =
-##     ## Piggyback on the already available string hash proc.
-##     ##
-##     ## Without this proc nothing works!
-##     result = x.firstName.hash !& x.lastName.hash
-##     result = !$result
-##
-##   var
-##     salaries = initTable[Person, int]()
-##     p1, p2: Person
-##
-##   p1.firstName = "Jon"
-##   p1.lastName = "Ross"
-##   salaries[p1] = 30_000
-##
-##   p2.firstName = "소진"
-##   p2.lastName = "박"
-##   salaries[p2] = 45_000
+## example implementing only `hash` suffices:
+
+runnableExamples:
+  import std/hashes
+
+  type
+    Person = object
+      firstName, lastName: string
+
+  proc hash(x: Person): Hash =
+    ## Piggyback on the already available string hash proc.
+    ##
+    ## Without this proc nothing works!
+    result = x.firstName.hash !& x.lastName.hash
+    result = !$result
+
+  var
+    salaries = initTable[Person, int]()
+    p1, p2: Person
+
+  p1.firstName = "Jon"
+  p1.lastName = "Ross"
+  salaries[p1] = 30_000
+
+  p2.firstName = "소진"
+  p2.lastName = "박"
+  salaries[p2] = 45_000
+
 ##
 ## ----
 ##
-## See also
-## ========
+
+## # See also
 ##
 ## * `json module<json.html>`_ for table-like structure which allows
 ##   heterogeneous members
-## * `sharedtables module<sharedtables.html>`_ for shared hash table support
 ## * `strtabs module<strtabs.html>`_ for efficient hash tables
 ##   mapping from strings to strings
 ## * `hashes module<hashes.html>`_ for helper functions for hashing
 
 
 import std/private/since
+import std/[hashes, math, algorithm]
+
 
-import hashes, math, algorithm
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
@@ -230,16 +209,14 @@ type
     ## `data` and `counter` are internal implementation details which
     ## can't be accessed.
     ##
-    ## For creating an empty Table, use `initTable proc<#initTable,int>`_.
+    ## For creating an empty Table, use `initTable proc<#initTable>`_.
     data: KeyValuePairSeq[A, B]
     counter: int
   TableRef*[A, B] = ref Table[A, B] ## Ref version of `Table<#Table>`_.
     ##
     ## For creating a new empty TableRef, use `newTable proc
-    ## <#newTable,int>`_.
+    ## <#newTable>`_.
 
-const
-  defaultInitialSize* = 64
 
 # ------------------------------ helpers ---------------------------------
 
@@ -250,18 +227,21 @@ template dataLen(t): untyped = len(t.data)
 
 include tableimpl
 
+proc raiseKeyError[T](key: T) {.noinline, noreturn.} =
+  when compiles($key):
+    raise newException(KeyError, "key not found: " & $key)
+  else:
+    raise newException(KeyError, "key not found")
+
 template get(t, key): untyped =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ## retrieves the value at `t[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
   if index >= 0: result = t.data[index].val
   else:
-    when compiles($key):
-      raise newException(KeyError, "key not found: " & $key)
-    else:
-      raise newException(KeyError, "key not found")
+    raiseKeyError(key)
 
 proc enlarge[A, B](t: var Table[A, B]) =
   var n: KeyValuePairSeq[A, B]
@@ -288,26 +268,21 @@ proc enlarge[A, B](t: var Table[A, B]) =
 proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] =
   ## Creates a new hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
   ## See also:
   ## * `toTable proc<#toTable,openArray[]>`_
-  ## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
+  ## * `newTable proc<#newTable>`_ for creating a `TableRef`
   runnableExamples:
     let
       a = initTable[int, string]()
       b = initTable[char, seq[int]]()
+  result = default(Table[A, B])
   initImpl(result, initialSize)
 
-proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) =
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
@@ -323,25 +298,25 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
   putImpl(enlarge)
 
 proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
-  ## Creates a new hash table that contains the given ``pairs``.
+  ## Creates a new hash table that contains the given `pairs`.
   ##
-  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ## `pairs` is a container consisting of `(key, value)` tuples.
   ##
   ## See also:
-  ## * `initTable proc<#initTable,int>`_
+  ## * `initTable proc<#initTable>`_
   ## * `newTable proc<#newTable,openArray[]>`_ for a `TableRef` version
   runnableExamples:
     let a = [('a', 5), ('b', 9)]
     let b = toTable(a)
     assert b == {'a': 5, 'b': 9}.toTable
 
-  result = initTable[A, B](rightSize(pairs.len))
+  result = initTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
-proc `[]`*[A, B](t: Table[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]``.
+proc `[]`*[A, B](t: Table[A, B], key: A): lent B =
+  ## Retrieves the value at `t[key]`.
   ##
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   ## One can check with `hasKey proc<#hasKey,Table[A,B],A>`_ whether
   ## the key exists.
   ##
@@ -350,7 +325,7 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in
   ##   the table
@@ -362,23 +337,23 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
   get(t, key)
 
 proc `[]`*[A, B](t: var Table[A, B], key: A): var B =
-  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ## Retrieves the value at `t[key]`. The value can be modified.
   ##
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   ##
   ## See also:
   ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in
   ##   the table
   get(t, key)
 
 proc hasKey*[A, B](t: Table[A, B], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,Table[A,B],A>`_ for use with the `in` operator
@@ -397,7 +372,7 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool =
 
 proc contains*[A, B](t: Table[A, B], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,Table[A,B],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toTable
     doAssert 'b' in a == true
@@ -406,7 +381,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
-  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   ##
   ## See also:
   ## * `hasKey proc<#hasKey,Table[A,B],A>`_
@@ -426,8 +401,8 @@ proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
   hasKeyOrPutImpl(enlarge)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## default initialization value for type `B` is returned (e.g. 0 for any
   ## integer type).
   ##
   ## See also:
@@ -441,12 +416,12 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
-
+  result = default(B)
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise, ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise, `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
@@ -459,13 +434,20 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
-
+  result = default(B)
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
-  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at `t[key]` or puts `val` if not present, either way
   ## returning a value which can be modified.
   ##
+  ##
+  ## Note that while the value returned is of type `var B`,
+  ## it is easy to accidentally create a copy of the value at `t[key]`.
+  ## Remember that seqs and strings are value types, and therefore
+  ## cannot be copied into a separate variable for modification.
+  ## See the example below.
+  ##
   ## See also:
   ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
   ## * `hasKey proc<#hasKey,Table[A,B],A>`_
@@ -480,27 +462,58 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
 
+    # An example of accidentally creating a copy
+    var t = initTable[int, seq[int]]()
+    # In this example, we expect t[10] to be modified,
+    # but it is not.
+    var copiedSeq = t.mgetOrPut(10, @[10])
+    copiedSeq.add(20)
+    doAssert t[10] == @[10]
+    # Correct
+    t.mgetOrPut(25, @[25]).add(35)
+    doAssert t[25] == @[25, 35]
+
+  mgetOrPutImpl(enlarge)
+
+proc mgetOrPut*[A, B](t: var Table[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
+
   mgetOrPutImpl(enlarge)
 
 proc len*[A, B](t: Table[A, B]): int =
-  ## Returns the number of keys in ``t``.
+  ## Returns the number of keys in `t`.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toTable
     doAssert len(a) == 2
 
   result = t.counter
 
-proc add*[A, B](t: var Table[A, B], key: A, val: B) =
-  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+proc add*[A, B](t: var Table[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists.
   ##
   ## **This can introduce duplicate keys into the table!**
   ##
-  ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ## Use `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new
   ## (key, value) pair in the table without introducing duplicates.
   addImpl(enlarge)
 
+template tabMakeEmpty(i) = t.data[i].hcode = 0
+template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
+template tabCellHash(i)  = t.data[i].hcode
+
 proc del*[A, B](t: var Table[A, B], key: A) =
-  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ## Deletes `key` from hash table `t`. Does nothing if the key does not exist.
+  ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
   ##
   ## See also:
   ## * `pop proc<#pop,Table[A,B],A,B>`_
@@ -512,14 +525,17 @@ proc del*[A, B](t: var Table[A, B], key: A) =
     a.del('z')
     doAssert a == {'b': 9, 'c': 13}.toTable
 
-  delImpl()
+  delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash)
 
 proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
+  ##
   ## See also:
   ## * `del proc<#del,Table[A,B],A>`_
   ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table
@@ -540,7 +556,7 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool =
   result = index >= 0
   if result:
     val = move(t.data[index].val)
-    delImplIdx(t, index)
+    delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
 
 proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool {.inline.} =
   ## Alias for:
@@ -562,12 +578,12 @@ proc clear*[A, B](t: var Table[A, B]) =
   clearImpl()
 
 proc `$`*[A, B](t: Table[A, B]): string =
-  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## The `$` operator for hash tables. Used internally when calling `echo`
   ## on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: Table[A, B]): bool =
-  ## The ``==`` operator for hash tables. Returns ``true`` if the content of both
+  ## The `==` operator for hash tables. Returns `true` if the content of both
   ## tables contains the same key-value pairs. Insert order does not matter.
   runnableExamples:
     let
@@ -587,17 +603,31 @@ proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
 
 
 template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
-  ## Retrieves the value at ``t[key]``.
-  ##
-  ## ``value`` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     value.name = "username"
-  ##     value.uid = 1000
+  ## Retrieves the value at `t[key]`.
   ##
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    type
+      User = object
+        name: string
+        uid: int
+
+    var t = initTable[int, User]()
+    let u = User(name: "Hello", uid: 99)
+    t[1] = u
+
+    t.withValue(1, value):
+      # block is executed only if `key` in `t`
+      value.name = "Nim"
+      value.uid = 1314
+
+    t.withValue(2, value):
+      value.name = "No"
+      value.uid = 521
+
+    assert t[1].name == "Nim"
+    assert t[1].uid == 1314
+
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
@@ -608,20 +638,35 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
 
 template withValue*[A, B](t: var Table[A, B], key: A,
                           value, body1, body2: untyped) =
-  ## Retrieves the value at ``t[key]``.
-  ##
-  ## ``value`` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   table.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##   do:
-  ##     # block is executed when ``key`` not in ``t``
-  ##     raise newException(KeyError, "Key not found")
+  ## Retrieves the value at `t[key]`.
   ##
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    type
+      User = object
+        name: string
+        uid: int
+
+    var t = initTable[int, User]()
+    let u = User(name: "Hello", uid: 99)
+    t[1] = u
+
+    t.withValue(1, value):
+      # block is executed only if `key` in `t`
+      value.name = "Nim"
+      value.uid = 1314
+
+    t.withValue(521, value):
+      doAssert false
+    do:
+      # block is executed when `key` not in `t`
+      t[1314] = User(name: "exist", uid: 521)
+
+    assert t[1].name == "Nim"
+    assert t[1].uid == 1314
+    assert t[1314].name == "exist"
+    assert t[1314].uid == 521
+
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
@@ -634,7 +679,7 @@ template withValue*[A, B](t: var Table[A, B], key: A,
 
 
 iterator pairs*[A, B](t: Table[A, B]): (A, B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ## Iterates over any `(key, value)` pair in the table `t`.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_
@@ -643,7 +688,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -657,6 +702,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) =
   ##   # value: [2, 4, 6, 8]
   ##   # key: o
   ##   # value: [1, 5, 7, 9]
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if isFilled(t.data[h].hcode):
@@ -664,7 +710,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## Iterates over any `(key, value)` pair in the table `t` (must be
   ## declared as `var`). The values can be modified.
   ##
   ## See also:
@@ -685,8 +731,8 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
       yield (t.data[h].key, t.data[h].val)
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator keys*[A, B](t: Table[A, B]): A =
-  ## Iterates over any key in the table ``t``.
+iterator keys*[A, B](t: Table[A, B]): lent A =
+  ## Iterates over any key in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,Table[A,B]>`_
@@ -706,8 +752,8 @@ iterator keys*[A, B](t: Table[A, B]): A =
       yield t.data[h].key
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator values*[A, B](t: Table[A, B]): B =
-  ## Iterates over any value in the table ``t``.
+iterator values*[A, B](t: Table[A, B]): lent B =
+  ## Iterates over any value in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,Table[A,B]>`_
@@ -728,7 +774,7 @@ iterator values*[A, B](t: Table[A, B]): B =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A, B](t: var Table[A, B]): var B =
-  ## Iterates over any value in the table ``t`` (must be
+  ## Iterates over any value in the table `t` (must be
   ## declared as `var`). The values can be modified.
   ##
   ## See also:
@@ -749,14 +795,15 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B =
       yield t.data[h].val
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator allValues*[A, B](t: Table[A, B]; key: A): B =
-  ## Iterates over any value in the table ``t`` that belongs to the given ``key``.
+iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated:
+    "Deprecated since v1.4; tables with duplicated keys are deprecated".} =
+  ## Iterates over any value in the table `t` that belongs to the given `key`.
   ##
   ## Used if you have a table with duplicate keys (as a result of using
-  ## `add proc<#add,Table[A,B],A,B>`_).
+  ## `add proc<#add,Table[A,B],A,sinkB>`_).
   ##
   runnableExamples:
-    import sequtils, algorithm
+    import std/[sequtils, algorithm]
 
     var a = {'a': 3, 'b': 5}.toTable
     for i in 1..3: a.add('z', 10*i)
@@ -777,34 +824,29 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
 # -------------------------------------------------------------------
 
 
-proc newTable*[A, B](initialSize = defaultInitialSize): <//>TableRef[A, B] =
+proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] =
   ## Creates a new ref hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef`
   ##   from a collection of `(key, value)` pairs
-  ## * `initTable proc<#initTable,int>`_ for creating a `Table`
+  ## * `initTable proc<#initTable>`_ for creating a `Table`
   runnableExamples:
     let
       a = newTable[int, string]()
       b = newTable[char, seq[int]]()
 
   new(result)
-  result[] = initTable[A, B](initialSize)
+  {.noSideEffect.}:
+    result[] = initTable[A, B](initialSize)
 
-proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] =
-  ## Creates a new ref hash table that contains the given ``pairs``.
+proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
+  ## Creates a new ref hash table that contains the given `pairs`.
   ##
-  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ## `pairs` is a container consisting of `(key, value)` tuples.
   ##
   ## See also:
-  ## * `newTable proc<#newTable,int>`_
+  ## * `newTable proc<#newTable>`_
   ## * `toTable proc<#toTable,openArray[]>`_ for a `Table` version
   runnableExamples:
     let a = [('a', 5), ('b', 9)]
@@ -812,19 +854,21 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] =
     assert b == {'a': 5, 'b': 9}.newTable
 
   new(result)
-  result[] = toTable[A, B](pairs)
+  {.noSideEffect.}:
+    result[] = toTable[A, B](pairs)
 
-proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): <//>TableRef[C, B] =
+proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
   ## Index the collection with the proc provided.
   # TODO: As soon as supported, change collection: A to collection: A[B]
   result = newTable[C, B]()
-  for item in collection:
-    result[index(item)] = item
+  {.noSideEffect.}:
+    for item in collection:
+      result[index(item)] = item
 
 proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
-  ## Retrieves the value at ``t[key]``.
+  ## Retrieves the value at `t[key]`.
   ##
-  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the  `KeyError` exception is raised.
   ## One can check with `hasKey proc<#hasKey,TableRef[A,B],A>`_ whether
   ## the key exists.
   ##
@@ -833,7 +877,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ for checking if a key is in
   ##   the table
@@ -845,8 +889,8 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
 
   result = t[][key]
 
-proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink B) =
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
@@ -862,7 +906,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
   t[][key] = val
 
 proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,TableRef[A,B],A>`_ for use with the `in`
@@ -881,7 +925,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
 
 proc contains*[A, B](t: TableRef[A, B], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,TableRef[A,B],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newTable
     doAssert 'b' in a == true
@@ -889,8 +933,8 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool =
 
   return hasKey[A, B](t, key)
 
-proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
-  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+proc hasKeyOrPut*[A, B](t: TableRef[A, B], key: A, val: B): bool =
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   ##
   ## See also:
   ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
@@ -910,8 +954,8 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
   t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## default initialization value for type `B` is returned (e.g. 0 for any
   ## integer type).
   ##
   ## See also:
@@ -929,8 +973,8 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise, ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise, `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
@@ -947,9 +991,15 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
-  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at `t[key]` or puts `val` if not present, either way
   ## returning a value which can be modified.
   ##
+  ## Note that while the value returned is of type `var B`,
+  ## it is easy to accidentally create an copy of the value at `t[key]`.
+  ## Remember that seqs and strings are value types, and therefore
+  ## cannot be copied into a separate variable for modification.
+  ## See the example below.
+  ##
   ## See also:
   ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
   ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
@@ -964,29 +1014,53 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
 
+    # An example of accidentally creating a copy
+    var t = newTable[int, seq[int]]()
+    # In this example, we expect t[10] to be modified,
+    # but it is not.
+    var copiedSeq = t.mgetOrPut(10, @[10])
+    copiedSeq.add(20)
+    doAssert t[10] == @[10]
+    # Correct
+    t.mgetOrPut(25, @[25]).add(35)
+    doAssert t[25] == @[25, 35]
   t[].mgetOrPut(key, val)
 
+proc mgetOrPut*[A, B](t: TableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
+
+  t[].mgetOrPut(key)
+
 proc len*[A, B](t: TableRef[A, B]): int =
-  ## Returns the number of keys in ``t``.
+  ## Returns the number of keys in `t`.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newTable
     doAssert len(a) == 2
 
   result = t.counter
 
-proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
-  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists.
   ##
   ## **This can introduce duplicate keys into the table!**
   ##
-  ## Use `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new
+  ## Use `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new
   ## (key, value) pair in the table without introducing duplicates.
   t[].add(key, val)
 
 proc del*[A, B](t: TableRef[A, B], key: A) =
-  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ## Deletes `key` from hash table `t`. Does nothing if the key does not exist.
   ##
-  ## **If duplicate keys were added, this may need to be called multiple times.**
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
   ##
   ## See also:
   ## * `pop proc<#pop,TableRef[A,B],A,B>`_
@@ -1001,12 +1075,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
   t[].del(key)
 
 proc pop*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
-  ## **If duplicate keys were added, this may need to be called multiple times.**
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
   ##
   ## See also:
   ## * `del proc<#del,TableRef[A,B],A>`_
@@ -1045,13 +1120,13 @@ proc clear*[A, B](t: TableRef[A, B]) =
   clearImpl()
 
 proc `$`*[A, B](t: TableRef[A, B]): string =
-  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## The `$` operator for hash tables. Used internally when calling `echo`
   ## on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: TableRef[A, B]): bool =
-  ## The ``==`` operator for hash tables. Returns ``true`` if either both tables
-  ## are ``nil``, or neither is ``nil`` and the content of both tables contains the
+  ## The `==` operator for hash tables. Returns `true` if either both tables
+  ## are `nil`, or neither is `nil` and the content of both tables contains the
   ## same key-value pairs. Insert order does not matter.
   runnableExamples:
     let
@@ -1066,7 +1141,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
 
 
 iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ## Iterates over any `(key, value)` pair in the table `t`.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
@@ -1075,7 +1150,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -1089,6 +1164,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
   ##   # value: [2, 4, 6, 8]
   ##   # key: o
   ##   # value: [1, 5, 7, 9]
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if isFilled(t.data[h].hcode):
@@ -1096,7 +1172,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values
+  ## Iterates over any `(key, value)` pair in the table `t`. The values
   ## can be modified.
   ##
   ## See also:
@@ -1117,8 +1193,8 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
       yield (t.data[h].key, t.data[h].val)
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator keys*[A, B](t: TableRef[A, B]): A =
-  ## Iterates over any key in the table ``t``.
+iterator keys*[A, B](t: TableRef[A, B]): lent A =
+  ## Iterates over any key in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
@@ -1138,8 +1214,8 @@ iterator keys*[A, B](t: TableRef[A, B]): A =
       yield t.data[h].key
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator values*[A, B](t: TableRef[A, B]): B =
-  ## Iterates over any value in the table ``t``.
+iterator values*[A, B](t: TableRef[A, B]): lent B =
+  ## Iterates over any value in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
@@ -1160,7 +1236,7 @@ iterator values*[A, B](t: TableRef[A, B]): B =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A, B](t: TableRef[A, B]): var B =
-  ## Iterates over any value in the table ``t``. The values can be modified.
+  ## Iterates over any value in the table `t`. The values can be modified.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
@@ -1199,14 +1275,14 @@ type
     ## Hash table that remembers insertion order.
     ##
     ## For creating an empty OrderedTable, use `initOrderedTable proc
-    ## <#initOrderedTable,int>`_.
+    ## <#initOrderedTable>`_.
     data: OrderedKeyValuePairSeq[A, B]
     counter, first, last: int
   OrderedTableRef*[A, B] = ref OrderedTable[A, B] ## Ref version of
     ## `OrderedTable<#OrderedTable>`_.
     ##
     ## For creating a new empty OrderedTableRef, use `newOrderedTable proc
-    ## <#newOrderedTable,int>`_.
+    ## <#newOrderedTable>`_.
 
 
 # ------------------------------ helpers ---------------------------------
@@ -1222,7 +1298,7 @@ proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
 
 proc rawInsert[A, B](t: var OrderedTable[A, B],
                      data: var OrderedKeyValuePairSeq[A, B],
-                     key: A, val: B, hc: Hash, h: Hash) =
+                     key: A, val: sink B, hc: Hash, h: Hash) =
   rawInsertImpl()
   data[h].next = -1
   if t.first < 0: t.first = h
@@ -1260,27 +1336,22 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
 proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, B] =
   ## Creates a new ordered hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
   ## See also:
   ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
-  ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
+  ## * `newOrderedTable proc<#newOrderedTable>`_ for creating an
   ##   `OrderedTableRef`
   runnableExamples:
     let
       a = initOrderedTable[int, string]()
       b = initOrderedTable[char, seq[int]]()
+  result = default(OrderedTable[A, B])
   initImpl(result, initialSize)
 
-proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) =
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
@@ -1296,12 +1367,12 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
   putImpl(enlarge)
 
 proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
-  ## Creates a new ordered hash table that contains the given ``pairs``.
+  ## Creates a new ordered hash table that contains the given `pairs`.
   ##
-  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ## `pairs` is a container consisting of `(key, value)` tuples.
   ##
   ## See also:
-  ## * `initOrderedTable proc<#initOrderedTable,int>`_
+  ## * `initOrderedTable proc<#initOrderedTable>`_
   ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for an
   ##   `OrderedTableRef` version
   runnableExamples:
@@ -1309,13 +1380,13 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
     let b = toOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.toOrderedTable
 
-  result = initOrderedTable[A, B](rightSize(pairs.len))
+  result = initOrderedTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
-proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]``.
+proc `[]`*[A, B](t: OrderedTable[A, B], key: A): lent B =
+  ## Retrieves the value at `t[key]`.
   ##
-  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the  `KeyError` exception is raised.
   ## One can check with `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ whether
   ## the key exists.
   ##
@@ -1324,7 +1395,7 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a
   ##   key is in the table
@@ -1337,23 +1408,23 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
   get(t, key)
 
 proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B =
-  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ## Retrieves the value at `t[key]`. The value can be modified.
   ##
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   ##
   ## See also:
   ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a
   ##   key is in the table
   get(t, key)
 
 proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,OrderedTable[A,B],A>`_ for use with the `in`
@@ -1368,12 +1439,12 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
 
-  var hc: Hash
+  var hc: Hash = default(Hash)
   result = rawGet(t, key, hc) >= 0
 
 proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert 'b' in a == true
@@ -1382,7 +1453,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
-  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   ##
   ## See also:
   ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
@@ -1402,8 +1473,8 @@ proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
   hasKeyOrPutImpl(enlarge)
 
 proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## default initialization value for type `B` is returned (e.g. 0 for any
   ## integer type).
   ##
   ## See also:
@@ -1417,12 +1488,12 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
-
+  result = default(B)
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise, ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise, `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
@@ -1435,11 +1506,11 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
-
+  result = default(B)
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
-  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at `t[key]` or puts `val` if not present, either way
   ## returning a value which can be modified.
   ##
   ## See also:
@@ -1458,25 +1529,38 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
 
   mgetOrPutImpl(enlarge)
 
+proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.toOrderedTable
+
+  mgetOrPutImpl(enlarge)
+
 proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
-  ## Returns the number of keys in ``t``.
+  ## Returns the number of keys in `t`.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert len(a) == 2
 
   result = t.counter
 
-proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
-  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists.
   ##
   ## **This can introduce duplicate keys into the table!**
   ##
-  ## Use `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ## Use `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new
   ## (key, value) pair in the table without introducing duplicates.
   addImpl(enlarge)
 
 proc del*[A, B](t: var OrderedTable[A, B], key: A) =
-  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ## Deletes `key` from hash table `t`. Does nothing if the key does not exist.
   ##
   ## O(n) complexity.
   ##
@@ -1509,9 +1593,9 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) =
     h = nxt
 
 proc pop*[A, B](t: var OrderedTable[A, B], key: A, val: var B): bool {.since: (1, 1).} =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
   ## O(n) complexity.
@@ -1555,15 +1639,15 @@ proc clear*[A, B](t: var OrderedTable[A, B]) =
   t.last = -1
 
 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int,
-    order = SortOrder.Ascending) =
-  ## Sorts ``t`` according to the function ``cmp``.
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
+  ## Sorts `t` according to the function `cmp`.
   ##
   ## This modifies the internal list
   ## that kept the insertion order, so insertion order is lost after this
-  ## call but key lookup and insertions remain possible after ``sort`` (in
+  ## call but key lookup and insertions remain possible after `sort` (in
   ## contrast to the `sort proc<#sort,CountTable[A]>`_ for count tables).
   runnableExamples:
-    import algorithm
+    import std/[algorithm]
     var a = initOrderedTable[char, int]()
     for i, c in "cab":
       a[c] = 10*i
@@ -1614,12 +1698,12 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int,
   t.last = tail
 
 proc `$`*[A, B](t: OrderedTable[A, B]): string =
-  ## The ``$`` operator for ordered hash tables. Used internally when calling
+  ## The `$` operator for ordered hash tables. Used internally when calling
   ## `echo` on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
-  ## The ``==`` operator for ordered hash tables. Returns ``true`` if both the
+  ## The `==` operator for ordered hash tables. Returns `true` if both the
   ## content and the order are equal.
   runnableExamples:
     let
@@ -1629,6 +1713,8 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
 
   if s.counter != t.counter:
     return false
+  if s.counter == 0 and t.counter == 0:
+    return true
   var ht = t.first
   var hs = s.first
   while ht >= 0 and hs >= 0:
@@ -1644,7 +1730,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
 
 
 iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## Iterates over any `(key, value)` pair in the table `t` in insertion
   ## order.
   ##
   ## See also:
@@ -1654,7 +1740,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -1668,6 +1754,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
   ##   # value: [1, 5, 7, 9]
   ##   # key: e
   ##   # value: [2, 4, 6, 8]
+  ##   ```
 
   let L = len(t)
   forAllOrderedPairs:
@@ -1675,7 +1762,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## Iterates over any `(key, value)` pair in the table `t` (must be
   ## declared as `var`) in insertion order. The values can be modified.
   ##
   ## See also:
@@ -1696,8 +1783,8 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
     yield (t.data[h].key, t.data[h].val)
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator keys*[A, B](t: OrderedTable[A, B]): A =
-  ## Iterates over any key in the table ``t`` in insertion order.
+iterator keys*[A, B](t: OrderedTable[A, B]): lent A =
+  ## Iterates over any key in the table `t` in insertion order.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
@@ -1717,8 +1804,8 @@ iterator keys*[A, B](t: OrderedTable[A, B]): A =
     yield t.data[h].key
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator values*[A, B](t: OrderedTable[A, B]): B =
-  ## Iterates over any value in the table ``t`` in insertion order.
+iterator values*[A, B](t: OrderedTable[A, B]): lent B =
+  ## Iterates over any value in the table `t` in insertion order.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
@@ -1738,7 +1825,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): B =
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
-  ## Iterates over any value in the table ``t`` (must be
+  ## Iterates over any value in the table `t` (must be
   ## declared as `var`) in insertion order. The values
   ## can be modified.
   ##
@@ -1760,42 +1847,33 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
     yield t.data[h].val
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
-
-
-
-
 # ---------------------------------------------------------------------------
 # --------------------------- OrderedTableRef -------------------------------
 # ---------------------------------------------------------------------------
 
-proc newOrderedTable*[A, B](initialSize = defaultInitialSize): <//>OrderedTableRef[A, B] =
+proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] =
   ## Creates a new ordered ref hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating
   ##   an `OrderedTableRef` from a collection of `(key, value)` pairs
-  ## * `initOrderedTable proc<#initOrderedTable,int>`_ for creating an
+  ## * `initOrderedTable proc<#initOrderedTable>`_ for creating an
   ##   `OrderedTable`
   runnableExamples:
     let
       a = newOrderedTable[int, string]()
       b = newOrderedTable[char, seq[int]]()
   new(result)
-  result[] = initOrderedTable[A, B](initialSize)
+  {.noSideEffect.}:
+    result[] = initOrderedTable[A, B](initialSize)
 
-proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B] =
-  ## Creates a new ordered ref hash table that contains the given ``pairs``.
+proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
+  ## Creates a new ordered ref hash table that contains the given `pairs`.
   ##
-  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ## `pairs` is a container consisting of `(key, value)` tuples.
   ##
   ## See also:
-  ## * `newOrderedTable proc<#newOrderedTable,int>`_
+  ## * `newOrderedTable proc<#newOrderedTable>`_
   ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ for an
   ##   `OrderedTable` version
   runnableExamples:
@@ -1803,14 +1881,15 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B]
     let b = newOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.newOrderedTable
 
-  result = newOrderedTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result.add(key, val)
+  result = newOrderedTable[A, B](pairs.len)
+  {.noSideEffect.}:
+    for key, val in items(pairs): result[key] = val
 
 
 proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
-  ## Retrieves the value at ``t[key]``.
+  ## Retrieves the value at `t[key]`.
   ##
-  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## If `key` is not in `t`, the  `KeyError` exception is raised.
   ## One can check with `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ whether
   ## the key exists.
   ##
@@ -1819,7 +1898,7 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
   ##   a default value (e.g. zero for int) if the key doesn't exist
   ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new
+  ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for checking if
   ##   a key is in the table
@@ -1830,8 +1909,8 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
       echo a['z']
   result = t[][key]
 
-proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) =
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
@@ -1847,7 +1926,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
   t[][key] = val
 
 proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,OrderedTableRef[A,B],A>`_ for use with the `in`
@@ -1866,7 +1945,7 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
 
 proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert 'b' in a == true
@@ -1874,8 +1953,8 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
 
   return hasKey[A, B](t, key)
 
-proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
-  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+proc hasKeyOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): bool =
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   ##
   ## See also:
   ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
@@ -1895,8 +1974,8 @@ proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
   result = t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## default initialization value for type `B` is returned (e.g. 0 for any
   ## integer type).
   ##
   ## See also:
@@ -1914,8 +1993,8 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise, ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise, `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
@@ -1932,7 +2011,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
-  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at `t[key]` or puts `val` if not present, either way
   ## returning a value which can be modified.
   ##
   ## See also:
@@ -1951,25 +2030,38 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
 
   result = t[].mgetOrPut(key, val)
 
+proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.toOrderedTable
+
+  t[].mgetOrPut(key)
+
 proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
-  ## Returns the number of keys in ``t``.
+  ## Returns the number of keys in `t`.
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert len(a) == 2
 
   result = t.counter
 
-proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists.
   ##
   ## **This can introduce duplicate keys into the table!**
   ##
-  ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new
+  ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new
   ## (key, value) pair in the table without introducing duplicates.
   t[].add(key, val)
 
 proc del*[A, B](t: OrderedTableRef[A, B], key: A) =
-  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ## Deletes `key` from hash table `t`. Does nothing if the key does not exist.
   ##
   ## See also:
   ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table
@@ -1983,9 +2075,9 @@ proc del*[A, B](t: OrderedTableRef[A, B], key: A) =
   t[].del(key)
 
 proc pop*[A, B](t: OrderedTableRef[A, B], key: A, val: var B): bool {.since: (1, 1).} =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
   ## See also:
@@ -2019,15 +2111,15 @@ proc clear*[A, B](t: OrderedTableRef[A, B]) =
   clear(t[])
 
 proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int,
-    order = SortOrder.Ascending) =
-  ## Sorts ``t`` according to the function ``cmp``.
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
+  ## Sorts `t` according to the function `cmp`.
   ##
   ## This modifies the internal list
   ## that kept the insertion order, so insertion order is lost after this
-  ## call but key lookup and insertions remain possible after ``sort`` (in
+  ## call but key lookup and insertions remain possible after `sort` (in
   ## contrast to the `sort proc<#sort,CountTableRef[A]>`_ for count tables).
   runnableExamples:
-    import algorithm
+    import std/[algorithm]
     var a = newOrderedTable[char, int]()
     for i, c in "cab":
       a[c] = 10*i
@@ -2040,13 +2132,13 @@ proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int,
   t[].sort(cmp, order = order)
 
 proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
-  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## The `$` operator for hash tables. Used internally when calling `echo`
   ## on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
-  ## The ``==`` operator for ordered hash tables. Returns true if either both
-  ## tables are ``nil``, or neither is ``nil`` and the content and the order of
+  ## The `==` operator for ordered hash tables. Returns true if either both
+  ## tables are `nil`, or neither is `nil` and the content and the order of
   ## both are equal.
   runnableExamples:
     let
@@ -2061,7 +2153,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
 
 
 iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## Iterates over any `(key, value)` pair in the table `t` in insertion
   ## order.
   ##
   ## See also:
@@ -2071,7 +2163,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -2085,6 +2177,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
   ##   # value: [1, 5, 7, 9]
   ##   # key: e
   ##   # value: [2, 4, 6, 8]
+  ##   ```
 
   let L = len(t)
   forAllOrderedPairs:
@@ -2092,7 +2185,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## Iterates over any `(key, value)` pair in the table `t` in insertion
   ## order. The values can be modified.
   ##
   ## See also:
@@ -2113,8 +2206,8 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
     yield (t.data[h].key, t.data[h].val)
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
-  ## Iterates over any key in the table ``t`` in insertion order.
+iterator keys*[A, B](t: OrderedTableRef[A, B]): lent A =
+  ## Iterates over any key in the table `t` in insertion order.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
@@ -2134,8 +2227,8 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
     yield t.data[h].key
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator values*[A, B](t: OrderedTableRef[A, B]): B =
-  ## Iterates over any value in the table ``t`` in insertion order.
+iterator values*[A, B](t: OrderedTableRef[A, B]): lent B =
+  ## Iterates over any value in the table `t` in insertion order.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
@@ -2155,7 +2248,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): B =
     assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
-  ## Iterates over any value in the table ``t`` in insertion order. The values
+  ## Iterates over any value in the table `t` in insertion order. The values
   ## can be modified.
   ##
   ## See also:
@@ -2191,7 +2284,7 @@ type
     ## Hash table that counts the number of each key.
     ##
     ## For creating an empty CountTable, use `initCountTable proc
-    ## <#initCountTable,int>`_.
+    ## <#initCountTable>`_.
     data: seq[tuple[key: A, val: int]]
     counter: int
     isSorted: bool
@@ -2199,7 +2292,7 @@ type
     ## `CountTable<#CountTable>`_.
     ##
     ## For creating a new empty CountTableRef, use `newCountTable proc
-    ## <#newCountTable,int>`_.
+    ## <#newCountTable>`_.
 
 
 # ------------------------------ helpers ---------------------------------
@@ -2218,19 +2311,6 @@ proc enlarge[A](t: var CountTable[A]) =
     if t.data[i].val != 0: ctRawInsert(t, n, move t.data[i].key, move t.data[i].val)
   swap(t.data, n)
 
-proc remove[A](t: var CountTable[A], key: A) =
-  var n: seq[tuple[key: A, val: int]]
-  newSeq(n, len(t.data))
-  var removed: bool
-  for i in countup(0, high(t.data)):
-    if t.data[i].val != 0:
-      if t.data[i].key != key:
-        ctRawInsert(t, n, move t.data[i].key, move t.data[i].val)
-      else:
-        removed = true
-  swap(t.data, n)
-  if removed: dec(t.counter)
-
 proc rawGet[A](t: CountTable[A], key: A): int =
   if t.data.len == 0:
     return -1
@@ -2244,42 +2324,36 @@ template ctget(t, key, default: untyped): untyped =
   var index = rawGet(t, key)
   result = if index >= 0: t.data[index].val else: default
 
-proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1)
+proc inc*[A](t: var CountTable[A], key: A, val = 1)
 
 # ----------------------------------------------------------------------
 
 proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] =
   ## Creates a new count table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
   ## See also:
   ## * `toCountTable proc<#toCountTable,openArray[A]>`_
-  ## * `newCountTable proc<#newCountTable,int>`_ for creating a
+  ## * `newCountTable proc<#newCountTable>`_ for creating a
   ##   `CountTableRef`
+  result = default(CountTable[A])
   initImpl(result, initialSize)
 
 proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
-  ## Creates a new count table with every member of a container ``keys``
+  ## Creates a new count table with every member of a container `keys`
   ## having a count of how many times it occurs in that container.
-  result = initCountTable[A](rightSize(keys.len))
+  result = initCountTable[A](keys.len)
   for key in items(keys): result.inc(key)
 
 proc `[]`*[A](t: CountTable[A], key: A): int =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise ``0`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise `0` is returned.
   ##
   ## See also:
   ## * `getOrDefault<#getOrDefault,CountTable[A],A,int>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `mget proc<#mget,CountTable[A],A>`_
   ## * `[]= proc<#[]%3D,CountTable[A],A,int>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key
@@ -2287,17 +2361,21 @@ proc `[]`*[A](t: CountTable[A], key: A): int =
   assert(not t.isSorted, "CountTable must not be used after sorting")
   ctget(t, key, 0)
 
+template cntMakeEmpty(i) = t.data[i].val = 0
+template cntCellEmpty(i) = t.data[i].val == 0
+template cntCellHash(i)  = hash(t.data[i].key)
+
 proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
-  ## * `inc proc<#inc,CountTable[A],A,Positive>`_ for incrementing a
+  ## * `inc proc<#inc,CountTable[A],A,int>`_ for incrementing a
   ##   value of a key
   assert(not t.isSorted, "CountTable must not be used after sorting")
   assert val >= 0
   if val == 0:
-    t.remove(key)
+    delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
   else:
     let h = rawGet(t, key)
     if h >= 0:
@@ -2305,11 +2383,8 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
     else:
       insertImpl()
 
-proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) =
-  ## Increments ``t[key]`` by ``val`` (default: 1).
-  ##
-  ## ``val`` must be a positive number. If you need to decrement a value,
-  ## use a regular ``Table`` instead.
+proc inc*[A](t: var CountTable[A], key: A, val = 1) =
+  ## Increments `t[key]` by `val` (default: 1).
   runnableExamples:
     var a = toCountTable("aab")
     a.inc('a')
@@ -2320,16 +2395,22 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) =
   var index = rawGet(t, key)
   if index >= 0:
     inc(t.data[index].val, val)
-    if t.data[index].val == 0: dec(t.counter)
+    if t.data[index].val == 0:
+      delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
   else:
-    insertImpl()
+    if val != 0:
+      insertImpl()
+
+proc len*[A](t: CountTable[A]): int =
+  ## Returns the number of keys in `t`.
+  result = t.counter
 
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
+  ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n)
   ##
   ## See also:
   ## * `largest proc<#largest,CountTable[A]>`_
-  assert t.len > 0
+  assert t.len > 0, "counttable is empty"
   var minIdx = -1
   for h in 0 .. high(t.data):
     if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val):
@@ -2338,11 +2419,11 @@ proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   result.val = t.data[minIdx].val
 
 proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
+  ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n)
   ##
   ## See also:
   ## * `smallest proc<#smallest,CountTable[A]>`_
-  assert t.len > 0
+  assert t.len > 0, "counttable is empty"
   var maxIdx = 0
   for h in 1 .. high(t.data):
     if t.data[maxIdx].val < t.data[h].val: maxIdx = h
@@ -2350,7 +2431,7 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   result.val = t.data[maxIdx].val
 
 proc hasKey*[A](t: CountTable[A], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,CountTable[A],A>`_ for use with the `in`
@@ -2363,12 +2444,12 @@ proc hasKey*[A](t: CountTable[A], key: A): bool =
 
 proc contains*[A](t: CountTable[A], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,CountTable[A],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   return hasKey[A](t, key)
 
 proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int =
-  ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## integer value of `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
@@ -2376,14 +2457,8 @@ proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int =
   ##   is in the table
   ctget(t, key, default)
 
-proc len*[A](t: CountTable[A]): int =
-  ## Returns the number of keys in ``t``.
-  result = t.counter
-
 proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} =
-  ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist.
-  ##
-  ## O(n) complexity.
+  ## Deletes `key` from table `t`. Does nothing if the key does not exist.
   ##
   ## See also:
   ## * `pop proc<#pop,CountTable[A],A,int>`_
@@ -2397,16 +2472,14 @@ proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} =
     a.del('c')
     assert a == toCountTable("aa")
 
-  remove(t, key)
+  delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
 
 proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
-  ## O(n) complexity.
-  ##
   ## See also:
   ## * `del proc<#del,CountTable[A],A>`_
   ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table
@@ -2423,7 +2496,7 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).}
   result = index >= 0
   if result:
     val = move(t.data[index].val)
-    remove(t, key)
+    delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
 
 proc clear*[A](t: var CountTable[A]) =
   ## Resets the table so that it is empty.
@@ -2441,13 +2514,13 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) =
   ## Sorts the count table so that, by default, the entry with the
   ## highest counter comes first.
   ##
-  ## **WARNING:** This is destructive! Once sorted, you must not modify ``t`` afterwards!
+  ## .. warning:: This is destructive! Once sorted, you must not modify `t` afterwards!
   ##
   ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_,
   ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_
-  ## to iterate over ``t`` in the sorted order.
+  ## to iterate over `t` in the sorted order.
   runnableExamples:
-    import algorithm, sequtils
+    import std/[algorithm, sequtils]
     var a = toCountTable("abracadabra")
     doAssert a == "aaaaabbrrcd".toCountTable
     a.sort()
@@ -2485,18 +2558,18 @@ when (NimMajor, NimMinor) <= (1, 0):
         result.inc(key, value)
 
 proc `$`*[A](t: CountTable[A]): string =
-  ## The ``$`` operator for count tables. Used internally when calling `echo`
+  ## The `$` operator for count tables. Used internally when calling `echo`
   ## on a table.
   dollarImpl()
 
 proc `==`*[A](s, t: CountTable[A]): bool =
-  ## The ``==`` operator for count tables. Returns ``true`` if both tables
+  ## The `==` operator for count tables. Returns `true` if both tables
   ## contain the same keys with the same count. Insert order does not matter.
   equalsImpl(s, t)
 
 
 iterator pairs*[A](t: CountTable[A]): (A, int) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ## Iterates over any `(key, value)` pair in the table `t`.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_
@@ -2505,7 +2578,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = toCountTable("abracadabra")
   ##
   ##   for k, v in pairs(a):
@@ -2522,6 +2595,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) =
   ##   # value: 1
   ##   # key: r
   ##   # value: 2
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if t.data[h].val != 0:
@@ -2529,7 +2603,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## Iterates over any `(key, value)` pair in the table `t` (must be
   ## declared as `var`). The values can be modified.
   ##
   ## See also:
@@ -2547,8 +2621,8 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
       yield (t.data[h].key, t.data[h].val)
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
-iterator keys*[A](t: CountTable[A]): A =
-  ## Iterates over any key in the table ``t``.
+iterator keys*[A](t: CountTable[A]): lent A =
+  ## Iterates over any key in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,CountTable[A]>`_
@@ -2566,7 +2640,7 @@ iterator keys*[A](t: CountTable[A]): A =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator values*[A](t: CountTable[A]): int =
-  ## Iterates over any value in the table ``t``.
+  ## Iterates over any value in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,CountTable[A]>`_
@@ -2584,7 +2658,7 @@ iterator values*[A](t: CountTable[A]): int =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A](t: var CountTable[A]): var int =
-  ## Iterates over any value in the table ``t`` (must be
+  ## Iterates over any value in the table `t` (must be
   ## declared as `var`). The values can be modified.
   ##
   ## See also:
@@ -2614,37 +2688,33 @@ iterator mvalues*[A](t: var CountTable[A]): var int =
 
 proc inc*[A](t: CountTableRef[A], key: A, val = 1)
 
-proc newCountTable*[A](initialSize = defaultInitialSize): <//>CountTableRef[A] =
+proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] =
   ## Creates a new ref count table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating
   ##   a `CountTableRef` from a collection
-  ## * `initCountTable proc<#initCountTable,int>`_ for creating a
+  ## * `initCountTable proc<#initCountTable>`_ for creating a
   ##   `CountTable`
   new(result)
-  result[] = initCountTable[A](initialSize)
+  {.noSideEffect.}:
+    result[] = initCountTable[A](initialSize)
 
-proc newCountTable*[A](keys: openArray[A]): <//>CountTableRef[A] =
-  ## Creates a new ref count table with every member of a container ``keys``
+proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
+  ## Creates a new ref count table with every member of a container `keys`
   ## having a count of how many times it occurs in that container.
-  result = newCountTable[A](rightSize(keys.len))
-  for key in items(keys): result.inc(key)
+  result = newCountTable[A](keys.len)
+  {.noSideEffect.}:
+    for key in items(keys): result.inc(key)
 
 proc `[]`*[A](t: CountTableRef[A], key: A): int =
-  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
-  ## Otherwise ``0`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`.
+  ## Otherwise `0` is returned.
   ##
   ## See also:
   ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return
   ##   a custom value if the key doesn't exist
-  ## * `mget proc<#mget,CountTableRef[A],A>`_
+  ## * `inc proc<#inc,CountTableRef[A],A,int>`_ to inc even if missing
   ## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new
   ##   (key, value) pair in the table
   ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key
@@ -2652,40 +2722,42 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int =
   result = t[][key]
 
 proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
-  ## Inserts a ``(key, value)`` pair into ``t``.
+  ## Inserts a `(key, value)` pair into `t`.
   ##
   ## See also:
   ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
   ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a
   ##   value of a key
   assert val > 0
-  t[][key] = val
+  {.noSideEffect.}:
+    t[][key] = val
 
 proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
-  ## Increments ``t[key]`` by ``val`` (default: 1).
+  ## Increments `t[key]` by `val` (default: 1).
   runnableExamples:
     var a = newCountTable("aab")
     a.inc('a')
     a.inc('b', 10)
     doAssert a == newCountTable("aaabbbbbbbbbbb")
-  t[].inc(key, val)
+  {.noSideEffect.}:
+    t[].inc(key, val)
 
 proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
-  ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
+  ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n)
   ##
   ## See also:
   ## * `largest proc<#largest,CountTableRef[A]>`_
   t[].smallest
 
 proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
-  ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
+  ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n)
   ##
   ## See also:
   ## * `smallest proc<#smallest,CountTable[A]>`_
   t[].largest
 
 proc hasKey*[A](t: CountTableRef[A], key: A): bool =
-  ## Returns true if ``key`` is in the table ``t``.
+  ## Returns true if `key` is in the table `t`.
   ##
   ## See also:
   ## * `contains proc<#contains,CountTableRef[A],A>`_ for use with the `in`
@@ -2697,12 +2769,12 @@ proc hasKey*[A](t: CountTableRef[A], key: A): bool =
 
 proc contains*[A](t: CountTableRef[A], key: A): bool =
   ## Alias of `hasKey proc<#hasKey,CountTableRef[A],A>`_ for use with
-  ## the ``in`` operator.
+  ## the `in` operator.
   return hasKey[A](t, key)
 
 proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
-  ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
+  ## integer value of `default` is returned.
   ##
   ## See also:
   ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
@@ -2711,13 +2783,11 @@ proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
   result = t[].getOrDefault(key, default)
 
 proc len*[A](t: CountTableRef[A]): int =
-  ## Returns the number of keys in ``t``.
+  ## Returns the number of keys in `t`.
   result = t.counter
 
 proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} =
-  ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist.
-  ##
-  ## O(n) complexity.
+  ## Deletes `key` from table `t`. Does nothing if the key does not exist.
   ##
   ## See also:
   ## * `pop proc<#pop,CountTableRef[A],A,int>`_
@@ -2725,13 +2795,11 @@ proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} =
   del(t[], key)
 
 proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).} =
-  ## Deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## Deletes the `key` from the table.
+  ## Returns `true`, if the `key` existed, and sets `val` to the
+  ## mapping of the key. Otherwise, returns `false`, and the `val` is
   ## unchanged.
   ##
-  ## O(n) complexity.
-  ##
   ## See also:
   ## * `del proc<#del,CountTableRef[A],A>`_
   ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table
@@ -2753,7 +2821,7 @@ proc sort*[A](t: CountTableRef[A], order = SortOrder.Descending) =
   ##
   ## You can use the iterators `pairs<#pairs.i,CountTableRef[A]>`_,
   ## `keys<#keys.i,CountTableRef[A]>`_, and `values<#values.i,CountTableRef[A]>`_
-  ## to iterate over ``t`` in the sorted order.
+  ## to iterate over `t` in the sorted order.
   t[].sort(order = order)
 
 proc merge*[A](s, t: CountTableRef[A]) =
@@ -2768,13 +2836,13 @@ proc merge*[A](s, t: CountTableRef[A]) =
   s[].merge(t[])
 
 proc `$`*[A](t: CountTableRef[A]): string =
-  ## The ``$`` operator for count tables. Used internally when calling `echo`
+  ## The `$` operator for count tables. Used internally when calling `echo`
   ## on a table.
   dollarImpl()
 
 proc `==`*[A](s, t: CountTableRef[A]): bool =
-  ## The ``==`` operator for count tables. Returns ``true`` if either both tables
-  ## are ``nil``, or neither is ``nil`` and both contain the same keys with the same
+  ## The `==` operator for count tables. Returns `true` if either both tables
+  ## are `nil`, or neither is `nil` and both contain the same keys with the same
   ## count. Insert order does not matter.
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
@@ -2782,7 +2850,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool =
 
 
 iterator pairs*[A](t: CountTableRef[A]): (A, int) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ## Iterates over any `(key, value)` pair in the table `t`.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
@@ -2791,7 +2859,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = newCountTable("abracadabra")
   ##
   ##   for k, v in pairs(a):
@@ -2808,6 +2876,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) =
   ##   # value: 1
   ##   # key: r
   ##   # value: 2
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if t.data[h].val != 0:
@@ -2815,7 +2884,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
-  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values can
+  ## Iterates over any `(key, value)` pair in the table `t`. The values can
   ## be modified.
   ##
   ## See also:
@@ -2834,7 +2903,7 @@ iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
       assert(len(t) == L, "table modified while iterating over it")
 
 iterator keys*[A](t: CountTableRef[A]): A =
-  ## Iterates over any key in the table ``t``.
+  ## Iterates over any key in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,CountTable[A]>`_
@@ -2852,7 +2921,7 @@ iterator keys*[A](t: CountTableRef[A]): A =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator values*[A](t: CountTableRef[A]): int =
-  ## Iterates over any value in the table ``t``.
+  ## Iterates over any value in the table `t`.
   ##
   ## See also:
   ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_
@@ -2870,7 +2939,7 @@ iterator values*[A](t: CountTableRef[A]): int =
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A](t: CountTableRef[A]): var int =
-  ## Iterates over any value in the table ``t``. The values can be modified.
+  ## Iterates over any value in the table `t`. The values can be modified.
   ##
   ## See also:
   ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
@@ -2887,316 +2956,17 @@ iterator mvalues*[A](t: CountTableRef[A]): var int =
       yield t.data[h].val
       assert(len(t) == L, "the length of the table changed while iterating over it")
 
+proc hash*[K,V](s: Table[K,V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result
 
+proc hash*[K,V](s: OrderedTable[K,V]): Hash =
+  for p in pairs(s):
+    result = result !& hash(p)
+  result = !$result
 
-
-when isMainModule:
-  type
-    Person = object
-      firstName, lastName: string
-
-  proc hash(x: Person): Hash =
-    ## Piggyback on the already available string hash proc.
-    ##
-    ## Without this proc nothing works!
-    result = x.firstName.hash !& x.lastName.hash
-    result = !$result
-
-  var
-    salaries = initTable[Person, int]()
-    p1, p2: Person
-  p1.firstName = "Jon"
-  p1.lastName = "Ross"
-  salaries[p1] = 30_000
-  p2.firstName = "소진"
-  p2.lastName = "박"
-  salaries[p2] = 45_000
-  var
-    s2 = initOrderedTable[Person, int]()
-    s3 = initCountTable[Person]()
-  s2[p1] = 30_000
-  s2[p2] = 45_000
-  s3[p1] = 30_000
-  s3[p2] = 45_000
-
-  block: # Ordered table should preserve order after deletion
-    var
-      s4 = initOrderedTable[int, int]()
-    s4[1] = 1
-    s4[2] = 2
-    s4[3] = 3
-
-    var prev = 0
-    for i in s4.values:
-      doAssert(prev < i)
-      prev = i
-
-    s4.del(2)
-    doAssert(2 notin s4)
-    doAssert(s4.len == 2)
-    prev = 0
-    for i in s4.values:
-      doAssert(prev < i)
-      prev = i
-
-  block: # Deletion from OrderedTable should account for collision groups. See issue #5057.
-    # The bug is reproducible only with exact keys
-    const key1 = "boy_jackpot.inGamma"
-    const key2 = "boy_jackpot.outBlack"
-
-    var t = {
-        key1: 0,
-        key2: 0
-    }.toOrderedTable()
-
-    t.del(key1)
-    assert(t.len == 1)
-    assert(key2 in t)
-
-  var
-    t1 = initCountTable[string]()
-    t2 = initCountTable[string]()
-  t1.inc("foo")
-  t1.inc("bar", 2)
-  t1.inc("baz", 3)
-  t2.inc("foo", 4)
-  t2.inc("bar")
-  t2.inc("baz", 11)
-  merge(t1, t2)
-  assert(t1["foo"] == 5)
-  assert(t1["bar"] == 3)
-  assert(t1["baz"] == 14)
-
-  let
-    t1r = newCountTable[string]()
-    t2r = newCountTable[string]()
-  t1r.inc("foo")
-  t1r.inc("bar", 2)
-  t1r.inc("baz", 3)
-  t2r.inc("foo", 4)
-  t2r.inc("bar")
-  t2r.inc("baz", 11)
-  merge(t1r, t2r)
-  assert(t1r["foo"] == 5)
-  assert(t1r["bar"] == 3)
-  assert(t1r["baz"] == 14)
-
-  var
-    t1l = initCountTable[string]()
-    t2l = initCountTable[string]()
-  t1l.inc("foo")
-  t1l.inc("bar", 2)
-  t1l.inc("baz", 3)
-  t2l.inc("foo", 4)
-  t2l.inc("bar")
-  t2l.inc("baz", 11)
-
-  block:
-    const testKey = "TESTKEY"
-    let t: CountTableRef[string] = newCountTable[string]()
-
-    # Before, does not compile with error message:
-    #test_counttable.nim(7, 43) template/generic instantiation from here
-    #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
-    #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
-    doAssert 0 == t[testKey]
-    t.inc(testKey, 3)
-    doAssert 3 == t[testKey]
-
-  block:
-    # Clear tests
-    var clearTable = newTable[int, string]()
-    clearTable[42] = "asd"
-    clearTable[123123] = "piuyqwb "
-    doAssert clearTable[42] == "asd"
-    clearTable.clear()
-    doAssert(not clearTable.hasKey(123123))
-    doAssert clearTable.getOrDefault(42) == ""
-
-  block: #5482
-    var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
-    var b = newOrderedTable[string, string](initialSize = 2)
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5482
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = newOrderedTable[string, string](initialSize = 2)
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5487
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = newOrderedTable[string, string]()         # notice, default size!
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5487
-    var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
-    var b = newOrderedTable[string, string]()         # notice, default size!
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block:
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
-    var c = newOrderedTable[string, string]()         # notice, default size!
-    c.add("wrong?", "foo")
-    c.add("wrong?", "foo2")
-    assert a == b
-    assert a == c
-
-  block: #6250
-    let
-      a = {3: 1}.toOrderedTable
-      b = {3: 2}.toOrderedTable
-    assert((a == b) == false)
-    assert((b == a) == false)
-
-  block: #6250
-    let
-      a = {3: 2}.toOrderedTable
-      b = {3: 2}.toOrderedTable
-    assert((a == b) == true)
-    assert((b == a) == true)
-
-  block: # CountTable.smallest
-    let t = toCountTable([0, 0, 5, 5, 5])
-    doAssert t.smallest == (0, 2)
-
-  block: #10065
-    let t = toCountTable("abracadabra")
-    doAssert t['z'] == 0
-
-    var t_mut = toCountTable("abracadabra")
-    doAssert t_mut['z'] == 0
-    # the previous read may not have modified the table.
-    doAssert t_mut.hasKey('z') == false
-    t_mut['z'] = 1
-    doAssert t_mut['z'] == 1
-    doAssert t_mut.hasKey('z') == true
-
-  block: #12813 #13079
-    var t = toCountTable("abracadabra")
-    doAssert len(t) == 5
-
-    t['a'] = 0 # remove a key
-    doAssert len(t) == 4
-
-  block:
-    var tp: Table[string, string] = initTable[string, string]()
-    doAssert "test1" == tp.getOrDefault("test1", "test1")
-    tp["test2"] = "test2"
-    doAssert "test2" == tp.getOrDefault("test2", "test1")
-    var tr: TableRef[string, string] = newTable[string, string]()
-    doAssert "test1" == tr.getOrDefault("test1", "test1")
-    tr["test2"] = "test2"
-    doAssert "test2" == tr.getOrDefault("test2", "test1")
-    var op: OrderedTable[string, string] = initOrderedTable[string, string]()
-    doAssert "test1" == op.getOrDefault("test1", "test1")
-    op["test2"] = "test2"
-    doAssert "test2" == op.getOrDefault("test2", "test1")
-    var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]()
-    doAssert "test1" == orf.getOrDefault("test1", "test1")
-    orf["test2"] = "test2"
-    doAssert "test2" == orf.getOrDefault("test2", "test1")
-
-  block tableWithoutInit:
-    var
-      a: Table[string, int]
-      b: Table[string, int]
-      c: Table[string, int]
-      d: Table[string, int]
-      e: Table[string, int]
-
-    a["a"] = 7
-    doAssert a.hasKey("a")
-    doAssert a.len == 1
-    doAssert a["a"] == 7
-    a["a"] = 9
-    doAssert a.len == 1
-    doAssert a["a"] == 9
-
-    doAssert b.hasKeyOrPut("b", 5) == false
-    doAssert b.hasKey("b")
-    doAssert b.hasKeyOrPut("b", 8)
-    doAssert b["b"] == 5
-
-    doAssert c.getOrDefault("a") == 0
-    doAssert c.getOrDefault("a", 3) == 3
-    c["a"] = 6
-    doAssert c.getOrDefault("a", 3) == 6
-
-    doAssert d.mgetOrPut("a", 3) == 3
-    doAssert d.mgetOrPut("a", 6) == 3
-
-    var x = 99
-    doAssert e.pop("a", x) == false
-    doAssert x == 99
-    e["a"] = 77
-    doAssert e.pop("a", x)
-    doAssert x == 77
-
-  block orderedTableWithoutInit:
-    var
-      a: OrderedTable[string, int]
-      b: OrderedTable[string, int]
-      c: OrderedTable[string, int]
-      d: OrderedTable[string, int]
-
-    a["a"] = 7
-    doAssert a.hasKey("a")
-    doAssert a.len == 1
-    doAssert a["a"] == 7
-    a["a"] = 9
-    doAssert a.len == 1
-    doAssert a["a"] == 9
-
-    doAssert b.hasKeyOrPut("b", 5) == false
-    doAssert b.hasKey("b")
-    doAssert b.hasKeyOrPut("b", 8)
-    doAssert b["b"] == 5
-
-    doAssert c.getOrDefault("a") == 0
-    doAssert c.getOrDefault("a", 3) == 3
-    c["a"] = 6
-    doAssert c.getOrDefault("a", 3) == 6
-
-    doAssert d.mgetOrPut("a", 3) == 3
-    doAssert d.mgetOrPut("a", 6) == 3
-
-  block countTableWithoutInit:
-    var
-      a: CountTable[string]
-      b: CountTable[string]
-      c: CountTable[string]
-      d: CountTable[string]
-      e: CountTable[string]
-
-    a["a"] = 7
-    doAssert a.hasKey("a")
-    doAssert a.len == 1
-    doAssert a["a"] == 7
-    a["a"] = 9
-    doAssert a.len == 1
-    doAssert a["a"] == 9
-
-    doAssert b["b"] == 0
-    b.inc("b")
-    doAssert b["b"] == 1
-
-    doAssert c.getOrDefault("a") == 0
-    doAssert c.getOrDefault("a", 3) == 3
-    c["a"] = 6
-    doAssert c.getOrDefault("a", 3) == 6
-
-    e["f"] = 3
-    merge(d, e)
-    doAssert d.hasKey("f")
-    d.inc("f")
-    merge(d, e)
-    doAssert d["f"] == 7
+proc hash*[V](s: CountTable[V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result