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.nim123
-rw-r--r--lib/pure/asyncmacro.nim14
-rw-r--r--lib/pure/collections/critbits.nim2
-rw-r--r--lib/pure/collections/deques.nim4
-rw-r--r--lib/pure/collections/lists.nim137
-rw-r--r--lib/pure/collections/queues.nim3
-rw-r--r--lib/pure/collections/sequtils.nim440
-rw-r--r--lib/pure/collections/sharedstrings.nim2
-rw-r--r--lib/pure/collections/tableimpl.nim2
-rw-r--r--lib/pure/concurrency/cpuinfo.nim21
-rw-r--r--lib/pure/concurrency/threadpool.nim18
-rw-r--r--lib/pure/future.nim6
-rw-r--r--lib/pure/httpclient.nim22
-rw-r--r--lib/pure/httpcore.nim3
-rw-r--r--lib/pure/includes/osenv.nim4
-rw-r--r--lib/pure/json.nim51
-rw-r--r--lib/pure/lexbase.nim5
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/matchers.nim2
-rw-r--r--lib/pure/math.nim2
-rw-r--r--lib/pure/nativesockets.nim14
-rw-r--r--lib/pure/net.nim7
-rw-r--r--lib/pure/options.nim5
-rw-r--r--lib/pure/os.nim5
-rw-r--r--lib/pure/osproc.nim4
-rw-r--r--lib/pure/parsecsv.nim5
-rw-r--r--lib/pure/parseopt2.nim2
-rw-r--r--lib/pure/parseutils.nim2
-rw-r--r--lib/pure/random.nim2
-rw-r--r--lib/pure/rationals.nim82
-rw-r--r--lib/pure/securehash.nim2
-rw-r--r--lib/pure/selectors.nim4
-rw-r--r--lib/pure/streams.nim32
-rw-r--r--lib/pure/strscans.nim32
-rw-r--r--lib/pure/strutils.nim254
-rw-r--r--lib/pure/times.nim19
-rw-r--r--lib/pure/typetraits.nim16
-rw-r--r--lib/pure/unicode.nim2
-rw-r--r--lib/pure/unittest.nim2
-rw-r--r--lib/pure/uri.nim15
40 files changed, 903 insertions, 466 deletions
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 9eee04404..fdf2d7cbb 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -371,3 +371,126 @@ when isMainModule:
   for i in 0 .. high(arr1):
     assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
     assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
+
+
+proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
+  ## A port of std::rotate from c++. Ported from `this reference <http://www.cplusplus.com/reference/algorithm/rotate/>`_.
+  result = first + last - middle
+
+  if first == middle or middle == last:
+    return
+
+  assert first < middle
+  assert middle < last
+
+  # m prefix for mutable
+  var
+    mFirst = first
+    mMiddle = middle
+    next = middle
+
+  swap(arg[mFirst], arg[next])
+  mFirst += 1
+  next  += 1
+  if mFirst == mMiddle:
+    mMiddle = next
+
+  while next != last:
+    swap(arg[mFirst], arg[next])
+    mFirst += 1
+    next += 1
+    if mFirst == mMiddle:
+      mMiddle = next
+
+  next = mMiddle
+  while next != last:
+    swap(arg[mFirst], arg[next])
+    mFirst += 1
+    next += 1
+    if mFirst == mMiddle:
+      mMiddle = next
+    elif next == last:
+      next = mMiddle
+
+proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] =
+  result = newSeq[T](arg.len)
+  for i in 0 ..< first:
+    result[i] = arg[i]
+  let N = last - middle
+  let M = middle - first
+  for i in 0 ..< N:
+    result[first+i] = arg[middle+i]
+  for i in 0 ..< M:
+    result[first+N+i] = arg[first+i]
+  for i in last ..< arg.len:
+    result[i] = arg[i]
+
+proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int =
+  ## 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.
+  ## The element at index ``slice.a + dist`` will be at index ``slice.a``.
+  ## The element at index ``slice.b`` will be at ``slice.a + dist -1``.
+  ## The element at index ``slice.a`` will be at ``slice.b + 1 - dist``.
+  ## The element at index ``slice.a + dist - 1`` will be at ``slice.b``.
+  #
+  ## Elements outsize of ``slice`` will be left unchanged.
+  ## The time complexity is linear to ``slice.b - slice.a + 1``.
+  ##
+  ## ``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.
+  ##
+  ## .. 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]
+  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)
+
+proc rotateLeft*[T](arg: var openarray[T]; dist: int): int =
+  ## default arguments for slice, so that this procedure operates on the entire
+  ## ``arg``, and not just on a part of it.
+  let arglen = arg.len
+  let distLeft = ((dist mod arglen) + arglen) mod arglen
+  arg.rotateInternal(0, distLeft, arglen)
+
+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
+  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)
+
+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
+  let arglen = arg.len
+  let distLeft = ((dist mod arglen) + arglen) mod arglen
+  arg.rotatedInternal(0, distLeft, arg.len)
+
+when isMainModule:
+  var list     = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+  let list2    = list.rotatedLeft(1 ..< 9, 3)
+  let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+
+  doAssert list.rotateLeft(1 ..< 9, 3) == 6
+  doAssert list  ==  expected
+  doAssert list2 == @expected
+
+  var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx"
+
+  doAssert s0.rotateLeft(3 ..< 10, 3) == 7
+  doAssert s0 == "xxxdefgabcxxx"
+  doAssert s1.rotateLeft(3 ..< 10, 2) == 8
+  doAssert s1 == "xxxcdefgabxxx"
+  doAssert s2.rotateLeft(3 ..< 10, 4) == 6
+  doAssert s2 == "xxxefgabcdxxx"
+  doAssert s3.rotateLeft(3 ..< 10, -3) == 6
+  doAssert s3 == "xxxefgabcdxxx"
+  doAssert s4.rotateLeft(3 ..< 10, -10) == 6
+  doAssert s4 == "xxxefgabcdxxx"
+  doAssert s5.rotateLeft(3 ..< 10, 11) == 6
+  doAssert s5 == "xxxefgabcdxxx"
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 6e7d7993f..981190211 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -61,14 +61,14 @@ proc generateExceptionCheck(futSym,
   else:
     var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
     let errorNode = newDotExpr(futSym, newIdentNode("error"))
-    for i in 1 .. <tryStmt.len:
+    for i in 1 ..< tryStmt.len:
       let exceptBranch = tryStmt[i]
       if exceptBranch[0].kind == nnkStmtList:
         exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
       else:
         var exceptIdentCount = 0
         var ifCond: NimNode
-        for i in 0 .. <exceptBranch.len:
+        for i in 0 ..< exceptBranch.len:
           let child = exceptBranch[i]
           if child.kind == nnkIdent:
             let cond = infix(errorNode, "of", child)
@@ -270,7 +270,7 @@ proc processBody(node, retFutureSym: NimNode,
     return
   else: discard
 
-  for i in 0 .. <result.len:
+  for i in 0 ..< result.len:
     result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
                             futureVarIdents, nil)
 
@@ -287,7 +287,7 @@ proc getName(node: NimNode): string {.compileTime.} =
 
 proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
   result = @[]
-  for i in 1 .. <len(params):
+  for i in 1 ..< len(params):
     expectKind(params[i], nnkIdentDefs)
     if params[i][1].kind == nnkBracketExpr and
        ($params[i][1][0].ident).normalize == "futurevar":
@@ -466,7 +466,7 @@ proc stripAwait(node: NimNode): NimNode =
       node[0][0] = emptyNoopSym
   else: discard
 
-  for i in 0 .. <result.len:
+  for i in 0 ..< result.len:
     result[i] = stripAwait(result[i])
 
 proc splitParamType(paramType: NimNode, async: bool): NimNode =
@@ -512,7 +512,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) =
   # Retrieve the `T` inside `Future[T]`.
   let returnType = stripReturnType(result[0][3][0])
   result[0][3][0] = splitParamType(returnType, async=false)
-  for i in 1 .. <result[0][3].len:
+  for i in 1 ..< result[0][3].len:
     # Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
     result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false)
@@ -521,7 +521,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) =
   result[1] = prc.copyNimTree()
   if result[1][3][0].kind == nnkBracketExpr:
     result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true)
-  for i in 1 .. <result[1][3].len:
+  for i in 1 ..< result[1][3].len:
     # Async proc (1) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
     result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true)
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index f70a12843..19f1f2e58 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -257,7 +257,7 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node
       p = p.child[dir]
       if q.byte < key.len: top = p
     if not longestMatch:
-      for i in 0 .. <key.len:
+      for i in 0 ..< key.len:
         if p.key[i] != key[i]: return
     result = top
 
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index 1bbe9f1ad..1e0cb82d2 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -207,9 +207,9 @@ when isMainModule:
   assert($deq == "[4, 56, 6, 789]")
 
   assert deq[0] == deq.peekFirst and deq.peekFirst == 4
-  assert deq[^1] == deq.peekLast and deq.peekLast == 789
+  #assert deq[^1] == deq.peekLast and deq.peekLast == 789
   deq[0] = 42
-  deq[^1] = 7
+  deq[deq.len - 1] = 7
 
   assert 6 in deq and 789 notin deq
   assert deq.find(6) >= 0
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index f847ddd58..560273dfa 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -37,6 +37,14 @@ type
   DoublyLinkedRing*[T] = object ## a doubly linked ring
     head*: DoublyLinkedNode[T]
 
+  SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
+
+  SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T]
+
+  SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T]
+
+  SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
+
 {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
     PDoublyLinkedNode: DoublyLinkedNode,
     TSinglyLinkedNode: SinglyLinkedNodeObj,
@@ -86,137 +94,57 @@ template itemsRingImpl() {.dirty.} =
       it = it.next
       if it == L.head: break
 
-template nodesListImpl() {.dirty.} =
-  var it = L.head
-  while it != nil:
-    var nxt = it.next
-    yield it
-    it = nxt
-
-template nodesRingImpl() {.dirty.} =
-  var it = L.head
-  if it != nil:
-    while true:
-      var nxt = it.next
-      yield it
-      it = nxt
-      if it == L.head: break
-
-template findImpl() {.dirty.} =
-  for x in nodes(L):
-    if x.value == value: return x
-
-iterator items*[T](L: DoublyLinkedList[T]): T =
+iterator items*[T](L: SomeLinkedList[T]): T =
   ## yields every value of `L`.
   itemsListImpl()
 
-iterator items*[T](L: SinglyLinkedList[T]): T =
-  ## yields every value of `L`.
-  itemsListImpl()
-
-iterator items*[T](L: SinglyLinkedRing[T]): T =
-  ## yields every value of `L`.
-  itemsRingImpl()
-
-iterator items*[T](L: DoublyLinkedRing[T]): T =
+iterator items*[T](L: SomeLinkedRing[T]): T =
   ## yields every value of `L`.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedList[T]): var T =
+iterator mitems*[T](L: var SomeLinkedList[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsListImpl()
 
-iterator mitems*[T](L: var SinglyLinkedList[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsListImpl()
-
-iterator mitems*[T](L: var SinglyLinkedRing[T]): var T =
+iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsRingImpl()
-
-iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  while it != nil:
+    var nxt = it.next
+    yield it
+    it = nxt
 
-iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  if it != nil:
+    while true:
+      var nxt = it.next
+      yield it
+      it = nxt
+      if it == L.head: break
 
-template dollarImpl() {.dirty.} =
+proc `$`*[T](L: SomeLinkedCollection[T]): string =
+  ## turns a list into its string representation.
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
     result.add($x.value)
   result.add("]")
 
-proc `$`*[T](L: SinglyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: SinglyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
+proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
   ## searches in the list for a value. Returns nil if the value does not
   ## exist.
-  findImpl()
-
-proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
+  for x in nodes(L):
+    if x.value == value: return x
 
-proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
+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.
   result = find(L, value) != nil
@@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   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).
   if L.head != nil:
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
index 401422162..ce792d6da 100644
--- a/lib/pure/collections/queues.nim
+++ b/lib/pure/collections/queues.nim
@@ -198,9 +198,8 @@ when isMainModule:
   assert($q == "[4, 56, 6, 789]")
 
   assert q[0] == q.front and q.front == 4
-  assert q[^1] == q.back and q.back == 789
   q[0] = 42
-  q[^1] = 7
+  q[q.len - 1] = 7
 
   assert 6 in q and 789 notin q
   assert q.find(6) >= 0
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index e8e725aa3..0eb8e6704 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -13,8 +13,9 @@
 ## were inspired by functional programming languages.
 ##
 ## For functional style programming you may want to pass `anonymous procs
-## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
-## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
+## <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.
 
 include "system/inclrtl"
@@ -43,8 +44,23 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
       result[i] = itm
       inc(i)
 
-proc cycle*[T](s: seq[T], n: Natural): seq[T] =
-  ## Returns a new sequence with the items of `s` repeated `n` times.
+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
+  for itm in items(s):
+    if itm == x:
+      inc result
+
+proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
+  ## Returns a new sequence with the items of the container `s` repeated
+  ## `n` times.
   ##
   ## Example:
   ##
@@ -56,7 +72,7 @@ proc cycle*[T](s: seq[T], n: Natural): seq[T] =
   ##   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:
+  for x in 0 ..< n:
     for e in s:
       result[o] = e
       inc o
@@ -72,12 +88,14 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
   ##     total = repeat(5, 3)
   ##   assert total == @[5, 5, 5]
   result = newSeq[T](n)
-  for i in 0..<n:
+  for i in 0 ..< n:
     result[i] = x
 
-proc deduplicate*[T](seq1: seq[T]): seq[T] =
+proc deduplicate*[T](s: openArray[T]): seq[T] =
   ## Returns a new sequence without duplicates.
   ##
+  ## Example:
+  ##
   ## .. code-block::
   ##   let
   ##     dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
@@ -87,17 +105,19 @@ proc deduplicate*[T](seq1: seq[T]): seq[T] =
   ##   assert unique1 == @[1, 3, 4, 2, 8]
   ##   assert unique2 == @["a", "c", "d"]
   result = @[]
-  for itm in items(seq1):
+  for itm in items(s):
     if not result.contains(itm): result.add(itm)
 
 {.deprecated: [distnct: deduplicate].}
 
-proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
-  ## Returns a new sequence with a combination of the two input sequences.
+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 sequence is shorter, the remaining items in the
-  ## longer sequence are discarded. Example:
+  ## fields `a` and `b`. If one container is shorter, the remaining items in
+  ## the longer container are discarded.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -110,15 +130,16 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
   ##   assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
   ##   assert zip1[2].b == 4
   ##   assert zip2[2].b == "three"
-  var m = min(seq1.len, seq2.len)
+  var m = min(s1.len, s2.len)
   newSeq(result, m)
-  for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
+  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.
   ##
   ## 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
+  ## 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
   ## `num` empty sequences.
@@ -159,48 +180,52 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
     # Use an algorithm which overcounts the stride and minimizes reading limits.
     if extra > 0: inc(stride)
 
-    for i in 0 .. <num:
+    for i in 0 ..< num:
       result[i] = newSeq[T]()
-      for g in first .. <min(s.len, first + stride):
+      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:
+    for i in 0 ..< num:
       last = first + stride
       if extra > 0:
         extra -= 1
         inc(last)
 
       result[i] = newSeq[T]()
-      for g in first .. <last:
+      for g in first ..< last:
         result[i].add(s[g])
       first = last
 
-
-proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}):
+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
-  ## `data`.
+  ## the container `s`.
   ##
   ## Since the input is not modified you can use this version of ``map`` to
-  ## transform the type of the elements in the input sequence. Example:
+  ## transform the type of the elements in the input container.
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   let
   ##     a = @[1, 2, 3, 4]
   ##     b = map(a, proc(x: int): string = $x)
   ##   assert b == @["1", "2", "3", "4"]
-  newSeq(result, data.len)
-  for i in 0..data.len-1: result[i] = op(data[i])
+  newSeq(result, s.len)
+  for i in 0 ..< s.len:
+    result[i] = op(s[i])
 
-proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
+proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
                                                               {.deprecated.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## 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:
+  ## be the same, since they are modified in-place.
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   var a = @["1", "2", "3", "4"]
@@ -210,15 +235,16 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
-  for i in 0..data.len-1: op(data[i])
+  for i in 0 ..< s.len: op(s[i])
 
-proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
+proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
                                                               {.inline.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## 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.
   ## The parameter function takes a ``var T`` type parameter.
+  ##
   ## Example:
   ##
   ## .. code-block:: nim
@@ -229,15 +255,16 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
-  for i in 0..data.len-1: op(data[i])
+  for i in 0 ..< s.len: op(s[i])
 
-proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
+proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
                                                               {.inline.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## 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.
   ## The parameter function takes and returns a ``T`` type variable.
+  ##
   ## Example:
   ##
   ## .. code-block:: nim
@@ -248,11 +275,10 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
-  for i in 0..data.len-1: data[i] = op(data[i])
+  for i in 0 ..< s.len: s[i] = op(s[i])
 
-
-iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
-  ## Iterates through a sequence and yields every item that fulfills the
+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.
   ##
   ## Example:
@@ -262,11 +288,11 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
   ##   for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
   ##     echo($n)
   ##   # echoes 4, 8, 4 in separate lines
-  for i in 0..<seq1.len:
-    if pred(seq1[i]):
-      yield seq1[i]
+  for i in 0 ..< s.len:
+    if pred(s[i]):
+      yield s[i]
 
-proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
+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.
   ##
@@ -280,11 +306,11 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
   ##   assert f1 == @["red", "black"]
   ##   assert f2 == @["yellow"]
   result = newSeq[T]()
-  for i in 0..<seq1.len:
-    if pred(seq1[i]):
-      result.add(seq1[i])
+  for i in 0 ..< s.len:
+    if pred(s[i]):
+      result.add(s[i])
 
-proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
+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.
@@ -296,12 +322,12 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
   ##   keepIf(floats, proc(x: float): bool = x > 10)
   ##   assert floats == @[13.0, 12.5, 10.1]
   var pos = 0
-  for i in 0 .. <len(seq1):
-    if pred(seq1[i]):
+  for i in 0 ..< len(s):
+    if pred(s[i]):
       if pos != i:
-        shallowCopy(seq1[pos], seq1[i])
+        shallowCopy(s[pos], s[i])
       inc(pos)
-  setLen(seq1, pos)
+  setLen(s, pos)
 
 proc delete*[T](s: var seq[T]; first, last: Natural) =
   ## Deletes in `s` the items at position `first` .. `last`. This modifies
@@ -354,11 +380,12 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
     inc(j)
 
 
-template filterIt*(seq1, pred: untyped): untyped =
+template filterIt*(s, pred: untyped): untyped =
   ## Returns a new sequence with all the items that fulfilled the predicate.
   ##
   ## Unlike the `proc` version, the predicate needs to be an expression using
   ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
+  ##
   ## Example:
   ##
   ## .. code-block::
@@ -368,8 +395,8 @@ template filterIt*(seq1, pred: untyped): untyped =
   ##      notAcceptable = filterIt(temperatures, 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(seq1[0])]()
-  for it {.inject.} in items(seq1):
+  var result = newSeq[type(s[0])]()
+  for it {.inject.} in items(s):
     if pred: result.add(it)
   result
 
@@ -378,6 +405,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
   ##
   ## Unlike the `proc` version, the predicate needs to be an expression using
   ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
+  ##
   ## Example:
   ##
   ## .. code-block::
@@ -385,7 +413,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
   ##   keepItIf(candidates, it.len == 3 and it[0] == 'b')
   ##   assert candidates == @["bar", "baz"]
   var pos = 0
-  for i in 0 .. <len(varSeq):
+  for i in 0 ..< len(varSeq):
     let it {.inject.} = varSeq[i]
     if pred:
       if pos != i:
@@ -393,8 +421,8 @@ template keepItIf*(varSeq: seq, pred: untyped) =
       inc(pos)
   setLen(varSeq, pos)
 
-proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
-  ## Iterates through a sequence and checks if every item fulfills the
+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:
@@ -403,12 +431,12 @@ proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
   ##   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 seq1:
+  for i in s:
     if not pred(i):
       return false
   return true
 
-template allIt*(seq1, pred: untyped): bool =
+template allIt*(s, pred: untyped): bool =
   ## Checks if every item fulfills the predicate.
   ##
   ## Example:
@@ -418,14 +446,14 @@ template allIt*(seq1, pred: untyped): bool =
   ##   assert allIt(numbers, it < 10) == true
   ##   assert allIt(numbers, it < 9) == false
   var result = true
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     if not pred:
       result = false
       break
   result
 
-proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
-  ## Iterates through a sequence and checks if some item fulfills the
+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:
@@ -434,12 +462,12 @@ proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
   ##   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 seq1:
+  for i in s:
     if pred(i):
       return true
   return false
 
-template anyIt*(seq1, pred: untyped): bool =
+template anyIt*(s, pred: untyped): bool =
   ## Checks if some item fulfills the predicate.
   ##
   ## Example:
@@ -449,7 +477,7 @@ template anyIt*(seq1, pred: untyped): bool =
   ##   assert anyIt(numbers, it > 8) == true
   ##   assert anyIt(numbers, it > 9) == false
   var result = false
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     if pred:
       result = true
       break
@@ -493,7 +521,9 @@ template foldl*(sequence, operation: untyped): untyped =
   ## variables ``a`` and ``b`` for each step of the fold. Since this is a left
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
-  ## 3).  Example:
+  ## 3).
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -527,6 +557,7 @@ template foldl*(sequence, operation, first): untyped =
   ## 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:
   ##
   ## .. code-block::
@@ -555,7 +586,9 @@ template foldr*(sequence, operation: untyped): untyped =
   ## variables ``a`` and ``b`` for each step of the fold. Since this is a right
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
-  ## (3))). Example:
+  ## (3))).
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -580,13 +613,15 @@ template foldr*(sequence, operation: untyped): untyped =
     result = operation
   result
 
-template mapIt*(seq1, typ, op: untyped): untyped =
+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:
+  ## original.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -596,16 +631,18 @@ template mapIt*(seq1, typ, op: untyped): untyped =
   ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
   ##   template instead.
   var result: seq[typ] = @[]
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     result.add(op)
   result
 
 
-template mapIt*(seq1, op: untyped): untyped =
+template mapIt*(s, 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. Example:
+  ## expression.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -614,19 +651,19 @@ template mapIt*(seq1, op: untyped): untyped =
   ##   assert strings == @["4", "8", "12", "16"]
   type outType = type((
     block:
-      var it{.inject.}: type(items(seq1));
+      var it{.inject.}: type(items(s));
       op))
   var result: seq[outType]
-  when compiles(seq1.len):
-    let s = seq1
+  when compiles(s.len):
+    let t = s
     var i = 0
     result = newSeq[outType](s.len)
-    for it {.inject.} in s:
+    for it {.inject.} in t:
       result[i] = op
       i += 1
   else:
     result = @[]
-    for it {.inject.} in seq1:
+    for it {.inject.} in s:
       result.add(op)
   result
 
@@ -635,20 +672,23 @@ template applyIt*(varSeq, op: untyped) =
   ##
   ## The template injects the ``it`` variable which you can use directly in an
   ## expression. The expression has to return the same type as the sequence you
-  ## are mutating. Example:
+  ## are mutating.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   var nums = @[1, 2, 3, 4]
   ##   nums.applyIt(it * 3)
   ##   assert nums[0] + nums[3] == 15
-  for i in 0 .. <varSeq.len:
+  for i in 0 ..< varSeq.len:
     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. Example:
+  ## creates a new sequence, calling `init` to initialize each value.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   var seq2D = newSeqWith(20, newSeq[bool](10))
@@ -660,7 +700,7 @@ template newSeqWith*(len: int, init: untyped): untyped =
   ##   var seqRand = newSeqWith(20, random(10))
   ##   echo seqRand
   var result = newSeq[type(init)](len)
-  for i in 0 .. <len:
+  for i in 0 ..< len:
     result[i] = init
   result
 
@@ -674,45 +714,178 @@ when isMainModule:
       total = concat(s1, s2, s3)
     assert total == @[1, 2, 3, 4, 5, 6, 7]
 
-  block: # duplicates test
+  block: # count test
+    let
+      s1 = @[1, 2, 3, 2]
+      s2 = @['a', 'b', 'x', 'a']
+      a1 = [1, 2, 3, 2]
+      a2 = ['a', 'b', 'x', 'a']
+      r0 = count(s1, 0)
+      r1 = count(s1, 1)
+      r2 = count(s1, 2)
+      r3 = count(s2, 'y')
+      r4 = count(s2, 'x')
+      r5 = count(s2, 'a')
+      ar0 = count(a1, 0)
+      ar1 = count(a1, 1)
+      ar2 = count(a1, 2)
+      ar3 = count(a2, 'y')
+      ar4 = count(a2, 'x')
+      ar5 = count(a2, 'a')
+    assert r0 == 0
+    assert r1 == 1
+    assert r2 == 2
+    assert r3 == 0
+    assert r4 == 1
+    assert r5 == 2
+    assert ar0 == 0
+    assert ar1 == 1
+    assert ar2 == 2
+    assert ar3 == 0
+    assert ar4 == 1
+    assert ar5 == 2
+
+  block: # cycle tests
+    let
+      a = @[1, 2, 3]
+      b: seq[int] = @[]
+      c = [1, 2, 3]
+
+    doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+    doAssert a.cycle(0) == @[]
+    #doAssert a.cycle(-1) == @[] # will not compile!
+    doAssert b.cycle(3) == @[]
+    doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+    doAssert c.cycle(0) == @[]
+
+  block: # repeat tests
+    assert repeat(10, 5) == @[10, 10, 10, 10, 10]
+    assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
+    assert repeat([1,2,3], 2) == @[[1,2,3], [1,2,3]]
+
+  block: # deduplicates test
     let
       dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
       dup2 = @["a", "a", "c", "d", "d"]
+      dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4]
+      dup4 = ["a", "a", "c", "d", "d"]
       unique1 = deduplicate(dup1)
       unique2 = deduplicate(dup2)
+      unique3 = deduplicate(dup3)
+      unique4 = deduplicate(dup4)
     assert unique1 == @[1, 3, 4, 2, 8]
     assert unique2 == @["a", "c", "d"]
+    assert unique3 == @[1, 3, 4, 2, 8]
+    assert unique4 == @["a", "c", "d"]
 
   block: # zip test
     let
       short = @[1, 2, 3]
       long = @[6, 5, 4, 3, 2, 1]
       words = @["one", "two", "three"]
+      ashort = [1, 2, 3]
+      along = [6, 5, 4, 3, 2, 1]
+      awords = ["one", "two", "three"]
       zip1 = zip(short, long)
       zip2 = zip(short, words)
+      zip3 = zip(ashort, along)
+      zip4 = zip(ashort, awords)
+      zip5 = zip(ashort, words)
     assert zip1 == @[(1, 6), (2, 5), (3, 4)]
     assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip3 == @[(1, 6), (2, 5), (3, 4)]
+    assert zip4 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip5 == @[(1, "one"), (2, "two"), (3, "three")]
     assert zip1[2].b == 4
     assert zip2[2].b == "three"
+    assert zip3[2].b == 4
+    assert zip4[2].b == "three"
+    assert zip5[2].b == "three"
+
+  block: # distribute tests
+    let numbers = @[1, 2, 3, 4, 5, 6, 7]
+    doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert numbers.distribute(6)[0] == @[1, 2]
+    doAssert numbers.distribute(6)[5] == @[7]
+    let a = @[1, 2, 3, 4, 5, 6, 7]
+    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
+    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
+    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
+    doAssert a.distribute(6, false)  == @[
+      @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
+    doAssert a.distribute(8, false)  == a.distribute(8, true)
+    doAssert a.distribute(90, false) == a.distribute(90, true)
+    var b = @[0]
+    for f in 1 .. 25: b.add(f)
+    doAssert b.distribute(5, true)[4].len == 5
+    doAssert b.distribute(5, false)[4].len == 2
+
+  block: # map test
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      m1 = map(numbers, proc(x: int): int = 2*x)
+      m2 = map(anumbers, proc(x: int): int = 2*x)
+    assert m1 == @[2, 8, 10, 16, 18, 14, 8]
+    assert m2 == @[2, 8, 10, 16, 18, 14, 8]
+
+  block: # apply test
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: var string) = x &= "42")
+    assert a == @["142", "242", "342", "442"]
 
   block: # filter proc test
     let
       colors = @["red", "yellow", "black"]
+      acolors = ["red", "yellow", "black"]
       f1 = filter(colors, proc(x: string): bool = x.len < 6)
       f2 = filter(colors) do (x: string) -> bool : x.len > 5
+      f3 = filter(acolors, proc(x: string): bool = x.len < 6)
+      f4 = filter(acolors) do (x: string) -> bool : x.len > 5
     assert f1 == @["red", "black"]
     assert f2 == @["yellow"]
+    assert f3 == @["red", "black"]
+    assert f4 == @["yellow"]
 
   block: # filter iterator test
     let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    let anumbers = [1, 4, 5, 8, 9, 7, 4]
     assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
       @[4, 8, 4]
+    assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) ==
+      @[4, 8, 4]
 
   block: # keepIf test
     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]
 
+  block: # delete tests
+    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, """\
+    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    is [1,1,1,1,1,1,1,1]"""
+
+  block: # insert tests
+    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, """\
+    Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
+    at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
+
   block: # filterIt test
     let
       temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
@@ -726,37 +899,49 @@ when isMainModule:
     keepItIf(candidates, it.len == 3 and it[0] == 'b')
     assert candidates == @["bar", "baz"]
 
-  block: # any
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert any(numbers, proc (x: int): bool = return x > 8) == true
-    assert any(numbers, proc (x: int): bool = return x > 9) == false
-    assert any(len0seq, proc (x: int): bool = return true) == false
-
-  block: # anyIt
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert anyIt(numbers, it > 8) == true
-    assert anyIt(numbers, it > 9) == false
-    assert anyIt(len0seq, true) == false
-
   block: # all
     let
       numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
       len0seq : seq[int] = @[]
     assert all(numbers, proc (x: int): bool = return x < 10) == true
     assert all(numbers, proc (x: int): bool = return x < 9) == false
     assert all(len0seq, proc (x: int): bool = return false) == true
+    assert all(anumbers, proc (x: int): bool = return x < 10) == true
+    assert all(anumbers, proc (x: int): bool = return x < 9) == false
 
   block: # allIt
     let
       numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
       len0seq : seq[int] = @[]
     assert allIt(numbers, it < 10) == true
     assert allIt(numbers, it < 9) == false
     assert allIt(len0seq, false) == true
+    assert allIt(anumbers, it < 10) == true
+    assert allIt(anumbers, it < 9) == false
+
+  block: # any
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      len0seq : seq[int] = @[]
+    assert any(numbers, proc (x: int): bool = return x > 8) == true
+    assert any(numbers, proc (x: int): bool = return x > 9) == false
+    assert any(len0seq, proc (x: int): bool = return true) == false
+    assert any(anumbers, proc (x: int): bool = return x > 8) == true
+    assert any(anumbers, proc (x: int): bool = return x > 9) == false
+
+  block: # anyIt
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      len0seq : seq[int] = @[]
+    assert anyIt(numbers, it > 8) == true
+    assert anyIt(numbers, it > 9) == false
+    assert anyIt(len0seq, true) == false
+    assert anyIt(anumbers, it > 8) == true
+    assert anyIt(anumbers, it > 9) == false
 
   block: # toSeq test
     let
@@ -792,56 +977,13 @@ when isMainModule:
     assert multiplication == 495, "Multiplication is (5*(9*(11)))"
     assert concatenation == "nimiscool"
 
-  block: # delete tests
-    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, """\
-    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-    is [1,1,1,1,1,1,1,1]"""
-
-  block: # insert tests
-    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, """\
-    Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
-    at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
-
   block: # mapIt tests
     var
       nums = @[1, 2, 3, 4]
       strings = nums.mapIt($(4 * it))
     nums.applyIt(it * 3)
     assert nums[0] + nums[3] == 15
-
-  block: # distribute tests
-    let numbers = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert numbers.distribute(6)[0] == @[1, 2]
-    doAssert numbers.distribute(6)[5] == @[7]
-    let a = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
-    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
-    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
-    doAssert a.distribute(6, false)  == @[
-      @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
-    doAssert a.distribute(8, false)  == a.distribute(8, true)
-    doAssert a.distribute(90, false) == a.distribute(90, true)
-    var b = @[0]
-    for f in 1 .. 25: b.add(f)
-    doAssert b.distribute(5, true)[4].len == 5
-    doAssert b.distribute(5, false)[4].len == 2
+    assert strings[2] == "12"
 
   block: # newSeqWith tests
     var seq2D = newSeqWith(4, newSeq[bool](2))
@@ -850,19 +992,5 @@ when isMainModule:
     seq2D[0][1] = true
     doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
 
-  block: # cycle tests
-    let
-      a = @[1, 2, 3]
-      b: seq[int] = @[]
-
-    doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
-    doAssert a.cycle(0) == @[]
-    #doAssert a.cycle(-1) == @[] # will not compile!
-    doAssert b.cycle(3) == @[]
-
-  block: # repeat tests
-    assert repeat(10, 5) == @[10, 10, 10, 10, 10]
-    assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
-
   when not defined(testing):
     echo "Finished doc tests"
diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim
index a9e194fb4..83edf8d94 100644
--- a/lib/pure/collections/sharedstrings.nim
+++ b/lib/pure/collections/sharedstrings.nim
@@ -55,7 +55,7 @@ proc `[]=`*(s: var SharedString; i: Natural; value: char) =
   if i < s.len: s.buffer.data[i+s.first] = value
   else: raise newException(IndexError, "index out of bounds")
 
-proc `[]`*(s: SharedString; ab: Slice[int]): SharedString =
+proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
   #incRef(src.buffer)
   if ab.a < s.len:
     result.buffer = s.buffer
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index eec98fcaf..9a5bffcef 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -149,7 +149,7 @@ template delImpl() {.dirty.} =
   delImplIdx(t, i)
 
 template clearImpl() {.dirty.} =
-  for i in 0 .. <t.data.len:
+  for i in 0 ..< t.data.len:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
     t.data[i].key = default(type(t.data[i].key))
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 603fee080..f01488811 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -45,8 +45,25 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
   ## returns the numer of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
   when defined(windows):
-    var x = getEnv("NUMBER_OF_PROCESSORS")
-    if x.len > 0: result = parseInt(x.string)
+    type
+      SYSTEM_INFO {.final, pure.} = object
+        u1: int32
+        dwPageSize: int32
+        lpMinimumApplicationAddress: pointer
+        lpMaximumApplicationAddress: pointer
+        dwActiveProcessorMask: ptr int32
+        dwNumberOfProcessors: int32
+        dwProcessorType: int32
+        dwAllocationGranularity: int32
+        wProcessorLevel: int16
+        wProcessorRevision: int16
+
+    proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".}
+
+    var
+      si: SYSTEM_INFO
+    GetSystemInfo(si)
+    result = si.dwNumberOfProcessors
   elif defined(macosx) or defined(bsd):
     var
       mib: array[0..3, cint]
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 2a0dbd2ca..a5eaec86e 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -149,7 +149,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
 proc cleanFlowVars(w: ptr Worker) =
   let q = addr(w.q)
   acquire(q.lock)
-  for i in 0 .. <q.len:
+  for i in 0 ..< q.len:
     GC_unref(cast[RootRef](q.data[i]))
     #echo "GC_unref"
   q.len = 0
@@ -401,7 +401,7 @@ proc setup() =
     gCpus = p
   currentPoolSize = min(p, MaxThreadPoolSize)
   readyWorker = addr(workersData[0])
-  for i in 0.. <currentPoolSize: activateWorkerThread(i)
+  for i in 0..<currentPoolSize: activateWorkerThread(i)
 
 proc preferSpawn*(): bool =
   ## Use this proc to determine quickly if a 'spawn' or a direct call is
@@ -446,14 +446,24 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
   # implementation of 'spawn' that is used by the code generator.
   while true:
     if selectWorker(readyWorker, fn, data): return
-    for i in 0.. <currentPoolSize:
+    for i in 0..<currentPoolSize:
       if selectWorker(addr(workersData[i]), fn, data): return
+
     # determine what to do, but keep in mind this is expensive too:
     # state.calls < maxPoolSize: warmup phase
     # (state.calls and 127) == 0: periodic check
     if state.calls < maxPoolSize or (state.calls and 127) == 0:
       # ensure the call to 'advice' is atomic:
       if tryAcquire(stateLock):
+        if currentPoolSize < minPoolSize:
+          if not workersData[currentPoolSize].initialized:
+            activateWorkerThread(currentPoolSize)
+          let w = addr(workersData[currentPoolSize])
+          atomicInc currentPoolSize
+          if selectWorker(w, fn, data):
+            release(stateLock)
+            return
+
         case advice(state)
         of doNothing: discard
         of doCreateThread:
@@ -533,7 +543,7 @@ proc sync*() =
   var toRelease = 0
   while true:
     var allReady = true
-    for i in 0 .. <currentPoolSize:
+    for i in 0 ..< currentPoolSize:
       if not allReady: break
       allReady = allReady and workersData[i].ready
     if allReady: break
diff --git a/lib/pure/future.nim b/lib/pure/future.nim
index 2a6d29933..f6592df71 100644
--- a/lib/pure/future.nim
+++ b/lib/pure/future.nim
@@ -22,7 +22,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
 
   case p.kind
   of nnkPar:
-    for i in 0 .. <p.len:
+    for i in 0 ..< p.len:
       let ident = p[i]
       var identDefs = newNimNode(nnkIdentDefs)
       case ident.kind
@@ -77,7 +77,7 @@ macro `=>`*(p, b: untyped): untyped =
         if c[0].kind == nnkIdent and c[0].ident == !"->":
           var procTy = createProcType(c[1], c[2])
           params[0] = procTy[0][0]
-          for i in 1 .. <procTy[0].len:
+          for i in 1 ..< procTy[0].len:
             params.add(procTy[0][i])
         else:
           error("Expected proc type (->) got (" & $c[0].ident & ").")
@@ -96,7 +96,7 @@ macro `=>`*(p, b: untyped): untyped =
     if p[0].kind == nnkIdent and p[0].ident == !"->":
       var procTy = createProcType(p[1], p[2])
       params[0] = procTy[0][0]
-      for i in 1 .. <procTy[0].len:
+      for i in 1 ..< procTy[0].len:
         params.add(procTy[0][i])
     else:
       error("Expected proc type (->) got (" & $p[0].ident & ").")
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index cb4f4f664..de1d332a3 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -883,7 +883,9 @@ proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int,
       let data = client.socket.recv(sizeToRecv, timeout)
     else:
       let data = await client.socket.recv(sizeToRecv)
-    if data == "": break # We've been disconnected.
+    if data == "":
+      client.close()
+      break # We've been disconnected.
 
     readLen.inc(data.len)
     if keep:
@@ -950,6 +952,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
       if length > 0:
         let recvLen = await client.recvFull(length, client.timeout, true)
         if recvLen == 0:
+          client.close()
           httpError("Got disconnected while trying to read body.")
         if recvLen != length:
           httpError("Received length doesn't match expected length. Wanted " &
@@ -962,13 +965,20 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
       if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
         while true:
           let recvLen = await client.recvFull(4000, client.timeout, true)
-          if recvLen == 0: break
+          if recvLen == 0:
+            client.close()
+            break
 
   when client is AsyncHttpClient:
     client.bodyStream.complete()
   else:
     client.bodyStream.setPosition(0)
 
+  # If the server will close our connection, then no matter the method of
+  # reading the body, we need to close our socket.
+  if headers.getOrDefault"Connection" == "close":
+    client.close()
+
 proc parseResponse(client: HttpClient | AsyncHttpClient,
                    getBody: bool): Future[Response | AsyncResponse]
                    {.multisync.} =
@@ -984,7 +994,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
       line = await client.socket.recvLine(client.timeout)
     else:
       line = await client.socket.recvLine()
-    if line == "": break # We've been disconnected.
+    if line == "":
+      # We've been disconnected.
+      client.close()
+      break
     if line == "\c\L":
       fullyRead = true
       break
@@ -1033,7 +1046,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
                    url: Uri) {.multisync.} =
   if client.currentURL.hostname != url.hostname or
       client.currentURL.scheme != url.scheme or
-      client.currentURL.port != url.port:
+      client.currentURL.port != url.port or
+      (not client.connected):
     let isSsl = url.scheme.toLowerAscii() == "https"
 
     if isSsl and not defined(ssl):
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index a5ab40ca4..f150fa1c1 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -113,6 +113,9 @@ proc newHttpHeaders*(keyValuePairs:
   new result
   result.table = newTable[string, seq[string]](pairs)
 
+proc `$`*(headers: HttpHeaders): string =
+  return $headers.table
+
 proc clear*(headers: HttpHeaders) =
   headers.table.clear()
 
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index 8d2fc235a..ae62a5c4e 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -94,7 +94,7 @@ proc findEnvVar(key: string): int =
     if startsWith(environment[i], temp): return i
   return -1
 
-proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
+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
@@ -108,7 +108,7 @@ proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
       return TaintedString(substr(environment[i], find(environment[i], '=')+1))
     else:
       var env = c_getenv(key)
-      if env == nil: return TaintedString("")
+      if env == nil: return TaintedString(default)
       result = TaintedString($env)
 
 proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 656114fb1..3d86cc9d7 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -37,15 +37,15 @@
 ## Retrieving the value of a JSON node can then be achieved using one of the
 ## helper procedures, which include:
 ##
-## * ``getNum``
-## * ``getFNum``
+## * ``getInt``
+## * ``getFloat``
 ## * ``getStr``
-## * ``getBVal``
+## * ``getBool``
 ##
 ## To retrieve the value of ``"key"`` you can do the following:
 ##
 ## .. code-block:: Nim
-##   doAssert jsonNode["key"].getFNum() == 3.14
+##   doAssert jsonNode["key"].getFloat() == 3.14
 ##
 ## The ``[]`` operator will raise an exception when the specified field does
 ## not exist. If you wish to avoid this behaviour you can use the ``{}``
@@ -681,14 +681,25 @@ proc getStr*(n: JsonNode, default: string = ""): string =
   if n.isNil or n.kind != JString: return default
   else: return n.str
 
-proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
+proc getInt*(n: JsonNode, default: int = 0): int =
   ## Retrieves the int value of a `JInt JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
   if n.isNil or n.kind != JInt: return default
+  else: return int(n.num)
+
+proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
+  ## Retrieves the BiggestInt value of a `JInt JsonNode`.
+  ##
+  ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
+  if n.isNil or n.kind != JInt: return default
   else: return n.num
 
-proc getFNum*(n: JsonNode, default: float = 0.0): float =
+proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
+  ## Deprecated - use getInt or getBiggestInt instead
+  getBiggestInt(n, default)
+
+proc getFloat*(n: JsonNode, default: float = 0.0): float =
   ## Retrieves the float value of a `JFloat JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil.
@@ -698,13 +709,21 @@ proc getFNum*(n: JsonNode, default: float = 0.0): float =
   of JInt: return float(n.num)
   else: return default
 
-proc getBVal*(n: JsonNode, default: bool = false): bool =
+proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
+  ## Deprecated - use getFloat instead
+  getFloat(n, default)
+
+proc getBool*(n: JsonNode, default: bool = false): bool =
   ## Retrieves the bool value of a `JBool JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil.
   if n.isNil or n.kind != JBool: return default
   else: return n.bval
 
+proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
+  ## Deprecated - use getBVal instead
+  getBool(n, default)
+
 proc getFields*(n: JsonNode,
     default = initOrderedTable[string, JsonNode](4)):
         OrderedTable[string, JsonNode] =
@@ -804,13 +823,13 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
   of nnkBracket: # array
     if x.len == 0: return newCall(bindSym"newJArray")
     result = newNimNode(nnkBracket)
-    for i in 0 .. <x.len:
+    for i in 0 ..< x.len:
       result.add(toJson(x[i]))
     result = newCall(bindSym"%", result)
   of nnkTableConstr: # object
     if x.len == 0: return newCall(bindSym"newJObject")
     result = newNimNode(nnkTableConstr)
-    for i in 0 .. <x.len:
+    for i in 0 ..< x.len:
       x[i].expectKind nnkExprColonExpr
       result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
     result = newCall(bindSym"%", result)
@@ -1284,7 +1303,7 @@ else:
     case getVarType(x)
     of JArray:
       result = newJArray()
-      for i in 0 .. <x.len:
+      for i in 0 ..< x.len:
         result.add(x[i].convertObject())
     of JObject:
       result = newJObject()
@@ -1342,7 +1361,7 @@ proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
     # TODO: I shouldn't need this proc.
     proc convert[T](x: BiggestInt): T = T(x)
     verifyJsonKind(node, {JInt}, ast)
-    return convert[T](node.getNum())
+    return convert[T](node.getBiggestInt())
   else:
     verifyJsonKind(node, {JString}, ast)
     return parseEnum[T](node.getStr())
@@ -1430,7 +1449,7 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
   # We need to build up a list of conditions from each ``of`` branch so that
   # we can then negate it to get ``else``.
   var cond = newIdentNode("false")
-  for i in 1 .. <len(recCaseNode):
+  for i in 1 ..< len(recCaseNode):
     if recCaseNode[i].kind == nnkElse:
       break
 
@@ -1492,7 +1511,7 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
     exprColonExpr.add(getEnumCall)
 
     # Iterate through each `of` branch.
-    for i in 1 .. <field.len:
+    for i in 1 ..< field.len:
       case field[i].kind
       of nnkOfBranch:
         result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
@@ -1621,7 +1640,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
         (
           var list: `typeSym` = @[];
           verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`);
+          for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
           list
         )
     of "array":
@@ -1635,7 +1654,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
         (
           var list: `typeSym`;
           verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`;
+          for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`;
           list
         )
 
@@ -1664,7 +1683,7 @@ proc postProcessValue(value: NimNode): NimNode =
     result = postProcess(value)
   else:
     result = value
-    for i in 0 .. <len(result):
+    for i in 0 ..< len(result):
       result[i] = postProcessValue(result[i])
 
 proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim
index cf2e8bb89..15a390f0b 100644
--- a/lib/pure/lexbase.nim
+++ b/lib/pure/lexbase.nim
@@ -37,6 +37,7 @@ type
     lineNumber*: int          ## the current line number
     sentinel: int
     lineStart: int            # index of last line start in buffer
+    offsetBase*: int          # use ``offsetBase + bufpos`` to get the offset
     refillChars: set[char]
 
 {.deprecated: [TBaseLexer: BaseLexer].}
@@ -107,7 +108,8 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int =
     result = pos + 1          # nothing to do
   else:
     fillBuffer(L)
-    L.bufpos = 0              # XXX: is this really correct?
+    L.offsetBase += pos
+    L.bufpos = 0
     result = 0
 
 proc handleCR*(L: var BaseLexer, pos: int): int =
@@ -147,6 +149,7 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
   assert(input != nil)
   L.input = input
   L.bufpos = 0
+  L.offsetBase = 0
   L.bufLen = bufLen
   L.refillChars = refillChars
   when defined(js):
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index c4c731acf..6ee830786 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -283,7 +283,7 @@ proc to*[T](data: string): T =
   loadAny(newStringStream(data), toAny(result), tab)
 
 when not defined(testing) and isMainModule:
-  template testit(x: expr) = echo($$to[type(x)]($$x))
+  template testit(x: untyped) = echo($$to[type(x)]($$x))
 
   var x: array[0..4, array[0..4, string]] = [
     ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
index 7022c21d9..6366fee1a 100644
--- a/lib/pure/matchers.nim
+++ b/lib/pure/matchers.nim
@@ -48,7 +48,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
      "aero", "jobs", "museum": return true
   else: return false
 
-proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
+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
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 8037b31b0..7fd8bbcef 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -184,6 +184,8 @@ when not defined(JS):
   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.
+    ##
+    ## To compute power between integers, use `^` e.g. 2 ^ 6
 
   proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
   proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 98fc62a3b..6c8701843 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -496,11 +496,12 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
                    addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    var buf: array[64, char]
+    result[0] = newString(64)
     if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
       raiseOSError(osLastError())
-    result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
+    setLen(result[0], result[0].cstring.len)
+    result[1] = Port(nativesockets.ntohs(name.sin6_port))
   else:
     raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
 
@@ -532,11 +533,12 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
                    addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    var buf: array[64, char]
+    result[0] = newString(64)
     if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
       raiseOSError(osLastError())
-    result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
+    setLen(result[0], result[0].cstring.len)
+    result[1] = Port(nativesockets.ntohs(name.sin6_port))
   else:
     raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
 
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 215a301b6..a405ce1bd 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -857,13 +857,16 @@ proc close*(socket: Socket) =
         # shutdown i.e not wait for the peers "close notify" alert with a second
         # call to SSLShutdown
         let res = SSLShutdown(socket.sslHandle)
-        SSLFree(socket.sslHandle)
-        socket.sslHandle = nil
         if res == 0:
           discard
         elif res != 1:
           socketError(socket, res)
   finally:
+    when defineSsl:
+      if socket.isSSL and socket.sslHandle != nil:
+        SSLFree(socket.sslHandle)
+        socket.sslHandle = nil
+
     socket.fd.close()
 
 proc toCInt*(opt: SOBool): cint =
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index ad63bbcb6..6d2869bff 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -153,7 +153,10 @@ proc `==`*(a, b: Option): bool =
   (a.has and b.has and a.val == b.val) or (not a.has and not b.has)
 
 proc `$`*[T](self: Option[T]): string =
-  ## Returns the contents of this option or `otherwise` if the option is none.
+  ## Get the string representation of this option. If the option has a value,
+  ## the result will be `Some(x)` where `x` is the string representation of the contained value.
+  ## If the option does not have a value, the result will be `None[T]` where `T` is the name of 
+  ## the type contained in the option.
   if self.has:
     "Some(" & $self.val & ")"
   else:
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index b85181edf..a1ae4e250 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -790,7 +790,10 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
         while true:
           var x = readdir(d)
           if x == nil: break
-          var y = $x.d_name
+          when defined(nimNoArrayToCstringConversion):
+            var y = $cstring(addr x.d_name)
+          else:
+            var y = $x.d_name.cstring
           if y != "." and y != "..":
             var s: Stat
             if not relative:
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 07429b9a9..71d3d9c72 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -280,7 +280,7 @@ proc execProcesses*(cmds: openArray[string],
   ## executes the commands `cmds` in parallel. Creates `n` processes
   ## that execute in parallel. The highest return value of all processes
   ## is returned. Runs `beforeRunEvent` before running each command.
-  when defined(posix):
+  when false:
     # poParentStreams causes problems on Posix, so we simply disable it:
     var options = options - {poParentStreams}
 
@@ -617,6 +617,7 @@ when defined(Windows) and not defined(useNimRtl):
     var res: int32
     discard getExitCodeProcess(p.fProcessHandle, res)
     result = res
+    p.exitStatus = res
     discard closeHandle(p.fProcessHandle)
 
   proc peekExitCode(p: Process): int =
@@ -625,6 +626,7 @@ when defined(Windows) and not defined(useNimRtl):
     else:
       var res: int32
       discard getExitCodeProcess(p.fProcessHandle, res)
+      if res == 0: return p.exitStatus
       return res
 
   proc inputStream(p: Process): Stream =
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index 77b145a73..ca0f3f9e0 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -72,7 +72,10 @@ proc raiseEInvalidCsv(filename: string, line, col: int,
                       msg: string) {.noreturn.} =
   var e: ref CsvError
   new(e)
-  e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
+  if filename.len == 0:
+    e.msg = "Error: " & msg
+  else:
+    e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
   raise e
 
 proc error(my: CsvParser, pos: int, msg: string) =
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
index 2e8dbe140..a2ff9bf0c 100644
--- a/lib/pure/parseopt2.nim
+++ b/lib/pure/parseopt2.nim
@@ -35,7 +35,7 @@ type
     cmd: seq[string]
     pos: int
     remainingShortOptions: string
-    kind*: CmdLineKind        ## the dected command line token
+    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
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index b78e8d000..3c790512f 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -8,6 +8,8 @@
 #
 
 ## This module contains helpers for parsing tokens, numbers, identifiers, etc.
+##
+## To unpack raw bytes look at the `streams <streams.html>`_ module.
 
 {.deadCodeElim: on.}
 
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 27fbfad45..e6a9162c5 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -93,7 +93,7 @@ proc random*(max: float): float {.benign.} =
     let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
     result = (cast[float](u) - 1.0) * max
 
-proc random*[T](x: Slice[T]): T =
+proc random*[T](x: HSlice[T, T]): T =
   ## For a slice `a .. b` returns a value in the range `a .. b-1`.
   result = T(random(x.b - x.a)) + x.a
 
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index c2ba2b1f3..7fb24c26f 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -39,47 +39,13 @@ proc toRational*[T:SomeInteger](x: T): Rational[T] =
   result.num = x
   result.den = 1
 
-proc toRationalSub(x: float, n: int): Rational[int] =
-  var
-    a = 0'i64
-    b, c, d = 1'i64
-  result = 0 // 1   # rational 0
-  while b <= n and d <= n:
-    let ac = (a+c)
-    let bd = (b+d)
-    # scale by 1000 so not overflow for high precision
-    let mediant = (ac.float/1000) / (bd.float/1000)
-    if x == mediant:
-      if bd <= n:
-        result.num = ac.int
-        result.den = bd.int
-        return result
-      elif d > b:
-        result.num = c.int
-        result.den = d.int
-        return result
-      else:
-        result.num = a.int
-        result.den = b.int
-        return result
-    elif x > mediant:
-      a = ac
-      b = bd
-    else:
-      c = ac
-      d = bd
-  if (b > n):
-    return initRational(c.int, d.int)
-  return initRational(a.int, b.int)
-
-proc toRational*(x: float, n: int = high(int)): Rational[int] =
-  ## Calculate the best rational numerator and denominator
+proc toRational*(x: float, n: int = high(int32)): Rational[int] =
+  ## Calculates the best rational numerator and denominator
   ## that approximates to `x`, where the denominator is
   ## smaller than `n` (default is the largest possible
-  ## int to give maximum resolution)
+  ## int to give maximum resolution).
   ##
-  ## The algorithm is based on the Farey sequence named
-  ## after John Farey
+  ## The algorithm is based on the theory of continued fractions.
   ##
   ## .. code-block:: Nim
   ##  import math, rationals
@@ -88,13 +54,24 @@ proc toRational*(x: float, n: int = high(int)): Rational[int] =
   ##    let x = toRational(PI, t)
   ##    let newPI = x.num / x.den
   ##    echo x, " ", newPI, " error: ", PI - newPI, "  ", t
-  if x > 1:
-    result = toRationalSub(1.0/x, n)
-    swap(result.num, result.den)
-  elif x == 1.0:
-    result = 1 // 1
-  else:
-    result = toRationalSub(x, n)
+
+  # David Eppstein / UC Irvine / 8 Aug 1993
+  # With corrections from Arno Formella, May 2008
+  var
+    m11, m22 = 1
+    m12, m21 = 0
+    ai = int(x)
+    x = x
+  while m21 * ai + m22 <= n:
+    swap m12, m11
+    swap m22, m21
+    m11 = m12 * ai + m11
+    m21 = m22 * ai + m21
+    if x == float(ai): break  # division by zero
+    x = 1/(x - float(ai))
+    if x > float(high(int32)): break  # representation failure
+    ai = int(x)
+  result = m11 // m21
 
 proc toFloat*[T](x: Rational[T]): float =
   ## Convert a rational number `x` to a float.
@@ -346,7 +323,14 @@ when isMainModule:
   assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7
   assert toInt(z) == 0
 
-  assert toRational(0.98765432) == 12345679 // 12500000
-  assert toRational(0.1, 1000000) == 1 // 10
-  assert toRational(0.9, 1000000) == 9 // 10
-  #assert toRational(PI) == 80143857 // 25510582
+  assert toRational(0.98765432) == 2111111029 // 2137499919
+  assert toRational(PI) == 817696623 // 260280919
+  assert toRational(0.1) == 1 // 10
+  assert toRational(0.9) == 9 // 10
+
+  assert toRational(0.0) == 0 // 1
+  assert toRational(-0.25) == 1 // -4
+  assert toRational(3.2) == 16 // 5
+  assert toRational(0.33) == 33 // 100
+  assert toRational(0.22) == 11 // 50
+  assert toRational(10.0) == 10 // 1
diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim
index c19146669..57c1f3631 100644
--- a/lib/pure/securehash.nim
+++ b/lib/pure/securehash.nim
@@ -181,7 +181,7 @@ proc `$`*(self: SecureHash): string =
     result.add(toHex(int(v), 2))
 
 proc parseSecureHash*(hash: string): SecureHash =
-  for i in 0.. <Sha1DigestSize:
+  for i in 0 ..< Sha1DigestSize:
     Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
 
 proc `==`*(a, b: SecureHash): bool =
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 506b2cec0..d17b6c253 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -162,7 +162,7 @@ elif defined(linux):
         return @[]
       raiseOSError(err)
     if evNum == 0: return @[]
-    for i in 0 .. <evNum:
+    for i in 0 ..< evNum:
       let fd = s.events[i].data.fd.SocketHandle
 
       var evSet: set[Event] = {}
@@ -253,7 +253,7 @@ elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
         return @[]
       raiseOSError(err)
     if evNum == 0: return @[]
-    for i in 0 .. <evNum:
+    for i in 0 ..< evNum:
       let fd = s.events[i].ident.SocketHandle
 
       var evSet: set[Event] = {}
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 69f673990..354e07da3 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -224,6 +224,38 @@ proc peekInt64*(s: Stream): int64 =
   ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
   peek(s, result)
 
+proc readUint8*(s: Stream): uint8 =
+  ## reads an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint8*(s: Stream): uint8 =
+  ## peeks an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint16*(s: Stream): uint16 =
+  ## reads an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint16*(s: Stream): uint16 =
+  ## peeks an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint32*(s: Stream): uint32 =
+  ## reads an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint32*(s: Stream): uint32 =
+  ## peeks an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint64*(s: Stream): uint64 =
+  ## reads an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint64*(s: Stream): uint64 =
+  ## peeks an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
 proc readFloat32*(s: Stream): float32 =
   ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index bf26d2e59..a54556915 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -253,7 +253,7 @@ is performed.
   for r in collectLinks(body):
     echo r
 
-In this example both macros are combined seamlessly in order to maximise 
+In this example both macros are combined seamlessly in order to maximise
 efficiency and perform different checks.
 
 .. code-block:: nim
@@ -308,7 +308,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
   ## See top level documentation of his module of how ``scanf`` works.
   template matchBind(parser) {.dirty.} =
     var resLen = genSym(nskLet, "resLen")
-    conds.add newLetStmt(resLen, newCall(bindSym(parser), input, results[i], idx))
+    conds.add newLetStmt(resLen, newCall(bindSym(parser), inp, results[i], idx))
     conds.add resLen.notZero
     conds.add resLen
 
@@ -316,7 +316,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
   var p = 0
   var idx = genSym(nskVar, "idx")
   var res = genSym(nskVar, "res")
-  result = newTree(nnkStmtListExpr, newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
+  let inp = genSym(nskLet, "inp")
+  result = newTree(nnkStmtListExpr, newLetStmt(inp, input), newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
   var conds = newTree(nnkStmtList)
   var fullMatch = false
   while p < pattern.len:
@@ -325,7 +326,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
       case pattern[p]
       of '$':
         var resLen = genSym(nskLet, "resLen")
-        conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit($pattern[p]), idx))
+        conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit($pattern[p]), idx))
         conds.add resLen.notZero
         conds.add resLen
       of 'w':
@@ -347,7 +348,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
           error("no float var given for $f")
         inc i
       of 's':
-        conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", input, idx))
+        conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx))
         conds.add newEmptyNode()
         conds.add newEmptyNode()
       of '.':
@@ -364,7 +365,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
             token.add pattern[q]
             inc q
           var resLen = genSym(nskLet, "resLen")
-          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", input, results[i], newLit(token), idx))
+          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx))
           conds.add newCall(bindSym"!=", resLen, newLit min)
           conds.add resLen
         else:
@@ -386,7 +387,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         let expr = pattern.substr(start, p-1)
         if i < results.len:
           var resLen = genSym(nskLet, "resLen")
-          conds.add newLetStmt(resLen, buildUserCall(expr, input, results[i], idx))
+          conds.add newLetStmt(resLen, buildUserCall(expr, inp, results[i], idx))
           conds.add newCall(bindSym"!=", resLen, newLit 0)
           conds.add resLen
         else:
@@ -406,7 +407,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
           else: discard
           inc p
         let expr = pattern.substr(start, p-1)
-        conds.add newCall(bindSym"inc", idx, buildUserCall(expr, input, idx))
+        conds.add newCall(bindSym"inc", idx, buildUserCall(expr, inp, idx))
         conds.add newEmptyNode()
         conds.add newEmptyNode()
       else: error("invalid format string")
@@ -417,13 +418,13 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         token.add pattern[p]
         inc p
       var resLen = genSym(nskLet, "resLen")
-      conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit(token), idx))
+      conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit(token), idx))
       conds.add resLen.notZero
       conds.add resLen
   result.add conditionsToIfChain(conds, idx, res, 0)
   if fullMatch:
     result.add newCall(bindSym"and", res,
-      newCall(bindSym">=", idx, newCall(bindSym"len", input)))
+      newCall(bindSym">=", idx, newCall(bindSym"len", inp)))
   else:
     result.add res
 
@@ -684,3 +685,14 @@ when isMainModule:
           "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
           "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
   doAssert parseGDB(gdbOut) == result
+
+  # bug #6487
+  var count = 0
+
+  proc test(): string =
+    inc count
+    result = ",123123"
+
+  var a: int
+  discard scanf(test(), ",$i", a)
+  doAssert count == 1
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 8b4e6bd05..2b87e0d43 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -138,7 +138,8 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isAlphaNumeric() and result
+    if not c.isAlphaNumeric():
+      return false
 
 proc isDigit*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsDigitStr".}=
@@ -153,7 +154,8 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isDigit() and result
+    if not c.isDigit():
+      return false
 
 proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsSpaceAsciiStr".}=
@@ -1062,8 +1064,8 @@ 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 `repeatChar
-  ## proc <#repeatChar>`_. Example:
+  ## returned unchanged. If you need to left align a string use the `alignLeft
+  ## proc <#alignLeft>`_. Example:
   ##
   ## .. code-block:: nim
   ##   assert align("abc", 4) == " abc"
@@ -1078,6 +1080,28 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   else:
     result = s
 
+proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect.} =
+  ## Left-Aligns a string `s` with `padding`, so that it is of length `count`.
+  ##
+  ## `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:
+  ##
+  ## .. code-block:: nim
+  ##   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:
+      result[0 .. (s.len - 1)] = s
+    for i in s.len ..< count:
+      result[i] = padding
+  else:
+    result = s
+
 iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
   token: string, isSep: bool] =
   ## Tokenizes the string `s` into substrings.
@@ -1175,7 +1199,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
     var indentCount = 0
     for j in 0..<count.int:
       indentCount.inc
-      if line[j .. j + <padding.len] != padding:
+      if line[j .. j + padding.len-1] != padding:
         indentCount = j
         break
     result.add(line[indentCount*padding.len .. ^1])
@@ -1306,18 +1330,36 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
     add(result, $x)
 
 type
-  SkipTable = array[char, int]
-
-{.push profiler: off.}
-proc preprocessSub(sub: string, a: var SkipTable) =
-  var m = len(sub)
-  for i in 0..0xff: a[chr(i)] = m+1
-  for i in 0..m-1: a[sub[i]] = m-i
-{.pop.}
+  SkipTable* = array[char, int]
 
-proc findAux(s, sub: string, start, last: int, a: SkipTable): int =
-  # Fast "quick search" algorithm:
-  var
+proc initSkipTable*(a: var SkipTable, sub: string)
+  {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
+  ## Preprocess table `a` for `sub`.
+  let m = len(sub)
+  let m1 = m + 1
+  var i = 0
+  while i <= 0xff-7:
+    a[chr(i + 0)] = m1
+    a[chr(i + 1)] = m1
+    a[chr(i + 2)] = m1
+    a[chr(i + 3)] = m1
+    a[chr(i + 4)] = m1
+    a[chr(i + 5)] = m1
+    a[chr(i + 6)] = m1
+    a[chr(i + 7)] = m1
+    i += 8
+
+  for i in 0..m-1:
+    a[sub[i]] = m-i
+
+proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 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`.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  let
+    last = if last==0: s.high else: last
     m = len(sub)
     n = last + 1
   # search:
@@ -1337,17 +1379,6 @@ when not (defined(js) or defined(nimdoc) or defined(nimscript)):
 else:
   const hasCStringBuiltin = false
 
-proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindStr".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
-  ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  var a {.noinit.}: SkipTable
-  let last = if last==0: s.high else: last
-  preprocessSub(sub, a)
-  result = findAux(s, sub, start, last, a)
-
 proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
   rtl, extern: "nsuFindChar".} =
   ## Searches for `sub` in `s` inside range `start`..`last`.
@@ -1366,9 +1397,24 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
     else:
       for i in start..last:
         if sub == s[i]: return i
-
   return -1
 
+proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
+  rtl, extern: "nsuFindStr".} =
+  ## Searches for `sub` in `s` inside range `start`..`last`.
+  ## If `last` is unspecified, it defaults to `s.high`.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  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: Natural = 0): int {.noSideEffect,
   rtl, extern: "nsuFindCharSet".} =
   ## Searches for `chars` in `s` inside range `start`..`last`.
@@ -1500,11 +1546,11 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   ## Replaces `sub` in `s` by the string `by`.
   var a {.noinit.}: SkipTable
   result = ""
-  preprocessSub(sub, a)
+  initSkipTable(a, sub)
   let last = s.high
   var i = 0
   while true:
-    var j = findAux(s, sub, i, last, a)
+    var j = find(a, s, sub, i, last)
     if j < 0: break
     add result, substr(s, i, j - 1)
     add result, by
@@ -1534,11 +1580,11 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
   var a {.noinit.}: SkipTable
   result = ""
-  preprocessSub(sub, a)
+  initSkipTable(a, sub)
   var i = 0
   let last = s.high
   while true:
-    var j = findAux(s, sub, i, last, a)
+    var j = find(a, s, sub, i, last)
     if j < 0: break
     # word boundary?
     if (j == 0 or s[j-1] notin wordChars) and
@@ -1890,17 +1936,32 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
       frmtstr[3] = '*'
       frmtstr[4] = floatFormatToChar[format]
       frmtstr[5] = '\0'
-      L = c_sprintf(buf, frmtstr, precision, f)
+      when defined(nimNoArrayToCstringConversion):
+        L = c_sprintf(addr buf, addr frmtstr, precision, f)
+      else:
+        L = c_sprintf(buf, frmtstr, precision, f)
     else:
       frmtstr[1] = floatFormatToChar[format]
       frmtstr[2] = '\0'
-      L = c_sprintf(buf, frmtstr, f)
+      when defined(nimNoArrayToCstringConversion):
+        L = c_sprintf(addr buf, addr frmtstr, f)
+      else:
+        L = c_sprintf(buf, frmtstr, f)
     result = newString(L)
     for i in 0 ..< L:
       # Depending on the locale either dot or comma is produced,
       # but nothing else is possible:
       if buf[i] in {'.', ','}: result[i] = decimalsep
       else: result[i] = buf[i]
+    when defined(vcc):
+      # VS pre 2015 violates the C standard: "The exponent always contains at
+      # least two digits, and only as many more digits as necessary to
+      # represent the exponent." [C11 ยง7.21.6.1]
+      # The following post-processing fixes this behavior.
+      if result.len > 4 and result[^4] == '+' and result[^3] == '0':
+        result[^3] = result[^2]
+        result[^2] = result[^1]
+        result.setLen(result.len - 1)
 
 proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
                   precision: range[0..32] = 16; decimalSep = '.'): string {.
@@ -2169,11 +2230,26 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
         if idx >% a.high: invalidFormatString()
         add s, a[idx]
       of '{':
-        var j = i+1
-        while formatstr[j] notin {'\0', '}'}: inc(j)
-        var x = findNormalized(substr(formatstr, i+2, j-1), a)
-        if x >= 0 and x < high(a): add s, a[x+1]
-        else: invalidFormatString()
+        var j = i+2
+        var k = 0
+        var negative = formatstr[j] == '-'
+        if negative: inc j
+        var isNumber = 0
+        while formatstr[j] notin {'\0', '}'}:
+          if formatstr[j] in Digits:
+            k = k * 10 + ord(formatstr[j]) - ord('0')
+            if isNumber == 0: isNumber = 1
+          else:
+            isNumber = -1
+          inc(j)
+        if isNumber == 1:
+          let idx = if not negative: k-1 else: a.len-k
+          if idx >% a.high: invalidFormatString()
+          add s, a[idx]
+        else:
+          var x = findNormalized(substr(formatstr, i+2, j-1), a)
+          if x >= 0 and x < high(a): add s, a[x+1]
+          else: invalidFormatString()
         i = j+1
       of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
         var j = i+1
@@ -2250,60 +2326,104 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect,
 
 proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
   rtl, extern: "nsuRemoveSuffixCharSet".} =
-  ## Removes the first matching character from the string (in-place) given a
-  ## set of characters. If the set of characters is only equal to `Newlines`
-  ## then it will remove both the newline and return feed.
+  ## Removes all characters from `chars` from the end of the string `s`
+  ## (in-place).
+  ##
   ## .. code-block:: nim
-  ##   var
-  ##     userInput = "Hello World!\r\n"
-  ##     otherInput = "Hello!?!"
+  ##   var userInput = "Hello World!*~\r\n"
   ##   userInput.removeSuffix
-  ##   userInput == "Hello World!"
-  ##   userInput.removeSuffix({'!', '?'})
-  ##   userInput == "Hello World"
+  ##   doAssert userInput == "Hello World!*~"
+  ##   userInput.removeSuffix({'~', '*'})
+  ##   doAssert userInput == "Hello World!"
+  ##
+  ##   var otherInput = "Hello!?!"
   ##   otherInput.removeSuffix({'!', '?'})
-  ##   otherInput == "Hello!?"
+  ##   doAssert otherInput == "Hello"
   if s.len == 0: return
-  var last = len(s) - 1
-  if chars == Newlines:
-    if s[last] == '\10':
-      last -= 1
-    if s[last] == '\13':
-      last -= 1
-  else:
-    if s[last] in chars:
-      last -= 1
+  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 a single character (in-place) from a string.
+  ## Removes all occurrences of a single character (in-place) from the end
+  ## of a string.
+  ##
   ## .. code-block:: nim
-  ##   var
-  ##     table = "users"
+  ##   var table = "users"
   ##   table.removeSuffix('s')
-  ##   table == "user"
+  ##   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.
+  ##
   ## .. code-block:: nim
-  ##   var
-  ##     answers = "yeses"
+  ##   var answers = "yeses"
   ##   answers.removeSuffix("es")
-  ##   answers == "yes"
+  ##   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).
+  ##
+  ## .. code-block:: nim
+  ##   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.
+  ##
+  ## .. code-block:: nim
+  ##   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.
+  ##
+  ## .. code-block:: nim
+  ##   var answers = "yesyes"
+  ##   answers.removePrefix("yes")
+  ##   doAssert answers == "yes"
+  if s.startsWith(prefix):
+    s.delete(0, prefix.len - 1)
+
 when isMainModule:
   doAssert align("abc", 4) == " abc"
   doAssert align("a", 0) == "a"
   doAssert align("1232", 6) == "  1232"
   doAssert align("1232", 6, '#') == "##1232"
 
+  doAssert alignLeft("abc", 4) == "abc "
+  doAssert alignLeft("a", 0) == "a"
+  doAssert alignLeft("1232", 6) == "1232  "
+  doAssert alignLeft("1232", 6, '#') == "1232##"
+
   let
     inp = """ this is a long text --  muchlongerthan10chars and here
                it goes"""
@@ -2313,8 +2433,12 @@ when isMainModule:
   doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
   doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
                                                    ["1,0e-11", "1,0e-011"]
+  # bug #6589
+  doAssert formatFloat(123.456, ffScientific, precision=0) in
+      ["1.234560e+02", "1.234560e+002"]
 
   doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+  doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
 
   block: # formatSize tests
     doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 68a457084..7dd428904 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -77,20 +77,17 @@ when defined(posix) and not defined(JS):
 
   when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
     var timezone {.importc, header: "<time.h>".}: int
+    proc tzset(): void {.importc, header: "<time.h>".}
+    tzset()
 
 elif defined(windows):
   import winlean
 
-  when defined(vcc) or defined(bcc) or defined(icl):
-    # newest version of Visual C++ defines time_t to be of 64 bits
-    type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
-    # visual c's c runtime exposes these under a different name
-    var
-      timezone {.importc: "_timezone", header: "<time.h>".}: int
-  else:
-    type TimeImpl {.importc: "time_t", header: "<time.h>".} = int
-    var
-      timezone {.importc, header: "<time.h>".}: int
+  # newest version of Visual C++ defines time_t to be of 64 bits
+  type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
+  # visual c's c runtime exposes these under a different name
+  var
+    timezone {.importc: "_timezone", header: "<time.h>".}: int
 
   type
     Time* = distinct TimeImpl
@@ -1028,7 +1025,7 @@ proc countLeapYears*(yearSpan: int): int =
   ## 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.
-  (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1) / 400)).int
+  (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400
 
 proc countDays*(yearSpan: int): int =
   ## Returns the number of days spanned by a given number of years.
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 55c4bf038..3b6f7de1a 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -31,6 +31,10 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
   ##   test(@['A','B'])
   ##   # --> type: seq[char], value: @[A, B]
 
+proc `$`*(t: typedesc): string =
+  ## An alias for `name`.
+  name(t)
+
 proc arity*(t: typedesc): int {.magic: "TypeTrait".}
   ## Returns the arity of the given type
 
@@ -49,3 +53,15 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
   ## This trait is similar to `genericHead`, but instead of producing
   ## error for non-generic types, it will just return them unmodified
 
+proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
+  ## This trait returns true iff the type ``t`` is safe to use for
+  ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:.
+
+
+when isMainModule:
+  # echo type(42)
+  import streams
+  var ss = newStringStream()
+  ss.write($type(42)) # needs `$`
+  ss.setPosition(0)
+  doAssert ss.readAll() == "int"
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 0c4f15c91..7d9c3108b 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -285,7 +285,7 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, 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 negativ they count from
+  ## 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.
   ##
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 3772a213a..7a8d1dad0 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -312,7 +312,7 @@ proc ensureInitialized() =
 
   if not testsToRun.isValid:
     testsToRun.init()
-    when declared(os):
+    when declared(paramCount):
       # Read tests to run from the command line.
       for i in 1 .. paramCount():
         testsToRun.incl(paramStr(i))
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d8e4ed52f..c702b054c 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -250,7 +250,7 @@ proc combine*(base: Uri, reference: Uri): Uri =
 proc combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
   result = uris[0]
-  for i in 1 .. <uris.len:
+  for i in 1 ..< uris.len:
     result = combine(result, uris[i])
 
 proc isAbsolute*(uri: Uri): bool =
@@ -278,7 +278,9 @@ proc `/`*(x: Uri, path: string): Uri =
   result = x
 
   if result.path.len == 0:
-    result.path = path
+    if path[0] != '/':
+      result.path = "/"
+    result.path.add(path)
     return
 
   if result.path[result.path.len-1] == '/':
@@ -476,6 +478,11 @@ when isMainModule:
     let foo = parseUri("http://example.com") / "/baz"
     doAssert foo.path == "/baz"
 
+  # bug found on stream 13/10/17
+  block:
+    let foo = parseUri("http://localhost:9515") / "status"
+    doAssert $foo == "http://localhost:9515/status"
+
   # isAbsolute tests
   block:
     doAssert "www.google.com".parseUri().isAbsolute() == false
@@ -515,4 +522,6 @@ when isMainModule:
     doAssert "https://example.com/about".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
-    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
\ No newline at end of file
+    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
+
+  echo("All good!")
\ No newline at end of file