Implement lookups in the Set
This commit is contained in:
parent
0f365758e1
commit
ea33ca62c8
@ -217,6 +217,9 @@ struct Set(T)
|
||||
12582917, 25165843, 139022417, 282312799, 573292817, 1164186217,
|
||||
];
|
||||
|
||||
/// The maximum number of buckets the container can have.
|
||||
enum size_t maxBucketCount = primes[$ - 1];
|
||||
|
||||
static private size_t calculateHash(ref T value)
|
||||
{
|
||||
static if (isIntegral!T || isSomeChar!T || is(T == bool))
|
||||
@ -229,7 +232,7 @@ struct Set(T)
|
||||
}
|
||||
}
|
||||
|
||||
static private size_t locateBucket(ref DataType buckets, size_t hash)
|
||||
static private size_t locateBucket(ref const DataType buckets, size_t hash)
|
||||
{
|
||||
return hash % buckets.length;
|
||||
}
|
||||
@ -241,15 +244,16 @@ struct Set(T)
|
||||
added = 1,
|
||||
}
|
||||
|
||||
// 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.
|
||||
static private InsertStatus insertInUnusedBucket(ref DataType buckets,
|
||||
ref T value)
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
private InsertStatus insertInUnusedBucket(ref T value)
|
||||
{
|
||||
auto bucketPosition = locateBucket(buckets, calculateHash(value));
|
||||
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||
|
||||
foreach (ref e; buckets[bucketPosition .. $])
|
||||
foreach (ref e; this.data[bucketPosition .. $])
|
||||
{
|
||||
if (e.content == value) // Already in the set.
|
||||
{
|
||||
@ -281,10 +285,15 @@ struct Set(T)
|
||||
this.data = DataType(primes[0], allocator);
|
||||
}
|
||||
|
||||
InsertStatus status = insertInUnusedBucket(this.data, value);
|
||||
for (; !status; status = insertInUnusedBucket(this.data, value))
|
||||
InsertStatus status = insertInUnusedBucket(value);
|
||||
for (; !status; status = insertInUnusedBucket(value))
|
||||
{
|
||||
rehash();
|
||||
if ((this.primes.length - 1) == this.lengthIndex)
|
||||
{
|
||||
throw make!HashContainerFullException(defaultAllocator,
|
||||
"Set is full");
|
||||
}
|
||||
rehashToSize(this.lengthIndex + 1);
|
||||
}
|
||||
return status == InsertStatus.added;
|
||||
}
|
||||
@ -295,7 +304,8 @@ struct Set(T)
|
||||
* Params:
|
||||
* value = Element value.
|
||||
*
|
||||
* Returns: Amount of the elements removed.
|
||||
* Returns: Number of elements removed, which is in the container with
|
||||
* unique values `1` if an element existed, and `0` otherwise.
|
||||
*/
|
||||
size_t remove(T value)
|
||||
{
|
||||
@ -312,53 +322,160 @@ struct Set(T)
|
||||
e.remove();
|
||||
return 1;
|
||||
}
|
||||
else if (e.status == BucketStatus.empty) // Insert the value.
|
||||
else if (e.status == BucketStatus.empty)
|
||||
{
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void rehash()
|
||||
/**
|
||||
* $(D_KEYWORD in) operator.
|
||||
*
|
||||
* Params:
|
||||
* value = Element to be searched for.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the given element exists in the container,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opBinaryRight(string op : "in")(auto ref T value)
|
||||
{
|
||||
if ((this.primes.length - 1) == this.lengthIndex)
|
||||
if (this.data.length == 0)
|
||||
{
|
||||
throw make!HashContainerFullException(defaultAllocator,
|
||||
"Set is full");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto storage = DataType(primes[this.lengthIndex + 1], allocator);
|
||||
foreach (ref e; this.data[])
|
||||
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||
foreach (ref e; this.data[bucketPosition .. $])
|
||||
{
|
||||
if (e.status == BucketStatus.used)
|
||||
if (e.content == value) // Found.
|
||||
{
|
||||
insertInUnusedBucket(storage, e.content);
|
||||
return true;
|
||||
}
|
||||
else if (e.status == BucketStatus.empty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
bool opBinaryRight(string op : "in")(auto ref const T value) const
|
||||
{
|
||||
if (this.data.length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||
foreach (ref e; this.data[bucketPosition .. $])
|
||||
{
|
||||
if (e.content == value) // Found.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (e.status == BucketStatus.empty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
Set!int set;
|
||||
|
||||
assert(5 !in set);
|
||||
set.insert(5);
|
||||
assert(5 in set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of buckets in the container to at least $(D_PARAM n)
|
||||
* and rearranges all the elements according to their hash values.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
|
||||
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
|
||||
* forced.
|
||||
*
|
||||
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
|
||||
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
|
||||
i *
|
||||
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
|
||||
* is forced without resizing the container.
|
||||
*
|
||||
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
|
||||
* function may have no effect.
|
||||
*
|
||||
* Rehashing is automatically performed whenever the container needs space
|
||||
* to insert new elements.
|
||||
*
|
||||
* Params:
|
||||
* n = Minimum number of buckets.
|
||||
*/
|
||||
void rehash(const size_t n)
|
||||
{
|
||||
size_t lengthIndex;
|
||||
for (; lengthIndex < primes.length; ++lengthIndex)
|
||||
{
|
||||
if (primes[lengthIndex] >= n)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
rehashToSize(lengthIndex);
|
||||
}
|
||||
|
||||
// Takes an index in the primes array.
|
||||
private void rehashToSize(const size_t n)
|
||||
{
|
||||
auto storage = DataType(primes[n], allocator);
|
||||
DataLoop: foreach (ref e1; this.data[])
|
||||
{
|
||||
if (e1.status == BucketStatus.used)
|
||||
{
|
||||
auto bucketPosition = locateBucket(storage,
|
||||
calculateHash(e1.content));
|
||||
|
||||
foreach (ref e2; storage[bucketPosition .. $])
|
||||
{
|
||||
if (e2.status != BucketStatus.used) // Insert the value.
|
||||
{
|
||||
e2.content = e1.content;
|
||||
continue DataLoop;
|
||||
}
|
||||
}
|
||||
return; // Rehashing failed.
|
||||
}
|
||||
}
|
||||
move(storage, this.data);
|
||||
++this.lengthIndex;
|
||||
this.lengthIndex = n;
|
||||
}
|
||||
|
||||
private alias DataType = Array!(Bucket!T);
|
||||
private DataType data;
|
||||
private size_t lengthIndex;
|
||||
|
||||
/**
|
||||
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
|
||||
* elements.
|
||||
*/
|
||||
Range opIndex()
|
||||
{
|
||||
return typeof(return)(data[]);
|
||||
return typeof(return)(this.data[]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
ConstRange opIndex() const
|
||||
{
|
||||
return typeof(return)(data[]);
|
||||
return typeof(return)(this.data[]);
|
||||
}
|
||||
|
||||
private alias DataType = Array!(Bucket!T);
|
||||
private DataType data;
|
||||
private size_t lengthIndex;
|
||||
|
||||
mixin DefaultAllocator;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user