From 4633bcc6802aea60906599540e39adba80286c11 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 7 Jun 2017 07:57:22 +0200 Subject: [PATCH] Set: Fix comparing with removed elements --- source/tanya/container/entry.d | 28 +++++++++ source/tanya/container/set.d | 103 ++++++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 0df388f..dd3c5e4 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -69,6 +69,34 @@ package struct Bucket(T) return this.content_; } + bool opEquals(ref T content) + { + if (this.status == BucketStatus.used && this.content == content) + { + return true; + } + return false; + } + + bool opEquals(ref T content) const + { + if (this.status == BucketStatus.used && this.content == content) + { + return true; + } + return false; + } + + bool opEquals(ref typeof(this) that) + { + return this.content == that.content && this.status == that.status; + } + + bool opEquals(ref typeof(this) that) const + { + return this.content == that.content && this.status == that.status; + } + void remove() { static if (hasElaborateDestructor!T) diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index d18c723..f2102cc 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -142,10 +142,13 @@ struct Range(E) * This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions * are resolved with linear probing. * + * Currently works only with integral types. + * * Params: * T = Element type. */ struct Set(T) + if (isIntegral!T || is(Unqual!T == bool)) { /// The range types for $(D_PSYMBOL Set). alias Range = .Range!T; @@ -191,6 +194,19 @@ struct Set(T) this.data = typeof(this.data)(allocator); } + /// + unittest + { + { + auto set = Set!int(defaultAllocator); + assert(set.capacity == 0); + } + { + auto set = Set!int(8); + assert(set.capacity == 13); + } + } + /** * Initializes this $(D_PARAM Set) from another one. * @@ -285,6 +301,16 @@ struct Set(T) return this.data.length; } + /// + unittest + { + Set!int set; + assert(set.capacity == 0); + + set.insert(8); + assert(set.capacity == 3); + } + /** * Iterates over the $(D_PSYMBOL Set) and counts the elements. * @@ -303,6 +329,16 @@ struct Set(T) return count; } + /// + unittest + { + Set!int set; + assert(set.length == 0); + + set.insert(8); + assert(set.length == 1); + } + private static const size_t[41] primes = [ 3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521, 769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593, @@ -339,8 +375,8 @@ struct Set(T) /* * Inserts the value in an empty or deleted bucket. If the value is - * already in there, does nothing and returns true. If the hash array - * is full returns false. + * already in there, does nothing and returns InsertStatus.found. If the + * hash array is full returns InsertStatus.failed. */ private InsertStatus insertInUnusedBucket(ref T value) { @@ -348,7 +384,7 @@ struct Set(T) foreach (ref e; this.data[bucketPosition .. $]) { - if (e.content == value) // Already in the set. + if (e == value) // Already in the set. { return InsertStatus.found; } @@ -391,6 +427,24 @@ struct Set(T) return status == InsertStatus.added; } + /// + unittest + { + Set!int set; + assert(8 !in set); + + assert(set.insert(8) == 1); + assert(set.length == 1); + assert(8 in set); + + assert(set.insert(8) == 0); + assert(set.length == 1); + assert(8 in set); + + assert(set.remove(8)); + assert(set.insert(8) == 1); + } + /** * Removes an element. * @@ -410,7 +464,7 @@ struct Set(T) auto bucketPosition = locateBucket(this.data, calculateHash(value)); foreach (ref e; this.data[bucketPosition .. $]) { - if (e.content == value) // Found. + if (e == value) // Found. { e.remove(); return 1; @@ -423,6 +477,20 @@ struct Set(T) return 0; } + /// + unittest + { + Set!int set; + assert(8 !in set); + + set.insert(8); + assert(8 in set); + + assert(set.remove(8) == 1); + assert(set.remove(8) == 0); + assert(8 !in set); + } + /** * $(D_KEYWORD in) operator. * @@ -442,7 +510,7 @@ struct Set(T) auto bucketPosition = locateBucket(this.data, calculateHash(value)); foreach (ref e; this.data[bucketPosition .. $]) { - if (e.content == value) // Found. + if (e == value) // Found. { return true; } @@ -465,7 +533,7 @@ struct Set(T) auto bucketPosition = locateBucket(this.data, calculateHash(value)); foreach (ref e; this.data[bucketPosition .. $]) { - if (e.content == value) // Found. + if (e.status == BucketStatus.used && e.content == value) // Found. { return true; } @@ -477,7 +545,6 @@ struct Set(T) return false; } - /// unittest { @@ -565,6 +632,21 @@ struct Set(T) return typeof(return)(this.data[]); } + /// + unittest + { + Set!int set; + assert(set[].empty); + + set.insert(8); + assert(!set[].empty); + assert(set[].front == 8); + assert(set[].back == 8); + + set.remove(8); + assert(set[].empty); + } + private alias DataType = Array!(Bucket!T); private DataType data; private size_t lengthIndex; @@ -618,4 +700,11 @@ private unittest static assert(!isInfinite!(Set!int.Range)); static assert(!hasLength!(Set!int.Range)); + + static assert(is(Set!uint)); + static assert(is(Set!long)); + static assert(is(Set!ulong)); + static assert(is(Set!short)); + static assert(is(Set!ushort)); + static assert(is(Set!bool)); }