summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/algorithm.nim439
-rw-r--r--lib/pure/asyncdispatch.nim43
-rw-r--r--lib/pure/asyncftpclient.nim52
-rw-r--r--lib/pure/asynchttpserver.nim11
-rw-r--r--lib/pure/asyncmacro.nim20
-rw-r--r--lib/pure/base64.nim84
-rw-r--r--lib/pure/bitops.nim87
-rw-r--r--lib/pure/cgi.nim5
-rw-r--r--lib/pure/collections/critbits.nim6
-rw-r--r--lib/pure/collections/deques.nim308
-rw-r--r--lib/pure/collections/heapqueue.nim157
-rw-r--r--lib/pure/collections/intsets.nim366
-rw-r--r--lib/pure/collections/lists.nim533
-rw-r--r--lib/pure/collections/queues.nim257
-rw-r--r--lib/pure/collections/sequtils.nim721
-rw-r--r--lib/pure/collections/sets.nim1320
-rw-r--r--lib/pure/collections/tableimpl.nim2
-rw-r--r--lib/pure/collections/tables.nim2699
-rw-r--r--lib/pure/concurrency/atomics.nim378
-rw-r--r--lib/pure/concurrency/threadpool.nim7
-rw-r--r--lib/pure/fenv.nim2
-rw-r--r--lib/pure/httpclient.nim245
-rw-r--r--lib/pure/httpserver.nim535
-rw-r--r--lib/pure/includes/osenv.nim44
-rw-r--r--lib/pure/includes/oserr.nim34
-rw-r--r--lib/pure/includes/osseps.nim36
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim22
-rw-r--r--lib/pure/json.nim79
-rw-r--r--lib/pure/matchers.nim68
-rw-r--r--lib/pure/math.nim518
-rw-r--r--lib/pure/memfiles.nim5
-rw-r--r--lib/pure/nativesockets.nim70
-rw-r--r--lib/pure/net.nim79
-rw-r--r--lib/pure/oids.nim20
-rw-r--r--lib/pure/os.nim1317
-rw-r--r--lib/pure/osproc.nim39
-rw-r--r--lib/pure/parsecfg.nim2
-rw-r--r--lib/pure/parsecsv.nim170
-rw-r--r--lib/pure/parseopt.nim339
-rw-r--r--lib/pure/parseopt2.nim164
-rw-r--r--lib/pure/parsesql.nim2
-rw-r--r--lib/pure/parseutils.nim301
-rw-r--r--lib/pure/parsexml.nim2
-rw-r--r--lib/pure/random.nim47
-rw-r--r--lib/pure/scgi.nim295
-rw-r--r--lib/pure/securehash.nim6
-rw-r--r--lib/pure/selectors.nim3
-rw-r--r--lib/pure/stats.nim2
-rw-r--r--lib/pure/streams.nim43
-rw-r--r--lib/pure/strtabs.nim59
-rw-r--r--lib/pure/strutils.nim2031
-rw-r--r--lib/pure/subexes.nim406
-rw-r--r--lib/pure/terminal.nim2
-rw-r--r--lib/pure/times.nim916
-rw-r--r--lib/pure/typetraits.nim3
-rw-r--r--lib/pure/unicode.nim207
-rw-r--r--lib/pure/unittest.nim4
-rw-r--r--lib/pure/uri.nim229
-rw-r--r--lib/pure/xmltree.nim543
59 files changed, 10162 insertions, 6222 deletions
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index a5b18ae58..8c8838ed2 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -8,19 +8,59 @@
 #
 
 ## This module implements some common generic algorithms.
+##
+## Basic usage
+## ===========
+##
+## .. code-block::
+##    import algorithm
+##
+##    type People = tuple
+##      year: int
+##      name: string
+##
+##    var a: seq[People]
+##
+##    a.add((2000, "John"))
+##    a.add((2005, "Marie"))
+##    a.add((2010, "Jane"))
+##
+##    # Sorting with default system.cmp
+##    a.sort()
+##    assert a == @[(year: 2000, name: "John"), (year: 2005, name: "Marie"),
+##                  (year: 2010, name: "Jane")]
+##
+##    proc myCmp(x, y: People): int =
+##      if x.name < y.name: -1 else: 1
+##
+##    # Sorting with custom proc
+##    a.sort(myCmp)
+##    assert a == @[(year: 2010, name: "Jane"), (year: 2000, name: "John"),
+##                  (year: 2005, name: "Marie")]
+##
+##
+## See also
+## ========
+## * `sequtils module<sequtils.html>`_ for working with the built-in seq type
+## * `tables module<tables.html>`_ for sorting tables
 
 type
   SortOrder* = enum
     Descending, Ascending
 
 proc `*`*(x: int, order: SortOrder): int {.inline.} =
-  ## flips ``x`` if ``order == Descending``.
+  ## Flips ``x`` if ``order == Descending``.
   ## If ``order == Ascending`` then ``x`` is returned.
   ##
   ## ``x`` is supposed to be the result of a comparator, i.e.
   ## | ``< 0`` for *less than*,
   ## | ``== 0`` for *equal*,
   ## | ``> 0`` for *greater than*.
+  runnableExamples:
+    assert `*`(-123, Descending) == 123
+    assert `*`(123, Descending) == -123
+    assert `*`(-123, Ascending) == -123
+    assert `*`(123, Ascending) == 123
   var y = order.ord - 1
   result = (x xor y) - y
 
@@ -31,28 +71,44 @@ template fillImpl[T](a: var openArray[T], first, last: int, value: T) =
     inc(x)
 
 proc fill*[T](a: var openArray[T], first, last: Natural, value: T) =
-  ## fills the slice ``a[first..last]`` with ``value``.
+  ## Fills the slice ``a[first..last]`` with ``value``.
+  ##
+  ## If an invalid range is passed, it raises IndexError.
   runnableExamples:
-      var a: array[6, int]
-      a.fill(1, 3, 9)
-      doAssert a == [0, 9, 9, 9, 0, 0]
+    var a: array[6, int]
+    a.fill(1, 3, 9)
+    assert a == [0, 9, 9, 9, 0, 0]
+    a.fill(3, 5, 7)
+    assert a == [0, 9, 9, 7, 7, 7]
+    doAssertRaises(IndexError, a.fill(1, 7, 9))
   fillImpl(a, first, last, value)
 
 proc fill*[T](a: var openArray[T], value: T) =
-  ## fills the container ``a`` with ``value``.
+  ## Fills the container ``a`` with ``value``.
   runnableExamples:
-      var a: array[6, int]
-      a.fill(9)
-      doAssert a == [9, 9, 9, 9, 9, 9]
+    var a: array[6, int]
+    a.fill(9)
+    assert a == [9, 9, 9, 9, 9, 9]
+    a.fill(4)
+    assert a == [4, 4, 4, 4, 4, 4]
   fillImpl(a, 0, a.high, value)
 
 
 proc reverse*[T](a: var openArray[T], first, last: Natural) =
-  ## reverses the slice ``a[first..last]``.
+  ## Reverses the slice ``a[first..last]``.
+  ##
+  ## If an invalid range is passed, it raises IndexError.
+  ##
+  ## **See also:**
+  ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a ``seq[T]``
+  ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a ``seq[T]``
   runnableExamples:
-      var a = [1, 2, 3, 4, 5, 6]
-      a.reverse(1, 3)
-      doAssert a == [1, 4, 3, 2, 5, 6]
+    var a = [1, 2, 3, 4, 5, 6]
+    a.reverse(1, 3)
+    assert a == [1, 4, 3, 2, 5, 6]
+    a.reverse(1, 3)
+    assert a == [1, 2, 3, 4, 5, 6]
+    doAssertRaises(IndexError, a.reverse(1, 7))
   var x = first
   var y = last
   while x < y:
@@ -61,20 +117,32 @@ proc reverse*[T](a: var openArray[T], first, last: Natural) =
     inc(x)
 
 proc reverse*[T](a: var openArray[T]) =
-  ## reverses the contents of the container ``a``.
+  ## Reverses the contents of the container ``a``.
+  ##
+  ## **See also:**
+  ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a ``seq[T]``
+  ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a ``seq[T]``
   runnableExamples:
-      var a = [1, 2, 3, 4, 5, 6]
-      a.reverse()
-      doAssert  a == [6, 5, 4, 3, 2, 1]
+    var a = [1, 2, 3, 4, 5, 6]
+    a.reverse()
+    assert a == [6, 5, 4, 3, 2, 1]
+    a.reverse()
+    assert a == [1, 2, 3, 4, 5, 6]
   reverse(a, 0, max(0, a.high))
 
 proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] =
-  ## returns the reverse of the slice ``a[first..last]``.
+  ## Returns the reverse of the slice ``a[first..last]``.
+  ##
+  ## If an invalid range is passed, it raises IndexError.
+  ##
+  ## **See also:**
+  ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice
+  ## * `reverse proc<#reverse,openArray[T]>`_
   runnableExamples:
-      let
-        a = [1, 2, 3, 4, 5, 6]
-        b = reversed(a, 1, 3)
-      doAssert b == @[4, 3, 2]
+    let
+      a = [1, 2, 3, 4, 5, 6]
+      b = a.reversed(1, 3)
+    assert b == @[4, 3, 2]
   assert last >= first-1
   var i = last - first
   var x = first.int
@@ -85,12 +153,16 @@ proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] =
     inc(x)
 
 proc reversed*[T](a: openArray[T]): seq[T] =
-  ## returns the reverse of the container ``a``.
+  ## Returns the reverse of the container ``a``.
+  ##
+  ## **See also:**
+  ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice
+  ## * `reverse proc<#reverse,openArray[T]>`_
   runnableExamples:
-      let
-        a = [1, 2, 3, 4, 5, 6]
-        b = reversed(a)
-      doAssert b == @[6, 5, 4, 3, 2, 1]
+    let
+      a = [1, 2, 3, 4, 5, 6]
+      b = reversed(a)
+    assert b == @[6, 5, 4, 3, 2, 1]
   reversed(a, 0, a.high)
 
 proc binarySearch*[T, K](a: openArray[T], key: K,
@@ -99,6 +171,9 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
   ##
   ## ``cmp`` is the comparator function to use, the expected return values are
   ## the same as that of system.cmp.
+  runnableExamples:
+    assert binarySearch(["a","b","c","d"], "d", system.cmp[string]) == 3
+    assert binarySearch(["a","b","d","c"], "d", system.cmp[string]) == 2
   if a.len == 0:
     return -1
 
@@ -141,31 +216,41 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
 
 proc binarySearch*[T](a: openArray[T], key: T): int =
   ## Binary search for ``key`` in ``a``. Returns -1 if not found.
+  runnableExamples:
+    assert binarySearch([0, 1, 2, 3, 4], 4) == 4
+    assert binarySearch([0, 1, 4, 2, 3], 4) == 2
   binarySearch(a, key, cmp[T])
 
 proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated.} =
-  ## **Deprecated since version 0.18.1**; Use ``binarySearch`` instead.
+  ## **Deprecated since version 0.18.1**; Use `binarySearch proc
+  ## <#binarySearch,openArray[T],T>`_ instead.
   binarySearch(a, key, cmp[T])
 
 const
   onlySafeCode = true
 
 proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
-  ## returns a position to the first element in the ``a`` that is greater than
+  ## Returns a position to the first element in the ``a`` that is greater than
   ## ``key``, or last if no such element is found.
   ## In other words if you have a sorted sequence and you call
   ## ``insert(thing, elm, lowerBound(thing, elm))``
   ## the sequence will still be sorted.
   ##
-  ## The first version uses ``cmp`` to compare the elements.
-  ## The expected return values are the same as that of ``system.cmp``.
-  ## The second version uses the default comparison function ``cmp``.
+  ## If an invalid range is passed, it raises IndexError.
   ##
-  ## .. code-block:: nim
+  ## The version uses ``cmp`` to compare the elements.
+  ## The expected return values are the same as that of ``system.cmp``.
   ##
-  ##   var arr = @[1,2,3,5,6,7,8,9]
-  ##   arr.insert(4, arr.lowerBound(4))
-  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
+  ## **See also:**
+  ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order
+  ## * `upperBound proc<#upperBound,openArray[T],T>`_
+  runnableExamples:
+    var arr = @[1,2,3,5,6,7,8,9]
+    assert arr.lowerBound(3, system.cmp[int]) == 2
+    assert arr.lowerBound(4, system.cmp[int]) == 3
+    assert arr.lowerBound(5, system.cmp[int]) == 3
+    arr.insert(4, arr.lowerBound(4, system.cmp[int]))
+    assert arr == [1,2,3,4,5,6,7,8,9]
   result = a.low
   var count = a.high - a.low + 1
   var step, pos: int
@@ -179,23 +264,40 @@ proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo
       count = step
 
 proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T])
+  ## Returns a position to the first element in the ``a`` that is greater than
+  ## ``key``, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## ``insert(thing, elm, lowerBound(thing, elm))``
+  ## the sequence will still be sorted.
+  ##
+  ## The version uses the default comparison function ``cmp``.
+  ##
+  ## **See also:**
+  ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order
+  ## * `upperBound proc<#upperBound,openArray[T],T>`_
 
 proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
-  ## returns a position to the first element in the ``a`` that is not less
+  ## Returns a position to the first element in the ``a`` that is not less
   ## (i.e. greater or equal to) than ``key``, or last if no such element is found.
   ## In other words if you have a sorted sequence and you call
   ## ``insert(thing, elm, upperBound(thing, elm))``
   ## the sequence will still be sorted.
   ##
-  ## The first version uses ``cmp`` to compare the elements. The expected
-  ## return values are the same as that of ``system.cmp``.
-  ## The second version uses the default comparison function ``cmp``.
+  ## If an invalid range is passed, it raises IndexError.
   ##
-  ## .. code-block:: nim
+  ## The version uses ``cmp`` to compare the elements. The expected
+  ## return values are the same as that of ``system.cmp``.
   ##
-  ##   var arr = @[1,2,3,4,6,7,8,9]
-  ##   arr.insert(5, arr.upperBound(4))
-  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
+  ## **See also:**
+  ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order
+  ## * `lowerBound proc<#lowerBound,openArray[T],T>`_
+  runnableExamples:
+    var arr = @[1,2,3,5,6,7,8,9]
+    assert arr.upperBound(2, system.cmp[int]) == 2
+    assert arr.upperBound(3, system.cmp[int]) == 3
+    assert arr.upperBound(4, system.cmp[int]) == 3
+    arr.insert(4, arr.upperBound(3, system.cmp[int]))
+    assert arr == [1,2,3,4,5,6,7,8,9]
   result = a.low
   var count = a.high - a.low + 1
   var step, pos: int
@@ -209,6 +311,17 @@ proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo
       count = step
 
 proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T])
+  ## Returns a position to the first element in the ``a`` that is not less
+  ## (i.e. greater or equal to) than ``key``, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## ``insert(thing, elm, upperBound(thing, elm))``
+  ## the sequence will still be sorted.
+  ##
+  ## The version uses the default comparison function ``cmp``.
+  ##
+  ## **See also:**
+  ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order
+  ## * `lowerBound proc<#lowerBound,openArray[T],T>`_
 
 template `<-` (a, b) =
   when false:
@@ -263,6 +376,7 @@ func sort*[T](a: var openArray[T],
   ## Default Nim sort (an implementation of merge sort). The sorting
   ## is guaranteed to be stable and the worst case is guaranteed to
   ## be O(n log n).
+  ##
   ## The current implementation uses an iterative
   ## mergesort to achieve this. It uses a temporary sequence of
   ## length ``a.len div 2``. If you do not wish to provide your own
@@ -272,7 +386,6 @@ func sort*[T](a: var openArray[T],
   ## .. code-block:: nim
   ##
   ##    sort(myIntArray, system.cmp[int])
-  ##
   ##    # do not use cmp[string] here as we want to use the specialized
   ##    # overload:
   ##    sort(myStrArray, system.cmp)
@@ -286,6 +399,19 @@ func sort*[T](a: var openArray[T],
   ##     result = cmp(x.surname, y.surname)
   ##     if result == 0:
   ##       result = cmp(x.name, y.name)
+  ##
+  ## **See also:**
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+  runnableExamples:
+    var d = ["boo", "fo", "barr", "qux"]
+    proc myCmp(x, y: string): int =
+      if x.len() > y.len() or x.len() == y.len(): 1
+      else: -1
+    sort(d, myCmp)
+    assert d == ["fo", "qux", "boo", "barr"]
   var n = a.len
   var b: seq[T]
   newSeq(b, n div 2)
@@ -299,17 +425,30 @@ func sort*[T](a: var openArray[T],
 
 proc sort*[T](a: var openArray[T], order = SortOrder.Ascending) = sort[T](a, system.cmp[T], order)
   ## Shortcut version of ``sort`` that uses ``system.cmp[T]`` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
 
 proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
                 order = SortOrder.Ascending): seq[T] =
-  ## returns ``a`` sorted by ``cmp`` in the specified ``order``.
+  ## Returns ``a`` sorted by ``cmp`` in the specified ``order``.
+  ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
   runnableExamples:
-      let
-        a = [2, 3, 1, 5, 4]
-        b = sorted(a, system.cmp)
-        c = sorted(a, system.cmp, Descending)
-      doAssert b == @[1, 2, 3, 4, 5]
-      doAssert c == @[5, 4, 3, 2, 1]
+    let
+      a = [2, 3, 1, 5, 4]
+      b = sorted(a, system.cmp[int])
+      c = sorted(a, system.cmp[int], Descending)
+      d = sorted(["adam", "dande", "brian", "cat"], system.cmp[string])
+    assert b == @[1, 2, 3, 4, 5]
+    assert c == @[5, 4, 3, 2, 1]
+    assert d == @["adam", "brian", "cat", "dande"]
   result = newSeq[T](a.len)
   for i in 0 .. a.high:
     result[i] = a[i]
@@ -317,33 +456,48 @@ proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
 
 proc sorted*[T](a: openArray[T], order = SortOrder.Ascending): seq[T] =
   ## Shortcut version of ``sorted`` that uses ``system.cmp[T]`` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = sorted(a)
+      c = sorted(a, Descending)
+      d = sorted(["adam", "dande", "brian", "cat"])
+    assert b == @[1, 2, 3, 4, 5]
+    assert c == @[5, 4, 3, 2, 1]
+    assert d == @["adam", "brian", "cat", "dande"]
   sorted[T](a, system.cmp[T], order)
 
 template sortedByIt*(seq1, op: untyped): untyped =
   ## Convenience template around the ``sorted`` proc to reduce typing.
   ##
   ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   type Person = tuple[name: string, age: int]
-  ##   var
-  ##     p1: Person = (name: "p1", age: 60)
-  ##     p2: Person = (name: "p2", age: 20)
-  ##     p3: Person = (name: "p3", age: 30)
-  ##     p4: Person = (name: "p4", age: 30)
-  ##     people = @[p1,p2,p4,p3]
-  ##
-  ##   echo people.sortedByIt(it.name)
+  ## expression.
   ##
   ## Because the underlying ``cmp()`` is defined for tuples you can do
-  ## a nested sort like in the following example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   echo people.sortedByIt((it.age, it.name))
+  ## a nested sort.
   ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  runnableExamples:
+    type Person = tuple[name: string, age: int]
+    var
+      p1: Person = (name: "p1", age: 60)
+      p2: Person = (name: "p2", age: 20)
+      p3: Person = (name: "p3", age: 30)
+      p4: Person = (name: "p4", age: 30)
+      people = @[p1,p2,p4,p3]
+
+    assert people.sortedByIt(it.name) == @[(name: "p1", age: 60), (name: "p2", age: 20), (name: "p3", age: 30), (name: "p4", age: 30)]
+    # Nested sort
+    assert people.sortedByIt((it.age, it.name)) == @[(name: "p2", age: 20), (name: "p3", age: 30), (name: "p4", age: 30), (name: "p1", age: 60)]
   var result = sorted(seq1, proc(x, y: type(seq1[0])): int =
     var it {.inject.} = x
     let a = op
@@ -355,9 +509,25 @@ template sortedByIt*(seq1, op: untyped): untyped =
 func isSorted*[T](a: openArray[T],
                  cmp: proc(x, y: T): int {.closure.},
                  order = SortOrder.Ascending): bool =
-  ## checks to see whether ``a`` is already sorted in ``order``
+  ## Checks to see whether ``a`` is already sorted in ``order``
   ## using ``cmp`` for the comparison. Parameters identical
   ## to ``sort``.
+  ##
+  ## **See also:**
+  ## * `isSorted proc<#isSorted,openArray[T]>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = [1, 2, 3, 4, 5]
+      c = [5, 4, 3, 2, 1]
+      d = ["adam", "brian", "cat", "dande"]
+      e = ["adam", "dande", "brian", "cat"]
+    assert isSorted(a) == false
+    assert isSorted(b) == true
+    assert isSorted(c) == false
+    assert isSorted(c, Descending) == true
+    assert isSorted(d) == true
+    assert isSorted(e) == false
   result = true
   for i in 0..<len(a)-1:
     if cmp(a[i],a[i+1]) * order > 0:
@@ -365,11 +535,30 @@ func isSorted*[T](a: openArray[T],
 
 proc isSorted*[T](a: openarray[T], order = SortOrder.Ascending): bool =
   ## Shortcut version of ``isSorted`` that uses ``system.cmp[T]`` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `isSorted func<#isSorted,openArray[T],proc(T,T)>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = [1, 2, 3, 4, 5]
+      c = [5, 4, 3, 2, 1]
+      d = ["adam", "brian", "cat", "dande"]
+      e = ["adam", "dande", "brian", "cat"]
+    assert isSorted(a) == false
+    assert isSorted(b) == true
+    assert isSorted(c) == false
+    assert isSorted(c, Descending) == true
+    assert isSorted(d) == true
+    assert isSorted(e) == false
   isSorted(a, system.cmp[T], order)
 
 proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
-  ## produces the Cartesian product of the array. Warning: complexity
+  ## Produces the Cartesian product of the array. Warning: complexity
   ## may explode.
+  runnableExamples:
+    assert product(@[@[1], @[2]]) == @[@[1, 2]]
+    assert product(@[@["A", "K"], @["Q"]]) == @[@["K", "Q"], @["A", "Q"]]
   result = newSeq[seq[T]]()
   if x.len == 0:
     return
@@ -401,15 +590,26 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
     indexes[index] -= 1
 
 proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
-  ## calculates the next lexicographic permutation, directly modifying ``x``.
+  ## Calculates the next lexicographic permutation, directly modifying ``x``.
   ## The result is whether a permutation happened, otherwise we have reached
   ## the last-ordered permutation.
   ##
-  ## .. code-block:: nim
+  ## If you start with an unsorted array/seq, the repeated permutations
+  ## will **not** give you all permutations but stop with last.
   ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-  ##     v.nextPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
+  ## **See also:**
+  ## * `prevPermutation proc<#prevPermutation,openArray[T]>`_
+  runnableExamples:
+    var v = @[0, 1, 2, 3]
+    assert v.nextPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    assert v.nextPermutation() == true
+    assert v == @[0, 2, 1, 3]
+    assert v.prevPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    v = @[3, 2, 1, 0]
+    assert v.nextPermutation() == false
+    assert v == @[3, 2, 1, 0]
   if x.len < 2:
     return false
 
@@ -430,15 +630,20 @@ proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
   result = true
 
 proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
-  ## calculates the previous lexicographic permutation, directly modifying
+  ## Calculates the previous lexicographic permutation, directly modifying
   ## ``x``. The result is whether a permutation happened, otherwise we have
   ## reached the first-ordered permutation.
   ##
-  ## .. code-block:: nim
-  ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
-  ##     v.prevPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+  ## **See also:**
+  ## * `nextPermutation proc<#nextPermutation,openArray[T]>`_
+  runnableExamples:
+    var v = @[0, 1, 2, 3]
+    assert v.prevPermutation() == false
+    assert v == @[0, 1, 2, 3]
+    assert v.nextPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    assert v.prevPermutation() == true
+    assert v == @[0, 1, 2, 3]
   if x.len < 2:
     return false
 
@@ -542,7 +747,7 @@ proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] =
     result[i] = arg[i]
 
 proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int {.discardable.} =
-  ## performs a left rotation on a range of elements. If you want to rotate
+  ## Performs a left rotation on a range of elements. If you want to rotate
   ## right, use a negative ``dist``. Specifically, ``rotateLeft`` rotates
   ## the elements at ``slice`` by ``dist`` positions.
   ##
@@ -553,6 +758,7 @@ proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int):
   ##
   ## Elements outside of ``slice`` will be left unchanged.
   ## The time complexity is linear to ``slice.b - slice.a + 1``.
+  ## If an invalid range (``HSlice``) is passed, it raises IndexError.
   ##
   ## ``slice``
   ##   The indices of the element range that should be rotated.
@@ -561,11 +767,18 @@ proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int):
   ##   The distance in amount of elements that the data should be rotated.
   ##   Can be negative, can be any number.
   ##
-  ## .. code-block:: nim
-  ##
-  ##   var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-  ##   list.rotateLeft(1 .. 8, 3)
-  ##   doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for a version which rotates the whole container
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which returns a ``seq[T]``
+  runnableExamples:
+    var a = [0, 1, 2, 3, 4, 5]
+    a.rotateLeft(1 .. 4, 3)
+    assert a == [0, 4, 1, 2, 3, 5]
+    a.rotateLeft(1 .. 4, 3)
+    assert a == [0, 3, 4, 1, 2, 5]
+    a.rotateLeft(1 .. 4, -3)
+    assert a == [0, 4, 1, 2, 3, 5]
+    doAssertRaises(IndexError, a.rotateLeft(1 .. 7, 2))
   let sliceLen = slice.b + 1 - slice.a
   let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
   arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1)
@@ -573,10 +786,18 @@ proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int):
 proc rotateLeft*[T](arg: var openarray[T]; dist: int): int {.discardable.} =
   ## Default arguments for slice, so that this procedure operates on the entire
   ## ``arg``, and not just on a part of it.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],HSlice[int,int],int>`_ for a version which rotates a range
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which returns a ``seq[T]``
   runnableExamples:
-      var a = [1, 2, 3, 4, 5]
-      a.rotateLeft(2)
-      doAssert a == [3, 4, 5, 1, 2]
+    var a = [1, 2, 3, 4, 5]
+    a.rotateLeft(2)
+    assert a == [3, 4, 5, 1, 2]
+    a.rotateLeft(4)
+    assert a == [2, 3, 4, 5, 1]
+    a.rotateLeft(-6)
+    assert a == [1, 2, 3, 4, 5]
   let arglen = arg.len
   let distLeft = ((dist mod arglen) + arglen) mod arglen
   arg.rotateInternal(0, distLeft, arglen)
@@ -584,6 +805,28 @@ proc rotateLeft*[T](arg: var openarray[T]; dist: int): int {.discardable.} =
 proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq[T] =
   ## Same as ``rotateLeft``, just with the difference that it does
   ## not modify the argument. It creates a new ``seq`` instead.
+  ##
+  ## Elements outside of ``slice`` will be left unchanged.
+  ## If an invalid range (``HSlice``) is passed, it raises IndexError.
+  ##
+  ## ``slice``
+  ##   The indices of the element range that should be rotated.
+  ##
+  ## ``dist``
+  ##   The distance in amount of elements that the data should be rotated.
+  ##   Can be negative, can be any number.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],HSlice[int,int],int>`_ for the in-place version of this proc
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which rotates the whole container
+  runnableExamples:
+    var a = @[1, 2, 3, 4, 5]
+    a = rotatedLeft(a, 1 .. 4, 3)
+    assert a == @[1, 5, 2, 3, 4]
+    a = rotatedLeft(a, 1 .. 3, 2)
+    assert a == @[1, 3, 5, 2, 4]
+    a = rotatedLeft(a, 1 .. 3, -2)
+    assert a == @[1, 5, 2, 3, 4]
   let sliceLen = slice.b + 1 - slice.a
   let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
   arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1)
@@ -591,6 +834,18 @@ proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq
 proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] =
   ## Same as ``rotateLeft``, just with the difference that it does
   ## not modify the argument. It creates a new ``seq`` instead.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for the in-place version of this proc
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which rotates a range
+  runnableExamples:
+    var a = @[1, 2, 3, 4, 5]
+    a = rotatedLeft(a, 2)
+    assert a == @[3, 4, 5, 1, 2]
+    a = rotatedLeft(a, 4)
+    assert a == @[2, 3, 4, 5, 1]
+    a = rotatedLeft(a, -6)
+    assert a == @[1, 2, 3, 4, 5]
   let arglen = arg.len
   let distLeft = ((dist mod arglen) + arglen) mod arglen
   arg.rotatedInternal(0, distLeft, arg.len)
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 36319a317..5953ed975 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -1674,7 +1674,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
         curFd = fdPerDomain[ord(domain)]
         if curFd == osInvalidSocket.AsyncFD:
           try:
-            curFd = newAsyncNativeSocket(domain, sockType, protocol)
+            curFd = createAsyncNativeSocket(domain, sockType, protocol)
           except:
             freeAddrInfo(addrInfo)
             closeUnusedFds()
@@ -1806,47 +1806,6 @@ proc readAll*(future: FutureStream[string]): Future[string] {.async.} =
     else:
       break
 
-proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
-  ## Reads a line of data from ``socket``. Returned future will complete once
-  ## a full line is read or an error occurs.
-  ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
-  ## will be set to it.
-  ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
-  ##
-  ## If the socket is disconnected in the middle of a line (before ``\r\L``
-  ## is read) then line will be set to ``""``.
-  ## The partial line **will be lost**.
-  ##
-  ## **Warning**: This assumes that lines are delimited by ``\r\L``.
-  ##
-  ## **Note**: This procedure is mostly used for testing. You likely want to
-  ## use ``asyncnet.recvLine`` instead.
-  ##
-  ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
-
-  template addNLIfEmpty(): typed =
-    if result.len == 0:
-      result.add("\c\L")
-
-  result = ""
-  var c = ""
-  while true:
-    c = await recv(socket, 1)
-    if c.len == 0:
-      return ""
-    if c == "\r":
-      c = await recv(socket, 1)
-      assert c == "\l"
-      addNLIfEmpty()
-      return
-    elif c == "\L":
-      addNLIfEmpty()
-      return
-    add(result, c)
-
 proc callSoon*(cbproc: proc ()) =
   ## Schedule `cbproc` to be called as soon as possible.
   ## The callback is called when control returns to the event loop.
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index 3d6a9a015..d28e9fb57 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -75,14 +75,54 @@
 ##      waitFor(main())
 
 
-import asyncdispatch, asyncnet, strutils, parseutils, os, times
-
-from ftpclient import FtpBaseObj, ReplyError, FtpEvent
+import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times
 from net import BufferSize
 
 type
-  AsyncFtpClientObj* = FtpBaseObj[AsyncSocket]
-  AsyncFtpClient* = ref AsyncFtpClientObj
+  AsyncFtpClient* = ref object
+    csock*: AsyncSocket
+    dsock*: AsyncSocket
+    user*, pass*: string
+    address*: string
+    port*: Port
+    jobInProgress*: bool
+    job*: FTPJob
+    dsockConnected*: bool
+
+  FTPJobType* = enum
+    JRetrText, JRetr, JStore
+
+  FtpJob = ref object
+    prc: proc (ftp: AsyncFtpClient, async: bool): bool {.nimcall, gcsafe.}
+    case typ*: FTPJobType
+    of JRetrText:
+      lines: string
+    of JRetr, JStore:
+      file: File
+      filename: string
+      total: BiggestInt # In bytes.
+      progress: BiggestInt # In bytes.
+      oneSecond: BiggestInt # Bytes transferred in one second.
+      lastProgressReport: float # Time
+      toStore: string # Data left to upload (Only used with async)
+
+  FTPEventType* = enum
+    EvTransferProgress, EvLines, EvRetr, EvStore
+
+  FTPEvent* = object ## Event
+    filename*: string
+    case typ*: FTPEventType
+    of EvLines:
+      lines*: string ## Lines that have been transferred.
+    of EvRetr, EvStore: ## Retr/Store operation finished.
+      nil
+    of EvTransferProgress:
+      bytesTotal*: BiggestInt     ## Bytes total.
+      bytesFinished*: BiggestInt  ## Bytes transferred.
+      speed*: BiggestInt          ## Speed in bytes/s
+      currentJob*: FTPJobType     ## The current job being performed.
+
+  ReplyError* = object of IOError
 
   ProgressChangedProc* =
     proc (total, progress: BiggestInt, speed: float):
@@ -183,7 +223,7 @@ proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
   ## Returns a list of filenames in the given directory. If ``dir`` is "",
   ## the current directory is used. If ``async`` is true, this
   ## function will return immediately and it will be your job to
-  ## use asyncio's ``poll`` to progress this operation.
+  ## use asyncdispatch's ``poll`` to progress this operation.
   await ftp.pasv()
 
   assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"])
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index e3fc75597..edccd6628 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -264,6 +264,9 @@ proc processRequest(
   if "upgrade" in request.headers.getOrDefault("connection"):
     return false
 
+  # The request has been served, from this point on returning `true` means the
+  # connection will not be closed and will be kept in the connection pool.
+
   # Persistent connections
   if (request.protocol == HttpVer11 and
       cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
@@ -273,7 +276,7 @@ proc processRequest(
     # header states otherwise.
     # In HTTP 1.0 we assume that the connection should not be persistent.
     # Unless the connection header states otherwise.
-    discard
+    return true
   else:
     request.client.close()
     return false
@@ -309,10 +312,8 @@ proc serve*(server: AsyncHttpServer, port: Port,
   server.socket.listen()
 
   while true:
-    # TODO: Causes compiler crash.
-    #var (address, client) = await server.socket.acceptAddr()
-    var fut = await server.socket.acceptAddr()
-    asyncCheck processClient(server, fut.client, fut.address, callback)
+    var (address, client) = await server.socket.acceptAddr()
+    asyncCheck processClient(server, client, address, callback)
     #echo(f.isNil)
     #echo(f.repr)
 
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index b18d20d55..23ddf4777 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -154,13 +154,13 @@ proc processBody(node, retFutureSym: NimNode,
                 newCommand, node)
 
   of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
+    case node[0][^1].kind
     of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
+      if node[0][^1][0].kind == nnkIdent and node[0][^1][0].eqIdent("await"):
         # var x = await y
         var newVarSection = node # TODO: Should this use copyNimNode?
-        result.createVar("future" & node[0][0].strVal, node[0][2][1],
-          newVarSection[0][2], newVarSection, node)
+        result.createVar("future" & node[0][0].strVal, node[0][^1][1],
+          newVarSection[0][^1], newVarSection, node)
     else: discard
   of nnkAsgn:
     case node[1].kind
@@ -245,6 +245,12 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
 
   var outerProcBody = newNimNode(nnkStmtList, prc.body)
 
+  # Extract the documentation comment from the original procedure declaration.
+  # Note that we're not removing it from the body in order not to make this
+  # transformation even more complex.
+  if prc.body.len > 1 and prc.body[0].kind == nnkCommentStmt:
+    outerProcBody.add(prc.body[0])
+
   # -> var retFuture = newFuture[T]()
   var retFutureSym = genSym(nskVar, "retFuture")
   var subRetType =
@@ -361,11 +367,11 @@ proc stripAwait(node: NimNode): NimNode =
       # foo await x
       node[1][0] = emptyNoopSym
   of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
+    case node[0][^1].kind
     of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
+      if node[0][^1][0].kind == nnkIdent and node[0][^1][0].eqIdent("await"):
         # var x = await y
-        node[0][2][0] = emptyNoopSym
+        node[0][^1][0] = emptyNoopSym
     else: discard
   of nnkAsgn:
     case node[1].kind
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index bfb8a1666..427f93926 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -9,37 +9,49 @@
 
 ## This module implements a base64 encoder and decoder.
 ##
+## Base64 is an encoding and decoding technique used to convert binary
+## data to an ASCII string format.
+## Each Base64 digit represents exactly 6 bits of data. Three 8-bit
+## bytes (i.e., a total of 24 bits) can therefore be represented by
+## four 6-bit Base64 digits.
+##
+##
+## Basic usage
+## ===========
+##
 ## Encoding data
 ## -------------
 ##
-## In order to encode some text simply call the ``encode`` procedure:
-##
-##   .. code-block::nim
-##      import base64
-##      let encoded = encode("Hello World")
-##      echo(encoded) # SGVsbG8gV29ybGQ=
+## .. code-block::nim
+##    import base64
+##    let encoded = encode("Hello World")
+##    assert encoded == "SGVsbG8gV29ybGQ="
 ##
 ## Apart from strings you can also encode lists of integers or characters:
 ##
-##   .. code-block::nim
-##      import base64
-##      let encodedInts = encode([1,2,3])
-##      echo(encodedInts) # AQID
-##      let encodedChars = encode(['h','e','y'])
-##      echo(encodedChars) # aGV5
+## .. code-block::nim
+##    import base64
+##    let encodedInts = encode([1,2,3])
+##    assert encodedInts == "AQID"
+##    let encodedChars = encode(['h','e','y'])
+##    assert encodedChars == "aGV5"
 ##
-## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
-## can be passed as parameters.
 ##
 ## Decoding data
 ## -------------
 ##
-## To decode a base64 encoded data string simply call the ``decode``
-## procedure:
+## .. code-block::nim
+##    import base64
+##    let decoded = decode("SGVsbG8gV29ybGQ=")
+##    assert decoded == "Hello World"
 ##
-##   .. code-block::nim
-##      import base64
-##      echo(decode("SGVsbG8gV29ybGQ=")) # Hello World
+##
+## See also
+## ========
+##
+## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
+## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
+## * `sha1 module<sha1.html>`_ implements a sha1 encoder and decoder
 
 const
   cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
     discard
 
 proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
+  ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
+  ## ``newline`` is added.
   ##
   ## This procedure encodes an openarray (array or sequence) of either integers
   ## or characters.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,string,int,string>`_ for encoding a string
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encode(['n', 'i', 'm']) == "bmlt"
+    assert encode(@['n', 'i', 'm']) == "bmlt"
+    assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
   encodeInternal(s, lineLen, newLine)
 
 proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
+  ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
+  ## ``newline`` is added.
   ##
   ## This procedure encodes a string.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encode("Hello World") == "SGVsbG8gV29ybGQ="
+    assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
   encodeInternal(s, lineLen, newLine)
 
 proc decodeByte(b: char): int {.inline.} =
@@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
   else: result = 63
 
 proc decode*(s: string): string =
-  ## decodes a string in base64 representation back into its original form.
-  ## Whitespace is skipped.
+  ## Decodes string ``s`` in base64 representation back into its original form.
+  ## The initial whitespace is skipped.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
+  ## * `encode proc<#encode,string,int,string>`_ for encoding a string
+  runnableExamples:
+    assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
+    assert decode("  SGVsbG8gV29ybGQ=") == "Hello World"
   const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
   var total = ((len(s) + 3) div 4) * 3
   # total is an upper bound, as we will skip arbitrary whitespace:
diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim
index 3f213c5ea..0eee3cd70 100644
--- a/lib/pure/bitops.nim
+++ b/lib/pure/bitops.nim
@@ -8,7 +8,8 @@
 #
 
 ## This module implements a series of low level methods for bit manipulation.
-## By default, this module use compiler intrinsics to improve performance
+
+## By default, this module use compiler intrinsics where possible to improve performance
 ## on supported compilers: ``GCC``, ``LLVM_GCC``, ``CLANG``, ``VCC``, ``ICC``.
 ##
 ## The module will fallback to pure nim procs incase the backend is not supported.
@@ -32,6 +33,75 @@ const useICC_builtins = defined(icc) and useBuiltins
 const useVCC_builtins = defined(vcc) and useBuiltins
 const arch64 = sizeof(int) == 8
 
+template forwardImpl(impl, arg) {.dirty.} =
+  when sizeof(x) <= 4:
+    when x is SomeSignedInt:
+      impl(cast[uint32](x.int32))
+    else:
+      impl(x.uint32)
+  else:
+    when x is SomeSignedInt:
+      impl(cast[uint64](x.int64))
+    else:
+      impl(x.uint64)
+
+when defined(nimHasalignOf):
+
+  import macros
+
+  type BitsRange*[T] = range[0..sizeof(T)*8-1]
+    ## Returns a range with all bit positions for type ``T``
+
+  proc setMask*[T: SomeInteger](v: var T, mask: T) {.inline.} =
+    ## Returns ``v``, with all the ``1`` bits from ``mask`` set to 1
+    v = v or mask
+
+  proc clearMask*[T: SomeInteger](v: var T, mask: T) {.inline.} =
+    ## Returns ``v``, with all the ``1`` bits from ``mask`` set to 0
+    v = v and not mask
+
+  proc flipMask*[T: SomeInteger](v: var T, mask: T) {.inline.} =
+    ## Returns ``v``, with all the ``1`` bits from ``mask`` flipped
+    v = v xor mask
+
+  proc setBit*[T: SomeInteger](v: var T, bit: BitsRange[T]) {.inline.} =
+    ## Returns ``v``, with the bit at position ``bit`` set to 1
+    v.setMask(1.T shl bit)
+
+  proc clearBit*[T: SomeInteger](v: var T, bit: BitsRange[T]) {.inline.} =
+    ## Returns ``v``, with the bit at position ``bit`` set to 0
+    v.clearMask(1.T shl bit)
+
+  proc flipBit*[T: SomeInteger](v: var T, bit: BitsRange[T]) {.inline.} =
+    ## Returns ``v``, with the bit at position ``bit`` flipped
+    v.flipMask(1.T shl bit)
+
+  macro setBits*(v: typed, bits: varargs[typed]): untyped =
+    ## Returns ``v``, with the bits at positions ``bits`` set to 1
+    bits.expectKind(nnkBracket)
+    result = newStmtList()
+    for bit in bits:
+      result.add newCall("setBit", v, bit)
+
+  macro clearBits*(v: typed, bits: varargs[typed]): untyped =
+    ## Returns ``v``, with the bits at positions ``bits`` set to 0
+    bits.expectKind(nnkBracket)
+    result = newStmtList()
+    for bit in bits:
+      result.add newCall("clearBit", v, bit)
+
+  macro flipBits*(v: typed, bits: varargs[typed]): untyped =
+    ## Returns ``v``, with the bits at positions ``bits`` set to 0
+    bits.expectKind(nnkBracket)
+    result = newStmtList()
+    for bit in bits:
+      result.add newCall("flipBit", v, bit)
+
+  proc testBit*[T: SomeInteger](v: var T, bit: BitsRange[T]): bool {.inline.} =
+    ## Returns true if the bit in ``v`` at positions ``bit`` is set to 1
+    let mask = 1.T shl bit
+    return (v and mask) == mask
+
 # #### Pure Nim version ####
 
 proc firstSetBit_nim(x: uint32): int {.inline, nosideeffect.} =
@@ -185,8 +255,7 @@ proc countSetBits*(x: SomeInteger): int {.inline, nosideeffect.} =
   # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
   # like GCC and MSVC
   when nimvm:
-    when sizeof(x) <= 4: result = countSetBits_nim(x.uint32)
-    else:                result = countSetBits_nim(x.uint64)
+    result = forwardImpl(countSetBits_nim, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int
@@ -216,8 +285,7 @@ proc parityBits*(x: SomeInteger): int {.inline, nosideeffect.} =
   # Can be used a base if creating ASM version.
   # https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
   when nimvm:
-    when sizeof(x) <= 4: result = parity_impl(x.uint32)
-    else:                result = parity_impl(x.uint64)
+    result = forwardImpl(parity_impl, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_parity(x.uint32).int
@@ -235,8 +303,7 @@ proc firstSetBit*(x: SomeInteger): int {.inline, nosideeffect.} =
     when noUndefined:
       if x == 0:
         return 0
-    when sizeof(x) <= 4: result = firstSetBit_nim(x.uint32)
-    else:                result = firstSetBit_nim(x.uint64)
+    result = forwardImpl(firstSetBit_nim, x)
   else:
     when noUndefined and not useGCC_builtins:
       if x == 0:
@@ -270,8 +337,7 @@ proc fastLog2*(x: SomeInteger): int {.inline, nosideeffect.} =
     if x == 0:
       return -1
   when nimvm:
-    when sizeof(x) <= 4: result = fastlog2_nim(x.uint32)
-    else:                result = fastlog2_nim(x.uint64)
+    result = forwardImpl(fastlog2_nim, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = 31 - builtin_clz(x.uint32).int
@@ -302,8 +368,7 @@ proc countLeadingZeroBits*(x: SomeInteger): int {.inline, nosideeffect.} =
     if x == 0:
       return 0
   when nimvm:
-      when sizeof(x) <= 4: result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint32)
-      else:                result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint64)
+    result = sizeof(x)*8 - 1 - forwardImpl(fastlog2_nim, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_clz(x.uint32).int - (32 - sizeof(x)*8)
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 869abc9cc..ec3562c35 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -337,11 +337,6 @@ proc setStackTraceStdout*() =
   ## Makes Nim output stacktraces to stdout, instead of server log.
   errorMessageWriter = writeErrorMessage
 
-proc setStackTraceNewLine*() {.deprecated.} =
-  ## Makes Nim output stacktraces to stdout, instead of server log.
-  ## Depracated alias for setStackTraceStdout.
-  setStackTraceStdout()
-
 proc setCookie*(name, value: string) =
   ## Sets a cookie.
   write(stdout, "Set-Cookie: ", name, "=", value, "\n")
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 32e0299ba..dd91fdb12 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -202,12 +202,6 @@ proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
   ## If `key` is not in `t`, the ``KeyError`` exception is raised.
   get(c, key)
 
-proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} =
-  ## retrieves the value at ``c[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
-  ## Use ```[]``` instead.
-  get(c, key)
-
 iterator leaves[T](n: Node[T]): Node[T] =
   if n != nil:
     # XXX actually we could compute the necessary stack size in advance:
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index e8342e208..cb05e5112 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -20,41 +20,59 @@
 ## access, unless your program logic guarantees it indirectly.
 ##
 ## .. code-block:: Nim
-##   proc foo(a, b: Positive) =  # assume random positive values for `a` and `b`
-##     var deq = initDeque[int]()  # initializes the object
-##     for i in 1 ..< a: deq.addLast i  # populates the deque
+##   import deques
 ##
-##     if b < deq.len:  # checking before indexed access
-##       echo "The element at index position ", b, " is ", deq[b]
+##   var a = initDeque[int]()
 ##
-##     # 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
+##   doAssertRaises(IndexError, echo a[0])
 ##
-##     while deq.len > 0:  # checking if the deque is empty
-##       echo deq.popLast()
+##   for i in 1 .. 5:
+##     a.addLast(10*i)
+##   assert $a == "[10, 20, 30, 40, 50]"
 ##
-## Note: For inter thread communication use
-## a `Channel <channels.html>`_ instead.
+##   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 math, typetraits
 
 type
   Deque*[T] = object
     ## A double-ended queue backed with a ringed seq buffer.
+    ##
+    ## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
     data: seq[T]
     head, tail, count, mask: int
 
 proc initDeque*[T](initialSize: int = 4): Deque[T] =
-  ## Create a new deque.
-  ## Optionally, the initial capacity can be reserved via `initialSize` as a
-  ## performance optimization. The length of a newly created deque will still
-  ## be 0.
+  ## Create a new empty deque.
   ##
-  ## `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.
+  ## Optionally, the initial capacity can be reserved via `initialSize`
+  ## as a performance optimization.
+  ## 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>`_.
   assert isPowerOfTwo(initialSize)
   result.mask = initialSize-1
   newSeq(result.data, initialSize)
@@ -75,33 +93,128 @@ template xBoundsCheck(deq, i) =
     if unlikely(i >= deq.count):  # x < deq.low is taken care by the Natural parameter
       raise newException(IndexError,
                          "Out of bounds: " & $i & " > " & $(deq.count - 1))
+    if unlikely(i < 0):  # when used with BackwardsIndex
+      raise newException(IndexError,
+                         "Out of bounds: " & $i & " < 0")
 
 proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
-  ## Access the i-th element of `deq` by order from first to last.
-  ## deq[0] is the first, deq[^1] is the last.
+  ## Access the i-th element of `deq`.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert a[0] == 10
+    assert a[3] == 40
+    doAssertRaises(IndexError, echo a[8])
+
   xBoundsCheck(deq, i)
   return deq.data[(deq.head + i) and deq.mask]
 
 proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
-  ## Access the i-th element of `deq` and returns a mutable
+  ## Access the i-th element of `deq` and return 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(IndexError, echo a[8])
+
   xBoundsCheck(deq, i)
   return deq.data[(deq.head + i) and deq.mask]
 
-proc `[]=`* [T] (deq: var Deque[T], i: Natural, val : T) {.inline.} =
+proc `[]=`*[T](deq: var Deque[T], i: Natural, val : T) {.inline.} =
   ## Change the i-th element of `deq`.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    a[0] = 99
+    a[3] = 66
+    assert $a == "[99, 20, 30, 66, 50]"
+
   xBoundsCheck(deq, i)
   deq.data[(deq.head + i) and deq.mask] = val
 
+proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
+  ## Access 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)
+    assert a[^1] == 50
+    assert a[^4] == 20
+    doAssertRaises(IndexError, echo a[^9])
+
+  xBoundsCheck(deq, deq.len - int(i))
+  return deq[deq.len - int(i)]
+
+proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} =
+  ## Access 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)
+    assert a[^1] == 50
+    assert a[^4] == 20
+    doAssertRaises(IndexError, echo a[^9])
+
+  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.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    a[^1] = 99
+    a[^3] = 77
+    assert $a == "[10, 20, 77, 40, 99]"
+
+  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
+  ##
   var i = deq.head
   for c in 0 ..< deq.count:
     yield deq.data[i]
     i = (i + 1) and deq.mask
 
 iterator mitems*[T](deq: var Deque[T]): var T =
-  ## Yield every element of `deq`.
+  ## Yield every element of `deq`, which can be modified.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      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]
@@ -109,18 +222,35 @@ iterator mitems*[T](deq: var Deque[T]): var T =
 
 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
 
 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``.
+  ## 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``.
   ##
   ## .. code-block:: Nim
   ##   if x in q:
-  ##     assert q.contains x
+  ##     assert q.contains(x)
   for e in deq:
     if e == item: return true
   return false
@@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
 
 proc addFirst*[T](deq: var Deque[T], item: T) =
   ## Add an `item` to the beginning of the `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]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addFirst(10*i)
+    assert $a == "[50, 40, 30, 20, 10]"
+
   expandIfNeeded(deq)
   inc deq.count
   deq.head = (deq.head - 1) and deq.mask
@@ -145,6 +288,19 @@ proc addFirst*[T](deq: var Deque[T], item: T) =
 
 proc addLast*[T](deq: var Deque[T], item: T) =
   ## Add an `item` to the end of the `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]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+
   expandIfNeeded(deq)
   inc deq.count
   deq.data[deq.tail] = item
@@ -152,11 +308,41 @@ proc addLast*[T](deq: var Deque[T], item: T) =
 
 proc peekFirst*[T](deq: Deque[T]): 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>`_
+  ## * `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)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.peekFirst == 10
+    assert len(a) == 5
+
   emptyCheck(deq)
   result = deq.data[deq.head]
 
 proc peekLast*[T](deq: Deque[T]): 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>`_
+  ## * `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)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.peekLast == 50
+    assert len(a) == 5
+
   emptyCheck(deq)
   result = deq.data[(deq.tail - 1) and deq.mask]
 
@@ -165,6 +351,23 @@ template destroy(x: untyped) =
 
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove 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)
+    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]
@@ -173,6 +376,23 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove 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]>`_
+  ## * `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)
+    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
@@ -181,17 +401,39 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
 
 proc clear*[T](deq: var Deque[T]) {.inline.} =
   ## Resets the deque so that it is empty.
+  ##
+  ## See also:
+  ## * `clear proc <#clear,Deque[T]>`_
+  ## * `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]"
+    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
-  ## `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.
+  ## `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.
   ##
-  ## Any user defined destructors
+  ## See also:
+  ## * `clear proc <#clear,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addFirst(10*i)
+    assert $a == "[50, 40, 30, 20, 10]"
+    a.shrink(fromFirst = 2, fromLast = 1)
+    assert $a == "[30, 20]"
+
   if fromFirst + fromLast > deq.count:
     clear(deq)
     return
@@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
     result.addQuoted(x)
   result.add("]")
 
+
+
 when isMainModule:
   var deq = initDeque[int](1)
   deq.addLast(4)
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index 60869142e..cdb8db6e1 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -1,4 +1,3 @@
-
 #
 #
 #            Nim's Runtime Library
@@ -7,32 +6,74 @@
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 
-##[ Heap queue algorithm (a.k.a. priority queue). Ported from Python heapq.
-
-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.  For the sake of comparison,
-non-existing elements are considered to be infinite.  The interesting
-property of a heap is that a[0] is always its smallest element.
-
+##[
+  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
+
+    var jobs = initHeapQueue[Job]()
+    jobs.push(Job(priority: 1))
+    jobs.push(Job(priority: 2))
+
+    assert jobs[0].priority == 1
 ]##
 
-type HeapQueue*[T] = distinct seq[T]
+type HeapQueue*[T] = object
+  ## A heap queue, commonly known as a priority queue.
+  data: seq[T]
 
-proc newHeapQueue*[T](): HeapQueue[T] {.inline.} = HeapQueue[T](newSeq[T]())
-proc newHeapQueue*[T](h: var HeapQueue[T]) {.inline.} = h = HeapQueue[T](newSeq[T]())
+proc initHeapQueue*[T](): HeapQueue[T] =
+  ## Create a new empty heap.
+  discard
 
-proc len*[T](h: HeapQueue[T]): int {.inline.} = seq[T](h).len
-proc `[]`*[T](h: HeapQueue[T], i: int): T {.inline.} = seq[T](h)[i]
-proc `[]=`[T](h: var HeapQueue[T], i: int, v: T) {.inline.} = seq[T](h)[i] = v
-proc add[T](h: var HeapQueue[T], v: T) {.inline.} = seq[T](h).add(v)
+proc len*[T](heap: HeapQueue[T]): int {.inline.} =
+  ## Return the number of elements of `heap`.
+  heap.data.len
+
+proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} =
+  ## Access the i-th element of `heap`.
+  heap.data[i]
 
 proc heapCmp[T](x, y: T): bool {.inline.} =
   return (x < y)
 
-# '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
-# heap invariant.
 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
+  ## heap invariant.
   var pos = p
   var newitem = heap[pos]
   # Follow the path to the root, moving parents down until finding a place
@@ -41,11 +82,11 @@ proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
     let parentpos = (pos - 1) shr 1
     let parent = heap[parentpos]
     if heapCmp(newitem, parent):
-      heap[pos] = parent
+      heap.data[pos] = parent
       pos = parentpos
     else:
       break
-  heap[pos] = newitem
+  heap.data[pos] = newitem
 
 proc siftup[T](heap: var HeapQueue[T], p: int) =
   let endpos = len(heap)
@@ -60,48 +101,50 @@ proc siftup[T](heap: var HeapQueue[T], p: int) =
     if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
       childpos = rightpos
     # Move the smaller child up.
-    heap[pos] = heap[childpos]
+    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
   # to its final resting place (by sifting its parents down).
-  heap[pos] = newitem
+  heap.data[pos] = newitem
   siftdown(heap, startpos, pos)
 
 proc push*[T](heap: var HeapQueue[T], item: T) =
-  ## Push item onto heap, maintaining the heap invariant.
-  (seq[T](heap)).add(item)
+  ## Push `item` onto heap, maintaining the heap invariant.
+  heap.data.add(item)
   siftdown(heap, 0, len(heap)-1)
 
 proc pop*[T](heap: var HeapQueue[T]): T =
-  ## Pop the smallest item off the heap, maintaining the heap invariant.
-  let lastelt = seq[T](heap).pop()
+  ## Pop and return the smallest item from `heap`,
+  ## maintaining the heap invariant.
+  let lastelt = heap.data.pop()
   if heap.len > 0:
     result = heap[0]
-    heap[0] = lastelt
+    heap.data[0] = lastelt
     siftup(heap, 0)
   else:
     result = lastelt
 
-proc del*[T](heap: var HeapQueue[T], index: int) =
-  ## Removes element at `index`, maintaining the heap invariant.
-  swap(seq[T](heap)[^1], seq[T](heap)[index])
+proc del*[T](heap: var HeapQueue[T], index: Natural) =
+  ## Removes the element at `index` from `heap`, maintaining the heap invariant.
+  swap(heap.data[^1], heap.data[index])
   let newLen = heap.len - 1
-  seq[T](heap).setLen(newLen)
+  heap.data.setLen(newLen)
   if index < newLen:
     heap.siftup(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
-  ## more appropriate when using a fixed-size heap.  Note that the value
-  ## returned may be larger than item!  That constrains reasonable uses of
+  ## 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:
-
+  ##
+  ## .. code-block:: nim
   ##    if item > heap[0]:
   ##        item = replace(heap, item)
   result = heap[0]
-  heap[0] = item
+  heap.data[0] = item
   siftup(heap, 0)
 
 proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
@@ -111,6 +154,36 @@ proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
     siftup(heap, 0)
   return item
 
+proc clear*[T](heap: var HeapQueue[T]) =
+  ## Remove all elements from `heap`, making it empty.
+  runnableExamples:
+    var heap = initHeapQueue[int]()
+    heap.push(1)
+    heap.clear()
+    assert heap.len == 0
+  heap.data.setLen(0)
+
+proc `$`*[T](heap: HeapQueue[T]): string =
+  ## Turn a heap into its string representation.
+  runnableExamples:
+    var heap = initHeapQueue[int]()
+    heap.push(1)
+    heap.push(2)
+    assert $heap == "[1, 2]"
+  result = "["
+  for x in heap.data:
+    if result.len > 1: result.add(", ")
+    result.addQuoted(x)
+  result.add("]")
+
+proc newHeapQueue*[T](): HeapQueue[T] {.deprecated.} =
+  ## **Deprecated since v0.20.0:** use ``initHeapQueue`` instead.
+  initHeapQueue[T]()
+
+proc newHeapQueue*[T](heap: var HeapQueue[T]) {.deprecated.} =
+  ## **Deprecated since v0.20.0:** use ``clear`` instead.
+  heap.clear()
+
 when isMainModule:
   proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
     var tmp = h
@@ -119,7 +192,7 @@ when isMainModule:
       result.add(pop(tmp))
 
   block: # Simple sanity test
-    var heap = newHeapQueue[int]()
+    var heap = initHeapQueue[int]()
     let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
     for item in data:
       push(heap, item)
@@ -127,27 +200,27 @@ when isMainModule:
     doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
 
   block: # Test del
-    var heap = newHeapQueue[int]()
+    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(seq[int](heap).find(7))
+    heap.del(heap.data.find(7))
     doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
 
-    heap.del(seq[int](heap).find(5))
+    heap.del(heap.data.find(5))
     doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
 
-    heap.del(seq[int](heap).find(6))
+    heap.del(heap.data.find(6))
     doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
 
-    heap.del(seq[int](heap).find(2))
+    heap.del(heap.data.find(2))
     doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
 
   block: # Test del last
-    var heap = newHeapQueue[int]()
+    var heap = initHeapQueue[int]()
     let data = [1, 2, 3]
     for item in data: push(heap, item)
 
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index f6d3a3d11..226401b92 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -7,13 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``intsets`` module implements an efficient int set implemented as a
+## The ``intsets`` module implements an efficient `int` set implemented as a
 ## `sparse bit set`:idx:.
-
-## **Note**: Currently the assignment operator ``=`` for ``intsets``
+##
+## **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`` to
-## get a deep copy.
+## 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
 
 
 import
@@ -40,7 +43,7 @@ type
     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
+  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
@@ -96,18 +99,33 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
   t.head = result
   t.data[h] = result
 
-proc contains*(s: IntSet, key: int): bool =
-  ## Returns true iff `key` is in `s`.
+proc bitincl(s: var IntSet, key: int) {.inline.} =
+  var t = intSetPut(s, `shr`(key, TrunkShift))
+  var u = key and TrunkMask
+  t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
+      `shl`(1, 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: return true
+      if s.a[i] == key:
+        s.a[i] = s.a[s.elems-1]
+        dec s.elems
+        return
   else:
     var t = intSetGet(s, `shr`(key, TrunkShift))
     if t != nil:
       var u = key and TrunkMask
-      result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
-    else:
-      result = false
+      t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
+          not `shl`(1, 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`.
@@ -131,14 +149,62 @@ iterator items*(s: IntSet): int {.inline.} =
         inc(i)
       r = r.next
 
-proc bitincl(s: var IntSet, key: int) {.inline.} =
-  var t = intSetPut(s, `shr`(key, TrunkShift))
-  var u = key and TrunkMask
-  t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
-      `shl`(1, u and IntMask)
+
+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[`shr`(u, IntShift)] and `shl`(1, 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
@@ -156,40 +222,42 @@ proc incl*(s: var IntSet, key: int) =
 
 proc incl*(s: var IntSet, other: IntSet) =
   ## Includes all elements from `other` into `s`.
-  for item in other: incl(s, item)
-
-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, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
-          not `shl`(1, u and IntMask)
-
-proc excl*(s: var IntSet, key: int) =
-  ## Excludes `key` from the set `s`.
-  exclImpl(s, key)
-
-proc excl*(s: var IntSet, other: IntSet) =
-  ## Excludes all elements from `other` from `s`.
-  for item in other: excl(s, item)
+  ##
+  ## 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
 
-proc missingOrExcl*(s: var IntSet, key: int) : bool =
-  ## Returns true if `s` does not contain `key`, otherwise
-  ## `key` is removed from `s` and false is returned.
-  var count = s.elems
-  exclImpl(s, key)
-  result = count == s.elems
+  for item in other: incl(s, item)
 
 proc containsOrIncl*(s: var IntSet, key: int): bool =
-  ## Returns true if `s` contains `key`, otherwise `key` is included in `s`
-  ## and false is returned.
+  ## 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:
@@ -208,25 +276,76 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
       incl(s, key)
       result = false
 
-proc initIntSet*: IntSet =
-  ## Returns an empty IntSet. Example:
+proc excl*(s: var IntSet, key: int) =
+  ## Excludes `key` from the set `s`.
   ##
-  ## .. code-block ::
-  ##   var a = initIntSet()
-  ##   a.incl(2)
+  ## 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)
 
-  # 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 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 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.elems
+  exclImpl(s, key)
+  result = count == s.elems
 
 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
@@ -243,8 +362,17 @@ proc clear*(result: var IntSet) =
 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`.
+  ## 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 = @[]
@@ -276,11 +404,33 @@ proc assign*(dest: var IntSet, src: IntSet) =
 
 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):
@@ -288,6 +438,17 @@ proc intersection*(s1, s2: IntSet): IntSet =
 
 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):
@@ -295,31 +456,50 @@ proc difference*(s1, s2: IntSet): IntSet =
 
 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>`_.
+  ## Alias for `union(s1, s2) <#union,IntSet,IntSet>`_.
   result = union(s1, s2)
 
 proc `*`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `intersection(s1, s2) <#intersection>`_.
+  ## Alias for `intersection(s1, s2) <#intersection,IntSet,IntSet>`_.
   result = intersection(s1, s2)
 
 proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `difference(s1, s2) <#difference>`_.
+  ## 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 len*(s: IntSet): int {.inline.} =
-  ## Returns the number of keys in `s`.
+  ## Returns the number of elements in `s`.
   if s.elems < s.a.len:
     result = s.elems
   else:
@@ -328,40 +508,58 @@ proc len*(s: IntSet): int {.inline.} =
       inc(result)
 
 proc card*(s: IntSet): int {.inline.} =
-  ## Alias for `len() <#len>` _.
+  ## Alias for `len() <#len,IntSet>`_.
   result = s.len()
 
 proc `<=`*(s1, s2: IntSet): bool =
-  ## Returns true iff `s1` is subset of `s2`.
+  ## 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 iff `s1` is proper subset of `s2`.
+  ## 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 `s` and `t` have the same members and set size.
+  ## Returns true if both `s1` and `s2` have the same elements and set size.
   return s1 <= s2 and s2 <= s1
 
-template dollarImpl(): untyped =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.add($key)
-  result.add("}")
-
 proc `$`*(s: IntSet): string =
   ## The `$` operator for int sets.
+  ##
+  ## Converts the set `s` to a string, mostly for logging and printing purposes.
   dollarImpl()
 
-proc empty*(s: IntSet): bool {.inline, deprecated.} =
-  ## Returns true if `s` is empty. This is safe to call even before
-  ## the set has been initialized with `initIntSet`. Note this never
-  ## worked reliably and so is deprecated.
-  result = s.counter == 0
+
 
 when isMainModule:
   import sequtils, algorithm
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 15ce5d074..1fd32c9fa 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -7,34 +7,112 @@
 #    distribution, for details about the copyright.
 #
 
-## Implementation of singly and doubly linked lists. Because it makes no sense
-## to do so, the 'next' and 'prev' pointers are not hidden from you and can
-## be manipulated directly for efficiency.
+## Implementation of:
+## * `singly linked lists <#SinglyLinkedList>`_
+## * `doubly linked lists <#DoublyLinkedList>`_
+## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists)
+## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists)
+##
+##
+## 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
+## ========
+##
+## * `deques module <#deques.html>`_ for double-ended queues
+## * `sharedlist module <#sharedlist.html>`_ for shared singly-linked lists
+
 
 when not defined(nimhygiene):
   {.pragma: dirty.}
 
 type
-  DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
+  DoublyLinkedNodeObj*[T] = object ## A node a doubly linked list consists of.
+    ##
+    ## It consists of a `value` field, and pointers to `next` and `prev`.
     next*, prev*: ref DoublyLinkedNodeObj[T]
     value*: T
   DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
 
-  SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
+  SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
+    ##
+    ## It consists of a `value` field, and a pointer to `next`.
     next*: ref SinglyLinkedNodeObj[T]
     value*: T
   SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
 
-  SinglyLinkedList*[T] = object ## a singly linked list
+  SinglyLinkedList*[T] = object ## A singly linked list.
+    ##
+    ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
+    ## a new empty list.
     head*, tail*: SinglyLinkedNode[T]
 
-  DoublyLinkedList*[T] = object ## a doubly linked list
+  DoublyLinkedList*[T] = object ## A doubly linked list.
+    ##
+    ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
+    ## a new empty list.
     head*, tail*: DoublyLinkedNode[T]
 
-  SinglyLinkedRing*[T] = object ## a singly linked ring
+  SinglyLinkedRing*[T] = object ## A singly linked ring.
+    ##
+    ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
+    ## a new empty ring.
     head*, tail*: SinglyLinkedNode[T]
 
-  DoublyLinkedRing*[T] = object ## a doubly linked ring
+  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]
@@ -46,28 +124,44 @@ type
   SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
 
 proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
-  ## creates a new singly linked list that is empty.
+  ## Creates a new singly linked list that is empty.
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
   discard
 
 proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
-  ## creates a new doubly linked list that is empty.
+  ## Creates a new doubly linked list that is empty.
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
   discard
 
 proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
-  ## creates a new singly linked ring that is empty.
+  ## Creates a new singly linked ring that is empty.
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
   discard
 
 proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
-  ## creates a new doubly linked ring that is empty.
+  ## Creates a new doubly linked ring that is empty.
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
   discard
 
 proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
-  ## creates a new doubly linked node with the given `value`.
+  ## Creates a new doubly linked node with the given `value`.
+  runnableExamples:
+    var n = newDoublyLinkedNode[int](5)
+    assert n.value == 5
+
   new(result)
   result.value = value
 
 proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
-  ## creates a new singly linked node with the given `value`.
+  ## Creates a new singly linked node with the given `value`.
+  runnableExamples:
+    var n = newSinglyLinkedNode[int](5)
+    assert n.value == 5
+
   new(result)
   result.value = value
 
@@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
       if it == L.head: break
 
 iterator items*[T](L: SomeLinkedList[T]): T =
-  ## yields every value of `L`.
+  ## Yields every value of `L`.
+  ##
+  ## 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
   itemsListImpl()
 
 iterator items*[T](L: SomeLinkedRing[T]): T =
-  ## yields every value of `L`.
+  ## Yields every value of `L`.
+  ##
+  ## 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
   itemsRingImpl()
 
 iterator mitems*[T](L: var SomeLinkedList[T]): var T =
-  ## yields every value of `L` so that you can modify it.
+  ## Yields every value of `L` so that you can modify it.
+  ##
+  ## 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)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      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.
+  ## Yields every value of `L` so that you can modify it.
+  ##
+  ## 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)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      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
+  ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
+  ##
+  ## 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)
+    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
+    assert $a == "[49, 99, 199, 249]"
+
   var it = L.head
   while it != nil:
     var nxt = it.next
@@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
     it = nxt
 
 iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
+  ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
+  ##
+  ## 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)
+    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
+    assert $a == "[49, 99, 199, 249]"
+
   var it = L.head
   if it != nil:
     while true:
@@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
       if it == L.head: break
 
 proc `$`*[T](L: SomeLinkedCollection[T]): string =
-  ## turns a list into its string representation.
+  ## Turns a list into its string representation for logging and printing.
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
@@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
   result.add("]")
 
 proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
+  ## Searches in the list for a value. Returns `nil` if the value does not
   ## exist.
+  ##
+  ## See also:
+  ## * `contains proc <#contains,SomeLinkedCollection[T],T>`_
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.find(9).value == 9
+    assert a.find(1) == nil
+
   for x in nodes(L):
     if x.value == value: return x
 
 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.
+  ## Searches in the list for a value. Returns `false` if the value does not
+  ## exist, `true` otherwise.
+  ##
+  ## See also:
+  ## * `find proc <#find,SomeLinkedCollection[T],T>`_
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
+    assert 8 in a
+    assert(not a.contains(1))
+    assert 2 notin a
+
   result = find(L, value) != nil
 
 proc append*[T](L: var SinglyLinkedList[T],
                 n: SinglyLinkedNode[T]) {.inline.} =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## 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
+  ## * `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)
+    assert a.contains(9)
+
   n.next = nil
   if L.tail != nil:
     assert(L.tail.next == nil)
@@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
   if L.head == nil: L.head = n
 
 proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,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)
+    assert a.contains(9)
   append(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedList[T],
                  n: SinglyLinkedNode[T]) {.inline.} =
-  ## prepends a node to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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)
+    a.prepend(n)
+    assert a.contains(9)
+
   n.next = L.head
   L.head = n
   if L.tail == nil: L.tail = n
 
 proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
-  ## prepends a node to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newSinglyLinkedNode(value))
 
+
+
 proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## 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
+  ## * `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)
+    assert a.contains(9)
+
   n.next = nil
   n.prev = L.tail
   if L.tail != nil:
@@ -176,11 +450,40 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if L.head == nil: L.head = n
 
 proc append*[T](L: var DoublyLinkedList[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `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]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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)
+    a.prepend(n)
+    assert a.contains(9)
+
   n.prev = nil
   n.next = L.head
   if L.head != nil:
@@ -190,18 +493,56 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if L.tail == nil: L.tail = n
 
 proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newDoublyLinkedNode(value))
 
 proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## removes `n` from `L`. Efficiency: O(1).
+  ## Removes a node `n` from `L`. Efficiency: O(1).
+  runnableExamples:
+    var
+      a = initDoublyLinkedList[int]()
+      n = newDoublyLinkedNode[int](5)
+    a.append(n)
+    assert 5 in a
+    a.remove(n)
+    assert 5 notin a
+
   if n == L.tail: L.tail = n.prev
   if n == L.head: L.head = n.next
   if n.next != nil: n.next.prev = n.prev
   if n.prev != nil: n.prev.next = n.next
 
+
+
 proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## 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
+  ## * `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)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
@@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
     L.tail = n
 
 proc append*[T](L: var SinglyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,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)
+    assert a.contains(9)
   append(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
@@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   L.head = n
 
 proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newSinglyLinkedNode(value))
 
+
+
 proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## 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
+  ## * `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)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
     L.head = n
 
 proc append*[T](L: var DoublyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `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]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   L.head = n
 
 proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,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]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
+    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).
+  ## Removes `n` from `L`. Efficiency: O(1).
+  runnableExamples:
+    var
+      a = initDoublyLinkedRing[int]()
+      n = newDoublyLinkedNode[int](5)
+    a.append(n)
+    assert 5 in a
+    a.remove(n)
+    assert 5 notin a
+
   n.next.prev = n.prev
   n.prev.next = n.next
   if n == L.head:
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
deleted file mode 100644
index 9a1d169fb..000000000
--- a/lib/pure/collections/queues.nim
+++ /dev/null
@@ -1,257 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Implementation of a `queue`:idx:. The underlying implementation uses a ``seq``.
-##
-## None of the procs that get an individual value from the queue can be used
-## on an empty queue.
-## If compiled with `boundChecks` option, those procs will raise an `IndexError`
-## on such access. This should not be relied upon, as `-d:release` will
-## disable those checks and may return garbage or crash the program.
-##
-## As such, a check to see if the queue is empty is needed before any
-## access, unless your program logic guarantees it indirectly.
-##
-## .. code-block:: Nim
-##   proc foo(a, b: Positive) =  # assume random positive values for `a` and `b`
-##     var q = initQueue[int]()  # initializes the object
-##     for i in 1 ..< a: q.add i  # populates the queue
-##
-##     if b < q.len:  # checking before indexed access
-##       echo "The element at index position ", b, " is ", q[b]
-##
-##     # 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 q.front == 1
-##     assert q.back == a
-##
-##     while q.len > 0:  # checking if the queue is empty
-##       echo q.pop()
-##
-## Note: For inter thread communication use
-## a `Channel <channels.html>`_ instead.
-
-import math
-
-{.warning: "`queues` module is deprecated - use `deques` instead".}
-
-type
-  Queue* {.deprecated.} [T] = object ## A queue.
-    data: seq[T]
-    rd, wr, count, mask: int
-
-proc initQueue*[T](initialSize: int = 4): Queue[T] =
-  ## Create a new queue.
-  ## Optionally, the initial capacity can be reserved via `initialSize` as a
-  ## performance optimization. The length of a newly created queue will still
-  ## be 0.
-  ##
-  ## `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.
-  assert isPowerOfTwo(initialSize)
-  result.mask = initialSize-1
-  newSeq(result.data, initialSize)
-
-proc len*[T](q: Queue[T]): int {.inline.}=
-  ## Return the number of elements of `q`.
-  result = q.count
-
-template emptyCheck(q) =
-  # Bounds check for the regular queue access.
-  when compileOption("boundChecks"):
-    if unlikely(q.count < 1):
-      raise newException(IndexError, "Empty queue.")
-
-template xBoundsCheck(q, i) =
-  # Bounds check for the array like accesses.
-  when compileOption("boundChecks"):  # d:release should disable this.
-    if unlikely(i >= q.count):  # x < q.low is taken care by the Natural parameter
-      raise newException(IndexError,
-                         "Out of bounds: " & $i & " > " & $(q.count - 1))
-
-proc front*[T](q: Queue[T]): T {.inline.}=
-  ## Return the oldest element of `q`. Equivalent to `q.pop()` but does not
-  ## remove it from the queue.
-  emptyCheck(q)
-  result = q.data[q.rd]
-
-proc back*[T](q: Queue[T]): T {.inline.} =
-  ## Return the newest element of `q` but does not remove it from the queue.
-  emptyCheck(q)
-  result = q.data[q.wr - 1 and q.mask]
-
-proc `[]`*[T](q: Queue[T], i: Natural) : T {.inline.} =
-  ## Access the i-th element of `q` by order of insertion.
-  ## q[0] is the oldest (the next one q.pop() will extract),
-  ## q[^1] is the newest (last one added to the queue).
-  xBoundsCheck(q, i)
-  return q.data[q.rd + i and q.mask]
-
-proc `[]`*[T](q: var Queue[T], i: Natural): var T {.inline.} =
-  ## Access the i-th element of `q` and returns a mutable
-  ## reference to it.
-  xBoundsCheck(q, i)
-  return q.data[q.rd + i and q.mask]
-
-proc `[]=`* [T] (q: var Queue[T], i: Natural, val : T) {.inline.} =
-  ## Change the i-th element of `q`.
-  xBoundsCheck(q, i)
-  q.data[q.rd + i and q.mask] = val
-
-iterator items*[T](q: Queue[T]): T =
-  ## Yield every element of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield q.data[i]
-    i = (i + 1) and q.mask
-
-iterator mitems*[T](q: var Queue[T]): var T =
-  ## Yield every element of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield q.data[i]
-    i = (i + 1) and q.mask
-
-iterator pairs*[T](q: Queue[T]): tuple[key: int, val: T] =
-  ## Yield every (position, value) of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield (c, q.data[i])
-    i = (i + 1) and q.mask
-
-proc contains*[T](q: Queue[T], item: T): bool {.inline.} =
-  ## Return true if `item` is in `q` or false if not found. Usually used
-  ## via the ``in`` operator. It is the equivalent of ``q.find(item) >= 0``.
-  ##
-  ## .. code-block:: Nim
-  ##   if x in q:
-  ##     assert q.contains x
-  for e in q:
-    if e == item: return true
-  return false
-
-proc add*[T](q: var Queue[T], item: T) =
-  ## Add an `item` to the end of the queue `q`.
-  var cap = q.mask+1
-  if unlikely(q.count >= cap):
-    var n = newSeq[T](cap*2)
-    for i, x in pairs(q):  # don't use copyMem because the GC and because it's slower.
-      shallowCopy(n[i], x)
-    shallowCopy(q.data, n)
-    q.mask = cap*2 - 1
-    q.wr = q.count
-    q.rd = 0
-  inc q.count
-  q.data[q.wr] = item
-  q.wr = (q.wr + 1) and q.mask
-
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
-
-proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
-  ## Remove and returns the first (oldest) element of the queue `q`.
-  emptyCheck(q)
-  dec q.count
-  result = q.data[q.rd]
-  q.data[q.rd] = default(type(result))
-  q.rd = (q.rd + 1) and q.mask
-
-proc enqueue*[T](q: var Queue[T], item: T) =
-  ## Alias for the ``add`` operation.
-  q.add(item)
-
-proc dequeue*[T](q: var Queue[T]): T =
-  ## Alias for the ``pop`` operation.
-  q.pop()
-
-proc `$`*[T](q: Queue[T]): string =
-  ## Turn a queue into its string representation.
-  result = "["
-  for x in items(q):  # Don't remove the items here for reasons that don't fit in this margin.
-    if result.len > 1: result.add(", ")
-    result.add($x)
-  result.add("]")
-
-when isMainModule:
-  var q = initQueue[int](1)
-  q.add(123)
-  q.add(9)
-  q.enqueue(4)
-  var first = q.dequeue()
-  q.add(56)
-  q.add(6)
-  var second = q.pop()
-  q.add(789)
-
-  assert first == 123
-  assert second == 9
-  assert($q == "[4, 56, 6, 789]")
-
-  assert q[0] == q.front and q.front == 4
-  q[0] = 42
-  q[q.len - 1] = 7
-
-  assert 6 in q and 789 notin q
-  assert q.find(6) >= 0
-  assert q.find(789) < 0
-
-  for i in -2 .. 10:
-    if i in q:
-      assert q.contains(i) and q.find(i) >= 0
-    else:
-      assert(not q.contains(i) and q.find(i) < 0)
-
-  when compileOption("boundChecks"):
-    try:
-      echo q[99]
-      assert false
-    except IndexError:
-      discard
-
-    try:
-      assert q.len == 4
-      for i in 0 ..< 5: q.pop()
-      assert false
-    except IndexError:
-      discard
-
-  # grabs some types of resize error.
-  q = initQueue[int]()
-  for i in 1 .. 4: q.add i
-  q.pop()
-  q.pop()
-  for i in 5 .. 8: q.add i
-  assert $q == "[3, 4, 5, 6, 7, 8]"
-
-  # Similar to proc from the documentation example
-  proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
-    var q = initQueue[int]()
-    assert q.len == 0
-    for i in 1 .. a: q.add i
-
-    if b < q.len: # checking before indexed access.
-      assert q[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 q.front == 1
-    assert q.back == a
-
-    while q.len > 0: # checking if the queue is empty
-      assert q.pop() > 0
-
-  #foo(0,0)
-  foo(8,5)
-  foo(10,9)
-  foo(1,1)
-  foo(2,1)
-  foo(1,5)
-  foo(3,2)
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 39ba6df49..253340379 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -7,16 +7,71 @@
 #    distribution, for details about the copyright.
 #
 
-## :Author: Alexander Mitchell-Robinson (Amrykid)
+## 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:
+## * sequences
+## * strings
+## * array
 ##
-## This module implements operations for the built-in `seq`:idx: type which
-## were inspired by functional programming languages.
+## 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
 ##
-## For functional style programming you may want to pass `anonymous procs
-## <manual.html#procedures-anonymous-procs>`_ to procs like ``filter`` to
-## reduce typing. Anonymous procs can use `the special do notation
-## <manual.html#procedures-do-notation>`_
-## which is more convenient in certain situations.
+## This module builds upon that, providing additional functionality in form of
+## procs, iterators and templates inspired by functional programming
+## 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>`_
+## * use `...It templates<#18>`_
+##   (`mapIt<#mapIt.t,typed,untyped>`_,
+##   `filterIt<#filterIt.t,untyped,untyped>`_, etc.)
+##
+## The chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procs-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**:
+## * `strutils module<strutils.html>`_ for common string functions
+## * `sugar module<sugar.html>`_ for syntactic sugar macros
+## * `algorithm module<algorithm.html>`_ for common generic algorithms
+## * `json module<json.html>`_ for a structure which allows
+##   heterogeneous members
+
 
 include "system/inclrtl"
 
@@ -31,7 +86,7 @@ macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped
   ##  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 (eg to handle openArray) where
+  ## except when ``letAssigneable`` is false (e.g. to handle openArray) where
   ## it just forwards ``exp`` unchanged
   expectKind(expAlias, nnkIdent)
   var val = exp
@@ -49,16 +104,20 @@ macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped
 
 proc 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.
   ##
-  ## Example:
+  ## See also:
+  ## * `distribute proc<#distribute,seq[T],Positive>`_ for a reverse
+  ##   operation
   ##
-  ## .. code-block::
-  ##   let
-  ##     s1 = @[1, 2, 3]
-  ##     s2 = @[4, 5]
-  ##     s3 = @[6, 7]
-  ##     total = concat(s1, s2, s3)
-  ##   assert total == @[1, 2, 3, 4, 5, 6, 7]
+  runnableExamples:
+    let
+      s1 = @[1, 2, 3]
+      s2 = @[4, 5]
+      s3 = @[6, 7]
+      total = concat(s1, s2, s3)
+    assert total == @[1, 2, 3, 4, 5, 6, 7]
+
   var L = 0
   for seqitm in items(seqs): inc(L, len(seqitm))
   newSeq(result, L)
@@ -71,13 +130,14 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
 proc count*[T](s: openArray[T], x: T): int =
   ## Returns the number of occurrences of the item `x` in the container `s`.
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let
-  ##     s = @[1, 2, 2, 3, 2, 4, 2]
-  ##     c = count(s, 2)
-  ##   assert c == 4
+  runnableExamples:
+    let
+      a = @[1, 2, 2, 3, 2, 4, 2]
+      b = "abracadabra"
+    assert count(a, 2) == 4
+    assert count(a, 99) == 0
+    assert count(b, 'r') == 2
+
   for itm in items(s):
     if itm == x:
       inc result
@@ -85,15 +145,14 @@ proc count*[T](s: openArray[T], x: T): int =
 proc 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).
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   let
-  ##     s = @[1, 2, 3]
-  ##     total = s.cycle(3)
-  ##   assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+  runnableExamples:
+    let
+      s = @[1, 2, 3]
+      total = s.cycle(3)
+    assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+
   result = newSeq[T](n * s.len)
   var o = 0
   for x in 0 ..< n:
@@ -103,14 +162,13 @@ proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
 
 proc repeat*[T](x: T, n: Natural): seq[T] =
   ## Returns a new sequence with the item `x` repeated `n` times.
+  ## `n` must be a non-negative number (zero or more).
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   let
-  ##     total = repeat(5, 3)
-  ##   assert total == @[5, 5, 5]
+  runnableExamples:
+    let
+      total = repeat(5, 3)
+    assert total == @[5, 5, 5]
+
   result = newSeq[T](n)
   for i in 0 ..< n:
     result[i] = x
@@ -118,16 +176,18 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
 proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
   ## Returns a new sequence without duplicates.
   ##
-  ## Example:
+  ## Setting the optional argument ``isSorted`` to ``true`` (default: false)
+  ## uses a faster algorithm for deduplication.
   ##
-  ## .. code-block::
-  ##   let
-  ##     dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
-  ##     dup2 = @["a", "a", "c", "d", "d"]
-  ##     unique1 = deduplicate(dup1)
-  ##     unique2 = deduplicate(dup2)
-  ##   assert unique1 == @[1, 3, 4, 2, 8]
-  ##   assert unique2 == @["a", "c", "d"]
+  runnableExamples:
+    let
+      dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
+      dup2 = @["a", "a", "c", "d", "d"]
+      unique1 = deduplicate(dup1)
+      unique2 = deduplicate(dup2, isSorted = true)
+    assert unique1 == @[1, 3, 4, 2, 8]
+    assert unique2 == @["a", "c", "d"]
+
   result = @[]
   if s.len > 0:
     if isSorted:
@@ -144,39 +204,44 @@ proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
 proc zip*[S, T](s1: openArray[S], s2: openArray[T]): seq[tuple[a: S, b: T]] =
   ## Returns a new sequence with a combination of the two input containers.
   ##
-  ## For convenience you can access the returned tuples through the named
-  ## fields `a` and `b`. If one container is shorter, the remaining items in
-  ## the longer container are discarded.
+  ## The input containers can be of different types.
+  ## If one container is shorter, the remaining items in the longer container
+  ## are discarded.
   ##
-  ## Example:
+  ## For convenience you can access the returned tuples through the named
+  ## fields `a` and `b`.
   ##
-  ## .. code-block::
-  ##   let
-  ##     short = @[1, 2, 3]
-  ##     long = @[6, 5, 4, 3, 2, 1]
-  ##     words = @["one", "two", "three"]
-  ##     zip1 = zip(short, long)
-  ##     zip2 = zip(short, words)
-  ##   assert zip1 == @[(1, 6), (2, 5), (3, 4)]
-  ##   assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
-  ##   assert zip1[2].b == 4
-  ##   assert zip2[2].b == "three"
+  runnableExamples:
+    let
+      short = @[1, 2, 3]
+      long = @[6, 5, 4, 3, 2, 1]
+      words = @["one", "two", "three"]
+      letters = "abcd"
+      zip1 = zip(short, long)
+      zip2 = zip(short, words)
+      zip3 = zip(long, letters)
+    assert zip1 == @[(1, 6), (2, 5), (3, 4)]
+    assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip3 == @[(a: 6, b: 'a'), (a: 5, b: 'b'), (a: 4, b: 'c'),
+                     (a: 3, b: 'd')]
+    assert zip1[2].b == 4
+    assert zip2[2].b == "three"
+
   var m = min(s1.len, s2.len)
   newSeq(result, m)
   for i in 0 ..< m:
     result[i] = (s1[i], s2[i])
 
 proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
-  ## Splits and distributes a sequence `s` into `num` sub sequences.
+  ## 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>`_ proc. The proc will assert in debug
-  ## builds if `s` is nil or `num` is less than one, and will likely crash on
-  ## release builds.  The input sequence `s` can be empty, which will produce
+  ## Returns a sequence of `num` sequences. For *some* input values this is the
+  ## inverse of the `concat <#concat,varargs[seq[T]]>`_ proc.
+  ## 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 sequences with ``1 + len(s) div num``
+  ## proc 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
@@ -184,18 +249,16 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   ## more suited to multithreading where you are passing equal sized work units
   ## to a thread pool and want to maximize core usage.
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let numbers = @[1, 2, 3, 4, 5, 6, 7]
-  ##   assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-  ##   assert numbers.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-  ##   assert numbers.distribute(6)[0] == @[1, 2]
-  ##   assert numbers.distribute(6)[5] == @[7]
+  runnableExamples:
+    let numbers = @[1, 2, 3, 4, 5, 6, 7]
+    assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    assert numbers.distribute(6)[0] == @[1, 2]
+    assert numbers.distribute(6)[1] == @[3]
+
   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.
@@ -209,13 +272,11 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   if extra == 0 or spread == false:
     # Use an algorithm which overcounts the stride and minimizes reading limits.
     if extra > 0: inc(stride)
-
     for i in 0 ..< num:
       result[i] = newSeq[T]()
       for g in first ..< min(s.len, first + stride):
         result[i].add(s[g])
       first += stride
-
   else:
     # Use an undercounting algorithm which *adds* the remainder each iteration.
     for i in 0 ..< num:
@@ -223,7 +284,6 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
       if extra > 0:
         extra -= 1
         inc(last)
-
       result[i] = newSeq[T]()
       for g in first ..< last:
         result[i].add(s[g])
@@ -231,110 +291,103 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
 
 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` applied to every item in
-  ## the container `s`.
+  ## Returns a new sequence with the results of `op` proc applied to every
+  ## item in the container `s`.
   ##
-  ## Since the input is not modified you can use this version of ``map`` to
+  ## Since the input is not modified you can use it to
   ## transform the type of the elements in the input container.
   ##
-  ## Example:
+  ## See also:
+  ## * `mapIt template<#mapIt.t,typed,untyped>`_
+  ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version
   ##
-  ## .. code-block:: nim
-  ##   let
-  ##     a = @[1, 2, 3, 4]
-  ##     b = map(a, proc(x: int): string = $x)
-  ##   assert b == @["1", "2", "3", "4"]
+  runnableExamples:
+    let
+      a = @[1, 2, 3, 4]
+      b = map(a, proc(x: int): string = $x)
+    assert b == @["1", "2", "3", "4"]
+
   newSeq(result, s.len)
   for i in 0 ..< s.len:
     result[i] = op(s[i])
 
-proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
-                                                              {.deprecated.} =
-  ## Applies `op` to every item in `s` modifying it directly.
-  ##
-  ## Note that this version of ``map`` requires your input and output types to
-  ## be the same, since they are modified in-place.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   map(a, proc(x: var string) = x &= "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
-  ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
-  for i in 0 ..< s.len: 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.
   ##
-  ## Note that this requires your input and output types to
-  ## be the same, since they are modified in-place.
+  ## 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.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   apply(a, proc(x: var string) = x &= "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
+  ## See also:
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_
+  ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
+  runnableExamples:
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: var string) = x &= "42")
+    assert a == @["142", "242", "342", "442"]
+
   for i in 0 ..< s.len: op(s[i])
 
 proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
                                                               {.inline.} =
   ## Applies `op` to every item in `s` modifying it directly.
   ##
-  ## Note that this requires your input and output types to
-  ## be the same, since they are modified in-place.
+  ## 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 and returns a ``T`` type variable.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   apply(a, proc(x: string): string = x & "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
+  ## See also:
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_
+  ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
+  runnableExamples:
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: string): string = x & "42")
+    assert a == @["142", "242", "342", "442"]
+
   for i in 0 ..< s.len: s[i] = op(s[i])
 
 iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
-  ## Iterates through a container and yields every item that fulfills the
-  ## predicate.
+  ## Iterates through a container `s` and yields every item that fulfills the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## See also:
+  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
-  ##     echo($n)
-  ##   # echoes 4, 8, 4 in separate lines
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    var evens = newSeq[int]()
+    for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
+      evens.add(n)
+    assert evens == @[4, 8, 4]
+
   for i in 0 ..< s.len:
     if pred(s[i]):
       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 that fulfilled the predicate.
+  ## Returns a new sequence with all the items of `s` that fulfilled the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## See also:
+  ## * `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
   ##
-  ## .. code-block::
-  ##   let
-  ##     colors = @["red", "yellow", "black"]
-  ##     f1 = filter(colors, proc(x: string): bool = x.len < 6)
-  ##     f2 = filter(colors) do (x: string) -> bool : x.len > 5
-  ##   assert f1 == @["red", "black"]
-  ##   assert f2 == @["yellow"]
+  runnableExamples:
+    let
+      colors = @["red", "yellow", "black"]
+      f1 = filter(colors, proc(x: string): bool = x.len < 6)
+      f2 = filter(colors, proc(x: string): bool = x.contains('y'))
+    assert f1 == @["red", "black"]
+    assert f2 == @["yellow"]
+
   result = newSeq[T]()
   for i in 0 ..< s.len:
     if pred(s[i]):
@@ -342,15 +395,23 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
 
 proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
                                                                 {.inline.} =
-  ## Keeps the items in the passed sequence if they fulfilled the predicate.
-  ## Same as the ``filter`` proc, but modifies the sequence directly.
+  ## Keeps the items in the passed sequence `s` if they fulfilled the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## Note that `s` must be declared as a ``var``.
   ##
-  ## .. code-block::
-  ##   var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
-  ##   keepIf(floats, proc(x: float): bool = x > 10)
-  ##   assert floats == @[13.0, 12.5, 10.1]
+  ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_,
+  ## but modifies the sequence directly.
+  ##
+  ## See also:
+  ## * `keepItIf template<#keepItIf.t,seq,untyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
+  ##
+  runnableExamples:
+    var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
+    keepIf(floats, proc(x: float): bool = x > 10)
+    assert floats == @[13.0, 12.5, 10.1]
+
   var pos = 0
   for i in 0 ..< len(s):
     if pred(s[i]):
@@ -360,16 +421,15 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
   setLen(s, pos)
 
 proc delete*[T](s: var seq[T]; first, last: Natural) =
-  ## Deletes in `s` the items at position `first` .. `last`. This modifies
-  ## `s` itself, it does not return a copy.
+  ## 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.
   ##
-  ## Example:
-  ##
-  ##.. code-block::
-  ##   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)
-  ##   assert outcome == dest
+  runnableExamples:
+    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)
+    assert outcome == dest
 
   var i = first
   var j = last+1
@@ -384,15 +444,15 @@ proc 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.
   ##
-  ## Example:
+  ## Notice that `src` and `dest` must be of the same type.
   ##
-  ##.. code-block::
-  ##   var dest = @[1,1,1,1,1,1,1,1]
-  ##   let
-  ##     src = @[2,2,2,2,2,2]
-  ##     outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-  ##   dest.insert(src, 3)
-  ##   assert dest == outcome
+  runnableExamples:
+    var dest = @[1,1,1,1,1,1,1,1]
+    let
+      src = @[2,2,2,2,2,2]
+      outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    dest.insert(src, 3)
+    assert dest == outcome
 
   var j = len(dest) - 1
   var i = len(dest) + len(src) - 1
@@ -411,37 +471,48 @@ 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 that fulfilled the predicate.
+  ## Returns a new sequence with all the items of `s` that fulfilled the
+  ## predicate `pred`.
   ##
-  ## Unlike the `proc` version, the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
+  ## 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')``.
   ##
-  ## Example:
+  ## See also:
+  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##    let
-  ##      temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
-  ##      acceptable = filterIt(temperatures, it < 50 and it > -10)
-  ##      notAcceptable = filterIt(temperatures, it > 50 or it < -10)
-  ##    assert acceptable == @[-2.0, 24.5, 44.31]
-  ##    assert notAcceptable == @[-272.15, 99.9, -113.44]
+  runnableExamples:
+    let
+      temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
+      acceptable = temperatures.filterIt(it < 50 and it > -10)
+      notAcceptable = temperatures.filterIt(it > 50 or it < -10)
+    assert acceptable == @[-2.0, 24.5, 44.31]
+    assert notAcceptable == @[-272.15, 99.9, -113.44]
+
   var result = newSeq[type(s[0])]()
   for it {.inject.} in items(s):
     if pred: result.add(it)
   result
 
 template keepItIf*(varSeq: seq, pred: untyped) =
-  ## Convenience template around the ``keepIf`` proc to reduce typing.
+  ## Keeps the items in the passed sequence (must be declared as a ``var``)
+  ## if they fulfilled the predicate.
   ##
-  ## Unlike the `proc` version, the predicate needs to be an expression using
+  ## 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')``.
   ##
-  ## Example:
+  ## See also:
+  ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_
+  ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   var candidates = @["foo", "bar", "baz", "foobar"]
-  ##   keepItIf(candidates, it.len == 3 and it[0] == 'b')
-  ##   assert candidates == @["bar", "baz"]
+  runnableExamples:
+    var candidates = @["foo", "bar", "baz", "foobar"]
+    candidates.keepItIf(it.len == 3 and it[0] == 'b')
+    assert candidates == @["bar", "baz"]
+
   var pos = 0
   for i in 0 ..< len(varSeq):
     let it {.inject.} = varSeq[i]
@@ -455,26 +526,37 @@ proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
   ## Iterates through a container and checks if every item fulfills the
   ## predicate.
   ##
-  ## Example:
+  ## See also:
+  ## * `allIt template<#allIt.t,untyped,untyped>`_
+  ## * `any proc<#any,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##   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
+  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
+
   for i in s:
     if not pred(i):
       return false
   return true
 
 template allIt*(s, pred: untyped): bool =
-  ## Checks if every item fulfills the predicate.
+  ## Iterates through a container and checks if every item fulfills the
+  ## predicate.
   ##
-  ## Example:
+  ## 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')``.
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert allIt(numbers, it < 10) == true
-  ##   assert allIt(numbers, it < 9) == false
+  ## See also:
+  ## * `all proc<#all,openArray[T],proc(T)>`_
+  ## * `anyIt template<#anyIt.t,untyped,untyped>`_
+  ##
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    assert numbers.allIt(it < 10) == true
+    assert numbers.allIt(it < 9) == false
+
   var result = true
   for it {.inject.} in items(s):
     if not pred:
@@ -486,26 +568,37 @@ 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.
   ##
-  ## Example:
+  ## See also:
+  ## * `anyIt template<#anyIt.t,untyped,untyped>`_
+  ## * `all proc<#all,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##   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
+  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
+
   for i in s:
     if pred(i):
       return true
   return false
 
 template anyIt*(s, pred: untyped): bool =
-  ## Checks if some item fulfills the predicate.
+  ## Iterates through a container and checks if some item fulfills the
+  ## predicate.
   ##
-  ## Example:
+  ## 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')``.
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert anyIt(numbers, it > 8) == true
-  ##   assert anyIt(numbers, it > 9) == false
+  ## See also:
+  ## * `any proc<#any,openArray[T],proc(T)>`_
+  ## * `allIt template<#allIt.t,untyped,untyped>`_
+  ##
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    assert numbers.anyIt(it > 8) == true
+    assert numbers.anyIt(it > 9) == false
+
   var result = false
   for it {.inject.} in items(s):
     if pred:
@@ -555,19 +648,28 @@ template toSeq2(iter: iterator): untyped =
     result
 
 template toSeq*(iter: untyped): untyped =
-  ## Transforms any iterable into a sequence.
+  ## Transforms any iterable (anything that can be iterated over, e.g. with
+  ## a for-loop) into a sequence.
+  ##
   runnableExamples:
     let
-      numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-      odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1))
-    doAssert odd_numbers == @[1, 3, 5, 7, 9]
+      myRange = 1..5
+      mySet: set[int8] = {5'i8, 3, 1}
+    assert type(myRange) is HSlice[system.int, system.int]
+    assert type(mySet) is set[int8]
+
+    let
+      mySeq1 = toSeq(myRange)
+      mySeq2 = toSeq(mySet)
+    assert mySeq1 == @[1, 2, 3, 4, 5]
+    assert mySeq2 == @[1'i8, 3, 5]
 
   when compiles(toSeq1(iter)):
     toSeq1(iter)
   elif compiles(toSeq2(iter)):
     toSeq2(iter)
   else:
-    # overload for untyped, eg: `toSeq(myInlineIterator(3))`
+    # overload for untyped, e.g.: `toSeq(myInlineIterator(3))`
     when compiles(iter.len):
       block:
         evalOnceAs(iter2, iter, true)
@@ -597,20 +699,23 @@ template foldl*(sequence, operation: untyped): untyped =
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
   ## 3).
   ##
-  ## Example:
+  ## See also:
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
+  ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[5, 9, 11]
-  ##     addition = foldl(numbers, a + b)
-  ##     subtraction = foldl(numbers, a - b)
-  ##     multiplication = foldl(numbers, a * b)
-  ##     words = @["nim", "is", "cool"]
-  ##     concatenation = foldl(words, a & b)
-  ##   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"
+  runnableExamples:
+    let
+      numbers = @[5, 9, 11]
+      addition = foldl(numbers, a + b)
+      subtraction = foldl(numbers, a - b)
+      multiplication = foldl(numbers, a * b)
+      words = @["nim", "is", "cool"]
+      concatenation = foldl(words, a & b)
+    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"
+
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
   var result: type(s[0])
@@ -625,20 +730,22 @@ 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.
   ##
-  ## Example:
+  ## See also:
+  ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[0, 8, 1, 5]
-  ##     digits = foldl(numbers, a & (chr(b + ord('0'))), "")
-  ##   assert digits == "0815"
+  runnableExamples:
+    let
+      numbers = @[0, 8, 1, 5]
+      digits = foldl(numbers, a & (chr(b + ord('0'))), "")
+    assert digits == "0815"
+
   var result: type(first)
   result = first
   for x in items(sequence):
@@ -662,20 +769,23 @@ template foldr*(sequence, operation: untyped): untyped =
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
   ## (3))).
   ##
-  ## Example:
+  ## See also:
+  ## * `foldl template<#foldl.t,untyped,untyped>`_
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[5, 9, 11]
-  ##     addition = foldr(numbers, a + b)
-  ##     subtraction = foldr(numbers, a - b)
-  ##     multiplication = foldr(numbers, a * b)
-  ##     words = @["nim", "is", "cool"]
-  ##     concatenation = foldr(words, a & b)
-  ##   assert addition == 25, "Addition is (5+(9+(11)))"
-  ##   assert subtraction == 7, "Subtraction is (5-(9-(11)))"
-  ##   assert multiplication == 495, "Multiplication is (5*(9*(11)))"
-  ##   assert concatenation == "nimiscool"
+  runnableExamples:
+    let
+      numbers = @[5, 9, 11]
+      addition = foldr(numbers, a + b)
+      subtraction = foldr(numbers, a - b)
+      multiplication = foldr(numbers, a * b)
+      words = @["nim", "is", "cool"]
+      concatenation = foldr(words, a & b)
+    assert addition == 25, "Addition is (5+(9+(11)))"
+    assert subtraction == 7, "Subtraction is (5-(9-(11)))"
+    assert multiplication == 495, "Multiplication is (5*(9*(11)))"
+    assert concatenation == "nimiscool"
+
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
   var result: type(s[0])
@@ -687,41 +797,26 @@ template foldr*(sequence, operation: untyped): untyped =
     result = operation
   result
 
-template mapIt*(s, typ, op: untyped): untyped =
-  ## Convenience template around the ``map`` proc to reduce typing.
-  ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. You also need to pass as `typ` the type of the expression,
-  ## since the new returned sequence can have a different type than the
-  ## original.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let
-  ##     nums = @[1, 2, 3, 4]
-  ##     strings = nums.mapIt(string, $(4 * it))
-  ##   assert strings == @["4", "8", "12", "16"]
-  ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
-  ##   template instead.
-  var result: seq[typ] = @[]
-  for it {.inject.} in items(s):
-    result.add(op)
-  result
-
 template mapIt*(s: typed, op: untyped): untyped =
-  ## Convenience template around the ``map`` proc to reduce typing.
+  ## Returns a new sequence with the results of `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
   ## expression.
   ##
-  ## Example:
+  ## See also:
+  ## * `map proc<#map,openArray[T],proc(T)>`_
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version
   ##
-  ## .. code-block::
-  ##   let
-  ##     nums = @[1, 2, 3, 4]
-  ##     strings = nums.mapIt($(4 * it))
-  ##   assert strings == @["4", "8", "12", "16"]
+  runnableExamples:
+    let
+      nums = @[1, 2, 3, 4]
+      strings = nums.mapIt($(4 * it))
+    assert strings == @["4", "8", "12", "16"]
+
   when defined(nimHasTypeof):
     type outType = typeof((
       block:
@@ -751,6 +846,15 @@ template mapIt*(s: typed, op: untyped): untyped =
       result.add(op)
     result
 
+template mapIt*(s, typ, op: untyped): untyped {.error:
+  "Use 'mapIt(seq1, op)' - without specifying the type of the returned seqence".} =
+  ## **Deprecated since version 0.12.0:** Use the `mapIt(seq1, op) template
+  ## <#mapIt.t,typed,untyped>`_ instead.
+  var result: seq[typ] = @[]
+  for it {.inject.} in items(s):
+    result.add(op)
+  result
+
 template applyIt*(varSeq, op: untyped) =
   ## Convenience template around the mutable ``apply`` proc to reduce typing.
   ##
@@ -758,31 +862,38 @@ template applyIt*(varSeq, op: untyped) =
   ## expression. The expression has to return the same type as the sequence you
   ## are mutating.
   ##
-  ## Example:
+  ## See also:
+  ## * `apply proc<#apply,openArray[T],proc(T)_2>`_
+  ## * `mapIt template<#mapIt.t,typed,untyped>`_
   ##
-  ## .. code-block::
-  ##   var nums = @[1, 2, 3, 4]
-  ##   nums.applyIt(it * 3)
-  ##   assert nums[0] + nums[3] == 15
+  runnableExamples:
+     var nums = @[1, 2, 3, 4]
+     nums.applyIt(it * 3)
+     assert nums[0] + nums[3] == 15
+
   for i in low(varSeq) .. high(varSeq):
     let it {.inject.} = varSeq[i]
     varSeq[i] = op
 
 
 template newSeqWith*(len: int, init: untyped): untyped =
-  ## creates a new sequence, calling `init` to initialize each value.
+  ## Creates a new sequence of length `len`, calling `init` to initialize
+  ## each value of the sequence.
   ##
-  ## Example:
+  ## Useful for creating "2D" sequences - sequences containing other sequences
+  ## or to populate fields of the created sequence.
   ##
-  ## .. code-block::
-  ##   var seq2D = newSeqWith(20, newSeq[bool](10))
-  ##   seq2D[0][0] = true
-  ##   seq2D[1][0] = true
-  ##   seq2D[0][1] = true
-  ##
-  ##   import random
-  ##   var seqRand = newSeqWith(20, random(10))
-  ##   echo seqRand
+  runnableExamples:
+    ## Creates a seqence containing 5 bool sequences, 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, random(10))
+
   var result = newSeq[type(init)](len)
   for i in 0 ..< len:
     result[i] = init
@@ -804,7 +915,7 @@ 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``
+  ## 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:
   ##
@@ -819,16 +930,20 @@ macro mapLiterals*(constructor, op: untyped;
   ## .. code-block::
   ##   let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
   ##
-  ## If ``nested`` is true, the literals are replaced everywhere
-  ## in the ``constructor`` AST, otherwise only the first level
+  ## 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::
-  ##   mapLiterals((1, ("abc"), 2), float, nested=false)
-  ##
-  ## Produces::
-  ##
-  ##   (float(1), ("abc"), float(2))
+  ##   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.
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 1273cbc33..5da5d9243 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -14,8 +14,38 @@
 ## <manual.html#types-set-type>`_. Sets allow you to store any value that can be
 ## `hashed <hashes.html>`_ and they don't contain duplicate entries.
 ##
-## **Note**: The data types declared here have *value semantics*: This means
+## Common usages of sets:
+## * removing duplicates from a container by converting it with `toSet proc
+##   <#toSet,openArray[A]>`_ (see also `sequtils.deduplicate proc
+##   <sequtils.html#deduplicate,openArray[T],bool>`_)
+## * membership testing
+## * mathematical operations on two sets, such as
+##   `union <#union,HashSet[A],HashSet[A]>`_,
+##   `intersection <#intersection,HashSet[A],HashSet[A]>`_,
+##   `difference <#difference,HashSet[A],HashSet[A]>`_, and
+##   `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
+##
+## .. code-block::
+##   echo toSet([9, 5, 1])         # {9, 1, 5}
+##   echo toOrderedSet([9, 5, 1])  # {9, 5, 1}
+##
+##   let
+##     s1 = toSet([9, 5, 1])
+##     s2 = toSet([3, 5, 7])
+##
+##   echo s1 + s2    # {9, 1, 3, 5, 7}
+##   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.
+##
+## **See also:**
+## * `intsets module <intsets.html>`_ for efficient int sets
+## * `tables module <tables.html>`_ for hash tables
+
 
 import
   hashes, math
@@ -31,27 +61,24 @@ when not defined(nimhygiene):
 type
   KeyValuePair[A] = tuple[hcode: Hash, key: A]
   KeyValuePairSeq[A] = seq[KeyValuePair[A]]
-  HashSet* {.myShallow.}[A] = object ## \
+  HashSet* {.myShallow.} [A] = object ## \
     ## A generic hash set.
     ##
-    ## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_
+    ## Use `init proc <#init,HashSet[A],int>`_ or `initSet proc <#initSet,int>`_
     ## before calling other procs on it.
     data: KeyValuePairSeq[A]
     counter: int
 
+
+# ---------------------- helpers -----------------------------------
+
+const growthFactor = 2
+
 template default[T](t: typedesc[T]): T =
   ## Used by clear methods to get a default value.
   var v: T
   v
 
-proc clear*[A](s: var HashSet[A]) =
-  ## Clears the HashSet back to an empty state, without shrinking
-  ## any of the existing storage. O(n) where n is the size of the hash bucket.
-  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))
-
 # 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.} =
@@ -60,87 +87,6 @@ proc isEmpty(hcode: Hash): bool {.inline.} =
 proc isFilled(hcode: Hash): bool {.inline.} =
   result = hcode != 0
 
-proc isValid*[A](s: HashSet[A]): bool =
-  ## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
-  ##
-  ## Most operations over an uninitialized set will crash at runtime and
-  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
-  ## your own procs to verify that sets passed to your procs are correctly
-  ## initialized. Example:
-  ##
-  ## .. code-block ::
-  ##   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
-
-proc len*[A](s: HashSet[A]): int =
-  ## Returns the number of keys 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. Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   var values: HashSet[int]
-  ##   assert(not values.isValid)
-  ##   assert values.len == 0
-  result = s.counter
-
-proc card*[A](s: HashSet[A]): int =
-  ## Alias for `len() <#len,TSet[A]>`_.
-  ##
-  ## Card stands for the `cardinality
-  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
-  result = s.counter
-
-iterator items*[A](s: HashSet[A]): A =
-  ## Iterates over keys in the set `s`.
-  ##
-  ## If you need a sequence with the keys you can use `sequtils.toSeq()
-  ## <sequtils.html#toSeq>`_ on the iterator. Usage example:
-  ##
-  ## .. code-block::
-  ##   type
-  ##     pair = tuple[a, b: int]
-  ##   var
-  ##     a, b = initSet[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 == 2
-  ##   echo b
-  ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
-    if isFilled(s.data[h].hcode): yield s.data[h].key
-
-proc hash*[A](s: HashSet[A]): Hash =
-  ## hashing of HashSet
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
-    result = result xor s.data[h].hcode
-  result = !$result
-
-const
-  growthFactor = 2
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - 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.
-  ##
-  ## Internally, we want mustRehash(rightSize(x), x) == false.
-  result = nextPowerOfTwo(count * 3 div 2  +  4)
-
 proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = (h + 1) and maxHash
 
@@ -176,45 +122,6 @@ proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
 proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
   rawGetImpl()
 
-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. This is useful
-  ## when one overloaded 'hash' and '==' but still needs reference semantics
-  ## for sharing.
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  if index >= 0: result = s.data[index].key
-  else:
-    when compiles($key):
-      raise newException(KeyError, "key not found: " & $key)
-    else:
-      raise newException(KeyError, "key not found")
-
-proc mget*[A](s: var HashSet[A], key: A): var A {.deprecated.} =
-  ## returns the element that is actually stored in 's' which has the same
-  ## value as 'key' or raises the ``KeyError`` exception. This is useful
-  ## when one overloaded 'hash' and '==' but still needs reference semantics
-  ## for sharing. Use ```[]``` instead.
-  s[key]
-
-proc contains*[A](s: HashSet[A], key: A): bool =
-  ## Returns true iff `key` is in `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   assert(not values.contains(2))
-  ##   values.incl(2)
-  ##   assert values.contains(2)
-  ##   values.excl(2)
-  ##   assert(not values.contains(2))
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  result = index >= 0
-
 proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
                   hc: Hash, h: Hash) =
   rawInsertImpl()
@@ -250,34 +157,6 @@ template containsOrInclImpl() {.dirty.} =
     rawInsert(s, s.data, key, hc, -1 - index)
     inc(s.counter)
 
-proc incl*[A](s: var HashSet[A], key: A) =
-  ## Includes an element `key` in `s`.
-  ##
-  ## This doesn't do anything if `key` is already in `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   values.incl(2)
-  ##   values.incl(2)
-  ##   assert values.len == 1
-  assert s.isValid, "The set needs to be initialized."
-  inclImpl()
-
-proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
-  ## Includes all elements from `other` into `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   values.incl(2)
-  ##   var others = toSet([6, 7])
-  ##   values.incl(others)
-  ##   assert values.len == 3
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
-  for item in other: incl(s, item)
-
 template doWhile(a, b) =
   while true:
     b
@@ -309,51 +188,279 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
         r = s.data[i].hcode and msk    # "home" location of key@i
       shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
 
-proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
-  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
+proc mustRehash(length, counter: int): bool {.inline.} =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+template dollarImpl() {.dirty.} =
+  result = "{"
+  for key in items(s):
+    if result.len > 1: result.add(", ")
+    result.addQuoted(key)
+  result.add("}")
+
+proc rightSize*(count: Natural): int {.inline.}
+
+
+
+
+
+
+
+
+# ---------------------------------------------------------------------
+# ------------------------------ HashSet ------------------------------
+# ---------------------------------------------------------------------
+
+
+proc init*[A](s: var HashSet[A], initialSize=64) =
+  ## Initializes a hash set.
   ##
-  ## The difference with regards to the `excl() <#excl,TSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was not present in `s`. Example:
+  ## 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>`_ or `rightSize proc
+  ## <#rightSize,Natural>`_ from this module.
   ##
-  ## .. code-block::
-  ##  var s = toSet([2, 3, 6, 7])
-  ##  assert s.missingOrExcl(4) == true
-  ##  assert s.missingOrExcl(6) == false
-  exclImpl(s, key)
+  ## All set variables must be initialized before
+  ## use with other procs from this module, with the exception of `isValid proc
+  ## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
+  ##
+  ## You can call this proc on a previously initialized hash set, which will
+  ## discard all its values. This might be more convenient than iterating over
+  ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
+  ##
+  ## See also:
+  ## * `initSet proc <#initSet,int>`_
+  ## * `toSet proc <#toSet,openArray[A]>`_
+  runnableExamples:
+    var a: HashSet[int]
+    assert(not a.isValid)
+    init(a)
+    assert a.isValid
+
+  assert isPowerOfTwo(initialSize)
+  s.counter = 0
+  newSeq(s.data, initialSize)
+
+proc initSet*[A](initialSize=64): HashSet[A] =
+  ## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of
+  ## hash sets.
+  ##
+  ## Returns an empty hash set you can assign directly in ``var`` blocks in a
+  ## single line.
+  ##
+  ## See also:
+  ## * `toSet proc <#toSet,openArray[A]>`_
+  runnableExamples:
+    var a = initSet[int]()
+    assert a.isValid
+    a.incl(3)
+    assert len(a) == 1
+  result.init(initialSize)
+
+proc toSet*[A](keys: openArray[A]): HashSet[A] =
+  ## Creates a new hash set that contains the members of the given
+  ## collection (seq, array, or string) `keys`.
+  ##
+  ## Duplicates are removed.
+  ##
+  ## See also:
+  ## * `initSet proc <#initSet,int>`_
+  runnableExamples:
+    let
+      a = toSet([5, 3, 2])
+      b = toSet("abracadabra")
+    assert len(a) == 3
+    ## a == {2, 3, 5}
+    assert len(b) == 5
+    ## b == {'a', 'b', 'c', 'd', 'r'}
+
+  result = initSet[A](rightSize(keys.len))
+  for key in items(keys): result.incl(key)
+
+proc isValid*[A](s: HashSet[A]): bool =
+  ## Returns `true` if the set has been initialized (with `initSet proc
+  ## <#initSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
+  ##
+  ## Most operations over an uninitialized set will crash at runtime and
+  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
+  ## your own procs to verify that sets passed to your procs are correctly
+  ## initialized.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block ::
+  ##   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
+
+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.
+  ##
+  ## This is useful when one overloaded `hash` and `==` but still needs
+  ## reference semantics for sharing.
+  assert s.isValid, "The set needs to be initialized."
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  if index >= 0: result = s.data[index].key
+  else:
+    when compiles($key):
+      raise newException(KeyError, "key not found: " & $key)
+    else:
+      raise newException(KeyError, "key not found")
+
+proc contains*[A](s: HashSet[A], key: A): bool =
+  ## Returns true if `key` is in `s`.
+  ##
+  ## This allows the usage of `in` operator.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initSet[int]()
+    assert(not values.contains(2))
+    assert 2 notin values
+
+    values.incl(2)
+    assert values.contains(2)
+    assert 2 in values
+
+  assert s.isValid, "The set needs to be initialized."
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  result = index >= 0
+
+proc incl*[A](s: var HashSet[A], key: A) =
+  ## Includes an element `key` in `s`.
+  ##
+  ## This doesn't do anything if `key` is already in `s`.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,HashSet[A],A>`_ for excluding an element
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initSet[int]()
+    values.incl(2)
+    values.incl(2)
+    assert values.len == 1
+
+  assert s.isValid, "The set needs to be initialized."
+  inclImpl()
+
+proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
+  ## Includes all elements from `other` set into `s` (must be declared as `var`).
+  ##
+  ## This is the in-place version of `s + other <#+,HashSet[A],HashSet[A]>`_.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `incl proc <#incl,HashSet[A],A>`_ for including an element
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var
+      values = toSet([1, 2, 3])
+      others = toSet([3, 4, 5])
+    values.incl(others)
+    assert values.len == 5
+
+  assert s.isValid, "The set `s` needs to be initialized."
+  assert other.isValid, "The set `other` needs to be initialized."
+  for item in other: incl(s, item)
+
+proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
+  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
+  ##
+  ## The difference with regards to the `incl proc <#incl,HashSet[A],A>`_ 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,HashSet[A],A>`_ for including an element
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initSet[int]()
+    assert values.containsOrIncl(2) == false
+    assert values.containsOrIncl(2) == true
+    assert values.containsOrIncl(3) == false
+
+  assert s.isValid, "The set needs to be initialized."
+  containsOrInclImpl()
 
 proc excl*[A](s: var HashSet[A], key: A) =
   ## Excludes `key` from the set `s`.
   ##
-  ## This doesn't do anything if `key` is not found in `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var s = toSet([2, 3, 6, 7])
-  ##   s.excl(2)
-  ##   s.excl(2)
-  ##   assert s.len == 3
+  ## This doesn't do anything if `key` is not found in `s`.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],A>`_ for including an element
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var s = toSet([2, 3, 6, 7])
+    s.excl(2)
+    s.excl(2)
+    assert s.len == 3
   discard exclImpl(s, key)
 
 proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
-  ## Excludes everything in `other` from `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     numbers = toSet([1, 2, 3, 4, 5])
-  ##     even = toSet([2, 4, 6, 8])
-  ##   numbers.excl(even)
-  ##   echo numbers
-  ##   # --> {1, 3, 5}
+  ## Excludes all elements of `other` set from `s`.
+  ##
+  ## This is the in-place version of `s - other <#-,HashSet[A],HashSet[A]>`_.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `excl proc <#excl,HashSet[A],A>`_ for excluding an element
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var
+      numbers = toSet([1, 2, 3, 4, 5])
+      even = toSet([2, 4, 6, 8])
+    numbers.excl(even)
+    assert len(numbers) == 3
+    ## numbers == {1, 3, 5}
+
   assert s.isValid, "The set `s` needs to be initialized."
   assert other.isValid, "The set `other` needs to be initialized."
   for item in other: discard exclImpl(s, item)
 
+proc missingOrExcl*[A](s: var HashSet[A], key: A): 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,HashSet[A],A>`_ 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,HashSet[A],A>`_ for excluding an element
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var s = toSet([2, 3, 6, 7])
+    assert s.missingOrExcl(4) == true
+    assert s.missingOrExcl(6) == false
+    assert s.missingOrExcl(6) == true
+  exclImpl(s, key)
+
 proc pop*[A](s: var HashSet[A]): A =
   ## Remove and return an arbitrary element from the set `s`.
   ##
   ## Raises KeyError if the set `s` is empty.
   ##
+  ## See also:
+  ## * `clear proc <#clear,HashSet[A]>`_
+  runnableExamples:
+    var s = toSet([2, 1])
+    assert s.pop == 1
+    assert s.pop == 2
+    doAssertRaises(KeyError, echo s.pop)
+
   for h in 0..high(s.data):
     if isFilled(s.data[h].hcode):
       result = s.data[h].key
@@ -361,103 +468,64 @@ proc pop*[A](s: var HashSet[A]): A =
       return result
   raise newException(KeyError, "set is empty")
 
-proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
-  ## Includes `key` in the set `s` and tells if `key` was added to `s`.
+proc clear*[A](s: var HashSet[A]) =
+  ## Clears the HashSet back to an empty state, without shrinking
+  ## any of the existing storage.
   ##
-  ## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was already present in `s`. The
-  ## proc will return false if `key` was added as a new value to `s` during
-  ## this call. Example:
+  ## `O(n)` operation, where `n` is the size of the hash bucket.
   ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   assert values.containsOrIncl(2) == false
-  ##   assert values.containsOrIncl(2) == true
-  assert s.isValid, "The set needs to be initialized."
-  containsOrInclImpl()
+  ## See also:
+  ## * `pop proc <#pop,HashSet[A]>`_
+  runnableExamples:
+    var s = toSet([3, 5, 7])
+    clear(s)
+    assert len(s) == 0
 
-proc init*[A](s: var HashSet[A], initialSize=64) =
-  ## Initializes a hash set.
-  ##
-  ## The `initialSize` parameter needs to be a power of two. You can use
-  ## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ or `rightSize` to
-  ## guarantee that at runtime. All set variables must be initialized before
-  ## use with other procs from this module with the exception of `isValid()
-  ## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_.
-  ##
-  ## You can call this proc on a previously initialized hash set, which will
-  ## discard all its values. This might be more convenient than iterating over
-  ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
-  ##
-  ## .. code-block ::
-  ##   var a: HashSet[int]
-  ##   a.init(4)
-  ##   a.incl(2)
-  ##   a.init
-  ##   assert a.len == 0 and a.isValid
-  assert isPowerOfTwo(initialSize)
   s.counter = 0
-  newSeq(s.data, initialSize)
+  for i in 0..<s.data.len:
+    s.data[i].hcode = 0
+    s.data[i].key   = default(type(s.data[i].key))
 
-proc initSet*[A](initialSize=64): HashSet[A] =
-  ## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash
-  ## sets.
-  ##
-  ## Returns an empty hash set you can assign directly in ``var`` blocks in a
-  ## single line. Example:
+proc len*[A](s: HashSet[A]): int =
+  ## Returns the number of elements in `s`.
   ##
-  ## .. code-block ::
-  ##   var a = initSet[int](4)
-  ##   a.incl(2)
-  result.init(initialSize)
+  ## 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 = toSet([3, 5, 7])
+    assert len(s) == 3
+  result = s.counter
 
-proc toSet*[A](keys: openArray[A]): HashSet[A] =
-  ## Creates a new hash set that contains the given `keys`.
-  ##
-  ## Example:
+proc card*[A](s: HashSet[A]): int =
+  ## Alias for `len() <#len,HashSet[A]>`_.
   ##
-  ## .. code-block::
-  ##   var numbers = toSet([1, 2, 3, 4, 5])
-  ##   assert numbers.contains(2)
-  ##   assert numbers.contains(4)
-  result = initSet[A](rightSize(keys.len))
-  for key in items(keys): result.incl(key)
-
-template dollarImpl() {.dirty.} =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.addQuoted(key)
-  result.add("}")
+  ## Card stands for the `cardinality
+  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
+  result = s.counter
 
-proc `$`*[A](s: HashSet[A]): string =
-  ## Converts the set `s` to a string, mostly for logging purposes.
-  ##
-  ## Don't use this proc for serialization, the representation may change at
-  ## any moment and values are not escaped. Example:
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   echo toSet([2, 4, 5])
-  ##   # --> {2, 4, 5}
-  ##   echo toSet(["no", "esc'aping", "is \" provided"])
-  ##   # --> {no, esc'aping, is " provided}
-  assert s.isValid, "The set needs to be initialized."
-  dollarImpl()
 
 proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the union of the sets `s1` and `s2`.
   ##
-  ## The union of two sets is represented mathematically as *A ∪ B* and is the
-  ## set of all objects that are members of `s1`, `s2` or both. Example:
+  ## The same as `s1 + s2 <#+,HashSet[A],HashSet[A]>`_.
   ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = union(a, b)
-  ##   assert c == toSet(["a", "b", "c"])
+  ## The union of two sets is represented mathematically as *A ∪ B* and is the
+  ## set of all objects that are members of `s1`, `s2` or both.
+  ##
+  ## See also:
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = union(a, b)
+    assert c == toSet(["a", "b", "c"])
+
   assert s1.isValid, "The set `s1` needs to be initialized."
   assert s2.isValid, "The set `s2` needs to be initialized."
   result = s1
@@ -466,16 +534,23 @@ proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
 proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the intersection of the sets `s1` and `s2`.
   ##
+  ## The same as `s1 * s2 <#*,HashSet[A],HashSet[A]>`_.
+  ##
   ## The intersection of two sets is represented mathematically as *A ∩ B* and
   ## is the set of all objects that are members of `s1` and `s2` at the same
-  ## time. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c == toSet(["b"])
+  ## time.
+  ##
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = intersection(a, b)
+    assert c == toSet(["b"])
+
   assert s1.isValid, "The set `s1` needs to be initialized."
   assert s2.isValid, "The set `s2` needs to be initialized."
   result = initSet[A](min(s1.data.len, s2.data.len))
@@ -485,16 +560,22 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
 proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the difference of the sets `s1` and `s2`.
   ##
+  ## The same as `s1 - s2 <#-,HashSet[A],HashSet[A]>`_.
+  ##
   ## The difference of two sets is represented mathematically as *A \ B* and is
   ## the set of all objects that are members of `s1` and not members of `s2`.
-  ## Example:
   ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = difference(a, b)
-  ##   assert c == toSet(["a"])
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = difference(a, b)
+    assert c == toSet(["a"])
+
   assert s1.isValid, "The set `s1` needs to be initialized."
   assert s2.isValid, "The set `s2` needs to be initialized."
   result = initSet[A]()
@@ -505,16 +586,23 @@ proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
 proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the symmetric difference of the sets `s1` and `s2`.
   ##
+  ## The same as `s1 -+- s2 <#-+-,HashSet[A],HashSet[A]>`_.
+  ##
   ## The symmetric difference of two sets is represented mathematically as *A △
   ## B* or *A ⊖ B* and is the set of all objects that are members of `s1` or
-  ## `s2` but not both at the same time. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = symmetricDifference(a, b)
-  ##   assert c == toSet(["a", "c"])
+  ## `s2` but not both at the same time.
+  ##
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = symmetricDifference(a, b)
+    assert c == toSet(["a", "c"])
+
   assert s1.isValid, "The set `s1` needs to be initialized."
   assert s2.isValid, "The set `s2` needs to be initialized."
   result = s1
@@ -522,32 +610,31 @@ proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
     if containsOrIncl(result, item): excl(result, item)
 
 proc `+`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `union(s1, s2) <#union>`_.
+  ## Alias for `union(s1, s2) <#union,HashSet[A],HashSet[A]>`_.
   result = union(s1, s2)
 
 proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `intersection(s1, s2) <#intersection>`_.
+  ## Alias for `intersection(s1, s2) <#intersection,HashSet[A],HashSet[A]>`_.
   result = intersection(s1, s2)
 
 proc `-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `difference(s1, s2) <#difference>`_.
+  ## Alias for `difference(s1, s2) <#difference,HashSet[A],HashSet[A]>`_.
   result = difference(s1, s2)
 
 proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_.
+  ## Alias for `symmetricDifference(s1, s2)
+  ## <#symmetricDifference,HashSet[A],HashSet[A]>`_.
   result = symmetricDifference(s1, s2)
 
 proc disjoint*[A](s1, s2: HashSet[A]): bool =
-  ## Returns true iff the sets `s1` and `s2` have no items in common.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##   assert disjoint(a, b) == false
-  ##   assert disjoint(a, b - a) == true
+  ## Returns `true` if the sets `s1` and `s2` have no items in common.
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+    assert disjoint(a, b) == false
+    assert disjoint(a, b - a) == true
+
   assert s1.isValid, "The set `s1` needs to be initialized."
   assert s2.isValid, "The set `s2` needs to be initialized."
   for item in s1:
@@ -558,30 +645,29 @@ proc `<`*[A](s, t: HashSet[A]): bool =
   ## Returns true if `s` is a strict or proper subset of `t`.
   ##
   ## A strict or proper subset `s` has all of its members in `t` but `t` has
-  ## more elements than `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c < a and c < b
-  ##   assert((a < a) == false)
+  ## more elements than `s`.
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = intersection(a, b)
+    assert c < a and c < b
+    assert(not (a < a))
   s.counter != t.counter and s <= t
 
 proc `<=`*[A](s, t: HashSet[A]): bool =
-  ## Returns true if `s` is subset of `t`.
+  ## Returns true if `s` is a subset of `t`.
   ##
   ## A subset `s` has all of its members in `t` and `t` doesn't necessarily
-  ## have more members than `s`. That is, `s` can be equal to `t`. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c <= a and c <= b
-  ##   assert((a <= a))
+  ## have more members than `s`. That is, `s` can be equal to `t`.
+  runnableExamples:
+    let
+      a = toSet(["a", "b"])
+      b = toSet(["b", "c"])
+      c = intersection(a, b)
+    assert c <= a and c <= b
+    assert a <= a
+
   result = false
   if s.counter > t.counter: return
   result = true
@@ -592,90 +678,109 @@ proc `<=`*[A](s, t: HashSet[A]): bool =
 
 proc `==`*[A](s, t: HashSet[A]): bool =
   ## Returns true if both `s` and `t` have the same members and set size.
+  runnableExamples:
+    var
+      a = toSet([1, 2])
+      b = toSet([2, 1])
+    assert a == b
+  s.counter == t.counter and s <= t
+
+proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
+  ## Returns a new set after applying `op` pric on each of the elements of
+  ##`data` set.
   ##
-  ## Example:
+  ## You can use this proc to transform the elements from a set.
+  runnableExamples:
+    let
+      a = toSet([1, 2, 3])
+      b = a.map(proc (x: int): string = $x)
+    assert b == toSet(["1", "2", "3"])
+
+  result = initSet[B]()
+  for item in data: result.incl(op(item))
+
+proc hash*[A](s: HashSet[A]): Hash =
+  ## Hashing of HashSet.
+  assert s.isValid, "The set needs to be initialized."
+  for h in 0..high(s.data):
+    result = result xor s.data[h].hcode
+  result = !$result
+
+proc `$`*[A](s: HashSet[A]): string =
+  ## Converts the set `s` to a string, mostly for logging and printing purposes.
+  ##
+  ## Don't use this proc for serialization, the representation may change at
+  ## any moment and values are not escaped.
+  ##
+  ## **Examples:**
   ##
   ## .. code-block::
-  ##   var
-  ##     a = toSet([1, 2])
-  ##     b = toSet([1])
-  ##   b.incl(2)
-  ##   assert a == b
-  s.counter == t.counter and s <= t
+  ##   echo toSet([2, 4, 5])
+  ##   # --> {2, 4, 5}
+  ##   echo toSet(["no", "esc'aping", "is \" provided"])
+  ##   # --> {no, esc'aping, is " provided}
+  assert s.isValid, "The set needs to be initialized."
+  dollarImpl()
 
-proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
-  ## Returns a new set after applying `op` on each of the elements of `data`.
+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.
+  ##
+  ## Internally, we want `mustRehash(rightSize(x), x) == false`.
+  result = nextPowerOfTwo(count * 3 div 2  +  4)
+
+
+
+iterator items*[A](s: HashSet[A]): A =
+  ## Iterates over elements of the set `s`.
   ##
-  ## You can use this proc to transform the elements from a set. Example:
+  ## If you need a sequence with the elelments you can use `sequtils.toSeq
+  ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
   ## .. code-block::
-  ##   var a = toSet([1, 2, 3])
-  ##   var b = a.map(proc (x: int): string = $x)
-  ##   assert b == toSet(["1", "2", "3"])
-  result = initSet[B]()
-  for item in data: result.incl(op(item))
+  ##   type
+  ##     pair = tuple[a, b: int]
+  ##   var
+  ##     a, b = initSet[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 == 2
+  ##   echo b
+  ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
+  assert s.isValid, "The set needs to be initialized."
+  for h in 0..high(s.data):
+    if isFilled(s.data[h].hcode): yield s.data[h].key
+
+
+
 
-# ------------------------------ ordered set ------------------------------
+
+
+
+
+# ---------------------------------------------------------------------
+# --------------------------- OrderedSet ------------------------------
+# ---------------------------------------------------------------------
 
 type
   OrderedKeyValuePair[A] = tuple[
     hcode: Hash, next: int, key: A]
   OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
-  OrderedSet* {.myShallow.}[A] = object ## \
+  OrderedSet* {.myShallow.} [A] = object ## \
     ## A generic hash set that remembers insertion order.
     ##
-    ## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]()
-    ## <#initOrderedSet>`_ before calling other procs on it.
+    ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
+    ## <#initOrderedSet,int>`_ before calling other procs on it.
     data: OrderedKeyValuePairSeq[A]
     counter, first, last: int
 
-proc clear*[A](s: var OrderedSet[A]) =
-  ## Clears the OrderedSet back to an empty state, without shrinking
-  ## any of the existing storage. O(n) where n is the size of the hash bucket.
-  s.counter = 0
-  s.first = -1
-  s.last = -1
-  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))
-
 
-proc isValid*[A](s: OrderedSet[A]): bool =
-  ## Returns `true` if the ordered set has been initialized with `initSet
-  ## <#initOrderedSet>`_.
-  ##
-  ## Most operations over an uninitialized ordered set will crash at runtime
-  ## and `assert <system.html#assert>`_ in debug builds. You can use this proc
-  ## in your own procs to verify that ordered sets passed to your procs are
-  ## correctly initialized. Example:
-  ##
-  ## .. code-block::
-  ##   proc saveTarotCards(cards: OrderedSet[int]) =
-  ##     assert cards.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
-  result = s.data.len > 0
-
-proc len*[A](s: OrderedSet[A]): int {.inline.} =
-  ## Returns the number of keys 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. Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   var values: OrderedSet[int]
-  ##   assert(not values.isValid)
-  ##   assert values.len == 0
-  result = s.counter
-
-proc card*[A](s: OrderedSet[A]): int {.inline.} =
-  ## Alias for `len() <#len,TOrderedSet[A]>`_.
-  ##
-  ## Card stands for the `cardinality
-  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
-  result = s.counter
+# ---------------------- helpers -----------------------------------
 
 template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
   var h = s.first
@@ -687,61 +792,12 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
       inc(idx)
     h = nxt
 
-iterator items*[A](s: OrderedSet[A]): A =
-  ## Iterates over keys in the ordered set `s` in insertion order.
-  ##
-  ## If you need a sequence with the keys you can use `sequtils.toSeq()
-  ## <sequtils.html#toSeq>`_ on the iterator. Usage example:
-  ##
-  ## .. code-block::
-  ##   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
-  ##   # --> Got 9
-  ##   # --> Got 2
-  ##   # --> Got 1
-  ##   # --> Got 5
-  ##   # --> Got 8
-  ##   # --> Got 4
-  assert s.isValid, "The set needs to be initialized."
-  forAllOrderedPairs:
-    yield s.data[h].key
-
-proc hash*[A](s: OrderedSet[A]): Hash =
-  ## hashing of OrderedSet
-  assert s.isValid, "The set needs to be initialized."
-  forAllOrderedPairs:
-    result = result !& s.data[h].hcode
-  result = !$result
-
-iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
-  assert s.isValid, "The set needs to be initialized"
-  forAllOrderedPairs:
-    yield (idx, s.data[h].key)
-
 proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
   rawGetKnownHCImpl()
 
 proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
   rawGetImpl()
 
-proc contains*[A](s: OrderedSet[A], key: A): bool =
-  ## Returns true iff `key` is in `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   assert(not values.contains(2))
-  ##   values.incl(2)
-  ##   assert values.contains(2)
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  result = index >= 0
-
 proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
                   key: A, hc: Hash, h: Hash) =
   rawInsertImpl()
@@ -764,33 +820,7 @@ proc enlarge[A](s: var OrderedSet[A]) =
       rawInsert(s, s.data, n[h].key, n[h].hcode, j)
     h = nxt
 
-proc incl*[A](s: var OrderedSet[A], key: A) =
-  ## Includes an element `key` in `s`.
-  ##
-  ## This doesn't do anything if `key` is already in `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   values.incl(2)
-  ##   values.incl(2)
-  ##   assert values.len == 1
-  assert s.isValid, "The set needs to be initialized."
-  inclImpl()
-
-proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
-  ## Includes all elements from `other` into `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   values.incl(2)
-  ##   var others = toOrderedSet([6, 7])
-  ##   values.incl(others)
-  ##   assert values.len == 3
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
-  for item in other: incl(s, item)
+proc isValid*[A](s: OrderedSet[A]): bool
 
 proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
   assert s.isValid, "The set needs to be initialized."
@@ -813,65 +843,37 @@ proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
         rawInsert(s, s.data, n[h].key, n[h].hcode, j)
     h = nxt
 
-proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
-  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
-  ##
-  ## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was not present in `s`. Example:
-  ##
-  ## .. code-block::
-  ##  var s = toOrderedSet([2, 3, 6, 7])
-  ##  assert s.missingOrExcl(4) == true
-  ##  assert s.missingOrExcl(6) == false
-  exclImpl(s, key)
 
 
-proc excl*[A](s: var OrderedSet[A], key: A) =
-  ## Excludes `key` from the set `s`. Efficiency: O(n).
-  ##
-  ## This doesn't do anything if `key` is not found in `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var s = toOrderedSet([2, 3, 6, 7])
-  ##   s.excl(2)
-  ##   s.excl(2)
-  ##   assert s.len == 3
-  discard exclImpl(s, key)
+# -----------------------------------------------------------------------
+
 
-proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
-  ## Includes `key` in the set `s` and tells if `key` was added to `s`.
-  ##
-  ## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc
-  ## is that this proc returns `true` if `key` was already present in `s`. The
-  ## proc will return false if `key` was added as a new value to `s` during
-  ## this call. Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   assert values.containsOrIncl(2) == false
-  ##   assert values.containsOrIncl(2) == true
-  assert s.isValid, "The set needs to be initialized."
-  containsOrInclImpl()
 
 proc init*[A](s: var OrderedSet[A], initialSize=64) =
   ## Initializes an ordered hash set.
   ##
-  ## The `initialSize` parameter needs to be a power of two. You can use
-  ## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ or `rightSize` to
-  ## guarantee that at runtime. All set variables must be initialized before
-  ## use with other procs from this module with the exception of `isValid()
-  ## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_.
+  ## 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>`_ or `rightSize proc
+  ## <#rightSize,Natural>`_ from this module.
   ##
-  ## You can call this proc on a previously initialized ordered hash set to
-  ## discard its values. At the moment this is the only proc to remove elements
-  ## from an ordered hash set. Example:
+  ## All set variables must be initialized before
+  ## use with other procs from this module, with the exception of `isValid proc
+  ## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
   ##
-  ## .. code-block ::
-  ##   var a: OrderedSet[int]
-  ##   a.init(4)
-  ##   a.incl(2)
-  ##   a.init
-  ##   assert a.len == 0 and a.isValid
+  ## You can call this proc on a previously initialized hash set, which will
+  ## discard all its values. This might be more convenient than iterating over
+  ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
+  ##
+  ## See also:
+  ## * `initOrderedSet proc <#initOrderedSet,int>`_
+  ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
+  runnableExamples:
+    var a: OrderedSet[int]
+    assert(not a.isValid)
+    init(a)
+    assert a.isValid
+
   assert isPowerOfTwo(initialSize)
   s.counter = 0
   s.first = -1
@@ -879,47 +881,215 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
   newSeq(s.data, initialSize)
 
 proc initOrderedSet*[A](initialSize=64): OrderedSet[A] =
-  ## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of
+  ## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of
   ## ordered hash sets.
   ##
-  ## Returns an empty ordered hash set you can assign directly in ``var``
-  ## blocks in a single line. Example:
+  ## Returns an empty ordered hash set you can assign directly in ``var`` blocks
+  ## in a single line.
   ##
-  ## .. code-block ::
-  ##   var a = initOrderedSet[int](4)
-  ##   a.incl(2)
+  ## See also:
+  ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
+  runnableExamples:
+    var a = initOrderedSet[int]()
+    assert a.isValid
+    a.incl(3)
+    assert len(a) == 1
   result.init(initialSize)
 
 proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
-  ## Creates a new ordered hash set that contains the given `keys`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var numbers = toOrderedSet([1, 2, 3, 4, 5])
-  ##   assert numbers.contains(2)
-  ##   assert numbers.contains(4)
+  ## Creates a new hash set that contains the members of the given
+  ## collection (seq, array, or string) `keys`.
+  ##
+  ## Duplicates are removed.
+  ##
+  ## See also:
+  ## * `initOrderedSet proc <#initOrderedSet,int>`_
+  runnableExamples:
+    let
+      a = toOrderedSet([5, 3, 2])
+      b = toOrderedSet("abracadabra")
+    assert len(a) == 3
+    ## a == {5, 3, 2} # different than in HashSet
+    assert len(b) == 5
+    ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet
+
   result = initOrderedSet[A](rightSize(keys.len))
   for key in items(keys): result.incl(key)
 
-proc `$`*[A](s: OrderedSet[A]): string =
-  ## Converts the ordered hash set `s` to a string, mostly for logging purposes.
+proc isValid*[A](s: OrderedSet[A]): bool =
+  ## Returns `true` if the set has been initialized (with `initSet proc
+  ## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_).
   ##
-  ## Don't use this proc for serialization, the representation may change at
-  ## any moment and values are not escaped. Example:
+  ## Most operations over an uninitialized set will crash at runtime and
+  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
+  ## your own procs to verify that sets passed to your procs are correctly
+  ## initialized.
   ##
-  ## Example:
+  ## **Examples:**
   ##
-  ## .. code-block::
-  ##   echo toOrderedSet([2, 4, 5])
-  ##   # --> {2, 4, 5}
-  ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
-  ##   # --> {no, esc'aping, is " provided}
+  ## .. code-block ::
+  ##   proc savePreferences(options: OrderedSet[string]) =
+  ##     assert options.isValid, "Pass an initialized set!"
+  ##     # Do stuff here, may crash in release builds!
+  result = s.data.len > 0
+
+proc contains*[A](s: OrderedSet[A], key: A): bool =
+  ## Returns true if `key` is in `s`.
+  ##
+  ## This allows the usage of `in` operator.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    assert(not values.contains(2))
+    assert 2 notin values
+
+    values.incl(2)
+    assert values.contains(2)
+    assert 2 in values
+
   assert s.isValid, "The set needs to be initialized."
-  dollarImpl()
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  result = index >= 0
+
+proc incl*[A](s: var OrderedSet[A], key: A) =
+  ## Includes an element `key` in `s`.
+  ##
+  ## This doesn't do anything if `key` is already in `s`.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,OrderedSet[A],A>`_ for excluding an element
+  ## * `incl proc <#incl,HashSet[A],OrderedSet[A]>`_ for including other set
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    values.incl(2)
+    values.incl(2)
+    assert values.len == 1
+
+  assert s.isValid, "The set needs to be initialized."
+  inclImpl()
+
+proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
+  ## Includes all elements from the OrderedSet `other` into
+  ## HashSet `s` (must be declared as `var`).
+  ##
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_ for including an element
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var
+      values = toSet([1, 2, 3])
+      others = toOrderedSet([3, 4, 5])
+    values.incl(others)
+    assert values.len == 5
+  assert s.isValid, "The set `s` needs to be initialized."
+  assert other.isValid, "The set `other` needs to be initialized."
+  for item in other: incl(s, item)
+
+proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
+  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
+  ##
+  ## The difference with regards to the `incl proc <#incl,OrderedSet[A],A>`_ 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,OrderedSet[A],A>`_ for including an element
+  ## * `missingOrExcl proc <#missingOrExcl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    assert values.containsOrIncl(2) == false
+    assert values.containsOrIncl(2) == true
+    assert values.containsOrIncl(3) == false
+
+  assert s.isValid, "The set needs to be initialized."
+  containsOrInclImpl()
+
+proc excl*[A](s: var OrderedSet[A], key: A) =
+  ## Excludes `key` from the set `s`. Efficiency: `O(n)`.
+  ##
+  ## This doesn't do anything if `key` is not found in `s`.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_ for including an element
+  ## * `missingOrExcl proc <#missingOrExcl,OrderedSet[A],A>`_
+  runnableExamples:
+    var s = toOrderedSet([2, 3, 6, 7])
+    s.excl(2)
+    s.excl(2)
+    assert s.len == 3
+  discard exclImpl(s, key)
+
+proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
+  ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`.
+  ## Efficiency: O(n).
+  ##
+  ## The difference with regards to the `excl proc <#excl,OrderedSet[A],A>`_ 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,OrderedSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var s = toOrderedSet([2, 3, 6, 7])
+    assert s.missingOrExcl(4) == true
+    assert s.missingOrExcl(6) == false
+    assert s.missingOrExcl(6) == true
+  exclImpl(s, key)
+
+proc clear*[A](s: var OrderedSet[A]) =
+  ## Clears the OrderedSet back to an empty state, without shrinking
+  ## any of the existing storage.
+  ##
+  ## `O(n)` operation where `n` is the size of the hash bucket.
+  runnableExamples:
+    var s = toOrderedSet([3, 5, 7])
+    clear(s)
+    assert len(s) == 0
+
+  s.counter = 0
+  s.first = -1
+  s.last = -1
+  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))
+
+proc len*[A](s: OrderedSet[A]): int {.inline.} =
+  ## 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: OrderedSet[string]
+    assert len(a) == 0
+    let s = toSet([3, 5, 7])
+    assert len(s) == 3
+  result = s.counter
+
+proc card*[A](s: OrderedSet[A]): int {.inline.} =
+  ## Alias for `len() <#len,OrderedSet[A]>`_.
+  ##
+  ## Card stands for the `cardinality
+  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
+  result = s.counter
 
 proc `==`*[A](s, t: OrderedSet[A]): bool =
   ## Equality for ordered sets.
+  runnableExamples:
+    let
+      a = toOrderedSet([1, 2])
+      b = toOrderedSet([2, 1])
+    assert(not (a == b))
+
   if s.counter != t.counter: return false
   var h = s.first
   var g = t.first
@@ -936,6 +1106,74 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
     g = nxg
   result = compared == s.counter
 
+proc hash*[A](s: OrderedSet[A]): Hash =
+  ## Hashing of OrderedSet.
+  assert s.isValid, "The set needs to be initialized."
+  forAllOrderedPairs:
+    result = result !& s.data[h].hcode
+  result = !$result
+
+proc `$`*[A](s: OrderedSet[A]): string =
+  ## Converts the ordered hash set `s` to a string, mostly for logging and
+  ## printing purposes.
+  ##
+  ## Don't use this proc for serialization, the representation may change at
+  ## any moment and values are not escaped.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   echo toOrderedSet([2, 4, 5])
+  ##   # --> {2, 4, 5}
+  ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
+  ##   # --> {no, esc'aping, is " provided}
+  assert s.isValid, "The set needs to be initialized."
+  dollarImpl()
+
+
+
+iterator items*[A](s: OrderedSet[A]): A =
+  ## Iterates over keys in the ordered set `s` in insertion order.
+  ##
+  ## If you need a sequence with the elelments you can use `sequtils.toSeq
+  ## template <sequtils.html#toSeq.t,untyped>`_.
+  ##
+  ## .. code-block::
+  ##   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
+  ##   # --> Got 9
+  ##   # --> Got 2
+  ##   # --> Got 1
+  ##   # --> Got 5
+  ##   # --> Got 8
+  ##   # --> Got 4
+  assert s.isValid, "The set needs to be initialized."
+  forAllOrderedPairs:
+    yield s.data[h].key
+
+
+iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
+  ## Iterates through (position, value) tuples of OrderedSet `s`.
+  runnableExamples:
+    let a = toOrderedSet("abracadabra")
+    var p = newSeq[(int, char)]()
+    for x in pairs(a):
+      p.add(x)
+    assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
+
+  assert s.isValid, "The set needs to be initialized"
+  forAllOrderedPairs:
+    yield (idx, s.data[h].key)
+
+
+
+# -----------------------------------------------------------------------
+
+
+
 when isMainModule and not defined(release):
   proc testModule() =
     ## Internal micro test to validate docstrings and such.
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 9a5bffcef..2cdc62996 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## An ``include`` file for the different table implementations.
+# An ``include`` file for the different table implementations.
 
 # 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.
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index f46a368b1..84ec422d4 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -9,16 +9,21 @@
 
 ## 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. ``Table`` is the usual hash table,
-## ``OrderedTable`` is like ``Table`` but remembers insertion order
-## and ``CountTable`` is a mapping from a key to its number of occurrences.
+## 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,
+## * `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.
-## For **reference** semantics use the ``Ref`` variant: ``TableRef``,
-## ``OrderedTableRef``, ``CountTableRef``.
 ##
-## To give an example, when ``a`` is a Table, then ``var b = a`` gives ``b``
+## For `ref semantics<manual.html#types-ref-and-pointer-types>`_
+## 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:
 ##
@@ -35,8 +40,8 @@
 ##   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`` reference the same data structure:
+## 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
@@ -51,27 +56,111 @@
 ##   echo a, b  # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three}
 ##   echo a == b  # output: true
 ##
+## ----
+##
+## 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 exists, 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"]}
+##
+##
 ##
-## Here is an example of ``CountTable`` usage:
+## 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
+## ----------
+##
+## `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
 ##
-## .. code-block:: nim
 ##   let myString = "abracadabra"
-##   var myTable = initCountTable[char]()
+##   let letterFrequencies = toCountTable(myString)
+##   echo letterFrequencies
+##   # 'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
+##
+## 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,int>`_:
+##
+## .. code-block::
+##   import tables
 ##
+##   let myString = "abracadabra"
+##   var letterFrequencies = initCountTable[char]()
 ##   for c in myString:
-##     myTable.inc(c)
+##     letterFrequencies.inc(c)
+##   echo letterFrequencies
+##   # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
+##
+## ----
+##
 ##
-##   echo myTable  # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
 ##
+## Hashing
+## -------
 ##
 ## 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:
 ##
 ##   Error: type mismatch: got (Person)
 ##   but expected one of:
-##   hashes.hash(x: openarray[A]): Hash
+##   hashes.hash(x: openArray[A]): Hash
 ##   hashes.hash(x: int): Hash
 ##   hashes.hash(x: float): Hash
 ##   …
@@ -89,6 +178,8 @@
 ## example implementing only ``hash`` suffices:
 ##
 ## .. code-block::
+##   import tables, hashes
+##
 ##   type
 ##     Person = object
 ##       firstName, lastName: string
@@ -111,45 +202,50 @@
 ##   p2.firstName = "소진"
 ##   p2.lastName = "박"
 ##   salaries[p2] = 45_000
+##
+## ----
+##
+## 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
-  hashes, math
+
+import hashes, math
 
 include "system/inclrtl"
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
   KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
-  Table*[A, B] = object ## generic hash table
+  Table*[A, B] = object
+    ## Generic hash table, consisting of a key-value pair.
+    ##
+    ## `data` and `counter` are internal implementation details which
+    ## can't be accessed.
+    ##
+    ## For creating an empty Table, use `initTable proc<#initTable,int>`_.
     data: KeyValuePairSeq[A, B]
     counter: int
-  TableRef*[A,B] = ref Table[A, B]
+  TableRef*[A,B] = ref Table[A, B] ## Ref version of `Table<#Table>`_.
+    ##
+    ## For creating a new empty TableRef, use `newTable proc
+    ## <#newTable,int>`_.
+
+
+# ------------------------------ helpers ---------------------------------
 
 template maxHash(t): untyped = high(t.data)
 template dataLen(t): untyped = len(t.data)
 
 include tableimpl
 
-proc clear*[A, B](t: var Table[A, B]) =
-  ## resets the table so that it is empty.
-  clearImpl()
-
-proc clear*[A, B](t: TableRef[A, B]) =
-  ## resets the ref table so that it is empty.
-  clearImpl()
-
-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.
-  ##
-  ## Internally, we want mustRehash(rightSize(x), x) == false.
-  result = nextPowerOfTwo(count * 3 div 2  +  4)
-
-proc len*[A, B](t: Table[A, B]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc rightSize*(count: Natural): int {.inline.}
 
 template get(t, key): untyped =
   ## retrieves the value at ``t[key]``. The value can be modified.
@@ -176,36 +272,340 @@ template getOrDefaultImpl(t, key, default: untyped): untyped =
   var index = rawGet(t, key, hc)
   result = if index >= 0: t.data[index].val else: default
 
-proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  get(t, key)
+template dollarImpl(): untyped {.dirty.} =
+  if t.len == 0:
+    result = "{:}"
+  else:
+    result = "{"
+    for key, val in pairs(t):
+      if result.len > 1: result.add(", ")
+      result.addQuoted(key)
+      result.add(": ")
+      result.addQuoted(val)
+    result.add("}")
 
-proc `[]`*[A, B](t: var Table[A, B], key: A): var B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
+proc enlarge[A, B](t: var Table[A, B]) =
+  var n: KeyValuePairSeq[A, B]
+  newSeq(n, len(t.data) * growthFactor)
+  swap(t.data, n)
+  for i in countup(0, high(n)):
+    let eh = n[i].hcode
+    if isFilled(eh):
+      var j: Hash = eh and maxHash(t)
+      while isFilled(t.data[j].hcode):
+        j = nextTry(j, maxHash(t))
+      rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
+
+template equalsImpl(s, t: typed): typed =
+  if s.counter == t.counter:
+    # different insertion orders mean different 'data' seqs, so we have
+    # to use the slow route here:
+    for key, val in s:
+      if not t.hasKey(key): return false
+      if t.getOrDefault(key) != val: return false
+    return true
+
+
+
+# -------------------------------------------------------------------
+# ------------------------------ Table ------------------------------
+# -------------------------------------------------------------------
+
+proc initTable*[A, B](initialSize=64): 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.
+  ##
+  ## See also:
+  ## * `toTable proc<#toTable,openArray[]>`_
+  ## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
+  runnableExamples:
+    let
+      a = initTable[int, string]()
+      b = initTable[char, seq[int]]()
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  newSeq(result.data, initialSize)
+
+proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
+  ## Creates a new hash table that contains the given ``pairs``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `initTable proc<#initTable,int>`_
+  ## * `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))
+  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]``.
+  ##
   ## 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.
+  ##
+  ## 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
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in
+  ##   the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
   get(t, key)
 
-proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
+proc `[]`*[A, B](t: var Table[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.
-  ## Use ``[]`` instead.
+  ##
+  ## 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
+  ##   (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 `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `del proc<#del,Table[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = initTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.toTable
+  putImpl(enlarge)
+
+proc hasKey*[A, B](t: Table[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,Table[A,B],A>`_ for use with the `in` operator
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `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
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  var hc: Hash
+  result = rawGet(t, key, hc) >= 0
+
+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.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  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``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
+  hasKeyOrPutImpl(enlarge)
+
 proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## 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:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``.
+  ## 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
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   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
+  ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
+  mgetOrPutImpl(enlarge)
+
+proc len*[A, B](t: Table[A, B]): int =
+  ## 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.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
+  addImpl(enlarge)
+
+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.
+  ##
+  ## See also:
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.toTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.toTable
+  delImpl()
+
+proc take*[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
+  ## unchanged.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'a': 5, 'b': 9, 'c': 13}.toTable
+      i: int
+    doAssert a.take('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.toTable
+    doAssert i == 9
+    i = 0
+    doAssert a.take('z', i) == false
+    doAssert a == {'a': 5, 'c': 13}.toTable
+    doAssert i == 0
+
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = index >= 0
+  if result:
+    shallowCopy(val, t.data[index].val)
+    delImplIdx(t, index)
+
+proc clear*[A, B](t: var Table[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
+
+proc `$`*[A, B](t: Table[A, B]): string =
+  ## 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
+  ## tables contains the same key-value pairs. Insert order does not matter.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.toTable
+      b = {'b': 9, 'c': 13, 'a': 5}.toTable
+    doAssert a == b
+  equalsImpl(s, t)
+
+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.
+  ##
+  ## Internally, we want mustRehash(rightSize(x), x) == false.
+  result = nextPowerOfTwo(count * 3 div 2  +  4)
+
+proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
+  ## Index the collection with the proc provided.
+  # TODO: As soon as supported, change collection: A to collection: A[B]
+  result = initTable[C, B]()
+  for item in collection:
+    result[index(item)] = item
+
+
+
 template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
-  ## retrieves the value at ``t[key]``.
+  ## Retrieves the value at ``t[key]``.
+  ##
   ## ``value`` can be modified in the scope of the ``withValue`` call.
   ##
   ## .. code-block:: nim
@@ -225,7 +625,8 @@ 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]``.
+  ## Retrieves the value at ``t[key]``.
+  ##
   ## ``value`` can be modified in the scope of the ``withValue`` call.
   ##
   ## .. code-block:: nim
@@ -248,370 +649,535 @@ template withValue*[A, B](t: var Table[A, B], key: A,
   else:
     body2
 
-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``.
-  var h: Hash = genHash(key) and high(t.data)
-  while isFilled(t.data[h].hcode):
-    if t.data[h].key == key:
-      yield t.data[h].val
-    h = nextTry(h, high(t.data))
-
-proc hasKey*[A, B](t: Table[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  var hc: Hash
-  result = rawGet(t, key, hc) >= 0
-
-proc contains*[A, B](t: Table[A, B], key: A): bool =
-  ## alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
 
 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]>`_
+  ## * `keys iterator<#keys.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.toTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
 iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values
-  ## can be modified.
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
 iterator keys*[A, B](t: Table[A, B]): A =
-  ## iterates over any key in the table ``t``.
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].key
 
 iterator values*[A, B](t: Table[A, B]): B =
-  ## iterates over any value in the table ``t``.
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `keys iterator<#keys.i,Table[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,Table[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for v in a.values:
+      doAssert v.len == 4
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
 iterator mvalues*[A, B](t: var Table[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`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
-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.
-  delImpl()
-
-proc take*[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
-  ## unchanged.
-  var hc: Hash
-  var index = rawGet(t, key, hc)
-  result = index >= 0
-  if result:
-    shallowCopy(val, t.data[index].val)
-    delImplIdx(t, index)
-
-proc enlarge[A, B](t: var Table[A, B]) =
-  var n: KeyValuePairSeq[A, B]
-  newSeq(n, len(t.data) * growthFactor)
-  swap(t.data, n)
-  for i in countup(0, high(n)):
-    let eh = n[i].hcode
-    if isFilled(eh):
-      var j: Hash = eh and maxHash(t)
-      while isFilled(t.data[j].hcode):
-        j = nextTry(j, maxHash(t))
-      rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
-
-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
-  ## returning a value which can be modified.
-  mgetOrPutImpl(enlarge)
-
-proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  hasKeyOrPutImpl(enlarge)
-
-proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  putImpl(enlarge)
-
-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.
-  ## This can introduce duplicate keys into the table!
-  addImpl(enlarge)
-
-proc len*[A, B](t: TableRef[A, B]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
-
-proc initTable*[A, B](initialSize=64): Table[A, B] =
-  ## creates a new hash table that is empty.
+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``.
   ##
-  ## ``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)
-  result.counter = 0
-  newSeq(result.data, initialSize)
+  ## Used if you have a table with duplicate keys (as a result of using
+  ## `add proc<#add,Table[A,B],A,B>`_).
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = {'a': 3, 'b': 5}.toTable
+  ##   for i in 1..3:
+  ##     a.add('z', 10*i)
+  ##   echo a # {'a': 3, 'b': 5, 'z': 10, 'z': 20, 'z': 30}
+  ##
+  ##   for v in a.allValues('z'):
+  ##     echo v
+  ##   # 10
+  ##   # 20
+  ##   # 30
+  var h: Hash = genHash(key) and high(t.data)
+  while isFilled(t.data[h].hcode):
+    if t.data[h].key == key:
+      yield t.data[h].val
+    h = nextTry(h, high(t.data))
 
-proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
-  ## creates a new hash table that contains the given ``pairs``.
-  result = initTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result[key] = val
 
-template dollarImpl(): untyped {.dirty.} =
-  if t.len == 0:
-    result = "{:}"
-  else:
-    result = "{"
-    for key, val in pairs(t):
-      if result.len > 1: result.add(", ")
-      result.addQuoted(key)
-      result.add(": ")
-      result.addQuoted(val)
-    result.add("}")
 
-proc `$`*[A, B](t: Table[A, B]): string =
-  ## the ``$`` operator for hash tables.
-  dollarImpl()
+# -------------------------------------------------------------------
+# ---------------------------- TableRef -----------------------------
+# -------------------------------------------------------------------
 
-proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
 
-template equalsImpl(s, t: typed): typed =
-  if s.counter == t.counter:
-    # different insertion orders mean different 'data' seqs, so we have
-    # to use the slow route here:
-    for key, val in s:
-      if not t.hasKey(key): return false
-      if t.getOrDefault(key) != val: return false
-    return true
+proc newTable*[A, B](initialSize=64): 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`
+  runnableExamples:
+    let
+      a = newTable[int, string]()
+      b = newTable[char, seq[int]]()
+  new(result)
+  result[] = initTable[A, B](initialSize)
 
-proc `==`*[A, B](s, t: Table[A, B]): bool =
-  ## The ``==`` operator for hash tables. Returns ``true`` iff the content of both
-  ## tables contains the same key-value pairs. Insert order does not matter.
-  equalsImpl(s, t)
+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.
+  ##
+  ## See also:
+  ## * `newTable proc<#newTable,int>`_
+  ## * `toTable proc<#toTable,openArray[]>`_ for a `Table` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = newTable(a)
+    assert b == {'a': 5, 'b': 9}.newTable
+  new(result)
+  result[] = toTable[A, B](pairs)
 
-proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[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 = initTable[C, B]()
+  result = newTable[C, B]()
   for item in collection:
     result[index(item)] = item
 
-iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
-
-iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values
-  ## can be modified.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
-
-iterator keys*[A, B](t: TableRef[A, B]): A =
-  ## iterates over any key in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].key
+proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## 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.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   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
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ for checking if a key is in
+  ##   the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
+  result = t[][key]
 
-iterator values*[A, B](t: TableRef[A, B]): B =
-  ## iterates over any value in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].val
+proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `del proc<#del,TableRef[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = newTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.newTable
+  t[][key] = val
 
-iterator mvalues*[A, B](t: TableRef[A, B]): var B =
-  ## iterates over any value in the table ``t``. The values can be modified.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].val
+proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,TableRef[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  result = t[].hasKey(key)
 
-proc `[]`*[A, B](t: TableRef[A, B], key: A): var B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  result = t[][key]
+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.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  return hasKey[A, B](t, key)
 
-proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  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``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
+  t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## 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:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``.
+  ## 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
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   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.
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
   t[].mgetOrPut(key, val)
 
-proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  t[].hasKeyOrPut(key, val)
-
-proc contains*[A, B](t: TableRef[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
-
-proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  t[][key] = val
+proc len*[A, B](t: TableRef[A, B]): int =
+  ## 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.
-  ## This can introduce duplicate keys into the table!
+  ## 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
+  ## (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.
+  ##
+  ## See also:
+  ## * `take proc<#take,TableRef[A,B],A,B>`_
+  ## * `clear proc<#clear,TableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.newTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.newTable
   t[].del(key)
 
 proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
-  ## deletes the ``key`` from the table.
+  ## 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:
+  ## * `del proc<#del,TableRef[A,B],A>`_
+  ## * `clear proc<#clear,TableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'a': 5, 'b': 9, 'c': 13}.newTable
+      i: int
+    doAssert a.take('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 9
+    i = 0
+    doAssert a.take('z', i) == false
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 0
   result = t[].take(key, val)
 
-proc newTable*[A, B](initialSize=64): TableRef[A, B] =
-  new(result)
-  result[] = initTable[A, B](initialSize)
-
-proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
-  ## creates a new hash table that contains the given ``pairs``.
-  new(result)
-  result[] = toTable[A, B](pairs)
+proc clear*[A, B](t: TableRef[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
 
 proc `$`*[A, B](t: TableRef[A, B]): string =
-  ## The ``$`` operator for hash tables.
+  ## 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`` iff either both tables
-  ## are ``nil`` or none 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
+      a = {'a': 5, 'b': 9, 'c': 13}.newTable
+      b = {'b': 9, 'c': 13, 'a': 5}.newTable
+    doAssert a == b
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: equalsImpl(s[], t[])
 
-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
 
-# ------------------------------ ordered table ------------------------------
 
-type
-  OrderedKeyValuePair[A, B] = tuple[
-    hcode: Hash, next: int, key: A, val: B]
-  OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
-  OrderedTable* [A, B] = object ## table that remembers insertion order
-    data: OrderedKeyValuePairSeq[A, B]
-    counter, first, last: int
-  OrderedTableRef*[A, B] = ref OrderedTable[A, B]
-
-proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.newTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
-proc clear*[A, B](t: var OrderedTable[A, B]) =
-  ## resets the table so that it is empty.
-  clearImpl()
-  t.first = -1
-  t.last = -1
+iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values
+  ## can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
-proc clear*[A, B](t: var OrderedTableRef[A, B]) =
-  ## resets the table so that is is empty.
-  clear(t[])
+iterator keys*[A, B](t: TableRef[A, B]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].key
 
-template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
-  var h = t.first
-  while h >= 0:
-    var nxt = t.data[h].next
-    if isFilled(t.data[h].hcode): yieldStmt
-    h = nxt
+iterator values*[A, B](t: TableRef[A, B]): B =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,TableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for v in a.values:
+      doAssert v.len == 4
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].val
 
-iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order.
-  forAllOrderedPairs:
-    yield (t.data[h].key, t.data[h].val)
+iterator mvalues*[A, B](t: TableRef[A, B]): var B =
+  ## Iterates over any value in the table ``t``. The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].val
 
-iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order. The values can be modified.
-  forAllOrderedPairs:
-    yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A, B](t: OrderedTable[A, B]): A =
-  ## iterates over any key in the table ``t`` in insertion order.
-  forAllOrderedPairs:
-    yield t.data[h].key
 
-iterator values*[A, B](t: OrderedTable[A, B]): B =
-  ## iterates over any value in the table ``t`` in insertion order.
-  forAllOrderedPairs:
-    yield t.data[h].val
 
-iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
-  ## iterates over any value in the table ``t`` in insertion order. The values
-  ## can be modified.
-  forAllOrderedPairs:
-    yield t.data[h].val
 
-proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
-  rawGetKnownHCImpl()
 
-proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
-  rawGetDeepImpl()
 
-proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
-  rawGetImpl()
 
-proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  get(t, key)
+# ---------------------------------------------------------------------------
+# ------------------------------ OrderedTable -------------------------------
+# ---------------------------------------------------------------------------
 
-proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B{.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  get(t, key)
+type
+  OrderedKeyValuePair[A, B] = tuple[
+    hcode: Hash, next: int, key: A, val: B]
+  OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
+  OrderedTable* [A, B] = object
+    ## Hash table that remembers insertion order.
+    ##
+    ## For creating an empty OrderedTable, use `initOrderedTable proc
+    ## <#initOrderedTable,int>`_.
+    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>`_.
 
-proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  get(t, key)
 
-proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
-  ## integer type).
-  getOrDefaultImpl(t, key)
+# ------------------------------ helpers ---------------------------------
 
-proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise,
-  ## ``default`` is returned.
-  getOrDefaultImpl(t, key, default)
+proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
+  rawGetKnownHCImpl()
 
-proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  var hc: Hash
-  result = rawGet(t, key, hc) >= 0
+proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
+  rawGetDeepImpl()
 
-proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
+proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
+  rawGetImpl()
 
 proc rawInsert[A, B](t: var OrderedTable[A, B],
                      data: var OrderedKeyValuePairSeq[A, B],
@@ -639,30 +1205,32 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) =
       rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
     h = nxt
 
-proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  putImpl(enlarge)
-
-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.
-  ## This can introduce duplicate keys into the table!
-  addImpl(enlarge)
+template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
+  var h = t.first
+  while h >= 0:
+    var nxt = t.data[h].next
+    if isFilled(t.data[h].hcode): yieldStmt
+    h = nxt
 
-proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``value`` if not present, either way
-  ## returning a value which can be modified.
-  mgetOrPutImpl(enlarge)
-
-proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  hasKeyOrPutImpl(enlarge)
+# ----------------------------------------------------------------------
 
 proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
-  ## creates a new ordered hash table that is empty.
+  ## 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.
   ##
-  ## ``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.
+  ## See also:
+  ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
+  ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
+  ##   `OrderedTableRef`
+  runnableExamples:
+    let
+      a = initOrderedTable[int, string]()
+      b = initOrderedTable[char, seq[int]]()
   assert isPowerOfTwo(initialSize)
   result.counter = 0
   result.first = -1
@@ -670,36 +1238,250 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
   newSeq(result.data, initialSize)
 
 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.
+  ##
+  ## See also:
+  ## * `initOrderedTable proc<#initOrderedTable,int>`_
+  ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for an
+  ##   `OrderedTableRef` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = toOrderedTable(a)
+    assert b == {'a': 5, 'b': 9}.toOrderedTable
   result = initOrderedTable[A, B](rightSize(pairs.len))
   for key, val in items(pairs): result[key] = val
 
-proc `$`*[A, B](t: OrderedTable[A, B]): string =
-  ## The ``$`` operator for ordered hash tables.
-  dollarImpl()
+proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## 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.
+  ##
+  ## 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
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a
+  ##   key is in the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
+  get(t, key)
 
-proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
-  ## The ``==`` operator for ordered hash tables. Returns true iff both the
-  ## content and the order are equal.
-  if s.counter != t.counter:
-    return false
-  var ht = t.first
-  var hs = s.first
-  while ht >= 0 and hs >= 0:
-    var nxtt = t.data[ht].next
-    var nxts = s.data[hs].next
-    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
-      if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val):
-        return false
-    ht = nxtt
-    hs = nxts
-  return true
+proc `[]`*[A, B](t: var OrderedTable[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.
+  ##
+  ## 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
+  ##   (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 `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `del proc<#del,OrderedTable[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = initOrderedTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.toOrderedTable
+  putImpl(enlarge)
+
+proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,OrderedTable[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `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
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  var hc: 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.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  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``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toOrderedTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
+  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
+  ## integer type).
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
+  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.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
+  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
+  ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
+  mgetOrPutImpl(enlarge)
+
+proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
+  ## 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.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ 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.
+  ##
+  ## O(n) complexity.
+  ##
+  ## See also:
+  ## * `clear proc<#clear,OrderedTable[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.toOrderedTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.toOrderedTable
+  var n: OrderedKeyValuePairSeq[A, B]
+  newSeq(n, len(t.data))
+  var h = t.first
+  t.first = -1
+  t.last = -1
+  swap(t.data, n)
+  let hc = genHash(key)
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      if n[h].hcode == hc and n[h].key == key:
+        dec t.counter
+      else:
+        var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
+        rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
+    h = nxt
+
+proc clear*[A, B](t: var OrderedTable[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTable[A,B],A>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
+  t.first = -1
+  t.last = -1
 
 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
-  ## sorts ``t`` according to ``cmp``. This modifies the internal list
+  ## 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
-  ## contrast to the ``sort`` for count tables).
+  ## contrast to the `sort proc<#sort,CountTable[A]>`_ for count tables).
+  runnableExamples:
+    var a = initOrderedTable[char, int]()
+    for i, c in "cab":
+      a[c] = 10*i
+    doAssert a == {'c': 0, 'a': 10, 'b': 20}.toOrderedTable
+    a.sort(system.cmp)
+    doAssert a == {'a': 10, 'b': 20, 'c': 0}.toOrderedTable
+
   var list = t.first
   var
     p, q, e, tail, oldhead: int
@@ -740,244 +1522,519 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
   t.first = list
   t.last = tail
 
-proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc `$`*[A, B](t: OrderedTable[A, B]): string =
+  ## The ``$`` operator for ordered hash tables. Used internally when calling
+  ## `echo` on a table.
+  dollarImpl()
 
-iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
+  ## The ``==`` operator for ordered hash tables. Returns ``true`` if both the
+  ## content and the order are equal.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+      b = {'b': 9, 'c': 13, 'a': 5}.toOrderedTable
+    doAssert a != b
+
+  if s.counter != t.counter:
+    return false
+  var ht = t.first
+  var hs = s.first
+  while ht >= 0 and hs >= 0:
+    var nxtt = t.data[ht].next
+    var nxts = s.data[hs].next
+    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
+      if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val):
+        return false
+    ht = nxtt
+    hs = nxts
+  return true
+
+
+
+iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
   ## order.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTable[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.toOrderedTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order. The values can be modified.
+iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
+  ## 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:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
-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: OrderedTable[A, B]): A =
+  ## Iterates over any key in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
   forAllOrderedPairs:
     yield t.data[h].key
 
-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: OrderedTable[A, B]): B =
+  ## Iterates over any value in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTable[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for v in a.values:
+      doAssert v.len == 4
   forAllOrderedPairs:
     yield t.data[h].val
 
-iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
-  ## iterates over any value in the table ``t`` in insertion order. The values
+iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
+  ## Iterates over any value in the table ``t`` (must be
+  ## declared as `var`) in insertion order. The values
   ## can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
   forAllOrderedPairs:
     yield t.data[h].val
 
+
+
+
+
+# ---------------------------------------------------------------------------
+# --------------------------- OrderedTableRef -------------------------------
+# ---------------------------------------------------------------------------
+
+proc newOrderedTable*[A, B](initialSize=64): 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
+  ##   `OrderedTable`
+  runnableExamples:
+    let
+      a = newOrderedTable[int, string]()
+      b = newOrderedTable[char, seq[int]]()
+  new(result)
+  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``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `newOrderedTable proc<#newOrderedTable,int>`_
+  ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ for an
+  ##   `OrderedTable` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    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)
+
+
 proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## 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.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   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
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for checking if
+  ##   a key is in the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
   result = t[][key]
 
-proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  result = t[][key]
+proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `del proc<#del,OrderedTableRef[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = newOrderedTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.newOrderedTable
+  t[][key] = val
+
+proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,OrderedTableRef[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  result = t[].hasKey(key)
+
+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.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  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``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newOrderedTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
+  result = t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## 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:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``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
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   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:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   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
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
   result = t[].mgetOrPut(key, val)
 
-proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``val``.
-  result = t[].hasKeyOrPut(key, val)
-
-proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
-
-proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
-
-proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  t[][key] = val
+proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
+  ## 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.
-  ## This can introduce duplicate keys into the table!
+  ## 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
+  ## (key, value) pair in the table without introducing duplicates.
   t[].add(key, val)
 
-proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
-  ## creates a new ordered hash table that is empty.
+proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
+  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
   ##
-  ## ``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.
-  new(result)
-  result[] = initOrderedTable[A, B](initialSize)
+  ## See also:
+  ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.newOrderedTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.newOrderedTable
+  t[].del(key)
 
-proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
-  ## creates a new ordered hash table that contains the given ``pairs``.
-  result = newOrderedTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result.add(key, val)
+proc clear*[A, B](t: var OrderedTableRef[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTable[A,B],A>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clear(t[])
+
+proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) =
+  ## 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
+  ## contrast to the `sort proc<#sort,CountTableRef[A]>`_ for count tables).
+  runnableExamples:
+    var a = newOrderedTable[char, int]()
+    for i, c in "cab":
+      a[c] = 10*i
+    doAssert a == {'c': 0, 'a': 10, 'b': 20}.newOrderedTable
+    a.sort(system.cmp)
+    doAssert a == {'a': 10, 'b': 20, 'c': 0}.newOrderedTable
+  t[].sort(cmp)
 
 proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
-  ## The ``$`` operator for ordered hash tables.
+  ## 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 iff either both
-  ## tables are ``nil`` or none 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
+      a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+      b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
+    doAssert a != b
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: result = s[] == t[]
 
-proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) =
-  ## sorts ``t`` according to ``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
-  ## contrast to the ``sort`` for count tables).
-  t[].sort(cmp)
-
-proc del*[A, B](t: var OrderedTable[A, B], key: A) =
-  ## deletes ``key`` from ordered hash table ``t``. O(n) complexity. Does nothing
-  ## if the key does not exist.
-  var n: OrderedKeyValuePairSeq[A, B]
-  newSeq(n, len(t.data))
-  var h = t.first
-  t.first = -1
-  t.last = -1
-  swap(t.data, n)
-  let hc = genHash(key)
-  while h >= 0:
-    var nxt = n[h].next
-    if isFilled(n[h].hcode):
-      if n[h].hcode == hc and n[h].key == key:
-        dec t.counter
-      else:
-        var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
-        rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
-    h = nxt
-
-proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
-  ## deletes ``key`` from ordered hash table ``t``. O(n) complexity.  Does nothing
-  ## if the key does not exist.
-  t[].del(key)
-
-# ------------------------------ count tables -------------------------------
-
-type
-  CountTable* [
-      A] = object ## table that counts the number of each key
-    data: seq[tuple[key: A, val: int]]
-    counter: int
-  CountTableRef*[A] = ref CountTable[A]
-
-proc len*[A](t: CountTable[A]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
 
-proc clear*[A](t: CountTableRef[A]) =
-  ## resets the table so that it is empty.
-  clearImpl()
 
-proc clear*[A](t: var CountTable[A]) =
-  ## resets the table so that it is empty.
-  clearImpl()
+iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## order.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.newOrderedTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
 
-iterator pairs*[A](t: CountTable[A]): (A, int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## order. The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values can
-  ## be modified.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
+  ## Iterates over any key in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+  forAllOrderedPairs:
+    yield t.data[h].key
 
-iterator keys*[A](t: CountTable[A]): A =
-  ## iterates over any key in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].key
+iterator values*[A, B](t: OrderedTableRef[A, B]): B =
+  ## Iterates over any value in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for v in a.values:
+      doAssert v.len == 4
+  forAllOrderedPairs:
+    yield t.data[h].val
 
-iterator values*[A](t: CountTable[A]): int =
-  ## iterates over any value in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].val
+iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
+  ## Iterates over any value in the table ``t`` in insertion order. The values
+  ## can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+  forAllOrderedPairs:
+    yield t.data[h].val
 
-iterator mvalues*[A](t: CountTable[A]): var int =
-  ## iterates over any value in the table ``t``. The values can be modified.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].val
 
-proc rawGet[A](t: CountTable[A], key: A): int =
-  var h: Hash = hash(key) and high(t.data) # start with real hash value
-  while t.data[h].val != 0:
-    if t.data[h].key == key: return h
-    h = nextTry(h, high(t.data))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
 
-template ctget(t, key: untyped): untyped =
-  var index = rawGet(t, key)
-  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")
 
-proc `[]`*[A](t: CountTable[A], key: A): int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``,
-  ## the ``KeyError`` exception is raised. One can check with ``hasKey``
-  ## whether the key exists.
-  ctget(t, key)
 
-proc `[]`*[A](t: var CountTable[A], key: A): var int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ctget(t, key)
 
-proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  ctget(t, key)
 
-proc getOrDefault*[A](t: CountTable[A], key: A): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, 0 (the
-  ## default initialization value of ``int``), is returned.
-  var index = rawGet(t, key)
-  if index >= 0: result = t.data[index].val
+# -------------------------------------------------------------------------
+# ------------------------------ CountTable -------------------------------
+# -------------------------------------------------------------------------
 
-proc getOrDefault*[A](t: CountTable[A], key: A, default: int): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
-  var index = rawGet(t, key)
-  result = if index >= 0: t.data[index].val else: default
+type
+  CountTable* [A] = object
+    ## Hash table that counts the number of each key.
+    ##
+    ## For creating an empty CountTable, use `initCountTable proc
+    ## <#initCountTable,int>`_.
+    data: seq[tuple[key: A, val: int]]
+    counter: int
+  CountTableRef*[A] = ref CountTable[A] ## Ref version of
+    ## `CountTable<#CountTable>`_.
+    ##
+    ## For creating a new empty CountTableRef, use `newCountTable proc
+    ## <#newCountTable,int>`_.
 
-proc hasKey*[A](t: CountTable[A], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = rawGet(t, key) >= 0
 
-proc contains*[A](t: CountTable[A], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A](t, key)
+# ------------------------------ helpers ---------------------------------
 
 proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
                   key: A, val: int) =
@@ -993,22 +2050,87 @@ proc enlarge[A](t: var CountTable[A]) =
     if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
+proc rawGet[A](t: CountTable[A], key: A): int =
+  var h: Hash = hash(key) and high(t.data) # start with real hash value
+  while t.data[h].val != 0:
+    if t.data[h].key == key: return h
+    h = nextTry(h, high(t.data))
+  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
+
+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 = 1)
+
+# ----------------------------------------------------------------------
+
+proc initCountTable*[A](initialSize=64): 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.
+  ##
+  ## See also:
+  ## * `toCountTable proc<#toCountTable,openArray[A]>`_
+  ## * `newCountTable proc<#newCountTable,int>`_ for creating a
+  ##   `CountTableRef`
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  newSeq(result.data, initialSize)
+
+proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
+  ## 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))
+  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.
+  ##
+  ## 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
+  ##   is in the table
+  ctget(t, key, 0)
+
+proc mget*[A](t: var CountTable[A], key: A): var int =
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
+  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  get(t, key)
+
 proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
-  ## puts 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,int>`_ for incrementing a
+  ##   value of a key
   assert val >= 0
-  var h = rawGet(t, key)
+  let h = rawGet(t, key)
   if h >= 0:
     t.data[h].val = val
   else:
     if mustRehash(len(t.data), t.counter): enlarge(t)
     rawInsert(t, t.data, key, val)
     inc(t.counter)
-    #h = -1 - h
-    #t.data[h].key = key
-    #t.data[h].val = val
 
 proc inc*[A](t: var CountTable[A], key: A, val = 1) =
-  ## increments ``t[key]`` by ``val``.
+  ## Increments ``t[key]`` by ``val`` (default: 1).
+  runnableExamples:
+    var a = toCountTable("aab")
+    a.inc('a')
+    a.inc('b', 10)
+    doAssert a == toCountTable("aaabbbbbbbbbbb")
   var index = rawGet(t, key)
   if index >= 0:
     inc(t.data[index].val, val)
@@ -1018,33 +2140,11 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
     rawInsert(t, t.data, key, val)
     inc(t.counter)
 
-proc initCountTable*[A](initialSize=64): CountTable[A] =
-  ## creates a new count table that is empty.
-  ##
-  ## ``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 in this module.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
-
-proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
-  ## creates a new count table with every key in ``keys`` having a count
-  ## of how many times it occurs in ``keys``.
-  result = initCountTable[A](rightSize(keys.len))
-  for key in items(keys): result.inc(key)
-
-proc `$`*[A](t: CountTable[A]): string =
-  ## The ``$`` operator for count tables.
-  dollarImpl()
-
-proc `==`*[A](s, t: CountTable[A]): bool =
-  ## The ``==`` operator for count tables. Returns ``true`` iff both tables
-  ## contain the same keys with the same count. Insert order does not matter.
-  equalsImpl(s, t)
-
 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
   var minIdx = -1
   for h in 0..high(t.data):
@@ -1054,7 +2154,10 @@ 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
   var maxIdx = 0
   for h in 1..high(t.data):
@@ -1062,11 +2165,49 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   result.key = t.data[maxIdx].key
   result.val = t.data[maxIdx].val
 
+proc hasKey*[A](t: CountTable[A], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,CountTable[A],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,CountTable[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  result = rawGet(t, key) >= 0
+
+proc contains*[A](t: CountTable[A], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,CountTable[A],A>`_ for use with
+  ## 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.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key
+  ##   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 clear*[A](t: var CountTable[A]) =
+  ## Resets the table so that it is empty.
+  clearImpl()
+
 proc sort*[A](t: var CountTable[A]) =
-  ## sorts the count table so that the entry with the highest counter comes
-  ## first. This is destructive! You must not modify ``t`` afterwards!
-  ## You can use the iterators ``pairs``, ``keys``, and ``values`` to iterate over
-  ## ``t`` in the sorted order.
+  ## Sorts the count table so that the entry with the highest counter comes
+  ## first.
+  ##
+  ## **This is destructive! You must not modify ``t`` afterwards!**
+  ##
+  ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_,
+  ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_
+  ## to iterate over ``t`` in the sorted order.
 
   # we use shellsort here; fast enough and simple
   var h = 1
@@ -1083,131 +2224,373 @@ proc sort*[A](t: var CountTable[A]) =
         if j < h: break
     if h == 1: break
 
-proc len*[A](t: CountTableRef[A]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
+  ## Merges the second table into the first one (must be declared as `var`).
+  runnableExamples:
+    var a = toCountTable("aaabbc")
+    let b = toCountTable("bcc")
+    a.merge(b)
+    doAssert a == toCountTable("aaabbbccc")
+  for key, value in t:
+    s.inc(key, value)
 
-iterator pairs*[A](t: CountTableRef[A]): (A, int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
+proc merge*[A](s, t: CountTable[A]): CountTable[A] =
+  ## Merges the two tables into a new one.
+  runnableExamples:
+    let
+      a = toCountTable("aaabbc")
+      b = toCountTable("bcc")
+    doAssert merge(a, b) == toCountTable("aaabbbccc")
+  result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
+  for table in @[s, t]:
+    for key, value in table:
+      result.inc(key, value)
+
+proc `$`*[A](t: CountTable[A]): string =
+  ## 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
+  ## 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``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_
+  ## * `keys iterator<#keys.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = toCountTable("abracadabra")
+  ##
+  ##   for k, v in pairs(a):
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: a
+  ##   # value: 5
+  ##   # key: b
+  ##   # value: 2
+  ##   # key: c
+  ##   # value: 1
+  ##   # key: d
+  ##   # value: 1
+  ##   # key: r
+  ##   # value: 2
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values can
-  ## be modified.
+iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for k, v in mpairs(a):
+      v = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A](t: CountTableRef[A]): A =
-  ## iterates over any key in the table ``t``.
+iterator keys*[A](t: CountTable[A]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for k in keys(a):
+      a[k] = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].key
 
-iterator values*[A](t: CountTableRef[A]): int =
-  ## iterates over any value in the table ``t``.
+iterator values*[A](t: CountTable[A]): int =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `keys iterator<#keys.i,CountTable[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTable[A]>`_
+  runnableExamples:
+    let a = toCountTable("abracadabra")
+    for v in values(a):
+      assert v < 10
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
-iterator mvalues*[A](t: CountTableRef[A]): var int =
-  ## iterates over any value in the table ``t``. The values can be modified.
+iterator mvalues*[A](t: var CountTable[A]): var int =
+  ## Iterates over any value in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for v in mvalues(a):
+      v = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
-proc `[]`*[A](t: CountTableRef[A], key: A): var int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  result = t[][key]
 
-proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  result = t[][key]
 
-proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, 0 (the
-  ## default initialization value of ``int``), is returned.
-  result = t[].getOrDefault(key)
 
-proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
-  result = t[].getOrDefault(key, default)
 
-proc hasKey*[A](t: CountTableRef[A], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
 
-proc contains*[A](t: CountTableRef[A], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A](t, key)
 
-proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
-  ## puts a ``(key, value)`` pair into ``t``. ``val`` has to be positive.
-  assert val > 0
-  t[][key] = val
+# ---------------------------------------------------------------------------
+# ---------------------------- CountTableRef --------------------------------
+# ---------------------------------------------------------------------------
 
-proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
-  ## increments ``t[key]`` by ``val``.
-  t[].inc(key, val)
+proc inc*[A](t: CountTableRef[A], key: A, val = 1)
 
 proc newCountTable*[A](initialSize=64): CountTableRef[A] =
-  ## creates a new count table that is empty.
+  ## 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.
   ##
-  ## ``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`` method in this module.
+  ## See also:
+  ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating
+  ##   a `CountTableRef` from a collection
+  ## * `initCountTable proc<#initCountTable,int>`_ for creating a
+  ##   `CountTable`
   new(result)
   result[] = initCountTable[A](initialSize)
 
 proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
-  ## creates a new count table with every key in ``keys`` having a count
-  ## of how many times it occurs in ``keys``.
+  ## 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)
 
+proc `[]`*[A](t: CountTableRef[A], key: A): int =
+  ## 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>`_
+  ## * `[]= 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
+  ##   is in the table
+  result = t[][key]
+
+proc mget*[A](t: CountTableRef[A], key: A): var int =
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
+  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  mget(t[], key)
+
+proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
+  ## 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
+
+proc inc*[A](t: CountTableRef[A], key: A, val = 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)
+
+proc smallest*[A](t: CountTableRef[A]): (A, int) =
+  ## 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]): (A, int) =
+  ## 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``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,CountTableRef[A],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,CountTableRef[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  result = t[].hasKey(key)
+
+proc contains*[A](t: CountTableRef[A], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,CountTableRef[A],A>`_ for use with
+  ## 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.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key
+  ##   is in the table
+  result = t[].getOrDefault(key, default)
+
+proc len*[A](t: CountTableRef[A]): int =
+  ## Returns the number of keys in ``t``.
+  result = t.counter
+
+proc clear*[A](t: CountTableRef[A]) =
+  ## Resets the table so that it is empty.
+  clearImpl()
+
+proc sort*[A](t: CountTableRef[A]) =
+  ## Sorts the count table so that the entry with the highest counter comes
+  ## first.
+  ##
+  ## **This is destructive! You must not modify `t` afterwards!**
+  ##
+  ## You can use the iterators `pairs<#pairs.i,CountTableRef[A]>`_,
+  ## `keys<#keys.i,CountTableRef[A]>`_, and `values<#values.i,CountTableRef[A]>`_
+  ## to iterate over ``t`` in the sorted order.
+  t[].sort
+
+proc merge*[A](s, t: CountTableRef[A]) =
+  ## Merges the second table into the first one.
+  runnableExamples:
+    let
+      a = newCountTable("aaabbc")
+      b = newCountTable("bcc")
+    a.merge(b)
+    doAssert a == newCountTable("aaabbbccc")
+  s[].merge(t[])
+
 proc `$`*[A](t: CountTableRef[A]): string =
-  ## The ``$`` operator for count tables.
+  ## 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`` iff either both tables
-  ## are ``nil`` or none 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
   else: result = s[] == t[]
 
-proc smallest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
-  t[].smallest
 
-proc largest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
-  t[].largest
+iterator pairs*[A](t: CountTableRef[A]): (A, int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
+  ## * `keys iterator<#keys.i,CountTableRef[A]>`_
+  ## * `values iterator<#values.i,CountTableRef[A]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = newCountTable("abracadabra")
+  ##
+  ##   for k, v in pairs(a):
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: a
+  ##   # value: 5
+  ##   # key: b
+  ##   # value: 2
+  ##   # key: c
+  ##   # value: 1
+  ##   # key: d
+  ##   # value: 1
+  ##   # key: r
+  ##   # value: 2
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-proc sort*[A](t: CountTableRef[A]) =
-  ## sorts the count table so that the entry with the highest counter comes
-  ## first. This is destructive! You must not modify ``t`` afterwards!
-  ## You can use the iterators ``pairs``, ``keys``, and ``values`` to iterate over
-  ## ``t`` in the sorted order.
-  t[].sort
+iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values can
+  ## be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTableRef[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for k, v in mpairs(a):
+      v = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+
+iterator keys*[A](t: CountTableRef[A]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for k in keys(a):
+      a[k] = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].key
+
+iterator values*[A](t: CountTableRef[A]): int =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_
+  ## * `keys iterator<#keys.i,CountTableRef[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTableRef[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for v in values(a):
+      assert v < 10
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].val
+
+iterator mvalues*[A](t: CountTableRef[A]): var int =
+  ## Iterates over any value in the table ``t``. The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
+  ## * `values iterator<#values.i,CountTableRef[A]>`_
+  runnableExamples:
+    var a = newCountTable("abracadabra")
+    for v in mvalues(a):
+      v = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].val
 
-proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
-  ## merges the second table into the first one.
-  for key, value in t:
-    s.inc(key, value)
 
-proc merge*[A](s, t: CountTable[A]): CountTable[A] =
-  ## merges the two tables into a new one.
-  result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
-  for table in @[s, t]:
-    for key, value in table:
-      result.inc(key, value)
 
-proc merge*[A](s, t: CountTableRef[A]) =
-  ## merges the second table into the first one.
-  s[].merge(t[])
 
 when isMainModule:
   type
@@ -1325,9 +2708,9 @@ when isMainModule:
     #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.getOrDefault(testKey)
+    doAssert 0 == t[testKey]
     t.inc(testKey, 3)
-    doAssert 3 == t.getOrDefault(testKey)
+    doAssert 3 == t[testKey]
 
   block:
     # Clear tests
@@ -1394,6 +2777,18 @@ when isMainModule:
     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:
     var tp: Table[string, string] = initTable[string, string]()
     doAssert "test1" == tp.getOrDefault("test1", "test1")
diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim
new file mode 100644
index 000000000..9e716bdf4
--- /dev/null
+++ b/lib/pure/concurrency/atomics.nim
@@ -0,0 +1,378 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Jörg Wollenschläger
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Types and operations for atomic operations and lockless algorithms.
+
+import macros
+
+when defined(cpp) or defined(nimdoc):
+  # For the C++ backend, types and operations map directly to C++11 atomics.
+
+  {.push, header: "<atomic>".}
+
+  type
+    MemoryOrder* {.importcpp: "std::memory_order".} = enum
+      ## Specifies how non-atomic operations can be reordered around atomic 
+      ## operations.
+
+      moRelaxed
+        ## No ordering constraints. Only the atomicity and ordering against
+        ## other atomic operations is guaranteed.
+
+      moConsume
+        ## This ordering is currently discouraged as it's semantics are
+        ## being revised. Acquire operations should be preferred.
+
+      moAcquire
+        ## When applied to a load operation, no reads or writes in the
+        ## current thread can be reordered before this operation.
+
+      moRelease
+        ## When applied to a store operation, no reads or writes in the
+        ## current thread can be reorderd after this operation.
+
+      moAcquireRelease
+        ## When applied to a read-modify-write operation, this behaves like
+        ## both an acquire and a release operation.
+
+      moSequentiallyConsistent
+        ## Behaves like Acquire when applied to load, like Release when
+        ## applied to a store and like AcquireRelease when applied to a
+        ## read-modify-write operation.
+        ## Also garantees that all threads observe the same total ordering
+        ## with other moSequentiallyConsistent operations.
+
+  type
+    Atomic* {.importcpp: "std::atomic".} [T] = object
+      ## An atomic object with underlying type `T`.
+
+    AtomicFlag* {.importcpp: "std::atomic_flag".} = object
+      ## An atomic boolean state.
+
+  # Access operations
+
+  proc load*[T](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.load(@)".}
+    ## Atomically obtains the value of the atomic object.
+
+  proc store*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.store(@)".}
+    ## Atomically replaces the value of the atomic object with the `desired`
+    ## value.
+
+  proc exchange*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.exchange(@)".}
+    ## Atomically replaces the value of the atomic object with the `desired`
+    ## value and returns the old value.
+
+  proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_strong(@)".}
+    ## Atomically compares the value of the atomic object with the `expected`
+    ## value and performs exchange with the `desired` one if equal or load if
+    ## not. Returns true if the exchange was successful.
+
+  proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_strong(@)".}
+    ## Same as above, but allows for different memory orders for success and
+    ## failure.
+
+  proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_weak(@)".}
+    ## Same as above, but is allowed to fail spuriously.
+
+  proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_weak(@)".}
+    ## Same as above, but allows for different memory orders for success and
+    ## failure.
+
+  # Numerical operations
+
+  proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_add(@)".}
+    ## Atomically adds a `value` to the atomic integer and returns the
+    ## original value.
+
+  proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_sub(@)".}
+    ## Atomically subtracts a `value` to the atomic integer and returns the
+    ## original value.
+
+  proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_and(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise AND
+    ## with the specified `value` and returns the original value.
+
+  proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_or(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise OR
+    ## with the specified `value` and returns the original value.
+
+  proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_xor(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise XOR
+    ## with the specified `value` and returns the original value.
+
+  # Flag operations
+
+  proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.test_and_set(@)".}
+    ## Atomically sets the atomic flag to true and returns the original value.
+
+  proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.clear(@)".}
+    ## Atomically sets the value of the atomic flag to false.
+
+  proc fence*(order: MemoryOrder) {.importcpp: "std::atomic_thread_fence(@)".}
+    ## Ensures memory ordering without using atomic operations.
+
+  proc signalFence*(order: MemoryOrder) {.importcpp: "std::atomic_signal_fence(@)".}
+    ## Prevents reordering of accesses by the compiler as would fence, but
+    ## inserts no CPU instructions for memory ordering.
+
+  {.pop.}
+
+else:
+  # For the C backend, atomics map to C11 built-ins on GCC and Clang for
+  # trivial Nim types. Other types are implemented using spin locks.
+  # This could be overcome by supporting advanced importc-patterns.
+
+  # Since MSVC does not implement C11, we fall back to MS intrinsics
+  # where available.
+
+  type 
+    Trivial = SomeNumber | bool | ptr | pointer
+      # A type that is known to be atomic and whose size is known at
+      # compile time to be 8 bytes or less
+
+  template nonAtomicType(T: typedesc[Trivial]): untyped =
+    # Maps types to integers of the same size
+    when sizeof(T) == 1: int8
+    elif sizeof(T) == 2: int16
+    elif sizeof(T) == 4: int32
+    elif sizeof(T) == 8: int64
+
+  when defined(vcc):
+    
+    # TODO: Trivial types should be volatile and use VC's special volatile
+    # semantics for store and loads.
+
+    type
+      MemoryOrder* = enum
+        moRelaxed
+        moConsume
+        moAcquire
+        moRelease
+        moAcquireRelease
+        moSequentiallyConsistent
+
+      Atomic*[T] = object
+        when T is Trivial:
+          value: T.nonAtomicType
+        else:
+          nonAtomicValue: T
+          guard: AtomicFlag
+
+      AtomicFlag* = distinct int8
+
+    {.push header: "<intrin.h>".}
+
+    # MSVC intrinsics
+    proc interlockedExchange(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8".}
+    proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange".}
+    proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange16".}
+    proc interlockedExchange(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64".}
+
+    proc interlockedCompareExchange(location: pointer; desired, expected: int8): int8 {.importc: "_InterlockedCompareExchange8".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int16): int16 {.importc: "_InterlockedCompareExchange16".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int32): int32 {.importc: "_InterlockedCompareExchange".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int64): int64 {.importc: "_InterlockedCompareExchange64".}
+
+    proc interlockedAnd(location: pointer; value: int8): int8 {.importc: "_InterlockedAnd8".}
+    proc interlockedAnd(location: pointer; value: int16): int16 {.importc: "_InterlockedAnd16".}
+    proc interlockedAnd(location: pointer; value: int32): int32 {.importc: "_InterlockedAnd".}
+    proc interlockedAnd(location: pointer; value: int64): int64 {.importc: "_InterlockedAnd64".}
+
+    proc interlockedOr(location: pointer; value: int8): int8 {.importc: "_InterlockedOr8".}
+    proc interlockedOr(location: pointer; value: int16): int16 {.importc: "_InterlockedOr16".}
+    proc interlockedOr(location: pointer; value: int32): int32 {.importc: "_InterlockedOr".}
+    proc interlockedOr(location: pointer; value: int64): int64 {.importc: "_InterlockedOr64".}
+
+    proc interlockedXor(location: pointer; value: int8): int8 {.importc: "_InterlockedXor8".}
+    proc interlockedXor(location: pointer; value: int16): int16 {.importc: "_InterlockedXor16".}
+    proc interlockedXor(location: pointer; value: int32): int32 {.importc: "_InterlockedXor".}
+    proc interlockedXor(location: pointer; value: int64): int64 {.importc: "_InterlockedXor64".}
+
+    proc fence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
+    proc signalFence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
+
+    {.pop.}
+
+    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool =
+      interlockedOr(addr(location), 1'i8) == 1'i8
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) =
+      discard interlockedAnd(addr(location), 0'i8)
+
+    proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedOr(addr(location.value), (nonAtomicType(T))0))
+    proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
+      discard interlockedExchange(addr(location.value), cast[nonAtomicType(T)](desired))
+
+    proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedExchange(addr(location.value), cast[int64](desired)))
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      cast[T](interlockedCompareExchange(addr(location.value), cast[nonAtomicType(T)](desired), cast[nonAtomicType(T)](expected))) == expected
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchange(location, expected, desired, order, order)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      compareExchange(location, expected, desired, success, failure)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchangeWeak(location, expected, desired, order, order)
+
+    proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      var currentValue = location.load()
+      while not compareExchangeWeak(location, currentValue, currentValue + value): discard
+    proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      fetchAdd(location, -value, order)
+    proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedAnd(addr(location.value), cast[nonAtomicType(T)](value)))
+    proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedOr(addr(location.value), cast[nonAtomicType(T)](value)))
+    proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
+    
+  else:
+    {.push, header: "<stdatomic.h>".}
+
+    type
+      MemoryOrder* {.importc: "memory_order".} = enum
+        moRelaxed
+        moConsume
+        moAcquire
+        moRelease
+        moAcquireRelease
+        moSequentiallyConsistent
+
+    type
+      # Atomic* {.importcpp: "_Atomic('0)".} [T] = object
+
+      AtomicInt8 {.importc: "_Atomic NI8".} = object
+      AtomicInt16 {.importc: "_Atomic NI16".} = object
+      AtomicInt32 {.importc: "_Atomic NI32".} = object
+      AtomicInt64 {.importc: "_Atomic NI64".} = object
+
+    template atomicType(T: typedesc[Trivial]): untyped =
+      # Maps the size of a trivial type to it's internal atomic type
+      when sizeof(T) == 1: AtomicInt8
+      elif sizeof(T) == 2: AtomicInt16
+      elif sizeof(T) == 4: AtomicInt32
+      elif sizeof(T) == 8: AtomicInt64
+
+    type
+      AtomicFlag* {.importc: "atomic_flag".} = object
+
+      Atomic*[T] = object
+        when T is Trivial:
+          value: T.atomicType
+        else:
+          nonAtomicValue: T
+          guard: AtomicFlag
+
+    #proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
+    proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
+    proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
+    proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
+    proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
+      
+    # Numerical operations
+    proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+  
+    # Flag operations
+    # var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
+    # proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
+    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
+  
+    proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
+    proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}  
+
+    {.pop.}
+
+    proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_load_explicit[nonAtomicType(T), type(location.value)](addr(location.value), order))
+    proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
+      atomic_store_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order)
+    proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_exchange_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order))
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      atomic_compare_exchange_strong_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchange(location, expected, desired, order, order)
+
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      atomic_compare_exchange_weak_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchangeWeak(location, expected, desired, order, order)
+  
+    # Numerical operations
+    proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_add_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_sub_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_and_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_or_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_xor_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+
+  template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped =
+    while location.guard.testAndSet(moAcquire): discard
+    body
+    location.guard.clear(moRelease)
+
+  proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =      
+    withLock(location, order):
+      result = location.nonAtomicValue
+
+  proc store*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =      
+    withLock(location, order):
+      location.nonAtomicValue = desired
+
+  proc exchange*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+    withLock(location, order):
+      result = location.nonAtomicValue
+      location.nonAtomicValue = desired
+
+  proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+    withLock(location, success):
+      if location.nonAtomicValue != expected:
+        return false
+      swap(location.nonAtomicValue, expected)
+      return true
+
+  proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+    withLock(location, success):
+      if location.nonAtomicValue != expected:
+        return false
+      swap(location.nonAtomicValue, expected)
+      return true
+
+  proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+    compareExchange(location, expected, desired, order, order)
+
+  proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+    compareExchangeWeak(location, expected, desired, order, order)
+
+proc atomicInc*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
+  ## Atomically increments the atomic integer by some `value`.
+  discard location.fetchAdd(value)
+
+proc atomicDec*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
+  ## Atomically decrements the atomic integer by some `value`.
+  discard location.fetchSub(value)
+
+proc `+=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
+  ## Atomically increments the atomic integer by some `value`.
+  discard location.fetchAdd(value)
+
+proc `-=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
+  ## Atomically decrements the atomic integer by some `value`.
+  discard location.fetchSub(value)
+  
\ No newline at end of file
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 9c9425d9f..4846c610f 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -320,6 +320,10 @@ gSomeReady.initSemaphore()
 proc slave(w: ptr Worker) {.thread.} =
   isSlave = true
   while true:
+    if w.shutdown:
+      w.shutdown = false
+      atomicDec currentPoolSize
+      break
     when declared(atomicStoreN):
       atomicStoreN(addr(w.ready), true, ATOMIC_SEQ_CST)
     else:
@@ -340,9 +344,6 @@ proc slave(w: ptr Worker) {.thread.} =
       dec numSlavesRunning
 
     if w.q.len != 0: w.cleanFlowVars
-    if w.shutdown:
-      w.shutdown = false
-      atomicDec currentPoolSize
 
 proc distinguishedSlave(w: ptr Worker) {.thread.} =
   while true:
diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim
index 0725973ca..ab47da08e 100644
--- a/lib/pure/fenv.nim
+++ b/lib/pure/fenv.nim
@@ -12,7 +12,7 @@
 
 {.deadCodeElim: on.}  # dce option deprecated
 
-when defined(Posix):
+when defined(Posix) and not defined(genode):
   {.passl: "-lm".}
 
 var
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index b7498b1c5..e5c0c6ac4 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -17,6 +17,7 @@
 ## ``http://google.com``:
 ##
 ## .. code-block:: Nim
+##   import httpClient
 ##   var client = newHttpClient()
 ##   echo client.getContent("http://google.com")
 ##
@@ -24,6 +25,7 @@
 ## ``AsyncHttpClient``:
 ##
 ## .. code-block:: Nim
+##   import httpClient
 ##   var client = newAsyncHttpClient()
 ##   echo await client.getContent("http://google.com")
 ##
@@ -189,12 +191,6 @@ proc body*(response: Response): string =
     response.body = response.bodyStream.readAll()
   return response.body
 
-proc `body=`*(response: Response, value: string) {.deprecated.} =
-  ## Setter for backward compatibility.
-  ##
-  ## **This is deprecated and should not be used**.
-  response.body = value
-
 proc body*(response: AsyncResponse): Future[string] {.async.} =
   ## Reads the response's body and caches it. The read is performed only
   ## once.
@@ -477,119 +473,6 @@ proc format(p: MultipartData): tuple[contentType, body: string] =
     result.body.add("--" & bound & "\c\L" & s)
   result.body.add("--" & bound & "--\c\L")
 
-proc request*(url: string, httpMethod: string, extraHeaders = "",
-              body = "", sslContext = getDefaultSSL(), timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response
-              {.deprecated: "use HttpClient.request instead".} =
-  ## | Requests ``url`` with the custom method string specified by the
-  ## | ``httpMethod`` parameter.
-  ## | Extra headers can be specified and must be separated by ``\c\L``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
-  var r = if proxy == nil: parseUri(url) else: proxy.url
-  var hostUrl = if proxy == nil: r else: parseUri(url)
-  var headers = httpMethod.toUpperAscii()
-  # TODO: Use generateHeaders further down once it supports proxies.
-
-  var s = newSocket()
-  defer: s.close()
-  if s == nil: raiseOSError(osLastError())
-  var port = net.Port(80)
-  if r.scheme == "https":
-    when defined(ssl):
-      sslContext.wrapSocket(s)
-      port = net.Port(443)
-    else:
-      raise newException(HttpRequestError,
-                "SSL support is not available. Cannot connect over SSL. Compile with -d:ssl to enable.")
-  if r.port != "":
-    port = net.Port(r.port.parseInt)
-
-
-  # get the socket ready. If we are connecting through a proxy to SSL,
-  # send the appropriate CONNECT header. If not, simply connect to the proper
-  # host (which may still be the proxy, for normal HTTP)
-  if proxy != nil and hostUrl.scheme == "https":
-    when defined(ssl):
-      var connectHeaders = "CONNECT "
-      let targetPort = if hostUrl.port == "": 443 else: hostUrl.port.parseInt
-      connectHeaders.add(hostUrl.hostname)
-      connectHeaders.add(":" & $targetPort)
-      connectHeaders.add(" HTTP/1.1\c\L")
-      connectHeaders.add("Host: " & hostUrl.hostname & ":" & $targetPort & "\c\L")
-      if proxy.auth != "":
-        let auth = base64.encode(proxy.auth, newline = "")
-        connectHeaders.add("Proxy-Authorization: basic " & auth & "\c\L")
-      connectHeaders.add("\c\L")
-      if timeout == -1:
-        s.connect(r.hostname, port)
-      else:
-        s.connect(r.hostname, port, timeout)
-
-      s.send(connectHeaders)
-      let connectResult = parseResponse(s, false, timeout)
-      if not connectResult.status.startsWith("200"):
-        raise newException(HttpRequestError,
-                           "The proxy server rejected a CONNECT request, " &
-                           "so a secure connection could not be established.")
-      sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
-    else:
-      raise newException(HttpRequestError, "SSL support not available. Cannot " &
-                         "connect via proxy over SSL. Compile with -d:ssl to enable.")
-  else:
-    if timeout == -1:
-      s.connect(r.hostname, port)
-    else:
-      s.connect(r.hostname, port, timeout)
-
-
-  # now that the socket is ready, prepare the headers
-  if proxy == nil:
-    headers.add ' '
-    if r.path[0] != '/': headers.add '/'
-    headers.add(r.path)
-    if r.query.len > 0:
-      headers.add("?" & r.query)
-  else:
-    headers.add(" " & url)
-
-  headers.add(" HTTP/1.1\c\L")
-
-  if hostUrl.port == "":
-    add(headers, "Host: " & hostUrl.hostname & "\c\L")
-  else:
-    add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
-
-  if userAgent != "":
-    add(headers, "User-Agent: " & userAgent & "\c\L")
-  if proxy != nil and proxy.auth != "":
-    let auth = base64.encode(proxy.auth, newline = "")
-    add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
-  add(headers, extraHeaders)
-  add(headers, "\c\L")
-
-  # headers are ready. send them, await the result, and close the socket.
-  s.send(headers)
-  if body != "":
-    s.send(body)
-
-  result = parseResponse(s, httpMethod != "HEAD", timeout)
-
-proc request*(url: string, httpMethod = HttpGET, extraHeaders = "",
-              body = "", sslContext = getDefaultSSL(), timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response
-              {.deprecated.} =
-  ## | Requests ``url`` with the specified ``httpMethod``.
-  ## | Extra headers can be specified and must be separated by ``\c\L``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
-  result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout,
-                   userAgent, proxy)
-
 proc redirection(status: string): bool =
   const redirectionNRs = ["301", "302", "303", "307"]
   for i in items(redirectionNRs):
@@ -608,130 +491,6 @@ proc getNewLocation(lastURL: string, headers: HttpHeaders): string =
     parsed.anchor = r.anchor
     result = $parsed
 
-proc get*(url: string, extraHeaders = "", maxRedirects = 5,
-          sslContext: SSLContext = getDefaultSSL(),
-          timeout = -1, userAgent = defUserAgent,
-          proxy: Proxy = nil): Response {.deprecated.} =
-  ## | GETs the ``url`` and returns a ``Response`` object
-  ## | This proc also handles redirection
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
-  result = request(url, HttpGET, extraHeaders, "", sslContext, timeout,
-                   userAgent, proxy)
-  var lastURL = url
-  for i in 1..maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      result = request(redirectTo, HttpGET, extraHeaders, "", sslContext,
-                       timeout, userAgent, proxy)
-      lastURL = redirectTo
-
-proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
-                 sslContext: SSLContext = getDefaultSSL(),
-                 timeout = -1, userAgent = defUserAgent,
-                 proxy: Proxy = nil): string {.deprecated.} =
-  ## | GETs the body and returns it as a string.
-  ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.getContent`` instead.
-  var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent,
-              proxy)
-  if r.status[0] in {'4','5'}:
-    raise newException(HttpRequestError, r.status)
-  else:
-    return r.body
-
-proc post*(url: string, extraHeaders = "", body = "",
-           maxRedirects = 5,
-           sslContext: SSLContext = getDefaultSSL(),
-           timeout = -1, userAgent = defUserAgent,
-           proxy: Proxy = nil,
-           multipart: MultipartData = nil): Response {.deprecated.} =
-  ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
-  ## | This proc adds the necessary Content-Length header.
-  ## | This proc also handles redirection.
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ## | The optional ``multipart`` parameter can be used to create
-  ## ``multipart/form-data`` POSTs comfortably.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead.
-  let (mpContentType, mpBody) = format(multipart)
-
-  template withNewLine(x): untyped =
-    if x.len > 0 and not x.endsWith("\c\L"):
-      x & "\c\L"
-    else:
-      x
-
-  var xb = mpBody.withNewLine() & body
-
-  var xh = extraHeaders.withNewLine() &
-    withNewLine("Content-Length: " & $len(xb))
-
-  if not multipart.isNil:
-    xh.add(withNewLine("Content-Type: " & mpContentType))
-
-  result = request(url, HttpPOST, xh, xb, sslContext, timeout, userAgent,
-                   proxy)
-  var lastURL = url
-  for i in 1..maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      var meth = if result.status != "307": HttpGet else: HttpPost
-      result = request(redirectTo, meth, xh, xb, sslContext, timeout,
-                       userAgent, proxy)
-      lastURL = redirectTo
-
-proc postContent*(url: string, extraHeaders = "", body = "",
-                  maxRedirects = 5,
-                  sslContext: SSLContext = getDefaultSSL(),
-                  timeout = -1, userAgent = defUserAgent,
-                  proxy: Proxy = nil,
-                  multipart: MultipartData = nil): string
-                  {.deprecated.} =
-  ## | POSTs ``body`` to ``url`` and returns the response's body as a string
-  ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ## | The optional ``multipart`` parameter can be used to create
-  ## ``multipart/form-data`` POSTs comfortably.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.postContent``
-  ## instead.
-  var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
-               userAgent, proxy, multipart)
-  if r.status[0] in {'4','5'}:
-    raise newException(HttpRequestError, r.status)
-  else:
-    return r.body
-
-proc downloadFile*(url: string, outputFilename: string,
-                   sslContext: SSLContext = getDefaultSSL(),
-                   timeout = -1, userAgent = defUserAgent,
-                   proxy: Proxy = nil) {.deprecated.} =
-  ## | Downloads ``url`` and saves it to ``outputFilename``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.16.2**: use ``HttpClient.downloadFile``
-  ## instead.
-  var f: File
-  if open(f, outputFilename, fmWrite):
-    f.write(getContent(url, sslContext = sslContext, timeout = timeout,
-            userAgent = userAgent, proxy = proxy))
-    f.close()
-  else:
-    fileError("Unable to open file")
-
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
deleted file mode 100644
index a81e8c0a8..000000000
--- a/lib/pure/httpserver.nim
+++ /dev/null
@@ -1,535 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements a simple HTTP-Server.
-##
-## **Warning**: This module will soon be deprecated in favour of
-## the ``asyncdispatch`` module, you should use it instead.
-##
-## Example:
-##
-## .. code-block:: nim
-##  import strutils, sockets, httpserver
-##
-##  var counter = 0
-##  proc handleRequest(client: Socket, path, query: string): bool {.procvar.} =
-##    inc(counter)
-##    client.send("Hello for the $#th time." % $counter & wwwNL)
-##    return false # do not stop processing
-##
-##  run(handleRequest, Port(80))
-##
-
-import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio
-
-const
-  wwwNL* = "\r\L"
-  ServerSig = "Server: httpserver.nim/1.0.0" & wwwNL
-
-# --------------- output messages --------------------------------------------
-
-proc sendTextContentType(client: Socket) =
-  send(client, "Content-type: text/html" & wwwNL)
-  send(client, wwwNL)
-
-proc sendStatus(client: Socket, status: string) =
-  send(client, "HTTP/1.1 " & status & wwwNL)
-
-proc badRequest(client: Socket) =
-  # Inform the client that a request it has made has a problem.
-  send(client, "HTTP/1.1 400 Bad Request" & wwwNL)
-  sendTextContentType(client)
-  send(client, "<p>Your browser sent a bad request, " &
-               "such as a POST without a Content-Length.</p>" & wwwNL)
-
-when false:
-  proc cannotExec(client: Socket) =
-    send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
-    sendTextContentType(client)
-    send(client, "<P>Error prohibited CGI execution." & wwwNL)
-
-proc headers(client: Socket, filename: string) =
-  # XXX could use filename to determine file type
-  send(client, "HTTP/1.1 200 OK" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-
-proc notFound(client: Socket) =
-  send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-  send(client, "<html><title>Not Found</title>" & wwwNL)
-  send(client, "<body><p>The server could not fulfill" & wwwNL)
-  send(client, "your request because the resource specified" & wwwNL)
-  send(client, "is unavailable or nonexistent.</p>" & wwwNL)
-  send(client, "</body></html>" & wwwNL)
-
-proc unimplemented(client: Socket) =
-  send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-  send(client, "<html><head><title>Method Not Implemented" &
-               "</title></head>" &
-               "<body><p>HTTP request method not supported.</p>" &
-               "</body></HTML>" & wwwNL)
-
-# ----------------- file serving ---------------------------------------------
-
-when false:
-  proc discardHeaders(client: Socket) = skip(client)
-
-proc serveFile*(client: Socket, filename: string) =
-  ## serves a file to the client.
-  var f: File
-  if open(f, filename):
-    headers(client, filename)
-    const bufSize = 8000 # != 8K might be good for memory manager
-    var buf = alloc(bufsize)
-    while true:
-      var bytesread = readBuffer(f, buf, bufsize)
-      if bytesread > 0:
-        var byteswritten = send(client, buf, bytesread)
-        if bytesread != bytesWritten:
-          dealloc(buf)
-          close(f)
-          raiseOSError(osLastError())
-      if bytesread != bufSize: break
-    dealloc(buf)
-    close(f)
-  else:
-    notFound(client)
-
-# ------------------ CGI execution -------------------------------------------
-when false:
-  # TODO: Fix this, or get rid of it.
-  type
-    RequestMethod = enum reqGet, reqPost
-
-  proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
-    var env = newStringTable(modeCaseInsensitive)
-    var contentLength = -1
-    case meth
-    of reqGet:
-      discardHeaders(client)
-
-      env["REQUEST_METHOD"] = "GET"
-      env["QUERY_STRING"] = query
-    of reqPost:
-      var buf = TaintedString""
-      var dataAvail = false
-      while dataAvail:
-        dataAvail = recvLine(client, buf) # TODO: This is incorrect.
-        var L = toLowerAscii(buf.string)
-        if L.startsWith("content-length:"):
-          var i = len("content-length:")
-          while L[i] in Whitespace: inc(i)
-          contentLength = parseInt(substr(L, i))
-
-      if contentLength < 0:
-        badRequest(client)
-        return
-
-      env["REQUEST_METHOD"] = "POST"
-      env["CONTENT_LENGTH"] = $contentLength
-
-    send(client, "HTTP/1.0 200 OK" & wwwNL)
-
-    var process = startProcess(command=path, env=env)
-    if meth == reqPost:
-      # get from client and post to CGI program:
-      var buf = alloc(contentLength)
-      if recv(client, buf, contentLength) != contentLength:
-        dealloc(buf)
-        raiseOSError()
-      var inp = process.inputStream
-      inp.writeData(buf, contentLength)
-      dealloc(buf)
-
-    var outp = process.outputStream
-    var line = newStringOfCap(120).TaintedString
-    while true:
-      if outp.readLine(line):
-        send(client, line.string)
-        send(client, wwwNL)
-      elif not running(process): break
-
-  # --------------- Server Setup -----------------------------------------------
-
-  proc acceptRequest(client: Socket) =
-    var cgi = false
-    var query = ""
-    var buf = TaintedString""
-    discard recvLine(client, buf)
-    var path = ""
-    var data = buf.string.split()
-    var meth = reqGet
-
-    var q = find(data[1], '?')
-
-    # extract path
-    if q >= 0:
-      # strip "?..." from path, this may be found in both POST and GET
-      path = "." & data[1].substr(0, q-1)
-    else:
-      path = "." & data[1]
-    # path starts with "/", by adding "." in front of it we serve files from cwd
-
-    if cmpIgnoreCase(data[0], "GET") == 0:
-      if q >= 0:
-        cgi = true
-        query = data[1].substr(q+1)
-    elif cmpIgnoreCase(data[0], "POST") == 0:
-      cgi = true
-      meth = reqPost
-    else:
-      unimplemented(client)
-
-    if path[path.len-1] == '/' or existsDir(path):
-      path = path / "index.html"
-
-    if not existsFile(path):
-      discardHeaders(client)
-      notFound(client)
-    else:
-      when defined(Windows):
-        var ext = splitFile(path).ext.toLowerAscii
-        if ext == ".exe" or ext == ".cgi":
-          # XXX: extract interpreter information here?
-          cgi = true
-      else:
-        if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}:
-          cgi = true
-      if not cgi:
-        serveFile(client, path)
-      else:
-        executeCgi(client, path, query, meth)
-
-type
-  Server* = object of RootObj  ## contains the current server state
-    socket: Socket
-    port: Port
-    client*: Socket          ## the socket to write the file data to
-    reqMethod*: string       ## Request method. GET or POST.
-    path*, query*: string    ## path and query the client requested
-    headers*: StringTableRef ## headers with which the client made the request
-    body*: string            ## only set with POST requests
-    ip*: string              ## ip address of the requesting client
-
-  PAsyncHTTPServer* = ref AsyncHTTPServer
-  AsyncHTTPServer = object of Server
-    asyncSocket: AsyncSocket
-
-proc open*(s: var Server, port = Port(80), reuseAddr = false) =
-  ## creates a new server at port `port`. If ``port == 0`` a free port is
-  ## acquired that can be accessed later by the ``port`` proc.
-  s.socket = socket(AF_INET)
-  if s.socket == invalidSocket: raiseOSError(osLastError())
-  if reuseAddr:
-    s.socket.setSockOpt(OptReuseAddr, true)
-  bindAddr(s.socket, port)
-  listen(s.socket)
-
-  if port == Port(0):
-    s.port = getSockName(s.socket)
-  else:
-    s.port = port
-  s.client = invalidSocket
-  s.reqMethod = ""
-  s.body = ""
-  s.path = ""
-  s.query = ""
-  s.headers = {:}.newStringTable()
-
-proc port*(s: var Server): Port =
-  ## get the port number the server has acquired.
-  result = s.port
-
-proc next*(s: var Server) =
-  ## proceed to the first/next request.
-  var client: Socket
-  new(client)
-  var ip: string
-  acceptAddr(s.socket, client, ip)
-  s.client = client
-  s.ip = ip
-  s.headers = newStringTable(modeCaseInsensitive)
-  #headers(s.client, "")
-  var data = ""
-  s.client.readLine(data)
-  if data == "":
-    # Socket disconnected
-    s.client.close()
-    next(s)
-    return
-  var header = ""
-  while true:
-    s.client.readLine(header)
-    if header == "\c\L": break
-    if header != "":
-      var i = 0
-      var key = ""
-      var value = ""
-      i = header.parseUntil(key, ':')
-      inc(i) # skip :
-      i += header.skipWhiteSpace(i)
-      i += header.parseUntil(value, {'\c', '\L'}, i)
-      s.headers[key] = value
-    else:
-      s.client.close()
-      next(s)
-      return
-
-  var i = skipWhitespace(data)
-  if skipIgnoreCase(data, "GET") > 0:
-    s.reqMethod = "GET"
-    inc(i, 3)
-  elif skipIgnoreCase(data, "POST") > 0:
-    s.reqMethod = "POST"
-    inc(i, 4)
-  else:
-    unimplemented(s.client)
-    s.client.close()
-    next(s)
-    return
-
-  if s.reqMethod == "POST":
-    # Check for Expect header
-    if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLowerAscii == "100-continue":
-        s.client.sendStatus("100 Continue")
-      else:
-        s.client.sendStatus("417 Expectation Failed")
-
-    # Read the body
-    # - Check for Content-length header
-    if s.headers.hasKey("Content-Length"):
-      var contentLength = 0
-      if parseInt(s.headers["Content-Length"], contentLength) == 0:
-        badRequest(s.client)
-        s.client.close()
-        next(s)
-        return
-      else:
-        var totalRead = 0
-        var totalBody = ""
-        while totalRead < contentLength:
-          var chunkSize = 8000
-          if (contentLength - totalRead) < 8000:
-            chunkSize = (contentLength - totalRead)
-          var bodyData = newString(chunkSize)
-          var octetsRead = s.client.recv(cstring(bodyData), chunkSize)
-          if octetsRead <= 0:
-            s.client.close()
-            next(s)
-            return
-          totalRead += octetsRead
-          totalBody.add(bodyData)
-        if totalBody.len != contentLength:
-          s.client.close()
-          next(s)
-          return
-
-        s.body = totalBody
-    else:
-      badRequest(s.client)
-      s.client.close()
-      next(s)
-      return
-
-  var L = skipWhitespace(data, i)
-  inc(i, L)
-  # XXX we ignore "HTTP/1.1" etc. for now here
-  var query = 0
-  var last = i
-  while last < data.len and data[last] notin Whitespace:
-    if data[last] == '?' and query == 0: query = last
-    inc(last)
-  if query > 0:
-    s.query = data.substr(query+1, last-1)
-    s.path = data.substr(i, query-1)
-  else:
-    s.query = ""
-    s.path = data.substr(i, last-1)
-
-proc close*(s: Server) =
-  ## closes the server (and the socket the server uses).
-  close(s.socket)
-
-proc run*(handleRequest: proc (client: Socket,
-                               path, query: string): bool {.closure.},
-          port = Port(80)) =
-  ## encapsulates the server object and main loop
-  var s: Server
-  open(s, port, reuseAddr = true)
-  #echo("httpserver running on port ", s.port)
-  while true:
-    next(s)
-    if handleRequest(s.client, s.path, s.query): break
-    close(s.client)
-  close(s)
-
-# -- AsyncIO begin
-
-proc nextAsync(s: PAsyncHTTPServer) =
-  ## proceed to the first/next request.
-  var client: Socket
-  new(client)
-  var ip: string
-  acceptAddr(getSocket(s.asyncSocket), client, ip)
-  s.client = client
-  s.ip = ip
-  s.headers = newStringTable(modeCaseInsensitive)
-  #headers(s.client, "")
-  var data = ""
-  s.client.readLine(data)
-  if data == "":
-    # Socket disconnected
-    s.client.close()
-    return
-  var header = ""
-  while true:
-    s.client.readLine(header) # TODO: Very inefficient here. Prone to DOS.
-    if header == "\c\L": break
-    if header != "":
-      var i = 0
-      var key = ""
-      var value = ""
-      i = header.parseUntil(key, ':')
-      inc(i) # skip :
-      if i < header.len:
-        i += header.skipWhiteSpace(i)
-        i += header.parseUntil(value, {'\c', '\L'}, i)
-      s.headers[key] = value
-    else:
-      s.client.close()
-      return
-
-  var i = skipWhitespace(data)
-  if skipIgnoreCase(data, "GET") > 0:
-    s.reqMethod = "GET"
-    inc(i, 3)
-  elif skipIgnoreCase(data, "POST") > 0:
-    s.reqMethod = "POST"
-    inc(i, 4)
-  else:
-    unimplemented(s.client)
-    s.client.close()
-    return
-
-  if s.reqMethod == "POST":
-    # Check for Expect header
-    if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLowerAscii == "100-continue":
-        s.client.sendStatus("100 Continue")
-      else:
-        s.client.sendStatus("417 Expectation Failed")
-
-    # Read the body
-    # - Check for Content-length header
-    if s.headers.hasKey("Content-Length"):
-      var contentLength = 0
-      if parseInt(s.headers["Content-Length"], contentLength) == 0:
-        badRequest(s.client)
-        s.client.close()
-        return
-      else:
-        var totalRead = 0
-        var totalBody = ""
-        while totalRead < contentLength:
-          var chunkSize = 8000
-          if (contentLength - totalRead) < 8000:
-            chunkSize = (contentLength - totalRead)
-          var bodyData = newString(chunkSize)
-          var octetsRead = s.client.recv(cstring(bodyData), chunkSize)
-          if octetsRead <= 0:
-            s.client.close()
-            return
-          totalRead += octetsRead
-          totalBody.add(bodyData)
-        if totalBody.len != contentLength:
-          s.client.close()
-          return
-
-        s.body = totalBody
-    else:
-      badRequest(s.client)
-      s.client.close()
-      return
-
-  var L = skipWhitespace(data, i)
-  inc(i, L)
-  # XXX we ignore "HTTP/1.1" etc. for now here
-  var query = 0
-  var last = i
-  while last < data.len and data[last] notin Whitespace:
-    if data[last] == '?' and query == 0: query = last
-    inc(last)
-  if query > 0:
-    s.query = data.substr(query+1, last-1)
-    s.path = data.substr(i, query-1)
-  else:
-    s.query = ""
-    s.path = data.substr(i, last-1)
-
-proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
-                        path, query: string): bool {.closure, gcsafe.},
-                     port = Port(80), address = "",
-                     reuseAddr = false): PAsyncHTTPServer =
-  ## Creates an Asynchronous HTTP server at ``port``.
-  var capturedRet: PAsyncHTTPServer
-  new(capturedRet)
-  capturedRet.asyncSocket = asyncSocket()
-  capturedRet.asyncSocket.handleAccept =
-    proc (s: AsyncSocket) =
-      nextAsync(capturedRet)
-      let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path,
-                               capturedRet.query)
-      if quit: capturedRet.asyncSocket.close()
-  if reuseAddr:
-    capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true)
-
-  capturedRet.asyncSocket.bindAddr(port, address)
-  capturedRet.asyncSocket.listen()
-  if port == Port(0):
-    capturedRet.port = getSockName(capturedRet.asyncSocket)
-  else:
-    capturedRet.port = port
-
-  capturedRet.client = invalidSocket
-  capturedRet.reqMethod = ""
-  capturedRet.body = ""
-  capturedRet.path = ""
-  capturedRet.query = ""
-  capturedRet.headers = {:}.newStringTable()
-  result = capturedRet
-
-proc register*(d: Dispatcher, s: PAsyncHTTPServer) =
-  ## Registers a ``PAsyncHTTPServer`` with a ``Dispatcher``.
-  d.register(s.asyncSocket)
-
-proc close*(h: PAsyncHTTPServer) =
-  ## Closes the ``PAsyncHTTPServer``.
-  h.asyncSocket.close()
-
-when not defined(testing) and isMainModule:
-  var counter = 0
-
-  var s: Server
-  open(s, Port(0))
-  echo("httpserver running on port ", s.port)
-  while true:
-    next(s)
-
-    inc(counter)
-    s.client.send("Hello, Andreas, for the $#th time. $# ? $#" % [
-      $counter, s.path, s.query] & wwwNL)
-
-    close(s.client)
-  close(s)
-
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index 4acc36b93..f9c076158 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -1,4 +1,4 @@
-## Include file that implements 'getEnv' and friends. Do not import it!
+# Include file that implements 'getEnv' and friends. Do not import it!
 
 when not declared(os):
   {.error: "This is an include file for os.nim!".}
@@ -102,9 +102,18 @@ proc findEnvVar(key: string): int =
 proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
   ## Returns the value of the `environment variable`:idx: named `key`.
   ##
-  ## If the variable does not exist, "" is returned. To distinguish
-  ## whether a variable exists or it's value is just "", call
-  ## `existsEnv(key)`.
+  ## If the variable does not exist, `""` is returned. To distinguish
+  ## whether a variable exists or it's value is just `""`, call
+  ## `existsEnv(key) proc <#existsEnv,string>`_.
+  ##
+  ## See also:
+  ## * `existsEnv proc <#existsEnv,string>`_
+  ## * `putEnv proc <#putEnv,string,string>`_
+  ## * `envPairs iterator <#envPairs.i>`_
+  runnableExamples:
+    assert getEnv("unknownEnv") == ""
+    assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
+
   when nimvm:
     discard "built into the compiler"
   else:
@@ -119,6 +128,14 @@ proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].}
 proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
   ## Checks whether the environment variable named `key` exists.
   ## Returns true if it exists, false otherwise.
+  ##
+  ## See also:
+  ## * `getEnv proc <#getEnv,string,string>`_
+  ## * `putEnv proc <#putEnv,string,string>`_
+  ## * `envPairs iterator <#envPairs.i>`_
+  runnableExamples:
+    assert not existsEnv("unknownEnv")
+
   when nimvm:
     discard "built into the compiler"
   else:
@@ -127,7 +144,12 @@ proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
 
 proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
   ## Sets the value of the `environment variable`:idx: named `key` to `val`.
-  ## If an error occurs, `EInvalidEnvVar` is raised.
+  ## If an error occurs, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `getEnv proc <#getEnv,string,string>`_
+  ## * `existsEnv proc <#existsEnv,string>`_
+  ## * `envPairs iterator <#envPairs.i>`_
 
   # Note: by storing the string in the environment sequence,
   # we guarantee that we don't free the memory before the program
@@ -154,9 +176,15 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
         raiseOSError(osLastError())
 
 iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
-  ## Iterate over all `environments variables`:idx:. In the first component
-  ## of the tuple is the name of the current variable stored, in the second
-  ## its value.
+  ## Iterate over all `environments variables`:idx:.
+  ##
+  ## In the first component of the tuple is the name of the current variable stored,
+  ## in the second its value.
+  ##
+  ## See also:
+  ## * `getEnv proc <#getEnv,string,string>`_
+  ## * `existsEnv proc <#existsEnv,string>`_
+  ## * `putEnv proc <#putEnv,string,string>`_
   getEnvVarsC()
   for i in 0..high(environment):
     var p = find(environment[i], '=')
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index abd0bf501..25e221d3b 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -1,4 +1,4 @@
-## Include file that implements 'osErrorMsg' and friends. Do not import it!
+# Include file that implements 'osErrorMsg' and friends. Do not import it!
 
 when not declared(os):
   {.error: "This is an include file for os.nim!".}
@@ -18,7 +18,7 @@ proc `$`*(err: OSErrorCode): string {.borrow.}
 proc osErrorMsg*(errorCode: OSErrorCode): string =
   ## Converts an OS error code into a human readable string.
   ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
+  ## The error code can be retrieved using the `osLastError proc <#osLastError>`_.
   ##
   ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
   ## returned.
@@ -26,6 +26,16 @@ proc osErrorMsg*(errorCode: OSErrorCode): string =
   ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
   ## make this procedure use the non-unicode Win API calls to retrieve the
   ## message.
+  ##
+  ## See also:
+  ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_
+  ## * `osLastError proc <#osLastError>`_
+  runnableExamples:
+    when defined(posix):
+      assert osErrorMsg(OSErrorCode(0)) == ""
+      assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted"
+      assert osErrorMsg(OSErrorCode(2)) == "No such file or directory"
+
   result = ""
   when defined(nimscript):
     discard
@@ -48,18 +58,26 @@ proc osErrorMsg*(errorCode: OSErrorCode): string =
       result = $c_strerror(errorCode.int32)
 
 proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
-  ## Raises an ``OSError`` exception. The ``errorCode`` will determine the
-  ## message, ``osErrorMsg`` will be used to get this message.
+  ## Raises an `OSError exception <system.html#OSError>`_.
+  ##
+  ## The ``errorCode`` will determine the
+  ## message, `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ will be used
+  ## to get this message.
   ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
+  ## The error code can be retrieved using the `osLastError proc
+  ## <#osLastError>`_.
   ##
   ## If the error code is ``0`` or an error message could not be retrieved,
   ## the message ``unknown OS error`` will be used.
+  ##
+  ## See also:
+  ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_
+  ## * `osLastError proc <#osLastError>`_
   var e: ref OSError; new(e)
   e.errorCode = errorCode.int32
   e.msg = osErrorMsg(errorCode)
   if additionalInfo.len > 0:
-    if e.msg[^1] != '\n': e.msg.add '\n'
+    if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n'
     e.msg.add  "Additional info: "
     e.msg.addQuoted additionalInfo
   if e.msg == "":
@@ -80,6 +98,10 @@ proc osLastError*(): OSErrorCode {.sideEffect.} =
   ## On Windows some OS calls can reset the error code to ``0`` causing this
   ## procedure to return ``0``. It is therefore advised to call this procedure
   ## immediately after an OS call fails. On POSIX systems this is not a problem.
+  ##
+  ## See also:
+  ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_
+  ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_
   when defined(nimscript):
     discard
   elif defined(windows):
diff --git a/lib/pure/includes/osseps.nim b/lib/pure/includes/osseps.nim
index 9a79fe303..859722f6a 100644
--- a/lib/pure/includes/osseps.nim
+++ b/lib/pure/includes/osseps.nim
@@ -7,44 +7,44 @@ const
 when defined(Nimdoc): # only for proper documentation:
   const
     CurDir* = '.'
-      ## The constant string used by the operating system to refer to the
+      ## The constant character used by the operating system to refer to the
       ## current directory.
       ##
-      ## For example: '.' for POSIX or ':' for the classic Macintosh.
+      ## For example: `'.'` for POSIX or `':'` for the classic Macintosh.
 
     ParDir* = ".."
       ## The constant string used by the operating system to refer to the
       ## parent directory.
       ##
-      ## For example: ".." for POSIX or "::" for the classic Macintosh.
+      ## For example: `".."` for POSIX or `"::"` for the classic Macintosh.
 
     DirSep* = '/'
       ## The character used by the operating system to separate pathname
-      ## components, for example, '/' for POSIX or ':' for the classic
-      ## Macintosh.
+      ## components, for example: `'/'` for POSIX, `':'` for the classic
+      ## Macintosh, and `'\\'` on Windows.
 
     AltSep* = '/'
       ## An alternative character used by the operating system to separate
-      ## pathname components, or the same as `DirSep` if only one separator
-      ## character exists. This is set to '/' on Windows systems
-      ## where `DirSep` is a backslash.
+      ## pathname components, or the same as `DirSep <#DirSep>`_ if only one separator
+      ## character exists. This is set to `'/'` on Windows systems
+      ## where `DirSep <#DirSep>`_ is a backslash (`'\\'`).
 
     PathSep* = ':'
       ## The character conventionally used by the operating system to separate
-      ## search patch components (as in PATH), such as ':' for POSIX
-      ## or ';' for Windows.
+      ## search patch components (as in PATH), such as `':'` for POSIX
+      ## or `';'` for Windows.
 
     FileSystemCaseSensitive* = true
-      ## true if the file system is case sensitive, false otherwise. Used by
-      ## `cmpPaths` to compare filenames properly.
+      ## True if the file system is case sensitive, false otherwise. Used by
+      ## `cmpPaths proc <#cmpPaths,string,string>`_ to compare filenames properly.
 
     ExeExt* = ""
       ## The file extension of native executables. For example:
-      ## "" for POSIX, "exe" on Windows.
+      ## `""` for POSIX, `"exe"` on Windows (without a dot).
 
     ScriptExt* = ""
-      ## The file extension of a script file. For example: "" for POSIX,
-      ## "bat" on Windows.
+      ## The file extension of a script file. For example: `""` for POSIX,
+      ## `"bat"` on Windows.
 
     DynlibFormat* = "lib$1.so"
       ## The format string to turn a filename into a `DLL`:idx: file (also
@@ -85,9 +85,9 @@ elif doslikeFileSystem:
   const
     CurDir* = '.'
     ParDir* = ".."
-    DirSep* = '\\' # seperator within paths
+    DirSep* = '\\' # separator within paths
     AltSep* = '/'
-    PathSep* = ';' # seperator between paths
+    PathSep* = ';' # separator between paths
     FileSystemCaseSensitive* = false
     ExeExt* = "exe"
     ScriptExt* = "bat"
@@ -127,4 +127,4 @@ else: # UNIX-like operating system
 const
   ExtSep* = '.'
     ## The character which separates the base filename from the extension;
-    ## for example, the '.' in ``os.nim``.
+    ## for example, the `'.'` in ``os.nim``.
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 16d901ff0..ffd60120e 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -53,6 +53,7 @@ when hasThreadSupport:
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: ptr SharedArray[SelectorKey[T]]
       count: int
     Selector*[T] = ptr SelectorImpl[T]
@@ -61,6 +62,7 @@ else:
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: seq[SelectorKey[T]]
       count: int
     Selector*[T] = ref SelectorImpl[T]
@@ -76,6 +78,8 @@ proc newSelector*[T](): Selector[T] =
     raiseOsError(osLastError())
   var maxFD = int(a.rlim_max)
   doAssert(maxFD > 0)
+  # Start with a reasonable size, checkFd() will grow this on demand
+  const numFD = 1024
 
   var epollFD = epoll_create(MAX_EPOLL_EVENTS)
   if epollFD < 0:
@@ -85,14 +89,16 @@ proc newSelector*[T](): Selector[T] =
     result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
     result.epollFD = epollFD
     result.maxFD = maxFD
-    result.fds = allocSharedArray[SelectorKey[T]](maxFD)
+    result.numFD = numFD
+    result.fds = allocSharedArray[SelectorKey[T]](numFD)
   else:
     result = Selector[T]()
     result.epollFD = epollFD
     result.maxFD = maxFD
-    result.fds = newSeq[SelectorKey[T]](maxFD)
+    result.numFD = numFD
+    result.fds = newSeq[SelectorKey[T]](numFD)
 
-  for i in 0 ..< maxFD:
+  for i in 0 ..< numFD:
     result.fds[i].ident = InvalidIdent
 
 proc close*[T](s: Selector[T]) =
@@ -127,6 +133,16 @@ template checkFd(s, f) =
   # FD if there is too many. -- DP
   if f >= s.maxFD:
     raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
+  if f >= s.numFD:
+    var numFD = s.numFD
+    while numFD <= f: numFD *= 2
+    when hasThreadSupport:
+      s.fds = reallocSharedArray(s.fds, numFD)
+    else:
+      s.fds.setLen(numFD)
+    for i in s.numFD ..< numFD:
+      s.fds[i].ident = InvalidIdent
+    s.numFD = numFD
 
 proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 010dd8f70..176da1d9d 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -130,9 +130,9 @@
 ##       { "name": "Susan", "age": herAge }
 ##     ]
 ##
-##    var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
-##    j2["details"] = %* {"age":35, "pi":3.1415}
-##    echo j2
+##   var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
+##   j2["details"] = %* {"age":35, "pi":3.1415}
+##   echo j2
 
 runnableExamples:
   ## Note: for JObject, key ordering is preserved, unlike in some languages,
@@ -142,7 +142,8 @@ runnableExamples:
   doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
 
 import
-  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
+  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson,
+  typetraits
 
 export
   tables.`$`
@@ -356,6 +357,25 @@ when false:
     assert false notin elements, "usage error: only empty sets allowed"
     assert true notin elements, "usage error: only empty sets allowed"
 
+proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
+  ## Sets a field from a `JObject`.
+  assert(obj.kind == JObject)
+  obj.fields[key] = val
+
+#[
+Note: could use simply:
+proc `%`*(o: object|tuple): JsonNode
+but blocked by https://github.com/nim-lang/Nim/issues/10019
+]#
+proc `%`*(o: tuple): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
+  when isNamedTuple(type(o)):
+    result = newJObject()
+    for k, v in o.fieldPairs: result[k] = %v
+  else:
+    result = newJArray()
+    for a in o.fields: result.add(%a)
+
 proc `%`*(o: object): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
   result = newJObject()
@@ -507,11 +527,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
 proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key)
   ## **Deprecated:** use `hasKey` instead.
 
-proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
-  ## Sets a field from a `JObject`.
-  assert(obj.kind == JObject)
-  obj.fields[key] = val
-
 proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
   ## Traverses the node and gets the given value. If any of the
   ## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
@@ -610,7 +625,7 @@ proc escapeJsonUnquoted*(s: string; result: var string) =
     of '\r': result.add("\\r")
     of '"': result.add("\\\"")
     of '\0'..'\7': result.add("\\u000" & $ord(c))
-    of '\14'..'\31': result.add("\\u00" & $ord(c))
+    of '\14'..'\31': result.add("\\u00" & toHex(ord(c), 2))
     of '\\': result.add("\\\\")
     else: result.add(c)
 
@@ -693,6 +708,22 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
 proc pretty*(node: JsonNode, indent = 2): string =
   ## Returns a JSON Representation of `node`, with indentation and
   ## on multiple lines.
+  ##
+  ## Similar to prettyprint in Python.
+  runnableExamples:
+    let j = %* {"name": "Isaac", "books": ["Robot Dreams"],
+                "details": {"age":35, "pi":3.1415}}
+    doAssert pretty(j) == """
+{
+  "name": "Isaac",
+  "books": [
+    "Robot Dreams"
+  ],
+  "details": {
+    "age": 35,
+    "pi": 3.1415
+  }
+}"""
   result = ""
   toPretty(result, node, indent)
 
@@ -1313,6 +1344,12 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
 
       let obj = getType(typeSym[1])
       result = processType(newIdentNode(typeName), obj, jsonNode, true)
+    of "range":
+      let typeNode = typeSym
+      # Deduce the base type from one of the endpoints
+      let baseType = getType(typeNode[1])
+
+      result = createConstructor(baseType, jsonNode)
     of "seq":
       let seqT = typeSym[1]
       let forLoopI = genSym(nskForVar, "i")
@@ -1671,9 +1708,9 @@ when isMainModule:
     doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
 
   doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
-  doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0020" # for #7887
+  doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887
   doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
-  doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0020\"" # for #7887
+  doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0014\"" # for #7887
 
   # Test with extra data
   when not defined(js):
@@ -1706,3 +1743,21 @@ when isMainModule:
       foo = js.to Foo
 
     doAssert(foo.b == "abc")
+
+  # Generate constructors for range[T] types
+  block:
+    type
+      Q1 = range[0..10]
+      Q2 = range[0'i8..10'i8]
+      Q3 = range[0'u16..10'u16]
+      X = object
+        m1: Q1
+        m2: Q2
+        m3: Q3
+
+    let
+      obj = X(m1: 1, m2: 2'i8, m3: 3'u16)
+      jsonObj = %obj
+      desObj = to(jsonObj, type(obj))
+
+    doAssert(desObj == obj)
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
deleted file mode 100644
index 97223ed01..000000000
--- a/lib/pure/matchers.nim
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module contains various string matchers for email addresses, etc.
-##
-## **Warning:** This module is deprecated since version 0.14.0.
-{.deprecated.}
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
-
-include "system/inclrtl"
-
-import parseutils, strutils
-
-proc validEmailAddress*(s: string): bool {.noSideEffect,
-  rtl, extern: "nsuValidEmailAddress".} =
-  ## returns true if `s` seems to be a valid e-mail address.
-  ## The checking also uses a domain list.
-  const
-    chars = Letters + Digits + {'!','#','$','%','&',
-      '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
-  var i = 0
-  if i >= s.len or s[i] notin chars or s[i] == '.': return false
-  while i < s.len and s[i] in chars:
-    if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
-    inc(i)
-  if i >= s.len or s[i] != '@': return false
-  var j = len(s)-1
-  if j >= 0 and s[j] notin Letters: return false
-  while j >= i and s[j] in Letters: dec(j)
-  inc(i) # skip '@'
-  while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
-  if i != s.len: return false
-
-  var x = substr(s, j+1)
-  if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
-  case toLowerAscii(x)
-  of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
-     "aero", "jobs", "museum": return true
-  else: return false
-
-proc parseInt*(s: string, value: var int, validRange: HSlice[int, int]) {.
-  noSideEffect, rtl, extern: "nmatchParseInt".} =
-  ## parses `s` into an integer in the range `validRange`. If successful,
-  ## `value` is modified to contain the result. Otherwise no exception is
-  ## raised and `value` is not touched; this way a reasonable default value
-  ## won't be overwritten.
-  var x = value
-  try:
-    discard parseutils.parseInt(s, x, 0)
-  except OverflowError:
-    discard
-  if x in validRange: value = x
-
-when isMainModule:
-  doAssert "wuseldusel@codehome.com".validEmailAddress
-
-{.pop.}
-
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index ee32772b1..526ddbbb2 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -7,15 +7,48 @@
 #    distribution, for details about the copyright.
 #
 
-##   Constructive mathematics is naturally typed. -- Simon Thompson
+## *Constructive mathematics is naturally typed.* -- Simon Thompson
 ##
 ## Basic math routines for Nim.
+##
+## Note that the trigonometric functions naturally operate on radians.
+## The helper functions `degToRad<#degToRad,T>`_ and `radToDeg<#radToDeg,T>`_
+## provide conversion between radians and degrees.
+##
+## .. code-block::
+##
+##   import math
+##   from sequtils import map
+##
+##   let a = [0.0, PI/6, PI/4, PI/3, PI/2]
+##
+##   echo a.map(sin)
+##   # @[0.0, 0.499…, 0.707…, 0.866…, 1.0]
+##
+##   echo a.map(tan)
+##   # @[0.0, 0.577…, 0.999…, 1.732…, 1.633…e+16]
+##
+##   echo cos(degToRad(180.0))
+##   # -1.0
+##
+##   echo sqrt(-1.0)
+##   # nan   (use `complex` module)
+##
 ## This module is available for the `JavaScript target
 ## <backends.html#the-javascript-target>`_.
 ##
-## Note that the trigonometric functions naturally operate on radians.
-## The helper functions `degToRad` and `radToDeg` provide conversion
-## between radians and degrees.
+## **See also:**
+## * `complex module<complex.html>`_ for complex numbers and their
+##   mathematical operations
+## * `rationals module<rationals.html>`_ for rational numbers and their
+##   mathematical operations
+## * `fenv module<fenv.html>`_ for handling of floating-point rounding
+##   and exceptions (overflow, zero-devide, etc.)
+## * `random module<random.html>`_ for fast and tiny random number generator
+## * `mersenne module<mersenne.html>`_ for Mersenne twister random number generator
+## * `stats module<stats.html>`_ for statistical analysis
+## * `strformat module<strformat>`_ for formatting floats for print
+
 
 include "system/inclrtl"
 {.push debugger:off .} # the user does not want to trace a part
@@ -25,9 +58,11 @@ import bitops
 
 proc binom*(n, k: int): int {.noSideEffect.} =
   ## Computes the `binomial coefficient <https://en.wikipedia.org/wiki/Binomial_coefficient>`_.
-  ##
-  ## .. code-block:: nim
-  ##  echo binom(6, 2) ## 15
+  runnableExamples:
+    doAssert binom(6, 2) == binom(6, 4)
+    doAssert binom(6, 2) == 15
+    doAssert binom(-6, 2) == 1
+    doAssert binom(6, 0) == 1
   if k <= 0: return 1
   if 2*k > n: return binom(n, n-k)
   result = n
@@ -40,10 +75,15 @@ proc createFactTable[N: static[int]]: array[N, int] =
     result[i] = result[i - 1] * i
 
 proc fac*(n: int): int =
-  ## Computes the `factorial <https://en.wikipedia.org/wiki/Factorial>`_ of a non-negative integer ``n``
+  ## Computes the `factorial <https://en.wikipedia.org/wiki/Factorial>`_ of
+  ## a non-negative integer ``n``.
   ##
-  ## .. code-block:: nim
-  ##  echo fac(4) ## 24
+  ## See also:
+  ## * `prod proc <#prod,openArray[T]>`_
+  runnableExamples:
+    doAssert fac(3) == 6
+    doAssert fac(4) == 24
+    doAssert fac(10) == 3628800
   const factTable =
     when sizeof(int) == 4:
       createFactTable[13]()
@@ -55,29 +95,30 @@ proc fac*(n: int): int =
 
 {.push checks:off, line_dir:off, stack_trace:off.}
 
-when defined(Posix):
+when defined(Posix) and not defined(genode):
   {.passl: "-lm".}
 
 const
-  PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
-  TAU* = 2.0 * PI ## the circle constant TAU (= 2 * PI)
+  PI* = 3.1415926535897932384626433 ## The circle constant PI (Ludolph's number)
+  TAU* = 2.0 * PI ## The circle constant TAU (= 2 * PI)
   E* = 2.71828182845904523536028747 ## Euler's number
 
-  MaxFloat64Precision* = 16 ## maximum number of meaningful digits
+  MaxFloat64Precision* = 16 ## Maximum number of meaningful digits
                             ## after the decimal point for Nim's
                             ## ``float64`` type.
-  MaxFloat32Precision* = 8  ## maximum number of meaningful digits
+  MaxFloat32Precision* = 8  ## Maximum number of meaningful digits
                             ## after the decimal point for Nim's
                             ## ``float32`` type.
-  MaxFloatPrecision* = MaxFloat64Precision ## maximum number of
+  MaxFloatPrecision* = MaxFloat64Precision ## Maximum number of
                                            ## meaningful digits
                                            ## after the decimal point
                                            ## for Nim's ``float`` type.
-  RadPerDeg = PI / 180.0 ## number of radians per degree
+  RadPerDeg = PI / 180.0 ## Number of radians per degree
 
 type
-  FloatClass* = enum ## describes the class a floating point value belongs to.
-                     ## This is the type that is returned by `classify`.
+  FloatClass* = enum ## Describes the class a floating point value belongs to.
+                     ## This is the type that is returned by
+                     ## `classify proc <#classify,float>`_.
     fcNormal,    ## value is an ordinary nonzero floating point value
     fcSubnormal, ## value is a subnormal (a very small) floating point value
     fcZero,      ## value is zero
@@ -87,13 +128,14 @@ type
     fcNegInf     ## value is negative infinity
 
 proc classify*(x: float): FloatClass =
-  ## Classifies a floating point value. Returns ``x``'s class as specified by
-  ## `FloatClass`.
+  ## Classifies a floating point value.
   ##
-  ## .. code-block:: nim
-  ##  echo classify(0.3) ## fcNormal
-  ##  echo classify(0.0) ## fcZero
-  ##  echo classify(0.3/0.0) ## fcInf
+  ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_.
+  runnableExamples:
+    doAssert classify(0.3) == fcNormal
+    doAssert classify(0.0) == fcZero
+    doAssert classify(0.3/0.0) == fcInf
+    doAssert classify(-0.3/0.0) == fcNegInf
 
   # JavaScript and most C compilers have no classify:
   if x == 0.0:
@@ -110,20 +152,30 @@ proc classify*(x: float): FloatClass =
 
 proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
   ## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise.
+  ##
   ## Zero and negative numbers are not a power of two.
   ##
-  ## .. code-block:: nim
-  ##  echo isPowerOfTwo(5) ## false
-  ##  echo isPowerOfTwo(8) ## true
+  ## See also:
+  ## * `nextPowerOfTwo proc<#nextPowerOfTwo,int>`_
+  runnableExamples:
+    doAssert isPowerOfTwo(16) == true
+    doAssert isPowerOfTwo(5) == false
+    doAssert isPowerOfTwo(0) == false
+    doAssert isPowerOfTwo(-16) == false
   return (x > 0) and ((x and (x - 1)) == 0)
 
 proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
   ## Returns ``x`` rounded up to the nearest power of two.
+  ##
   ## Zero and negative numbers get rounded up to 1.
   ##
-  ## .. code-block:: nim
-  ##  echo nextPowerOfTwo(8) ## 8
-  ##  echo nextPowerOfTwo(9) ## 16
+  ## See also:
+  ## * `isPowerOfTwo proc<#isPowerOfTwo,int>`_
+  runnableExamples:
+    doAssert nextPowerOfTwo(16) == 16
+    doAssert nextPowerOfTwo(5) == 8
+    doAssert nextPowerOfTwo(0) == 1
+    doAssert nextPowerOfTwo(-16) == 1
   result = x - 1
   when defined(cpu64):
     result = result or (result shr 32)
@@ -138,9 +190,12 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
 
 proc countBits32*(n: int32): int {.noSideEffect.} =
   ## Counts the set bits in ``n``.
-  ##
-  ## .. code-block:: nim
-  ##  echo countBits32(13'i32) ## 3
+  runnableExamples:
+    doAssert countBits32(7) == 3
+    doAssert countBits32(8) == 1
+    doAssert countBits32(15) == 4
+    doAssert countBits32(16) == 1
+    doAssert countBits32(17) == 2
   var v = n
   v = v -% ((v shr 1'i32) and 0x55555555'i32)
   v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
@@ -148,41 +203,99 @@ proc countBits32*(n: int32): int {.noSideEffect.} =
 
 proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
   ## Computes the sum of the elements in ``x``.
+  ##
   ## If ``x`` is empty, 0 is returned.
   ##
-  ## .. code-block:: nim
-  ##  echo sum([1.0, 2.5, -3.0, 4.3]) ## 4.8
+  ## See also:
+  ## * `prod proc <#prod,openArray[T]>`_
+  ## * `cumsum proc <#cumsum,openArray[T]>`_
+  ## * `cumsummed proc <#cumsummed,openArray[T]>`_
+  runnableExamples:
+    doAssert sum([1, 2, 3, 4]) == 10
+    doAssert sum([-1.5, 2.7, -0.1]) == 1.1
   for i in items(x): result = result + i
 
 proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
   ## Computes the product of the elements in ``x``.
+  ##
   ## If ``x`` is empty, 1 is returned.
   ##
-  ## .. code-block:: nim
-  ##  echo prod([1.0, 3.0, -0.2]) ## -0.6
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `fac proc <#fac,int>`_
+  runnableExamples:
+    doAssert prod([1, 2, 3, 4]) == 24
+    doAssert prod([-4, 3, 5]) == -60
   result = 1.T
   for i in items(x): result = result * i
 
+proc cumsummed*[T](x: openArray[T]): seq[T] =
+  ## Return cumulative (aka prefix) summation of ``x``.
+  ##
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `cumsum proc <#cumsum,openArray[T]>`_ for the in-place version
+  runnableExamples:
+    let a = [1, 2, 3, 4]
+    doAssert cumsummed(a) == @[1, 3, 6, 10]
+  result.setLen(x.len)
+  result[0] = x[0]
+  for i in 1 ..< x.len: result[i] = result[i-1] + x[i]
+
+proc cumsum*[T](x: var openArray[T]) =
+  ## Transforms ``x`` in-place (must be declared as `var`) into its
+  ## cumulative (aka prefix) summation.
+  ##
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `cumsummed proc <#cumsummed,openArray[T]>`_ for a version which
+  ##   returns cumsummed sequence
+  runnableExamples:
+    var a = [1, 2, 3, 4]
+    cumsum(a)
+    doAssert a == @[1, 3, 6, 10]
+  for i in 1 ..< x.len: x[i] = x[i-1] + x[i]
+
 {.push noSideEffect.}
 when not defined(JS): # C
   proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
   proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".}
     ## Computes the square root of ``x``.
     ##
+    ## See also:
+    ## * `cbrt proc <#cbrt,float64>`_ for cubic root
+    ##
     ## .. code-block:: nim
+    ##  echo sqrt(4.0)  ## 2.0
     ##  echo sqrt(1.44) ## 1.2
+    ##  echo sqrt(-4.0) ## nan
   proc cbrt*(x: float32): float32 {.importc: "cbrtf", header: "<math.h>".}
   proc cbrt*(x: float64): float64 {.importc: "cbrt", header: "<math.h>".}
     ## Computes the cubic root of ``x``.
     ##
+    ## See also:
+    ## * `sqrt proc <#sqrt,float64>`_ for square root
+    ##
     ## .. code-block:: nim
+    ##  echo cbrt(8.0)   ## 2.0
     ##  echo cbrt(2.197) ## 1.3
+    ##  echo cbrt(-27.0) ## -3.0
   proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
   proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
-    ## Computes the `natural logarithm <https://en.wikipedia.org/wiki/Natural_logarithm>`_ of ``x``.
+    ## Computes the `natural logarithm <https://en.wikipedia.org/wiki/Natural_logarithm>`_
+    ## of ``x``.
+    ##
+    ## See also:
+    ## * `log proc <#log,T,T>`_
+    ## * `log10 proc <#log10,float64>`_
+    ## * `log2 proc <#log2,float64>`_
+    ## * `exp proc <#exp,float64>`_
     ##
     ## .. code-block:: nim
     ##  echo ln(exp(4.0)) ## 4.0
+    ##  echo ln(1.0))     ## 0.0
+    ##  echo ln(0.0)      ## -inf
+    ##  echo ln(-7.0)     ## nan
 else: # JS
   proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
   proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
@@ -193,8 +306,18 @@ else: # JS
 proc log*[T: SomeFloat](x, base: T): T =
   ## Computes the logarithm of ``x`` to base ``base``.
   ##
+  ## See also:
+  ## * `ln proc <#ln,float64>`_
+  ## * `log10 proc <#log10,float64>`_
+  ## * `log2 proc <#log2,float64>`_
+  ## * `exp proc <#exp,float64>`_
+  ##
   ## .. code-block:: nim
-  ##  echo log(9.0, 3.0) ## 2.0
+  ##  echo log(9.0, 3.0)  ## 2.0
+  ##  echo log(32.0, 2.0) ## 5.0
+  ##  echo log(0.0, 2.0)  ## -inf
+  ##  echo log(-7.0, 4.0) ## nan
+  ##  echo log(8.0, -2.0) ## nan
   ln(x) / ln(base)
 
 when not defined(JS): # C
@@ -202,77 +325,164 @@ when not defined(JS): # C
   proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
     ## Computes the common logarithm (base 10) of ``x``.
     ##
+    ## See also:
+    ## * `ln proc <#ln,float64>`_
+    ## * `log proc <#log,T,T>`_
+    ## * `log2 proc <#log2,float64>`_
+    ## * `exp proc <#exp,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo log10(100.0) ## 2.0
+    ##  echo log10(100.0)  ## 2.0
+    ##  echo log10(0.0)    ## nan
+    ##  echo log10(-100.0) ## -inf
   proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
   proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
-    ## Computes the exponential function of ``x`` (pow(E, x)).
+    ## Computes the exponential function of ``x`` (e^x).
+    ##
+    ## See also:
+    ## * `ln proc <#ln,float64>`_
+    ## * `log proc <#log,T,T>`_
+    ## * `log10 proc <#log10,float64>`_
+    ## * `log2 proc <#log2,float64>`_
     ##
     ## .. code-block:: nim
-    ##  echo exp(1.0) ## 2.718281828459045
+    ##  echo exp(1.0)     ## 2.718281828459045
     ##  echo ln(exp(4.0)) ## 4.0
+    ##  echo exp(0.0)     ## 1.0
+    ##  echo exp(-1.0)    ## 0.3678794411714423
   proc sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
   proc sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".}
     ## Computes the sine of ``x``.
     ##
+    ## See also:
+    ## * `cos proc <#cos,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `sinh proc <#sinh,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo sin(PI / 6) ## 0.4999999999999999
+    ##  echo sin(PI / 6)         ## 0.4999999999999999
     ##  echo sin(degToRad(90.0)) ## 1.0
   proc cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
   proc cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".}
     ## Computes the cosine of ``x``.
     ##
+    ## See also:
+    ## * `sin proc <#sin,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `cosh proc <#cosh,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo cos(2 * PI) ## 1.0
+    ##  echo cos(2 * PI)         ## 1.0
     ##  echo cos(degToRad(60.0)) ## 0.5000000000000001
   proc tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
   proc tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".}
     ## Computes the tangent of ``x``.
     ##
+    ## See also:
+    ## * `sin proc <#sin,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ##
     ## .. code-block:: nim
     ##  echo tan(degToRad(45.0)) ## 0.9999999999999999
-    ##  echo tan(PI / 4) ## 0.9999999999999999
+    ##  echo tan(PI / 4)         ## 0.9999999999999999
   proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
   proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
     ## Computes the `hyperbolic sine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `cosh proc <#cosh,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ## * `arcsinh proc <#arcsinh,float64>`_
+    ## * `sin proc <#sin,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo sinh(1.0) ## 1.175201193643801
+    ##  echo sinh(0.0)            ## 0.0
+    ##  echo sinh(1.0)            ## 1.175201193643801
+    ##  echo sinh(degToRad(90.0)) ## 2.301298902307295
   proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
   proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
     ## Computes the `hyperbolic cosine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `sinh proc <#sinh,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ## * `arccosh proc <#arccosh,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo cosh(1.0) ## 1.543080634815244
+    ##  echo cosh(0.0)            ## 1.0
+    ##  echo cosh(1.0)            ## 1.543080634815244
+    ##  echo cosh(degToRad(90.0)) ## 2.509178478658057
   proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
   proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
     ## Computes the `hyperbolic tangent <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `sinh proc <#sinh,float64>`_
+    ## * `cosh proc <#cosh,float64>`_
+    ## * `arctanh proc <#arctanh,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo tanh(1.0) ## 0.7615941559557649
+    ##  echo tanh(0.0)            ## 0.0
+    ##  echo tanh(1.0)            ## 0.7615941559557649
+    ##  echo tanh(degToRad(90.0)) ## 0.9171523356672744
 
   proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
   proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".}
     ## Computes the arc cosine of ``x``.
     ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo arccos(1.0) ## 0.0
+    ##  echo radToDeg(arccos(0.0)) ## 90.0
+    ##  echo radToDeg(arccos(1.0)) ## 0.0
   proc arcsin*(x: float32): float32 {.importc: "asinf", header: "<math.h>".}
   proc arcsin*(x: float64): float64 {.importc: "asin", header: "<math.h>".}
     ## Computes the arc sine of ``x``.
+    ##
+    ## See also:
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `sin proc <#sin,float64>`_
+    ##
+    ## .. code-block:: nim
+    ##  echo radToDeg(arcsin(0.0)) ## 0.0
+    ##  echo radToDeg(arcsin(1.0)) ## 90.0
   proc arctan*(x: float32): float32 {.importc: "atanf", header: "<math.h>".}
   proc arctan*(x: float64): float64 {.importc: "atan", header: "<math.h>".}
     ## Calculate the arc tangent of ``x``.
     ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ##
     ## .. code-block:: nim
     ##  echo arctan(1.0) ## 0.7853981633974483
     ##  echo radToDeg(arctan(1.0)) ## 45.0
   proc arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "<math.h>".}
   proc arctan2*(y, x: float64): float64 {.importc: "atan2", header: "<math.h>".}
     ## Calculate the arc tangent of ``y`` / ``x``.
-    ## `arctan2` returns the arc tangent of ``y`` / ``x``; it produces correct
-    ## results even when the resulting angle is near pi/2 or -pi/2
-    ## (``x`` near 0).
+    ##
+    ## It produces correct results even when the resulting angle is near
+    ## pi/2 or -pi/2 (``x`` near 0).
+    ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `tan proc <#tan,float64>`_
     ##
     ## .. code-block:: nim
     ##  echo arctan2(1.0, 0.0) ## 1.570796326794897
@@ -313,18 +523,18 @@ else: # JS
   proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.}
 
 proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x)
-  ## Computes the cotangent of ``x``.
+  ## Computes the cotangent of ``x`` (1 / tan(x)).
 proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
-  ## Computes the secant of ``x``.
+  ## Computes the secant of ``x`` (1 / cos(x)).
 proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
-  ## Computes the cosecant of ``x``.
+  ## Computes the cosecant of ``x`` (1 / sin(x)).
 
 proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
-  ## Computes the hyperbolic cotangent of ``x``.
+  ## Computes the hyperbolic cotangent of ``x`` (1 / tanh(x)).
 proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
-  ## Computes the hyperbolic secant of ``x``.
+  ## Computes the hyperbolic secant of ``x`` (1 / cosh(x)).
 proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
-  ## Computes the hyperbolic cosecant of ``x``.
+  ## Computes the hyperbolic cosecant of ``x`` (1 / sinh(x)).
 
 proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
   ## Computes the inverse cotangent of ``x``.
@@ -352,11 +562,17 @@ when not defined(JS): # C
     ##  echo hypot(4.0, 3.0) ## 5.0
   proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
   proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
-    ## computes x to power raised of y.
+    ## Computes x to power raised of y.
+    ##
+    ## To compute power between integers (e.g. 2^6), use `^ proc<#^,T,Natural>`_.
     ##
-    ## To compute power between integers, use ``^`` e.g. 2 ^ 6
+    ## See also:
+    ## * `^ proc<#^,T,Natural>`_
+    ## * `sqrt proc <#sqrt,float64>`_
+    ## * `cbrt proc <#cbrt,float64>`_
     ##
     ## .. code-block:: nim
+    ##  echo pow(100, 1.5)  ## 1000.0
     ##  echo pow(16.0, 0.5) ## 4.0
 
   # TODO: add C89 version on windows
@@ -370,6 +586,15 @@ when not defined(JS): # C
     proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
     proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
       ## Computes the the `gamma function <https://en.wikipedia.org/wiki/Gamma_function>`_ for ``x``.
+      ##
+      ## See also:
+      ## * `lgamma proc <#lgamma,float64>`_ for a natural log of gamma function
+      ##
+      ## .. code-block:: Nim
+      ##  echo gamma(1.0)  # 1.0
+      ##  echo gamma(4.0)  # 6.0
+      ##  echo gamma(11.0) # 3628800.0
+      ##  echo gamma(-1.0) # nan
     proc tgamma*(x: float32): float32
       {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
     proc tgamma*(x: float64): float64
@@ -379,19 +604,43 @@ when not defined(JS): # C
     proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
     proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
       ## Computes the natural log of the gamma function for ``x``.
+      ##
+      ## See also:
+      ## * `gamma proc <#gamma,float64>`_ for gamma function
+      ##
+      ## .. code-block:: Nim
+      ##  echo lgamma(1.0)  # 1.0
+      ##  echo lgamma(4.0)  # 1.791759469228055
+      ##  echo lgamma(11.0) # 15.10441257307552
+      ##  echo lgamma(-1.0) # inf
 
   proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
   proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
     ## Computes the floor function (i.e., the largest integer not greater than ``x``).
     ##
+    ## See also:
+    ## * `ceil proc <#ceil,float64>`_
+    ## * `round proc <#round,float64>`_
+    ## * `trunc proc <#trunc,float64>`_
+    ##
     ## .. code-block:: nim
+    ##  echo floor(2.1)  ## 2.0
+    ##  echo floor(2.9)  ## 2.0
     ##  echo floor(-3.5) ## -4.0
 
   proc ceil*(x: float32): float32 {.importc: "ceilf", header: "<math.h>".}
   proc ceil*(x: float64): float64 {.importc: "ceil", header: "<math.h>".}
-    ## Computes the ceiling function (i.e., the smallest integer not less than ``x``).
+    ## Computes the ceiling function (i.e., the smallest integer not smaller
+    ## than ``x``).
+    ##
+    ## See also:
+    ## * `floor proc <#floor,float64>`_
+    ## * `round proc <#round,float64>`_
+    ## * `trunc proc <#trunc,float64>`_
     ##
     ## .. code-block:: nim
+    ##  echo ceil(2.1)  ## 3.0
+    ##  echo ceil(2.9)  ## 3.0
     ##  echo ceil(-2.1) ## -2.0
 
   when windowsCC89:
@@ -452,26 +701,50 @@ when not defined(JS): # C
   else:
     proc round*(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
     proc round*(x: float64): float64 {.importc: "round", header: "<math.h>".}
-      ## Rounds a float to zero decimal places.  Used internally by the round
-      ## function when the specified number of places is 0.
+      ## Rounds a float to zero decimal places.
+      ##
+      ## Used internally by the `round proc <#round,T,int>`_
+      ## when the specified number of places is 0.
+      ##
+      ## See also:
+      ## * `round proc <#round,T,int>`_ for rounding to the specific
+      ##   number of decimal places
+      ## * `floor proc <#floor,float64>`_
+      ## * `ceil proc <#ceil,float64>`_
+      ## * `trunc proc <#trunc,float64>`_
+      ##
+      ## .. code-block:: nim
+      ##   echo round(3.4) ## 3.0
+      ##   echo round(3.5) ## 4.0
+      ##   echo round(4.5) ## 5.0
 
     proc trunc*(x: float32): float32 {.importc: "truncf", header: "<math.h>".}
     proc trunc*(x: float64): float64 {.importc: "trunc", header: "<math.h>".}
       ## Truncates ``x`` to the decimal point.
       ##
+      ## See also:
+      ## * `floor proc <#floor,float64>`_
+      ## * `ceil proc <#ceil,float64>`_
+      ## * `round proc <#round,float64>`_
+      ##
       ## .. code-block:: nim
       ##  echo trunc(PI) # 3.0
       ##  echo trunc(-1.85) # -1.0
 
   proc fmod*(x, y: float32): float32 {.deprecated: "use mod instead", importc: "fmodf", header: "<math.h>".}
   proc fmod*(x, y: float64): float64 {.deprecated: "use mod instead", importc: "fmod", header: "<math.h>".}
+    ## **Deprecated since version 0.19.0**: Use the `mod proc
+    ## <#mod,float64,float64>`_ instead.
+    ##
     ## Computes the remainder of ``x`` divided by ``y``.
-    ## **Deprecated since version 0.19.0**: Use the ``mod`` operator instead.
 
   proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
   proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
     ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``).
     ##
+    ## See also:
+    ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior
+    ##
     ## .. code-block:: nim
     ##  ( 6.5 mod  2.5) ==  1.5
     ##  (-6.5 mod  2.5) == -1.5
@@ -502,16 +775,22 @@ else: # JS
     ##  (-6.5 mod -2.5) == -1.5
 
 proc round*[T: float32|float64](x: T, places: int): T {.deprecated: "use format instead".} =
+  ## **Deprecated:** use `strformat module <strformat.html>`_
+  ##
   ## Decimal rounding on a binary floating point number.
   ##
   ## This function is NOT reliable. Floating point numbers cannot hold
-  ## non integer decimals precisely.  If ``places`` is 0 (or omitted),
+  ## non integer decimals precisely. If ``places`` is 0 (or omitted),
   ## round to the nearest integral value following normal mathematical
-  ## rounding rules (e.g.  ``round(54.5) -> 55.0``).  If ``places`` is
+  ## rounding rules (e.g.  ``round(54.5) -> 55.0``). If ``places`` is
   ## greater than 0, round to the given number of decimal places,
-  ## e.g. ``round(54.346, 2) -> 54.350000000000001421...``.  If ``places`` is negative, round
-  ## to the left of the decimal place, e.g.  ``round(537.345, -1) ->
+  ## e.g. ``round(54.346, 2) -> 54.350000000000001421…``. If ``places`` is negative, round
+  ## to the left of the decimal place, e.g. ``round(537.345, -1) ->
   ## 540.0``
+  ##
+  ## .. code-block:: Nim
+  ##  echo round(PI, 2) ## 3.14
+  ##  echo round(PI, 4) ## 3.1416
   if places == 0:
     result = round(x)
   else:
@@ -520,9 +799,14 @@ proc round*[T: float32|float64](x: T, places: int): T {.deprecated: "use format
 
 proc floorDiv*[T: SomeInteger](x, y: T): T =
   ## Floor division is conceptually defined as ``floor(x / y)``.
-  ## This is different from the ``div`` operator, which is defined
-  ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
-  ## rounds down.
+  ##
+  ## This is different from the `system.div <system.html#div,int,int>`_
+  ## operator, which is defined as ``trunc(x / y)``.
+  ## That is, ``div`` rounds towards ``0`` and ``floorDiv`` rounds down.
+  ##
+  ## See also:
+  ## * `system.div proc <system.html#div,int,int>`_ for integer division
+  ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior
   ##
   ## .. code-block:: nim
   ##  echo floorDiv( 13,  3) #  4
@@ -535,8 +819,13 @@ proc floorDiv*[T: SomeInteger](x, y: T): T =
 
 proc floorMod*[T: SomeNumber](x, y: T): T =
   ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y)``.
+  ##
   ## This proc behaves the same as the ``%`` operator in Python.
   ##
+  ## See also:
+  ## * `mod proc <#mod,float64,float64>`_
+  ## * `floorDiv proc <#floorDiv,T,T>`_
+  ##
   ## .. code-block:: nim
   ##  echo floorMod( 13,  3) #  1
   ##  echo floorMod(-13,  3) #  2
@@ -552,6 +841,7 @@ when not defined(JS):
     importc: "frexp", header: "<math.h>".}
   proc frexp*[T, U](x: T, exponent: var U): T =
     ## Split a number into mantissa and exponent.
+    ##
     ## ``frexp`` calculates the mantissa m (a float greater than or equal to 0.5
     ## and less than 1) and the integer value n such that ``x`` (the original
     ## float value) equals ``m * 2**n``. frexp stores n in `exponent` and returns
@@ -584,7 +874,19 @@ when not defined(JS):
   else:
     proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
     proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
-      ## Computes the binary logarithm (base 2) of ``x``
+      ## Computes the binary logarithm (base 2) of ``x``.
+      ##
+      ## See also:
+      ## * `log proc <#log,T,T>`_
+      ## * `log10 proc <#log10,float64>`_
+      ## * `ln proc <#ln,float64>`_
+      ## * `exp proc <#exp,float64>`_
+      ##
+      ## .. code-block:: Nim
+      ##  echo log2(8.0)  # 3.0
+      ##  echo log2(1.0)  # 0.0
+      ##  echo log2(0.0)  # -inf
+      ##  echo log2(-2.0) # nan
 
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
@@ -613,7 +915,8 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## function in C.
   ##
   ## .. code-block:: nim
-  ##  echo splitDecimal(5.25) # (intpart: 5.0, floatpart: 0.25)
+  ##  echo splitDecimal(5.25)  # (intpart: 5.0, floatpart: 0.25)
+  ##  echo splitDecimal(-2.73) # (intpart: -2.0, floatpart: -0.73)
   var
     absolute: T
   absolute = abs(x)
@@ -626,26 +929,36 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
 {.pop.}
 
 proc degToRad*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from degrees to radians
+  ## Convert from degrees to radians.
+  ##
+  ## See also:
+  ## * `radToDeg proc <#radToDeg,T>`_
   ##
   ## .. code-block:: nim
   ##  echo degToRad(180.0) # 3.141592653589793
   result = T(d) * RadPerDeg
 
 proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from radians to degrees
-
+  ## Convert from radians to degrees.
+  ##
+  ## See also:
+  ## * `degToRad proc <#degToRad,T>`_
+  ##
   ## .. code-block:: nim
   ##  echo degToRad(2 * PI) # 360.0
   result = T(d) / RadPerDeg
 
 proc sgn*[T: SomeNumber](x: T): int {.inline.} =
-  ## Sign function. Returns -1 for negative numbers and ``NegInf``, 1 for
-  ## positive numbers and ``Inf``, and 0 for positive zero, negative zero and
-  ## ``NaN``.
+  ## Sign function.
+  ##
+  ## Returns:
+  ## * `-1` for negative numbers and ``NegInf``,
+  ## * `1` for positive numbers and ``Inf``,
+  ## * `0` for positive zero, negative zero and ``NaN``
   ##
   ## .. code-block:: nim
-  ##  echo sgn(-5) # 1
+  ##  echo sgn(5)    # 1
+  ##  echo sgn(0)    # 0
   ##  echo sgn(-4.1) # -1
   ord(T(0) < x) - ord(x < T(0))
 
@@ -653,11 +966,20 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
 {.pop.}
 
 proc `^`*[T](x: T, y: Natural): T =
-  ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
-  ## `pow <#pow,float,float>`_ for negative exponents.
+  ## Computes ``x`` to the power ``y``.
+  ##
+  ## Exponent ``y`` must be non-negative, use
+  ## `pow proc <#pow,float64,float64>`_ for negative exponents.
+  ##
+  ## See also:
+  ## * `pow proc <#pow,float64,float64>`_ for negative exponent or
+  ##   floats
+  ## * `sqrt proc <#sqrt,float64>`_
+  ## * `cbrt proc <#cbrt,float64>`_
   ##
   ## .. code-block:: nim
-  ##  echo 2 ^ 3 # 8
+  ##  echo 2^3  # 8
+  ##  echo -2^3 # -8
   when compiles(y >= T(0)):
     assert y >= T(0)
   else:
@@ -675,9 +997,16 @@ proc `^`*[T](x: T, y: Natural): T =
 
 proc gcd*[T](x, y: T): T =
   ## Computes the greatest common (positive) divisor of ``x`` and ``y``.
+  ##
   ## Note that for floats, the result cannot always be interpreted as
   ## "greatest decimal `z` such that ``z*N == x and z*M == y``
   ## where N and M are positive integers."
+  ##
+  ## See also:
+  ## * `gcd proc <#gcd,SomeInteger,SomeInteger>`_ for integer version
+  ## * `lcm proc <#lcm,T,T>`_
+  runnableExamples:
+    doAssert gcd(13.5, 9.0) == 4.5
   var (x, y) = (x, y)
   while y != 0:
     x = x mod y
@@ -685,11 +1014,15 @@ proc gcd*[T](x, y: T): T =
   abs x
 
 proc gcd*(x, y: SomeInteger): SomeInteger =
-  ## Computes the greatest common (positive) divisor of ``x`` and ``y``.
-  ## Using binary GCD (aka Stein's) algorithm.
+  ## Computes the greatest common (positive) divisor of ``x`` and ``y``,
+  ## using binary GCD (aka Stein's) algorithm.
   ##
-  ## .. code-block:: nim
-  ##  echo gcd(24, 30) # 6
+  ## See also:
+  ## * `gcd proc <#gcd,T,T>`_ for floats version
+  ## * `lcm proc <#lcm,T,T>`_
+  runnableExamples:
+    doAssert gcd(12, 8) == 4
+    doAssert gcd(17, 63) == 1
   when x is SomeSignedInt:
     var x = abs(x)
   else:
@@ -716,10 +1049,15 @@ proc gcd*(x, y: SomeInteger): SomeInteger =
 proc lcm*[T](x, y: T): T =
   ## Computes the least common multiple of ``x`` and ``y``.
   ##
-  ## .. code-block:: nim
-  ##  echo lcm(24, 30) # 120
+  ## See also:
+  ## * `gcd proc <#gcd,T,T>`_
+  runnableExamples:
+    doAssert lcm(24, 30) == 120
+    doAssert lcm(13, 39) == 39
   x div gcd(x, y) * y
 
+
+
 when isMainModule and not defined(JS) and not windowsCC89:
   # Check for no side effect annotation
   proc mySqrt(num: float): float {.noSideEffect.} =
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index d1a006eee..277c4ddb6 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -372,9 +372,8 @@ proc `==`*(x, y: MemSlice): bool =
 
 proc `$`*(ms: MemSlice): string {.inline.} =
   ## Return a Nim string built from a MemSlice.
-  var buf = newString(ms.size)
-  copyMem(addr(buf[0]), ms.data, ms.size)
-  result = buf
+  result.setLen(ms.size)
+  copyMem(addr(result[0]), ms.data, ms.size)
 
 iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} =
   ## Iterates over [optional `eat`] `delim`-delimited slices in MemFile `mfile`.
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index f98f9a444..6d4df1c5d 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -71,6 +71,7 @@ type
     IPPROTO_IPV6,       ## Internet Protocol Version 6. Unsupported on Windows.
     IPPROTO_RAW,        ## Raw IP Packets Protocol. Unsupported on Windows.
     IPPROTO_ICMP        ## Control message protocol. Unsupported on Windows.
+    IPPROTO_ICMPV6      ## Control message protocol for IPv6. Unsupported on Windows.
 
   Servent* = object ## information about a service
     name*: string
@@ -154,6 +155,7 @@ when not useWinVersion:
     of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
     of IPPROTO_RAW:    result = posix.IPPROTO_RAW
     of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
+    of IPPROTO_ICMPV6:   result = posix.IPPROTO_ICMPV6
 
 else:
   proc toInt(domain: Domain): cint =
@@ -179,7 +181,7 @@ proc toSockType*(protocol: Protocol): SockType =
     SOCK_STREAM
   of IPPROTO_UDP:
     SOCK_DGRAM
-  of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP:
+  of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6:
     SOCK_RAW
 
 proc createNativeSocket*(domain: Domain = AF_INET,
@@ -255,17 +257,14 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku):
     if domain == AF_INET6:
       hints.ai_flags = AI_V4MAPPED
-  var gaiResult = getaddrinfo(address, $port, addr(hints), result)
+  let socket_port = if sockType == SOCK_RAW: "" else: $port
+  var gaiResult = getaddrinfo(address, socket_port, addr(hints), result)
   if gaiResult != 0'i32:
     when useWinVersion:
       raiseOSError(osLastError())
     else:
       raiseOSError(osLastError(), $gai_strerror(gaiResult))
 
-proc dealloc*(ai: ptr AddrInfo) {.deprecated.} =
-  ## Deprecated since 0.16.2. Use ``freeAddrInfo`` instead.
-  freeaddrinfo(ai)
-
 proc ntohl*(x: uint32): uint32 =
   ## Converts 32-bit unsigned integers from network to host byte order.
   ## On machines where the host byte order is the same as network byte order,
@@ -276,15 +275,6 @@ proc ntohl*(x: uint32): uint32 =
                  (x shl 8'u32 and 0xff0000'u32) or
                  (x shl 24'u32)
 
-template ntohl*(x: int32): untyped {.deprecated.} =
-  ## Converts 32-bit integers from network to host byte order.
-  ## On machines where the host byte order is the same as network byte order,
-  ## this is a no-op; otherwise, it performs a 4-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, IPv4
-  ## addresses are now treated as unsigned integers. Please use the unsigned
-  ## version of this template.
-  cast[int32](nativesockets.ntohl(cast[uint32](x)))
-
 proc ntohs*(x: uint16): uint16 =
   ## Converts 16-bit unsigned integers from network to host byte order. On
   ## machines where the host byte order is the same as network byte order,
@@ -292,39 +282,12 @@ proc ntohs*(x: uint16): uint16 =
   when cpuEndian == bigEndian: result = x
   else: result = (x shr 8'u16) or (x shl 8'u16)
 
-template ntohs*(x: int16): untyped {.deprecated.} =
-  ## Converts 16-bit integers from network to host byte order. On
-  ## machines where the host byte order is the same as network byte order,
-  ## this is a no-op; otherwise, it performs a 2-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, where port
-  ## numbers became unsigned integers. Please use the unsigned version of
-  ## this template.
-  cast[int16](nativesockets.ntohs(cast[uint16](x)))
-
-template htonl*(x: int32): untyped {.deprecated.} =
-  ## Converts 32-bit integers from host to network byte order. On machines
-  ## where the host byte order is the same as network byte order, this is
-  ## a no-op; otherwise, it performs a 4-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, IPv4
-  ## addresses are now treated as unsigned integers. Please use the unsigned
-  ## version of this template.
-  nativesockets.ntohl(x)
-
 template htonl*(x: uint32): untyped =
   ## Converts 32-bit unsigned integers from host to network byte order. On
   ## machines where the host byte order is the same as network byte order,
   ## this is a no-op; otherwise, it performs a 4-byte swap operation.
   nativesockets.ntohl(x)
 
-template htons*(x: int16): untyped {.deprecated.} =
-  ## Converts 16-bit integers from host to network byte order.
-  ## On machines where the host byte order is the same as network byte
-  ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, where port
-  ## numbers became unsigned integers. Please use the unsigned version of
-  ## this template.
-  nativesockets.ntohs(x)
-
 template htons*(x: uint16): untyped =
   ## Converts 16-bit unsigned integers from host to network byte order.
   ## On machines where the host byte order is the same as network byte
@@ -646,29 +609,6 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
       inc(i)
   setLen(s, L)
 
-proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated: "use selectRead instead".} =
-  ## When a socket in ``readfds`` is ready to be read from then a non-zero
-  ## value will be returned specifying the count of the sockets which can be
-  ## read from. The sockets which can be read from will also be removed
-  ## from ``readfds``.
-  ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
-  ## an unlimited time.
-  ## **Warning:** This is deprecated since version 0.16.2.
-  ## Use the ``selectRead`` procedure instead.
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var rd: TFdSet
-  var m = 0
-  createFdSet((rd), readfds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
-  else:
-    result = int(select(cint(m+1), addr(rd), nil, nil, nil))
-
-  pruneSocketSet(readfds, (rd))
-
 proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
   ## When a socket in ``readfds`` is ready to be read from then a non-zero
   ## value will be returned specifying the count of the sockets which can be
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 840a81f17..43284f872 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -233,7 +233,7 @@ proc parseIPv4Address(addressStr: string): IpAddress =
   var
     byteCount = 0
     currentByte:uint16 = 0
-    seperatorValid = false
+    separatorValid = false
 
   result.family = IpAddressFamily.IPv4
 
@@ -244,20 +244,20 @@ proc parseIPv4Address(addressStr: string): IpAddress =
       if currentByte > 255'u16:
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
-      seperatorValid = true
+      separatorValid = true
     elif addressStr[i] == '.': # IPv4 address separator
-      if not seperatorValid or byteCount >= 3:
+      if not separatorValid or byteCount >= 3:
         raise newException(ValueError,
           "Invalid IP Address. The address consists of too many groups")
       result.address_v4[byteCount] = cast[uint8](currentByte)
       currentByte = 0
       byteCount.inc
-      seperatorValid = false
+      separatorValid = false
     else:
       raise newException(ValueError,
         "Invalid IP Address. Address contains an invalid character")
 
-  if byteCount != 3 or not seperatorValid:
+  if byteCount != 3 or not separatorValid:
     raise newException(ValueError, "Invalid IP Address")
   result.address_v4[byteCount] = cast[uint8](currentByte)
 
@@ -272,7 +272,7 @@ proc parseIPv6Address(addressStr: string): IpAddress =
     groupCount = 0
     currentGroupStart = 0
     currentShort:uint32 = 0
-    seperatorValid = true
+    separatorValid = true
     dualColonGroup = -1
     lastWasColon = false
     v4StartPos = -1
@@ -280,15 +280,15 @@ proc parseIPv6Address(addressStr: string): IpAddress =
 
   for i,c in addressStr:
     if c == ':':
-      if not seperatorValid:
+      if not separatorValid:
         raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid seperator")
+          "Invalid IP Address. Address contains an invalid separator")
       if lastWasColon:
         if dualColonGroup != -1:
           raise newException(ValueError,
-            "Invalid IP Address. Address contains more than one \"::\" seperator")
+            "Invalid IP Address. Address contains more than one \"::\" separator")
         dualColonGroup = groupCount
-        seperatorValid = false
+        separatorValid = false
       elif i != 0 and i != high(addressStr):
         if groupCount >= 8:
           raise newException(ValueError,
@@ -297,7 +297,7 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
         currentShort = 0
         groupCount.inc()
-        if dualColonGroup != -1: seperatorValid = false
+        if dualColonGroup != -1: separatorValid = false
       elif i == 0: # only valid if address starts with ::
         if addressStr[1] != ':':
           raise newException(ValueError,
@@ -309,11 +309,11 @@ proc parseIPv6Address(addressStr: string): IpAddress =
       lastWasColon = true
       currentGroupStart = i + 1
     elif c == '.': # Switch to parse IPv4 mode
-      if i < 3 or not seperatorValid or groupCount >= 7:
+      if i < 3 or not separatorValid or groupCount >= 7:
         raise newException(ValueError, "Invalid IP Address")
       v4StartPos = currentGroupStart
       currentShort = 0
-      seperatorValid = false
+      separatorValid = false
       break
     elif c in strutils.HexDigits:
       if c in strutils.Digits: # Normal digit
@@ -326,14 +326,14 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
       lastWasColon = false
-      seperatorValid = true
+      separatorValid = true
     else:
       raise newException(ValueError,
         "Invalid IP Address. Address contains an invalid character")
 
 
   if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
-    if seperatorValid: # Copy remaining data
+    if separatorValid: # Copy remaining data
       if groupCount >= 8:
         raise newException(ValueError,
           "Invalid IP Address. The address consists of too many groups")
@@ -347,19 +347,19 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         if currentShort > 255'u32:
           raise newException(ValueError,
             "Invalid IP Address. Value is out of range")
-        seperatorValid = true
+        separatorValid = true
       elif c == '.': # IPv4 address separator
-        if not seperatorValid or byteCount >= 3:
+        if not separatorValid or byteCount >= 3:
           raise newException(ValueError, "Invalid IP Address")
         result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
         currentShort = 0
         byteCount.inc()
-        seperatorValid = false
+        separatorValid = false
       else: # Invalid character
         raise newException(ValueError,
           "Invalid IP Address. Address contains an invalid character")
 
-    if byteCount != 3 or not seperatorValid:
+    if byteCount != 3 or not separatorValid:
       raise newException(ValueError, "Invalid IP Address")
     result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
     groupCount += 2
@@ -967,39 +967,6 @@ when defined(posix) or defined(nimdoc):
         raiseOSError(osLastError())
 
 when defined(ssl):
-  proc handshake*(socket: Socket): bool
-    {.tags: [ReadIOEffect, WriteIOEffect], deprecated.} =
-    ## This proc needs to be called on a socket after it connects. This is
-    ## only applicable when using ``connectAsync``.
-    ## This proc performs the SSL handshake.
-    ##
-    ## Returns ``False`` whenever the socket is not yet ready for a handshake,
-    ## ``True`` whenever handshake completed successfully.
-    ##
-    ## A SslError error is raised on any other errors.
-    ##
-    ## **Note:** This procedure is deprecated since version 0.14.0.
-    result = true
-    if socket.isSSL:
-      var ret = SSLConnect(socket.sslHandle)
-      if ret <= 0:
-        var errret = SSLGetError(socket.sslHandle, ret)
-        case errret
-        of SSL_ERROR_ZERO_RETURN:
-          raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-        of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
-          SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE:
-          return false
-        of SSL_ERROR_WANT_X509_LOOKUP:
-          raiseSSLError("Function for x509 lookup has been called.")
-        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
-          raiseSSLError()
-        else:
-          raiseSSLError("Unknown Error")
-      socket.sslNoHandshake = false
-    else:
-      raiseSSLError("Socket is not an SSL socket.")
-
   proc gotHandshake*(socket: Socket): bool =
     ## Determines whether a handshake has occurred between a client (``socket``)
     ## and the server that ``socket`` is connected to.
@@ -1026,7 +993,7 @@ proc select(readfd: Socket, timeout = 500): int =
     return 1
 
   var fds = @[readfd.fd]
-  result = select(fds, timeout)
+  result = selectRead(fds, timeout)
 
 proc isClosed(socket: Socket): bool =
   socket.fd == osInvalidSocket
@@ -1328,7 +1295,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
 
   if result != -1:
     data.setLen(result)
-    address = $inet_ntoa(sockAddress.sin_addr)
+    address = getAddrString(cast[ptr SockAddr](addr(sockAddress)))
     port = ntohs(sockAddress.sin_port).Port
   else:
     raiseOSError(osLastError())
@@ -1694,7 +1661,5 @@ proc connect*(socket: Socket, address: string, port = Port(0),
     when defineSsl and not defined(nimdoc):
       if socket.isSSL:
         socket.fd.setBlocking(true)
-        {.warning[Deprecated]: off.}
-        doAssert socket.handshake()
-        {.warning[Deprecated]: on.}
+        doAssert socket.gotHandshake()
   socket.fd.setBlocking(true)
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index d6369b5f9..3aee3941d 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -60,23 +60,21 @@ proc `$`*(oid: Oid): string =
   result = newString(24)
   oidToString(oid, result)
 
+proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
+proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
+
+var t = getTime().toUnix.int32
+srand(t)
+
 var
-  incr: int
-  fuzz: int32
+  incr: int = rand()
+  fuzz: int32 = rand()
 
 proc genOid*(): Oid =
   ## generates a new OID.
-  proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
-  proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
-
-  var t = getTime().toUnix.int32
-
+  t = getTime().toUnix.int32
   var i = int32(atomicInc(incr))
 
-  if fuzz == 0:
-    # racy, but fine semantically:
-    srand(t)
-    fuzz = rand()
   bigEndian32(addr result.time, addr(t))
   result.fuzz = fuzz
   bigEndian32(addr result.count, addr(i))
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index b6cef191b..e88c3c6e8 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -10,6 +10,36 @@
 ## This module contains basic operating system facilities like
 ## retrieving environment variables, reading command line arguments,
 ## working with directories, running shell commands, etc.
+##
+## .. code-block::
+##   import os
+##
+##   let myFile = "/path/to/my/file.nim"
+##
+##   let splittedPath = splitPath(myFile)
+##   assert splittedPath.head == "/path/to/my"
+##   assert splittedPath.tail == "file.nim"
+##
+##   assert parentDir(myFile) == "/path/to/my"
+##
+##   let splittedFile = splitFile(myFile)
+##   assert splittedFile.dir == "/path/to/my"
+##   assert splittedFile.name == "file"
+##   assert splittedFile.ext == ".nim"
+##
+##   assert myFile.changeFileExt("c") == "/path/to/my/file.c"
+##
+##
+## **See also:**
+## * `osproc module <osproc.html>`_ for process communication beyond
+##   `execShellCmd proc <#execShellCmd,string>`_
+## * `parseopt module <parseopt.html>`_ for command-line parser beyond
+##   `parseCmdLine proc <#parseCmdLine,string>`_
+## * `distros module <distros.html>`_
+## * `dynlib module <dynlib.html>`_
+## * `streams module <streams.html>`_
+
+
 {.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger: off.}
@@ -39,24 +69,24 @@ else:
   {.pragma: noNimScript.}
 
 type
-  ReadEnvEffect* = object of ReadIOEffect   ## effect that denotes a read
-                                            ## from an environment variable
-  WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
-                                            ## to an environment variable
+  ReadEnvEffect* = object of ReadIOEffect   ## Effect that denotes a read
+                                            ## from an environment variable.
+  WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write
+                                            ## to an environment variable.
 
-  ReadDirEffect* = object of ReadIOEffect   ## effect that denotes a read
+  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
                                             ## operation from the directory
-                                            ## structure
-  WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
+                                            ## structure.
+  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
                                             ## operation to
-                                            ## the directory structure
+                                            ## the directory structure.
 
   OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
 
 include "includes/osseps"
 
 proc normalizePathEnd(path: var string, trailingSep = false) =
-  ## ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
+  ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
   ## ``trailingSep``, and taking care of edge cases: it preservers whether
   ## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
   ## not `AltSep`.
@@ -83,27 +113,24 @@ proc joinPath*(head, tail: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Joins two directory names to one.
   ##
-  ## For example on Unix:
-  ##
-  ## .. code-block:: nim
-  ##   joinPath("usr", "lib")
-  ##
-  ## results in:
-  ##
-  ## .. code-block:: nim
-  ##   "usr/lib"
-  ##
-  ## If head is the empty string, tail is returned. If tail is the empty
-  ## string, head is returned with a trailing path separator. If tail starts
-  ## with a path separator it will be removed when concatenated to head. Other
-  ## path separators not located on boundaries won't be modified. More
-  ## examples on Unix:
+  ## If `head` is the empty string, `tail` is returned. If `tail` is the empty
+  ## string, `head` is returned with a trailing path separator. If `tail` starts
+  ## with a path separator it will be removed when concatenated to `head`. Other
+  ## path separators not located on boundaries won't be modified.
   ##
-  ## .. code-block:: nim
-  ##   assert joinPath("usr", "") == "usr/"
-  ##   assert joinPath("", "lib") == "lib"
-  ##   assert joinPath("", "/lib") == "/lib"
-  ##   assert joinPath("usr/", "/lib") == "usr/lib"
+  ## See also:
+  ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_
+  ## * `/ proc <#/,string,string>`_
+  ## * `splitPath proc <#splitPath,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("usr", "lib") == "usr/lib"
+      assert joinPath("usr", "") == "usr/"
+      assert joinPath("", "lib") == "lib"
+      assert joinPath("", "/lib") == "/lib"
+      assert joinPath("usr/", "/lib") == "usr/lib"
+      assert joinPath("usr/lib", "../bin") == "usr/bin"
+
   result = newStringOfCap(head.len + tail.len)
   var state = 0
   addNormalizePath(head, result, state, DirSep)
@@ -127,9 +154,23 @@ proc joinPath*(head, tail: string): string {.
 
 proc joinPath*(parts: varargs[string]): string {.noSideEffect,
   rtl, extern: "nos$1OpenArray".} =
-  ## The same as `joinPath(head, tail)`, but works with any number of
-  ## directory parts. You need to pass at least one element or the proc
+  ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_,
+  ## but works with any number of directory parts.
+  ##
+  ## You need to pass at least one element or the proc
   ## will assert in debug builds and crash on release builds.
+  ##
+  ## See also:
+  ## * `joinPath(head, tail) proc <#joinPath,string,string>`_
+  ## * `/ proc <#/,string,string>`_
+  ## * `/../ proc <#/../,string,string>`_
+  ## * `splitPath proc <#splitPath,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("a") == "a"
+      assert joinPath("a", "b", "c") == "a/b/c"
+      assert joinPath("usr/lib", "../../var", "log") == "var/log"
+
   var estimatedLen = 0
   for p in parts: estimatedLen += p.len
   result = newStringOfCap(estimatedLen)
@@ -138,30 +179,41 @@ proc joinPath*(parts: varargs[string]): string {.noSideEffect,
     addNormalizePath(parts[i], result, state, DirSep)
 
 proc `/`*(head, tail: string): string {.noSideEffect.} =
-  ## The same as ``joinPath(head, tail)``
-  ##
-  ## Here are some examples for Unix:
+  ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_.
   ##
-  ## .. code-block:: nim
-  ##   assert "usr" / "" == "usr/"
-  ##   assert "" / "lib" == "lib"
-  ##   assert "" / "/lib" == "/lib"
-  ##   assert "usr/" / "/lib" == "usr/lib"
+  ## See also:
+  ## * `/../ proc <#/../,string,string>`_
+  ## * `joinPath(head, tail) proc <#joinPath,string,string>`_
+  ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_
+  ## * `splitPath proc <#splitPath,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert "usr" / "" == "usr/"
+      assert "" / "lib" == "lib"
+      assert "" / "/lib" == "/lib"
+      assert "usr/" / "/lib" == "usr/lib"
+      assert "usr" / "lib" / "../bin" == "usr/bin"
+
   return joinPath(head, tail)
 
 proc splitPath*(path: string): tuple[head, tail: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a directory into (head, tail), so that
+  ## Splits a directory into `(head, tail)` tuple, so that
   ## ``head / tail == path`` (except for edge cases like "/usr").
   ##
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##   splitPath("usr/local/bin") -> ("usr/local", "bin")
-  ##   splitPath("usr/local/bin/") -> ("usr/local/bin", "")
-  ##   splitPath("bin") -> ("", "bin")
-  ##   splitPath("/bin") -> ("", "bin")
-  ##   splitPath("") -> ("", "")
+  ## See also:
+  ## * `joinPath(head, tail) proc <#joinPath,string,string>`_
+  ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_
+  ## * `/ proc <#/,string,string>`_
+  ## * `/../ proc <#/../,string,string>`_
+  ## * `relativePath proc <#relativePath,string,string>`_
+  runnableExamples:
+    assert splitPath("usr/local/bin") == ("usr/local", "bin")
+    assert splitPath("usr/local/bin/") == ("usr/local/bin", "")
+    assert splitPath("bin") == ("", "bin")
+    assert splitPath("/bin") == ("", "bin")
+    assert splitPath("") == ("", "")
+
   var sepPos = -1
   for i in countdown(len(path)-1, 0):
     if path[i] in {DirSep, AltSep}:
@@ -182,16 +234,21 @@ else:
 proc relativePath*(path, base: string; sep = DirSep): string {.
   noSideEffect, rtl, extern: "nos$1", raises: [].} =
   ## Converts `path` to a path relative to `base`.
-  ## The `sep` is used for the path normalizations, this can be useful to
-  ## ensure the relative path only contains '/' so that it can be used for
-  ## URL constructions.
+  ##
+  ## The `sep` (default: `DirSep <#DirSep>`_) is used for the path normalizations,
+  ## this can be useful to ensure the relative path only contains `'/'`
+  ## so that it can be used for URL constructions.
+  ##
+  ## See also:
+  ## * `splitPath proc <#splitPath,string>`_
+  ## * `parentDir proc <#parentDir,string>`_
+  ## * `tailDir proc <#tailDir,string>`_
   runnableExamples:
-    doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
-    doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
-    doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
-    doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
-    doAssert relativePath("", "/users/moo", '/') == ""
-
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
+    assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
+    assert relativePath("", "/users/moo", '/') == ""
 
   # Todo: If on Windows, path and base do not agree on the drive letter,
   # return `path` as is.
@@ -252,12 +309,21 @@ proc parentDir*(path: string): string {.
   ##
   ## This is the same as ``splitPath(path).head`` when ``path`` doesn't end
   ## in a dir separator.
-  ## The remainder can be obtained with ``lastPathPart(path)``
+  ## The remainder can be obtained with `lastPathPart(path) proc
+  ## <#lastPathPart,string>`_.
+  ##
+  ## See also:
+  ## * `relativePath proc <#relativePath,string,string>`_
+  ## * `splitPath proc <#splitPath,string>`_
+  ## * `tailDir proc <#tailDir,string>`_
+  ## * `parentDirs iterator <#parentDirs.i,string>`_
   runnableExamples:
-    doAssert parentDir("") == ""
+    assert parentDir("") == ""
     when defined(posix):
-      doAssert parentDir("/usr/local/bin") == "/usr/local"
-      doAssert parentDir("foo/bar/") == "foo"
+      assert parentDir("/usr/local/bin") == "/usr/local"
+      assert parentDir("foo/bar/") == "foo"
+      assert parentDir("./foo") == "."
+      assert parentDir("/foo") == ""
 
   let sepPos = parentDirPos(path)
   if sepPos >= 0:
@@ -267,11 +333,18 @@ proc parentDir*(path: string): string {.
 
 proc tailDir*(path: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Returns the tail part of `path`..
+  ## Returns the tail part of `path`.
   ##
-  ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``.
-  ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``.
-  ## | Example: ``tailDir("bin") == ""``.
+  ## See also:
+  ## * `relativePath proc <#relativePath,string,string>`_
+  ## * `splitPath proc <#splitPath,string>`_
+  ## * `parentDir proc <#parentDir,string>`_
+  runnableExamples:
+    assert tailDir("/bin") == "bin"
+    assert tailDir("bin") == ""
+    assert tailDir("/usr/local/bin") == "usr/local/bin"
+    assert tailDir("usr/local/bin") == "local/bin"
+
   var q = 1
   if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
   for i in 0..len(path)-q:
@@ -281,18 +354,53 @@ proc tailDir*(path: string): string {.
 
 proc isRootDir*(path: string): bool {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Checks whether a given `path` is a root directory
+  ## Checks whether a given `path` is a root directory.
+  runnableExamples:
+    assert isRootDir("")
+    assert isRootDir(".")
+    assert isRootDir("/")
+    assert isRootDir("a")
+    assert not isRootDir("/a")
+    assert not isRootDir("a/b/c")
+
   result = parentDirPos(path) < 0
 
 iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
-  ## Walks over all parent directories of a given `path`
+  ## Walks over all parent directories of a given `path`.
   ##
-  ## If `fromRoot` is set, the traversal will start from the file system root
-  ## diretory. If `inclusive` is set, the original argument will be included
+  ## If `fromRoot` is true (default: false), the traversal will start from
+  ## the file system root diretory.
+  ## If `inclusive` is true (default), the original argument will be included
   ## in the traversal.
   ##
-  ## Relative paths won't be expanded by this proc. Instead, it will traverse
+  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
   ## only the directories appearing in the relative path.
+  ##
+  ## See also:
+  ## * `parentDir proc <#parentDir,string>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let g = "a/b/c"
+  ##
+  ##   for p in g.parentDirs:
+  ##     echo p
+  ##   # a/b/c
+  ##   # a/b
+  ##   # a
+  ##
+  ##   for p in g.parentDirs(fromRoot=true):
+  ##     echo p
+  ##   # a/
+  ##   # a/b/
+  ##   # a/b/c
+  ##
+  ##   for p in g.parentDirs(inclusive=false):
+  ##     echo p
+  ##   # a/b
+  ##   # a
+
   if not fromRoot:
     var current = path
     if inclusive: yield path
@@ -310,8 +418,17 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
     if inclusive: yield path
 
 proc `/../`*(head, tail: string): string {.noSideEffect.} =
-  ## The same as ``parentDir(head) / tail`` unless there is no parent
+  ## The same as ``parentDir(head) / tail``, unless there is no parent
   ## directory. Then ``head / tail`` is performed instead.
+  ##
+  ## See also:
+  ## * `/ proc <#/,string,string>`_
+  ## * `parentDir proc <#parentDir,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert "a/b/c" /../ "d/e" == "a/b/d/e"
+      assert "a" /../ "d/e" == "a/d/e"
+
   let sepPos = parentDirPos(head)
   if sepPos >= 0:
     result = substr(head, 0, sepPos-1) / tail
@@ -323,8 +440,21 @@ proc normExt(ext: string): string =
   else: result = ExtSep & ext
 
 proc searchExtPos*(path: string): int =
-  ## Returns index of the '.' char in `path` if it signifies the beginning
+  ## Returns index of the `'.'` char in `path` if it signifies the beginning
   ## of extension. Returns -1 otherwise.
+  ##
+  ## See also:
+  ## * `splitFile proc <#splitFile,string>`_
+  ## * `extractFilename proc <#extractFilename,string>`_
+  ## * `lastPathPart proc <#lastPathPart,string>`_
+  ## * `changeFileExt proc <#changeFileExt,string,string>`_
+  ## * `addFileExt proc <#addFileExt,string,string>`_
+  runnableExamples:
+    assert searchExtPos("a/b/c") == -1
+    assert searchExtPos("c.nim") == 1
+    assert searchExtPos("a/b/c.nim") == 5
+    assert searchExtPos("a.b.c.nim") == 5
+
   # BUGFIX: do not search until 0! .DS_Store is no file extension!
   result = -1
   for i in countdown(len(path)-1, 1):
@@ -336,21 +466,35 @@ proc searchExtPos*(path: string): int =
 
 proc splitFile*(path: string): tuple[dir, name, ext: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a filename into (dir, filename, extension).
-  ## `dir` does not end in `DirSep`.
-  ## `extension` includes the leading dot.
+  ## Splits a filename into `(dir, name, extension)` tuple.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var (dir, name, ext) = splitFile("usr/local/nimc.html")
-  ##   assert dir == "usr/local"
-  ##   assert name == "nimc"
-  ##   assert ext == ".html"
+  ## `dir` does not end in `DirSep <#DirSep>`_.
+  ## `extension` includes the leading dot.
   ##
   ## If `path` has no extension, `ext` is the empty string.
   ## If `path` has no directory component, `dir` is the empty string.
   ## If `path` has no filename component, `name` and `ext` are empty strings.
+  ##
+  ## See also:
+  ## * `searchExtPos proc <#searchExtPos,string>`_
+  ## * `extractFilename proc <#extractFilename,string>`_
+  ## * `lastPathPart proc <#lastPathPart,string>`_
+  ## * `changeFileExt proc <#changeFileExt,string,string>`_
+  ## * `addFileExt proc <#addFileExt,string,string>`_
+  runnableExamples:
+    var (dir, name, ext) = splitFile("usr/local/nimc.html")
+    assert dir == "usr/local"
+    assert name == "nimc"
+    assert ext == ".html"
+    (dir, name, ext) = splitFile("/usr/local/os")
+    assert dir == "/usr/local"
+    assert name == "os"
+    assert ext == ""
+    (dir, name, ext) = splitFile("/usr/local/")
+    assert dir == "/usr/local"
+    assert name == ""
+    assert ext == ""
+
   if path.len == 0:
       result = ("", "", "")
   elif path[^1] in {DirSep, AltSep}:
@@ -380,12 +524,22 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
 
 proc extractFilename*(path: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Extracts the filename of a given `path`. This is the same as
-  ## ``name & ext`` from ``splitFile(path)``. See also ``lastPathPart``.
+  ## Extracts the filename of a given `path`.
+  ##
+  ## This is the same as ``name & ext`` from `splitFile(path) proc
+  ## <#splitFile,string>`_.
+  ##
+  ## See also:
+  ## * `searchExtPos proc <#searchExtPos,string>`_
+  ## * `splitFile proc <#splitFile,string>`_
+  ## * `lastPathPart proc <#lastPathPart,string>`_
+  ## * `changeFileExt proc <#changeFileExt,string,string>`_
+  ## * `addFileExt proc <#addFileExt,string,string>`_
   runnableExamples:
-    when defined(posix):
-      doAssert extractFilename("foo/bar/") == ""
-      doAssert extractFilename("foo/bar") == "bar"
+    assert extractFilename("foo/bar/") == ""
+    assert extractFilename("foo/bar") == "bar"
+    assert extractFilename("foo/bar.baz") == "bar.baz"
+
   if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
     result = ""
   else:
@@ -393,11 +547,19 @@ proc extractFilename*(path: string): string {.
 
 proc lastPathPart*(path: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## like ``extractFilename``, but ignores trailing dir separator; aka: `baseName`:idx:
-  ## in some other languages.
+  ## Like `extractFilename proc <#extractFilename,string>`_, but ignores
+  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
+  ##
+  ## See also:
+  ## * `searchExtPos proc <#searchExtPos,string>`_
+  ## * `splitFile proc <#splitFile,string>`_
+  ## * `extractFilename proc <#extractFilename,string>`_
+  ## * `changeFileExt proc <#changeFileExt,string,string>`_
+  ## * `addFileExt proc <#addFileExt,string,string>`_
   runnableExamples:
-    when defined(posix):
-      doAssert lastPathPart("foo/bar/") == "bar"
+    assert lastPathPart("foo/bar/") == "bar"
+    assert lastPathPart("foo/bar") == "bar"
+
   let path = path.normalizePathEnd(trailingSep = false)
   result = extractFilename(path)
 
@@ -407,9 +569,22 @@ proc changeFileExt*(filename, ext: string): string {.
   ##
   ## If the `filename` has no extension, `ext` will be added.
   ## If `ext` == "" then any extension is removed.
-  ## `Ext` should be given without the leading '.', because some
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
   ## filesystems may use a different character. (Although I know
   ## of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc <#searchExtPos,string>`_
+  ## * `splitFile proc <#splitFile,string>`_
+  ## * `extractFilename proc <#extractFilename,string>`_
+  ## * `lastPathPart proc <#lastPathPart,string>`_
+  ## * `addFileExt proc <#addFileExt,string,string>`_
+  runnableExamples:
+    assert changeFileExt("foo.bar", "baz") == "foo.baz"
+    assert changeFileExt("foo.bar", "") == "foo"
+    assert changeFileExt("foo", "baz") == "foo.baz"
+
   var extPos = searchExtPos(filename)
   if extPos < 0: result = filename & normExt(ext)
   else: result = substr(filename, 0, extPos-1) & normExt(ext)
@@ -419,9 +594,21 @@ proc addFileExt*(filename, ext: string): string {.
   ## Adds the file extension `ext` to `filename`, unless
   ## `filename` already has an extension.
   ##
-  ## `Ext` should be given without the leading '.', because some
+  ## `Ext` should be given without the leading `'.'`, because some
   ## filesystems may use a different character.
   ## (Although I know of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc <#searchExtPos,string>`_
+  ## * `splitFile proc <#splitFile,string>`_
+  ## * `extractFilename proc <#extractFilename,string>`_
+  ## * `lastPathPart proc <#lastPathPart,string>`_
+  ## * `changeFileExt proc <#changeFileExt,string,string>`_
+  runnableExamples:
+    assert addFileExt("foo.bar", "baz") == "foo.bar"
+    assert addFileExt("foo.bar", "") == "foo.bar"
+    assert addFileExt("foo", "baz") == "foo.baz"
+
   var extPos = searchExtPos(filename)
   if extPos < 0: result = filename & normExt(ext)
   else: result = filename
@@ -438,9 +625,9 @@ proc cmpPaths*(pathA, pathB: string): int {.
   ## | > 0 iff pathA > pathB
   runnableExamples:
     when defined(macosx):
-      doAssert cmpPaths("foo", "Foo") == 0
+      assert cmpPaths("foo", "Foo") == 0
     elif defined(posix):
-      doAssert cmpPaths("foo", "Foo") > 0
+      assert cmpPaths("foo", "Foo") > 0
 
   let a = normalizePath(pathA)
   let b = normalizePath(pathB)
@@ -458,11 +645,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
   ##
   ## On Windows, network paths are considered absolute too.
   runnableExamples:
-    doAssert(not "".isAbsolute)
-    doAssert(not ".".isAbsolute)
+    assert not "".isAbsolute
+    assert not ".".isAbsolute
     when defined(posix):
-      doAssert "/".isAbsolute
-      doAssert(not "a/".isAbsolute)
+      assert "/".isAbsolute
+      assert not "a/".isAbsolute
+      assert "/a/".isAbsolute
 
   if len(path) == 0: return false
 
@@ -483,7 +671,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
   ## Converts an UNIX-like path to a native one.
   ##
   ## On an UNIX system this does nothing. Else it converts
-  ## '/', '.', '..' to the appropriate things.
+  ## `'/'`, `'.'`, `'..'` to the appropriate things.
   ##
   ## On systems with a concept of "drives", `drive` is used to determine
   ## which drive label to use during absolute path conversion.
@@ -542,8 +730,18 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1",
   tags: [ReadEnvEffect, ReadIOEffect].} =
   ## Returns the home directory of the current user.
   ##
-  ## This proc is wrapped by the expandTilde proc for the convenience of
-  ## processing paths coming from user configuration files.
+  ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_
+  ## for the convenience of processing paths coming from user configuration files.
+  ##
+  ## See also:
+  ## * `getConfigDir proc <#getConfigDir>`_
+  ## * `getTempDir proc <#getTempDir>`_
+  ## * `expandTilde proc <#expandTilde,string>`_
+  ## * `getCurrentDir proc <#getCurrentDir>`_
+  ## * `setCurrentDir proc <#setCurrentDir,string>`_
+  runnableExamples:
+    assert getHomeDir() == expandTilde("~")
+
   when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
   else: return string(getEnv("HOME")) & "/"
 
@@ -552,12 +750,19 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1",
   ## Returns the config directory of the current user for applications.
   ##
   ## On non-Windows OSs, this proc conforms to the XDG Base Directory
-  ## spec. Thus, this proc returns the value of the XDG_CONFIG_HOME environment
-  ## variable if it is set, and returns the default configuration directory,
-  ## "~/.config/", otherwise.
+  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.config/").
   ##
   ## An OS-dependent trailing slash is always present at the end of the
-  ## returned string; `\` on Windows and `/` on all other OSs.
+  ## returned string: `\\` on Windows and `/` on all other OSs.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <#getHomeDir>`_
+  ## * `getTempDir proc <#getTempDir>`_
+  ## * `expandTilde proc <#expandTilde,string>`_
+  ## * `getCurrentDir proc <#getCurrentDir>`_
+  ## * `setCurrentDir proc <#setCurrentDir,string>`_
   when defined(windows):
     result = getEnv("APPDATA").string
   else:
@@ -573,6 +778,13 @@ proc getTempDir*(): string {.rtl, extern: "nos$1",
   ## returns ``getHomeDir()``, and on other Unix based systems it can cause
   ## security problems too. That said, you can override this implementation
   ## by adding ``-d:tempDir=mytempname`` to your compiler invokation.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <#getHomeDir>`_
+  ## * `getConfigDir proc <#getConfigDir>`_
+  ## * `expandTilde proc <#expandTilde,string>`_
+  ## * `getCurrentDir proc <#getCurrentDir>`_
+  ## * `setCurrentDir proc <#setCurrentDir,string>`_
   when defined(tempDir):
     const tempDir {.strdefine.}: string = nil
     return tempDir
@@ -583,12 +795,22 @@ proc getTempDir*(): string {.rtl, extern: "nos$1",
 proc expandTilde*(path: string): string {.
   tags: [ReadEnvEffect, ReadIOEffect].} =
   ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
-  ## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified).
+  ## ``~`` with `getHomeDir() <#getHomeDir>`_ (otherwise returns ``path`` unmodified).
   ##
   ## Windows: this is still supported despite Windows platform not having this
   ## convention; also, both ``~/`` and ``~\`` are handled.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <#getHomeDir>`_
+  ## * `getConfigDir proc <#getConfigDir>`_
+  ## * `getTempDir proc <#getTempDir>`_
+  ## * `getCurrentDir proc <#getCurrentDir>`_
+  ## * `setCurrentDir proc <#setCurrentDir,string>`_
   runnableExamples:
-    doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
+    assert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
+    assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar"
+    assert expandTilde("/foo/bar") == "/foo/bar"
+
   if len(path) == 0 or path[0] != '~':
     result = path
   elif len(path) == 1:
@@ -602,11 +824,13 @@ proc expandTilde*(path: string): string {.
 # TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand
 # belong in `strutils` instead; they are not specific to paths
 proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote s, so it can be safely passed to Windows API.
-  ## Based on Python's subprocess.list2cmdline
-  ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
-  let needQuote = {' ', '\t'} in s or s.len == 0
+  ## Quote `s`, so it can be safely passed to Windows API.
+  ##
+  ## Based on Python's `subprocess.list2cmdline`.
+  ## See `this link <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_
+  ## for more details.
 
+  let needQuote = {' ', '\t'} in s or s.len == 0
   result = ""
   var backslashBuff = ""
   if needQuote:
@@ -631,7 +855,7 @@ proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1"
 
 proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
   ## Quote ``s``, so it can be safely passed to POSIX shell.
-  ## Based on Python's pipes.quote
+  ## Based on Python's `pipes.quote`.
   const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
                          '0'..'9', 'A'..'Z', 'a'..'z'}
   if s.len == 0:
@@ -647,18 +871,23 @@ proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".}
 when defined(windows) or defined(posix) or defined(nintendoswitch):
   proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
     ## Quote ``s``, so it can be safely passed to shell.
+    ##
+    ## When on Windows, it calls `quoteShellWindows proc
+    ## <#quoteShellWindows,string>`_. Otherwise, calls `quoteShellPosix proc
+    ## <#quoteShellPosix,string>`_.
     when defined(windows):
       return quoteShellWindows(s)
     else:
       return quoteShellPosix(s)
 
   proc quoteShellCommand*(args: openArray[string]): string =
-    ## Concatenates and quotes shell arguments `args`
+    ## Concatenates and quotes shell arguments `args`.
     runnableExamples:
       when defined(posix):
         assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'"
       when defined(windows):
         assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
+
     # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
     for i in 0..<args.len:
       if i > 0: result.add " "
@@ -705,8 +934,12 @@ when defined(windows) and not weirdTarget:
 proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
                                           tags: [ReadDirEffect], noNimScript.} =
   ## Returns true if `filename` exists and is a regular file or symlink.
-  ## (directories, device files, named pipes and sockets return false)
-  ## This proc is not available for NimScript.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  ##
+  ## See also:
+  ## * `existsDir proc <#existsDir,string>`_
+  ## * `symlinkExists proc <#symlinkExists,string>`_
   when defined(windows):
     when useWinUnicode:
       wrapUnary(a, getFileAttributesW, filename)
@@ -722,6 +955,10 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect]
                                      noNimScript.} =
   ## Returns true iff the directory `dir` exists. If `dir` is a file, false
   ## is returned. Follows symlinks.
+  ##
+  ## See also:
+  ## * `existsFile proc <#existsFile,string>`_
+  ## * `symlinkExists proc <#symlinkExists,string>`_
   when defined(windows):
     when useWinUnicode:
       wrapUnary(a, getFileAttributesW, dir)
@@ -738,6 +975,10 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
                                           noNimScript.} =
   ## Returns true iff the symlink `link` exists. Will return true
   ## regardless of whether the link points to a directory or file.
+  ##
+  ## See also:
+  ## * `existsFile proc <#existsFile,string>`_
+  ## * `existsDir proc <#existsDir,string>`_
   when defined(windows):
     when useWinUnicode:
       wrapUnary(a, getFileAttributesW, link)
@@ -750,11 +991,19 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
     return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
 
 proc fileExists*(filename: string): bool {.inline, noNimScript.} =
-  ## Synonym for existsFile
+  ## Alias for `existsFile proc <#existsFile,string>`_.
+  ##
+  ## See also:
+  ## * `existsDir proc <#existsDir,string>`_
+  ## * `symlinkExists proc <#symlinkExists,string>`_
   existsFile(filename)
 
 proc dirExists*(dir: string): bool {.inline, noNimScript.} =
-  ## Synonym for existsDir
+  ## Alias for `existsDir proc <#existsDir,string>`_.
+  ##
+  ## See also:
+  ## * `existsFile proc <#existsFile,string>`_
+  ## * `symlinkExists proc <#symlinkExists,string>`_
   existsDir(dir)
 
 when not defined(windows) and not weirdTarget:
@@ -764,19 +1013,23 @@ when not defined(windows) and not weirdTarget:
     else: result = S_ISLNK(rawInfo.st_mode)
 
 const
-  ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
-    ## platform specific file extension for executables. On Windows
-    ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
+  ExeExts* = ## Platform specific file extension for executables.
+    ## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
+    when defined(windows): ["exe", "cmd", "bat"] else: [""]
 
 proc findExe*(exe: string, followSymlinks: bool = true;
               extensions: openarray[string]=ExeExts): string {.
   tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect], noNimScript.} =
   ## Searches for `exe` in the current working directory and then
   ## in directories listed in the ``PATH`` environment variable.
-  ## Returns "" if the `exe` cannot be found. `exe`
+  ##
+  ## Returns `""` if the `exe` cannot be found. `exe`
   ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
+  ##
   ## If the system supports symlinks it also resolves them until it
-  ## meets the actual file. This behavior can be disabled if desired.
+  ## meets the actual file. This behavior can be disabled if desired
+  ## by setting `followSymlinks = false`.
+
   if exe.len == 0: return
   template checkCurrentDir() =
     for ext in extensions:
@@ -824,6 +1077,11 @@ when weirdTarget:
 
 proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} =
   ## Returns the `file`'s last modification time.
+  ##
+  ## See also:
+  ## * `getLastAccessTime proc <#getLastAccessTime,string>`_
+  ## * `getCreationTime proc <#getCreationTime,string>`_
+  ## * `fileNewer proc <#fileNewer,string,string>`_
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
@@ -837,6 +1095,11 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1",
 
 proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} =
   ## Returns the `file`'s last read or write access time.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc <#getLastModificationTime,string>`_
+  ## * `getCreationTime proc <#getCreationTime,string>`_
+  ## * `fileNewer proc <#fileNewer,string,string>`_
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
@@ -854,6 +1117,11 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScr
   ## **Note:** Under POSIX OS's, the returned time may actually be the time at
   ## which the file's attribute's were last modified. See
   ## `here <https://github.com/nim-lang/Nim/issues/1058>`_ for details.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc <#getLastModificationTime,string>`_
+  ## * `getLastAccessTime proc <#getLastAccessTime,string>`_
+  ## * `fileNewer proc <#fileNewer,string,string>`_
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
@@ -868,6 +1136,11 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScr
 proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} =
   ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
   ## modification time is later than `b`'s.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc <#getLastModificationTime,string>`_
+  ## * `getLastAccessTime proc <#getLastAccessTime,string>`_
+  ## * `getCreationTime proc <#getCreationTime,string>`_
   when defined(posix):
     # If we don't have access to nanosecond resolution, use '>='
     when not StatHasNanoseconds:
@@ -879,6 +1152,12 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} =
 
 proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
   ## Returns the `current working directory`:idx:.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <#getHomeDir>`_
+  ## * `getConfigDir proc <#getConfigDir>`_
+  ## * `getTempDir proc <#getTempDir>`_
+  ## * `setCurrentDir proc <#setCurrentDir,string>`_
   when defined(windows):
     var bufsize = MAX_PATH.int32
     when useWinUnicode:
@@ -922,8 +1201,14 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
           raiseOSError(osLastError())
 
 proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} =
-  ## Sets the `current working directory`:idx:; `OSError` is raised if
-  ## `newDir` cannot been set.
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <#getHomeDir>`_
+  ## * `getConfigDir proc <#getConfigDir>`_
+  ## * `getTempDir proc <#getTempDir>`_
+  ## * `getCurrentDir proc <#getCurrentDir>`_
   when defined(Windows):
     when useWinUnicode:
       if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
@@ -935,68 +1220,43 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} =
 
 when not weirdTarget:
   proc absolutePath*(path: string, root = getCurrentDir()): string {.noNimScript.} =
-    ## Returns the absolute path of `path`, rooted at `root` (which must be absolute)
-    ## if `path` is absolute, return it, ignoring `root`
+    ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
+    ## default: current directory).
+    ## If `path` is absolute, return it, ignoring `root`.
+    ##
+    ## See also:
+    ## * `normalizedPath proc <#normalizedPath,string>`_
+    ## * `normalizePath proc <#normalizePath,string>`_
     runnableExamples:
-      doAssert absolutePath("a") == getCurrentDir() / "a"
+      assert absolutePath("a") == getCurrentDir() / "a"
+
     if isAbsolute(path): path
     else:
       if not root.isAbsolute:
         raise newException(ValueError, "The specified root is not absolute: " & root)
       joinPath(root, path)
 
-proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect], noNimScript.} =
-  ## Returns the full (`absolute`:idx:) path of an existing file `filename`,
-  ## raises OSError in case of an error. Follows symlinks.
-  when defined(windows):
-    var bufsize = MAX_PATH.int32
-    when useWinUnicode:
-      var unused: WideCString = nil
-      var res = newWideCString("", bufsize)
-      while true:
-        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          res = newWideCString("", L)
-          bufsize = L
-        else:
-          result = res$L
-          break
-    else:
-      var unused: cstring = nil
-      result = newString(bufsize)
-      while true:
-        var L = getFullPathNameA(filename, bufsize, result, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
-  else:
-    # according to Posix we don't need to allocate space for result pathname.
-    # But we need to free return value with free(3).
-    var r = realpath(filename, nil)
-    if r.isNil:
-      raiseOSError(osLastError())
-    else:
-      result = $r
-      c_free(cast[pointer](r))
-
 proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScript.} =
   ## Normalize a path.
   ##
   ## Consecutive directory separators are collapsed, including an initial double slash.
   ##
-  ## On relative paths, double dot (..) sequences are collapsed if possible.
+  ## On relative paths, double dot (`..`) sequences are collapsed if possible.
   ## On absolute paths they are always collapsed.
   ##
   ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected.
   ## Triple dot is not handled.
+  ##
+  ## See also:
+  ## * `absolutePath proc <#absolutePath,string>`_
+  ## * `normalizedPath proc <#normalizedPath,string>`_ for a version which returns
+  ##   a new string
+  runnableExamples:
+    when defined(posix):
+      var a = "a///b//..//c///d"
+      a.normalizePath()
+      assert a == "a/c/d"
+
   path = pathnorm.normalizePath(path)
   when false:
     let isAbs = isAbsolute(path)
@@ -1026,7 +1286,14 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScr
       path = "."
 
 proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
-  ## Returns a normalized path for the current OS. See `<#normalizePath>`_
+  ## Returns a normalized path for the current OS.
+  ##
+  ## See also:
+  ## * `absolutePath proc <#absolutePath,string>`_
+  ## * `normalizePath proc <#normalizePath,string>`_ for the in-place version
+  runnableExamples:
+    when defined(posix):
+      assert normalizedPath("a///b//..//c///d") == "a/c/d"
   result = pathnorm.normalizePath(path)
 
 when defined(Windows) and not weirdTarget:
@@ -1052,11 +1319,16 @@ when defined(Windows) and not weirdTarget:
 proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
   tags: [ReadDirEffect], noNimScript.} =
   ## Returns true if both pathname arguments refer to the same physical
-  ## file or directory. Raises an exception if any of the files does not
+  ## file or directory.
+  ##
+  ## Raises `OSError` if any of the files does not
   ## exist or information about it can not be obtained.
   ##
   ## This proc will return true if given two alternative hard-linked or
   ## sym-linked paths to the same file or directory.
+  ##
+  ## See also:
+  ## * `sameFileContent proc <#sameFileContent,string,string>`_
   when defined(Windows):
     var success = true
     var f1 = openHandle(path1)
@@ -1093,6 +1365,9 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
   tags: [ReadIOEffect], noNimScript.} =
   ## Returns true if both pathname arguments refer to files with identical
   ## binary content.
+  ##
+  ## See also:
+  ## * `sameFile proc <#sameFile,string,string>`_
   const
     bufSize = 8192 # 8K buffer
   var
@@ -1121,7 +1396,12 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
   close(b)
 
 type
-  FilePermission* = enum   ## file access permission; modelled after UNIX
+  FilePermission* = enum   ## File access permission, modelled after UNIX.
+    ##
+    ## See also:
+    ## * `getFilePermissions <#getFilePermissions,string>`_
+    ## * `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_
+    ## * `FileInfo object <#FileInfo>`_
     fpUserExec,            ## execute access for the file owner
     fpUserWrite,           ## write access for the file owner
     fpUserRead,            ## read access for the file owner
@@ -1134,9 +1414,15 @@ type
 
 proc getFilePermissions*(filename: string): set[FilePermission] {.
   rtl, extern: "nos$1", tags: [ReadDirEffect], noNimScript.} =
-  ## retrieves file permissions for `filename`. `OSError` is raised in case of
-  ## an error. On Windows, only the ``readonly`` flag is checked, every other
+  ## Retrieves file permissions for `filename`.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is checked, every other
   ## permission is available in any case.
+  ##
+  ## See also:
+  ## * `setFilePermissions proc <#setFilePermissions,string,set[FilePermission]>`_
+  ## * `FilePermission enum <#FilePermission>`_
   when defined(posix):
     var a: Stat
     if stat(filename, a) < 0'i32: raiseOSError(osLastError())
@@ -1166,9 +1452,15 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
 
 proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
   rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} =
-  ## sets the file permissions for `filename`. `OSError` is raised in case of
-  ## an error. On Windows, only the ``readonly`` flag is changed, depending on
-  ## ``fpUserWrite``.
+  ## Sets the file permissions for `filename`.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is changed, depending on
+  ## ``fpUserWrite`` permission.
+  ##
+  ## See also:
+  ## * `getFilePermissions <#getFilePermissions,string>`_
+  ## * `FilePermission enum <#FilePermission>`_
   when defined(posix):
     var p = 0'i32
     if fpUserRead in permissions: p = p or S_IRUSR
@@ -1204,14 +1496,29 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
   tags: [ReadIOEffect, WriteIOEffect], noNimScript.} =
   ## Copies a file from `source` to `dest`.
   ##
-  ## If this fails, `OSError` is raised. On the Windows platform this proc will
-  ## copy the source file's attributes into dest. On other platforms you need
-  ## to use `getFilePermissions() <#getFilePermissions>`_ and
-  ## `setFilePermissions() <#setFilePermissions>`_ to copy them by hand (or use
-  ## the convenience `copyFileWithPermissions() <#copyFileWithPermissions>`_
-  ## proc), otherwise `dest` will inherit the default permissions of a newly
-  ## created file for the user. If `dest` already exists, the file attributes
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will
+  ## copy the source file's attributes into dest.
+  ##
+  ## On other platforms you need
+  ## to use `getFilePermissions <#getFilePermissions,string>`_ and
+  ## `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_ procs
+  ## to copy them by hand (or use the convenience `copyFileWithPermissions
+  ## proc <#copyFileWithPermissions,string,string>`_),
+  ## otherwise `dest` will inherit the default permissions of a newly
+  ## created file for the user.
+  ##
+  ## If `dest` already exists, the file attributes
   ## will be preserved and the content overwritten.
+  ##
+  ## See also:
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `tryRemoveFile proc <#tryRemoveFile,string>`_
+  ## * `removeFile proc <#removeFile,string>`_
+  ## * `moveFile proc <#moveFile,string,string>`_
+
   when defined(Windows):
     when useWinUnicode:
       let s = newWideCString(source)
@@ -1263,9 +1570,18 @@ when defined(Windows) and not weirdTarget:
       setFileAttributesA(file, attrs)
 
 proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} =
-  ## Removes the `file`. If this fails, returns `false`. This does not fail
+  ## Removes the `file`.
+  ##
+  ## If this fails, returns `false`. This does not fail
   ## if the file never existed in the first place.
+  ##
   ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `removeFile proc <#removeFile,string>`_
+  ## * `moveFile proc <#moveFile,string,string>`_
   result = true
   when defined(Windows):
     when useWinUnicode:
@@ -1286,9 +1602,19 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE
       result = false
 
 proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} =
-  ## Removes the `file`. If this fails, `OSError` is raised. This does not fail
+  ## Removes the `file`.
+  ##
+  ## If this fails, `OSError` is raised. This does not fail
   ## if the file never existed in the first place.
+  ##
   ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `tryRemoveFile proc <#tryRemoveFile,string>`_
+  ## * `moveFile proc <#moveFile,string,string>`_
   if not tryRemoveFile(file):
     when defined(Windows):
       raiseOSError(osLastError())
@@ -1296,9 +1622,11 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n
       raiseOSError(osLastError(), $strerror(errno))
 
 proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} =
-  ## Moves a file or directory from `source` to `dest`. Returns false in case
-  ## of `EXDEV` error. In case of other errors `OSError` is raised. Returns
-  ## true in case of success.
+  ## Moves a file or directory from `source` to `dest`.
+  ##
+  ## Returns false in case of `EXDEV` error.
+  ## In case of other errors `OSError` is raised.
+  ## Returns true in case of success.
   when defined(Windows):
     when useWinUnicode:
       let s = newWideCString(source)
@@ -1317,8 +1645,19 @@ proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} =
 
 proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
   tags: [ReadIOEffect, WriteIOEffect], noNimScript.} =
-  ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
-  ## Can be used to `rename files`:idx:
+  ## Moves a file from `source` to `dest`.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## Can be used to `rename files`:idx:.
+  ##
+  ## See also:
+  ## * `moveDir proc <#moveDir,string,string>`_
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `removeFile proc <#removeFile,string>`_
+  ## * `tryRemoveFile proc <#tryRemoveFile,string>`_
+
   if not tryMoveFSObject(source, dest):
     when not defined(windows):
       # Fallback to copy & del
@@ -1329,20 +1668,34 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
         discard tryRemoveFile(dest)
         raise
 
+proc exitStatusLikeShell*(status: cint): cint =
+  ## Converts exit code from `c_system` into a shell exit code.
+  when defined(posix) and not weirdTarget:
+    if WIFSIGNALED(status):
+      # like the shell!
+      128 + WTERMSIG(status)
+    else:
+      WEXITSTATUS(status)
+  else:
+    status
+
 proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   tags: [ExecIOEffect], noNimScript.} =
   ## Executes a `shell command`:idx:.
   ##
   ## Command has the form 'program args' where args are the command
   ## line arguments given to program. The proc returns the error code
-  ## of the shell when it has finished. The proc does not return until
-  ## the process has finished. To execute a program without having a
-  ## shell involved, use the `execProcess` proc of the `osproc`
-  ## module.
-  when defined(posix):
-    result = c_system(command) shr 8
-  else:
-    result = c_system(command)
+  ## of the shell when it has finished (zero if there is no error).
+  ## The proc does not return until the process has finished.
+  ##
+  ## To execute a program without having a shell involved, use `osproc.execProcess proc
+  ## <osproc.html#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   discard execShellCmd("ls -la")
+  result = exitStatusLikeShell(c_system(command))
 
 # Templates for filtering directories and files
 when defined(windows) and not weirdTarget:
@@ -1403,37 +1756,110 @@ template walkCommon(pattern: string, filter) =
 
 iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
   ## Iterate over all the files and directories that match the `pattern`.
-  ## On POSIX this uses the `glob`:idx: call.
   ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"\*.ext"`
   ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkFiles iterator <#walkFiles.i,string>`_
+  ## * `walkDirs iterator <#walkDirs.i,string>`_
+  ## * `walkDir iterator <#walkDir.i,string>`_
+  ## * `walkDirRec iterator <#walkDirRec.i,string>`_
   walkCommon(pattern, defaultWalkFilter)
 
 iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
-  ## Iterate over all the files that match the `pattern`. On POSIX this uses
-  ## the `glob`:idx: call.
+  ## Iterate over all the files that match the `pattern`.
   ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"\*.ext"`
   ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator <#walkPattern.i,string>`_
+  ## * `walkDirs iterator <#walkDirs.i,string>`_
+  ## * `walkDir iterator <#walkDir.i,string>`_
+  ## * `walkDirRec iterator <#walkDirRec.i,string>`_
   walkCommon(pattern, isFile)
 
 iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
   ## Iterate over all the directories that match the `pattern`.
-  ## On POSIX this uses the `glob`:idx: call.
   ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"\*.ext"`
   ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator <#walkPattern.i,string>`_
+  ## * `walkFiles iterator <#walkFiles.i,string>`_
+  ## * `walkDir iterator <#walkDir.i,string>`_
+  ## * `walkDirRec iterator <#walkDirRec.i,string>`_
   walkCommon(pattern, isDir)
 
+proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noNimScript.} =
+  ## Returns the full (`absolute`:idx:) path of an existing file `filename`.
+  ##
+  ## Raises `OSError` in case of an error. Follows symlinks.
+  when defined(windows):
+    var bufsize = MAX_PATH.int32
+    when useWinUnicode:
+      var unused: WideCString = nil
+      var res = newWideCString("", bufsize)
+      while true:
+        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString("", L)
+          bufsize = L
+        else:
+          result = res$L
+          break
+    else:
+      var unused: cstring = nil
+      result = newString(bufsize)
+      while true:
+        var L = getFullPathNameA(filename, bufsize, result, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          result = newString(L)
+          bufsize = L
+        else:
+          setLen(result, L)
+          break
+    # getFullPathName doesn't do case corrections, so we have to use this convoluted
+    # way of retrieving the true filename
+    for x in walkFiles(result.string):
+      result = x
+    if not existsFile(result) and not existsDir(result):
+      raise newException(OSError, "file '" & result & "' does not exist")
+  else:
+    # according to Posix we don't need to allocate space for result pathname.
+    # But we need to free return value with free(3).
+    var r = realpath(filename, nil)
+    if r.isNil:
+      raiseOSError(osLastError())
+    else:
+      result = $r
+      c_free(cast[pointer](r))
+
 type
   PathComponent* = enum   ## Enumeration specifying a path component.
+    ##
+    ## See also:
+    ## * `walkDirRec iterator <#walkDirRec.i,string>`_
+    ## * `FileInfo object <#FileInfo>`_
     pcFile,               ## path refers to a file
     pcLinkToFile,         ## path refers to a symbolic link to a file
     pcDir,                ## path refers to a directory
     pcLinkToDir           ## path refers to a symbolic link to a directory
 
 proc getCurrentCompilerExe*(): string {.compileTime.} = discard
-  ## `getAppFilename` at CT; can be used to retrive the currently executing
+  ## This is `getAppFilename() <#getAppFilename>`_ at compile time.
+  ##
+  ## Can be used to retrive the currently executing
   ## Nim compiler from a Nim or nimscript program, or the nimble binary
   ## inside a nimble program (likewise with other binaries built from
   ## compiler API).
@@ -1454,10 +1880,11 @@ proc staticWalkDir(dir: string; relative: bool): seq[
 
 iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
   tags: [ReadDirEffect].} =
-  ## walks over the directory `dir` and yields for each directory or file in
-  ## `dir`. The component type and full path for each item is returned.
-  ## Walking is not recursive. If ``relative`` is true the resulting path is
-  ## shortened to be relative to ``dir``.
+  ## Walks over the directory `dir` and yields for each directory or file in
+  ## `dir`. The component type and full path for each item are returned.
+  ##
+  ## Walking is not recursive. If ``relative`` is true (default: false)
+  ## the resulting path is shortened to be relative to ``dir``.
   ## Example: This directory structure::
   ##   dirA / dirB / fileB1.txt
   ##        / dirC
@@ -1470,11 +1897,18 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
   ##     for kind, path in walkDir("dirA"):
   ##       echo(path)
   ##
-  ## produces this output (but not necessarily in this order!)::
+  ## produce this output (but not necessarily in this order!)::
   ##   dirA/dirB
   ##   dirA/dirC
   ##   dirA/fileA1.txt
   ##   dirA/fileA2.txt
+  ##
+  ## See also:
+  ## * `walkPattern iterator <#walkPattern.i,string>`_
+  ## * `walkFiles iterator <#walkFiles.i,string>`_
+  ## * `walkDirs iterator <#walkDirs.i,string>`_
+  ## * `walkDirRec iterator <#walkDirRec.i,string>`_
+
   when nimvm:
     for k, v in items(staticWalkDir(dir, relative)):
       yield (k, v)
@@ -1541,19 +1975,20 @@ iterator walkDirRec*(dir: string,
                      relative = false): string {.tags: [ReadDirEffect].} =
   ## Recursively walks over the directory `dir` and yields for each file
   ## or directory in `dir`.
-  ## If ``relative`` is true the resulting path is
+  ##
+  ## If ``relative`` is true (default: false) the resulting path is
   ## shortened to be relative to ``dir``, otherwise the full path is returned.
   ##
   ## **Warning**:
   ## Modifying the directory structure while the iterator
   ## is traversing may result in undefined behavior!
   ##
-  ## Walking is recursive. `filters` controls the behaviour of the iterator:
+  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
   ##
   ## ---------------------   ---------------------------------------------
   ## yieldFilter             meaning
   ## ---------------------   ---------------------------------------------
-  ## ``pcFile``              yield real files
+  ## ``pcFile``              yield real files (default)
   ## ``pcLinkToFile``        yield symbolic links to files
   ## ``pcDir``               yield real directories
   ## ``pcLinkToDir``         yield symbolic links to directories
@@ -1562,10 +1997,17 @@ iterator walkDirRec*(dir: string,
   ## ---------------------   ---------------------------------------------
   ## followFilter            meaning
   ## ---------------------   ---------------------------------------------
-  ## ``pcDir``               follow real directories
+  ## ``pcDir``               follow real directories (default)
   ## ``pcLinkToDir``         follow symbolic links to directories
   ## ---------------------   ---------------------------------------------
   ##
+  ##
+  ## See also:
+  ## * `walkPattern iterator <#walkPattern.i,string>`_
+  ## * `walkFiles iterator <#walkFiles.i,string>`_
+  ## * `walkDirs iterator <#walkDirs.i,string>`_
+  ## * `walkDir iterator <#walkDir.i,string>`_
+
   var stack = @[""]
   while stack.len > 0:
     let d = stack.pop()
@@ -1596,6 +2038,15 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
   ##
   ## If this fails, `OSError` is raised. This does not fail if the directory never
   ## existed in the first place.
+  ##
+  ## See also:
+  ## * `tryRemoveFile proc <#tryRemoveFile,string>`_
+  ## * `removeFile proc <#removeFile,string>`_
+  ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
+  ## * `createDir proc <#createDir,string>`_
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## * `moveDir proc <#moveDir,string,string>`_
   for kind, path in walkDir(dir):
     case kind
     of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
@@ -1653,6 +2104,13 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   ## Does not create parent directories (fails if parent does not exist).
   ## Returns `true` if the directory already exists, and `false`
   ## otherwise.
+  ##
+  ## See also:
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `createDir proc <#createDir,string>`_
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## * `moveDir proc <#moveDir,string,string>`_
   result = not rawCreateDir(dir)
   if result:
     # path already exists - need to check that it is indeed a directory
@@ -1664,9 +2122,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
   ## Creates the `directory`:idx: `dir`.
   ##
   ## The directory may contain several subdirectories that do not exist yet.
-  ## The full path is created. If this fails, `OSError` is raised. It does **not**
-  ## fail if the directory already exists because for most usages this does not
-  ## indicate an error.
+  ## The full path is created. If this fails, `OSError` is raised.
+  ##
+  ## It does **not** fail if the directory already exists because for
+  ## most usages this does not indicate an error.
+  ##
+  ## See also:
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## * `moveDir proc <#moveDir,string,string>`_
   var omitNext = false
   when doslikeFileSystem:
     omitNext = isAbsolute(dir)
@@ -1686,11 +2152,24 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
   tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} =
   ## Copies a directory from `source` to `dest`.
   ##
-  ## If this fails, `OSError` is raised. On the Windows platform this proc will
-  ## copy the attributes from `source` into `dest`. On other platforms created
-  ## files and directories will inherit the default permissions of a newly
-  ## created file/directory for the user. To preserve attributes recursively on
-  ## these platforms use `copyDirWithPermissions() <#copyDirWithPermissions>`_.
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will copy the attributes from
+  ## `source` into `dest`.
+  ##
+  ## On other platforms created files and directories will inherit the
+  ## default permissions of a newly created file/directory for the user.
+  ## Use `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## to preserve attributes recursively on these platforms.
+  ##
+  ## See also:
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
+  ## * `createDir proc <#createDir,string>`_
+  ## * `moveDir proc <#moveDir,string,string>`_
   createDir(dest)
   for kind, path in walkDir(source):
     var noSource = splitPath(path).tail
@@ -1701,6 +2180,24 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
       copyDir(path, dest / noSource)
     else: discard
 
+proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noNimScript.} =
+  ## Moves a directory from `source` to `dest`.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `moveFile proc <#moveFile,string,string>`_
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
+  ## * `createDir proc <#createDir,string>`_
+  if not tryMoveFSObject(source, dest):
+    when not defined(windows):
+      # Fallback to copy & del
+      copyDir(source, dest)
+      removeDir(source)
+
 proc createSymlink*(src, dest: string) {.noNimScript.} =
   ## Create a symbolic link at `dest` which points to the item specified
   ## by `src`. On most operating systems, will fail if a link already exists.
@@ -1708,6 +2205,11 @@ proc createSymlink*(src, dest: string) {.noNimScript.} =
   ## **Warning**:
   ## Some OS's (such as Microsoft Windows) restrict the creation
   ## of symlinks to root users (administrators).
+  ##
+  ## See also:
+  ## * `createHardlink proc <#createHardlink,string,string>`_
+  ## * `expandSymlink proc <#expandSymlink,string>`_
+
   when defined(Windows):
     # 2 is the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. This allows
     # anyone with developer mode on to create a link
@@ -1730,6 +2232,9 @@ proc createHardlink*(src, dest: string) {.noNimScript.} =
   ##
   ## **Warning**: Some OS's restrict the creation of hard links to
   ## root users (administrators).
+  ##
+  ## See also:
+  ## * `createSymlink proc <#createSymlink,string,string>`_
   when defined(Windows):
     when useWinUnicode:
       var wSrc = newWideCString(src)
@@ -1743,13 +2248,128 @@ proc createHardlink*(src, dest: string) {.noNimScript.} =
     if link(src, dest) != 0:
       raiseOSError(osLastError())
 
+proc copyFileWithPermissions*(source, dest: string,
+                              ignorePermissionErrors = true) {.noNimScript.} =
+  ## Copies a file from `source` to `dest` preserving file permissions.
+  ##
+  ## This is a wrapper proc around `copyFile <#copyFile,string,string>`_,
+  ## `getFilePermissions <#getFilePermissions,string>`_ and
+  ## `setFilePermissions<#setFilePermissions,string,set[FilePermission]>`_
+  ## procs on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyFile proc
+  ## <#copyFile,string,string>`_ since that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file itself has
+  ## been copied, which won't happen atomically and could lead to a race
+  ## condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `tryRemoveFile proc <#tryRemoveFile,string>`_
+  ## * `removeFile proc <#removeFile,string>`_
+  ## * `moveFile proc <#moveFile,string,string>`_
+  ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
+  copyFile(source, dest)
+  when not defined(Windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source))
+    except:
+      if not ignorePermissionErrors:
+        raise
+
+proc copyDirWithPermissions*(source, dest: string,
+    ignorePermissionErrors = true) {.rtl, extern: "nos$1",
+    tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} =
+  ## Copies a directory from `source` to `dest` preserving file permissions.
+  ##
+  ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir
+  ## <#copyDir,string,string>`_ and `copyFileWithPermissions
+  ## <#copyFileWithPermissions,string,string>`_ procs
+  ## on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyDir proc
+  ## <#copyDir,string,string>`_ since that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file or directory
+  ## itself has been copied, which won't happen atomically and could lead to a
+  ## race condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `copyDir proc <#copyDir,string,string>`_
+  ## * `copyFile proc <#copyFile,string,string>`_
+  ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
+  ## * `removeDir proc <#removeDir,string>`_
+  ## * `moveDir proc <#moveDir,string,string>`_
+  ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
+  ## * `createDir proc <#createDir,string>`_
+  createDir(dest)
+  when not defined(Windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source))
+    except:
+      if not ignorePermissionErrors:
+        raise
+  for kind, path in walkDir(source):
+    var noSource = splitPath(path).tail
+    case kind
+    of pcFile:
+      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors)
+    of pcDir:
+      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
+    else: discard
+
+proc inclFilePermissions*(filename: string,
+                          permissions: set[FilePermission]) {.
+  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} =
+  ## A convenience proc for:
+  ##
+  ## .. code-block:: nim
+  ##   setFilePermissions(filename, getFilePermissions(filename)+permissions)
+  setFilePermissions(filename, getFilePermissions(filename)+permissions)
+
+proc exclFilePermissions*(filename: string,
+                          permissions: set[FilePermission]) {.
+  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} =
+  ## A convenience proc for:
+  ##
+  ## .. code-block:: nim
+  ##   setFilePermissions(filename, getFilePermissions(filename)-permissions)
+  setFilePermissions(filename, getFilePermissions(filename)-permissions)
+
+proc expandSymlink*(symlinkPath: string): string {.noNimScript.} =
+  ## Returns a string representing the path to which the symbolic link points.
+  ##
+  ## On Windows this is a noop, ``symlinkPath`` is simply returned.
+  ##
+  ## See also:
+  ## * `createSymlink proc <#createSymlink,string,string>`_
+  when defined(windows):
+    result = symlinkPath
+  else:
+    result = newString(256)
+    var len = readlink(symlinkPath, result, 256)
+    if len < 0:
+      raiseOSError(osLastError())
+    if len > 256:
+      result = newString(len+1)
+      len = readlink(symlinkPath, result, len)
+    setLen(result, len)
+
 proc parseCmdLine*(c: string): seq[string] {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a `command line`:idx: into several components;
-  ## This proc is only occasionally useful, better use the `parseopt` module.
+  ## Splits a `command line`:idx: into several components.
+  ##
+  ## **Note**: This proc is only occasionally useful, better use the
+  ## `parseopt module <parseopt.html>`_.
   ##
-  ## On Windows, it uses the following parsing rules
-  ## (see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx ):
+  ## On Windows, it uses the `following parsing rules
+  ## <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_:
   ##
   ## * Arguments are delimited by white space, which is either a space or a tab.
   ## * The caret character (^) is not recognized as an escape character or
@@ -1774,6 +2394,13 @@ proc parseCmdLine*(c: string): seq[string] {.
   ## On Posix systems, it uses the following parsing rules:
   ## Components are separated by whitespace unless the whitespace
   ## occurs within ``"`` or ``'`` quotes.
+  ##
+  ## See also:
+  ## * `parseopt module <parseopt.html>`_
+  ## * `paramCount proc <#paramCount>`_
+  ## * `paramStr proc <#paramStr,int>`_
+  ## * `commandLineParams proc <#commandLineParams>`_
+
   result = @[]
   var i = 0
   var a = ""
@@ -1831,102 +2458,6 @@ proc parseCmdLine*(c: string): seq[string] {.
           inc(i)
     add(result, a)
 
-proc copyFileWithPermissions*(source, dest: string,
-                              ignorePermissionErrors = true) {.noNimScript.} =
-  ## Copies a file from `source` to `dest` preserving file permissions.
-  ##
-  ## This is a wrapper proc around `copyFile() <#copyFile>`_,
-  ## `getFilePermissions() <#getFilePermissions>`_ and `setFilePermissions()
-  ## <#setFilePermissions>`_ on non Windows platform. On Windows this proc is
-  ## just a wrapper for `copyFile() <#copyFile>`_ since that proc already
-  ## copies attributes.
-  ##
-  ## On non Windows systems permissions are copied after the file itself has
-  ## been copied, which won't happen atomically and could lead to a race
-  ## condition. If `ignorePermissionErrors` is true, errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  copyFile(source, dest)
-  when not defined(Windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source))
-    except:
-      if not ignorePermissionErrors:
-        raise
-
-proc copyDirWithPermissions*(source, dest: string,
-    ignorePermissionErrors = true) {.rtl, extern: "nos$1",
-    tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} =
-  ## Copies a directory from `source` to `dest` preserving file permissions.
-  ##
-  ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir()
-  ## <#copyDir>`_ and `copyFileWithPermissions() <#copyFileWithPermissions>`_
-  ## on non Windows platforms. On Windows this proc is just a wrapper for
-  ## `copyDir() <#copyDir>`_ since that proc already copies attributes.
-  ##
-  ## On non Windows systems permissions are copied after the file or directory
-  ## itself has been copied, which won't happen atomically and could lead to a
-  ## race condition. If `ignorePermissionErrors` is true, errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  createDir(dest)
-  when not defined(Windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source))
-    except:
-      if not ignorePermissionErrors:
-        raise
-  for kind, path in walkDir(source):
-    var noSource = splitPath(path).tail
-    case kind
-    of pcFile:
-      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors)
-    of pcDir:
-      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
-    else: discard
-
-proc inclFilePermissions*(filename: string,
-                          permissions: set[FilePermission]) {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} =
-  ## a convenience procedure for:
-  ##
-  ## .. code-block:: nim
-  ##   setFilePermissions(filename, getFilePermissions(filename)+permissions)
-  setFilePermissions(filename, getFilePermissions(filename)+permissions)
-
-proc exclFilePermissions*(filename: string,
-                          permissions: set[FilePermission]) {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} =
-  ## a convenience procedure for:
-  ##
-  ## .. code-block:: nim
-  ##   setFilePermissions(filename, getFilePermissions(filename)-permissions)
-  setFilePermissions(filename, getFilePermissions(filename)-permissions)
-
-proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noNimScript.} =
-  ## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised.
-  if not tryMoveFSObject(source, dest):
-    when not defined(windows):
-      # Fallback to copy & del
-      copyDir(source, dest)
-      removeDir(source)
-
-proc expandSymlink*(symlinkPath: string): string {.noNimScript.} =
-  ## Returns a string representing the path to which the symbolic link points.
-  ##
-  ## On Windows this is a noop, ``symlinkPath`` is simply returned.
-  when defined(windows):
-    result = symlinkPath
-  else:
-    result = newString(256)
-    var len = readlink(symlinkPath, result, 256)
-    if len < 0:
-      raiseOSError(osLastError())
-    if len > 256:
-      result = newString(len+1)
-      len = readlink(symlinkPath, result, len)
-    setLen(result, len)
-
 when defined(nimdoc):
   # Common forward declaration docstring block for parameter retrieval procs.
   proc paramCount*(): int {.tags: [ReadIOEffect].} =
@@ -1935,14 +2466,21 @@ when defined(nimdoc):
     ##
     ## Unlike `argc`:idx: in C, if your binary was called without parameters this
     ## will return zero.
-    ## You can query each individual paramater with `paramStr() <#paramStr>`_
-    ## or retrieve all of them in one go with `commandLineParams()
+    ## You can query each individual paramater with `paramStr proc <#paramStr,int>`_
+    ## or retrieve all of them in one go with `commandLineParams proc
     ## <#commandLineParams>`_.
     ##
-    ## **Availability**: When generating a dynamic library (see --app:lib) on
+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on
     ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared>`_.
-    ## Example:
+    ## Test for availability using `declared() <system.html#declared,untyped>`_.
+    ##
+    ## See also:
+    ## * `parseopt module <parseopt.html>`_
+    ## * `parseCmdLine proc <#parseCmdLine,string>`_
+    ## * `paramStr proc <#paramStr,int>`_
+    ## * `commandLineParams proc <#commandLineParams>`_
+    ##
+    ## **Examples:**
     ##
     ## .. code-block:: nim
     ##   when declared(paramCount):
@@ -1963,10 +2501,18 @@ when defined(nimdoc):
     ## contents (usually the name of the invoked executable). You should avoid
     ## this and call `getAppFilename() <#getAppFilename>`_ instead.
     ##
-    ## **Availability**: When generating a dynamic library (see --app:lib) on
+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on
     ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared>`_.
-    ## Example:
+    ## Test for availability using `declared() <system.html#declared,untyped>`_.
+    ##
+    ## See also:
+    ## * `parseopt module <parseopt.html>`_
+    ## * `parseCmdLine proc <#parseCmdLine,string>`_
+    ## * `paramCount proc <#paramCount>`_
+    ## * `commandLineParams proc <#commandLineParams>`_
+    ## * `getAppFilename proc <#getAppFilename>`_
+    ##
+    ## **Examples:**
     ##
     ## .. code-block:: nim
     ##   when declared(paramStr):
@@ -2039,8 +2585,17 @@ when declared(paramCount) or defined(nimdoc):
     ##
     ## **Availability**: On Posix there is no portable way to get the command
     ## line from a DLL and thus the proc isn't defined in this environment. You
-    ## can test for its availability with `declared() <system.html#declared>`_.
-    ## Example:
+    ## can test for its availability with `declared()
+    ## <system.html#declared,untyped>`_.
+    ##
+    ## See also:
+    ## * `parseopt module <parseopt.html>`_
+    ## * `parseCmdLine proc <#parseCmdLine,string>`_
+    ## * `paramCount proc <#paramCount>`_
+    ## * `paramStr proc <#paramStr,int>`_
+    ## * `getAppFilename proc <#getAppFilename>`_
+    ##
+    ## **Examples:**
     ##
     ## .. code-block:: nim
     ##   when declared(commandLineParams):
@@ -2138,10 +2693,12 @@ when defined(haiku):
       result = ""
 
 proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} =
-  ## Returns the filename of the application's executable. See also
-  ## `getCurrentCompilerExe`.
+  ## Returns the filename of the application's executable.
+  ## This proc will resolve symlinks.
   ##
-  ## This procedure will resolve symlinks.
+  ## See also:
+  ## * `getAppDir proc <#getAppDir>`_
+  ## * `getCurrentCompilerExe proc <#getCurrentCompilerExe>`_
 
   # Linux: /proc/<pid>/exe
   # Solaris:
@@ -2200,10 +2757,13 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noN
 
 proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} =
   ## Returns the directory of the application's executable.
+  ##
+  ## See also:
+  ## * `getAppFilename proc <#getAppFilename>`_
   result = splitFile(getAppFilename()).dir
 
 proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noNimScript.} =
-  ## sleeps `milsecs` milliseconds.
+  ## Sleeps `milsecs` milliseconds.
   when defined(windows):
     winlean.sleep(int32(milsecs))
   else:
@@ -2214,7 +2774,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noNimScrip
 
 proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
   tags: [ReadIOEffect], noNimScript.} =
-  ## returns the file size of `file` (in bytes). An ``OSError`` exception is
+  ## Returns the file size of `file` (in bytes). ``OSError`` is
   ## raised in case of an error.
   when defined(windows):
     var a: WIN32_FIND_DATA
@@ -2241,14 +2801,19 @@ else:
 type
   FileInfo* = object
     ## Contains information associated with a file object.
-    id*: tuple[device: DeviceId, file: FileId] # Device and file id.
-    kind*: PathComponent # Kind of file object - directory, symlink, etc.
-    size*: BiggestInt # Size of file.
-    permissions*: set[FilePermission] # File permissions
-    linkCount*: BiggestInt # Number of hard links the file object has.
-    lastAccessTime*: times.Time # Time file was last accessed.
-    lastWriteTime*: times.Time # Time file was last modified/written to.
-    creationTime*: times.Time # Time file was created. Not supported on all systems!
+    ##
+    ## See also:
+    ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_
+    ## * `getFileInfo(file) proc <#getFileInfo,File>`_
+    ## * `getFileInfo(path) proc <#getFileInfo,string>`_
+    id*: tuple[device: DeviceId, file: FileId] ## Device and file id.
+    kind*: PathComponent              ## Kind of file object - directory, symlink, etc.
+    size*: BiggestInt                 ## Size of file.
+    permissions*: set[FilePermission] ## File permissions
+    linkCount*: BiggestInt            ## Number of hard links the file object has.
+    lastAccessTime*: times.Time       ## Time file was last accessed.
+    lastWriteTime*: times.Time        ## Time file was last modified/written to.
+    creationTime*: times.Time         ## Time file was created. Not supported on all systems!
 
 template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## Transforms the native file info structure into the one nim uses.
@@ -2320,7 +2885,12 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} =
   ## handle.
   ##
   ## If the information cannot be retrieved, such as when the file handle
-  ## is invalid, an error will be thrown.
+  ## is invalid, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `getFileInfo(file) proc <#getFileInfo,File>`_
+  ## * `getFileInfo(path) proc <#getFileInfo,string>`_
+
   # Done: ID, Kind, Size, Permissions, Link Count
   when defined(Windows):
     var rawInfo: BY_HANDLE_FILE_INFORMATION
@@ -2337,6 +2907,11 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} =
     rawToFormalFileInfo(rawInfo, "", result)
 
 proc getFileInfo*(file: File): FileInfo {.noNimScript.} =
+  ## Retrieves file information for the file object.
+  ##
+  ## See also:
+  ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_
+  ## * `getFileInfo(path) proc <#getFileInfo,string>`_
   if file.isNil:
     raise newException(IOError, "File is nil")
   result = getFileInfo(file.getFileHandle())
@@ -2345,16 +2920,20 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noNimScript.}
   ## Retrieves file information for the file object pointed to by `path`.
   ##
   ## Due to intrinsic differences between operating systems, the information
-  ## contained by the returned `FileInfo` structure will be slightly different
-  ## across platforms, and in some cases, incomplete or inaccurate.
+  ## contained by the returned `FileInfo object <#FileInfo>`_ will be slightly
+  ## different across platforms, and in some cases, incomplete or inaccurate.
   ##
-  ## When `followSymlink` is true, symlinks are followed and the information
-  ## retrieved is information related to the symlink's target. Otherwise,
-  ## information on the symlink itself is retrieved.
+  ## When `followSymlink` is true (default), symlinks are followed and the
+  ## information retrieved is information related to the symlink's target.
+  ## Otherwise, information on the symlink itself is retrieved.
   ##
   ## If the information cannot be retrieved, such as when the path doesn't
   ## exist, or when permission restrictions prevent the program from retrieving
-  ## file information, an error will be thrown.
+  ## file information, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_
+  ## * `getFileInfo(file) proc <#getFileInfo,File>`_
   when defined(Windows):
     var
       handle = openHandle(path, followSymlink)
@@ -2376,21 +2955,23 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noNimScript.}
     rawToFormalFileInfo(rawInfo, path, result)
 
 proc isHidden*(path: string): bool {.noNimScript.} =
-  ## Determines whether ``path`` is hidden or not, using this
-  ## reference https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory
+  ## Determines whether ``path`` is hidden or not, using `this
+  ## reference <https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory>`_.
   ##
   ## On Windows: returns true if it exists and its "hidden" attribute is set.
   ##
   ## On posix: returns true if ``lastPathPart(path)`` starts with ``.`` and is
-  ## not ``.`` or ``..``. Note: paths are not normalized to determine `isHidden`.
+  ## not ``.`` or ``..``.
+  ##
+  ## **Note**: paths are not normalized to determine `isHidden`.
   runnableExamples:
     when defined(posix):
-      doAssert ".foo".isHidden
-      doAssert: not ".foo/bar".isHidden
-      doAssert: not ".".isHidden
-      doAssert: not "..".isHidden
-      doAssert: not "".isHidden
-      doAssert ".foo/".isHidden
+      assert ".foo".isHidden
+      assert not ".foo/bar".isHidden
+      assert not ".".isHidden
+      assert not "..".isHidden
+      assert not "".isHidden
+      assert ".foo/".isHidden
 
   when defined(Windows):
     when useWinUnicode:
@@ -2404,7 +2985,10 @@ proc isHidden*(path: string): bool {.noNimScript.} =
     result = len(fileName) >= 2 and fileName[0] == '.' and fileName != ".."
 
 proc getCurrentProcessId*(): int {.noNimScript.} =
-  ## return current process ID. See also ``osproc.processID(p: Process)``.
+  ## Return current process ID.
+  ##
+  ## See also:
+  ## * `osproc.processID(p: Process) <osproc.html#processID,Process>`_
   when defined(windows):
     proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32",
                                         importc: "GetCurrentProcessId".}
@@ -2431,6 +3015,7 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noNimScript.} =
     discard h.closeHandle
     if res == 0'i32: raiseOSError(osLastError())
 
+
 when isMainModule:
   assert quoteShellWindows("aaa") == "aaa"
   assert quoteShellWindows("aaa\"") == "aaa\\\""
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 72581f47c..78f9a06eb 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -60,9 +60,6 @@ type
   Process* = ref ProcessObj ## represents an operating system process
 
 
-const poUseShell* {.deprecated.} = poUsePath
-  ## Deprecated alias for poUsePath.
-
 proc execProcess*(command: string,
                   workingDir: string = "",
                   args: openArray[string] = [],
@@ -129,7 +126,7 @@ proc startCmd*(command: string, options: set[ProcessOption] = {
   ## Deprecated - use `startProcess` directly.
   result = startProcess(command=command, options=options + {poEvalCommand})
 
-proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
+proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].}
   ## When the process has finished executing, cleanup related handles.
   ##
   ## **Warning:** If the process has not finished executing, this will forcibly
@@ -335,19 +332,6 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
-proc select*(readfds: var seq[Process], timeout = 500): int
-  {.benign, deprecated.}
-  ## `select` with a sensible Nim interface. `timeout` is in milliseconds.
-  ## Specify -1 for no timeout. Returns the number of processes that are
-  ## ready to read from. The processes that are ready to be read from are
-  ## removed from `readfds`.
-  ##
-  ## **Warning**: This function may give unexpected or completely wrong
-  ## results on Windows.
-  ##
-  ## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
-  ## and so should not be used in newly written code.
-
 when not defined(useNimRtl):
   proc execProcess(command: string,
                    workingDir: string = "",
@@ -724,13 +708,6 @@ elif not defined(useNimRtl):
   proc isExitStatus(status: cint): bool =
     WIFEXITED(status) or WIFSIGNALED(status)
 
-  proc exitStatus(status: cint): cint =
-    if WIFSIGNALED(status):
-      # like the shell!
-      128 + WTERMSIG(status)
-    else:
-      WEXITSTATUS(status)
-
   proc envToCStringArray(t: StringTableRef): cstringArray =
     result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
     var i = 0
@@ -1054,7 +1031,7 @@ elif not defined(useNimRtl):
 
     proc waitForExit(p: Process, timeout: int = -1): int =
       if p.exitFlag:
-        return exitStatus(p.exitStatus)
+        return exitStatusLikeShell(p.exitStatus)
 
       if timeout == -1:
         var status: cint = 1
@@ -1109,7 +1086,7 @@ elif not defined(useNimRtl):
         finally:
           discard posix.close(kqFD)
 
-      result = exitStatus(p.exitStatus)
+      result = exitStatusLikeShell(p.exitStatus)
   else:
     import times
 
@@ -1142,7 +1119,7 @@ elif not defined(useNimRtl):
         s.tv_nsec = b.tv_nsec
 
       if p.exitFlag:
-        return exitStatus(p.exitStatus)
+        return exitStatusLikeShell(p.exitStatus)
 
       if timeout == -1:
         var status: cint = 1
@@ -1220,20 +1197,20 @@ elif not defined(useNimRtl):
             if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
               raiseOSError(osLastError())
 
-      result = exitStatus(p.exitStatus)
+      result = exitStatusLikeShell(p.exitStatus)
 
   proc peekExitCode(p: Process): int =
     var status = cint(0)
     result = -1
     if p.exitFlag:
-      return exitStatus(p.exitStatus)
+      return exitStatusLikeShell(p.exitStatus)
 
     var ret = waitpid(p.id, status, WNOHANG)
     if ret > 0:
       if isExitStatus(status):
         p.exitFlag = true
         p.exitStatus = status
-        result = exitStatus(status)
+        result = exitStatusLikeShell(status)
 
   proc createStream(stream: var Stream, handle: var FileHandle,
                     fileMode: FileMode) =
@@ -1265,7 +1242,7 @@ elif not defined(useNimRtl):
   proc execCmd(command: string): int =
     when defined(linux):
       let tmp = csystem(command)
-      result = if tmp == -1: tmp else: exitStatus(tmp)
+      result = if tmp == -1: tmp else: exitStatusLikeShell(tmp)
     else:
       result = csystem(command)
 
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index b991dd57f..106d59017 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -111,7 +111,7 @@
 ##     dict.writeConfig("config.ini")
 
 import
-  hashes, strutils, lexbase, streams, tables
+  strutils, lexbase, streams, tables
 
 include "system/inclrtl"
 
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index 796114d37..e0c4f38a4 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -10,13 +10,18 @@
 ## This module implements a simple high performance `CSV`:idx:
 ## (`comma separated value`:idx:) parser.
 ##
-## Example: How to use the parser
-## ==============================
+## Basic usage
+## ===========
 ##
 ## .. code-block:: nim
-##   import os, parsecsv, streams
+##   import parsecsv
+##   from os import paramStr
+##   from streams import newFileStream
+##
 ##   var s = newFileStream(paramStr(1), fmRead)
-##   if s == nil: quit("cannot open the file" & paramStr(1))
+##   if s == nil:
+##     quit("cannot open the file" & paramStr(1))
+##
 ##   var x: CsvParser
 ##   open(x, s, paramStr(1))
 ##   while readRow(x):
@@ -26,11 +31,11 @@
 ##   close(x)
 ##
 ## For CSV files with a header row, the header can be read and then used as a
-## reference for item access with `rowEntry <#rowEntry.CsvParser.string>`_:
+## reference for item access with `rowEntry <#rowEntry,CsvParser,string>`_:
 ##
 ## .. code-block:: nim
 ##   import parsecsv
-##   import os
+##
 ##   # Prepare a file
 ##   let content = """One,Two,Three,Four
 ##   1,2,3,4
@@ -47,24 +52,40 @@
 ##     for col in items(p.headers):
 ##       echo "##", col, ":", p.rowEntry(col), "##"
 ##   p.close()
+##
+## See also
+## ========
+##
+## * `streams module <streams.html>`_ for using
+##   `open proc <#open,CsvParser,Stream,string,Char,Char,Char>`_
+##   and other stream processing (like `close proc <streams.html#close,Stream>`_)
+## * `parseopt module <parseopt.html>`_ for a command line parser
+## * `parsecfg module <parsecfg.html>`_ for a configuration file parser
+## * `parsexml module <parsexml.html>`_ for a XML / HTML parser
+## * `parsesql module <parsesql.html>`_ for a SQL parser
+## * `other parsers <lib.html#pure-libraries-parsers>`_ for other parsers
 
 import
   lexbase, streams
 
 type
-  CsvRow* = seq[string] ## a row in a CSV file
-  CsvParser* = object of BaseLexer ## the parser object.
-    row*: CsvRow                    ## the current row
+  CsvRow* = seq[string] ## A row in a CSV file.
+  CsvParser* = object of BaseLexer ## The parser object.
+    ##
+    ## It consists of two public fields:
+    ## * `row` is the current row
+    ## * `headers` are the columns that are defined in the csv file
+    ##   (read using `readHeaderRow <#readHeaderRow,CsvParser>`_).
+    ##   Used with `rowEntry <#rowEntry,CsvParser,string>`_).
+    row*: CsvRow
     filename: string
     sep, quote, esc: char
     skipWhite: bool
     currRow: int
-    headers*: seq[string] ## The columns that are defined in the csv file
-                          ## (read using `readHeaderRow <#readHeaderRow.CsvParser>`_).
-                          ## Used with `rowEntry <#rowEntry.CsvParser.string>`_).
+    headers*: seq[string]
 
-  CsvError* = object of IOError ## exception that is raised if
-                                ## a parsing error occurs
+  CsvError* = object of IOError ## An exception that is raised if
+                                ## a parsing error occurs.
 
 proc raiseEInvalidCsv(filename: string, line, col: int,
                       msg: string) {.noreturn.} =
@@ -82,7 +103,7 @@ proc error(my: CsvParser, pos: int, msg: string) =
 proc open*(my: var CsvParser, input: Stream, filename: string,
            separator = ',', quote = '"', escape = '\0',
            skipInitialSpace = false) =
-  ## initializes the parser with an input stream. `Filename` is only used
+  ## Initializes the parser with an input stream. `Filename` is only used
   ## for nice error messages. The parser's behaviour can be controlled by
   ## the diverse optional parameters:
   ## - `separator`: character used to separate fields
@@ -94,6 +115,18 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
   ##   two `quote` characters are parsed one literal `quote` character.
   ## - `skipInitialSpace`: If true, whitespace immediately following the
   ##   `separator` is ignored.
+  ##
+  ## See also:
+  ## * `open proc <#open,CsvParser,string,Char,Char,Char>`_ which creates the
+  ##   file stream for you
+  runnableExamples:
+    import streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    parser.close()
+    strm.close()
+
   lexbase.open(my, input)
   my.filename = filename
   my.sep = separator
@@ -106,7 +139,16 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
 proc open*(my: var CsvParser, filename: string,
            separator = ',', quote = '"', escape = '\0',
            skipInitialSpace = false) =
-  ## same as the other `open` but creates the file stream for you.
+  ## Similar to the `other open proc<#open,CsvParser,Stream,string,Char,Char,Char>`_,
+  ## but creates the file stream for you.
+  runnableExamples:
+    from os import removeFile
+    writeFile("tmp.csv", "One,Two,Three\n1,2,3\n10,20,300")
+    var parser: CsvParser
+    parser.open("tmp.csv")
+    parser.close()
+    removeFile("tmp.csv")
+
   var s = newFileStream(filename, fmRead)
   if s == nil: my.error(0, "cannot open: " & filename)
   open(my, s, filename, separator,
@@ -159,17 +201,66 @@ proc parseField(my: var CsvParser, a: var string) =
   my.bufpos = pos
 
 proc processedRows*(my: var CsvParser): int =
-  ## returns number of the processed rows
+  ## Returns number of the processed rows.
+  ##
+  ## But even if `readRow <#readRow,CsvParser,int>`_ arrived at EOF then
+  ## processed rows counter is incremented.
+  runnableExamples:
+    import streams
+
+    var strm = newStringStream("One,Two,Three\n1,2,3")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    doAssert parser.readRow()
+    doAssert parser.processedRows() == 1
+    doAssert parser.readRow()
+    doAssert parser.processedRows() == 2
+    ## Even if `readRow` arrived at EOF then `processedRows` is incremented.
+    doAssert parser.readRow() == false
+    doAssert parser.processedRows() == 3
+    doAssert parser.readRow() == false
+    doAssert parser.processedRows() == 4
+    parser.close()
+    strm.close()
+
   return my.currRow
 
 proc readRow*(my: var CsvParser, columns = 0): bool =
-  ## reads the next row; if `columns` > 0, it expects the row to have
+  ## Reads the next row; if `columns` > 0, it expects the row to have
   ## exactly this many columns. Returns false if the end of the file
   ## has been encountered else true.
   ##
   ## Blank lines are skipped.
+  runnableExamples:
+    import streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    doAssert parser.readRow()
+    doAssert parser.row == @["One", "Two", "Three"]
+    doAssert parser.readRow()
+    doAssert parser.row == @["1", "2", "3"]
+    ## Blank lines are skipped.
+    doAssert parser.readRow()
+    doAssert parser.row == @["10", "20", "30"]
+
+    var emptySeq: seq[string]
+    doAssert parser.readRow() == false
+    doAssert parser.row == emptySeq
+    doAssert parser.readRow() == false
+    doAssert parser.row == emptySeq
+
+    parser.close()
+    strm.close()
+
   var col = 0 # current column
   let oldpos = my.bufpos
+  # skip initial empty lines #8365
+  while true:
+    case my.buf[my.bufpos]
+    of '\c': my.bufpos = handleCR(my, my.bufpos)
+    of '\l': my.bufpos = handleLF(my, my.bufpos)
+    else: break
   while my.buf[my.bufpos] != '\0':
     let oldlen = my.row.len
     if oldlen < col+1:
@@ -200,12 +291,31 @@ proc readRow*(my: var CsvParser, columns = 0): bool =
   inc(my.currRow)
 
 proc close*(my: var CsvParser) {.inline.} =
-  ## closes the parser `my` and its associated input stream.
+  ## Closes the parser `my` and its associated input stream.
   lexbase.close(my)
 
 proc readHeaderRow*(my: var CsvParser) =
   ## Reads the first row and creates a look-up table for column numbers
-  ## See also `rowEntry <#rowEntry.CsvParser.string>`_.
+  ## See also:
+  ## * `rowEntry proc <#rowEntry,CsvParser,string>`_
+  runnableExamples:
+    import streams
+
+    var strm = newStringStream("One,Two,Three\n1,2,3")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+
+    parser.readHeaderRow()
+    doAssert parser.headers == @["One", "Two", "Three"]
+    doAssert parser.row == @["One", "Two", "Three"]
+
+    doAssert parser.readRow()
+    doAssert parser.headers == @["One", "Two", "Three"]
+    doAssert parser.row == @["1", "2", "3"]
+
+    parser.close()
+    strm.close()
+
   let present = my.readRow()
   if present:
     my.headers = my.row
@@ -213,8 +323,23 @@ proc readHeaderRow*(my: var CsvParser) =
 proc rowEntry*(my: var CsvParser, entry: string): var string =
   ## Acceses a specified `entry` from the current row.
   ##
-  ## Assumes that `readHeaderRow <#readHeaderRow.CsvParser>`_ has already been
+  ## Assumes that `readHeaderRow <#readHeaderRow,CsvParser>`_ has already been
   ## called.
+  runnableExamples:
+    import streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    ## Need calling `readHeaderRow`.
+    parser.readHeaderRow()
+    doAssert parser.readRow()
+    doAssert parser.rowEntry("One") == "1"
+    doAssert parser.rowEntry("Two") == "2"
+    doAssert parser.rowEntry("Three") == "3"
+    ## `parser.rowEntry("NotExistEntry")` causes SIGSEGV fault.
+    parser.close()
+    strm.close()
+
   let index = my.headers.find(entry)
   if index >= 0:
     result = my.row[index]
@@ -235,7 +360,7 @@ when isMainModule:
   import os
   import strutils
   block: # Tests for reading the header row
-    let content = "One,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n"
+    let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n"
     writeFile("temp.csv", content)
 
     var p: CsvParser
@@ -262,4 +387,3 @@ when isMainModule:
 
     # Tidy up
     removeFile("temp.csv")
-
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index eba915604..0f8f8197c 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -11,23 +11,141 @@
 ## It supports one convenience iterator over all command line options and some
 ## lower-level features.
 ##
-## Supported syntax with default empty ``shortNoVal``/``longNoVal``:
+## Supported Syntax
+## ================
 ##
-## 1. short options - ``-abcd``, where a, b, c, d are names
-## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
-## 3. argument - everything else
+## The following syntax is supported when arguments for the ``shortNoVal`` and
+## ``longNoVal`` parameters, which are
+## `described later<#shortnoval-and-longnoval>`_, are not provided:
 ##
-## When ``shortNoVal``/``longNoVal`` are non-empty then the ':' and '=' above
-## are still accepted, but become optional.  Note that these option key sets
-## must be updated along with the set of option keys taking no value, but
-## keys which do take values need no special updates as their set evolves.
+## 1. Short options: ``-abcd``, ``-e:5``, ``-e=5``
+## 2. Long options: ``--foo:bar``, ``--foo=bar``, ``--foo``
+## 3. Arguments: everything that does not start with a ``-``
 ##
-## When option values begin with ':' or '=' they need to be doubled up (as in
+## These three kinds of tokens are enumerated in the
+## `CmdLineKind enum<#CmdLineKind>`_.
+##
+## When option values begin with ':' or '=', they need to be doubled up (as in
 ## ``--delim::``) or alternated (as in ``--delim=:``).
 ##
-## The common ``--`` non-option argument delimiter appears as an empty string
-## long option key.  ``OptParser.cmd``, ``OptParser.pos``, and
-## ``os.parseCmdLine`` may be used to complete parsing in that case.
+## The ``--`` option, commonly used to denote that every token that follows is
+## an argument, is interpreted as a long option, and its name is the empty
+## string.
+##
+## Parsing
+## =======
+##
+## Use an `OptParser<#OptParser>`_ to parse command line options. It can be
+## created with `initOptParser<#initOptParser,string,set[char],seq[string]>`_,
+## and `next<#next,OptParser>`_ advances the parser by one token.
+##
+## For each token, the parser's ``kind``, ``key``, and ``val`` fields give
+## information about that token. If the token is a long or short option, ``key``
+## is the option's name, and  ``val`` is either the option's value, if provided,
+## or the empty string. For arguments, the ``key`` field contains the argument
+## itself, and ``val`` is unused. To check if the end of the command line has
+## been reached, check if ``kind`` is equal to ``cmdEnd``.
+##
+## Here is an example:
+##
+## .. code-block::
+##   import parseopt
+##
+##   var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt")
+##   while true:
+##     p.next()
+##     case p.kind
+##     of cmdEnd: break
+##     of cmdShortOption, cmdLongOption:
+##       if p.val == "":
+##         echo "Option: ", p.key
+##       else:
+##         echo "Option and value: ", p.key, ", ", p.val
+##     of cmdArgument:
+##       echo "Argument: ", p.key
+##
+##   # Output:
+##   # Option: a
+##   # Option: b
+##   # Option and value: e, 5
+##   # Option: foo
+##   # Option and value: bar, 20
+##   # Argument: file.txt
+##
+## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for
+## convenience, can be used to iterate through all command line options as well.
+##
+## ``shortNoVal`` and ``longNoVal``
+## ================================
+##
+## The optional ``shortNoVal`` and ``longNoVal`` parameters present in
+## `initOptParser<#initOptParser,string,set[char],seq[string]>`_ are for
+## specifying which short and long options do not accept values.
+##
+## When ``shortNoVal`` is non-empty, users are not required to separate short
+## options and their values with a ':' or '=' since the parser knows which
+## options accept values and which ones do not. This behavior also applies for
+## long options if ``longNoVal`` is non-empty. For short options, ``-j4``
+## becomes supported syntax, and for long options, ``--foo bar`` becomes
+## supported. This is in addition to the `previously mentioned
+## syntax<#supported-syntax>`_. Users can still separate options and their
+## values with ':' or '=', but that becomes optional.
+##
+## As more options which do not accept values are added to your program,
+## remember to amend ``shortNoVal`` and ``longNoVal`` accordingly.
+##
+## The following example illustrates the difference between having an empty
+## ``shortNoVal`` and ``longNoVal``, which is the default, and providing
+## arguments for those two parameters:
+##
+## .. code-block::
+##   import parseopt
+##
+##   proc printToken(kind: CmdLineKind, key: string, val: string) =
+##     case kind
+##     of cmdEnd: doAssert(false)  # Doesn't happen with getopt()
+##     of cmdShortOption, cmdLongOption:
+##       if val == "":
+##         echo "Option: ", key
+##       else:
+##         echo "Option and value: ", key, ", ", val
+##     of cmdArgument:
+##       echo "Argument: ", key
+##
+##   let cmdLine = "-j4 --first bar"
+##
+##   var emptyNoVal = initOptParser(cmdLine)
+##   for kind, key, val in emptyNoVal.getopt():
+##     printToken(kind, key, val)
+##
+##   # Output:
+##   # Option: j
+##   # Option: 4
+##   # Option: first
+##   # Argument: bar
+##
+##   var withNoVal = initOptParser(cmdLine, shortNoVal = {'c'},
+##                                 longNoVal = @["second"])
+##   for kind, key, val in withNoVal.getopt():
+##     printToken(kind, key, val)
+##
+##   # Output:
+##   # Option and value: j, 4
+##   # Option and value: first, bar
+##
+## See also
+## ========
+##
+## * `os module<os.html>`_ for lower-level command line parsing procs
+## * `parseutils module<parseutils.html>`_ for helpers that parse tokens,
+##   numbers, identifiers, etc.
+## * `strutils module<strutils.html>`_ for common string handling operations
+## * `json module<json.html>`_ for a JSON parser
+## * `parsecfg module<parsecfg.html>`_ for a configuration file parser
+## * `parsecsv module<parsecsv.html>`_ for a simple CSV (comma separated value)
+##   parser
+## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
+## * `other parsers<lib.html#pure-libraries-parsers>`_ for more parsers
 
 {.push debugger: off.}
 
@@ -37,23 +155,26 @@ import
   os, strutils
 
 type
-  CmdLineKind* = enum         ## the detected command line token
-    cmdEnd,                   ## end of command line reached
-    cmdArgument,              ## argument detected
-    cmdLongOption,            ## a long option ``--option`` detected
-    cmdShortOption            ## a short option ``-c`` detected
+  CmdLineKind* = enum         ## The detected command line token.
+    cmdEnd,                   ## End of command line reached
+    cmdArgument,              ## An argument such as a filename
+    cmdLongOption,            ## A long option such as --option
+    cmdShortOption            ## A short option such as -c
   OptParser* =
-      object of RootObj ## this object implements the command line parser
-    pos*: int                 # ..empty key or subcmd cmdArg & handle specially
+      object of RootObj ## Implementation of the command line parser.
+      ##
+      ## To initialize it, use the
+      ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos*: int
     inShortState: bool
     allowWhitespaceAfterColon: bool
     shortNoVal: set[char]
     longNoVal: seq[string]
     cmds: seq[string]
     idx: int
-    kind*: CmdLineKind        ## the dected command line token
-    key*, val*: TaintedString ## key and value pair; ``key`` is the option
-                              ## or the argument, ``value`` is not "" if
+    kind*: CmdLineKind        ## The detected command line token
+    key*, val*: TaintedString ## Key and value pair; the key is the option
+                              ## or the argument, and the value is not "" if
                               ## the option was given a value
 
 proc parseWord(s: string, i: int, w: var string,
@@ -73,37 +194,30 @@ proc parseWord(s: string, i: int, w: var string,
       inc(result)
 
 when declared(os.paramCount):
-  proc quote(s: string): string =
-    if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"':
-      if s[0] == '-':
-        result = newStringOfCap(s.len)
-        var i = parseWord(s, 0, result, {' ', '\t', ':', '='})
-        if i < s.len and s[i] in {':','='}:
-          result.add s[i]
-          inc i
-        result.add '"'
-        while i < s.len:
-          result.add s[i]
-          inc i
-        result.add '"'
-      else:
-        result = '"' & s & '"'
-    else:
-      result = s
-
   # we cannot provide this for NimRtl creation on Posix, because we can't
   # access the command line arguments then!
 
   proc initOptParser*(cmdline = "", shortNoVal: set[char]={},
                       longNoVal: seq[string] = @[];
                       allowWhitespaceAfterColon = true): OptParser =
-    ## inits the option parser. If ``cmdline == ""``, the real command line
-    ## (as provided by the ``OS`` module) is taken.  If ``shortNoVal`` is
-    ## provided command users do not need to delimit short option keys and
-    ## values with a ':' or '='.  If ``longNoVal`` is provided command users do
-    ## not need to delimit long option keys and values with a ':' or '='
-    ## (though they still need at least a space).  In both cases, ':' or '='
-    ## may still be used if desired.  They just become optional.
+    ## Initializes the command line parser.
+    ##
+    ## If ``cmdline == ""``, the real command line as provided by the
+    ## ``os`` module is retrieved instead.
+    ##
+    ## ``shortNoVal`` and ``longNoVal`` are used to specify which options
+    ## do not take values. See the `documentation about these
+    ## parameters<#shortnoval-and-longnoval>`_ for more information on
+    ## how this affects parsing.
+    ##
+    ## See also:
+    ## * `getopt iterator<#getopt.i,OptParser>`_
+    runnableExamples:
+      var p = initOptParser()
+      p = initOptParser("--left --debug:3 -l -r:2")
+      p = initOptParser("--left --debug:3 -l -r:2",
+                        shortNoVal = {'l'}, longNoVal = @["left"])
+
     result.pos = 0
     result.idx = 0
     result.inShortState = false
@@ -124,9 +238,21 @@ when declared(os.paramCount):
   proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={},
                       longNoVal: seq[string] = @[];
                       allowWhitespaceAfterColon = true): OptParser =
-    ## inits the option parser. If ``cmdline.len == 0``, the real command line
-    ## (as provided by the ``OS`` module) is taken. ``shortNoVal`` and
-    ## ``longNoVal`` behavior is the same as for ``initOptParser(string,...)``.
+    ## Initializes the command line parser.
+    ##
+    ## If ``cmdline.len == 0``, the real command line as provided by the
+    ## ``os`` module is retrieved instead. Behavior of the other parameters
+    ## remains the same as in `initOptParser(string, ...)
+    ## <#initOptParser,string,set[char],seq[string]>`_.
+    ##
+    ## See also:
+    ## * `getopt iterator<#getopt.i,seq[TaintedString],set[char],seq[string]>`_
+    runnableExamples:
+      var p = initOptParser()
+      p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"])
+      p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"],
+                        shortNoVal = {'l'}, longNoVal = @["left"])
+
     result.pos = 0
     result.idx = 0
     result.inShortState = false
@@ -171,8 +297,21 @@ proc handleShortOption(p: var OptParser; cmd: string) =
     inc p.idx
 
 proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
-  ## parses the first or next option; ``p.kind`` describes what token has been
-  ## parsed. ``p.key`` and ``p.val`` are set accordingly.
+  ## Parses the next token.
+  ##
+  ## ``p.kind`` describes what kind of token has been parsed. ``p.key`` and
+  ## ``p.val`` are set accordingly.
+  runnableExamples:
+    var p = initOptParser("--left -r:2 file.txt")
+    p.next()
+    doAssert p.kind == cmdLongOption and p.key == "left"
+    p.next()
+    doAssert p.kind == cmdShortOption and p.key == "r" and p.val == "2"
+    p.next()
+    doAssert p.kind == cmdArgument and p.key == "file.txt"
+    p.next()
+    doAssert p.kind == cmdEnd
+
   if p.idx >= p.cmds.len:
     p.kind = cmdEnd
     return
@@ -227,24 +366,61 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
 
 when declared(os.paramCount):
   proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
-    ## retrieves the rest of the command line that has not been parsed yet.
-    var res = ""
-    for i in p.idx..<p.cmds.len:
-      if i > p.idx: res.add ' '
-      res.add quote(p.cmds[i])
-    result = res.TaintedString
+    ## Retrieves the rest of the command line that has not been parsed yet.
+    ##
+    ## See also:
+    ## * `remainingArgs proc<#remainingArgs,OptParser>`_
+    ##
+    ## **Examples:**
+    ##
+    ## .. code-block::
+    ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
+    ##   while true:
+    ##     p.next()
+    ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
+    ##       break
+    ##     else: continue
+    ##   doAssert p.cmdLineRest == "foo.txt bar.txt"
+    result = p.cmds[p.idx .. ^1].quoteShellCommand.TaintedString
 
   proc remainingArgs*(p: OptParser): seq[TaintedString] {.rtl, extern: "npo$1".} =
-    ## retrieves the rest of the command line that has not been parsed yet.
+    ## Retrieves a sequence of the arguments that have not been parsed yet.
+    ##
+    ## See also:
+    ## * `cmdLineRest proc<#cmdLineRest,OptParser>`_
+    ##
+    ## **Examples:**
+    ##
+    ## .. code-block::
+    ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
+    ##   while true:
+    ##     p.next()
+    ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
+    ##       break
+    ##     else: continue
+    ##   doAssert p.remainingArgs == @["foo.txt", "bar.txt"]
     result = @[]
     for i in p.idx..<p.cmds.len: result.add TaintedString(p.cmds[i])
 
 iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] =
-  ## This is an convenience iterator for iterating over the given OptParser object.
-  ## Example:
+  ## Convenience iterator for iterating over the given
+  ## `OptParser<#OptParser>`_.
+  ##
+  ## There is no need to check for ``cmdEnd`` while iterating.
   ##
-  ## .. code-block:: nim
+  ## See also:
+  ## * `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   # these are placeholders, of course
+  ##   proc writeHelp() = discard
+  ##   proc writeVersion() = discard
+  ##
+  ##   var filename: string
   ##   var p = initOptParser("--left --debug:3 -l -r:2")
+  ##
   ##   for kind, key, val in p.getopt():
   ##     case kind
   ##     of cmdArgument:
@@ -255,7 +431,7 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedSt
   ##       of "version", "v": writeVersion()
   ##     of cmdEnd: assert(false) # cannot happen
   ##   if filename == "":
-  ##     # no filename has been given, so we show the help:
+  ##     # no filename has been given, so we show the help
   ##     writeHelp()
   p.pos = 0
   p.idx = 0
@@ -268,15 +444,34 @@ when declared(initOptParser):
   iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(),
                    shortNoVal: set[char]={}, longNoVal: seq[string] = @[]):
              tuple[kind: CmdLineKind, key, val: TaintedString] =
-    ## This is an convenience iterator for iterating over command line arguments.
-    ## This creates a new OptParser.  See the above ``getopt(var OptParser)``
-    ## example for using default empty ``NoVal`` parameters.  This example is
-    ## for the same option keys as that example but here option key-value
-    ## separators become optional for command users:
+    ## Convenience iterator for iterating over command line arguments.
+    ##
+    ## This creates a new `OptParser<#OptParser>`_. If no command line 
+    ## arguments are provided, the real command line as provided by the
+    ## ``os`` module is retrieved instead.
+    ##
+    ## ``shortNoVal`` and ``longNoVal`` are used to specify which options
+    ## do not take values. See the `documentation about these
+    ## parameters<#shortnoval-and-longnoval>`_ for more information on
+    ## how this affects parsing.
+    ##
+    ## There is no need to check for ``cmdEnd`` while iterating.
     ##
-    ## .. code-block:: nim
-    ##   for kind, key, val in getopt(shortNoVal = { 'l' },
-    ##                                longNoVal = @[ "left" ]):
+    ## See also:
+    ## * `initOptParser proc<#initOptParser,seq[TaintedString],set[char],seq[string]>`_
+    ##
+    ## **Examples:**
+    ##
+    ## .. code-block::
+    ##
+    ##   # these are placeholders, of course
+    ##   proc writeHelp() = discard
+    ##   proc writeVersion() = discard
+    ##
+    ##   var filename: string
+    ##   let params = @["--left", "--debug:3", "-l", "-r:2"]
+    ##
+    ##   for kind, key, val in getopt(params):
     ##     case kind
     ##     of cmdArgument:
     ##       filename = key
@@ -286,8 +481,8 @@ when declared(initOptParser):
     ##       of "version", "v": writeVersion()
     ##     of cmdEnd: assert(false) # cannot happen
     ##   if filename == "":
+    ##     # no filename has been written, so we show the help
     ##     writeHelp()
-    ##
     var p = initOptParser(cmdline, shortNoVal=shortNoVal, longNoVal=longNoVal)
     while true:
       next(p)
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
deleted file mode 100644
index 51a70b6d1..000000000
--- a/lib/pure/parseopt2.nim
+++ /dev/null
@@ -1,164 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module provides the standard Nim command line parser.
-## It supports one convenience iterator over all command line options and some
-## lower-level features.
-##
-## Supported syntax:
-##
-## 1. short options - ``-abcd``, where a, b, c, d are names
-## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
-## 3. argument - everything else
-
-{.deprecated: "Use the 'parseopt' module instead".}
-{.push debugger: off.}
-
-include "system/inclrtl"
-
-import
-  os, strutils
-
-type
-  CmdLineKind* = enum         ## the detected command line token
-    cmdEnd,                   ## end of command line reached
-    cmdArgument,              ## argument detected
-    cmdLongOption,            ## a long option ``--option`` detected
-    cmdShortOption            ## a short option ``-c`` detected
-  OptParser* =
-      object of RootObj ## this object implements the command line parser
-    cmd: seq[string]
-    pos: int
-    remainingShortOptions: string
-    kind*: CmdLineKind        ## the detected command line token
-    key*, val*: TaintedString ## key and value pair; ``key`` is the option
-                              ## or the argument, ``value`` is not "" if
-                              ## the option was given a value
-
-proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
-  ## Initalizes option parses with cmdline. cmdline should not contain
-  ## argument 0 - program name.
-  ## If cmdline.len == 0 default to current command line arguments.
-  result.remainingShortOptions = ""
-  when not defined(createNimRtl):
-    if cmdline.len == 0:
-      result.cmd = commandLineParams()
-      return
-  else:
-    assert cmdline != nil, "Cannot determine command line arguments."
-
-  result.cmd = @cmdline
-
-proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
-  ## Initalizes option parses with cmdline. Splits cmdline in on spaces
-  ## and calls initOptParser(openarray[string])
-  ## Do not use.
-  if cmdline == "": # backward compatibility
-    return initOptParser(@[])
-  else:
-    return initOptParser(cmdline.split)
-
-when not defined(createNimRtl):
-  proc initOptParser*(): OptParser =
-    ## Initializes option parser from current command line arguments.
-    return initOptParser(commandLineParams())
-
-proc next*(p: var OptParser) {.rtl, extern: "npo2$1".}
-
-proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
-  for splitchar in [':', '=']:
-    if splitchar in token:
-      let pos = token.find(splitchar)
-      p.key = token[0..pos-1]
-      p.val = token[pos+1..token.len-1]
-      return
-
-  p.key = token
-  if allowEmpty:
-    p.val = ""
-  else:
-    p.remainingShortOptions = token[0..token.len-1]
-    p.next()
-
-proc next(p: var OptParser) =
-  if p.remainingShortOptions.len != 0:
-    p.kind = cmdShortOption
-    p.key = TaintedString(p.remainingShortOptions[0..0])
-    p.val = ""
-    p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1]
-    return
-
-  if p.pos >= p.cmd.len:
-    p.kind = cmdEnd
-    return
-
-  let token = p.cmd[p.pos]
-  p.pos += 1
-
-  if token.startsWith("--"):
-    p.kind = cmdLongOption
-    nextOption(p, token[2..token.len-1], allowEmpty=true)
-  elif token.startsWith("-"):
-    p.kind = cmdShortOption
-    nextOption(p, token[1..token.len-1], allowEmpty=true)
-  else:
-    p.kind = cmdArgument
-    p.key = token
-    p.val = ""
-
-proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecated.} =
-  ## Returns part of command line string that has not been parsed yet.
-  ## Do not use - does not correctly handle whitespace.
-  return p.cmd[p.pos..p.cmd.len-1].join(" ")
-
-type
-  GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
-
-iterator getopt*(p: var OptParser): GetoptResult =
-  ## This is an convenience iterator for iterating over the given OptParser object.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var p = initOptParser("--left --debug:3 -l=4 -r:2")
-  ##   for kind, key, val in p.getopt():
-  ##     case kind
-  ##     of cmdArgument:
-  ##       filename = key
-  ##     of cmdLongOption, cmdShortOption:
-  ##       case key
-  ##       of "help", "h": writeHelp()
-  ##       of "version", "v": writeVersion()
-  ##     of cmdEnd: assert(false) # cannot happen
-  ##   if filename == "":
-  ##     # no filename has been given, so we show the help:
-  ##     writeHelp()
-  p.pos = 0
-  while true:
-    next(p)
-    if p.kind == cmdEnd: break
-    yield (p.kind, p.key, p.val)
-
-when declared(paramCount):
-  iterator getopt*(): GetoptResult =
-    ## This is an convenience iterator for iterating over the command line arguments.
-    ## This create a new OptParser object.
-    ## See above for a more detailed example
-    ##
-    ## .. code-block:: nim
-    ##   for kind, key, val in getopt():
-    ##     # this will iterate over all arguments passed to the cmdline.
-    ##     continue
-    ##
-    var p = initOptParser()
-    while true:
-      next(p)
-      if p.kind == cmdEnd: break
-      yield (p.kind, p.key, p.val)
-
-{.pop.}
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index 20f02e815..f0961829b 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -11,7 +11,7 @@
 ## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
 
 import
-  hashes, strutils, lexbase
+  strutils, lexbase
 
 # ------------------- scanner -------------------------------------------------
 
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index fb4bc19af..ba09347a2 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -7,9 +7,46 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains helpers for parsing tokens, numbers, identifiers, etc.
+## This module contains helpers for parsing tokens, numbers, integers, floats,
+## identifiers, etc.
 ##
 ## To unpack raw bytes look at the `streams <streams.html>`_ module.
+##
+##
+## .. code-block::
+##    import parseutils
+##
+##    let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
+##
+##    for log in logs:
+##      var res: string
+##      if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
+##        echo res & " - " & captureBetween(log, ' ', '_')
+##        # => 2019-01-10 - OK
+##
+##
+## .. code-block::
+##    import parseutils
+##    from strutils import Digits, parseInt
+##
+##    let userInput1 = "2019 school start"
+##    let userInput2 = "3 years back"
+##
+##    let startYear = input1[0..skipWhile(input1, Digits)-1] # 2019
+##    let yearsBack = input2[0..skipWhile(input2, Digits)-1] # 3
+##
+##    echo "Examination is in " & $(parseInt(startYear) + parseInt(yearsBack))
+##
+##
+## **See also:**
+## * `strutils module<strutils.html>`_ for combined and identical parsing proc's
+## * `json module<json.html>`_ for a JSON parser
+## * `parsecfg module<parsecfg.html>`_ for a configuration file parser
+## * `parsecsv module<parsecsv.html>`_ for a simple CSV (comma separated value) parser
+## * `parseopt module<parseopt.html>`_ for a command line parser
+## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
+## * `other parsers<lib.html#pure-libraries-parsers>`_ for other parsers
+
 
 {.deadCodeElim: on.}  # dce option deprecated
 
@@ -35,21 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
   ## proc is sensitive to the already existing value of ``number`` and will
   ## likely not do what you want unless you make sure ``number`` is zero. You
   ## can use this feature to *chain* calls, though the result int will quickly
-  ## overflow. Example:
-  ##
-  ## .. code-block:: nim
-  ##   var value = 0
-  ##   discard parseHex("0x38", value)
-  ##   assert value == 56
-  ##   discard parseHex("0x34", value)
-  ##   assert value == 56 * 256 + 52
-  ##   value = -1
-  ##   discard parseHex("0x38", value)
-  ##   assert value == -200
+  ## overflow.
   ##
   ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var value = 0
+    discard parseHex("0x38", value)
+    assert value == 56
+    discard parseHex("0x34", value)
+    assert value == 56 * 256 + 52
+    value = -1
+    discard parseHex("0x38", value)
+    assert value == -200
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -80,6 +116,11 @@ proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   ## If ``maxLen == 0`` the length of the octal number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var res: int
+    doAssert parseOct("12", res) == 2
+    doAssert res == 10
+    doAssert parseOct("9", res) == 0
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -95,7 +136,7 @@ proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
     inc(i)
   if foundDigit: result = i-start
 
-proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
+proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {.
   rtl, extern: "npuParseBin", noSideEffect.} =
   ## Parses an binary number and stores its value in ``number``. Returns
   ## the number of the parsed characters or 0 in case of an error.
@@ -103,6 +144,10 @@ proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   ## If ``maxLen == 0`` the length of the binary number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var res: int
+    doAssert parseBin("010011100110100101101101", res) == 24
+    doAssert parseBin("3", res) == 0
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -119,8 +164,16 @@ proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   if foundDigit: result = i-start
 
 proc parseIdent*(s: string, ident: var string, start = 0): int =
-  ## parses an identifier and stores it in ``ident``. Returns
+  ## Parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
+  runnableExamples:
+    var res: string
+    doAssert parseIdent("Hello World", res, 0) == 5
+    doAssert res == "Hello"
+    doAssert parseIdent("Hello World", res, 1) == 4
+    doAssert res == "ello"
+    doAssert parseIdent("Hello World", res, 6) == 5
+    doAssert res == "World"
   var i = start
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
@@ -129,8 +182,13 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     result = i-start
 
 proc parseIdent*(s: string, start = 0): string =
-  ## parses an identifier and returns it or an empty string in
+  ## Parses an identifier and returns it or an empty string in
   ## case of an error.
+  runnableExamples:
+    doAssert parseIdent("Hello World", 0) == "Hello"
+    doAssert parseIdent("Hello World", 1) == "ello"
+    doAssert parseIdent("Hello World", 5) == ""
+    doAssert parseIdent("Hello World", 6) == "World"
   result = ""
   var i = start
   if i < s.len and s[i] in IdentStartChars:
@@ -138,33 +196,35 @@ proc parseIdent*(s: string, start = 0): string =
     while i < s.len and s[i] in IdentChars: inc(i)
     result = substr(s, start, i-1)
 
-proc parseToken*(s: string, token: var string, validChars: set[char],
-                 start = 0): int {.inline, deprecated.} =
-  ## parses a token and stores it in ``token``. Returns
-  ## the number of the parsed characters or 0 in case of an error. A token
-  ## consists of the characters in `validChars`.
-  ##
-  ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead.
-  var i = start
-  while i < s.len and s[i] in validChars: inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
-
 proc skipWhitespace*(s: string, start = 0): int {.inline.} =
-  ## skips the whitespace starting at ``s[start]``. Returns the number of
+  ## Skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
+  runnableExamples:
+    doAssert skipWhitespace("Hello World", 0) == 0
+    doAssert skipWhitespace(" Hello World", 0) == 1
+    doAssert skipWhitespace("Hello World", 5) == 1
+    doAssert skipWhitespace("Hello  World", 5) == 2
   while start+result < s.len and s[start+result] in Whitespace: inc(result)
 
 proc skip*(s, token: string, start = 0): int {.inline.} =
-  ## skips the `token` starting at ``s[start]``. Returns the length of `token`
+  ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
+  runnableExamples:
+    doAssert skip("2019-01-22", "2019", 0) == 4
+    doAssert skip("2019-01-22", "19", 0) == 0
+    doAssert skip("2019-01-22", "19", 2) == 2
+    doAssert skip("CAPlow", "CAP", 0) == 3
+    doAssert skip("CAPlow", "cap", 0) == 0
   while start+result < s.len and result < token.len and
       s[result+start] == token[result]:
     inc(result)
   if result != token.len: result = 0
 
 proc skipIgnoreCase*(s, token: string, start = 0): int =
-  ## same as `skip` but case is ignored for token matching.
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
   while start+result < s.len and result < token.len and
       toLower(s[result+start]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
@@ -173,24 +233,45 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
+    doAssert skipUntil("Hello World", {'W'}, 0) == 6
+    doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
   while start+result < s.len and s[result+start] notin until: inc(result)
 
 proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", 'o', 0) == 4
+    doAssert skipUntil("Hello World", 'o', 4) == 0
+    doAssert skipUntil("Hello World", 'W', 0) == 6
+    doAssert skipUntil("Hello World", 'w', 0) == 11
   while start+result < s.len and s[result+start] != until: inc(result)
 
 proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
   ## Skips all characters while one char from the set `token` is found.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipWhile("Hello World", {'H', 'e'}) == 2
+    doAssert skipWhile("Hello World", {'e'}) == 0
+    doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
   while start+result < s.len and s[result+start] in toSkip: inc(result)
 
 proc parseUntil*(s: string, token: var string, until: set[char],
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters notin `until`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
+    doAssert myToken == "lo "
   var i = start
   while i < s.len and s[i] notin until: inc(i)
   result = i-start
@@ -198,9 +279,17 @@ proc parseUntil*(s: string, token: var string, until: set[char],
 
 proc parseUntil*(s: string, token: var string, until: char,
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that is not the `until` character.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, 'W') == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, 'o') == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
+    doAssert myToken == "ll"
   var i = start
   while i < s.len and s[i] != until: inc(i)
   result = i-start
@@ -208,9 +297,15 @@ proc parseUntil*(s: string, token: var string, until: char,
 
 proc parseUntil*(s: string, token: var string, until: string,
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that comes before the `until`  token.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, "Wor") == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4
+    doAssert myToken == "llo "
   if until.len == 0:
     token.setLen(0)
     return 0
@@ -227,9 +322,15 @@ proc parseUntil*(s: string, token: var string, until: string,
 
 proc parseWhile*(s: string, token: var string, validChars: set[char],
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters in `validChars`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0
+    doAssert myToken.len() == 0
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
+    doAssert myToken == "Wor"
   var i = start
   while i < s.len and s[i] in validChars: inc(i)
   result = i-start
@@ -238,12 +339,21 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
 proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
   ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
+  runnableExamples:
+    doAssert captureBetween("Hello World", 'e') == "llo World"
+    doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
+    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
   var i = skipUntil(s, first, start)+1+start
   result = ""
   discard s.parseUntil(result, if second == '\0': first else: second, i)
 
-{.push overflowChecks: on.}
-# this must be compiled with overflow checking turned on:
+proc integerOutOfRangeError() {.noinline.} =
+  raise newException(ValueError, "Parsed integer outside of valid range")
+
+# See #6752
+when defined(js):
+  {.push overflowChecks: off.}
+
 proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
   var
     sign: BiggestInt = -1
@@ -256,48 +366,67 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
     while i < s.len and s[i] in {'0'..'9'}:
-      b = b * 10 - (ord(s[i]) - ord('0'))
+      let c = ord(s[i]) - ord('0')
+      if b >= (low(BiggestInt) + c) div 10:
+        b = b * 10 - c
+      else:
+        integerOutOfRangeError()
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    b = b * sign
-    result = i - start
-{.pop.} # overflowChecks
+    if sign == -1 and b == low(BiggestInt):
+      integerOutOfRangeError()
+    else:
+      b = b * sign
+      result = i - start
+
+when defined(js):
+  {.pop.} # overflowChecks: off
 
 proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
-  rtl, extern: "npuParseBiggestInt", noSideEffect.} =
-  ## parses an integer starting at `start` and stores the value into `number`.
+  rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `OverflowError` is raised if an overflow occurs.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestInt
+    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert res == 9223372036854775807
   var res: BiggestInt
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
   result = rawParseInt(s, res, start)
-  number = res
+  if result != 0:
+    number = res
 
 proc parseInt*(s: string, number: var int, start = 0): int {.
-  rtl, extern: "npuParseInt", noSideEffect.} =
-  ## parses an integer starting at `start` and stores the value into `number`.
+  rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `OverflowError` is raised if an overflow occurs.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("2019", res, 0) == 4
+    doAssert res == 2019
+    doAssert parseInt("2019", res, 2) == 2
+    doAssert res == 19
   var res: BiggestInt
   result = parseBiggestInt(s, res, start)
-  if (sizeof(int) <= 4) and
-      ((res < low(int)) or (res > high(int))):
-    raise newException(OverflowError, "overflow")
-  elif result != 0:
+  when sizeof(int) <= 4:
+    if res < low(int) or res > high(int):
+      integerOutOfRangeError()
+  if result != 0:
     number = int(res)
 
-proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
-  ## parses a natural number into ``b``. This cannot raise an overflow
+proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+  raises: [].}=
+  ## Parses a natural number into ``b``. This cannot raise an overflow
   ## error. ``high(int)`` is returned for an overflow.
   ## The number of processed character is returned.
   ## This is usually what you really want to use instead of `parseInt`:idx:.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var res = 0
-  ##   discard parseSaturatedNatural("848", res)
-  ##   doAssert res == 848
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
   var i = start
   if i < s.len and s[i] == '+': inc(i)
   if i < s.len and s[i] in {'0'..'9'}:
@@ -312,12 +441,13 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     result = i - start
 
-# overflowChecks doesn't work with BiggestUInt
 proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
   var
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
     i = start
+  if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
+    integerOutOfRangeError()
   if i < s.len and s[i] == '+': inc(i) # Allow
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
@@ -325,56 +455,75 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
       prev = res
       res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
       if prev > res:
-        return 0 # overflowChecks emulation
+        integerOutOfRangeError()
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = res
     result = i - start
 
 proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
-  rtl, extern: "npuParseBiggestUInt", noSideEffect.} =
-  ## parses an unsigned integer starting at `start` and stores the value
+  rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer
-  ## or overflow detected.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestUInt
+    doAssert parseBiggestUInt("12", res, 0) == 2
+    doAssert res == 12
+    doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
+    doAssert res == 1111111111111111111'u64
   var res: BiggestUInt
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
   result = rawParseUInt(s, res, start)
-  number = res
+  if result != 0:
+    number = res
 
 proc parseUInt*(s: string, number: var uint, start = 0): int {.
-  rtl, extern: "npuParseUInt", noSideEffect.} =
-  ## parses an unsigned integer starting at `start` and stores the value
+  rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer or
-  ## overflow detected.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: uint
+    doAssert parseUInt("3450", res) == 4
+    doAssert res == 3450
+    doAssert parseUInt("3450", res, 2) == 2
+    doAssert res == 50
   var res: BiggestUInt
   result = parseBiggestUInt(s, res, start)
   when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
     if res > 0xFFFF_FFFF'u64:
-      raise newException(OverflowError, "overflow")
+      integerOutOfRangeError()
   if result != 0:
     number = uint(res)
 
 proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
   magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if a parsing error
   ## occurred.
 
 proc parseFloat*(s: string, number: var float, start = 0): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there occurred a parsing
   ## error.
+  runnableExamples:
+    var res: float
+    doAssert parseFloat("32", res, 0) == 2
+    doAssert res == 32.0
+    doAssert parseFloat("32.57", res, 0) == 5
+    doAssert res == 32.57
+    doAssert parseFloat("32.57", res, 3) == 2
+    doAssert res == 57.00
   var bf: BiggestFloat
   result = parseBiggestFloat(s, bf, start)
   if result != 0:
     number = bf
 
 type
-  InterpolatedKind* = enum   ## describes for `interpolatedFragments`
+  InterpolatedKind* = enum   ## Describes for `interpolatedFragments`
                              ## which part of the interpolated string is
                              ## yielded; for example in "str$$$var${expr}"
     ikStr,                   ## ``str`` part of the interpolated string
@@ -490,4 +639,8 @@ when isMainModule:
   doAssert(parseSaturatedNatural("1_000_000", value) == 9)
   doAssert value == 1_000_000
 
+  var i64Value: int64
+  discard parseBiggestInt("9223372036854775807", i64Value)
+  doAssert i64Value == 9223372036854775807
+
 {.pop.}
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 0967f7983..953c5cdde 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -147,7 +147,7 @@ an HTML document contains.
 ]##
 
 import
-  hashes, strutils, lexbase, streams, unicode
+  strutils, lexbase, streams, unicode
 
 # the parser treats ``<br />`` as ``<br></br>``
 
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 26e6740ea..378ca6f87 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -158,10 +158,14 @@ proc rand*[T](x: HSlice[T, T]): T =
   result = rand(state, x)
 
 proc rand*[T](r: var Rand; a: openArray[T]): T {.deprecated.} =
-  ## returns a random element from the openarray `a`.
+  ## Returns a random element from the openarray `a`.
   ## **Deprecated since v0.20.0:** use ``sample`` instead.
   result = a[rand(r, a.low..a.high)]
 
+proc rand*[T: SomeInteger](t: typedesc[T]): T =
+  ## Returns a random integer in the range `low(T)..high(T)`.
+  result = cast[T](state.next)
+
 proc rand*[T](a: openArray[T]): T {.deprecated.} =
   ## returns a random element from the openarray `a`.
   ## **Deprecated since v0.20.0:** use ``sample`` instead.
@@ -175,27 +179,26 @@ proc sample*[T](a: openArray[T]): T =
   ## returns a random element from openArray ``a`` using non-thread-safe state.
   result = a[rand(a.low..a.high)]
 
-proc sample*[T, U](r: var Rand; a: openArray[T], w: openArray[U], n=1): seq[T] =
-  ## Return a sample (with replacement) of size ``n`` from elements of ``a``
-  ## according to convertible-to-``float``, not necessarily normalized, and
-  ## non-negative weights ``w``.  Uses state in ``r``.  Must have sum ``w > 0.0``.
-  assert(w.len == a.len)
-  var cdf = newSeq[float](a.len)   # The *unnormalized* CDF
-  var tot = 0.0                    # Unnormalized is fine if we sample up to tot
-  for i, w in w:
-    assert(w >= 0)
-    tot += float(w)
-    cdf[i] = tot
-  assert(tot > 0.0)                # Need at least one non-zero weight
-  for i in 0 ..< n:
-    result.add(a[cdf.upperBound(r.rand(tot))])
-
-proc sample*[T, U](a: openArray[T], w: openArray[U], n=1): seq[T] =
-  ## Return a sample (with replacement) of size ``n`` from elements of ``a``
-  ## according to convertible-to-``float``, not necessarily normalized, and
-  ## non-negative weights ``w``.  Uses default non-thread-safe state.
-  state.sample(a, w, n)
-
+proc sample*[T, U](r: var Rand; a: openArray[T], cdf: openArray[U]): T =
+  ## Sample one element from openArray ``a`` when it has cumulative distribution
+  ## function (CDF) ``cdf`` (not necessarily normalized, any type of elements
+  ## convertible to ``float``). Uses state in ``r``. E.g.:
+  ##
+  ## .. code-block:: nim
+  ##   let val = [ "a", "b", "c", "d" ]  # some values
+  ##   var cnt = [1, 2, 3, 4]            # histogram of counts
+  ##   echo r.sample(val, cnt.cumsummed) # echo a sample
+  assert(cdf.len == a.len)              # Two basic sanity checks.
+  assert(float(cdf[^1]) > 0.0)
+  #While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get
+  #awfully expensive even in debugging modes.
+  let u = r.rand(float(cdf[^1]))
+  a[cdf.upperBound(U(u))]
+
+proc sample*[T, U](a: openArray[T], cdf: openArray[U]): T =
+  ## Like ``sample(var Rand; openArray[T], openArray[U])``, but uses default
+  ## non-thread-safe state.
+  state.sample(a, cdf)
 
 proc initRand*(seed: int64): Rand =
   ## Creates a new ``Rand`` state from ``seed``.
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
deleted file mode 100644
index e36803823..000000000
--- a/lib/pure/scgi.nim
+++ /dev/null
@@ -1,295 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements helper procs for SCGI applications. Example:
-##
-## .. code-block:: Nim
-##
-##    import strtabs, sockets, scgi
-##
-##    var counter = 0
-##    proc handleRequest(client: Socket, input: string,
-##                       headers: StringTableRef): bool {.procvar.} =
-##      inc(counter)
-##      client.writeStatusOkTextContent()
-##      client.send("Hello for the $#th time." % $counter & "\c\L")
-##      return false # do not stop processing
-##
-##    run(handleRequest)
-##
-## **Warning:** The API of this module is unstable, and therefore is subject
-## to change.
-##
-## **Warning:** This module only supports the old asynchronous interface.
-## You may wish to use the `asynchttpserver <asynchttpserver.html>`_
-## instead for web applications.
-
-include "system/inclrtl"
-
-import sockets, strutils, os, strtabs, asyncio
-
-type
-  ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
-
-proc raiseScgiError*(msg: string) {.noreturn.} =
-  ## raises an ScgiError exception with message `msg`.
-  var e: ref ScgiError
-  new(e)
-  e.msg = msg
-  raise e
-
-proc parseWord(inp: string, outp: var string, start: int): int =
-  result = start
-  while inp[result] != '\0': inc(result)
-  outp = substr(inp, start, result-1)
-
-proc parseHeaders(s: string, L: int): StringTableRef =
-  result = newStringTable()
-  var i = 0
-  while i < L:
-    var key, val: string
-    i = parseWord(s, key, i)+1
-    i = parseWord(s, val, i)+1
-    result[key] = val
-  if s[i] == ',': inc(i)
-  else: raiseScgiError("',' after netstring expected")
-
-proc recvChar(s: Socket): char =
-  var c: char
-  if recv(s, addr(c), sizeof(c)) == sizeof(c):
-    result = c
-
-type
-  ScgiState* = object of RootObj ## SCGI state object
-    server: Socket
-    bufLen: int
-    client*: Socket ## the client socket to send data to
-    headers*: StringTableRef ## the parsed headers
-    input*: string  ## the input buffer
-
-
-  # Async
-
-  ClientMode = enum
-    ClientReadChar, ClientReadHeaders, ClientReadContent
-
-  AsyncClient = ref object
-    c: AsyncSocket
-    mode: ClientMode
-    dataLen: int
-    headers: StringTableRef ## the parsed headers
-    input: string  ## the input buffer
-
-  AsyncScgiStateObj = object
-    handleRequest: proc (client: AsyncSocket,
-                         input: string,
-                         headers: StringTableRef) {.closure, gcsafe.}
-    asyncServer: AsyncSocket
-    disp: Dispatcher
-  AsyncScgiState* = ref AsyncScgiStateObj
-
-proc recvBuffer(s: var ScgiState, L: int) =
-  if L > s.bufLen:
-    s.bufLen = L
-    s.input = newString(L)
-  if L > 0 and recv(s.client, cstring(s.input), L) != L:
-    raiseScgiError("could not read all data")
-  setLen(s.input, L)
-
-proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
-           reuseAddr = false) =
-  ## opens a connection.
-  s.bufLen = 4000
-  s.input = newString(s.bufLen) # will be reused
-
-  s.server = socket()
-  if s.server == invalidSocket: raiseOSError(osLastError())
-  new(s.client) # Initialise s.client for `next`
-  if s.server == invalidSocket: raiseScgiError("could not open socket")
-  #s.server.connect(connectionName, port)
-  if reuseAddr:
-    s.server.setSockOpt(OptReuseAddr, true)
-  bindAddr(s.server, port, address)
-  listen(s.server)
-
-proc close*(s: var ScgiState) =
-  ## closes the connection.
-  s.server.close()
-
-proc next*(s: var ScgiState, timeout: int = -1): bool =
-  ## proceed to the first/next request. Waits ``timeout`` milliseconds for a
-  ## request, if ``timeout`` is `-1` then this function will never time out.
-  ## Returns `true` if a new request has been processed.
-  var rsocks = @[s.server]
-  if select(rsocks, timeout) == 1 and rsocks.len == 1:
-    new(s.client)
-    accept(s.server, s.client)
-    var L = 0
-    while true:
-      var d = s.client.recvChar()
-      if d == '\0':
-        s.client.close()
-        return false
-      if d notin strutils.Digits:
-        if d != ':': raiseScgiError("':' after length expected")
-        break
-      L = L * 10 + ord(d) - ord('0')
-    recvBuffer(s, L+1)
-    s.headers = parseHeaders(s.input, L)
-    if s.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected")
-    L = parseInt(s.headers.getOrDefault("CONTENT_LENGTH"))
-    recvBuffer(s, L)
-    return true
-
-proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
-  ## sends the following string to the socket `c`::
-  ##
-  ##   Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
-  ##
-  ## You should send this before sending your HTML page, for example.
-  c.send("Status: 200 OK\r\L" &
-         "Content-Type: $1\r\L\r\L" % contentType)
-
-proc run*(handleRequest: proc (client: Socket, input: string,
-                               headers: StringTableRef): bool {.nimcall,gcsafe.},
-          port = Port(4000)) =
-  ## encapsulates the SCGI object and main loop.
-  var s: ScgiState
-  s.open(port)
-  var stop = false
-  while not stop:
-    if next(s):
-      stop = handleRequest(s.client, s.input, s.headers)
-      s.client.close()
-  s.close()
-
-# -- AsyncIO start
-
-proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
-  result = ReadPartialLine
-  var data = ""
-  if L < 1:
-    raiseScgiError("Cannot read negative or zero length: " & $L)
-  let ret = recvAsync(client.c, data, L)
-  if ret == 0 and data == "":
-    client.c.close()
-    return ReadDisconnected
-  if ret == -1:
-    return ReadNone # No more data available
-  client.input.add(data)
-  if ret == L:
-    return ReadFullLine
-
-proc checkCloseSocket(client: AsyncClient) =
-  if not client.c.isClosed:
-    if client.c.isSendDataBuffered:
-      client.c.setHandleWrite do (s: AsyncSocket):
-        if not s.isClosed and not s.isSendDataBuffered:
-          s.close()
-          s.delHandleWrite()
-    else: client.c.close()
-
-proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
-  case client.mode
-  of ClientReadChar:
-    while true:
-      var d = ""
-      let ret = client.c.recvAsync(d, 1)
-      if d == "" and ret == 0:
-        # Disconnected
-        client.c.close()
-        return
-      if ret == -1:
-        return # No more data available
-      if d[0] notin strutils.Digits:
-        if d[0] != ':': raiseScgiError("':' after length expected")
-        break
-      client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
-    client.mode = ClientReadHeaders
-    handleClientRead(client, s) # Allow progression
-  of ClientReadHeaders:
-    let ret = recvBufferAsync(client, (client.dataLen+1)-client.input.len)
-    case ret
-    of ReadFullLine:
-      client.headers = parseHeaders(client.input, client.input.len-1)
-      if client.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected")
-      client.input = "" # For next part
-
-      let contentLen = parseInt(client.headers.getOrDefault("CONTENT_LENGTH"))
-      if contentLen > 0:
-        client.mode = ClientReadContent
-      else:
-        s.handleRequest(client.c, client.input, client.headers)
-        checkCloseSocket(client)
-    of ReadPartialLine, ReadDisconnected, ReadNone: return
-  of ClientReadContent:
-    let L = parseInt(client.headers.getOrDefault("CONTENT_LENGTH")) -
-               client.input.len
-    if L > 0:
-      let ret = recvBufferAsync(client, L)
-      case ret
-      of ReadFullLine:
-        s.handleRequest(client.c, client.input, client.headers)
-        checkCloseSocket(client)
-      of ReadPartialLine, ReadDisconnected, ReadNone: return
-    else:
-      s.handleRequest(client.c, client.input, client.headers)
-      checkCloseSocket(client)
-
-proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
-  var client: AsyncSocket
-  new(client)
-  accept(s.asyncServer, client)
-  var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
-                                 headers: newStringTable(), input: "")
-  client.handleRead =
-    proc (sock: AsyncSocket) =
-      handleClientRead(asyncClient, s)
-  s.disp.register(client)
-
-proc open*(handleRequest: proc (client: AsyncSocket,
-                                input: string, headers: StringTableRef) {.
-                                closure, gcsafe.},
-           port = Port(4000), address = "127.0.0.1",
-           reuseAddr = false): AsyncScgiState =
-  ## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
-  ##
-  ## After the execution of ``handleRequest`` the client socket will be closed
-  ## automatically unless it has already been closed.
-  var cres: AsyncScgiState
-  new(cres)
-  cres.asyncServer = asyncSocket()
-  cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
-  if reuseAddr:
-    cres.asyncServer.setSockOpt(OptReuseAddr, true)
-  bindAddr(cres.asyncServer, port, address)
-  listen(cres.asyncServer)
-  cres.handleRequest = handleRequest
-  result = cres
-
-proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
-  ## Registers ``s`` with dispatcher ``d``.
-  result = d.register(s.asyncServer)
-  s.disp = d
-
-proc close*(s: AsyncScgiState) =
-  ## Closes the ``AsyncScgiState``.
-  s.asyncServer.close()
-
-when false:
-  var counter = 0
-  proc handleRequest(client: Socket, input: string,
-                     headers: StringTableRef): bool {.procvar.} =
-    inc(counter)
-    client.writeStatusOkTextContent()
-    client.send("Hello for the $#th time." % $counter & "\c\L")
-    return false # do not stop processing
-
-  run(handleRequest)
-
diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim
deleted file mode 100644
index c6cde599a..000000000
--- a/lib/pure/securehash.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-## This module is a deprecated alias for the ``sha1`` module.
-{.deprecated.}
-
-include "../std/sha1"
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index e4c2b2124..b9c834127 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -239,6 +239,9 @@ else:
     proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
       result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
 
+    proc reallocSharedArray[T](sa: ptr SharedArray[T], nsize: int): ptr SharedArray[T] =
+      result = cast[ptr SharedArray[T]](reallocShared(sa, sizeof(T) * nsize))
+
     proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
       deallocShared(cast[pointer](sa))
   type
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
index ce32108c2..b5c8d3784 100644
--- a/lib/pure/stats.nim
+++ b/lib/pure/stats.nim
@@ -224,7 +224,7 @@ proc standardDeviation*[T](x: openArray[T]): float =
   result = rs.standardDeviation()
 
 proc standardDeviationS*[T](x: openArray[T]): float =
-  ## computes the sanple standardDeviation of `x`
+  ## computes the sample standardDeviation of `x`
   var rs: RunningStat
   rs.push(x)
   result = rs.standardDeviationS()
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index b0ac62525..0f65d6c0e 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -45,17 +45,22 @@ type
                                  ## here shouldn't be used directly. They are
                                  ## accessible so that a stream implementation
                                  ## can override them.
-    closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
-    atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
-    setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
-    getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
-    readDataImpl*: proc (s: Stream, buffer: pointer,
-                         bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
-    peekDataImpl*: proc (s: Stream, buffer: pointer,
-                         bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
-    writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
-      tags: [WriteIOEffect], gcsafe.}
-    flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
+    closeImpl*: proc (s: Stream)
+      {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
+    atEndImpl*: proc (s: Stream): bool
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+    setPositionImpl*: proc (s: Stream, pos: int)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+    getPositionImpl*: proc (s: Stream): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+    readDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+    peekDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+    writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
+    flushImpl*: proc (s: Stream)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
 
 proc flush*(s: Stream) =
   ## flushes the buffers that the stream `s` might use.
@@ -65,10 +70,6 @@ proc close*(s: Stream) =
   ## closes the stream `s`.
   if not isNil(s.closeImpl): s.closeImpl(s)
 
-proc close*(s, unused: Stream) {.deprecated.} =
-  ## closes the stream `s`.
-  s.closeImpl(s)
-
 proc atEnd*(s: Stream): bool =
   ## checks if more data can be read from `f`. Returns true if all data has
   ## been read.
@@ -111,12 +112,6 @@ proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
   ## to the stream `s`.
   s.writeDataImpl(s, buffer, bufLen)
 
-proc writeData*(s, unused: Stream, buffer: pointer,
-                bufLen: int) {.deprecated.} =
-  ## low level proc that writes an untyped `buffer` of `bufLen` size
-  ## to the stream `s`.
-  s.writeDataImpl(s, buffer, bufLen)
-
 proc write*[T](s: Stream, x: T) =
   ## generic write procedure. Writes `x` to the stream `s`. Implementation:
   ##
@@ -146,12 +141,12 @@ proc writeLine*(s: Stream, args: varargs[string, `$`]) =
   for str in args: write(s, str)
   write(s, "\n")
 
-proc read[T](s: Stream, result: var T) =
+proc read*[T](s: Stream, result: var T) =
   ## generic read procedure. Reads `result` from the stream `s`.
   if readData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
-proc peek[T](s: Stream, result: var T) =
+proc peek*[T](s: Stream, result: var T) =
   ## generic peek procedure. Peeks `result` from the stream `s`.
   if peekData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
@@ -271,7 +266,7 @@ proc peekStr*(s: Stream, length: int): TaintedString =
 proc readLine*(s: Stream, line: var TaintedString): bool =
   ## reads a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
-  ## A line of text may be delimited by ```LF`` or ``CRLF``.
+  ## A line of text may be delimited by ``LF`` or ``CRLF``.
   ## The newline character(s) are not part of the returned string.
   ## Returns ``false`` if the end of the file has been reached, ``true``
   ## otherwise. If ``false`` is returned `line` contains no new data.
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index d8a23286a..377178f92 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -12,6 +12,34 @@
 ## style-insensitive mode. An efficient string substitution operator  ``%``
 ## for the string table is also provided.
 
+runnableExamples:
+
+  var t = newStringTable()
+  t["name"] = "John"
+  t["city"] = "Monaco"
+  doAssert t.len == 2
+  doAssert t.hasKey "name"
+  doAssert "name" in t
+
+## String tables can be created from a table constructor:
+
+runnableExamples:
+  var t = {"name": "John", "city": "Monaco"}.newStringTable
+
+
+## When using the style insensitive mode ``modeStyleInsensitive``, 
+## all letters are compared case insensitively within the ASCII range
+## and underscores are ignored.
+
+runnableExamples:
+
+  var x = newStringTable(modeStyleInsensitive)
+  x["first_name"] = "John"
+  x["LastName"] = "Doe"
+
+  doAssert x["firstName"] == "John"
+  doAssert x["last_name"] == "Doe"
+
 import
   hashes, strutils
 
@@ -89,7 +117,7 @@ proc mustRehash(length, counter: int): bool =
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
 proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = ((5 * h) + 1) and maxHash
+  result = (h + 1) and maxHash
 
 proc rawGet(t: StringTableRef, key: string): int =
   var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
@@ -214,6 +242,9 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
 proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
   rtlFunc, extern: "nstFormat".} =
   ## The `%` operator for string tables.
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
   const
     PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
   result = ""
@@ -241,6 +272,32 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
       add(result, f[i])
       inc(i)
 
+proc del*(t: StringTableRef, key: string) =
+  ## Removes `key` from `t`.
+  # Impl adapted from `tableimpl.delImplIdx`
+  var i = rawGet(t, key)
+  let msk = high(t.data)
+  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 r = j         # though may be adaptable to other simple sequences.
+        t.data[i].hasValue = false              # mark current EMPTY
+        t.data[i].key = ""
+        t.data[i].val = ""
+        while true:
+          i = (i + 1) and msk      # increment mod table size
+          if not t.data[i].hasValue:   # end of collision cluster; So all done
+            break outer
+          r = t.myhash(t.data[i].key) and msk    # "home" location of key@i
+          if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
+            break
+        when defined(js):
+          t.data[j] = t.data[i]
+        else:
+          shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
+
 proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
   ## The `$` operator for string tables.
   if t.len == 0:
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 00469f9e5..8385eb24e 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -7,11 +7,70 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains various string utility routines.
-## See the module `re <re.html>`_ for regular expression support.
-## See the module `pegs <pegs.html>`_ for PEG support.
+## The system module defines several common functions for working with strings,
+## such as:
+## * ``$`` for converting other data-types to strings
+## * ``&`` for string concatenation
+## * ``add`` for adding a new character or a string to the existing one
+## * ``in`` (alias for ``contains``) and ``notin`` for checking if a character
+##   is in a string
+##
+## This module builds upon that, providing additional functionality in form of
+## procedures, iterators and templates for strings.
+##
+## .. code-block::
+##   import strutils
+##
+##   let
+##     numbers = @[867, 5309]
+##     multiLineString = "first line\nsecond line\nthird line"
+##
+##   let jenny = numbers.join("-")
+##   assert jenny == "867-5309"
+##
+##   assert splitLines(multiLineString) ==
+##          @["first line", "second line", "third line"]
+##   assert split(multiLineString) == @["first", "line", "second",
+##                                      "line", "third", "line"]
+##   assert indent(multiLineString, 4) ==
+##          "    first line\n    second line\n    third line"
+##   assert 'z'.repeat(5) == "zzzzz"
+##
+## The chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procedures-method-call-syntax>`_:
+##
+## .. code-block::
+##   import strutils
+##   from sequtils import map
+##
+##   let jenny = "867-5309"
+##   assert jenny.split('-').map(parseInt) == @[867, 5309]
+##
+##   assert "Beetlejuice".indent(1).repeat(3).strip ==
+##          "Beetlejuice Beetlejuice Beetlejuice"
+##
 ## This module is available for the `JavaScript target
 ## <backends.html#the-javascript-target>`_.
+##
+## ----
+##
+## **See also:**
+## * `strformat module<strformat.html>`_ for string interpolation and formatting
+## * `unicode module<unicode.html>`_ for Unicode UTF-8 handling
+## * `sequtils module<collections/sequtils.html>`_ for operations on container
+##   types (including strings)
+## * `parseutils module<parseutils.html>`_ for lower-level parsing of tokens,
+##   numbers, identifiers, etc.
+## * `parseopt module<parseopt.html>`_ for command-line parsing
+## * `strtabs module<strtabs.html>`_ for efficient hash tables
+##   (dictionaries, in some programming languages) mapping from strings to strings
+## * `pegs module<pegs.html>`_ for PEG (Parsing Expression Grammar) support
+## * `ropes module<ropes.html>`_ for rope data type, which can represent very
+##   long strings efficiently
+## * `re module<re.html>`_ for regular expression (regex) support
+## * `strscans<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which offer
+##   easier substring extraction than regular expressions
+
 
 import parseutils
 from math import pow, floor, log10
@@ -38,7 +97,8 @@ else:
 
 const
   Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
-    ## All the characters that count as whitespace.
+    ## All the characters that count as whitespace (space, tab, vertical tab,
+    ## carriage return, new line, form feed)
 
   Letters* = {'A'..'Z', 'a'..'z'}
     ## the set of letters
@@ -56,14 +116,15 @@ const
     ## the set of characters an identifier can start with
 
   NewLines* = {'\13', '\10'}
-    ## the set of characters a newline terminator can start with
+    ## the set of characters a newline terminator can start with (carriage
+    ## return, line feed)
 
   AllChars* = {'\x00'..'\xFF'}
     ## A set with all the possible characters.
     ##
     ## Not very useful by its own, you can use it to create *inverted* sets to
-    ## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
-    ## characters in strings.  Example:
+    ## make the `find proc<#find,string,set[char],Natural,int>`_
+    ## find **invalid** characters in strings. Example:
     ##
     ## .. code-block:: nim
     ##   let invalid = AllChars - Digits
@@ -72,9 +133,10 @@ const
 
 proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsAlphaAsciiChar".}=
-  ## Checks whether or not `c` is alphabetical.
+  ## Checks whether or not character `c` is alphabetical.
   ##
   ## This checks a-z, A-Z ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
   runnableExamples:
     doAssert isAlphaAscii('e') == true
     doAssert isAlphaAscii('E') == true
@@ -108,6 +170,7 @@ proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
   runnableExamples:
     doAssert isSpaceAscii('n') == false
     doAssert isSpaceAscii(' ') == true
+    doAssert isSpaceAscii('\t') == true
   return c in Whitespace
 
 proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
@@ -115,6 +178,10 @@ proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
   ## Checks whether or not `c` is a lower case character.
   ##
   ## This checks ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toLowerAscii proc<#toLowerAscii,char>`_
   runnableExamples:
     doAssert isLowerAscii('e') == true
     doAssert isLowerAscii('E') == false
@@ -126,138 +193,28 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
   ## Checks whether or not `c` is an upper case character.
   ##
   ## This checks ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toUpperAscii proc<#toUpperAscii,char>`_
   runnableExamples:
     doAssert isUpperAscii('e') == false
     doAssert isUpperAscii('E') == true
     doAssert isUpperAscii('7') == false
   return c in {'A'..'Z'}
 
-template isImpl(call) =
-  if s.len == 0: return false
-  result = true
-  for c in s:
-    if not call(c): return false
-
-proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaAsciiStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is alphabetical.
-  ##
-  ## This checks a-z, A-Z ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## alphabetic and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isAlphaAscii("fooBar") == true
-    doAssert isAlphaAscii("fooBar1") == false
-    doAssert isAlphaAscii("foo Bar") == false
-  isImpl isAlphaAscii
-
-proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is alphanumeric.
-  ##
-  ## This checks a-z, A-Z, 0-9 ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## alpanumeric and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isAlphaNumeric("fooBar") == true
-    doAssert isAlphaNumeric("fooBar") == true
-    doAssert isAlphaNumeric("foo Bar") == false
-  isImpl isAlphaNumeric
-
-proc isDigit*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is a numeric value.
-  ##
-  ## This checks 0-9 ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## numeric and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isDigit("1908") == true
-    doAssert isDigit("fooBar1") == false
-  isImpl isDigit
-
-proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is completely whitespace.
-  ##
-  ## Returns true if all characters in `s` are whitespace
-  ## characters and there is at least one character in `s`.
-  runnableExamples:
-    doAssert isSpaceAscii("   ") == true
-    doAssert isSpaceAscii("") == false
-  isImpl isSpaceAscii
-
-template isCaseImpl(s, charProc, skipNonAlpha) =
-  var hasAtleastOneAlphaChar = false
-  if s.len == 0: return false
-  for c in s:
-    if skipNonAlpha:
-      var charIsAlpha = c.isAlphaAscii()
-      if not hasAtleastOneAlphaChar:
-        hasAtleastOneAlphaChar = charIsAlpha
-      if charIsAlpha and (not charProc(c)):
-        return false
-    else:
-      if not charProc(c):
-        return false
-  return if skipNonAlpha: hasAtleastOneAlphaChar else: true
-
-proc isLowerAscii*(s: string, skipNonAlpha: bool): bool {.
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether ``s`` is lower case.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## characters in ``s`` are lower case.  Returns false if none of the
-  ## characters in ``s`` are alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all characters
-  ## in ``s`` are alphabetical and lower case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  runnableExamples:
-    doAssert isLowerAscii("1foobar", false) == false
-    doAssert isLowerAscii("1foobar", true) == true
-    doAssert isLowerAscii("1fooBar", true) == false
-  isCaseImpl(s, isLowerAscii, skipNonAlpha)
-
-proc isUpperAscii*(s: string, skipNonAlpha: bool): bool {.
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether ``s`` is upper case.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## characters in ``s`` are upper case.  Returns false if none of the
-  ## characters in ``s`` are alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all characters
-  ## in ``s`` are alphabetical and upper case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  runnableExamples:
-    doAssert isUpperAscii("1FOO", false) == false
-    doAssert isUpperAscii("1FOO", true) == true
-    doAssert isUpperAscii("1Foo", true) == false
-  isCaseImpl(s, isUpperAscii, skipNonAlpha)
 
 proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiChar".} =
-  ## Returns the lower case version of ``c``.
+  ## Returns the lower case version of character ``c``.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
   ## <unicode.html#toLower>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `isLowerAscii proc<#isLowerAscii,char>`_
+  ## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string
   runnableExamples:
     doAssert toLowerAscii('A') == 'a'
     doAssert toLowerAscii('e') == 'e'
@@ -273,22 +230,30 @@ template toImpl(call) =
 
 proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiStr".} =
-  ## Converts `s` into lower case.
+  ## Converts string `s` into lower case.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
   ## <unicode.html#toLower>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `normalize proc<#normalize,string>`_
   runnableExamples:
     doAssert toLowerAscii("FooBar!") == "foobar!"
   toImpl toLowerAscii
 
 proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToUpperAsciiChar".} =
-  ## Converts `c` into upper case.
+  ## Converts character `c` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
   ## <unicode.html#toUpper>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `isLowerAscii proc<#isLowerAscii,char>`_
+  ## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string
+  ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
   runnableExamples:
     doAssert toUpperAscii('a') == 'A'
     doAssert toUpperAscii('E') == 'E'
@@ -299,20 +264,27 @@ proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
 
 proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuToUpperAsciiStr".} =
-  ## Converts `s` into upper case.
+  ## Converts string `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
   ## <unicode.html#toUpper>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
   runnableExamples:
     doAssert toUpperAscii("FooBar!") == "FOOBAR!"
   toImpl toUpperAscii
 
 proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuCapitalizeAscii".} =
-  ## Converts the first character of `s` into upper case.
+  ## Converts the first character of string `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toUpperAscii proc<#toUpperAscii,char>`_
   runnableExamples:
     doAssert capitalizeAscii("foo") == "Foo"
     doAssert capitalizeAscii("-bar") == "-bar"
@@ -325,6 +297,9 @@ proc normalize*(s: string): string {.noSideEffect, procvar,
   ##
   ## That means to convert it to lower case and remove any '_'. This
   ## should NOT be used to normalize Nim identifier names.
+  ##
+  ## See also:
+  ## * `toLowerAscii proc<#toLowerAscii,string>`_
   runnableExamples:
     doAssert normalize("Foo_bar") == "foobar"
     doAssert normalize("Foo Bar") == "foo bar"
@@ -343,9 +318,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
   rtl, extern: "nsuCmpIgnoreCase", procvar.} =
   ## Compares two strings in a case insensitive manner. Returns:
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
+  ## | 0 if a == b
+  ## | < 0 if a < b
+  ## | > 0 if a > b
   runnableExamples:
     doAssert cmpIgnoreCase("FooBar", "foobar") == 0
     doAssert cmpIgnoreCase("bar", "Foo") < 0
@@ -365,12 +340,14 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
   rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
   ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
   ## is just optimized to not allocate temporary strings. This should
-  ## NOT be used to compare Nim identifier names. use `macros.eqIdent`
-  ## for that. Returns:
+  ## NOT be used to compare Nim identifier names.
+  ## Use `macros.eqIdent<macros.html#eqIdent,string,string>`_ for that.
+  ##
+  ## Returns:
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
+  ## | 0 if a == b
+  ## | < 0 if a < b
+  ## | > 0 if a > b
   runnableExamples:
     doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
     doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
@@ -394,51 +371,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
     inc i
     inc j
 
-proc strip*(s: string, leading = true, trailing = true,
-            chars: set[char] = Whitespace): string
-  {.noSideEffect, rtl, extern: "nsuStrip".} =
-  ## Strips leading or trailing `chars` from `s` and returns
-  ## the resulting string.
-  ##
-  ## If `leading` is true, leading `chars` are stripped.
-  ## If `trailing` is true, trailing `chars` are stripped.
-  ## If both are false, the string is returned unchanged.
-  runnableExamples:
-    doAssert " vhellov ".strip().strip(trailing = false, chars = {'v'}) == "hellov"
-  var
-    first = 0
-    last = len(s)-1
-  if leading:
-    while first <= last and s[first] in chars: inc(first)
-  if trailing:
-    while last >= 0 and s[last] in chars: dec(last)
-  result = substr(s, first, last)
-
-proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
-  ## Converts a character `c` to its octal representation.
-  ##
-  ## The resulting string may not have a leading zero. Its length is always
-  ## exactly 3.
-  runnableExamples:
-    doAssert toOctal('!') == "041"
-  result = newString(3)
-  var val = ord(c)
-  for i in countdown(2, 0):
-    result[i] = chr(val mod 8 + ord('0'))
-    val = val div 8
 
-proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
-                                      extern: "nsuIsNilOrEmpty",
-                                      deprecated: "use 'x.len == 0' instead".} =
-  ## Checks if `s` is nil or empty.
-  result = len(s) == 0
-
-proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
-  ## Checks if `s` is nil or consists entirely of whitespace characters.
-  result = true
-  for c in s:
-    if not c.isSpaceAscii():
-      return false
+# --------- Private templates for different split separators -----------
 
 proc substrEq(s: string, pos: int, substr: string): bool =
   var i = 0
@@ -447,8 +381,6 @@ proc substrEq(s: string, pos: int, substr: string): bool =
     inc i
   return i == length
 
-# --------- Private templates for different split separators -----------
-
 template stringHasSep(s: string, index: int, seps: set[char]): bool =
   s[index] in seps
 
@@ -459,7 +391,7 @@ template stringHasSep(s: string, index: int, sep: string): bool =
   s.substrEq(index, sep)
 
 template splitCommon(s, sep, maxsplit, sepLen) =
-  ## Common code for split procedures
+  ## Common code for split procs
   var last = 0
   var splits = maxsplit
 
@@ -487,6 +419,42 @@ template oldSplit(s, seps, maxsplit) =
       if splits == 0: break
       dec(splits)
 
+template accResult(iter: untyped) =
+  result = @[]
+  for x in iter: add(result, x)
+
+
+iterator split*(s: string, sep: char, maxsplit: int = -1): string =
+  ## Splits the string `s` into substrings using a single separator.
+  ##
+  ## Substrings are separated by the character `sep`.
+  ## The code:
+  ##
+  ## .. code-block:: nim
+  ##   for word in split(";;this;is;an;;example;;;", ';'):
+  ##     writeLine(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block::
+  ##   ""
+  ##   ""
+  ##   "this"
+  ##   "is"
+  ##   "an"
+  ##   ""
+  ##   "example"
+  ##   ""
+  ##   ""
+  ##   ""
+  ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,char,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,char,int>`_
+  splitCommon(s, sep, maxsplit, 1)
+
 iterator split*(s: string, seps: set[char] = Whitespace,
                 maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a group of separators.
@@ -529,79 +497,13 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   ##   "08"
   ##   "08.398990"
   ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,set[char],int>`_
   splitCommon(s, seps, maxsplit, 1)
 
-iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
-  ## Splits the string ``s`` at whitespace stripping leading and trailing
-  ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
-  ## no more than ``maxsplit`` splits is made.
-  ##
-  ## The following code:
-  ##
-  ## .. code-block:: nim
-  ##   let s = "  foo \t bar  baz  "
-  ##   for ms in [-1, 1, 2, 3]:
-  ##     echo "------ maxsplit = ", ms, ":"
-  ##     for item in s.splitWhitespace(maxsplit=ms):
-  ##       echo '"', item, '"'
-  ##
-  ## ...results in:
-  ##
-  ## .. code-block::
-  ##   ------ maxsplit = -1:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz"
-  ##   ------ maxsplit = 1:
-  ##   "foo"
-  ##   "bar  baz  "
-  ##   ------ maxsplit = 2:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz  "
-  ##   ------ maxsplit = 3:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz"
-  ##
-  oldSplit(s, Whitespace, maxsplit)
-
-template accResult(iter: untyped) =
-  result = @[]
-  for x in iter: add(result, x)
-
-proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
-  rtl, extern: "nsuSplitWhitespace".} =
-  ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
-  ## iterator, but is a proc that returns a sequence of substrings.
-  accResult(splitWhitespace(s, maxsplit))
-
-iterator split*(s: string, sep: char, maxsplit: int = -1): string =
-  ## Splits the string `s` into substrings using a single separator.
-  ##
-  ## Substrings are separated by the character `sep`.
-  ## The code:
-  ##
-  ## .. code-block:: nim
-  ##   for word in split(";;this;is;an;;example;;;", ';'):
-  ##     writeLine(stdout, word)
-  ##
-  ## Results in:
-  ##
-  ## .. code-block::
-  ##   ""
-  ##   ""
-  ##   "this"
-  ##   "is"
-  ##   "an"
-  ##   ""
-  ##   "example"
-  ##   ""
-  ##   ""
-  ##   ""
-  ##
-  splitCommon(s, sep, maxsplit, 1)
-
 iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a string separator.
   ##
@@ -619,8 +521,14 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ##   "is"
   ##   "corrupted"
   ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,string,int>`_
   splitCommon(s, sep, maxsplit, sep.len)
 
+
 template rsplitCommon(s, sep, maxsplit, sepLen) =
   ## Common code for rsplit functions
   var
@@ -645,14 +553,14 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
     dec(first)
     last = first
 
-iterator rsplit*(s: string, seps: set[char] = Whitespace,
+iterator rsplit*(s: string, sep: char,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
   ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
-  ##   for piece in "foo bar".rsplit(WhiteSpace):
+  ##   for piece in "foo:bar".rsplit(':'):
   ##     echo piece
   ##
   ## Results in:
@@ -661,17 +569,23 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the set of chars `seps`
-  rsplitCommon(s, seps, maxsplit, 1)
+  ## Substrings are separated from the right by the char `sep`.
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,char,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,char,int>`_
+  rsplitCommon(s, sep, maxsplit, 1)
 
-iterator rsplit*(s: string, sep: char,
+iterator rsplit*(s: string, seps: set[char] = Whitespace,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
   ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
-  ##   for piece in "foo:bar".rsplit(':'):
+  ##   for piece in "foo bar".rsplit(WhiteSpace):
   ##     echo piece
   ##
   ## Results in:
@@ -680,8 +594,14 @@ iterator rsplit*(s: string, sep: char,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the char `sep`
-  rsplitCommon(s, sep, maxsplit, 1)
+  ## Substrings are separated from the right by the set of chars `seps`
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,set[char],int>`_
+  rsplitCommon(s, seps, maxsplit, 1)
 
 iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
                  keepSeparators: bool = false): string =
@@ -700,6 +620,12 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
   ##   "foo"
   ##
   ## Substrings are separated from the right by the string `sep`
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,string,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,string,int>`_
   rsplitCommon(s, sep, maxsplit, sep.len)
 
 iterator splitLines*(s: string, keepEol = false): string =
@@ -726,6 +652,10 @@ iterator splitLines*(s: string, keepEol = false): string =
   ##   ""
   ##   "example"
   ##   ""
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
   var first = 0
   var last = 0
   var eolpos = 0
@@ -747,76 +677,102 @@ iterator splitLines*(s: string, keepEol = false): string =
 
     first = last
 
-proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
-  rtl, extern: "nsuSplitLines".} =
-  ## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
-  ## proc that returns a sequence of substrings.
-  accResult(splitLines(s, keepEol=keepEol))
-
-proc countLines*(s: string): int {.noSideEffect,
-  rtl, extern: "nsuCountLines".} =
-  ## Returns the number of lines in the string `s`.
+iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
+  ## Splits the string ``s`` at whitespace stripping leading and trailing
+  ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
+  ## no more than ``maxsplit`` splits is made.
   ##
-  ## This is the same as ``len(splitLines(s))``, but much more efficient
-  ## because it doesn't modify the string creating temporal objects. Every
-  ## `character literal <manual.html#character-literals>`_ newline combination
-  ## (CR, LF, CR-LF) is supported.
+  ## The following code:
   ##
-  ## In this context, a line is any string seperated by a newline combination.
-  ## A line can be an empty string.
-  runnableExamples:
-    doAssert countLines("First line\l and second line.") == 2
-  result = 1
-  var i = 0
-  while i < s.len:
-    case s[i]
-    of '\c':
-      if i+1 < s.len and s[i+1] == '\l': inc i
-      inc result
-    of '\l': inc result
-    else: discard
-    inc i
+  ## .. code-block:: nim
+  ##   let s = "  foo \t bar  baz  "
+  ##   for ms in [-1, 1, 2, 3]:
+  ##     echo "------ maxsplit = ", ms, ":"
+  ##     for item in s.splitWhitespace(maxsplit=ms):
+  ##       echo '"', item, '"'
+  ##
+  ## ...results in:
+  ##
+  ## .. code-block::
+  ##   ------ maxsplit = -1:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##   ------ maxsplit = 1:
+  ##   "foo"
+  ##   "bar  baz  "
+  ##   ------ maxsplit = 2:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz  "
+  ##   ------ maxsplit = 3:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##
+  ## See also:
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  oldSplit(s, Whitespace, maxsplit)
+
 
-proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
-  noSideEffect, rtl, extern: "nsuSplitCharSet".} =
-  ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
-  ## proc that returns a sequence of substrings.
-  runnableExamples:
-    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
-    doAssert "".split({' '}) == @[""]
-  accResult(split(s, seps, maxsplit))
 
 proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitChar".} =
-  ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
-  ## that returns a sequence of substrings.
+  ## The same as the `split iterator <#split.i,string,char,int>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,char,int>`_
+  ## * `rsplit proc<#rsplit,string,char,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a,b,c".split(',') == @["a", "b", "c"]
     doAssert "".split(' ') == @[""]
   accResult(split(s, sep, maxsplit))
 
+proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
+  noSideEffect, rtl, extern: "nsuSplitCharSet".} =
+  ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,set[char],int>`_
+  ## * `rsplit proc<#rsplit,string,set[char],int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  runnableExamples:
+    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
+    doAssert "".split({' '}) == @[""]
+  accResult(split(s, seps, maxsplit))
+
 proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitString".} =
   ## Splits the string `s` into substrings using a string separator.
   ##
   ## Substrings are separated by the string `sep`. This is a wrapper around the
   ## `split iterator <#split.i,string,string,int>`_.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,string,int>`_
+  ## * `rsplit proc<#rsplit,string,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a,b,c".split(",") == @["a", "b", "c"]
     doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"]
     doAssert "".split("Elon Musk") == @[""]
     doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
-
     doAssert "a  largely    spaced sentence".split(" ", maxsplit=1) == @["a", " largely    spaced sentence"]
   doAssert(sep.len > 0)
 
   accResult(split(s, sep, maxsplit))
 
-proc rsplit*(s: string, seps: set[char] = Whitespace,
-             maxsplit: int = -1): seq[string]
-             {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
-  ## proc that returns a sequence of substrings.
+proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
+             {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
+  ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -825,20 +781,26 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
   ## do the following to get the tail of the path:
   ##
   ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
   ##
   ## Results in `tailSplit` containing:
   ##
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
-  accResult(rsplit(s, seps, maxsplit))
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,char,int>`_
+  ## * `split proc<#split,string,char,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
-proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
-             {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
-  ## that returns a sequence of substrings.
+proc rsplit*(s: string, seps: set[char] = Whitespace,
+             maxsplit: int = -1): seq[string]
+             {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
+  ## proc that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -847,19 +809,24 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
   ## do the following to get the tail of the path:
   ##
   ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
   ##
   ## Results in `tailSplit` containing:
   ##
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
-  accResult(rsplit(s, sep, maxsplit))
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
+  ## * `split proc<#split,string,set[char],int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  accResult(rsplit(s, seps, maxsplit))
   result.reverse()
 
 proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
              {.noSideEffect, rtl, extern: "nsuRSplitString".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc
+  ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a proc
   ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
@@ -876,9 +843,13 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
+  ## * `split proc<#split,string,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a  largely    spaced sentence".rsplit(" ", maxsplit=1) == @["a  largely    spaced", "sentence"]
-
     doAssert "a,b,c".rsplit(",") == @["a", "b", "c"]
     doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ", "plan ", "canal panama"]
     doAssert "".rsplit("Elon Musk") == @[""]
@@ -886,6 +857,75 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
+proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
+  rtl, extern: "nsuSplitLines".} =
+  ## The same as the `splitLines iterator<#splitLines.i,string>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  ## * `countLines proc<#countLines,string>`_
+  accResult(splitLines(s, keepEol=keepEol))
+
+proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
+  rtl, extern: "nsuSplitWhitespace".} =
+  ## The same as the `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## (see its documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  accResult(splitWhitespace(s, maxsplit))
+
+proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect,
+  rtl, extern: "nsuToBin".} =
+  ## Converts `x` into its binary representation.
+  ##
+  ## The resulting string is always `len` characters long. No leading ``0b``
+  ## prefix is generated.
+  runnableExamples:
+    let
+      a = 29
+      b = 257
+    doAssert a.toBin(8) == "00011101"
+    doAssert b.toBin(8) == "00000001"
+    doAssert b.toBin(9) == "100000001"
+  var
+    mask: BiggestInt = 1
+    shift: BiggestInt = 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((x and mask) shr shift) + ord('0'))
+    shift = shift + 1
+    mask = mask shl 1
+
+proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
+  rtl, extern: "nsuToOct".} =
+  ## Converts `x` into its octal representation.
+  ##
+  ## The resulting string is always `len` characters long. No leading ``0o``
+  ## prefix is generated.
+  ##
+  ## Do not confuse it with `toOctal proc<#toOctal,char>`_.
+  runnableExamples:
+    let
+      a = 62
+      b = 513
+    doAssert a.toOct(3) == "076"
+    doAssert b.toOct(3) == "001"
+    doAssert b.toOct(5) == "01001"
+  var
+    mask: BiggestInt = 7
+    shift: BiggestInt = 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((x and mask) shr shift) + ord('0'))
+    shift = shift + 3
+    mask = mask shl 3
+
 proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
   rtl, extern: "nsuToHex".} =
   ## Converts `x` to its hexadecimal representation.
@@ -893,8 +933,12 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
   ## The resulting string will be exactly `len` characters long. No prefix like
   ## ``0x`` is generated. `x` is treated as an unsigned value.
   runnableExamples:
-    doAssert toHex(1984, 6) == "0007C0"
-    doAssert toHex(1984, 2) == "C0"
+    let
+      a = 62
+      b = 4097
+    doAssert a.toHex(3) == "03E"
+    doAssert b.toHex(3) == "001"
+    doAssert b.toHex(4) == "1001"
   const
     HexChars = "0123456789ABCDEF"
   var
@@ -917,6 +961,18 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} =
   ##
   ## The output is twice the input long. No prefix like
   ## ``0x`` is generated.
+  ##
+  ## See also:
+  ## * `parseHexStr proc<#parseHexStr,string>`_ for the reverse operation
+  runnableExamples:
+    let
+      a = "1"
+      b = "A"
+      c = "\0\255"
+    doAssert a.toHex() == "31"
+    doAssert b.toHex() == "41"
+    doAssert c.toHex() == "00FF"
+
   const HexChars = "0123456789ABCDEF"
   result = newString(s.len * 2)
   for pos, c in s:
@@ -925,6 +981,25 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} =
     n = n shr 4
     result[pos * 2] = HexChars[n]
 
+proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
+  ## Converts a character `c` to its octal representation.
+  ##
+  ## The resulting string may not have a leading zero. Its length is always
+  ## exactly 3.
+  ##
+  ## Do not confuse it with `toOct proc<#toOct,BiggestInt,Positive>`_.
+  runnableExamples:
+    doAssert toOctal('1') == "061"
+    doAssert toOctal('A') == "101"
+    doAssert toOctal('a') == "141"
+    doAssert toOctal('!') == "041"
+
+  result = newString(3)
+  var val = ord(c)
+  for i in countdown(2, 0):
+    result[i] = chr(val mod 8 + ord('0'))
+    val = val div 8
+
 proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
   rtl, extern: "nsuIntToStr".} =
   ## Converts `x` to its decimal representation.
@@ -980,9 +1055,10 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar,
 
 proc parseFloat*(s: string): float {.noSideEffect, procvar,
   rtl, extern: "nsuParseFloat".} =
-  ## Parses a decimal floating point value contained in `s`. If `s` is not
-  ## a valid floating point number, `ValueError` is raised. ``NAN``,
-  ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
+  ## Parses a decimal floating point value contained in `s`.
+  ##
+  ## If `s` is not a valid floating point number, `ValueError` is raised.
+  ##``NAN``, ``INF``, ``-INF`` are also supported (case insensitive comparison).
   runnableExamples:
     doAssert parseFloat("3.14") == 3.14
     doAssert parseFloat("inf") == 1.0/0
@@ -997,6 +1073,13 @@ proc parseBinInt*(s: string): int {.noSideEffect, procvar,
   ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
   ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within
   ## `s` are ignored.
+  runnableExamples:
+    let
+      a = "0b11_0101"
+      b = "111"
+    doAssert a.parseBinInt() == 53
+    doAssert b.parseBinInt() == 7
+
   let L = parseutils.parseBin(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid binary integer: " & s)
@@ -1042,11 +1125,20 @@ proc parseHexStr*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuParseHexStr".} =
   ## Convert hex-encoded string to byte string, e.g.:
   ##
-  ## .. code-block:: nim
-  ##    hexToStr("00ff") == "\0\255"
-  ##
   ## Raises ``ValueError`` for an invalid hex values. The comparison is
   ## case-insensitive.
+  ##
+  ## See also:
+  ## * `toHex proc<#toHex,string>`_ for the reverse operation
+  runnableExamples:
+    let
+      a = "41"
+      b = "3161"
+      c = "00ff"
+    doAssert parseHexStr(a) == "A"
+    doAssert parseHexStr(b) == "1a"
+    doAssert parseHexStr(c) == "\0\255"
+
   if s.len mod 2 != 0:
     raise newException(ValueError, "Incorrect hex string len")
   result = newString(s.len div 2)
@@ -1067,6 +1159,10 @@ proc parseBool*(s: string): bool =
   ## returns `true`. If ``s`` is one of the following values: ``n, no, false,
   ## 0, off``, then returns `false`.  If ``s`` is something else a
   ## ``ValueError`` exception is raised.
+  runnableExamples:
+    let a = "n"
+    doAssert parseBool(a) == false
+
   case normalize(s)
   of "y", "yes", "true", "1", "on": result = true
   of "n", "no", "false", "0", "off": result = false
@@ -1095,39 +1191,41 @@ proc parseEnum*[T: enum](s: string, default: T): T =
 proc repeat*(c: char, count: Natural): string {.noSideEffect,
   rtl, extern: "nsuRepeatChar".} =
   ## Returns a string of length `count` consisting only of
-  ## the character `c`. You can use this proc to left align strings. Example:
-  ##
-  ## .. code-block:: nim
-  ##   proc tabexpand(indent: int, text: string, tabsize: int = 4) =
-  ##     echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize),
-  ##         text
-  ##
-  ##   tabexpand(4, "At four")
-  ##   tabexpand(5, "At five")
-  ##   tabexpand(6, "At six")
+  ## the character `c`.
+  runnableExamples:
+    let a = 'z'
+    doAssert a.repeat(5) == "zzzzz"
   result = newString(count)
   for i in 0..count-1: result[i] = c
 
 proc repeat*(s: string, n: Natural): string {.noSideEffect,
   rtl, extern: "nsuRepeatStr".} =
-  ## Returns String `s` concatenated `n` times.  Example:
-  ##
-  ## .. code-block:: nim
-  ##   echo "+++ STOP ".repeat(4), "+++"
+  ## Returns string `s` concatenated `n` times.
+  runnableExamples:
+    doAssert "+ foo +".repeat(3) == "+ foo ++ foo ++ foo +"
+
   result = newStringOfCap(n * s.len)
   for i in 1..n: result.add(s)
 
-template spaces*(n: Natural): string = repeat(' ', n)
-  ## Returns a String with `n` space characters. You can use this proc
-  ## to left align strings. Example:
+proc spaces*(n: Natural): string {.inline.} =
+  ## Returns a string with `n` space characters. You can use this proc
+  ## to left align strings.
   ##
-  ## .. code-block:: nim
-  ##   let
-  ##     width = 15
-  ##     text1 = "Hello user!"
-  ##     text2 = "This is a very long string"
-  ##   echo text1 & spaces(max(0, width - text1.len)) & "|"
-  ##   echo text2 & spaces(max(0, width - text2.len)) & "|"
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    let
+      width = 15
+      text1 = "Hello user!"
+      text2 = "This is a very long string"
+    doAssert text1 & spaces(max(0, width - text1.len)) & "|" ==
+             "Hello user!    |"
+    doAssert text2 & spaces(max(0, width - text2.len)) & "|" ==
+             "This is a very long string|"
+  repeat(' ', n)
 
 proc align*(s: string, count: Natural, padding = ' '): string {.
   noSideEffect, rtl, extern: "nsuAlignString".} =
@@ -1136,13 +1234,18 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   ## `padding` characters (by default spaces) are added before `s` resulting in
   ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
   ## returned unchanged. If you need to left align a string use the `alignLeft
-  ## proc <#alignLeft>`_. Example:
+  ## proc <#alignLeft,string,Natural,Char>`_.
   ##
-  ## .. code-block:: nim
-  ##   assert align("abc", 4) == " abc"
-  ##   assert align("a", 0) == "a"
-  ##   assert align("1232", 6) == "  1232"
-  ##   assert align("1232", 6, '#') == "##1232"
+  ## See also:
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    assert align("abc", 4) == " abc"
+    assert align("a", 0) == "a"
+    assert align("1232", 6) == "  1232"
+    assert align("1232", 6, '#') == "##1232"
   if s.len < count:
     result = newString(count)
     let spaces = count - s.len
@@ -1157,13 +1260,18 @@ proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect
   ## `padding` characters (by default spaces) are added after `s` resulting in
   ## left alignment. If ``s.len >= count``, no spaces are added and `s` is
   ## returned unchanged. If you need to right align a string use the `align
-  ## proc <#align>`_. Example:
+  ## proc <#align,string,Natural,Char>`_.
   ##
-  ## .. code-block:: nim
-  ##   assert alignLeft("abc", 4) == "abc "
-  ##   assert alignLeft("a", 0) == "a"
-  ##   assert alignLeft("1232", 6) == "1232  "
-  ##   assert alignLeft("1232", 6, '#') == "1232##"
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    assert alignLeft("abc", 4) == "abc "
+    assert alignLeft("a", 0) == "a"
+    assert alignLeft("1232", 6) == "1232  "
+    assert alignLeft("1232", 6, '#') == "1232##"
   if s.len < count:
     result = newString(count)
     if s.len > 0:
@@ -1173,83 +1281,55 @@ proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect
   else:
     result = s
 
-iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
-  token: string, isSep: bool] =
-  ## Tokenizes the string `s` into substrings.
-  ##
-  ## Substrings are separated by a substring containing only `seps`.
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##   for word in tokenize("  this is an  example  "):
-  ##     writeLine(stdout, word)
+proc center*(s: string, width: int, fillChar: char = ' '): string {.
+  noSideEffect, rtl, extern: "nsuCenterString".} =
+  ## Return the contents of `s` centered in a string `width` long using
+  ## `fillChar` (default: space) as padding.
   ##
-  ## Results in:
+  ## The original string is returned if `width` is less than or equal
+  ## to `s.len`.
   ##
-  ## .. code-block:: nim
-  ##   ("  ", true)
-  ##   ("this", false)
-  ##   (" ", true)
-  ##   ("is", false)
-  ##   (" ", true)
-  ##   ("an", false)
-  ##   ("  ", true)
-  ##   ("example", false)
-  ##   ("  ", true)
-  var i = 0
-  while true:
-    var j = i
-    var isSep = j < s.len and s[j] in seps
-    while j < s.len and (s[j] in seps) == isSep: inc(j)
-    if j > i:
-      yield (substr(s, i, j-1), isSep)
-    else:
-      break
-    i = j
-
-proc wordWrap*(s: string, maxLineWidth = 80,
-               splitLongWords = true,
-               seps: set[char] = Whitespace,
-               newLine = "\n"): string {.
-               noSideEffect, rtl, extern: "nsuWordWrap",
-               deprecated: "use wrapWords in std/wordwrap instead".} =
-  ## Word wraps `s`.
-  result = newStringOfCap(s.len + s.len shr 6)
-  var spaceLeft = maxLineWidth
-  var lastSep = ""
-  for word, isSep in tokenize(s, seps):
-    if isSep:
-      lastSep = word
-      spaceLeft = spaceLeft - len(word)
-      continue
-    if len(word) > spaceLeft:
-      if splitLongWords and len(word) > maxLineWidth:
-        result.add(substr(word, 0, spaceLeft-1))
-        var w = spaceLeft
-        var wordLeft = len(word) - spaceLeft
-        while wordLeft > 0:
-          result.add(newLine)
-          var L = min(maxLineWidth, wordLeft)
-          spaceLeft = maxLineWidth - L
-          result.add(substr(word, w, w+L-1))
-          inc(w, L)
-          dec(wordLeft, L)
-      else:
-        spaceLeft = maxLineWidth - len(word)
-        result.add(newLine)
-        result.add(word)
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  runnableExamples:
+    let a = "foo"
+    doAssert a.center(2) == "foo"
+    doAssert a.center(5) == " foo "
+    doAssert a.center(6) == " foo  "
+  if width <= s.len: return s
+  result = newString(width)
+  # Left padding will be one fillChar
+  # smaller if there are an odd number
+  # of characters
+  let
+    charsLeft = (width - s.len)
+    leftPadding = charsLeft div 2
+  for i in 0 ..< width:
+    if i >= leftPadding and i < leftPadding + s.len:
+      # we are where the string should be located
+      result[i] = s[i-leftPadding]
     else:
-      spaceLeft = spaceLeft - len(word)
-      result.add(lastSep & word)
-      lastSep.setLen(0)
+      # we are either before or after where
+      # the string s should go
+      result[i] = fillChar
 
 proc indent*(s: string, count: Natural, padding: string = " "): string
     {.noSideEffect, rtl, extern: "nsuIndent".} =
   ## Indents each line in ``s`` by ``count`` amount of ``padding``.
   ##
   ## **Note:** This does not preserve the new line characters used in ``s``.
+  ##
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `unindent proc<#unindent,string,Natural,string>`_
   runnableExamples:
-    doAssert indent("First line\c\l and second line.", 2) == "  First line\l   and second line."
+    doAssert indent("First line\c\l and second line.", 2) ==
+             "  First line\l   and second line."
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1266,8 +1346,15 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
   ## Sometimes called `dedent`:idx:
   ##
   ## **Note:** This does not preserve the new line characters used in ``s``.
+  ##
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
   runnableExamples:
-    doAssert unindent("  First line\l   and second line", 3) == "First line\land second line"
+    doAssert unindent("  First line\l   and second line", 3) ==
+             "First line\land second line"
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1286,37 +1373,105 @@ proc unindent*(s: string): string
     {.noSideEffect, rtl, extern: "nsuUnindentAll".} =
   ## Removes all indentation composed of whitespace from each line in ``s``.
   ##
-  ## For example:
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  runnableExamples:
+    let x = """
+      Hello
+      There
+    """.unindent()
+
+    doAssert x == "Hello\nThere\n"
+  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+
+proc delete*(s: var string, first, last: int) {.noSideEffect,
+  rtl, extern: "nsuDelete".} =
+  ## Deletes in `s` (must be declared as ``var``) the characters at positions
+  ## ``first ..last`` (both ends included).
   ##
-  ## .. code-block:: nim
-  ##   const x = """
-  ##     Hello
-  ##     There
-  ##   """.unindent()
+  ## This modifies `s` itself, it does not return a copy.
+  runnableExamples:
+    var a = "abracadabra"
+
+    a.delete(4, 5)
+    doAssert a == "abradabra"
+
+    a.delete(1, 6)
+    doAssert a == "ara"
+
+  var i = first
+  var j = last+1
+  var newLen = len(s)-j+i
+  while i < newLen:
+    s[i] = s[j]
+    inc(i)
+    inc(j)
+  setLen(s, newLen)
+
+
+proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` starts with character ``prefix``.
   ##
-  ##   doAssert x == "Hello\nThere\n"
-  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+  ## See also:
+  ## * `endsWith proc<#endsWith,string,char>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix proc<#removePrefix,string,char>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.startsWith('a') == true
+    doAssert a.startsWith('b') == false
+  result = s.len > 0 and s[0] == prefix
 
 proc startsWith*(s, prefix: string): bool {.noSideEffect,
   rtl, extern: "nsuStartsWith".} =
-  ## Returns true iff ``s`` starts with ``prefix``.
+  ## Returns true if ``s`` starts with string ``prefix``.
   ##
   ## If ``prefix == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `endsWith proc<#endsWith,string,string>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix proc<#removePrefix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.startsWith("abra") == true
+    doAssert a.startsWith("bra") == false
   var i = 0
   while true:
     if i >= prefix.len: return true
     if i >= s.len or s[i] != prefix[i]: return false
     inc(i)
 
-proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
-  ## Returns true iff ``s`` starts with ``prefix``.
-  result = s.len > 0 and s[0] == prefix
+proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` ends with ``suffix``.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,char>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix proc<#removeSuffix,string,char>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.endsWith('a') == true
+    doAssert a.endsWith('b') == false
+  result = s.len > 0 and s[s.high] == suffix
 
 proc endsWith*(s, suffix: string): bool {.noSideEffect,
   rtl, extern: "nsuEndsWith".} =
-  ## Returns true iff ``s`` ends with ``suffix``.
+  ## Returns true if ``s`` ends with ``suffix``.
   ##
   ## If ``suffix == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,string>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix proc<#removeSuffix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.endsWith("abra") == true
+    doAssert a.endsWith("dab") == false
   var i = 0
   var j = len(s) - len(suffix)
   while i+j <% s.len:
@@ -1324,21 +1479,136 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
     inc(i)
   if i >= suffix.len: return true
 
-proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
-  ## Returns true iff ``s`` ends with ``suffix``.
-  result = s.len > 0 and s[s.high] == suffix
-
 proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
   rtl, extern: "nsuContinuesWith".} =
-  ## Returns true iff ``s`` continues with ``substr`` at position ``start``.
+  ## Returns true if ``s`` continues with ``substr`` at position ``start``.
   ##
   ## If ``substr == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,string>`_
+  ## * `endsWith proc<#endsWith,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.continuesWith("ca", 4) == true
+    doAssert a.continuesWith("ca", 5) == false
+    doAssert a.continuesWith("dab", 6) == true
   var i = 0
   while true:
     if i >= substr.len: return true
     if i+start >= s.len or s[i+start] != substr[i]: return false
     inc(i)
 
+
+proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
+  rtl, extern: "nsuRemovePrefixCharSet".} =
+  ## Removes all characters from `chars` from the start of the string `s`
+  ## (in-place).
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,set[char]>`_
+  runnableExamples:
+     var userInput = "\r\n*~Hello World!"
+     userInput.removePrefix
+     doAssert userInput == "*~Hello World!"
+     userInput.removePrefix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "?!?Hello!?!"
+     otherInput.removePrefix({'!', '?'})
+     doAssert otherInput == "Hello!?!"
+
+  var start = 0
+  while start < s.len and s[start] in chars: start += 1
+  if start > 0: s.delete(0, start - 1)
+
+proc removePrefix*(s: var string, c: char) {.
+  rtl, extern: "nsuRemovePrefixChar".} =
+  ## Removes all occurrences of a single character (in-place) from the start
+  ## of a string.
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,char>`_
+  ## * `startsWith proc<#startsWith,string,char>`_
+  runnableExamples:
+    var ident = "pControl"
+    ident.removePrefix('p')
+    doAssert ident == "Control"
+  removePrefix(s, chars = {c})
+
+proc removePrefix*(s: var string, prefix: string) {.
+  rtl, extern: "nsuRemovePrefixString".} =
+  ## Remove the first matching prefix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,string>`_
+  ## * `startsWith proc<#startsWith,string,string>`_
+  runnableExamples:
+     var answers = "yesyes"
+     answers.removePrefix("yes")
+     doAssert answers == "yes"
+  if s.startsWith(prefix):
+    s.delete(0, prefix.len - 1)
+
+proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
+  rtl, extern: "nsuRemoveSuffixCharSet".} =
+  ## Removes all characters from `chars` from the end of the string `s`
+  ## (in-place).
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,set[char]>`_
+  runnableExamples:
+     var userInput = "Hello World!*~\r\n"
+     userInput.removeSuffix
+     doAssert userInput == "Hello World!*~"
+     userInput.removeSuffix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "Hello!?!"
+     otherInput.removeSuffix({'!', '?'})
+     doAssert otherInput == "Hello"
+
+  if s.len == 0: return
+  var last = s.high
+  while last > -1 and s[last] in chars: last -= 1
+  s.setLen(last + 1)
+
+proc removeSuffix*(s: var string, c: char) {.
+  rtl, extern: "nsuRemoveSuffixChar".} =
+  ## Removes all occurrences of a single character (in-place) from the end
+  ## of a string.
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,char>`_
+  ## * `endsWith proc<#endsWith,string,char>`_
+  runnableExamples:
+    var table = "users"
+    table.removeSuffix('s')
+    doAssert table == "user"
+
+    var dots = "Trailing dots......."
+    dots.removeSuffix('.')
+    doAssert dots == "Trailing dots"
+
+  removeSuffix(s, chars = {c})
+
+proc removeSuffix*(s: var string, suffix: string) {.
+  rtl, extern: "nsuRemoveSuffixString".} =
+  ## Remove the first matching suffix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,string>`_
+  ## * `endsWith proc<#endsWith,string,string>`_
+  runnableExamples:
+    var answers = "yeses"
+    answers.removeSuffix("es")
+    doAssert answers == "yes"
+  var newLen = s.len
+  if s.endsWith(suffix):
+    newLen -= len(suffix)
+    s.setLen(newLen)
+
+
 proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
   {.noSideEffect, inline.} =
   ## Adds a separator to `dest` only if its length is bigger than `startLen`.
@@ -1353,24 +1623,28 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
   ## `startLen`. The following example creates a string describing
   ## an array of integers.
   runnableExamples:
-     var arr = "["
-     for x in items([2, 3, 5, 7, 11]):
-       addSep(arr, startLen=len("["))
-       add(arr, $x)
-     add(arr, "]")
+    var arr = "["
+    for x in items([2, 3, 5, 7, 11]):
+      addSep(arr, startLen=len("["))
+      add(arr, $x)
+    add(arr, "]")
+    doAssert arr == "[2, 3, 5, 7, 11]"
+
   if dest.len > startLen: add(dest, sep)
 
 proc allCharsInSet*(s: string, theSet: set[char]): bool =
-  ## Returns true iff each character of `s` is in the set `theSet`.
+  ## Returns true if every character of `s` is in the set `theSet`.
   runnableExamples:
     doAssert allCharsInSet("aeea", {'a', 'e'}) == true
     doAssert allCharsInSet("", {'a', 'e'}) == true
+
   for c in items(s):
     if c notin theSet: return false
   return true
 
 proc abbrev*(s: string, possibilities: openArray[string]): int =
-  ## Returns the index of the first item in ``possibilities`` which starts with ``s``, if not ambiguous.
+  ## Returns the index of the first item in ``possibilities`` which starts
+  ## with ``s``, if not ambiguous.
   ##
   ## Returns -1 if no item has been found and -2 if multiple items match.
   runnableExamples:
@@ -1378,6 +1652,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
     doAssert abbrev("foo", ["college", "faculty", "industry"]) == -1 # Not found
     doAssert abbrev("fac", ["college", "faculty", "faculties"]) == -2 # Ambiguous
     doAssert abbrev("college", ["college", "colleges", "industry"]) == 0
+
   result = -1 # none found
   for i in 0..possibilities.len-1:
     if possibilities[i].startsWith(s):
@@ -1391,9 +1666,10 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
 
 proc join*(a: openArray[string], sep: string = ""): string {.
   noSideEffect, rtl, extern: "nsuJoinSep".} =
-  ## Concatenates all strings in `a` separating them with `sep`.
+  ## Concatenates all strings in the container `a`, separating them with `sep`.
   runnableExamples:
     doAssert join(["A", "B", "Conclusion"], " -> ") == "A -> B -> Conclusion"
+
   if len(a) > 0:
     var L = sep.len * (a.len-1)
     for i in 0..high(a): inc(L, a[i].len)
@@ -1407,10 +1683,11 @@ proc join*(a: openArray[string], sep: string = ""): string {.
 
 proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
   noSideEffect, rtl.} =
-  ## Converts all elements in `a` to strings using `$` and concatenates them
-  ## with `sep`.
+  ## Converts all elements in the container `a` to strings using `$`,
+  ## and concatenates them with `sep`.
   runnableExamples:
     doAssert join([1, 2, 3], " -> ") == "1 -> 2 -> 3"
+
   result = ""
   for i, x in a:
     if i > 0:
@@ -1441,11 +1718,11 @@ proc initSkipTable*(a: var SkipTable, sub: string)
 
 proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
   {.noSideEffect, rtl, extern: "nsuFindStrA".} =
-  ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed table `a`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed
+  ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last
+  ## element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-
   let
     last = if last==0: s.high else: last
     subLast = sub.len - 1
@@ -1466,7 +1743,6 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
         return skip
       dec i
     inc skip, a[s[skip + subLast]]
-
   return -1
 
 when not (defined(js) or defined(nimdoc) or defined(nimscript)):
@@ -1478,10 +1754,14 @@ else:
 
 proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffect,
   rtl, extern: "nsuFindChar".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,char,int>`_
+  ## * `replace proc<#replace,string,char,char>`_
   let last = if last==0: s.high else: last
   when nimvm:
     for i in int(start)..last:
@@ -1498,34 +1778,72 @@ proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffe
         if sub == s[i]: return i
   return -1
 
+proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect,
+  rtl, extern: "nsuFindCharSet".} =
+  ## Searches for `chars` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
+  ##
+  ## If `s` contains none of the characters in `chars`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,set[char],int>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
+  let last = if last==0: s.high else: last
+  for i in int(start)..last:
+    if s[i] in chars: return i
+  return -1
+
 proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect,
   rtl, extern: "nsuFindStr".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,string,int>`_
+  ## * `replace proc<#replace,string,string,string>`_
   if sub.len > s.len: return -1
   if sub.len == 1: return find(s, sub[0], start, last)
   var a {.noinit.}: SkipTable
   initSkipTable(a, sub)
   result = find(a, s, sub, start, last)
 
-proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindCharSet".} =
-  ## Searches for `chars` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect,
+  rtl.} =
+  ## Searches for characer `sub` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
   ##
-  ## If `s` contains none of the characters in `chars`, -1 is returned.
-  let last = if last==0: s.high else: last
-  for i in int(start)..last:
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,char,int,int>`_
+  let realStart = if start == -1: s.len-1 else: start
+  for i in countdown(realStart, 0):
+    if sub == s[i]: return i
+  return -1
+
+proc rfind*(s: string, chars: set[char], start: int = -1): int {.noSideEffect.} =
+  ## Searches for `chars` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,set[char],Natural,int>`_
+  let realStart = if start == -1: s.len-1 else: start
+  for i in countdown(realStart, 0):
     if s[i] in chars: return i
   return -1
 
 proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
-  ## Searches for `sub` in `s` in reverse, starting at `start` and going
-  ## backwards to 0.
+  ## Searches for string `sub` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
   if sub.len == 0:
     return -1
   let realStart = if start == -1: s.len else: start
@@ -1538,54 +1856,34 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
     if result != -1: return
   return -1
 
-proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect,
-  rtl.} =
-  ## Searches for `sub` in `s` in reverse starting at position `start`.
-  ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  let realStart = if start == -1: s.len-1 else: start
-  for i in countdown(realStart, 0):
-    if sub == s[i]: return i
-  return -1
 
-proc rfind*(s: string, chars: set[char], start: int = -1): int {.noSideEffect.} =
-  ## Searches for `chars` in `s` in reverse starting at position `start`.
+proc count*(s: string, sub: char): int {.noSideEffect,
+  rtl, extern: "nsuCountChar".} =
+  ## Count the occurrences of the character `sub` in the string `s`.
   ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  let realStart = if start == -1: s.len-1 else: start
-  for i in countdown(realStart, 0):
-    if s[i] in chars: return i
-  return -1
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
+  for c in s:
+    if c == sub: inc result
 
-proc center*(s: string, width: int, fillChar: char = ' '): string {.
-  noSideEffect, rtl, extern: "nsuCenterString".} =
-  ## Return the contents of `s` centered in a string `width` long using
-  ## `fillChar` as padding.
+proc count*(s: string, subs: set[char]): int {.noSideEffect,
+  rtl, extern: "nsuCountCharSet".} =
+  ## Count the occurrences of the group of character `subs` in the string `s`.
   ##
-  ## The original string is returned if `width` is less than or equal
-  ## to `s.len`.
-  if width <= s.len: return s
-  result = newString(width)
-  # Left padding will be one fillChar
-  # smaller if there are an odd number
-  # of characters
-  let
-    charsLeft = (width - s.len)
-    leftPadding = charsLeft div 2
-  for i in 0 ..< width:
-    if i >= leftPadding and i < leftPadding + s.len:
-      # we are where the string should be located
-      result[i] = s[i-leftPadding]
-    else:
-      # we are either before or after where
-      # the string s should go
-      result[i] = fillChar
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
+  doAssert card(subs) > 0
+  for c in s:
+    if c in subs: inc result
 
 proc count*(s: string, sub: string, overlapping: bool = false): int {.
   noSideEffect, rtl, extern: "nsuCountString".} =
   ## Count the occurrences of a substring `sub` in the string `s`.
   ## Overlapping occurrences of `sub` only count when `overlapping`
-  ## is set to true.
+  ## is set to true (default: false).
+  ##
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
   doAssert sub.len > 0
   var i = 0
   while true:
@@ -1595,43 +1893,58 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
     else: i += sub.len
     inc result
 
-proc count*(s: string, sub: char): int {.noSideEffect,
-  rtl, extern: "nsuCountChar".} =
-  ## Count the occurrences of the character `sub` in the string `s`.
-  for c in s:
-    if c == sub: inc result
-
-proc count*(s: string, subs: set[char]): int {.noSideEffect,
-  rtl, extern: "nsuCountCharSet".} =
-  ## Count the occurrences of the group of character `subs` in the string `s`.
-  doAssert card(subs) > 0
-  for c in s:
-    if c in subs: inc result
-
-proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
-  ## Returns ``'"' & s & '"'`` if `s` contains a space and does not
-  ## start with a quote, else returns `s`.
+proc countLines*(s: string): int {.noSideEffect,
+  rtl, extern: "nsuCountLines".} =
+  ## Returns the number of lines in the string `s`.
+  ##
+  ## This is the same as ``len(splitLines(s))``, but much more efficient
+  ## because it doesn't modify the string creating temporal objects. Every
+  ## `character literal <manual.html#lexical-analysis-character-literals>`_
+  ## newline combination (CR, LF, CR-LF) is supported.
   ##
-  ## **DEPRECATED** as it was confused for shell quoting function.  For this
-  ## application use `osproc.quoteShell <osproc.html#quoteShell>`_.
-  if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"'
-  else: result = s
+  ## In this context, a line is any string seperated by a newline combination.
+  ## A line can be an empty string.
+  ##
+  ## See also:
+  ## * `splitLines proc<#splitLines,string>`_
+  runnableExamples:
+    doAssert countLines("First line\l and second line.") == 2
+  result = 1
+  var i = 0
+  while i < s.len:
+    case s[i]
+    of '\c':
+      if i+1 < s.len and s[i+1] == '\l': inc i
+      inc result
+    of '\l': inc result
+    else: discard
+    inc i
 
-proc contains*(s: string, c: char): bool {.noSideEffect.} =
-  ## Same as ``find(s, c) >= 0``.
-  return find(s, c) >= 0
 
 proc contains*(s, sub: string): bool {.noSideEffect.} =
   ## Same as ``find(s, sub) >= 0``.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
   return find(s, sub) >= 0
 
 proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
   ## Same as ``find(s, chars) >= 0``.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,set[char],Natural,int>`_
   return find(s, chars) >= 0
 
 proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   rtl, extern: "nsuReplaceStr".} =
   ## Replaces `sub` in `s` by the string `by`.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
+  ## * `replace proc<#replace,string,char,char>`_ for replacing
+  ##   single characters
+  ## * `replaceWord proc<#replaceWord,string,string,string>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
   result = ""
   let subLen = sub.len
   if subLen == 0:
@@ -1669,6 +1982,11 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect,
   ## Replaces `sub` in `s` by the character `by`.
   ##
   ## Optimized version of `replace <#replace,string,string>`_ for characters.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,char,Natural,int>`_
+  ## * `replaceWord proc<#replaceWord,string,string,string>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
   result = newString(s.len)
   var i = 0
   while i < s.len:
@@ -1681,7 +1999,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   ## Replaces `sub` in `s` by the string `by`.
   ##
   ## Each occurrence of `sub` has to be surrounded by word boundaries
-  ## (comparable to ``\\w`` in regular expressions), otherwise it is not
+  ## (comparable to ``\b`` in regular expressions), otherwise it is not
   ## replaced.
   if sub.len == 0: return s
   const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
@@ -1711,14 +2029,14 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
   ## Same as replace, but specialized for doing multiple replacements in a single
   ## pass through the input string.
   ##
-  ## multiReplace performs all replacements in a single pass, this means it can be used
-  ## to swap the occurences of "a" and "b", for instance.
+  ## `multiReplace` performs all replacements in a single pass, this means it
+  ## can be used to swap the occurences of "a" and "b", for instance.
   ##
-  ## If the resulting string is not longer than the original input string, only a single
-  ## memory allocation is required.
+  ## If the resulting string is not longer than the original input string,
+  ## only a single memory allocation is required.
   ##
-  ## The order of the replacements does matter. Earlier replacements are preferred over later
-  ## replacements in the argument list.
+  ## The order of the replacements does matter. Earlier replacements are
+  ## preferred over later replacements in the argument list.
   result = newStringOfCap(s.len)
   var i = 0
   var fastChk: set[char] = {}
@@ -1740,55 +2058,12 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
       add result, s[i]
       inc(i)
 
-proc delete*(s: var string, first, last: int) {.noSideEffect,
-  rtl, extern: "nsuDelete".} =
-  ## Deletes in `s` the characters at position `first` .. `last`.
-  ##
-  ## This modifies `s` itself, it does not return a copy.
-  var i = first
-  var j = last+1
-  var newLen = len(s)-j+i
-  while i < newLen:
-    s[i] = s[j]
-    inc(i)
-    inc(j)
-  setLen(s, newLen)
 
-proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
-  rtl, extern: "nsuToOct".} =
-  ## Converts `x` into its octal representation.
-  ##
-  ## The resulting string is always `len` characters long. No leading ``0o``
-  ## prefix is generated.
-  var
-    mask: BiggestInt = 7
-    shift: BiggestInt = 0
-  assert(len > 0)
-  result = newString(len)
-  for j in countdown(len-1, 0):
-    result[j] = chr(int((x and mask) shr shift) + ord('0'))
-    shift = shift + 3
-    mask = mask shl 3
-
-proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect,
-  rtl, extern: "nsuToBin".} =
-  ## Converts `x` into its binary representation.
-  ##
-  ## The resulting string is always `len` characters long. No leading ``0b``
-  ## prefix is generated.
-  var
-    mask: BiggestInt = 1
-    shift: BiggestInt = 0
-  assert(len > 0)
-  result = newString(len)
-  for j in countdown(len-1, 0):
-    result[j] = chr(int((x and mask) shr shift) + ord('0'))
-    shift = shift + 1
-    mask = mask shl 1
 
 proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
   rtl, extern: "nsuInsertSep".} =
-  ## Inserts the separator `sep` after `digits` digits from right to left.
+  ## Inserts the separator `sep` after `digits` characters (default: 3)
+  ## from right to left.
   ##
   ## Even though the algorithm works with any string `s`, it is only useful
   ## if `s` contains a number.
@@ -1810,11 +2085,15 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
 
 proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   rtl, extern: "nsuEscape".} =
-  ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_
-  ## for the escaping scheme.
+  ## Escapes a string `s`. See `system.addEscapedChar
+  ## <system.html#addEscapedChar,string,char>`_ for the escaping scheme.
   ##
   ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
   ## Both may be empty strings.
+  ##
+  ## See also:
+  ## * `unescape proc<#unescape,string,string,string>`_ for the opposite
+  ## operation
   result = newStringOfCap(s.len + s.len shr 2)
   result.add(prefix)
   for c in items(s):
@@ -1832,8 +2111,8 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   rtl, extern: "nsuUnescape".} =
   ## Unescapes a string `s`.
   ##
-  ## This complements `escape <#escape>`_ as it performs the opposite
-  ## operations.
+  ## This complements `escape proc<#escape,string,string,string>`_
+  ## as it performs the opposite operations.
   ##
   ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
   ## ValueError exception will be raised.
@@ -1879,101 +2158,12 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
   ## and is followed by any number of characters of the set `IdentChars`.
   runnableExamples:
     doAssert "abc_def08".validIdentifier
+
   if s.len > 0 and s[0] in IdentStartChars:
     for i in 1..s.len-1:
       if s[i] notin IdentChars: return false
     return true
 
-{.push warning[Deprecated]: off.}
-proc editDistance*(a, b: string): int {.noSideEffect,
-  rtl, extern: "nsuEditDistance",
-  deprecated: "use editdistance.editDistanceAscii instead".} =
-  ## Returns the edit distance between `a` and `b`.
-  ##
-  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
-  ## memory overhead.
-  var len1 = a.len
-  var len2 = b.len
-  if len1 > len2:
-    # make `b` the longer string
-    return editDistance(b, a)
-
-  # strip common prefix:
-  var s = 0
-  while s < len1 and a[s] == b[s]:
-    inc(s)
-    dec(len1)
-    dec(len2)
-  # strip common suffix:
-  while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
-    dec(len1)
-    dec(len2)
-  # trivial cases:
-  if len1 == 0: return len2
-  if len2 == 0: return len1
-
-  # another special case:
-  if len1 == 1:
-    for j in s..s+len2-1:
-      if a[s] == b[j]: return len2 - 1
-    return len2
-
-  inc(len1)
-  inc(len2)
-  var half = len1 shr 1
-  # initalize first row:
-  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
-  var row: seq[int]
-  newSeq(row, len2)
-  var e = s + len2 - 1 # end marker
-  for i in 1..len2 - half - 1: row[i] = i
-  row[0] = len1 - half - 1
-  for i in 1 .. len1 - 1:
-    var char1 = a[i + s - 1]
-    var char2p: int
-    var D, x: int
-    var p: int
-    if i >= len1 - half:
-      # skip the upper triangle:
-      var offset = i - len1 + half
-      char2p = offset
-      p = offset
-      var c3 = row[p] + ord(char1 != b[s + char2p])
-      inc(p)
-      inc(char2p)
-      x = row[p] + 1
-      D = x
-      if x > c3: x = c3
-      row[p] = x
-      inc(p)
-    else:
-      p = 1
-      char2p = 0
-      D = i
-      x = i
-    if i <= half + 1:
-      # skip the lower triangle:
-      e = len2 + i - half - 2
-    # main:
-    while p <= e:
-      dec(D)
-      var c3 = D + ord(char1 != b[char2p + s])
-      inc(char2p)
-      inc(x)
-      if x > c3: x = c3
-      D = row[p] + 1
-      if x > D: x = D
-      row[p] = x
-      inc(p)
-    # lower triangle sentinel:
-    if i <= half:
-      dec(D)
-      var c3 = D + ord(char1 != b[char2p + s])
-      inc(x)
-      if x > c3: x = c3
-      row[p] = x
-  result = row[e]
-{.pop.}
 
 # floating point formating:
 when not defined(js):
@@ -1981,10 +2171,11 @@ when not defined(js):
                                      importc: "sprintf", varargs, noSideEffect.}
 
 type
-  FloatFormatMode* = enum ## the different modes of floating point formating
-    ffDefault,         ## use the shorter floating point notation
-    ffDecimal,         ## use decimal floating point notation
-    ffScientific       ## use scientific notation (using ``e`` character)
+  FloatFormatMode* = enum
+    ## the different modes of floating point formating
+    ffDefault,   ## use the shorter floating point notation
+    ffDecimal,   ## use decimal floating point notation
+    ffScientific ## use scientific notation (using ``e`` character)
 
 proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
                          precision: range[-1..32] = 16;
@@ -2000,6 +2191,11 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
   ## after the decimal point for Nim's ``biggestFloat`` type.
   ##
   ## If ``precision == -1``, it tries to format it nicely.
+  runnableExamples:
+    let x = 123.456
+    doAssert x.formatBiggestFloat() == "123.4560000000000"
+    doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560"
+    doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02"
   when defined(js):
     var precision = precision
     if precision == -1:
@@ -2079,11 +2275,18 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
     doAssert x.formatFloat() == "123.4560000000000"
     doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
     doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
+
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
 proc trimZeros*(x: var string) {.noSideEffect.} =
   ## Trim trailing zeros from a formatted floating point
-  ## value (`x`).  Modifies the passed value.
+  ## value `x` (must be declared as ``var``).
+  ##
+  ## This modifies `x` itself, it does not return a copy.
+  runnableExamples:
+    var x = "123.456000000"
+    x.trimZeros()
+    doAssert x == "123.456"
   var spl: seq[string]
   if x.contains('.') or x.contains(','):
     if x.contains('e'):
@@ -2113,6 +2316,9 @@ proc formatSize*(bytes: int64,
   ##
   ## `includeSpace` can be set to true to include the (SI preferred) space
   ## between the number and the unit (e.g. 1 KiB).
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   runnableExamples:
     doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
     doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
@@ -2120,6 +2326,7 @@ proc formatSize*(bytes: int64,
     doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
     doAssert formatSize(4096) == "4KiB"
     doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+
   const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
   const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
   var
@@ -2215,6 +2422,9 @@ proc formatEng*(f: BiggestFloat,
   ##    formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " # Space with useUnitSpace=true
   ##
   ## `decimalSep` is used as the decimal separator.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   var
     absolute: BiggestFloat
     significand: BiggestFloat
@@ -2402,110 +2612,69 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
   ##
   ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
   ## raised if an ill-formed format string has been passed to the `%` operator.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   result = newStringOfCap(formatstr.len + a.len shl 4)
   addf(result, formatstr, a)
 
 proc `%` *(formatstr, a: string): string {.noSideEffect,
   rtl, extern: "nsuFormatSingleElem".} =
-  ## This is the same as ``formatstr % [a]``.
+  ## This is the same as ``formatstr % [a]`` (see
+  ## `% proc<#%25,string,openArray[string]>`_).
   result = newStringOfCap(formatstr.len + a.len)
   addf(result, formatstr, [a])
 
 proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect,
   rtl, extern: "nsuFormatVarargs".} =
-  ## This is the same as ``formatstr % a`` except that it supports
+  ## This is the same as ``formatstr % a`` (see
+  ## `% proc<#%25,string,openArray[string]>`_) except that it supports
   ## auto stringification.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   result = newStringOfCap(formatstr.len + a.len)
   addf(result, formatstr, a)
 
 {.pop.}
 
-proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
-  rtl, extern: "nsuRemoveSuffixCharSet".} =
-  ## Removes all characters from `chars` from the end of the string `s`
-  ## (in-place).
-  runnableExamples:
-     var userInput = "Hello World!*~\r\n"
-     userInput.removeSuffix
-     doAssert userInput == "Hello World!*~"
-     userInput.removeSuffix({'~', '*'})
-     doAssert userInput == "Hello World!"
 
-     var otherInput = "Hello!?!"
-     otherInput.removeSuffix({'!', '?'})
-     doAssert otherInput == "Hello"
-  if s.len == 0: return
-  var last = s.high
-  while last > -1 and s[last] in chars: last -= 1
-  s.setLen(last + 1)
 
-proc removeSuffix*(s: var string, c: char) {.
-  rtl, extern: "nsuRemoveSuffixChar".} =
-  ## Removes all occurrences of a single character (in-place) from the end
-  ## of a string.
+proc strip*(s: string, leading = true, trailing = true,
+            chars: set[char] = Whitespace): string
+  {.noSideEffect, rtl, extern: "nsuStrip".} =
+  ## Strips leading or trailing `chars` (default: whitespace characters)
+  ## from `s` and returns the resulting string.
   ##
-  runnableExamples:
-     var table = "users"
-     table.removeSuffix('s')
-     doAssert table == "user"
-
-     var dots = "Trailing dots......."
-     dots.removeSuffix('.')
-     doAssert dots == "Trailing dots"
-  removeSuffix(s, chars = {c})
-
-proc removeSuffix*(s: var string, suffix: string) {.
-  rtl, extern: "nsuRemoveSuffixString".} =
-  ## Remove the first matching suffix (in-place) from a string.
-  runnableExamples:
-     var answers = "yeses"
-     answers.removeSuffix("es")
-     doAssert answers == "yes"
-  var newLen = s.len
-  if s.endsWith(suffix):
-    newLen -= len(suffix)
-    s.setLen(newLen)
-
-proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
-  rtl, extern: "nsuRemovePrefixCharSet".} =
-  ## Removes all characters from `chars` from the start of the string `s`
-  ## (in-place).
+  ## If `leading` is true (default), leading `chars` are stripped.
+  ## If `trailing` is true (default), trailing `chars` are stripped.
+  ## If both are false, the string is returned unchanged.
   ##
+  ## See also:
+  ## * `stripLineEnd proc<#stripLineEnd,string>`_
   runnableExamples:
-     var userInput = "\r\n*~Hello World!"
-     userInput.removePrefix
-     doAssert userInput == "*~Hello World!"
-     userInput.removePrefix({'~', '*'})
-     doAssert userInput == "Hello World!"
+    let a = "  vhellov   "
+    let b = strip(a)
+    doAssert b == "vhellov"
 
-     var otherInput = "?!?Hello!?!"
-     otherInput.removePrefix({'!', '?'})
-     doAssert otherInput == "Hello!?!"
-  var start = 0
-  while start < s.len and s[start] in chars: start += 1
-  if start > 0: s.delete(0, start - 1)
+    doAssert a.strip(leading = false) == "  vhellov"
+    doAssert a.strip(trailing = false) == "vhellov   "
 
-proc removePrefix*(s: var string, c: char) {.
-  rtl, extern: "nsuRemovePrefixChar".} =
-  ## Removes all occurrences of a single character (in-place) from the start
-  ## of a string.
-  ##
-  runnableExamples:
-     var ident = "pControl"
-     ident.removePrefix('p')
-     doAssert ident == "Control"
-  removePrefix(s, chars = {c})
+    doAssert b.strip(chars = {'v'}) == "hello"
+    doAssert b.strip(leading = false, chars = {'v'}) == "vhello"
 
-proc removePrefix*(s: var string, prefix: string) {.
-  rtl, extern: "nsuRemovePrefixString".} =
-  ## Remove the first matching prefix (in-place) from a string.
-  ##
-  runnableExamples:
-     var answers = "yesyes"
-     answers.removePrefix("yes")
-     doAssert answers == "yes"
-  if s.startsWith(prefix):
-    s.delete(0, prefix.len - 1)
+    let c = "blaXbla"
+    doAssert c.strip(chars = {'b', 'a'}) == "laXbl"
+    doAssert c.strip(chars = {'b', 'a', 'l'}) == "X"
+
+  var
+    first = 0
+    last = len(s)-1
+  if leading:
+    while first <= last and s[first] in chars: inc(first)
+  if trailing:
+    while last >= 0 and s[last] in chars: dec(last)
+  result = substr(s, first, last)
 
 proc stripLineEnd*(s: var string) =
   ## Returns ``s`` stripped from one of these suffixes:
@@ -2519,6 +2688,7 @@ proc stripLineEnd*(s: var string) =
     s = "foo\r\n"
     s.stripLineEnd
     doAssert s == "foo"
+
   if s.len > 0:
     case s[^1]
     of '\n':
@@ -2531,6 +2701,331 @@ proc stripLineEnd*(s: var string) =
     else:
       discard
 
+
+iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
+  token: string, isSep: bool] =
+  ## Tokenizes the string `s` into substrings.
+  ##
+  ## Substrings are separated by a substring containing only `seps`.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   for word in tokenize("  this is an  example  "):
+  ##     writeLine(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nim
+  ##   ("  ", true)
+  ##   ("this", false)
+  ##   (" ", true)
+  ##   ("is", false)
+  ##   (" ", true)
+  ##   ("an", false)
+  ##   ("  ", true)
+  ##   ("example", false)
+  ##   ("  ", true)
+  var i = 0
+  while true:
+    var j = i
+    var isSep = j < s.len and s[j] in seps
+    while j < s.len and (s[j] in seps) == isSep: inc(j)
+    if j > i:
+      yield (substr(s, i, j-1), isSep)
+    else:
+      break
+    i = j
+
+
+
+
+
+# --------------------------------------------------------------------------
+# Deprecated procs
+
+{.push warning[Deprecated]: off.}
+proc editDistance*(a, b: string): int {.noSideEffect,
+  rtl, extern: "nsuEditDistance",
+  deprecated: "use editdistance.editDistanceAscii instead".} =
+  ## **Deprecated**: Use `editdistance module<editdistance.html>`_
+  ##
+  ## Returns the edit distance between `a` and `b`.
+  ##
+  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
+  ## memory overhead.
+  var len1 = a.len
+  var len2 = b.len
+  if len1 > len2:
+    # make `b` the longer string
+    return editDistance(b, a)
+
+  # strip common prefix:
+  var s = 0
+  while s < len1 and a[s] == b[s]:
+    inc(s)
+    dec(len1)
+    dec(len2)
+  # strip common suffix:
+  while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
+    dec(len1)
+    dec(len2)
+  # trivial cases:
+  if len1 == 0: return len2
+  if len2 == 0: return len1
+
+  # another special case:
+  if len1 == 1:
+    for j in s..s+len2-1:
+      if a[s] == b[j]: return len2 - 1
+    return len2
+
+  inc(len1)
+  inc(len2)
+  var half = len1 shr 1
+  # initalize first row:
+  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
+  var row: seq[int]
+  newSeq(row, len2)
+  var e = s + len2 - 1 # end marker
+  for i in 1..len2 - half - 1: row[i] = i
+  row[0] = len1 - half - 1
+  for i in 1 .. len1 - 1:
+    var char1 = a[i + s - 1]
+    var char2p: int
+    var D, x: int
+    var p: int
+    if i >= len1 - half:
+      # skip the upper triangle:
+      var offset = i - len1 + half
+      char2p = offset
+      p = offset
+      var c3 = row[p] + ord(char1 != b[s + char2p])
+      inc(p)
+      inc(char2p)
+      x = row[p] + 1
+      D = x
+      if x > c3: x = c3
+      row[p] = x
+      inc(p)
+    else:
+      p = 1
+      char2p = 0
+      D = i
+      x = i
+    if i <= half + 1:
+      # skip the lower triangle:
+      e = len2 + i - half - 2
+    # main:
+    while p <= e:
+      dec(D)
+      var c3 = D + ord(char1 != b[char2p + s])
+      inc(char2p)
+      inc(x)
+      if x > c3: x = c3
+      D = row[p] + 1
+      if x > D: x = D
+      row[p] = x
+      inc(p)
+    # lower triangle sentinel:
+    if i <= half:
+      dec(D)
+      var c3 = D + ord(char1 != b[char2p + s])
+      inc(x)
+      if x > c3: x = c3
+      row[p] = x
+  result = row[e]
+{.pop.}
+
+proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
+                                      extern: "nsuIsNilOrEmpty",
+                                      deprecated: "use 'x.len == 0' instead".} =
+  ## **Deprecated**: use 'x.len == 0'
+  ##
+  ## Checks if `s` is nil or empty.
+  result = len(s) == 0
+
+proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
+  ## Checks if `s` is nil or consists entirely of whitespace characters.
+  result = true
+  for c in s:
+    if not c.isSpaceAscii():
+      return false
+
+template isImpl(call) =
+  if s.len == 0: return false
+  result = true
+  for c in s:
+    if not call(c): return false
+
+proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaAsciiStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is alphabetical.
+  ##
+  ## This checks a-z, A-Z ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## alphabetic and there is at least one character
+  ## in `s`.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isAlphaAscii("fooBar") == true
+    doAssert isAlphaAscii("fooBar1") == false
+    doAssert isAlphaAscii("foo Bar") == false
+  isImpl isAlphaAscii
+
+proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaNumericStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is alphanumeric.
+  ##
+  ## This checks a-z, A-Z, 0-9 ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## alpanumeric and there is at least one character
+  ## in `s`.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isAlphaNumeric("fooBar") == true
+    doAssert isAlphaNumeric("fooBar1") == true
+    doAssert isAlphaNumeric("foo Bar") == false
+  isImpl isAlphaNumeric
+
+proc isDigit*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsDigitStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is a numeric value.
+  ##
+  ## This checks 0-9 ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## numeric and there is at least one character
+  ## in `s`.
+  runnableExamples:
+    doAssert isDigit("1908") == true
+    doAssert isDigit("fooBar1") == false
+  isImpl isDigit
+
+proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsSpaceAsciiStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is completely whitespace.
+  ##
+  ## Returns true if all characters in `s` are whitespace
+  ## characters and there is at least one character in `s`.
+  runnableExamples:
+    doAssert isSpaceAscii("   ") == true
+    doAssert isSpaceAscii("") == false
+  isImpl isSpaceAscii
+
+template isCaseImpl(s, charProc, skipNonAlpha) =
+  var hasAtleastOneAlphaChar = false
+  if s.len == 0: return false
+  for c in s:
+    if skipNonAlpha:
+      var charIsAlpha = c.isAlphaAscii()
+      if not hasAtleastOneAlphaChar:
+        hasAtleastOneAlphaChar = charIsAlpha
+      if charIsAlpha and (not charProc(c)):
+        return false
+    else:
+      if not charProc(c):
+        return false
+  return if skipNonAlpha: hasAtleastOneAlphaChar else: true
+
+proc isLowerAscii*(s: string, skipNonAlpha: bool): bool {.
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether ``s`` is lower case.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
+  ## characters in ``s`` are lower case.  Returns false if none of the
+  ## characters in ``s`` are alphabetical.
+  ##
+  ## If ``skipNonAlpha`` is false, returns true only if all characters
+  ## in ``s`` are alphabetical and lower case.
+  ##
+  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
+  ## an empty string.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isLowerAscii("1foobar", false) == false
+    doAssert isLowerAscii("1foobar", true) == true
+    doAssert isLowerAscii("1fooBar", true) == false
+  isCaseImpl(s, isLowerAscii, skipNonAlpha)
+
+proc isUpperAscii*(s: string, skipNonAlpha: bool): bool {.
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether ``s`` is upper case.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
+  ## characters in ``s`` are upper case.  Returns false if none of the
+  ## characters in ``s`` are alphabetical.
+  ##
+  ## If ``skipNonAlpha`` is false, returns true only if all characters
+  ## in ``s`` are alphabetical and upper case.
+  ##
+  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
+  ## an empty string.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isUpperAscii("1FOO", false) == false
+    doAssert isUpperAscii("1FOO", true) == true
+    doAssert isUpperAscii("1Foo", true) == false
+  isCaseImpl(s, isUpperAscii, skipNonAlpha)
+
+proc wordWrap*(s: string, maxLineWidth = 80,
+               splitLongWords = true,
+               seps: set[char] = Whitespace,
+               newLine = "\n"): string {.
+               noSideEffect, rtl, extern: "nsuWordWrap",
+               deprecated: "use wrapWords in std/wordwrap instead".} =
+  ## **Deprecated**: use wrapWords in std/wordwrap instead
+  ##
+  ## Word wraps `s`.
+  result = newStringOfCap(s.len + s.len shr 6)
+  var spaceLeft = maxLineWidth
+  var lastSep = ""
+  for word, isSep in tokenize(s, seps):
+    if isSep:
+      lastSep = word
+      spaceLeft = spaceLeft - len(word)
+      continue
+    if len(word) > spaceLeft:
+      if splitLongWords and len(word) > maxLineWidth:
+        result.add(substr(word, 0, spaceLeft-1))
+        var w = spaceLeft
+        var wordLeft = len(word) - spaceLeft
+        while wordLeft > 0:
+          result.add(newLine)
+          var L = min(maxLineWidth, wordLeft)
+          spaceLeft = maxLineWidth - L
+          result.add(substr(word, w, w+L-1))
+          inc(w, L)
+          dec(wordLeft, L)
+      else:
+        spaceLeft = maxLineWidth - len(word)
+        result.add(newLine)
+        result.add(word)
+    else:
+      spaceLeft = spaceLeft - len(word)
+      result.add(lastSep & word)
+      lastSep.setLen(0)
+
+
+
 when isMainModule:
   proc nonStaticTests =
     doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
deleted file mode 100644
index 638e71f04..000000000
--- a/lib/pure/subexes.nim
+++ /dev/null
@@ -1,406 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nim support for `substitution expressions`:idx: (`subex`:idx:).
-##
-## .. include:: ../../doc/subexes.txt
-##
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
-
-from strutils import parseInt, cmpIgnoreStyle, Digits
-include "system/inclrtl"
-import system/helpers2
-
-proc findNormalized(x: string, inArray: openarray[string]): int =
-  var i = 0
-  while i < high(inArray):
-    if cmpIgnoreStyle(x, inArray[i]) == 0: return i
-    inc(i, 2) # incrementing by 1 would probably lead to a
-              # security hole...
-  return -1
-
-type
-  SubexError* = object of ValueError ## exception that is raised for
-                                     ## an invalid subex
-
-proc raiseInvalidFormat(msg: string) {.noinline.} =
-  raise newException(SubexError, "invalid format string: " & msg)
-
-type
-  FormatParser = object {.pure, final.}
-    when defined(js):
-      f: string # we rely on the '\0' terminator
-                # which JS's native string doesn't have
-    else:
-      f: cstring
-    num, i, lineLen: int
-
-template call(x: untyped): untyped =
-  p.i = i
-  x
-  i = p.i
-
-template callNoLineLenTracking(x: untyped): untyped =
-  let oldLineLen = p.lineLen
-  p.i = i
-  x
-  i = p.i
-  p.lineLen = oldLineLen
-
-proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
-  const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
-  var i = p.i
-  var f = p.f
-  case f[i]
-  of '#':
-    result = p.num
-    inc i
-    inc p.num
-  of '1'..'9', '-':
-    var j = 0
-    var negative = f[i] == '-'
-    if negative: inc i
-    while f[i] in Digits:
-      j = j * 10 + ord(f[i]) - ord('0')
-      inc i
-    result = if not negative: j-1 else: a.len-j
-  of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
-    var name = ""
-    while f[i] in PatternChars:
-      name.add(f[i])
-      inc(i)
-    result = findNormalized(name, a)+1
-  of '$':
-    inc(i)
-    call:
-      result = getFormatArg(p, a)
-    result = parseInt(a[result])-1
-  else:
-    raiseInvalidFormat("'#', '$', number or identifier expected")
-  if result >=% a.len: raiseInvalidFormat(formatErrorIndexBound(result, a.len))
-  p.i = i
-
-proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {.
-  noSideEffect.}
-
-proc emitChar(p: var FormatParser, x: var string, ch: char) {.inline.} =
-  x.add(ch)
-  if ch == '\L': p.lineLen = 0
-  else: inc p.lineLen
-
-proc emitStrLinear(p: var FormatParser, x: var string, y: string) {.inline.} =
-  for ch in items(y): emitChar(p, x, ch)
-
-proc emitStr(p: var FormatParser, x: var string, y: string) {.inline.} =
-  x.add(y)
-  inc p.lineLen, y.len
-
-proc scanQuote(p: var FormatParser, x: var string, toAdd: bool) =
-  var i = p.i+1
-  var f = p.f
-  while true:
-    if f[i] == '\'':
-      inc i
-      if f[i] != '\'': break
-      inc i
-      if toAdd: emitChar(p, x, '\'')
-    elif f[i] == '\0': raiseInvalidFormat("closing \"'\" expected")
-    else:
-      if toAdd: emitChar(p, x, f[i])
-      inc i
-  p.i = i
-
-proc scanBranch(p: var FormatParser, a: openArray[string],
-                x: var string, choice: int) =
-  var i = p.i
-  var f = p.f
-  var c = 0
-  var elsePart = i
-  var toAdd = choice == 0
-  while true:
-    case f[i]
-    of ']': break
-    of '|':
-      inc i
-      elsePart = i
-      inc c
-      if toAdd: break
-      toAdd = choice == c
-    of '\'':
-      call: scanQuote(p, x, toAdd)
-    of '\0': raiseInvalidFormat("closing ']' expected")
-    else:
-      if toAdd:
-        if f[i] == '$':
-          inc i
-          call: scanDollar(p, a, x)
-        else:
-          emitChar(p, x, f[i])
-          inc i
-      else:
-        inc i
-  if not toAdd and choice >= 0:
-    # evaluate 'else' part:
-    var last = i
-    i = elsePart
-    while true:
-      case f[i]
-      of '|', ']': break
-      of '\'':
-        call: scanQuote(p, x, true)
-      of '$':
-        inc i
-        call: scanDollar(p, a, x)
-      else:
-        emitChar(p, x, f[i])
-        inc i
-    i = last
-  p.i = i+1
-
-proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
-  var slice = false
-  var i = p.i
-  var f = p.f
-
-  if f[i] == '{': inc i
-  else: raiseInvalidFormat("'{' expected")
-  if f[i] == '.' and f[i+1] == '.':
-    inc i, 2
-    slice = true
-  else:
-    call: result.x = getFormatArg(p, a)
-    if f[i] == '.' and f[i+1] == '.':
-      inc i, 2
-      slice = true
-  if slice:
-    if f[i] != '}':
-      call: result.y = getFormatArg(p, a)
-    else:
-      result.y = high(a)
-  else:
-    result.y = result.x
-  if f[i] != '}': raiseInvalidFormat("'}' expected")
-  inc i
-  p.i = i
-
-proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
-  var i = p.i
-  var f = p.f
-  case f[i]
-  of '$':
-    emitChar p, s, '$'
-    inc i
-  of '*':
-    for j in 0..a.high: emitStr p, s, a[j]
-    inc i
-  of '{':
-    call:
-      let (x, y) = scanSlice(p, a)
-    for j in x..y: emitStr p, s, a[j]
-  of '[':
-    inc i
-    var start = i
-    call: scanBranch(p, a, s, -1)
-    var x: int
-    if f[i] == '{':
-      inc i
-      call: x = getFormatArg(p, a)
-      if f[i] != '}': raiseInvalidFormat("'}' expected")
-      inc i
-    else:
-      call: x = getFormatArg(p, a)
-    var last = i
-    let choice = parseInt(a[x])
-    i = start
-    call: scanBranch(p, a, s, choice)
-    i = last
-  of '\'':
-    var sep = ""
-    callNoLineLenTracking: scanQuote(p, sep, true)
-    if f[i] == '~':
-      # $' '~{1..3}
-      # insert space followed by 1..3 if not empty
-      inc i
-      call:
-        let (x, y) = scanSlice(p, a)
-      var L = 0
-      for j in x..y: inc L, a[j].len
-      if L > 0:
-        emitStrLinear p, s, sep
-        for j in x..y: emitStr p, s, a[j]
-    else:
-      block StringJoin:
-        block OptionalLineLengthSpecifier:
-          var maxLen = 0
-          case f[i]
-          of '0'..'9':
-            while f[i] in Digits:
-              maxLen = maxLen * 10 + ord(f[i]) - ord('0')
-              inc i
-          of '$':
-            # do not skip the '$' here for `getFormatArg`!
-            call:
-              maxLen = getFormatArg(p, a)
-          else: break OptionalLineLengthSpecifier
-          var indent = ""
-          case f[i]
-          of 'i':
-            inc i
-            callNoLineLenTracking: scanQuote(p, indent, true)
-
-            call:
-              let (x, y) = scanSlice(p, a)
-            if maxLen < 1: emitStrLinear(p, s, indent)
-            var items = 1
-            emitStr p, s, a[x]
-            for j in x+1..y:
-              emitStr p, s, sep
-              if items >= maxLen:
-                emitStrLinear p, s, indent
-                items = 0
-              emitStr p, s, a[j]
-              inc items
-          of 'c':
-            inc i
-            callNoLineLenTracking: scanQuote(p, indent, true)
-
-            call:
-              let (x, y) = scanSlice(p, a)
-            if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent)
-            emitStr p, s, a[x]
-            for j in x+1..y:
-              emitStr p, s, sep
-              if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent)
-              emitStr p, s, a[j]
-
-          else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected")
-          break StringJoin
-
-        call:
-          let (x, y) = scanSlice(p, a)
-        emitStr p, s, a[x]
-        for j in x+1..y:
-          emitStr p, s, sep
-          emitStr p, s, a[j]
-  else:
-    call:
-      var x = getFormatArg(p, a)
-    emitStr p, s, a[x]
-  p.i = i
-
-
-type
-  Subex* = distinct string ## string that contains a substitution expression
-
-proc subex*(s: string): Subex =
-  ## constructs a *substitution expression* from `s`. Currently this performs
-  ## no syntax checking but this may change in later versions.
-  result = Subex(s)
-
-proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
-           noSideEffect, rtl, extern: "nfrmtAddf".} =
-  ## The same as ``add(s, formatstr % a)``, but more efficient.
-  var p: FormatParser
-  p.f = formatstr.string
-  var i = 0
-  while i < len(formatstr.string):
-    if p.f[i] == '$':
-      inc i
-      call: scanDollar(p, a, s)
-    else:
-      emitChar(p, s, p.f[i])
-      inc(i)
-
-proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatOpenArray".} =
-  ## The `substitution`:idx: operator performs string substitutions in
-  ## `formatstr` and returns a modified `formatstr`. This is often called
-  ## `string interpolation`:idx:.
-  ##
-  result = newStringOfCap(formatstr.string.len + a.len shl 4)
-  addf(result, formatstr, a)
-
-proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatSingleElem".} =
-  ## This is the same as ``formatstr % [a]``.
-  result = newStringOfCap(formatstr.string.len + a.len)
-  addf(result, formatstr, [a])
-
-proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatVarargs".} =
-  ## The `substitution`:idx: operator performs string substitutions in
-  ## `formatstr` and returns a modified `formatstr`. This is often called
-  ## `string interpolation`:idx:.
-  ##
-  result = newStringOfCap(formatstr.string.len + a.len shl 4)
-  addf(result, formatstr, a)
-
-{.pop.}
-
-when isMainModule:
-  from strutils import replace
-
-  proc `%`(formatstr: string, a: openarray[string]): string =
-    result = newStringOfCap(formatstr.len + a.len shl 4)
-    addf(result, formatstr.Subex, a)
-
-  proc `%`(formatstr: string, a: string): string =
-    result = newStringOfCap(formatstr.len + a.len)
-    addf(result, formatstr.Subex, [a])
-
-
-  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
-  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
-           "The cat eats fish."
-
-
-  doAssert "$[abc|def]# $3 $# $#" % ["17", "b", "c"] == "def c b c"
-  doAssert "$[abc|def]# $3 $# $#" % ["1", "b", "c"] == "def c b c"
-  doAssert "$[abc|def]# $3 $# $#" % ["0", "b", "c"] == "abc c b c"
-  doAssert "$[abc|def|]# $3 $# $#" % ["17", "b", "c"] == " c b c"
-
-  doAssert "$[abc|def|]# $3 $# $#" % ["-9", "b", "c"] == " c b c"
-  doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)"
-
-  doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3"
-
-  doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)"
-  doAssert "$' '~{..}" % "" == ""
-  doAssert "$' '~{..}" % "P0" == " P0"
-  doAssert "${$1}" % "1" == "1"
-  doAssert "${$$-1} $$1" % "1" == "1 $1"
-
-  doAssert(("$#($', '10c'\n    '{#..})" % ["doAssert", "longishA", "longish"]).replace(" \n", "\n") ==
-           """doAssert(
-    longishA,
-    longish)""")
-
-  doAssert(("type MyEnum* = enum\n  $', '2i'\n  '{..}" % ["fieldA",
-    "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]).replace(" \n", "\n") ==
-    strutils.unindent("""
-      type MyEnum* = enum
-        fieldA, fieldB,
-        FiledClkad, fieldD,
-        fieldE, longishFieldName""", 6))
-
-  doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
-
-  doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
-
-  doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
-
-  doAssert((subex("type\n  Enum = enum\n    $', '40c'\n    '{..}") % [
-    "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]).replace(" \n", "\n") ==
-    strutils.unindent("""
-      type
-        Enum = enum
-          fieldNameA, fieldNameB, fieldNameC,
-          fieldNameD""", 6))
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 35dc2483c..2b3c08d0d 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -37,7 +37,7 @@ type
 
 var gTerm {.threadvar.}: PTerminal
 
-proc newTerminal(): PTerminal
+proc newTerminal(): PTerminal {.gcsafe.}
 
 proc getTerminal(): PTerminal {.inline.} =
   if isNil(gTerm):
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 0a06d5f9f..260850a0e 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1,35 +1,41 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
+#        (c) Copyright 2018 Nim contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
 ##[
-  This module contains routines and types for dealing with time using a proleptic Gregorian calendar.
-  It's also available for the `JavaScript target <backends.html#the-javascript-target>`_.
+  The ``times`` module contains routines and types for dealing with time using
+  the `proleptic Gregorian calendar<https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar>`_.
+  It's also available for the
+  `JavaScript target <backends.html#backends-the-javascript-target>`_.
 
-  Although the types use nanosecond time resolution, the underlying resolution used by ``getTime()``
-  depends on the platform and backend (JS is limited to millisecond precision).
+  Although the ``times`` module support nanosecond time resolution, the
+  resolution used by ``getTime()`` depends on the platform and backend
+  (JS is limited to millisecond precision).
 
   Examples:
 
   .. code-block:: nim
-
     import times, os
+    # Simple benchmarking
     let time = cpuTime()
-
-    sleep(100)   # replace this with something to be timed
+    sleep(100)   # Replace this with something to be timed
     echo "Time taken: ", cpuTime() - time
 
-    echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
-    echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
+    # Current date & time
+    let now1 = now()     # Current timestamp as a DateTime in local time
+    let now2 = now().utc # Current timestamp as a DateTime in UTC
+    let now3 = getTime() # Current timestamp as a Time
 
-    echo "cpuTime()  float value: ", cpuTime()
-    echo "An hour from now      : ", now() + 1.hours
-    echo "An hour from (UTC) now: ", getTime().utc + initDuration(hours = 1)
+    # Arithmetic using Duration
+    echo "One hour from now      : ", now() + initDuration(hours = 1)
+    # Arithmetic using TimeInterval
+    echo "One year from now      : ", now() + 1.years
+    echo "One month from now     : ", now() + 1.months
 
   Parsing and Formatting Dates
   ----------------------------
@@ -97,14 +103,14 @@
                                                                                                     | ``24 AD -> 24``
                                                                                                     | ``24 BC -> -23``
                                                                                                     | ``12345 AD -> 12345``
-  ``z``          Displays the timezone offset from UTC.                                             | ``GMT+7 -> +7``
-                                                                                                    | ``GMT-5 -> -5``
-  ``zz``         Same as above but with leading 0.                                                  | ``GMT+7 -> +07``
-                                                                                                    | ``GMT-5 -> -05``
-  ``zzz``        Same as above but with ``:mm`` where *mm* represents minutes.                      | ``GMT+7 -> +07:00``
-                                                                                                    | ``GMT-5 -> -05:00``
-  ``zzzz``       Same as above but with ``:ss`` where *ss* represents seconds.                      | ``GMT+7 -> +07:00:00``
-                                                                                                    | ``GMT-5 -> -05:00:00``
+  ``z``          Displays the timezone offset from UTC.                                             | ``UTC+7 -> +7``
+                                                                                                    | ``UTC-5 -> -5``
+  ``zz``         Same as above but with leading 0.                                                  | ``UTC+7 -> +07``
+                                                                                                    | ``UTC-5 -> -05``
+  ``zzz``        Same as above but with ``:mm`` where *mm* represents minutes.                      | ``UTC+7 -> +07:00``
+                                                                                                    | ``UTC-5 -> -05:00``
+  ``zzzz``       Same as above but with ``:ss`` where *ss* represents seconds.                      | ``UTC+7 -> +07:00:00``
+                                                                                                    | ``UTC-5 -> -05:00:00``
   ``g``          Era: AD or BC                                                                      | ``300 AD -> AD``
                                                                                                     | ``300 BC -> BC``
   ``fff``        Milliseconds display                                                               | ``1000000 nanoseconds -> 1``
@@ -117,69 +123,142 @@
   inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
   ``,``. A literal ``'`` can be specified with ``''``.
 
-  However you don't need to necessarily separate format patterns, a
+  However you don't need to necessarily separate format patterns, an
   unambiguous format string like ``yyyyMMddhhmmss`` is valid too (although
   only for years in the range 1..9999).
+
+  Duration vs TimeInterval
+  ----------------------------
+  The ``times`` module exports two similiar types that are both used to
+  represent some amount of time: ``Duration`` and ``TimeInterval``.
+  This section explains how they differ and when one should be prefered over the
+  other (short answer: use ``Duration`` unless support for months and years is
+  needed).
+
+  Duration
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  A ``Duration`` represents a duration of time stored as seconds and
+  nanoseconds. A ``Duration`` is always fully normalized, so
+``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
+
+  Arithmetics with a ``Duration`` is very fast, especially when used with the
+  ``Time`` type, since it only involves basic arithmetic. Because ``Duration``
+  is more performant and easier to understand it should generally prefered.
+
+  TimeInterval
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  A ``TimeInterval`` represents some amount of time expressed in calendar
+  units, for example "1 year and 2 days". Since some units cannot be
+  normalized (the length of a year is different for leap years for example),
+  the ``TimeInterval`` type uses seperate fields for every unit. The
+  ``TimeInterval``'s returned form the this module generally don't normalize
+  **anything**, so even units that could be normalized (like seconds,
+  milliseconds and so on) are left untouched.
+
+  Arithmetics with a ``TimeInterval`` can be very slow, because it requires
+  timezone information.
+
+  Since it's slower and more complex, the ``TimeInterval`` type should be
+  avoided unless the program explicitly needs the features it offers that
+  ``Duration`` doesn't have.
+
+  How long is a day?
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  It should be especially noted that the handling of days differs between
+  ``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
+  as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
+
+  As an example, consider the amount of time between these two timestamps, both
+  in the same timezone:
+
+    - 2018-03-25T12:00+02:00
+    - 2018-03-26T12:00+01:00
+
+  If only the date & time is considered, it appears that exatly one day has
+  passed. However, the UTC offsets are different, which means that the
+  UTC offset was changed somewhere between. This happens twice each year for
+  timezones that use daylight savings time. Because of this change, the amount
+  of time that has passed is actually 25 hours.
+
+  The ``TimeInterval`` type uses calendar units, and will say that exactly one
+  day has passed. The ``Duration`` type on the other hand normalizes everything
+  to seconds, and will therefore say that 90000 seconds has passed, which is
+  the same as 25 hours.
 ]##
 
-import
-  strutils, algorithm, math, options, strformat
+import strutils, algorithm, math, options, strformat
 
 include "system/inclrtl"
 
-# This is really bad, but overflow checks are broken badly for
-# ints on the JS backend. See #6752.
 when defined(JS):
+  import jscore
+
+  # This is really bad, but overflow checks are broken badly for
+  # ints on the JS backend. See #6752.
   {.push overflowChecks: off.}
   proc `*`(a, b: int64): int64 =
-    system.`* `(a, b)
+    system.`*`(a, b)
   proc `*`(a, b: int): int =
-    system.`* `(a, b)
+    system.`*`(a, b)
   proc `+`(a, b: int64): int64 =
-    system.`+ `(a, b)
+    system.`+`(a, b)
   proc `+`(a, b: int): int =
-    system.`+ `(a, b)
+    system.`+`(a, b)
   proc `-`(a, b: int64): int64 =
-    system.`- `(a, b)
+    system.`-`(a, b)
   proc `-`(a, b: int): int =
-    system.`- `(a, b)
+    system.`-`(a, b)
   proc inc(a: var int, b: int) =
     system.inc(a, b)
   proc inc(a: var int64, b: int) =
     system.inc(a, b)
   {.pop.}
 
-when defined(posix):
+elif defined(posix):
   import posix
 
   type CTime = posix.Time
 
   var
     realTimeClockId {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
-    cpuClockId {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid
-
-  proc gettimeofday(tp: var Timeval, unused: pointer = nil) {.
-    importc: "gettimeofday", header: "<sys/time.h>".}
+    cpuClockId
+      {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid
 
   when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
     var timezone {.importc, header: "<time.h>".}: int
     when not defined(valgrind_workaround_10121):
       tzset()
 
+  when defined(macosx):
+    proc gettimeofday(tp: var Timeval, unused: pointer = nil)
+      {.importc: "gettimeofday", header: "<sys/time.h>".}
+
 elif defined(windows):
-  import winlean
+  import winlean, std/time_t
+
+  type CTime = time_t.Time
 
-  when defined(i386) and defined(gcc):
-    type CTime {.importc: "time_t", header: "<time.h>".} = distinct int32
-  else:
-    # newest version of Visual C++ defines time_t to be of 64 bits
-    type CTime {.importc: "time_t", header: "<time.h>".} = distinct int64
   # visual c's c runtime exposes these under a different name
   var timezone {.importc: "_timezone", header: "<time.h>".}: int
 
+  type
+    Tm {.importc: "struct tm", header: "<time.h>", final, pure.} = object
+      tm_sec*: cint   ## Seconds [0,60].
+      tm_min*: cint   ## Minutes [0,59].
+      tm_hour*: cint  ## Hour [0,23].
+      tm_mday*: cint  ## Day of month [1,31].
+      tm_mon*: cint   ## Month of year [0,11].
+      tm_year*: cint  ## Years since 1900.
+      tm_wday*: cint  ## Day of week [0,6] (Sunday =0).
+      tm_yday*: cint  ## Day of year [0,365].
+      tm_isdst*: cint ## Daylight Savings flag.
+
+  proc localtime(a1: var CTime): ptr Tm {.importc, header: "<time.h>".}
+
 type
-  Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
-                ## the month number in the range ``[1..12]``.
+  Month* = enum ## Represents a month. Note that the enum starts at ``1``,
+                ## so ``ord(month)`` will give the month number in the
+                ## range ``1..12``.
     mJan = (1, "January")
     mFeb = "February"
     mMar = "March"
@@ -213,13 +292,19 @@ type
     seconds: int64
     nanosecond: NanosecondRange
 
-  DateTime* = object of RootObj ## Represents a time in different parts.
-                                ## Although this type can represent leap
-                                ## seconds, they are generally not supported
-                                ## in this module. They are not ignored,
-                                ## but the ``DateTime``'s returned by
-                                ## procedures in this module will never have
-                                ## a leap second.
+  DateTime* = object of RootObj ## \
+      ## Represents a time in different parts. Although this type can represent
+      ## leap seconds, they are generally not supported in this module. They are
+      ## not ignored, but the ``DateTime``'s returned by procedures in this
+      ## module will never have a leap second.
+      ##
+      ## **Warning**: even though the fields of ``DateTime`` are exported,
+      ## they should never be mutated directly. Doing so is unsafe and will
+      ## result in the ``DateTime`` ending up in an invalid state.
+      ##
+      ## Instead of mutating the fields directly, use the ``Duration``
+      ## and ``TimeInterval`` types for arithmetic and use the ``initDateTime``
+      ## procedure for changing a specific field.
     nanosecond*: NanosecondRange ## The number of nanoseconds after the second,
                                  ## in the range 0 to 999_999_999.
     second*: SecondRange      ## The number of seconds after the minute,
@@ -230,27 +315,48 @@ type
     hour*: HourRange          ## The number of hours past midnight,
                               ## in the range 0 to 23.
     monthday*: MonthdayRange  ## The day of the month, in the range 1 to 31.
-    month*: Month             ## The current month.
-    year*: int                ## The current year, using astronomical year numbering
-                              ## (meaning that before year 1 is year 0, then year -1 and so on).
-    weekday*: WeekDay         ## The current day of the week.
+    month*: Month             ## The month.
+    year*: int                ## The year, using astronomical year numbering
+                              ## (meaning that before year 1 is year 0,
+                              ## then year -1 and so on).
+    weekday*: WeekDay         ## The day of the week.
     yearday*: YeardayRange    ## The number of days since January 1,
                               ## in the range 0 to 365.
     isDst*: bool              ## Determines whether DST is in effect.
                               ## Always false for the JavaScript backend.
-    timezone*: Timezone       ## The timezone represented as an implementation of ``Timezone``.
-    utcOffset*: int           ## The offset in seconds west of UTC, including any offset due to DST.
-                              ## Note that the sign of this number is the opposite
-                              ## of the one in a formatted offset string like ``+01:00``
-                              ## (which would be parsed into the UTC offset ``-3600``).
-
-  TimeInterval* = object ## Represents a non-fixed duration of time. Can be used to add and subtract
-                         ## non-fixed time units from a ``DateTime`` or ``Time``.
-                         ## ``TimeInterval`` doesn't represent a fixed duration of time,
-                         ## since the duration of some units depend on the context (e.g a year
-                         ## can be either 365 or 366 days long). The non-fixed time units are years,
-                         ## months and days.
+    timezone*: Timezone       ## The timezone represented as an implementation
+                              ## of ``Timezone``.
+    utcOffset*: int           ## The offset in seconds west of UTC, including
+                              ## any offset due to DST. Note that the sign of
+                              ## this number is the opposite of the one in a
+                              ## formatted offset string like ``+01:00`` (which
+                              ## would be equivalent to the UTC offset
+                              ## ``-3600``).
+
+  Duration* = object ## Represents a fixed duration of time, meaning a duration
+                     ## that has constant length independent of the context.
+    seconds: int64
+    nanosecond: NanosecondRange
 
+  TimeUnit* = enum ## Different units of time.
+    Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days,
+    Weeks, Months, Years
+
+  FixedTimeUnit* = range[Nanoseconds..Weeks] ## \
+      ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
+      ## These are the units that can be represented by a ``Duration``.
+
+  TimeInterval* = object ## \
+      ## Represents a non-fixed duration of time. Can be used to add and
+      ## subtract non-fixed time units from a ``DateTime`` or ``Time``.
+      ## Note that ``TimeInterval`` doesn't represent a fixed duration of time,
+      ## since the duration of some units depend on the context (e.g a year
+      ## can be either 365 or 366 days long). The non-fixed time units are
+      ## years, months, days and week.
+      ##
+      ## Note that ``TimeInterval``'s returned from the ``times`` module are
+      ## never normalized. If you want to normalize a time unit, ``Duration``
+      ## should be used instead.
     nanoseconds*: int  ## The number of nanoseconds
     microseconds*: int ## The number of microseconds
     milliseconds*: int ## The number of milliseconds
@@ -262,19 +368,6 @@ type
     months*: int       ## The number of months
     years*: int        ## The number of years
 
-  Duration* = object ## Represents a fixed duration of time.
-                     ## Uses the same time resolution as ``Time``.
-                     ## This type should be prefered over ``TimeInterval`` unless
-                     ## non-static time units is needed.
-    seconds: int64
-    nanosecond: NanosecondRange
-
-  TimeUnit* = enum ## Different units of time.
-    Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years
-
-  FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
-                                             ## These are the units that can be represented by a ``Duration``.
-
   Timezone* = ref object ## \
       ## Timezone interface for supporting ``DateTime``'s of arbritary
       ## timezones. The ``times`` module only supplies implementations for the
@@ -317,8 +410,10 @@ const unitWeights: array[FixedTimeUnit, int64] = [
   7 * secondsInDay * 1e9.int64,
 ]
 
-proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} =
+proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T
+    {.inline.} =
   ## Convert a quantity of some duration unit to another duration unit.
+  ## This proc only deals with integers, so the result might be truncated.
   runnableExamples:
     doAssert convert(Days, Hours, 2) == 48
     doAssert convert(Days, Weeks, 13) == 1 # Truncated
@@ -340,21 +435,41 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
   result.nanosecond = nanosecond.int
 
 # Forward declarations
-proc utcTzInfo(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZonedTimeFromTime(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc utcTzInfo(time: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
+proc localZonedTimeFromTime(time: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
+proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
 proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
-  {.tags: [], raises: [], benign noSideEffect.}
-
-proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration
-  {.tags: [], raises: [], benign noSideEffect.}
+    {.tags: [], raises: [], benign, noSideEffect.}
 
 proc nanosecond*(time: Time): NanosecondRange =
   ## Get the fractional part of a ``Time`` as the number
   ## of nanoseconds of the second.
   time.nanosecond
 
+proc initDuration*(nanoseconds, microseconds, milliseconds,
+                   seconds, minutes, hours, days, weeks: int64 = 0): Duration =
+  ## Create a new duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, milliseconds = 1)
+    doAssert dur.milliseconds == 1
+    doAssert dur.seconds == 1
+
+  let seconds = convert(Weeks, Seconds, weeks) +
+    convert(Days, Seconds, days) +
+    convert(Minutes, Seconds, minutes) +
+    convert(Hours, Seconds, hours) +
+    convert(Seconds, Seconds, seconds) +
+    convert(Milliseconds, Seconds, milliseconds) +
+    convert(Microseconds, Seconds, microseconds) +
+    convert(Nanoseconds, Seconds, nanoseconds)
+  let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
+    convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
+    nanoseconds mod 1_000_000_000).int
+  # Nanoseconds might be negative so we must normalize.
+  result = normalize[Duration](seconds, nanoseconds)
 
 proc weeks*(dur: Duration): int64 {.inline.} =
   ## Number of whole weeks represented by the duration.
@@ -407,9 +522,10 @@ proc fractional*(dur: Duration): Duration {.inline.} =
     doAssert dur.fractional == initDuration(nanoseconds = 5)
   initDuration(nanoseconds = dur.nanosecond)
 
-
-proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} =
-  ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``.
+proc fromUnix*(unix: int64): Time
+    {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``)
+  ## to a ``Time``.
   runnableExamples:
     doAssert $fromUnix(0).utc == "1970-01-01T00:00:00Z"
   initTime(unix, 0)
@@ -421,15 +537,16 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
   t.seconds
 
 proc fromWinTime*(win: int64): Time =
-  ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
-  ## to a ``Time``.
+  ## Convert a Windows file time (100-nanosecond intervals since
+  ## ``1601-01-01T00:00:00Z``) to a ``Time``.
   const hnsecsPerSec = convert(Seconds, Nanoseconds, 1) div 100
   let nanos = floorMod(win, hnsecsPerSec) * 100
   let seconds = floorDiv(win - epochDiff, hnsecsPerSec)
   result = initTime(seconds, nanos)
 
 proc toWinTime*(t: Time): int64 =
-  ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
+  ## Convert ``t`` to a Windows file time (100-nanosecond intervals
+  ## since ``1601-01-01T00:00:00Z``).
   result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
 
 proc isLeapYear*(year: int): bool =
@@ -437,7 +554,7 @@ proc isLeapYear*(year: int): bool =
   year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
 
 proc getDaysInMonth*(month: Month, year: int): int =
-  ## Get the number of days in a ``month`` of a ``year``.
+  ## Get the number of days in ``month`` of ``year``.
   # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
   case month
   of mFeb: result = if isLeapYear(year): 29 else: 28
@@ -448,15 +565,18 @@ proc getDaysInYear*(year: int): int =
   ## Get the number of days in a ``year``
   result = 365 + (if isLeapYear(year): 1 else: 0)
 
-proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
+proc assertValidDate(monthday: MonthdayRange, month: Month, year: int)
+    {.inline.} =
   assert monthday <= getDaysInMonth(month, year),
-    $year & "-" & intToStr(ord(month), 2) & "-" & $monthday & " is not a valid date"
+    $year & "-" & intToStr(ord(month), 2) & "-" & $monthday &
+      " is not a valid date"
 
 proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
   ## Get the epoch day from a year/month/day date.
-  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
-  assertValidDate monthday, month, year
+  ## The epoch day is the number of days since 1970/01/01
+  ## (it might be negative).
   # Based on http://howardhinnant.github.io/date_algorithms.html
+  assertValidDate monthday, month, year
   var (y, m, d) = (year, ord(month), monthday.int)
   if m <= 2:
     y.dec
@@ -467,9 +587,11 @@ proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
   let doe = yoe * 365 + yoe div 4 - yoe div 100 + doy
   return era * 146097 + doe - 719468
 
-proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month, year: int] =
+proc fromEpochDay(epochday: int64):
+    tuple[monthday: MonthdayRange, month: Month, year: int] =
   ## Get the year/month/day date from a epoch day.
-  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
+  ## The epoch day is the number of days since 1970/01/01
+  ## (it might be negative).
   # Based on http://howardhinnant.github.io/date_algorithms.html
   var z = epochday
   z.inc 719468
@@ -483,19 +605,23 @@ proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month,
   let m = mp + (if mp < 10: 3 else: -9)
   return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
 
-proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
+proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int):
+    YeardayRange {.tags: [], raises: [], benign.} =
   ## Returns the day of the year.
   ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``.
   assertValidDate monthday, month, year
-  const daysUntilMonth:     array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
-  const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
+  const daysUntilMonth: array[Month, int] =
+    [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+  const daysUntilMonthLeap: array[Month, int] =
+    [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
 
   if isLeapYear(year):
     result = daysUntilMonthLeap[month] + monthday - 1
   else:
     result = daysUntilMonth[month] + monthday - 1
 
-proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
+proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay
+    {.tags: [], raises: [], benign.} =
   ## Returns the day of the week enum from day, month and year.
   ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``.
   assertValidDate monthday, month, year
@@ -507,8 +633,7 @@ proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.
   # so we must correct for the WeekDay type.
   result = if wd == 0: dSun else: WeekDay(wd - 1)
 
-
-{. pragma: operator, rtl, noSideEffect, benign .}
+{.pragma: operator, rtl, noSideEffect, benign.}
 
 template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
   normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond)
@@ -526,28 +651,6 @@ template lqImpl(a: Duration|Time, b: Duration|Time): bool =
 
 template eqImpl(a: Duration|Time, b: Duration|Time): bool =
   a.seconds == b.seconds and a.nanosecond == b.nanosecond
-
-proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration =
-  runnableExamples:
-    let dur = initDuration(seconds = 1, milliseconds = 1)
-    doAssert dur.milliseconds == 1
-    doAssert dur.seconds == 1
-
-  let seconds = convert(Weeks, Seconds, weeks) +
-    convert(Days, Seconds, days) +
-    convert(Minutes, Seconds, minutes) +
-    convert(Hours, Seconds, hours) +
-    convert(Seconds, Seconds, seconds) +
-    convert(Milliseconds, Seconds, milliseconds) +
-    convert(Microseconds, Seconds, microseconds) +
-    convert(Nanoseconds, Seconds, nanoseconds)
-  let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
-    convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
-    nanoseconds mod 1_000_000_000).int
-  # Nanoseconds might be negative so we must normalize.
-  result = normalize[Duration](seconds, nanoseconds)
-
 const DurationZero* = initDuration() ## \
   ## Zero value for durations. Useful for comparisons.
   ##
@@ -564,7 +667,7 @@ proc toParts*(dur: Duration): DurationParts =
   ##
   ## This procedure is useful for converting ``Duration`` values to strings.
   runnableExamples:
-    var dp = toParts(initDuration(weeks=2, days=1))
+    var dp = toParts(initDuration(weeks = 2, days = 1))
     doAssert dp[Days] == 1
     doAssert dp[Weeks] == 2
     dp = toParts(initDuration(days = -1))
@@ -616,12 +719,14 @@ proc humanizeParts(parts: seq[string]): string =
     result.add "and " & parts[high(parts)]
 
 proc `$`*(dur: Duration): string =
-  ## Human friendly string representation of ``Duration``.
+  ## Human friendly string representation of a ``Duration``.
   runnableExamples:
     doAssert $initDuration(seconds = 2) == "2 seconds"
     doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
-    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
-    doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
+    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) ==
+      "1 hour, 2 minutes, and 3 seconds"
+    doAssert $initDuration(milliseconds = -1500) ==
+      "-1 second and -500 milliseconds"
   var parts = newSeq[string]()
   var numParts = toParts(dur)
 
@@ -659,7 +764,7 @@ proc `<`*(a, b: Duration): bool {.operator.} =
   ## Use ``abs(a) < abs(b)`` to compare the absolute
   ## duration.
   runnableExamples:
-    doAssert initDuration(seconds =  1) < initDuration(seconds = 2)
+    doAssert initDuration(seconds = 1) < initDuration(seconds = 2)
     doAssert initDuration(seconds = -2) < initDuration(seconds = 1)
   ltImpl(a, b)
 
@@ -669,23 +774,25 @@ proc `<=`*(a, b: Duration): bool {.operator.} =
 proc `==`*(a, b: Duration): bool {.operator.} =
   eqImpl(a, b)
 
-proc `*`*(a: int64, b: Duration): Duration {.operator} =
+proc `*`*(a: int64, b: Duration): Duration {.operator.} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
   normalize[Duration](a * b.seconds, a * b.nanosecond)
 
-proc `*`*(a: Duration, b: int64): Duration {.operator} =
+proc `*`*(a: Duration, b: int64): Duration {.operator.} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert initDuration(seconds = 1) * 5 == initDuration(seconds = 5)
   b * a
 
-proc `div`*(a: Duration, b: int64): Duration {.operator} =
+proc `div`*(a: Duration, b: int64): Duration {.operator.} =
   ## Integer division for durations.
   runnableExamples:
-    doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500)
-    doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1)
+    doAssert initDuration(seconds = 3) div 2 ==
+      initDuration(milliseconds = 1500)
+    doAssert initDuration(nanoseconds = 3) div 2 ==
+      initDuration(nanoseconds = 1)
   let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b)
   normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b)
 
@@ -714,7 +821,7 @@ proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
   ## Returns true iff ``a < b``, that is iff a happened before b.
   ltImpl(a, b)
 
-proc `<=` * (a, b: Time): bool {.operator, extern: "ntLeTime".} =
+proc `<=`*(a, b: Time): bool {.operator, extern: "ntLeTime".} =
   ## Returns true iff ``a <= b``.
   lqImpl(a, b)
 
@@ -783,8 +890,10 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
 
 proc newTimezone*(
       name: string,
-      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.},
-      zonedTimeFromAdjTimeImpl:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
+      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime
+          {.tags: [], raises: [], benign.},
+      zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime
+          {.tags: [], raises: [], benign.}
     ): Timezone =
   ## Create a new ``Timezone``.
   ##
@@ -847,11 +956,13 @@ proc `==`*(zone1, zone2: Timezone): bool =
     doAssert local() != utc()
   zone1.name == zone2.name
 
-proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
+proc inZone*(time: Time, zone: Timezone): DateTime
+    {.tags: [], raises: [], benign.} =
   ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone.
   result = initDateTime(zone.zonedTimeFromTime(time), zone)
 
-proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
+proc inZone*(dt: DateTime, zone: Timezone): DateTime
+    {.tags: [], raises: [], benign.} =
   ## Returns a ``DateTime`` representing the same point in time as ``dt`` but
   ## using ``zone`` as the timezone.
   dt.toTime.inZone(zone)
@@ -865,94 +976,38 @@ proc toAdjTime(dt: DateTime): Time =
   result = initTime(seconds, dt.nanosecond)
 
 when defined(JS):
-    type JsDate = object
-    proc newDate(year, month, date, hours, minutes, seconds, milliseconds: int): JsDate {.tags: [], raises: [], importc: "new Date".}
-    proc newDate(): JsDate {.importc: "new Date".}
-    proc newDate(value: float): JsDate {.importc: "new Date".}
-    proc getTimezoneOffset(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getTime(js: JsDate): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
-    proc getDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
-
-    proc localZonedTimeFromTime(time: Time): ZonedTime =
-      let jsDate = newDate(time.seconds.float * 1000)
-      let offset = jsDate.getTimezoneOffset() * secondsInMin
-      result.time = time
-      result.utcOffset = offset
-      result.isDst = false
-
-    proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
-      let utcDate = newDate(adjTime.seconds.float * 1000)
-      let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
-        utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
-
-      # This is as dumb as it looks - JS doesn't support years in the range 0-99 in the constructor
-      # because they are assumed to be 19xx...
-      # Because JS doesn't support timezone history, it doesn't really matter in practice.
-      if utcDate.getUTCFullYear() in 0 .. 99:
-        localDate.setFullYear(utcDate.getUTCFullYear())
-
-      result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
-      result.time = adjTime + initDuration(seconds = result.utcOffset)
-      result.isDst = false
+  proc localZonedTimeFromTime(time: Time): ZonedTime =
+    let jsDate = newDate(time.seconds * 1000)
+    let offset = jsDate.getTimezoneOffset() * secondsInMin
+    result.time = time
+    result.utcOffset = offset
+    result.isDst = false
 
-else:
-  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
-      defined(macosx):
-    type
-      StructTm {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        gmtoff {.importc: "tm_gmtoff".}: clong
-  else:
-    type
-      StructTm {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        when defined(linux) and defined(amd64) or defined(haiku):
-          gmtoff {.importc: "tm_gmtoff".}: clong
-          zone {.importc: "tm_zone".}: cstring
-  type
-    StructTmPtr = ptr StructTm
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
+    let utcDate = newDate(adjTime.seconds * 1000)
+    let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(),
+        utcDate.getUTCDate(), utcDate.getUTCHours(), utcDate.getUTCMinutes(),
+        utcDate.getUTCSeconds(), 0)
 
-  proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
+    # This is as dumb as it looks - JS doesn't support years in the range
+    # 0-99 in the constructor because they are assumed to be 19xx...
+    # Because JS doesn't support timezone history,
+    # it doesn't really matter in practice.
+    if utcDate.getUTCFullYear() in 0 .. 99:
+      localDate.setFullYear(utcDate.getUTCFullYear())
 
-  proc toAdjUnix(tm: StructTm): int64 =
-    let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
+    result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
+    result.time = adjTime + initDuration(seconds = result.utcOffset)
+    result.isDst = false
+
+else:
+  proc toAdjUnix(tm: Tm): int64 =
+    let epochDay = toEpochday(tm.tm_mday, (tm.tm_mon + 1).Month,
+                              tm.tm_year.int + 1900)
     result = epochDay * secondsInDay
-    result.inc tm.hour * secondsInHour
-    result.inc tm.minute * 60
-    result.inc tm.second
+    result.inc tm.tm_hour * secondsInHour
+    result.inc tm.tm_min * 60
+    result.inc tm.tm_sec
 
   proc getLocalOffsetAndDst(unix: int64): tuple[offset: int, dst: bool] =
     # Windows can't handle unix < 0, so we fall back to unix = 0.
@@ -960,7 +1015,7 @@ else:
     when defined(windows):
       if unix < 0:
         var a = 0.CTime
-        let tmPtr = localtime(addr(a))
+        let tmPtr = localtime(a)
         if not tmPtr.isNil:
           let tm = tmPtr[]
           return ((0 - tm.toAdjUnix).int, false)
@@ -969,10 +1024,10 @@ else:
     # In case of a 32-bit time_t, we fallback to the closest available
     # timezone information.
     var a = clamp(unix, low(CTime), high(CTime)).CTime
-    let tmPtr = localtime(addr(a))
+    let tmPtr = localtime(a)
     if not tmPtr.isNil:
       let tm = tmPtr[]
-      return ((a.int64 - tm.toAdjUnix).int, tm.isdst > 0)
+      return ((a.int64 - tm.toAdjUnix).int, tm.tm_isdst > 0)
     return (0, false)
 
   proc localZonedTimeFromTime(time: Time): ZonedTime =
@@ -981,7 +1036,7 @@ else:
     result.utcOffset = offset
     result.isDst = dst
 
-  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime  =
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
     var adjUnix = adjTime.seconds
     let past = adjUnix - secondsInDay
     let (pastOffset, _) = getLocalOffsetAndDst(past)
@@ -991,7 +1046,7 @@ else:
 
     var utcOffset: int
     if pastOffset == futureOffset:
-        utcOffset = pastOffset.int
+      utcOffset = pastOffset.int
     else:
       if pastOffset > futureOffset:
         adjUnix -= secondsInHour
@@ -1025,8 +1080,8 @@ proc utc*(): TimeZone =
 proc local*(): TimeZone =
   ## Get the ``Timezone`` implementation for the local timezone.
   runnableExamples:
-   doAssert now().timezone == local()
-   doAssert local().name == "LOCAL"
+    doAssert now().timezone == local()
+    doAssert local().name == "LOCAL"
   if localInstance.isNil:
     localInstance = newTimezone("LOCAL", localZonedTimeFromTime,
       localZonedTimeFromAdjTime)
@@ -1056,11 +1111,11 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
     let nanos = convert(Milliseconds, Nanoseconds,
       millis mod convert(Seconds, Milliseconds, 1).int)
     result = initTime(seconds, nanos)
-  # I'm not entirely certain if freebsd needs to use `gettimeofday`.
-  elif defined(macosx) or defined(freebsd):
+  elif defined(macosx):
     var a: Timeval
     gettimeofday(a)
-    result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int))
+    result = initTime(a.tv_sec.int64,
+                      convert(Microseconds, Nanoseconds, a.tv_usec.int))
   elif defined(posix):
     var ts: Timespec
     discard clock_gettime(realTimeClockId, ts)
@@ -1081,13 +1136,17 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
                        days, weeks, months, years: int = 0): TimeInterval =
   ## Creates a new ``TimeInterval``.
   ##
+  ## This proc doesn't perform any normalization! For example,
+  ## ``initTimeInterval(hours = 24)`` and ``initTimeInterval(days = 1)`` are
+  ## not equal.
+  ##
   ## You can also use the convenience procedures called ``milliseconds``,
   ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
-  ##
   runnableExamples:
-    let day = initTimeInterval(hours=24)
+    let day = initTimeInterval(hours = 24)
     let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
     doAssert $(dt + day) == "2000-01-02T12:00:00Z"
+    doAssert initTimeInterval(hours = 24) != initTimeInterval(days = 1)
   result.nanoseconds = nanoseconds
   result.microseconds = microseconds
   result.milliseconds = milliseconds
@@ -1115,7 +1174,7 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
 proc `-`*(ti: TimeInterval): TimeInterval =
   ## Reverses a time interval
   runnableExamples:
-    let day = -initTimeInterval(hours=24)
+    let day = -initTimeInterval(hours = 24)
     doAssert day.hours == -24
 
   result = TimeInterval(
@@ -1136,9 +1195,9 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ##
   ## Time components are subtracted one-by-one, see output:
   runnableExamples:
-    let ti1 = initTimeInterval(hours=24)
-    let ti2 = initTimeInterval(hours=4)
-    doAssert (ti1 - ti2) == initTimeInterval(hours=20)
+    let ti1 = initTimeInterval(hours = 24)
+    let ti2 = initTimeInterval(hours = 4)
+    doAssert (ti1 - ti2) == initTimeInterval(hours = 20)
 
   result = ti1 + (-ti2)
 
@@ -1155,13 +1214,13 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
     ':' & intToStr(dt.second, 2)
 
 proc toParts* (ti: TimeInterval): TimeIntervalParts =
-  ## Converts a `TimeInterval` into an array consisting of its time units,
-  ## starting with nanoseconds and ending with years
+  ## Converts a ``TimeInterval`` into an array consisting of its time units,
+  ## starting with nanoseconds and ending with years.
   ##
   ## This procedure is useful for converting ``TimeInterval`` values to strings.
   ## E.g. then you need to implement custom interval printing
   runnableExamples:
-    var tp = toParts(initTimeInterval(years=1, nanoseconds=123))
+    var tp = toParts(initTimeInterval(years = 1, nanoseconds = 123))
     doAssert tp[Years] == 1
     doAssert tp[Nanoseconds] == 123
 
@@ -1171,9 +1230,10 @@ proc toParts* (ti: TimeInterval): TimeIntervalParts =
     index += 1
 
 proc `$`*(ti: TimeInterval): string =
-  ## Get string representation of `TimeInterval`
+  ## Get string representation of ``TimeInterval``.
   runnableExamples:
-    doAssert $initTimeInterval(years=1, nanoseconds=123) == "1 year and 123 nanoseconds"
+    doAssert $initTimeInterval(years = 1, nanoseconds = 123) ==
+      "1 year and 123 nanoseconds"
     doAssert $initTimeInterval() == "0 nanoseconds"
 
   var parts: seq[string] = @[]
@@ -1199,7 +1259,7 @@ proc milliseconds*(ms: int): TimeInterval {.inline.} =
 proc seconds*(s: int): TimeInterval {.inline.} =
   ## TimeInterval of ``s`` seconds.
   ##
-  ## ``echo getTime() + 5.second``
+  ## ``echo getTime() + 5.seconds``
   initTimeInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
@@ -1238,7 +1298,8 @@ proc years*(y: int): TimeInterval {.inline.} =
   ## ``echo getTime() + 2.years``
   initTimeInterval(years = y)
 
-proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDur: Duration] =
+proc evaluateInterval(dt: DateTime, interval: TimeInterval):
+    tuple[adjDur, absDur: Duration] =
   ## Evaluates how many nanoseconds the interval is worth
   ## in the context of ``dt``.
   ## The result in split into an adjusted diff and an absolute diff.
@@ -1277,10 +1338,10 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDu
     minutes = interval.minutes,
     hours = interval.hours)
 
-
 proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
                    hour: HourRange, minute: MinuteRange, second: SecondRange,
-                   nanosecond: NanosecondRange, zone: Timezone = local()): DateTime =
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime =
   ## Create a new ``DateTime`` in the specified timezone.
   runnableExamples:
     let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc())
@@ -1288,12 +1349,12 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
 
   assertValidDate monthday, month, year
   let dt = DateTime(
-    monthday:  monthday,
-    year:  year,
-    month:  month,
-    hour:  hour,
-    minute:  minute,
-    second:  second,
+    monthday: monthday,
+    year: year,
+    month: month,
+    hour: hour,
+    minute: minute,
+    second: second,
     nanosecond: nanosecond
   )
   result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone)
@@ -1310,14 +1371,15 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
 
 proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
   ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
-  ##
-  ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
-  ## month doesn't have enough days it, the month will be incremented and the monthday will be
-  ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
-  ## which will overflow and result in `1 December`.
+  ## in the order of their size, i.e first the ``years`` component, then the
+  ## ``months`` component and so on. The returned ``DateTime`` will have the
+  ## same timezone as the input.
   ##
+  ## Note that when adding months, monthday overflow is allowed. This means that
+  ## if the resulting month doesn't have enough days it, the month will be
+  ## incremented and the monthday will be set to the number of days overflowed.
+  ## So adding one month to `31 October` will result in `31 November`, which
+  ## will overflow and result in `1 December`.
   runnableExamples:
     let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
     doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z"
@@ -1337,9 +1399,10 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
     result = initDateTime(zt, dt.timezone)
 
 proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
-  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
+  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are
+  ## subtracted in the order of their size, i.e first the ``years`` component,
+  ## then the ``months`` component and so on. The returned ``DateTime`` will
+  ## have the same timezone as the input.
   runnableExamples:
     let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
     doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z"
@@ -1373,15 +1436,15 @@ proc `-`*(dt1, dt2: DateTime): Duration =
   dt1.toTime - dt2.toTime
 
 proc `<`*(a, b: DateTime): bool =
-  ## Returns true iff ``a < b``, that is iff a happened before b.
+  ## Returns true iff ``a`` happened before ``b``.
   return a.toTime < b.toTime
 
-proc `<=` * (a, b: DateTime): bool =
-  ## Returns true iff ``a <= b``.
+proc `<=`*(a, b: DateTime): bool =
+  ## Returns true iff ``a`` happened before or at the same time as ``b``.
   return a.toTime <= b.toTime
 
 proc `==`*(a, b: DateTime): bool =
-  ## Returns true if ``a == b``, that is if both dates represent the same point in time.
+  ## Returns true iff ``a`` and ``b`` represent the same point in time.
   return a.toTime == b.toTime
 
 proc isStaticInterval(interval: TimeInterval): bool =
@@ -1399,97 +1462,108 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration =
 
 proc between*(startDt, endDt: DateTime): TimeInterval =
   ## Gives the difference between ``startDt`` and ``endDt`` as a
-  ## ``TimeInterval``.
+  ## ``TimeInterval``. The following guarantees about the result is given:
   ##
-  ## **Warning:** This proc currently gives very few guarantees about the
-  ## result. ``a + between(a, b) == b`` is **not** true in general
-  ## (it's always true when UTC is used however). Neither is it guaranteed that
-  ## all components in the result will have the same sign. The behavior of this
-  ## proc might change in the future.
+  ## - All fields will have the same sign.
+  ## - If `startDt.timezone == endDt.timezone`, it is guaranteed that
+  ##   `startDt + between(startDt, endDt) == endDt`.
+  ## - If `startDt.timezone != endDt.timezone`, then the result will be
+  ##   equivalent to `between(startDt.utc, endDt.utc)`.
   runnableExamples:
     var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc())
     var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc())
-    var ti = initTimeInterval(years = 2, days = 7, hours = 3, seconds = 15)
+    var ti = initTimeInterval(years = 2, weeks = 1, hours = 3, seconds = 15)
     doAssert between(a, b) == ti
     doAssert between(a, b) == -between(b, a)
 
-  var startDt = startDt.utc()
-  var endDt = endDt.utc()
-
-  if endDt == startDt:
-    return initTimeInterval()
+  if startDt.timezone != endDt.timezone:
+    return between(startDt.utc, endDt.utc)
   elif endDt < startDt:
     return -between(endDt, startDt)
 
-  var coeffs: array[FixedTimeUnit, int64] = unitWeights
-  var timeParts: array[FixedTimeUnit, int]
-  for unit in Nanoseconds..Weeks:
-    timeParts[unit] = 0
-
-  for unit in Seconds..Days:
-    coeffs[unit] = coeffs[unit] div unitWeights[Seconds]
-
-  var startTimepart = initTime(
-    nanosecond = startDt.nanosecond,
-    unix = startDt.hour * coeffs[Hours] + startDt.minute * coeffs[Minutes] +
-    startDt.second
-  )
-  var endTimepart = initTime(
-    nanosecond = endDt.nanosecond,
-    unix = endDt.hour * coeffs[Hours] + endDt.minute * coeffs[Minutes] +
-    endDt.second
-  )
-  # We wand timeParts for Seconds..Hours be positive, so we'll borrow one day
-  if endTimepart < startTimepart:
-    timeParts[Days] = -1
-
-  let diffTime = endTimepart - startTimepart
-  timeParts[Seconds] = diffTime.seconds.int()
-  #Nanoseconds - preliminary count
-  timeParts[Nanoseconds] = diffTime.nanoseconds
-  for unit in countdown(Milliseconds, Microseconds):
-    timeParts[unit] += timeParts[Nanoseconds] div coeffs[unit].int()
-    timeParts[Nanoseconds] -= timeParts[unit] * coeffs[unit].int()
-
-  #Counting Seconds .. Hours - final, Days - preliminary
-  for unit in countdown(Days, Minutes):
-    timeParts[unit] += timeParts[Seconds] div coeffs[unit].int()
-    # Here is accounted the borrowed day
-    timeParts[Seconds] -= timeParts[unit] * coeffs[unit].int()
-
-  # Set Nanoseconds .. Hours in result
-  result.nanoseconds = timeParts[Nanoseconds]
-  result.microseconds = timeParts[Microseconds]
-  result.milliseconds = timeParts[Milliseconds]
-  result.seconds = timeParts[Seconds]
-  result.minutes = timeParts[Minutes]
-  result.hours = timeParts[Hours]
-
-  #Days
-  if endDt.monthday.int + timeParts[Days] < startDt.monthday.int():
-    if endDt.month > 1.Month:
-      endDt.month -= 1.Month
+  type Date = tuple[year, month, monthday: int]
+  var startDate: Date = (startDt.year, startDt.month.ord, startDt.monthday)
+  var endDate: Date = (endDt.year, endDt.month.ord, endDt.monthday)
+
+  # Subtract one day from endDate if time of day is earlier than startDay
+  # The subtracted day will be counted by fixed units (hour and lower)
+  # at the end of this proc
+  if (endDt.hour, endDt.minute, endDt.second, endDt.nanosecond) <
+      (startDt.hour, startDt.minute, startDt.second, startDt.nanosecond):
+    if endDate.month == 1 and endDate.monthday == 1:
+      endDate.year.dec
+      endDate.monthday = 31
+      endDate.month = 12
+    elif endDate.monthday == 1:
+      endDate.month.dec
+      endDate.monthday = getDaysInMonth(endDate.month.Month, endDate.year)
     else:
-      endDt.month = 12.Month
-      endDt.year -= 1
-    timeParts[Days] += endDt.monthday.int() + getDaysInMonth(
-      endDt.month, endDt.year) - startDt.monthday.int()
-  else:
-    timeParts[Days] += endDt.monthday.int() -
-      startDt.monthday.int()
-
-  result.days = timeParts[Days]
-
-  #Months
-  if endDt.month < startDt.month:
-      result.months = endDt.month.int() + 12 - startDt.month.int()
-      endDt.year -= 1
-  else:
-    result.months = endDt.month.int() -
-      startDt.month.int()
+      endDate.monthday.dec
 
   # Years
-  result.years = endDt.year - startDt.year
+  result.years.inc endDate.year - startDate.year - 1
+  if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday):
+    result.years.inc
+  startDate.year.inc result.years
+
+  # Months
+  if startDate.year < endDate.year:
+    result.months.inc 12 - startDate.month # Move to dec
+    if endDate.month != 1 or (startDate.monthday <= endDate.monthday):
+      result.months.inc
+      startDate.year = endDate.year
+      startDate.month = 1
+    else:
+      startDate.month = 12
+  if startDate.year == endDate.year:
+    if (startDate.monthday <= endDate.monthday):
+      result.months.inc endDate.month - startDate.month
+      startDate.month = endDate.month
+    elif endDate.month != 1:
+      let month = endDate.month - 1
+      let daysInMonth = getDaysInMonth(month.Month, startDate.year)
+      if daysInMonth < startDate.monthday:
+        if startDate.monthday - daysInMonth < endDate.monthday:
+          result.months.inc endDate.month - startDate.month - 1
+          startDate.month = endDate.month
+          startDate.monthday = startDate.monthday - daysInMonth
+        else:
+          result.months.inc endDate.month - startDate.month - 2
+          startDate.month = endDate.month - 2
+      else:
+        result.months.inc endDate.month - startDate.month - 1
+        startDate.month = endDate.month - 1
+
+  # Days
+  # This means that start = dec and end = jan
+  if startDate.year < endDate.year:
+    result.days.inc 31 - startDate.monthday + endDate.monthday
+    startDate = endDate
+  else:
+    while startDate.month < endDate.month:
+      let daysInMonth = getDaysInMonth(startDate.month.Month, startDate.year)
+      result.days.inc daysInMonth - startDate.monthday + 1
+      startDate.month.inc
+      startDate.monthday = 1
+    result.days.inc endDate.monthday - startDate.monthday
+    result.weeks = result.days div 7
+    result.days = result.days mod 7
+    startDate = endDate
+
+  # Handle hours, minutes, seconds, milliseconds, microseconds and nanoseconds
+  let newStartDt = initDateTime(startDate.monthday, startDate.month.Month,
+    startDate.year, startDt.hour, startDt.minute, startDt.second,
+    startDt.nanosecond, startDt.timezone)
+  let dur = endDt - newStartDt
+  let parts = toParts(dur)
+  # There can still be a full day in `parts` since `Duration` and `TimeInterval`
+  # models days differently.
+  result.hours = parts[Hours].int + parts[Days].int * 24
+  result.minutes = parts[Minutes].int
+  result.seconds = parts[Seconds].int
+  result.milliseconds = parts[Milliseconds].int
+  result.microseconds = parts[Microseconds].int
+  result.nanoseconds = parts[Nanoseconds].int
 
 proc `+`*(time: Time, interval: TimeInterval): Time =
   ## Adds `interval` to `time`.
@@ -1604,7 +1678,7 @@ type
   TimeFormatParseError* = object of ValueError ## \
     ## Raised when parsing a ``TimeFormat`` string fails.
 
-const FormatLiterals = { ' ', '-', '/', ':', '(', ')', '[', ']', ',' }
+const FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','}
 
 proc `$`*(f: TimeFormat): string =
   ## Returns the format string that was used to construct ``f``.
@@ -1672,9 +1746,9 @@ iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] =
         i.inc
         yield (tkLiteral, token)
     of FormatLiterals:
-        yieldCurrToken()
-        yield (tkLiteral, $f[i])
-        i.inc
+      yieldCurrToken()
+      yield (tkLiteral, $f[i])
+      i.inc
     else:
       # Check if the letter being added matches previous accumulated buffer.
       if currToken.len == 0 or currToken[0] == f[i]:
@@ -1832,12 +1906,12 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) =
     else:
       result.add '+' & $year
   of UUUU:
-      result.add $dt.year
+    result.add $dt.year
   of z, zz, zzz, zzzz:
     if dt.timezone != nil and dt.timezone.name == "Etc/UTC":
       result.add 'Z'
     else:
-      result.add  if -dt.utcOffset >= 0: '+' else: '-'
+      result.add if -dt.utcOffset >= 0: '+' else: '-'
       let absOffset = abs(dt.utcOffset)
       case pattern:
       of z:
@@ -1856,7 +1930,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) =
         result.add h & ":" & m & ":" & s
       else: assert false
   of g:
-      result.add if dt.year < 1: "BC" else: "AD"
+    result.add if dt.year < 1: "BC" else: "AD"
   of Lit: assert false # Can't happen
 
 proc parsePattern(input: string, pattern: FormatPattern, i: var int,
@@ -2017,7 +2091,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     result = year > 0
   of yyyy:
     let year =
-      if input[i] in { '+', '-' }:
+      if input[i] in {'+', '-'}:
         takeInt(4..high(int), allowSign = true)
       else:
         takeInt(4..4)
@@ -2029,7 +2103,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     result = year > 0
   of uuuu:
     let year =
-      if input[i] in { '+', '-' }:
+      if input[i] in {'+', '-'}:
         takeInt(4..high(int), allowSign = true)
       else:
         takeInt(4..4)
@@ -2244,7 +2318,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime
 
   if patIdx <= f.patterns.high:
     raiseParseException(f, input,
-                        "Parsing ended but there was still patterns remaining")
+                            "Parsing ended but there was still patterns remaining")
 
   result = toDateTime(parsed, zone, f, input)
 
@@ -2261,8 +2335,8 @@ proc parse*(input, f: string, tz: Timezone = local()): DateTime
   let dtFormat = initTimeFormat(f)
   result = input.parse(dtFormat, tz)
 
-proc parse*(input: string, f: static[string], zone: Timezone = local()): DateTime
-    {.raises: [TimeParseError, Defect].} =
+proc parse*(input: string, f: static[string], zone: Timezone = local()):
+    DateTime {.raises: [TimeParseError, Defect].} =
   ## Overload that validates ``f`` at compile time.
   const f2 = initTimeFormat(f)
   result = input.parse(f2, zone)
@@ -2298,7 +2372,7 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
   result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz")
 
 proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
-  ## converts a `Time` value to a string representation. It will use the local
+  ## Converts a `Time` value to a string representation. It will use the local
   ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
   runnableExamples:
     let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
@@ -2306,34 +2380,48 @@ proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
     doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz")
   $time.local
 
-proc countLeapYears*(yearSpan: int): int =
+proc countLeapYears*(yearSpan: int): int
+    {.deprecated.} =
   ## Returns the number of leap years spanned by a given number of years.
   ##
   ## **Note:** For leap years, start date is assumed to be 1 AD.
   ## counts the number of leap years up to January 1st of a given year.
   ## Keep in mind that if specified year is a leap year, the leap day
   ## has not happened before January 1st of that year.
+  ##
+  ## **Deprecated since v0.20.0**.
   (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400
 
-proc countDays*(yearSpan: int): int =
+proc countDays*(yearSpan: int): int
+    {.deprecated.} =
   ## Returns the number of days spanned by a given number of years.
+  ##
+  ## **Deprecated since v0.20.0**.
   (yearSpan - 1) * 365 + countLeapYears(yearSpan)
 
-proc countYears*(daySpan: int): int =
+proc countYears*(daySpan: int): int
+    {.deprecated.} =
   ## Returns the number of years spanned by a given number of days.
+  ##
+  ## **Deprecated since v0.20.0**.
   ((daySpan - countLeapYears(daySpan div 365)) div 365)
 
-proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
+proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int]
+    {.deprecated.} =
   ## Returns the number of years spanned by a given number of days and the
   ## remainder as days.
+  ##
+  ## **Deprecated since v0.20.0**.
   let days = daySpan - countLeapYears(daySpan div 365)
   result.years = days div 365
   result.days = days mod 365
 
-proc toTimeInterval*(time: Time): TimeInterval =
-  ## Converts a Time to a TimeInterval.
+proc toTimeInterval*(time: Time): TimeInterval
+    {.deprecated: "Use `between` instead".} =
+  ## Converts a Time to a TimeInterval. To be used when diffing times.
   ##
-  ## To be used when diffing times. Consider using `between` instead.
+  ## **Deprecated since version 0.20.0:** Use the `between proc
+  ## <#between,DateTime,DateTime>`_ instead.
   runnableExamples:
     let a = fromUnix(10)
     let b = fromUnix(1_500_000_000)
@@ -2347,7 +2435,8 @@ when not defined(JS):
   type
     Clock {.importc: "clock_t".} = distinct int
 
-  proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
+  proc getClock(): Clock
+      {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
 
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
@@ -2384,10 +2473,15 @@ when not defined(JS):
       ## on the hardware/OS).
       ##
       ## ``getTime`` should generally be prefered over this proc.
-      when defined(posix):
+      when defined(macosx):
         var a: Timeval
         gettimeofday(a)
         result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
+      elif defined(posix):
+        var ts: Timespec
+        discard clock_gettime(realTimeClockId, ts)
+        result = toBiggestFloat(ts.tv_sec.int64) +
+          toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000
       elif defined(windows):
         var f: winlean.FILETIME
         getSystemTimeAsFileTime(f)
@@ -2405,59 +2499,68 @@ when defined(JS):
 # Deprecated procs
 
 when not defined(JS):
-  proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
+  proc unixTimeToWinTime*(time: CTime): int64
+      {.deprecated: "Use toWinTime instead".} =
     ## Converts a UNIX `Time` (``time_t``) to a Windows file time
     ##
     ## **Deprecated:** use ``toWinTime`` instead.
     result = int64(time) * rateDiff + epochDiff
 
-  proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
+  proc winTimeToUnixTime*(time: int64): CTime
+      {.deprecated: "Use fromWinTime instead".} =
     ## Converts a Windows time to a UNIX `Time` (``time_t``)
     ##
     ## **Deprecated:** use ``fromWinTime`` instead.
     result = CTime((time - epochDiff) div rateDiff)
 
-proc initInterval*(seconds, minutes, hours, days, months,
-                   years: int = 0): TimeInterval {.deprecated.} =
+proc initInterval*(seconds, minutes, hours, days, months, years: int = 0):
+    TimeInterval {.deprecated.} =
   ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.
   initTimeInterval(0, 0, 0, seconds, minutes, hours, days, 0, months, years)
 
-proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign, deprecated.} =
+proc fromSeconds*(since1970: float): Time
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Takes a float which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  let nanos = ((since1970 - since1970.int64.float) * convert(Seconds, Nanoseconds, 1).float).int
+  let nanos = ((since1970 - since1970.int64.float) *
+    convert(Seconds, Nanoseconds, 1).float).int
   initTime(since1970.int64, nanos)
 
-proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprecated.} =
+proc fromSeconds*(since1970: int64): Time
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Takes an int which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
   fromUnix(since1970)
 
-proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
+proc toSeconds*(time: Time): float
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Returns the time in seconds since the unix epoch.
   ##
   ## **Deprecated since v0.18.0:** use ``toUnix`` instead
   time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
 
-proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+proc getLocalTime*(time: Time): DateTime
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-time representation,
   ## expressed relative to the user's specified time zone.
   ##
   ## **Deprecated since v0.18.0:** use ``local`` instead
   time.local
 
-proc getGMTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+proc getGMTime*(time: Time): DateTime
+      {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-down time representation,
   ## expressed in Coordinated Universal Time (UTC).
   ##
   ## **Deprecated since v0.18.0:** use ``utc`` instead
   time.utc
 
-proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} =
+proc getTimezone*(): int
+    {.tags: [TimeEffect], raises: [], benign, deprecated.} =
   ## Returns the offset of the local (non-DST) timezone in seconds west of UTC.
   ##
   ## **Deprecated since v0.18.0:** use ``now().utcOffset`` to get the current
@@ -2465,45 +2568,14 @@ proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.}
   when defined(JS):
     return newDate().getTimezoneOffset() * 60
   elif defined(freebsd) or defined(netbsd) or defined(openbsd):
-    var a: CTime
-    discard time(a)
-    let lt = localtime(addr(a))
-    # BSD stores in `gmtoff` offset east of UTC in seconds,
-    # but posix systems using west of UTC in seconds
-    return -(lt.gmtoff)
+    # This is wrong since it will include DST offsets, but the behavior has
+    # always been wrong for bsd and the proc is deprecated so lets ignore it.
+    return now().utcOffset
   else:
     return timezone
 
-proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
-  ## Converts a broken-down time structure to calendar time representation.
-  ##
-  ## **Deprecated since v0.14.0:** use ``toTime`` instead.
-  dt.toTime
-
-when defined(JS):
-  var start = getTime()
-  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    let dur = getTime() - start
-    result = (convert(Seconds, Milliseconds, dur.seconds) +
-      convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
-else:
-  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    ## get the milliseconds from the start of the program.
-    ##
-    ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
-    when defined(macosx):
-      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
-    else:
-      result = int(getClock()) div (clocksPerSec div 1000)
-
-proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
-  ## Converts a Time to a TimeInterval.
-  ##
-  ## **Deprecated since v0.14.0:** use ``toTimeInterval`` instead.
-  # Milliseconds not available from Time
-  t.toTimeInterval()
-
-proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
+proc getDayOfWeek*(day, month, year: int): WeekDay
+    {.tags: [], raises: [], benign, deprecated.} =
   ## **Deprecated since v0.18.0:** use
   ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
   getDayOfWeek(day, month.Month, year)
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 5f5bfdbd7..a373a9370 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -10,7 +10,10 @@
 ## This module defines compile-time reflection procs for
 ## working with types
 
+include "system/helpers" # for `isNamedTuple`
+
 export system.`$`
+export isNamedTuple
 
 proc name*(t: typedesc): string {.magic: "TypeTrait".}
   ## Alias for system.`$`(t) since Nim v0.20.0.
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 712cc46c8..27eabecc6 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -8,6 +8,11 @@
 #
 
 ## This module provides support to handle the Unicode UTF-8 encoding.
+##
+## There are no specialized ``insert``, ``delete``, ``add`` and ``contains``
+## procedures for ``seq[Rune]`` in this module because the generic variants
+## of these procedures in the system module already work with it.
+
 
 {.deadCodeElim: on.}  # dce option deprecated
 
@@ -15,7 +20,7 @@ include "system/inclrtl"
 
 type
   RuneImpl = int32 # underlying type of Rune
-  Rune* = distinct RuneImpl   ## type that can hold any Unicode character
+  Rune* = distinct RuneImpl ## Unicode code point. Can hold any Unicode character.
   Rune16* = distinct int16 ## 16 bit Unicode character
 
 proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b)
@@ -25,7 +30,12 @@ proc `==`*(a, b: Rune): bool = return int(a) == int(b)
 template ones(n: untyped): untyped = ((1 shl n)-1)
 
 proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
-  ## Returns the number of Unicode characters of the string ``s``
+  ## Returns the number of runes of the string ``s``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLen == 6
+    ## note: a.len == 8
+
   var i = 0
   while i < len(s):
     if ord(s[i]) <=% 127: inc(i)
@@ -38,7 +48,12 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
     inc(result)
 
 proc runeLenAt*(s: string, i: Natural): int =
-  ## Returns the number of bytes the rune starting at ``s[i]`` takes
+  ## Returns the number of bytes the rune starting at ``s[i]`` takes.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLenAt(0) == 1
+    doAssert a.runeLenAt(1) == 2
+
   if ord(s[i]) <=% 127: result = 1
   elif ord(s[i]) shr 5 == 0b110: result = 2
   elif ord(s[i]) shr 4 == 0b1110: result = 3
@@ -50,7 +65,7 @@ proc runeLenAt*(s: string, i: Natural): int =
 const replRune = Rune(0xFFFD)
 
 template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) =
-  ## Returns the Unicode character ``s[i]`` in ``result``. If ``doInc == true``
+  ## Returns the rune ``s[i]`` in ``result``. If ``doInc == true``
   ## ``i`` is incremented by the number of bytes that have been processed.
   bind ones
   if ord(s[i]) <=% 127:
@@ -152,17 +167,21 @@ proc validateUtf8*(s: string): int =
   return -1
 
 proc runeAt*(s: string, i: Natural): Rune =
-  ## Returns the unicode character in ``s`` at byte index ``i``
+  ## Returns the rune in ``s`` at **byte index** ``i``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeAt(1) == "ñ".runeAt(0)
+    doAssert a.runeAt(2) == "ñ".runeAt(1)
+    doAssert a.runeAt(3) == "y".runeAt(0)
   fastRuneAt(s, i, result, false)
 
 template fastToUTF8Copy*(c: Rune, s: var string, pos: int, doInc = true) =
-  ## Copies UTF-8 representation of `c` into the preallocated string `s`
-  ## starting at position `pos`. If `doInc == true`, `pos` is incremented
+  ## Copies UTF-8 representation of ``c`` into the preallocated string ``s``
+  ## starting at position ``pos``. If ``doInc == true``, ``pos`` is incremented
   ## by the number of bytes that have been processed.
   ##
-  ## To be the most efficient, make sure `s` is preallocated
-  ## with an additional amount equal to the byte length of
-  ## `c`.
+  ## To be the most efficient, make sure ``s`` is preallocated
+  ## with an additional amount equal to the byte length of ``c``.
   var i = RuneImpl(c)
   if i <=% 127:
     s.setLen(pos+1)
@@ -207,28 +226,39 @@ template fastToUTF8Copy*(c: Rune, s: var string, pos: int, doInc = true) =
     discard # error, exception?
 
 proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
-  ## Converts a rune into its UTF-8 representation
+  ## Converts a rune into its UTF-8 representation.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeAt(1).toUTF8 == "ñ"
+
   result = ""
   fastToUTF8Copy(c, result, 0, false)
 
 proc add*(s: var string; c: Rune) =
+  ## Adds a rune ``c`` to a string ``s``.
+  runnableExamples:
+    var s = "abc"
+    let c = "ä".runeAt(0)
+    s.add(c)
+    doAssert s == "abcä"
+
   let pos = s.len
   fastToUTF8Copy(c, s, pos, false)
 
 proc `$`*(rune: Rune): string =
-  ## Converts a Rune to a string
+  ## An alias for `toUTF8 <#toUTF8%2CRune>`_.
   rune.toUTF8
 
 proc `$`*(runes: seq[Rune]): string =
-  ## Converts a sequence of Runes to a string
+  ## Converts a sequence of Runes to a string.
   result = ""
   for rune in runes:
     result.add rune
 
 proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int =
-  ## Returns the byte position of unicode character
-  ## at position pos in s with an optional start byte position.
-  ## returns the special value -1 if it runs out of the string
+  ## Returns the byte position of rune
+  ## at position ``pos`` in ``s`` with an optional start byte position.
+  ## Returns the special value -1 if it runs out of the string.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -244,7 +274,7 @@ proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int =
   return o
 
 proc runeAtPos*(s: string, pos: int): Rune =
-  ## Returns the unicode character at position pos
+  ## Returns the rune at position ``pos``.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -252,7 +282,7 @@ proc runeAtPos*(s: string, pos: int): Rune =
   fastRuneAt(s, runeOffset(s, pos), result, false)
 
 proc runeStrAtPos*(s: string, pos: Natural): string =
-  ## Returns the unicode character at position pos as UTF8 String
+  ## Returns the rune at position ``pos`` as UTF8 String.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -262,7 +292,7 @@ proc runeStrAtPos*(s: string, pos: Natural): string =
 
 proc runeReverseOffset*(s: string, rev:Positive): (int, int) =
   ## Returns a tuple with the the byte offset of the
-  ## unicode character at position ``rev`` in s counting
+  ## rune at position ``rev`` in ``s``, counting
   ## from the end (starting with 1) and the total
   ## number of runes in the string. Returns a negative value
   ## for offset if there are to few runes in the string to
@@ -286,13 +316,21 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) =
     return (-a, rev.int-a)
   return (x, -a+rev.int)
 
-proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
-  ## Returns the UTF-8 substring starting at codepoint pos
-  ## with len codepoints. If pos or len is negative they count from
-  ## the end of the string. If len is not given it means the longest
+proc runeSubStr*(s: string, pos: int, len: int = int.high): string =
+  ## Returns the UTF-8 substring starting at codepoint ``pos``
+  ## with ``len`` codepoints. If ``pos`` or ``len`` is negative they count from
+  ## the end of the string. If ``len`` is not given it means the longest
   ## possible string.
   ##
-  ## (Needs some examples)
+  runnableExamples:
+    let s = "Hänsel  ««: 10,00€"
+    doAssert(runeSubStr(s, 0, 2) == "Hä")
+    doAssert(runeSubStr(s, 10, 1) == ":")
+    doAssert(runeSubStr(s, -6) == "10,00€")
+    doAssert(runeSubStr(s, 10) == ": 10,00€")
+    doAssert(runeSubStr(s, 12, 5) == "10,00")
+    doAssert(runeSubStr(s, -6, 3) == "10,")
+
   if pos < 0:
     let (o, rl) = runeReverseOffset(s, -pos)
     if len >= rl:
@@ -1321,7 +1359,7 @@ proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
   return -1
 
 proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into lower case. This works for any Unicode character.
+  ## Converts ``c`` into lower case. This works for any rune.
   ## If possible, prefer ``toLower`` over ``toUpper``.
   var c = RuneImpl(c)
   var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
@@ -1333,7 +1371,7 @@ proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into upper case. This works for any Unicode character.
+  ## Converts ``c`` into upper case. This works for any rune.
   ## If possible, prefer ``toLower`` over ``toUpper``.
   var c = RuneImpl(c)
   var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
@@ -1345,7 +1383,7 @@ proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` to title case
+  ## Converts ``c`` to title case.
   var c = RuneImpl(c)
   var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
   if p >= 0 and c == toTitleSinglets[p]:
@@ -1353,7 +1391,7 @@ proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a lower case Unicode character.
+  ## Returns true iff ``c`` is a lower case rune.
   ## If possible, prefer ``isLower`` over ``isUpper``.
   var c = RuneImpl(c)
   # Note: toUpperRanges is correct here!
@@ -1365,7 +1403,7 @@ proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a upper case Unicode character.
+  ## Returns true iff ``c`` is a upper case rune.
   ## If possible, prefer ``isLower`` over ``isUpper``.
   var c = RuneImpl(c)
   # Note: toLowerRanges is correct here!
@@ -1377,7 +1415,7 @@ proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is an *alpha* Unicode character (i.e., a letter)
+  ## Returns true iff ``c`` is an *alpha* rune (i.e., a letter)
   if isUpper(c) or isLower(c):
     return true
   var c = RuneImpl(c)
@@ -1389,18 +1427,18 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode titlecase character
+  ## Returns true iff ``c`` is a Unicode titlecase character.
   return isUpper(c) and isLower(c)
 
 proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode whitespace character
+  ## Returns true iff ``c`` is a Unicode whitespace character.
   var c = RuneImpl(c)
   var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
   if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
     return true
 
 proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode combining character
+  ## Returns true iff ``c`` is a Unicode combining character.
   var c = RuneImpl(c)
 
   # Optimized to return false immediately for ASCII
@@ -1424,12 +1462,12 @@ template runeCheck(s, runeProc) =
 
 proc isAlpha*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all alphabetic unicode characters.
+  ## Returns true iff ``s`` contains all alphabetic runes.
   runeCheck(s, isAlpha)
 
 proc isSpace*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all whitespace unicode characters.
+  ## Returns true iff ``s`` contains all whitespace runes.
   runeCheck(s, isWhiteSpace)
 
 template runeCaseCheck(s, runeProc, skipNonAlpha) =
@@ -1459,7 +1497,7 @@ proc isLower*(s: string, skipNonAlpha: bool): bool {.
   ## Checks whether ``s`` is lower case.
   ##
   ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## runes in ``s`` are lower case.  Returns false if none of the
+  ## runes in ``s`` are lower case. Returns false if none of the
   ## runes in ``s`` are alphabetical.
   ##
   ## If ``skipNonAlpha`` is false, returns true only if all runes in
@@ -1474,7 +1512,7 @@ proc isUpper*(s: string, skipNonAlpha: bool): bool {.
   ## Checks whether ``s`` is upper case.
   ##
   ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## runes in ``s`` are upper case.  Returns false if none of the
+  ## runes in ``s`` are upper case. Returns false if none of the
   ## runes in ``s`` are alphabetical.
   ##
   ## If ``skipNonAlpha`` is false, returns true only if all runes in
@@ -1485,7 +1523,7 @@ proc isUpper*(s: string, skipNonAlpha: bool): bool {.
   runeCaseCheck(s, isUpper, skipNonAlpha)
 
 template convertRune(s, runeProc) =
-  ## Convert runes in `s` using `runeProc` as the converter.
+  ## Convert runes in ``s`` using ``runeProc`` as the converter.
   result = newString(len(s))
 
   var
@@ -1502,20 +1540,20 @@ template convertRune(s, runeProc) =
 
 proc toUpper*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into upper-case unicode characters.
+  ## Converts ``s`` into upper-case runes.
   convertRune(s, toUpper)
 
 proc toLower*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into lower-case unicode characters.
+  ## Converts ``s`` into lower-case runes.
   convertRune(s, toLower)
 
 proc swapCase*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Swaps the case of unicode characters in `s`
+  ## Swaps the case of runes in ``s``.
   ##
-  ## Returns a new string such that the cases of all unicode characters
-  ## are swapped if possible
+  ## Returns a new string such that the cases of all runes
+  ## are swapped if possible.
 
   var
     i = 0
@@ -1538,7 +1576,7 @@ proc swapCase*(s: string): string {.noSideEffect, procvar,
 
 proc capitalize*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Converts the first character of `s` into an upper-case unicode character.
+  ## Converts the first character of ``s`` into an upper-case rune.
   if len(s) == 0:
     return s
 
@@ -1552,10 +1590,10 @@ proc capitalize*(s: string): string {.noSideEffect, procvar,
 
 proc translate*(s: string, replacements: proc(key: string): string): string {.
   rtl, extern: "nuc$1".} =
-  ## Translates words in a string using the `replacements` proc to substitute
-  ## words inside `s` with their replacements
+  ## Translates words in a string using the ``replacements`` proc to substitute
+  ## words inside ``s`` with their replacements.
   ##
-  ## `replacements` is any proc that takes a word and returns
+  ## ``replacements`` is any proc that takes a word and returns
   ## a new word to fill it's place.
 
   # Allocate memory for the new string based on the old one.
@@ -1601,10 +1639,10 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
 proc title*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Converts `s` to a unicode title.
+  ## Converts ``s`` to a unicode title.
   ##
   ## Returns a new string such that the first character
-  ## in each word inside `s` is capitalized
+  ## in each word inside ``s`` is capitalized.
 
   var
     i = 0
@@ -1631,10 +1669,10 @@ proc title*(s: string): string {.noSideEffect, procvar,
 proc isTitle*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str",
   deprecated: "Deprecated since version 0.20 since its semantics are unclear".}=
-  ## Checks whether or not `s` is a unicode title.
+  ## Checks whether or not ``s`` is a unicode title.
   ##
-  ## Returns true if the first character in each word inside `s`
-  ## are upper case and there is at least one character in `s`.
+  ## Returns true if the first character in each word inside ``s``
+  ## are upper case and there is at least one character in ``s``.
   if s.len == 0:
     return false
 
@@ -1656,7 +1694,7 @@ proc isTitle*(s: string): bool {.noSideEffect, procvar,
       firstRune = true
 
 iterator runes*(s: string): Rune =
-  ## Iterates over any unicode character of the string ``s`` returning runes
+  ## Iterates over any rune of the string ``s`` returning runes.
   var
     i = 0
     result: Rune
@@ -1665,7 +1703,7 @@ iterator runes*(s: string): Rune =
     yield result
 
 iterator utf8*(s: string): string =
-  ## Iterates over any unicode character of the string ``s`` returning utf8 values
+  ## Iterates over any rune of the string ``s`` returning utf8 values.
   var o = 0
   while o < s.len:
     let n = runeLenAt(s, o)
@@ -1673,7 +1711,7 @@ iterator utf8*(s: string): string =
     o += n
 
 proc toRunes*(s: string): seq[Rune] =
-  ## Obtains a sequence containing the Runes in ``s``
+  ## Obtains a sequence containing the Runes in ``s``.
   result = newSeq[Rune]()
   for r in s.runes:
     result.add(r)
@@ -1696,15 +1734,14 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
   result = a.len - b.len
 
 proc reversed*(s: string): string =
-  ## Returns the reverse of ``s``, interpreting it as Unicode characters.
-  ## Unicode combining characters are correctly interpreted as well:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   assert reversed("Reverse this!") == "!siht esreveR"
-  ##   assert reversed("先秦兩漢") == "漢兩秦先"
-  ##   assert reversed("as⃝df̅") == "f̅ds⃝a"
-  ##   assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+  ## Returns the reverse of ``s``, interpreting it as runes.
+  ## Unicode combining characters are correctly interpreted as well.
+  runnableExamples:
+    assert reversed("Reverse this!") == "!siht esreveR"
+    assert reversed("先秦兩漢") == "漢兩秦先"
+    assert reversed("as⃝df̅") == "f̅ds⃝a"
+    assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+
   var
     i = 0
     lastI = 0
@@ -1731,7 +1768,7 @@ proc reversed*(s: string): string =
   reverseUntil(len(s))
 
 proc graphemeLen*(s: string; i: Natural): Natural =
-  ## The number of bytes belonging to 's[i]' including following combining
+  ## The number of bytes belonging to ``s[i]`` including following combining
   ## characters.
   var j = i.int
   var r, r2: Rune
@@ -1744,7 +1781,7 @@ proc graphemeLen*(s: string; i: Natural): Natural =
       result = j-i
 
 proc lastRune*(s: string; last: int): (Rune, int) =
-  ## length of the last rune in 's[0..last]'. Returns the rune and its length
+  ## Length of the last rune in ``s[0..last]``. Returns the rune and its length
   ## in bytes.
   if s[last] <= chr(127):
     result = (Rune(s[last]), 1)
@@ -1778,7 +1815,7 @@ proc stringHasSep(s: string, index: int, sep: Rune): bool =
   return sep == rune
 
 template splitCommon(s, sep, maxsplit: untyped, sepLen: int = -1) =
-  ## Common code for split procedures
+  ## Common code for split procedures.
   var
     last = 0
     splits = maxsplit
@@ -1801,9 +1838,9 @@ template splitCommon(s, sep, maxsplit: untyped, sepLen: int = -1) =
 
 iterator split*(s: string, seps: openarray[Rune] = unicodeSpaces,
   maxsplit: int = -1): string =
-  ## Splits the unicode string `s` into substrings using a group of separators.
+  ## Splits the unicode string ``s`` into substrings using a group of separators.
   ##
-  ## Substrings are separated by a substring containing only `seps`.
+  ## Substrings are separated by a substring containing only ``seps``.
   ##
   ## .. code-block:: nim
   ##   for word in split("this\lis an\texample"):
@@ -1844,7 +1881,7 @@ iterator split*(s: string, seps: openarray[Rune] = unicodeSpaces,
   splitCommon(s, seps, maxsplit)
 
 iterator splitWhitespace*(s: string): string =
-  ## Splits a unicode string at whitespace runes
+  ## Splits a unicode string at whitespace runes.
   splitCommon(s, unicodeSpaces, -1)
 
 template accResult(iter: untyped) =
@@ -1858,9 +1895,9 @@ proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
   accResult(splitWhitespace(s))
 
 iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
-  ## Splits the unicode string `s` into substrings using a single separator.
+  ## Splits the unicode string ``s`` into substrings using a single separator.
   ##
-  ## Substrings are separated by the rune `sep`.
+  ## Substrings are separated by the rune ``sep``.
   ## The code:
   ##
   ## .. code-block:: nim
@@ -1898,11 +1935,11 @@ proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffec
 proc strip*(s: string, leading = true, trailing = true,
             runes: openarray[Rune] = unicodeSpaces): string {.noSideEffect,
             rtl, extern: "nucStrip".} =
-  ## Strips leading or trailing `runes` from `s` and returns
+  ## Strips leading or trailing ``runes`` from ``s`` and returns
   ## the resulting string.
   ##
-  ## If `leading` is true, leading `runes` are stripped.
-  ## If `trailing` is true, trailing `runes` are stripped.
+  ## If ``leading`` is true, leading ``runes`` are stripped.
+  ## If ``trailing`` is true, trailing ``runes`` are stripped.
   ## If both are false, the string is returned unchanged.
   var
     s_i = 0 ## starting index into string ``s``
@@ -1948,9 +1985,9 @@ proc strip*(s: string, leading = true, trailing = true,
 
 proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
   rtl, extern: "nucRepeatRune".} =
-  ## Returns a string of `count` Runes `c`.
+  ## Returns a string of ``count`` Runes ``c``.
   ##
-  ## The returned string will have a rune-length of `count`.
+  ## The returned string will have a rune-length of ``count``.
   let s = $c
   result = newStringOfCap(count * s.len)
   for i in 0 ..< count:
@@ -1958,11 +1995,11 @@ proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
 
 proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
   noSideEffect, rtl, extern: "nucAlignString".} =
-  ## Aligns a unicode string `s` with `padding`, so that it has a rune-length
-  ## of `count`.
+  ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length
+  ## of ``count``.
   ##
-  ## `padding` characters (by default spaces) are added before `s` resulting in
-  ## right alignment. If ``s.runelen >= count``, no spaces are added and `s` is
+  ## ``padding`` characters (by default spaces) are added before ``s`` resulting in
+  ## right alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
   ## returned unchanged. If you need to left align a string use the `alignLeft
   ## proc <#alignLeft>`_.
   runnableExamples:
@@ -1985,11 +2022,11 @@ proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
 
 proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.
     noSideEffect.} =
-  ## Left-Aligns a unicode string `s` with `padding`, so that it has a
-  ## rune-length of `count`.
+  ## Left-Aligns a unicode string ``s`` with ``padding``, so that it has a
+  ## rune-length of ``count``.
   ##
-  ## `padding` characters (by default spaces) are added after `s` resulting in
-  ## left alignment. If ``s.runelen >= count``, no spaces are added and `s` is
+  ## ``padding`` characters (by default spaces) are added after ``s`` resulting in
+  ## left alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
   ## returned unchanged. If you need to right align a string use the `align
   ## proc <#align>`_.
   runnableExamples:
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 135a24e9a..ce147cccc 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -499,7 +499,7 @@ template test*(name, body) {.dirty.} =
 
     finally:
       if testStatusIMPL == FAILED:
-        programResult += 1
+        programResult = 1
       let testResult = TestResult(
         suiteName: when declared(testSuiteName): testSuiteName else: "",
         testName: name,
@@ -540,7 +540,7 @@ template fail* =
   when declared(testStatusIMPL):
     testStatusIMPL = FAILED
   else:
-    programResult += 1
+    programResult = 1
 
   ensureInitialized()
 
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d296017dd..f322718d1 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -8,6 +8,34 @@
 #
 
 ## This module implements URI parsing as specified by RFC 3986.
+##
+## A Uniform Resource Identifier (URI) provides a simple and extensible
+## means for identifying a resource. A URI can be further classified
+## as a locator, a name, or both. The term “Uniform Resource Locator”
+## (URL) refers to the subset of URIs.
+##
+## Basic usage
+## ===========
+##
+## Combine URIs
+## -------------
+## .. code-block::
+##    import uri
+##    let host = parseUri("https://nim-lang.org")
+##    let blog = "/blog.html"
+##    let bloguri = host / blog
+##    assert $host == "https://nim-lang.org"
+##    assert $bloguri == "https://nim-lang.org/blog.html"
+##
+## Access URI item
+## ---------------
+## .. code-block::
+##    import uri
+##    let res = parseUri("sftp://127.0.0.1:4343")
+##    if isAbsolute(res):
+##      assert res.port == "4343"
+##    else:
+##      echo "Wrong format"
 
 import strutils, parseutils
 type
@@ -18,44 +46,24 @@ type
     hostname*, port*, path*, query*, anchor*: string
     opaque*: bool
 
-{.push warning[deprecated]: off.}
-proc `$`*(url: Url): string {.deprecated: "use Uri instead".} =
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  return string(url)
-
-proc `/`*(a, b: Url): Url {.deprecated: "use Uri instead".} =
-  ## Joins two URLs together, separating them with / if needed.
-  ##
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  var urlS = $a
-  var bS = $b
-  if urlS == "": return b
-  if urlS[urlS.len-1] != '/':
-    urlS.add('/')
-  if bS[0] == '/':
-    urlS.add(bS.substr(1))
-  else:
-    urlS.add(bs)
-  result = Url(urlS)
-
-proc add*(url: var Url, a: Url) {.deprecated: "use Uri instead".} =
-  ## Appends url to url.
-  ##
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  url = url / a
-{.pop.}
-
 proc encodeUrl*(s: string, usePlus=true): string =
   ## Encodes a URL according to RFC3986.
   ##
   ## This means that characters in the set
   ## ``{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}`` are
   ## carried over to the result.
-  ## All other characters are encoded as ``''%xx'`` where ``xx``
+  ## All other characters are encoded as ``%xx`` where ``xx``
   ## denotes its hexadecimal value.
   ##
   ## As a special rule, when the value of ``usePlus`` is true,
-  ## spaces are encoded as ``'+'`` instead of ``'%20'``.
+  ## spaces are encoded as ``+`` instead of ``%20``.
+  ##
+  ## **See also:**
+  ## * `decodeUrl proc<#decodeUrl,string>`_
+  runnableExamples:
+    assert encodeUrl("https://nim-lang.org") == "https%3A%2F%2Fnim-lang.org"
+    assert encodeUrl("https://nim-lang.org/this is a test") == "https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test"
+    assert encodeUrl("https://nim-lang.org/this is a test", false) == "https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test"
   result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
   let fromSpace = if usePlus: "+" else: "%20"
   for c in s:
@@ -70,12 +78,19 @@ proc encodeUrl*(s: string, usePlus=true): string =
 proc decodeUrl*(s: string, decodePlus=true): string =
   ## Decodes a URL according to RFC3986.
   ##
-  ## This means that any ``'%xx'`` (where ``xx`` denotes a hexadecimal
+  ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal
   ## value) are converted to the character with ordinal number ``xx``,
   ## and every other character is carried over.
   ##
-  ## As a special rule, when the value of ``decodePlus`` is true, ``'+'``
+  ## As a special rule, when the value of ``decodePlus`` is true, ``+``
   ## characters are converted to a space.
+  ##
+  ## **See also:**
+  ## * `encodeUrl proc<#encodeUrl,string>`_
+  runnableExamples:
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org") == "https://nim-lang.org"
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test") == "https://nim-lang.org/this is a test"
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test", false) == "https://nim-lang.org/this is a test"
   proc handleHexChar(c: char, x: var int) {.inline.} =
     case c
     of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
@@ -104,6 +119,33 @@ proc decodeUrl*(s: string, decodePlus=true): string =
     inc(j)
   setLen(result, j)
 
+proc encodeQuery*(query: openArray[(string, string)], usePlus=true, omitEq=true): string =
+  ## Encodes a set of (key, value) parameters into a URL query string.
+  ##
+  ## Every (key, value) pair is URL-encoded and written as ``key=value``. If the
+  ## value is an empty string then the ``=`` is omitted, unless ``omitEq`` is
+  ## false.
+  ## The pairs are joined together by a ``&`` character.
+  ##
+  ## The ``usePlus`` parameter is passed down to the `encodeUrl` function that
+  ## is used for the URL encoding of the string values.
+  ##
+  ## **See also:**
+  ## * `encodeUrl proc<#encodeUrl,string>`_
+  runnableExamples:
+    assert encodeQuery({:}) == ""
+    assert encodeQuery({"a": "1", "b": "2"}) == "a=1&b=2"
+    assert encodeQuery({"a": "1", "b": ""}) == "a=1&b"
+  for elem in query:
+    # Encode the `key = value` pairs and separate them with a '&'
+    if result.len > 0: result.add('&')
+    let (key, val) = elem
+    result.add(encodeUrl(key, usePlus))
+    # Omit the '=' if the value string is empty
+    if not omitEq or val.len > 0:
+      result.add('=')
+      result.add(encodeUrl(val, usePlus))
+
 proc parseAuthority(authority: string, result: var Uri) =
   var i = 0
   var inPort = false
@@ -150,7 +192,14 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
     i.inc parseUntil(uri, result.anchor, {}, i)
 
 proc initUri*(): Uri =
-  ## Initializes a URI.
+  ## Initializes a URI with ``scheme``, ``username``, ``password``,
+  ## ``hostname``, ``port``, ``path``, ``query`` and ``anchor``.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  runnableExamples:
+    var uri: Uri
+    assert initUri() == uri
   result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
                 path: "", query: "", anchor: "")
 
@@ -163,6 +212,16 @@ proc resetUri(uri: var Uri) =
 
 proc parseUri*(uri: string, result: var Uri) =
   ## Parses a URI. The `result` variable will be cleared before.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  ## * `initUri proc <#initUri>`_ for initializing a URI
+  runnableExamples:
+    var res = initUri()
+    parseUri("https://nim-lang.org/docs/manual.html", res)
+    assert res.scheme == "https"
+    assert res.hostname == "nim-lang.org"
+    assert res.path == "/docs/manual.html"
   resetUri(result)
 
   var i = 0
@@ -201,6 +260,14 @@ proc parseUri*(uri: string, result: var Uri) =
 
 proc parseUri*(uri: string): Uri =
   ## Parses a URI and returns it.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  runnableExamples:
+    let res = parseUri("ftp://Username:Password@Hostname")
+    assert res.username == "Username"
+    assert res.password == "Password"
+    assert res.scheme == "ftp"
   result = initUri()
   parseUri(uri, result)
 
@@ -251,22 +318,18 @@ proc combine*(base: Uri, reference: Uri): Uri =
   ## This uses the algorithm specified in
   ## `section 5.2.2 of RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2.2>`_.
   ##
-  ## This means that the slashes inside the base URI's path as well as reference
-  ## URI's path affect the resulting URI.
-  ##
-  ## For building URIs you may wish to use \`/\` instead.
-  ##
-  ## Examples:
+  ## This means that the slashes inside the base URIs path as well as reference
+  ## URIs path affect the resulting URI.
   ##
-  ## .. code-block::
-  ##   let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
-  ##   assert foo.path == "/baz"
-  ##
-  ##   let bar = combine(parseUri("http://example.com/foo/bar"), parseUri("baz"))
-  ##   assert bar.path == "/foo/baz"
-  ##
-  ##   let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz"))
-  ##   assert bar.path == "/foo/bar/baz"
+  ## **See also:**
+  ## * `/ proc <#/,Uri,string>`_ for building URIs
+  runnableExamples:
+    let foo = combine(parseUri("https://nim-lang.org/foo/bar"), parseUri("/baz"))
+    assert foo.path == "/baz"
+    let bar = combine(parseUri("https://nim-lang.org/foo/bar"), parseUri("baz"))
+    assert bar.path == "/foo/baz"
+    let qux = combine(parseUri("https://nim-lang.org/foo/bar/"), parseUri("baz"))
+    assert qux.path == "/foo/bar/baz"
 
   template setAuthority(dest, src): untyped =
     dest.hostname = src.hostname
@@ -302,32 +365,42 @@ proc combine*(base: Uri, reference: Uri): Uri =
 
 proc combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
+  ##
+  ## **See also:**
+  ## * `/ proc <#/,Uri,string>`_ for building URIs
+  runnableExamples:
+    let foo = combine(parseUri("https://nim-lang.org/"), parseUri("docs/"), parseUri("manual.html"))
+    assert foo.hostname == "nim-lang.org"
+    assert foo.path == "/docs/manual.html"
   result = uris[0]
   for i in 1 ..< uris.len:
     result = combine(result, uris[i])
 
 proc isAbsolute*(uri: Uri): bool =
-  ## returns true if URI is absolute, false otherwise
+  ## Returns true if URI is absolute, false otherwise.
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org")
+    assert isAbsolute(foo) == true
+    let bar = parseUri("nim-lang")
+    assert isAbsolute(bar) == false
   return uri.scheme != "" and (uri.hostname != "" or uri.path != "")
 
 proc `/`*(x: Uri, path: string): Uri =
-  ## Concatenates the path specified to the specified URI's path.
+  ## Concatenates the path specified to the specified URIs path.
   ##
-  ## Contrary to the ``combine`` procedure you do not have to worry about
-  ## the slashes at the beginning and end of the path and URI's path
+  ## Contrary to the `combine proc <#combine,Uri,Uri>`_ you do not have to worry about
+  ## the slashes at the beginning and end of the path and URIs path
   ## respectively.
   ##
-  ## Examples:
-  ##
-  ## .. code-block::
-  ##   let foo = parseUri("http://example.com/foo/bar") / "/baz"
-  ##   assert foo.path == "/foo/bar/baz"
-  ##
-  ##   let bar = parseUri("http://example.com/foo/bar") / "baz"
-  ##   assert bar.path == "/foo/bar/baz"
-  ##
-  ##   let bar = parseUri("http://example.com/foo/bar/") / "baz"
-  ##   assert bar.path == "/foo/bar/baz"
+  ## **See also:**
+  ## * `combine proc <#combine,Uri,Uri>`_
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org/foo/bar") / "/baz"
+    assert foo.path == "/foo/bar/baz"
+    let bar = parseUri("https://nim-lang.org/foo/bar") / "baz"
+    assert bar.path == "/foo/bar/baz"
+    let qux = parseUri("https://nim-lang.org/foo/bar/") / "baz"
+    assert qux.path == "/foo/bar/baz"
   result = x
 
   if result.path.len == 0:
@@ -346,8 +419,19 @@ proc `/`*(x: Uri, path: string): Uri =
       result.path.add '/'
     result.path.add(path)
 
+proc `?`*(u: Uri, query: openArray[(string, string)]): Uri =
+  ## Concatenates the query parameters to the specified URI object.
+  runnableExamples:
+    let foo = parseUri("https://example.com") / "foo" ? {"bar": "qux"}
+    assert $foo == "https://example.com/foo?bar=qux"
+  result = u
+  result.query = encodeQuery(query)
+
 proc `$`*(u: Uri): string =
   ## Returns the string representation of the specified URI object.
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org")
+    assert $foo == "https://nim-lang.org"
   result = ""
   if u.scheme.len > 0:
     result.add(u.scheme)
@@ -627,4 +711,25 @@ when isMainModule:
     doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
 
+  # encodeQuery tests
+  block:
+    doAssert encodeQuery({:}) == ""
+    doAssert encodeQuery({"foo": "bar"}) == "foo=bar"
+    doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz"
+    doAssert encodeQuery({"foo": "bar & baz"}, usePlus=false) == "foo=bar%20%26%20baz"
+    doAssert encodeQuery({"foo": ""}) == "foo"
+    doAssert encodeQuery({"foo": ""}, omitEq=false) == "foo="
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3"
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq=false) == "a=1&b=&c=3"
+
+    block:
+      var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"}
+      var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux")
+      doAssert foo == foo1
+
+    block:
+      var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""}
+      var foo1 = parseUri("http://example.com/foo?do=do&bar")
+      doAssert foo == foo1
+
   echo("All good!")
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index b99d46bc8..e1d24ea42 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -7,21 +7,52 @@
 #    distribution, for details about the copyright.
 #
 
-## A simple XML tree.
+## A simple XML tree generator.
+##
+## .. code-block::
+##   import xmltree
+##
+##   var g = newElement("myTag")
+##   g.add newText("some text")
+##   g.add newComment("this is comment")
+##
+##   var h = newElement("secondTag")
+##   h.add newEntity("some entity")
+##
+##   let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+##   let k = newXmlTree("treeTag", [g, h], att)
+##
+##   echo k
+##   # <treeTag key2="second value" key1="first value">
+##   #   <myTag>some text<!-- this is comment --></myTag>
+##   #   <secondTag>&some entity;</secondTag>
+##   # </treeTag>
+##
+##
+## **See also:**
+## * `xmlparser module <xmlparser.html>`_ for high-level XML parsing
+## * `parsexml module <parsexml.html>`_ for low-level XML parsing
+## * `htmlgen module <htmlgen.html>`_ for html code generator
 
 import macros, strtabs, strutils
 
 type
-  XmlNode* = ref XmlNodeObj ## an XML tree consists of ``XmlNode``'s.
+  XmlNode* = ref XmlNodeObj ## An XML tree consisting of XML nodes.
+    ##
+    ## Use `newXmlTree proc <#newXmlTree,string,openArray[XmlNode],XmlAttributes>`_
+    ## for creating a new tree.
 
-  XmlNodeKind* = enum  ## different kinds of ``XmlNode``'s
+  XmlNodeKind* = enum  ## Different kinds of XML nodes.
     xnText,             ## a text element
     xnElement,          ## an element with 0 or more children
     xnCData,            ## a CDATA node
     xnEntity,           ## an entity (like ``&thing;``)
     xnComment           ## an XML comment
 
-  XmlAttributes* = StringTableRef ## an alias for a string to string mapping
+  XmlAttributes* = StringTableRef ## An alias for a string to string mapping.
+    ##
+    ## Use `toXmlAttributes proc <#toXmlAttributes,varargs[tuple[string,string]]>`_
+    ## to create `XmlAttributes`.
 
   XmlNodeObj {.acyclic.} = object
     case k: XmlNodeKind # private, use the kind() proc to read this field.
@@ -33,67 +64,203 @@ type
       fAttr: XmlAttributes
     fClientData: int              ## for other clients
 
+const
+  xmlHeader* = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+    ## Header to use for complete XML output.
+
 proc newXmlNode(kind: XmlNodeKind): XmlNode =
-  ## creates a new ``XmlNode``.
+  ## Creates a new ``XmlNode``.
   new(result)
   result.k = kind
 
 proc newElement*(tag: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
+  ## Creates a new ``XmlNode`` of kind ``xnElement`` with the given `tag`.
+  ##
+  ## See also:
+  ## * `newXmlTree proc <#newXmlTree,string,openArray[XmlNode],XmlAttributes>`_
+  ## * [<> macro](#<>.m,untyped)
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert a.kind == xnElement
+    assert $a == "<firstTag><childTag /></firstTag>"
+
   result = newXmlNode(xnElement)
   result.fTag = tag
   result.s = @[]
-  # init attributes lazily to safe memory
+  # init attributes lazily to save memory
 
 proc newText*(text: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnText`` with the text `text`.
+  ## Creates a new ``XmlNode`` of kind ``xnText`` with the text `text`.
+  runnableExamples:
+    var b = newText("my text")
+    assert b.kind == xnText
+    assert $b == "my text"
+
   result = newXmlNode(xnText)
   result.fText = text
 
 proc newComment*(comment: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `comment`.
+  ## Creates a new ``XmlNode`` of kind ``xnComment`` with the text `comment`.
+  runnableExamples:
+    var c = newComment("my comment")
+    assert c.kind == xnComment
+    assert $c == "<!-- my comment -->"
+
   result = newXmlNode(xnComment)
   result.fText = comment
 
 proc newCData*(cdata: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `cdata`.
+  ## Creates a new ``XmlNode`` of kind ``xnCData`` with the text `cdata`.
+  runnableExamples:
+    var d = newCData("my cdata")
+    assert d.kind == xnCData
+    assert $d == "<![CDATA[my cdata]]>"
+
   result = newXmlNode(xnCData)
   result.fText = cdata
 
 proc newEntity*(entity: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnEntity`` with the text `entity`.
+  ## Creates a new ``XmlNode`` of kind ``xnEntity`` with the text `entity`.
+  runnableExamples:
+    var e = newEntity("my entity")
+    assert e.kind == xnEntity
+    assert $e == "&my entity;"
+
   result = newXmlNode(xnEntity)
   result.fText = entity
 
+proc newXmlTree*(tag: string, children: openArray[XmlNode],
+                 attributes: XmlAttributes = nil): XmlNode =
+  ## Creates a new XML tree with `tag`, `children` and `attributes`.
+  ##
+  ## See also:
+  ## * `newElement proc <#newElement,string>`_
+  ## * [<> macro](#<>.m,untyped)
+  runnableExamples:
+    from strutils import unindent
+    var g = newElement("myTag")
+    g.add newText("some text")
+    g.add newComment("this is comment")
+    var h = newElement("secondTag")
+    h.add newEntity("some entity")
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    let k = newXmlTree("treeTag", [g, h], att)
+    assert ($k).unindent == """<treeTag key2="second value" key1="first value">
+        <myTag>some text<!-- this is comment --></myTag>
+        <secondTag>&some entity;</secondTag>
+      </treeTag>""".unindent
+
+  result = newXmlNode(xnElement)
+  result.fTag = tag
+  newSeq(result.s, children.len)
+  for i in 0..children.len-1: result.s[i] = children[i]
+  result.fAttr = attributes
+
 proc text*(n: XmlNode): string {.inline.} =
-  ## gets the associated text with the node `n`. `n` can be a CDATA, Text,
-  ## comment, or entity node.
+  ## Gets the associated text with the node `n`.
+  ##
+  ## `n` can be a CDATA, Text, comment, or entity node.
+  ##
+  ## See also:
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  ## * `innerText proc <#innerText,XmlNode>`_
+  runnableExamples:
+    var c = newComment("my comment")
+    assert $c == "<!-- my comment -->"
+    assert c.text == "my comment"
+
   assert n.k in {xnText, xnComment, xnCData, xnEntity}
   result = n.fText
 
 proc `text=`*(n: XmlNode, text: string){.inline.} =
-  ## sets the associated text with the node `n`. `n` can be a CDATA, Text,
-  ## comment, or entity node.
+  ## Sets the associated text with the node `n`.
+  ##
+  ## `n` can be a CDATA, Text, comment, or entity node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  runnableExamples:
+    var e = newEntity("my entity")
+    assert $e == "&my entity;"
+    e.text = "a new entity text"
+    assert $e == "&a new entity text;"
+
   assert n.k in {xnText, xnComment, xnCData, xnEntity}
   n.fText = text
 
+proc tag*(n: XmlNode): string {.inline.} =
+  ## Gets the tag name of `n`.
+  ##
+  ## `n` has to be an ``xnElement`` node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  ## * `innerText proc <#innerText,XmlNode>`_
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert $a == "<firstTag><childTag /></firstTag>"
+    assert a.tag == "firstTag"
+
+  assert n.k == xnElement
+  result = n.fTag
+
+proc `tag=`*(n: XmlNode, tag: string) {.inline.} =
+  ## Sets the tag name of `n`.
+  ##
+  ## `n` has to be an ``xnElement`` node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert $a == "<firstTag><childTag /></firstTag>"
+    a.tag = "newTag"
+    assert $a == "<newTag><childTag /></newTag>"
+
+  assert n.k == xnElement
+  n.fTag = tag
+
 proc rawText*(n: XmlNode): string {.inline.} =
-  ## returns the underlying 'text' string by reference.
+  ## Returns the underlying 'text' string by reference.
+  ##
   ## This is only used for speed hacks.
   shallowCopy(result, n.fText)
 
 proc rawTag*(n: XmlNode): string {.inline.} =
-  ## returns the underlying 'tag' string by reference.
+  ## Returns the underlying 'tag' string by reference.
+  ##
   ## This is only used for speed hacks.
   shallowCopy(result, n.fTag)
 
 proc innerText*(n: XmlNode): string =
-  ## gets the inner text of `n`:
+  ## Gets the inner text of `n`:
   ##
   ## - If `n` is `xnText` or `xnEntity`, returns its content.
   ## - If `n` is `xnElement`, runs recursively on each child node and
   ##   concatenates the results.
   ## - Otherwise returns an empty string.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newText("my text")
+    f.add newComment("my comment")
+    f.add newEntity("my entity")
+    assert $f == "<myTag>my text<!-- my comment -->&my entity;</myTag>"
+    assert innerText(f) == "my textmy entity"
+
   proc worker(res: var string, n: XmlNode) =
     case n.k
     of xnText, xnEntity:
@@ -107,89 +274,218 @@ proc innerText*(n: XmlNode): string =
   result = ""
   worker(result, n)
 
-proc tag*(n: XmlNode): string {.inline.} =
-  ## gets the tag name of `n`. `n` has to be an ``xnElement`` node.
-  assert n.k == xnElement
-  result = n.fTag
-
-proc `tag=`*(n: XmlNode, tag: string) {.inline.} =
-  ## sets the tag name of `n`. `n` has to be an ``xnElement`` node.
-  assert n.k == xnElement
-  n.fTag = tag
-
 proc add*(father, son: XmlNode) {.inline.} =
-  ## adds the child `son` to `father`.
+  ## Adds the child `son` to `father`.
+  ##
+  ## See also:
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newText("my text")
+    f.add newElement("sonTag")
+    f.add newEntity("my entity")
+    assert $f == "<myTag>my text<sonTag />&my entity;</myTag>"
   add(father.s, son)
 
 proc insert*(father, son: XmlNode, index: int) {.inline.} =
-  ## insert the child `son` to a given position in `father`.
+  ## Insert the child `son` to a given position in `father`.
+  ##
+  ## `father` and `son` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  runnableExamples:
+    from strutils import unindent
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert ($f).unindent == "<myTag>\n<second />\n<first />\n</myTag>"
+
   assert father.k == xnElement and son.k == xnElement
   if len(father.s) > index:
     insert(father.s, son, index)
   else:
     insert(father.s, son, len(father.s))
 
+proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} =
+  ## Delete the `i`'th child of `n`.
+  ##
+  ## See also:
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    f.delete(0)
+    assert $f == "<myTag><first /></myTag>"
+
+  assert n.k == xnElement
+  n.s.delete(i)
+
 proc len*(n: XmlNode): int {.inline.} =
-  ## returns the number `n`'s children.
+  ## Returns the number of `n`'s children.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert len(f) == 2
   if n.k == xnElement: result = len(n.s)
 
 proc kind*(n: XmlNode): XmlNodeKind {.inline.} =
-  ## returns `n`'s kind.
+  ## Returns `n`'s kind.
+  runnableExamples:
+    var a = newElement("firstTag")
+    assert a.kind == xnElement
+    var b = newText("my text")
+    assert b.kind == xnText
   result = n.k
 
 proc `[]`* (n: XmlNode, i: int): XmlNode {.inline.} =
-  ## returns the `i`'th child of `n`.
-  assert n.k == xnElement
-  result = n.s[i]
+  ## Returns the `i`'th child of `n`.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert $f[1] == "<first />"
+    assert $f[0] == "<second />"
 
-proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} =
-  ## delete the `i`'th child of `n`.
   assert n.k == xnElement
-  n.s.delete(i)
+  result = n.s[i]
 
 proc `[]`* (n: var XmlNode, i: int): var XmlNode {.inline.} =
-  ## returns the `i`'th child of `n` so that it can be modified
+  ## Returns the `i`'th child of `n` so that it can be modified.
   assert n.k == xnElement
   result = n.s[i]
 
 iterator items*(n: XmlNode): XmlNode {.inline.} =
-  ## iterates over any child of `n`.
+  ## Iterates over any child of `n`.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var g = newElement("myTag")
+  ##   g.add newText("some text")
+  ##   g.add newComment("this is comment")
+  ##
+  ##   var h = newElement("secondTag")
+  ##   h.add newEntity("some entity")
+  ##   g.add h
+  ##
+  ##   assert $g == "<myTag>some text<!-- this is comment --><secondTag>&some entity;</secondTag></myTag>"
+  ##   for x in g: # the same as `for x in items(g):`
+  ##     echo x
+  ##
+  ##   # some text
+  ##   # <!-- this is comment -->
+  ##   # <secondTag>&some entity;<![CDATA[some cdata]]></secondTag>
+
   assert n.k == xnElement
   for i in 0 .. n.len-1: yield n[i]
 
 iterator mitems*(n: var XmlNode): var XmlNode {.inline.} =
-  ## iterates over any child of `n`.
+  ## Iterates over any child of `n` so that it can be modified.
   assert n.k == xnElement
   for i in 0 .. n.len-1: yield n[i]
 
+proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, val: string]]): XmlAttributes =
+  ## Converts `{key: value}` pairs into `XmlAttributes`.
+  runnableExamples:
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    var j = newElement("myTag")
+    j.attrs = att
+    assert $j == """<myTag key2="second value" key1="first value" />"""
+
+  newStringTable(keyValuePairs)
+
 proc attrs*(n: XmlNode): XmlAttributes {.inline.} =
-  ## gets the attributes belonging to `n`.
+  ## Gets the attributes belonging to `n`.
+  ##
   ## Returns `nil` if attributes have not been initialised for this node.
+  ##
+  ## See also:
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for numbef of attributes
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrs == nil
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrs == att
+
   assert n.k == xnElement
   result = n.fAttr
 
 proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} =
-  ## sets the attributes belonging to `n`.
+  ## Sets the attributes belonging to `n`.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for numbef of attributes
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrs == nil
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrs == att
+
   assert n.k == xnElement
   n.fAttr = attr
 
 proc attrsLen*(n: XmlNode): int {.inline.} =
-  ## returns the number of `n`'s attributes.
+  ## Returns the number of `n`'s attributes.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrsLen == 0
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrsLen == 2
+
   assert n.k == xnElement
   if not isNil(n.fAttr): result = len(n.fAttr)
 
+proc attr*(n: XmlNode, name: string): string =
+  ## Finds the first attribute of `n` with a name of `name`.
+  ## Returns "" on failure.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for numbef of attributes
+  runnableExamples:
+    var j = newElement("myTag")
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attr("key1") == "first value"
+    assert j.attr("key2") == "second value"
+
+  assert n.kind == xnElement
+  if n.attrs == nil: return ""
+  return n.attrs.getOrDefault(name)
+
 proc clientData*(n: XmlNode): int {.inline.} =
-  ## gets the client data of `n`. The client data field is used by the HTML
-  ## parser and generator.
+  ## Gets the client data of `n`.
+  ##
+  ## The client data field is used by the HTML parser and generator.
   result = n.fClientData
 
 proc `clientData=`*(n: XmlNode, data: int) {.inline.} =
-  ## sets the client data of `n`. The client data field is used by the HTML
-  ## parser and generator.
+  ## Sets the client data of `n`.
+  ##
+  ## The client data field is used by the HTML parser and generator.
   n.fClientData = data
 
 proc addEscaped*(result: var string, s: string) =
-  ## same as ``result.add(escape(s))``, but more efficient.
+  ## The same as `result.add(escape(s)) <#escape,string>`_, but more efficient.
   for c in items(s):
     case c
     of '<': result.add("&lt;")
@@ -201,7 +497,8 @@ proc addEscaped*(result: var string, s: string) =
     else: result.add(c)
 
 proc escape*(s: string): string =
-  ## escapes `s` for inclusion into an XML document.
+  ## Escapes `s` for inclusion into an XML document.
+  ##
   ## Escapes these characters:
   ##
   ## ------------    -------------------
@@ -214,6 +511,8 @@ proc escape*(s: string): string =
   ##  ``'``          ``&#x27;``
   ##  ``/``          ``&#x2F;``
   ## ------------    -------------------
+  ##
+  ## You can also use `addEscaped proc <#addEscaped,string,string>`_.
   result = newStringOfCap(s.len)
   addEscaped(result, s)
 
@@ -223,14 +522,22 @@ proc addIndent(result: var string, indent: int, addNewLines: bool) =
   for i in 1..indent: result.add(' ')
 
 proc noWhitespace(n: XmlNode): bool =
-  #for i in 1..n.len-1:
-  #  if n[i].kind != n[0].kind: return true
   for i in 0..n.len-1:
     if n[i].kind in {xnText, xnEntity}: return true
 
 proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
           addNewLines=true) =
-  ## adds the textual representation of `n` to `result`.
+  ## Adds the textual representation of `n` to string `result`.
+  runnableExamples:
+    var
+      a = newElement("firstTag")
+      b = newText("my text")
+      c = newComment("my comment")
+      s = ""
+    s.add(c)
+    s.add(a)
+    s.add(b)
+    assert s == "<!-- my comment --><firstTag />my text"
 
   proc addEscapedAttr(result: var string, s: string) =
     # `addEscaped` alternative with less escaped characters.
@@ -291,24 +598,76 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
     result.add(n.fText)
     result.add(';')
 
-const
-  xmlHeader* = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
-    ## header to use for complete XML output
-
 proc `$`*(n: XmlNode): string =
-  ## converts `n` into its string representation. No ``<$xml ...$>`` declaration
-  ## is produced, so that the produced XML fragments are composable.
+  ## Converts `n` into its string representation.
+  ##
+  ## No ``<$xml ...$>`` declaration is produced, so that the produced
+  ## XML fragments are composable.
   result = ""
   result.add(n)
 
-proc newXmlTree*(tag: string, children: openArray[XmlNode],
-                 attributes: XmlAttributes = nil): XmlNode =
-  ## creates a new XML tree with `tag`, `children` and `attributes`
-  result = newXmlNode(xnElement)
-  result.fTag = tag
-  newSeq(result.s, children.len)
-  for i in 0..children.len-1: result.s[i] = children[i]
-  result.fAttr = attributes
+proc child*(n: XmlNode, name: string): XmlNode =
+  ## Finds the first child element of `n` with a name of `name`.
+  ## Returns `nil` on failure.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("firstSon")
+    f.add newElement("secondSon")
+    f.add newElement("thirdSon")
+    assert $(f.child("secondSon")) == "<secondSon />"
+
+  assert n.kind == xnElement
+  for i in items(n):
+    if i.kind == xnElement:
+      if i.tag == name:
+        return i
+
+proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) =
+  ## Iterates over all the children of `n` returning those matching `tag`.
+  ##
+  ## Found nodes satisfying the condition will be appended to the `result`
+  ## sequence.
+  runnableExamples:
+    var
+      b = newElement("good")
+      c = newElement("bad")
+      d = newElement("bad")
+      e = newElement("good")
+    b.add newText("b text")
+    c.add newText("c text")
+    d.add newText("d text")
+    e.add newText("e text")
+    let a = newXmlTree("father", [b, c, d, e])
+    var s = newSeq[XmlNode]()
+    a.findAll("good", s)
+    assert $s == "@[<good>b text</good>, <good>e text</good>]"
+
+  assert n.k == xnElement
+  for child in n.items():
+    if child.k != xnElement:
+      continue
+    if child.tag == tag:
+      result.add(child)
+    child.findAll(tag, result)
+
+proc findAll*(n: XmlNode, tag: string): seq[XmlNode] =
+  ## A shortcut version to assign in let blocks.
+  runnableExamples:
+    var
+      b = newElement("good")
+      c = newElement("bad")
+      d = newElement("bad")
+      e = newElement("good")
+    b.add newText("b text")
+    c.add newText("c text")
+    d.add newText("d text")
+    e.add newText("e text")
+    let a = newXmlTree("father", [b, c, d, e])
+    assert $(a.findAll("good")) == "@[<good>b text</good>, <good>e text</good>]"
+    assert $(a.findAll("bad")) == "@[<bad>c text</bad>, <bad>d text</bad>]"
+
+  newSeq(result, 0)
+  findAll(n, tag, result)
 
 proc xmlConstructor(a: NimNode): NimNode {.compileTime.} =
   if a.kind == nnkCall:
@@ -346,56 +705,6 @@ macro `<>`*(x: untyped): untyped =
   ##
   result = xmlConstructor(x)
 
-proc child*(n: XmlNode, name: string): XmlNode =
-  ## Finds the first child element of `n` with a name of `name`.
-  ## Returns `nil` on failure.
-  assert n.kind == xnElement
-  for i in items(n):
-    if i.kind == xnElement:
-      if i.tag == name:
-        return i
-
-proc attr*(n: XmlNode, name: string): string =
-  ## Finds the first attribute of `n` with a name of `name`.
-  ## Returns "" on failure.
-  assert n.kind == xnElement
-  if n.attrs == nil: return ""
-  return n.attrs.getOrDefault(name)
-
-proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) =
-  ## Iterates over all the children of `n` returning those matching `tag`.
-  ##
-  ## Found nodes satisfying the condition will be appended to the `result`
-  ## sequence, which can't be nil or the proc will crash. Usage example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     html: XmlNode
-  ##     tags: seq[XmlNode] = @[]
-  ##
-  ##   html = buildHtml()
-  ##   findAll(html, "img", tags)
-  ##   for imgTag in tags:
-  ##     process(imgTag)
-  assert n.k == xnElement
-  for child in n.items():
-    if child.k != xnElement:
-      continue
-    if child.tag == tag:
-      result.add(child)
-    child.findAll(tag, result)
-
-proc findAll*(n: XmlNode, tag: string): seq[XmlNode] =
-  ## Shortcut version to assign in let blocks. Example:
-  ##
-  ## .. code-block::
-  ##   var html: XmlNode
-  ##
-  ##   html = buildHtml(html)
-  ##   for imgTag in html.findAll("img"):
-  ##     process(imgTag)
-  newSeq(result, 0)
-  findAll(n, tag, result)
 
 when isMainModule:
   assert """<a href="http://nim-lang.org">Nim rules.</a>""" ==