# # # Nim's Runtime Library # (c) Copyright 2019 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # An `include` file for the different hash set implementations. template maxHash(t): untyped = high(t.data) template dataLen(t): untyped = len(t.data) include hashcommon template initImpl(s: typed, size: int) = let correctSize = slotsNeeded(size) when s is OrderedSet: s.first = -1 s.last = -1 s.counter = 0 newSeq(s.data, correctSize) template rawInsertImpl() {.dirty.} = if data.len == 0: initImpl(s, defaultInitialSize) data[h].key = key data[h].hcode = hc proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A, hc: Hash, h: Hash) = rawInsertImpl() proc enlarge[A](s: var HashSet[A]) = var n: KeyValuePairSeq[A] newSeq(n, len(s.data) * growthFactor) swap(s.data, n) # n is now old seq for i in countup(0, high(n)): if isFilled(n[i].hcode): var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode) rawInsert(s, s.data, n[i].key, n[i].hcode, j) template inclImpl() {.dirty.} = if s.data.len == 0: initImpl(s, defaultInitialSize) var hc: Hash var index = rawGet(s, key, hc) if index < 0: if mustRehash(s): enlarge(s) index = rawGetKnownHC(s, key, hc) rawInsert(s, s.data, key, hc, -1 - index) inc(s.counter) template containsOrInclImpl() {.dirty.} = if s.data.len == 0: initImpl(s, defaultInitialSize) var hc: Hash var index = rawGet(s, key, hc) if index >= 0: result = true else: if mustRehash(s): enlarge(s) index = rawGetKnownHC(s, key, hc) rawInsert(s, s.data, key, hc, -1 - index) inc(s.counter) template doWhile(a, b) = while true: b if not a: break proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} = var hc: Hash var i = rawGet(s, key, hc) var msk = high(s.data) result = true if i >= 0: result = false dec(s.counter) while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 var j = i # The correctness of this depends on (h+1) in nextTry, var r = j # though may be adaptable to other simple sequences. s.data[i].hcode = 0 # mark current EMPTY s.data[i].key = default(typeof(s.data[i].key)) doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): i = (i + 1) and msk # increment mod table size if isEmpty(s.data[i].hcode): # end of collision cluster; So all done return r = s.data[i].hcode and msk # "home" location of key@i s.data[j] = move(s.data[i]) # data[i] will be marked EMPTY next loop template dollarImpl() {.dirty.} = result = "{" for key in items(s): if result.len > 1: result.add(", ") result.addQuoted(key) result.add("}") # --------------------------- OrderedSet ------------------------------ proc rawGet[A](t: OrderedSet[A], key: A, hc: var Hash): int {.inline.} = rawGetImpl() proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A], key: A, hc: Hash, h: Hash) = rawInsertImpl() data[h].next = -1 if s.first < 0: s.first = h if s.last >= 0: data[s.last].next = h s.last = h proc enlarge[A](s: var OrderedSet[A]) = var n: OrderedKeyValuePairSeq[A] newSeq(n, len(s.data) * growthFactor) var h = s.first s.first = -1 s.last = -1 swap(s.data, n) while h >= 0: var nxt = n[h].next if isFilled(n[h].hcode): var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode) rawInsert(s, s.data, n[h].key, n[h].hcode, j) h = nxt proc exclImpl[A](s: var OrderedSet[A], key: A): bool {.inline.} = if len(s.data) == 0: return true var n: OrderedKeyValuePairSeq[A] newSeq(n, len(s.data)) var h = s.first s.first = -1 s.last = -1 swap(s.data, n) let hc = genHash(key) result = true while h >= 0: var nxt = n[h].next if isFilled(n[h].hcode): if n[h].hcode == hc and n[h].key == key: dec s.counter result = false else: var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode) rawInsert(s, s.data, n[h].key, n[h].hcode, j) h = nxt