intsets.nim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## The ``intsets`` module implements an efficient `int` set implemented as a
  10. ## `sparse bit set`:idx:.
  11. ##
  12. ## **Note**: Currently the assignment operator ``=`` for ``IntSet``
  13. ## performs some rather meaningless shallow copy. Since Nim currently does
  14. ## not allow the assignment operator to be overloaded, use `assign proc
  15. ## <#assign,IntSet,IntSet>`_ to get a deep copy.
  16. ##
  17. ## **See also:**
  18. ## * `sets module <sets.html>`_ for more general hash sets
  19. import
  20. hashes
  21. type
  22. BitScalar = uint
  23. const
  24. InitIntSetSize = 8 # must be a power of two!
  25. TrunkShift = 9
  26. BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
  27. # divisible by 64
  28. TrunkMask = BitsPerTrunk - 1
  29. IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
  30. IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
  31. IntMask = 1 shl IntShift - 1
  32. type
  33. PTrunk = ref Trunk
  34. Trunk = object
  35. next: PTrunk # all nodes are connected with this pointer
  36. key: int # start address at bit 0
  37. bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
  38. TrunkSeq = seq[PTrunk]
  39. IntSet* = object ## An efficient set of `int` implemented as a sparse bit set.
  40. elems: int # only valid for small numbers
  41. counter, max: int
  42. head: PTrunk
  43. data: TrunkSeq
  44. a: array[0..33, int] # profiling shows that 34 elements are enough
  45. proc mustRehash(length, counter: int): bool {.inline.} =
  46. assert(length > counter)
  47. result = (length * 2 < counter * 3) or (length - counter < 4)
  48. proc nextTry(h, maxHash: Hash): Hash {.inline.} =
  49. result = ((5 * h) + 1) and maxHash
  50. proc intSetGet(t: IntSet, key: int): PTrunk =
  51. var h = key and t.max
  52. while t.data[h] != nil:
  53. if t.data[h].key == key:
  54. return t.data[h]
  55. h = nextTry(h, t.max)
  56. result = nil
  57. proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
  58. var h = desc.key and t.max
  59. while data[h] != nil:
  60. assert(data[h] != desc)
  61. h = nextTry(h, t.max)
  62. assert(data[h] == nil)
  63. data[h] = desc
  64. proc intSetEnlarge(t: var IntSet) =
  65. var n: TrunkSeq
  66. var oldMax = t.max
  67. t.max = ((t.max + 1) * 2) - 1
  68. newSeq(n, t.max + 1)
  69. for i in countup(0, oldMax):
  70. if t.data[i] != nil: intSetRawInsert(t, n, t.data[i])
  71. swap(t.data, n)
  72. proc intSetPut(t: var IntSet, key: int): PTrunk =
  73. var h = key and t.max
  74. while t.data[h] != nil:
  75. if t.data[h].key == key:
  76. return t.data[h]
  77. h = nextTry(h, t.max)
  78. if mustRehash(t.max + 1, t.counter): intSetEnlarge(t)
  79. inc(t.counter)
  80. h = key and t.max
  81. while t.data[h] != nil: h = nextTry(h, t.max)
  82. assert(t.data[h] == nil)
  83. new(result)
  84. result.next = t.head
  85. result.key = key
  86. t.head = result
  87. t.data[h] = result
  88. proc bitincl(s: var IntSet, key: int) {.inline.} =
  89. var t = intSetPut(s, `shr`(key, TrunkShift))
  90. var u = key and TrunkMask
  91. t.bits[u shr IntShift] = t.bits[u shr IntShift] or
  92. (BitScalar(1) shl (u and IntMask))
  93. proc exclImpl(s: var IntSet, key: int) =
  94. if s.elems <= s.a.len:
  95. for i in 0..<s.elems:
  96. if s.a[i] == key:
  97. s.a[i] = s.a[s.elems-1]
  98. dec s.elems
  99. return
  100. else:
  101. var t = intSetGet(s, key shr TrunkShift)
  102. if t != nil:
  103. var u = key and TrunkMask
  104. t.bits[u shr IntShift] = t.bits[u shr IntShift] and
  105. not(BitScalar(1) shl (u and IntMask))
  106. template dollarImpl(): untyped =
  107. result = "{"
  108. for key in items(s):
  109. if result.len > 1: result.add(", ")
  110. result.add($key)
  111. result.add("}")
  112. iterator items*(s: IntSet): int {.inline.} =
  113. ## Iterates over any included element of `s`.
  114. if s.elems <= s.a.len:
  115. for i in 0..<s.elems:
  116. yield s.a[i]
  117. else:
  118. var r = s.head
  119. while r != nil:
  120. var i = 0
  121. while i <= high(r.bits):
  122. var w: uint = r.bits[i]
  123. # taking a copy of r.bits[i] here is correct, because
  124. # modifying operations are not allowed during traversation
  125. var j = 0
  126. while w != 0: # test all remaining bits for zero
  127. if (w and 1) != 0: # the bit is set!
  128. yield (r.key shl TrunkShift) or (i shl IntShift +% j)
  129. inc(j)
  130. w = w shr 1
  131. inc(i)
  132. r = r.next
  133. proc initIntSet*: IntSet =
  134. ## Returns an empty IntSet.
  135. runnableExamples:
  136. var a = initIntSet()
  137. assert len(a) == 0
  138. # newSeq(result.data, InitIntSetSize)
  139. # result.max = InitIntSetSize-1
  140. result = IntSet(
  141. elems: 0,
  142. counter: 0,
  143. max: 0,
  144. head: nil,
  145. data: when defined(nimNoNilSeqs): @[] else: nil)
  146. # a: array[0..33, int] # profiling shows that 34 elements are enough
  147. proc contains*(s: IntSet, key: int): bool =
  148. ## Returns true if `key` is in `s`.
  149. ##
  150. ## This allows the usage of `in` operator.
  151. runnableExamples:
  152. var a = initIntSet()
  153. for x in [1, 3, 5]:
  154. a.incl(x)
  155. assert a.contains(3)
  156. assert 3 in a
  157. assert(not a.contains(8))
  158. assert 8 notin a
  159. if s.elems <= s.a.len:
  160. for i in 0..<s.elems:
  161. if s.a[i] == key: return true
  162. else:
  163. var t = intSetGet(s, `shr`(key, TrunkShift))
  164. if t != nil:
  165. var u = key and TrunkMask
  166. result = (t.bits[u shr IntShift] and
  167. (BitScalar(1) shl (u and IntMask))) != 0
  168. else:
  169. result = false
  170. proc incl*(s: var IntSet, key: int) =
  171. ## Includes an element `key` in `s`.
  172. ##
  173. ## This doesn't do anything if `key` is already in `s`.
  174. ##
  175. ## See also:
  176. ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
  177. ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set
  178. ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
  179. runnableExamples:
  180. var a = initIntSet()
  181. a.incl(3)
  182. a.incl(3)
  183. assert len(a) == 1
  184. if s.elems <= s.a.len:
  185. for i in 0..<s.elems:
  186. if s.a[i] == key: return
  187. if s.elems < s.a.len:
  188. s.a[s.elems] = key
  189. inc s.elems
  190. return
  191. newSeq(s.data, InitIntSetSize)
  192. s.max = InitIntSetSize-1
  193. for i in 0..<s.elems:
  194. bitincl(s, s.a[i])
  195. s.elems = s.a.len + 1
  196. # fall through:
  197. bitincl(s, key)
  198. proc incl*(s: var IntSet, other: IntSet) =
  199. ## Includes all elements from `other` into `s`.
  200. ##
  201. ## This is the in-place version of `s + other <#+,IntSet,IntSet>`_.
  202. ##
  203. ## See also:
  204. ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
  205. ## * `incl proc <#incl,IntSet,int>`_ for including an element
  206. ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
  207. runnableExamples:
  208. var
  209. a = initIntSet()
  210. b = initIntSet()
  211. a.incl(1)
  212. b.incl(5)
  213. a.incl(b)
  214. assert len(a) == 2
  215. assert 5 in a
  216. for item in other: incl(s, item)
  217. proc containsOrIncl*(s: var IntSet, key: int): bool =
  218. ## Includes `key` in the set `s` and tells if `key` was already in `s`.
  219. ##
  220. ## The difference with regards to the `incl proc <#incl,IntSet,int>`_ is
  221. ## that this proc returns `true` if `s` already contained `key`. The
  222. ## proc will return `false` if `key` was added as a new value to `s` during
  223. ## this call.
  224. ##
  225. ## See also:
  226. ## * `incl proc <#incl,IntSet,int>`_ for including an element
  227. ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
  228. runnableExamples:
  229. var a = initIntSet()
  230. assert a.containsOrIncl(3) == false
  231. assert a.containsOrIncl(3) == true
  232. assert a.containsOrIncl(4) == false
  233. if s.elems <= s.a.len:
  234. for i in 0..<s.elems:
  235. if s.a[i] == key:
  236. return true
  237. incl(s, key)
  238. result = false
  239. else:
  240. var t = intSetGet(s, `shr`(key, TrunkShift))
  241. if t != nil:
  242. var u = key and TrunkMask
  243. result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0
  244. if not result:
  245. t.bits[u shr IntShift] = t.bits[u shr IntShift] or
  246. (BitScalar(1) shl (u and IntMask))
  247. else:
  248. incl(s, key)
  249. result = false
  250. proc excl*(s: var IntSet, key: int) =
  251. ## Excludes `key` from the set `s`.
  252. ##
  253. ## This doesn't do anything if `key` is not found in `s`.
  254. ##
  255. ## See also:
  256. ## * `incl proc <#incl,IntSet,int>`_ for including an element
  257. ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
  258. ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
  259. runnableExamples:
  260. var a = initIntSet()
  261. a.incl(3)
  262. a.excl(3)
  263. a.excl(3)
  264. a.excl(99)
  265. assert len(a) == 0
  266. exclImpl(s, key)
  267. proc excl*(s: var IntSet, other: IntSet) =
  268. ## Excludes all elements from `other` from `s`.
  269. ##
  270. ## This is the in-place version of `s - other <#-,IntSet,IntSet>`_.
  271. ##
  272. ## See also:
  273. ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set
  274. ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
  275. ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_
  276. runnableExamples:
  277. var
  278. a = initIntSet()
  279. b = initIntSet()
  280. a.incl(1)
  281. a.incl(5)
  282. b.incl(5)
  283. a.excl(b)
  284. assert len(a) == 1
  285. assert 5 notin a
  286. for item in other: excl(s, item)
  287. proc len*(s: IntSet): int {.inline.} =
  288. ## Returns the number of elements in `s`.
  289. if s.elems < s.a.len:
  290. result = s.elems
  291. else:
  292. result = 0
  293. for _ in s:
  294. inc(result)
  295. proc missingOrExcl*(s: var IntSet, key: int): bool =
  296. ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`.
  297. ##
  298. ## The difference with regards to the `excl proc <#excl,IntSet,int>`_ is
  299. ## that this proc returns `true` if `key` was missing from `s`.
  300. ## The proc will return `false` if `key` was in `s` and it was removed
  301. ## during this call.
  302. ##
  303. ## See also:
  304. ## * `excl proc <#excl,IntSet,int>`_ for excluding an element
  305. ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set
  306. ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_
  307. runnableExamples:
  308. var a = initIntSet()
  309. a.incl(5)
  310. assert a.missingOrExcl(5) == false
  311. assert a.missingOrExcl(5) == true
  312. var count = s.len
  313. exclImpl(s, key)
  314. result = count == s.len
  315. proc clear*(result: var IntSet) =
  316. ## Clears the IntSet back to an empty state.
  317. runnableExamples:
  318. var a = initIntSet()
  319. a.incl(5)
  320. a.incl(7)
  321. clear(a)
  322. assert len(a) == 0
  323. # setLen(result.data, InitIntSetSize)
  324. # for i in 0..InitIntSetSize-1: result.data[i] = nil
  325. # result.max = InitIntSetSize-1
  326. when defined(nimNoNilSeqs):
  327. result.data = @[]
  328. else:
  329. result.data = nil
  330. result.max = 0
  331. result.counter = 0
  332. result.head = nil
  333. result.elems = 0
  334. proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0
  335. proc assign*(dest: var IntSet, src: IntSet) =
  336. ## Copies `src` to `dest`.
  337. ## `dest` does not need to be initialized by `initIntSet proc <#initIntSet>`_.
  338. runnableExamples:
  339. var
  340. a = initIntSet()
  341. b = initIntSet()
  342. b.incl(5)
  343. b.incl(7)
  344. a.assign(b)
  345. assert len(a) == 2
  346. if src.elems <= src.a.len:
  347. when defined(nimNoNilSeqs):
  348. dest.data = @[]
  349. else:
  350. dest.data = nil
  351. dest.max = 0
  352. dest.counter = src.counter
  353. dest.head = nil
  354. dest.elems = src.elems
  355. dest.a = src.a
  356. else:
  357. dest.counter = src.counter
  358. dest.max = src.max
  359. dest.elems = src.elems
  360. newSeq(dest.data, src.data.len)
  361. var it = src.head
  362. while it != nil:
  363. var h = it.key and dest.max
  364. while dest.data[h] != nil: h = nextTry(h, dest.max)
  365. assert(dest.data[h] == nil)
  366. var n: PTrunk
  367. new(n)
  368. n.next = dest.head
  369. n.key = it.key
  370. n.bits = it.bits
  371. dest.head = n
  372. dest.data[h] = n
  373. it = it.next
  374. proc union*(s1, s2: IntSet): IntSet =
  375. ## Returns the union of the sets `s1` and `s2`.
  376. ##
  377. ## The same as `s1 + s2 <#+,IntSet,IntSet>`_.
  378. runnableExamples:
  379. var
  380. a = initIntSet()
  381. b = initIntSet()
  382. a.incl(1); a.incl(2); a.incl(3)
  383. b.incl(3); b.incl(4); b.incl(5)
  384. assert union(a, b).len == 5
  385. ## {1, 2, 3, 4, 5}
  386. result.assign(s1)
  387. incl(result, s2)
  388. proc intersection*(s1, s2: IntSet): IntSet =
  389. ## Returns the intersection of the sets `s1` and `s2`.
  390. ##
  391. ## The same as `s1 * s2 <#*,IntSet,IntSet>`_.
  392. runnableExamples:
  393. var
  394. a = initIntSet()
  395. b = initIntSet()
  396. a.incl(1); a.incl(2); a.incl(3)
  397. b.incl(3); b.incl(4); b.incl(5)
  398. assert intersection(a, b).len == 1
  399. ## {3}
  400. result = initIntSet()
  401. for item in s1:
  402. if contains(s2, item):
  403. incl(result, item)
  404. proc difference*(s1, s2: IntSet): IntSet =
  405. ## Returns the difference of the sets `s1` and `s2`.
  406. ##
  407. ## The same as `s1 - s2 <#-,IntSet,IntSet>`_.
  408. runnableExamples:
  409. var
  410. a = initIntSet()
  411. b = initIntSet()
  412. a.incl(1); a.incl(2); a.incl(3)
  413. b.incl(3); b.incl(4); b.incl(5)
  414. assert difference(a, b).len == 2
  415. ## {1, 2}
  416. result = initIntSet()
  417. for item in s1:
  418. if not contains(s2, item):
  419. incl(result, item)
  420. proc symmetricDifference*(s1, s2: IntSet): IntSet =
  421. ## Returns the symmetric difference of the sets `s1` and `s2`.
  422. runnableExamples:
  423. var
  424. a = initIntSet()
  425. b = initIntSet()
  426. a.incl(1); a.incl(2); a.incl(3)
  427. b.incl(3); b.incl(4); b.incl(5)
  428. assert symmetricDifference(a, b).len == 4
  429. ## {1, 2, 4, 5}
  430. result.assign(s1)
  431. for item in s2:
  432. if containsOrIncl(result, item): excl(result, item)
  433. proc `+`*(s1, s2: IntSet): IntSet {.inline.} =
  434. ## Alias for `union(s1, s2) <#union,IntSet,IntSet>`_.
  435. result = union(s1, s2)
  436. proc `*`*(s1, s2: IntSet): IntSet {.inline.} =
  437. ## Alias for `intersection(s1, s2) <#intersection,IntSet,IntSet>`_.
  438. result = intersection(s1, s2)
  439. proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
  440. ## Alias for `difference(s1, s2) <#difference,IntSet,IntSet>`_.
  441. result = difference(s1, s2)
  442. proc disjoint*(s1, s2: IntSet): bool =
  443. ## Returns true if the sets `s1` and `s2` have no items in common.
  444. runnableExamples:
  445. var
  446. a = initIntSet()
  447. b = initIntSet()
  448. a.incl(1); a.incl(2)
  449. b.incl(2); b.incl(3)
  450. assert disjoint(a, b) == false
  451. b.excl(2)
  452. assert disjoint(a, b) == true
  453. for item in s1:
  454. if contains(s2, item):
  455. return false
  456. return true
  457. proc card*(s: IntSet): int {.inline.} =
  458. ## Alias for `len() <#len,IntSet>`_.
  459. result = s.len()
  460. proc `<=`*(s1, s2: IntSet): bool =
  461. ## Returns true if `s1` is subset of `s2`.
  462. ##
  463. ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily
  464. ## have more elements than `s1`. That is, `s1` can be equal to `s2`.
  465. runnableExamples:
  466. var
  467. a = initIntSet()
  468. b = initIntSet()
  469. a.incl(1)
  470. b.incl(1); b.incl(2)
  471. assert a <= b
  472. a.incl(2)
  473. assert a <= b
  474. a.incl(3)
  475. assert(not (a <= b))
  476. for item in s1:
  477. if not s2.contains(item):
  478. return false
  479. return true
  480. proc `<`*(s1, s2: IntSet): bool =
  481. ## Returns true if `s1` is proper subset of `s2`.
  482. ##
  483. ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has
  484. ## more elements than `s1`.
  485. runnableExamples:
  486. var
  487. a = initIntSet()
  488. b = initIntSet()
  489. a.incl(1)
  490. b.incl(1); b.incl(2)
  491. assert a < b
  492. a.incl(2)
  493. assert(not (a < b))
  494. return s1 <= s2 and not (s2 <= s1)
  495. proc `==`*(s1, s2: IntSet): bool =
  496. ## Returns true if both `s1` and `s2` have the same elements and set size.
  497. return s1 <= s2 and s2 <= s1
  498. proc `$`*(s: IntSet): string =
  499. ## The `$` operator for int sets.
  500. ##
  501. ## Converts the set `s` to a string, mostly for logging and printing purposes.
  502. dollarImpl()
  503. when isMainModule:
  504. import sequtils, algorithm
  505. var x = initIntSet()
  506. x.incl(1)
  507. x.incl(2)
  508. x.incl(7)
  509. x.incl(1056)
  510. x.incl(1044)
  511. x.excl(1044)
  512. assert x.containsOrIncl(888) == false
  513. assert 888 in x
  514. assert x.containsOrIncl(888) == true
  515. assert x.missingOrExcl(888) == false
  516. assert 888 notin x
  517. assert x.missingOrExcl(888) == true
  518. var xs = toSeq(items(x))
  519. xs.sort(cmp[int])
  520. assert xs == @[1, 2, 7, 1056]
  521. var y: IntSet
  522. assign(y, x)
  523. var ys = toSeq(items(y))
  524. ys.sort(cmp[int])
  525. assert ys == @[1, 2, 7, 1056]
  526. assert x == y
  527. var z: IntSet
  528. for i in 0..1000:
  529. incl z, i
  530. assert z.len() == i+1
  531. for i in 0..1000:
  532. assert z.contains(i)
  533. var w = initIntSet()
  534. w.incl(1)
  535. w.incl(4)
  536. w.incl(50)
  537. w.incl(1001)
  538. w.incl(1056)
  539. var xuw = x.union(w)
  540. var xuws = toSeq(items(xuw))
  541. xuws.sort(cmp[int])
  542. assert xuws == @[1, 2, 4, 7, 50, 1001, 1056]
  543. var xiw = x.intersection(w)
  544. var xiws = toSeq(items(xiw))
  545. xiws.sort(cmp[int])
  546. assert xiws == @[1, 1056]
  547. var xdw = x.difference(w)
  548. var xdws = toSeq(items(xdw))
  549. xdws.sort(cmp[int])
  550. assert xdws == @[2, 7]
  551. var xsw = x.symmetricDifference(w)
  552. var xsws = toSeq(items(xsw))
  553. xsws.sort(cmp[int])
  554. assert xsws == @[2, 4, 7, 50, 1001]
  555. x.incl(w)
  556. xs = toSeq(items(x))
  557. xs.sort(cmp[int])
  558. assert xs == @[1, 2, 4, 7, 50, 1001, 1056]
  559. assert w <= x
  560. assert w < x
  561. assert(not disjoint(w, x))
  562. var u = initIntSet()
  563. u.incl(3)
  564. u.incl(5)
  565. u.incl(500)
  566. assert disjoint(u, x)
  567. var v = initIntSet()
  568. v.incl(2)
  569. v.incl(50)
  570. x.excl(v)
  571. xs = toSeq(items(x))
  572. xs.sort(cmp[int])
  573. assert xs == @[1, 4, 7, 1001, 1056]
  574. proc bug12366 =
  575. var
  576. x = initIntSet()
  577. y = initIntSet()
  578. n = 3584
  579. for i in 0..n:
  580. x.incl(i)
  581. y.incl(i)
  582. let z = symmetricDifference(x, y)
  583. doAssert z.len == 0
  584. doAssert $z == "{}"
  585. bug12366()