Set: Fix comparing with removed elements

This commit is contained in:
Eugen Wissner 2017-06-07 07:57:22 +02:00
parent dc39efd316
commit 4633bcc680
2 changed files with 124 additions and 7 deletions

View File

@ -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)

View File

@ -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));
}