diff --git a/changelog.md b/changelog.md index eb3f123096a7..e17dff5ebccb 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,11 @@ rounding guarantees (via the ## Standard library additions and changes +[//]: # "Additions:" +- `setutils.symmetricDifference` along with its operator version + `` setutils.`-+-` `` and in-place version `setutils.toggle` have been added + to more efficiently calculate the symmetric difference of bitsets. + [//]: # "Changes:" - `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type. diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5043fc5d4c45..b071a649abf4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -169,3 +169,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasGenericsOpenSym2") defineSymbol("nimHasGenericsOpenSym3") defineSymbol("nimHasJsNoLambdaLifting") + defineSymbol("nimHasXorSet") diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index 8e7bc6a92d30..32a2e88f2cd6 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -75,3 +75,31 @@ func `[]=`*[T](t: var set[T], key: T, val: bool) {.inline.} = s[a3] = true assert s == {a2, a3} if val: t.incl key else: t.excl key + +when defined(nimHasXorSet): + func symmetricDifference*[T](x, y: set[T]): set[T] {.magic: "XorSet".} = + ## This operator computes the symmetric difference of two sets, + ## equivalent to but more efficient than `x + y - x * y` or + ## `(x - y) + (y - x)`. + runnableExamples: + assert symmetricDifference({1, 2, 3}, {2, 3, 4}) == {1, 4} +else: + func symmetricDifference*[T](x, y: set[T]): set[T] {.inline.} = + result = x + y - (x * y) + +proc `-+-`*[T](x, y: set[T]): set[T] {.inline.} = + ## Operator alias for `symmetricDifference`. + runnableExamples: + assert {1, 2, 3} -+- {2, 3, 4} == {1, 4} + result = symmetricDifference(x, y) + +proc toggle*[T](x: var set[T], y: set[T]) {.inline.} = + ## Toggles the existence of each value of `y` in `x`. + ## If any element in `y` is also in `x`, it is excluded from `x`; + ## otherwise it is included. + ## Equivalent to `x = symmetricDifference(x, y)`. + runnableExamples: + var x = {1, 2, 3} + x.toggle({2, 3, 4}) + assert x == {1, 4} + x = symmetricDifference(x, y) diff --git a/lib/system/setops.nim b/lib/system/setops.nim index cd3e7e9d6a72..67aa3097ab10 100644 --- a/lib/system/setops.nim +++ b/lib/system/setops.nim @@ -67,12 +67,6 @@ func `-`*[T](x, y: set[T]): set[T] {.magic: "MinusSet".} = runnableExamples: assert {1, 2, 3} - {2, 3, 4} == {1} -func symmetricDifference*[T](x, y: set[T]): set[T] {.magic: "XorSet".} = - ## This operator computes the symmetric difference of two sets, - ## equivalent to `x + y - x * y` or `(x - y) + (y - x)`. - runnableExamples: - assert symmetricDifference({1, 2, 3}, {2, 3, 4}) == {1, 4} - func contains*[T](x: set[T], y: T): bool {.magic: "InSet".} = ## One should overload this proc if one wants to overload the `in` operator. ## diff --git a/tests/sets/tsets_various.nim b/tests/sets/tsets_various.nim index 27635e20f22f..419bcfdcc139 100644 --- a/tests/sets/tsets_various.nim +++ b/tests/sets/tsets_various.nim @@ -281,28 +281,6 @@ template main() = let foo = proc() = discard setA.incl(foo) doAssert setA.contains(foo) - - block: # set symmetric difference (xor), https://github.com/nim-lang/RFCs/issues/554 - type T = set[range[0..15]] - let x: T = {1, 4, 5, 8, 9} - let y: T = {0, 2..6, 9} - let res = symmetricDifference(x, y) - doAssert res == {0, 1, 2, 3, 6, 8} - doAssert res == (x + y - x * y) - doAssert res == ((x - y) + (y - x)) - template toggle[T](a: var set[T], b: set[T]) = - a = symmetricDifference(a, b) - var z = x - doAssert z == {1, 4, 5, 8, 9} - doAssert z == x - z.toggle(y) - doAssert z == res - z.toggle(y) - doAssert z == x - z.toggle({1, 5}) - doAssert z == {4, 8, 9} - z.toggle({3, 8}) - doAssert z == {3, 4, 9} static: main() main() diff --git a/tests/stdlib/tsetutils.nim b/tests/stdlib/tsetutils.nim index c8498f23e028..c3338a3a2729 100644 --- a/tests/stdlib/tsetutils.nim +++ b/tests/stdlib/tsetutils.nim @@ -44,6 +44,26 @@ template main = s[a2] = true s[a3] = true doAssert s == {a2, a3} + + block: # set symmetric difference (xor), https://github.com/nim-lang/RFCs/issues/554 + type T = set[range[0..15]] + let x: T = {1, 4, 5, 8, 9} + let y: T = {0, 2..6, 9} + let res = symmetricDifference(x, y) + doAssert res == {0, 1, 2, 3, 6, 8} + doAssert res == (x + y - x * y) + doAssert res == ((x - y) + (y - x)) + var z = x + doAssert z == {1, 4, 5, 8, 9} + doAssert z == x + z.toggle(y) + doAssert z == res + z.toggle(y) + doAssert z == x + z.toggle({1, 5}) + doAssert z == {4, 8, 9} + z.toggle({3, 8}) + doAssert z == {3, 4, 9} main() static: main()