summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDmitry Atamanov <data-man@users.noreply.github.com>2018-05-29 07:28:15 +0300
committerGitHub <noreply@github.com>2018-05-29 07:28:15 +0300
commita075a912cfaba685ad1ba9e41d046265fdf104b8 (patch)
tree21843fae9e0b2dafc96c5333aa5b90f27f0fe27c
parent5866e64ebcc4d6a80fb3403b6a38317fe5a52e74 (diff)
downloadNim-a075a912cfaba685ad1ba9e41d046265fdf104b8.tar.gz
Add algorithm.upperBound (#7851)
* Add algorithm.upperBound
* Docs updated
-rw-r--r--changelog.md1
-rw-r--r--lib/pure/algorithm.nim43
2 files changed, 40 insertions, 4 deletions
diff --git a/changelog.md b/changelog.md
index 3482c9eba..27090a1df 100644
--- a/changelog.md
+++ b/changelog.md
@@ -57,6 +57,7 @@
 - Added the proc ``times.convert`` for converting between different time units,
   e.g days to seconds.
 - Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
+- Added the proc ``algorithm.upperBound``.
 
 ### Library changes
 
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 98330b680..81badfae6 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -122,14 +122,14 @@ const
   onlySafeCode = true
 
 proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
-  ## same as binarySearch except that if key is not in `a` then this
-  ## returns the location where `key` would be if it were. In other
-  ## words if you have a sorted sequence and you call
+  ## Returns a position to the first element in the `a` that is greater than `key`, or last
+  ## if no such element is found. In other words if you have a sorted sequence and you call
   ## insert(thing, elm, lowerBound(thing, elm))
   ## the sequence will still be sorted.
   ##
-  ## `cmp` is the comparator function to use, the expected return values are
+  ## The first version uses `cmp` to compare the elements. The expected return values are
   ## the same as that of system.cmp.
+  ## The second version uses the default comparison function `cmp`.
   ##
   ## example::
   ##
@@ -150,6 +150,36 @@ proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo
 
 proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T])
 
+proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
+  ## Returns a position to the first element in the `a` that is not less
+  ## (i.e. greater or equal to) than `key`, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## insert(thing, elm, upperBound(thing, elm))
+  ## the sequence will still be sorted.
+  ##
+  ## The first version uses `cmp` to compare the elements. The expected return values are
+  ## the same as that of system.cmp.
+  ## The second version uses the default comparison function `cmp`.
+  ##
+  ## example::
+  ##
+  ##   var arr = @[1,2,3,4,6,7,8,9]
+  ##   arr.insert(5, arr.upperBound(4))
+  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
+  result = a.low
+  var count = a.high - a.low + 1
+  var step, pos: int
+  while count != 0:
+    step = count shr 1
+    pos = result + step
+    if cmp(a[pos], key) <= 0:
+      result = pos + 1
+      count -= step + 1
+    else:
+      count = step
+
+proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T])
+
 template `<-` (a, b) =
   when false:
     a = b
@@ -546,6 +576,11 @@ when isMainModule:
     doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4
     doAssert lowerBound([1,2,3,10], 11) == 4
 
+  block upperBound:
+    doAssert upperBound([1,2,4], 3, system.cmp[int]) == 2
+    doAssert upperBound([1,2,2,3], 3, system.cmp[int]) == 4
+    doAssert upperBound([1,2,3,5], 3) == 3
+
   block fillEmptySeq:
     var s = newSeq[int]()
     s.fill(0)