Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add algorithm.merge #16182

Merged
merged 18 commits into from
Mar 23, 2021
6 changes: 5 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@

- `writeStackTrace` is available in JS backend now.



ringabout marked this conversation as resolved.
Show resolved Hide resolved
- Added `decodeQuery` to `std/uri`.
- `strscans.scanf` now supports parsing single characters.
- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`.
Expand Down Expand Up @@ -122,7 +124,6 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.

- Added `math.signbit`.


- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably)
- In `lists`: renamed `append` to `add` and retained `append` as an alias;
added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
Expand Down Expand Up @@ -180,6 +181,9 @@ provided by the operating system.

- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
and `$none(int)` to `"none(int)"` instead of `"None[int]"`.

- Added `algorithm.merge`.


## Language changes

Expand Down
81 changes: 81 additions & 0 deletions lib/pure/algorithm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ runnableExamples:
## * `sequtils module<sequtils.html>`_ for working with the built-in seq type
## * `tables module<tables.html>`_ for sorting tables

import std/private/since
ringabout marked this conversation as resolved.
Show resolved Hide resolved

type
SortOrder* = enum
Descending, Ascending
Expand Down Expand Up @@ -559,6 +561,85 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool =
assert isSorted(e) == false
isSorted(a, system.cmp[T], order)

proc merge*[T](
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
result: var seq[T],
x, y: openArray[T], cmp: proc(x, y: T): int {.closure.}
ringabout marked this conversation as resolved.
Show resolved Hide resolved
) {.since: (1, 5, 1).} =
## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted.
ringabout marked this conversation as resolved.
Show resolved Hide resolved
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## If you do not wish to provide your own `cmp`,
## you may use `system.cmp` or instead call the overloaded
## version of `merge`, which uses `system.cmp`.
##
## **See also:**
## * `merge proc<#merge,seq[T],openArray[T],openArray[T]>`_
runnableExamples:
let x = @[1, 3, 6]
let y = @[2, 3, 4]

var merged = @[7] # just to illustrate that its content will be discarded
ringabout marked this conversation as resolved.
Show resolved Hide resolved
merged.merge(x, y, system.cmp[int])
assert merged.isSorted
assert merged == @[1, 2, 3, 3, 4, 6]

import sugar

var res: seq[(int, int)]
res.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0])
ringabout marked this conversation as resolved.
Show resolved Hide resolved
assert res == @[(1, 1), (1, 2)]

let
sizeX = x.len
sizeY = y.len

result.setLen(sizeX + sizeY)

var
ix = 0
iy = 0
i = 0

ringabout marked this conversation as resolved.
Show resolved Hide resolved
while true:
if ix == sizeX:
while iy < sizeY:
ringabout marked this conversation as resolved.
Show resolved Hide resolved
result[i] = y[iy]
inc i
inc iy
return

if iy == sizeY:
while ix < sizeX:
result[i] = x[ix]
inc i
inc ix
return

let itemX = x[ix]
let itemY = y[iy]

if cmp(itemX, itemY) > 0: # to have a stable sort
result[i] = itemY
inc iy
else:
result[i] = itemX
inc ix

inc i

proc merge*[T](result: var seq[T], x, y: openArray[T]) {.inline, since: (1, 5, 1).} =
## Shortcut version of `merge` that uses `system.cmp[T]` as the comparison function.
##
## **See also:**
## * `merge proc<#merge,seq[T],openArray[T],openArray[T],proc(T,T)>`_
runnableExamples:
let x = [5,10,15,20,25]
let y = [50,40,30,20,10].sorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved

var merged: seq[int]
merged.merge(x, y)
assert merged.isSorted
assert merged == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50]
merge(result, x, y, system.cmp)

proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## Produces the Cartesian product of the array.
## Every element of the result is a combination of one element from each seq in `x`,
Expand Down
155 changes: 154 additions & 1 deletion tests/stdlib/talgorithm.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
discard """
targets: "c js"
output:'''@["3", "2", "1"]
'''
"""
#12928,10456
import sequtils, algorithm, json
import std/[sequtils, algorithm, json, sugar]

proc test() =
try:
Expand Down Expand Up @@ -113,3 +114,155 @@ block:
doAssert binarySearch(moreData, 6) == -1
doAssert binarySearch(moreData, 4711) == 4
doAssert binarySearch(moreData, 4712) == -1

# merge
ringabout marked this conversation as resolved.
Show resolved Hide resolved
proc main() =
block:
var x = @[1, 7, 8, 11, 21, 33, 45, 99]
var y = @[6, 7, 9, 12, 57, 66]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged == sorted(x & y)

block:
var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3]
var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13]

var merged: seq[int]
merged.merge(x, y, (x, y) => -system.cmp(x, y))
doAssert merged.isSorted((x, y) => -system.cmp(x, y))
doAssert merged == sorted(x & y, SortOrder.Descending)

block:
var x: seq[int] = @[]
var y = @[1]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged.isSorted(SortOrder.Descending)
doAssert merged == @[1]

block:
var x = [1, 3, 5, 5, 7]
var y: seq[int] = @[]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged == @x

block:
var x = [1, 3, 5, 5, 7]
var y: seq[int] = @[]

var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged == @x


block:
var x: array[0, int]
var y = [1, 4, 6, 7, 9]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged == @y

block:
var x: array[0, int]
var y: array[0, int]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged.len == 0

block:
var x: array[0, int]
var y: array[0, int]

var merged: seq[int] = @[99, 99, 99]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged.len == 0

block:
var x: seq[int]
var y: seq[int]

var merged: seq[int]
merged.merge(x, y)
doAssert merged.isSorted
doAssert merged.len == 0

block:
type
Record = object
id: int

proc r(id: int): Record =
Record(id: id)

proc cmp(x, y: Record): int =
if x.id == y.id: return 0
if x.id < y.id: return -1
result = 1

var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)]
var y = @[r(4), r(7), r(12), r(13), r(77), r(99)]

var merged: seq[Record] = @[]
merged.merge(x, y, cmp)
doAssert merged.isSorted(cmp)
doAssert merged.len == 12

block:
type
Record = object
id: int

proc r(id: int): Record =
Record(id: id)

proc ascendingCmp(x, y: Record): int =
if x.id == y.id: return 0
if x.id < y.id: return -1
result = 1

proc descendingCmp(x, y: Record): int =
if x.id == y.id: return 0
if x.id < y.id: return 1
result = -1

var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)]
var y = @[r(4), r(7), r(12), r(13), r(77), r(99)]

var merged: seq[Record]
merged.merge(x, y, ascendingCmp)
doAssert merged.isSorted(ascendingCmp)
doAssert merged == sorted(x & y, ascendingCmp)

reverse(x)
reverse(y)
merged.merge(x, y, descendingCmp)
doAssert merged.isSorted(descendingCmp)
doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending)

reverse(x)
reverse(y)
merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y))
doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y))
doAssert merged == sorted(x & y, ascendingCmp)


var x: seq[(int, int)]
x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0])
doAssert x == @[(1, 1), (1, 2)]

static: main()
main()