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,
|
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 private size_t calculateHash(ref T value)
|
||||||
{
|
{
|
||||||
static if (isIntegral!T || isSomeChar!T || is(T == bool))
|
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;
|
return hash % buckets.length;
|
||||||
}
|
}
|
||||||
@ -241,15 +244,16 @@ struct Set(T)
|
|||||||
added = 1,
|
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
|
* Inserts the value in an empty or deleted bucket. If the value is
|
||||||
// is full returns false.
|
* already in there, does nothing and returns true. If the hash array
|
||||||
static private InsertStatus insertInUnusedBucket(ref DataType buckets,
|
* is full returns false.
|
||||||
ref T value)
|
*/
|
||||||
|
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.
|
if (e.content == value) // Already in the set.
|
||||||
{
|
{
|
||||||
@ -281,10 +285,15 @@ struct Set(T)
|
|||||||
this.data = DataType(primes[0], allocator);
|
this.data = DataType(primes[0], allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
InsertStatus status = insertInUnusedBucket(this.data, value);
|
InsertStatus status = insertInUnusedBucket(value);
|
||||||
for (; !status; status = insertInUnusedBucket(this.data, 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;
|
return status == InsertStatus.added;
|
||||||
}
|
}
|
||||||
@ -295,7 +304,8 @@ struct Set(T)
|
|||||||
* Params:
|
* Params:
|
||||||
* value = Element value.
|
* 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)
|
size_t remove(T value)
|
||||||
{
|
{
|
||||||
@ -312,53 +322,160 @@ struct Set(T)
|
|||||||
e.remove();
|
e.remove();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else if (e.status == BucketStatus.empty) // Insert the value.
|
else if (e.status == BucketStatus.empty)
|
||||||
{
|
{
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
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,
|
return 0;
|
||||||
"Set is full");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto storage = DataType(primes[this.lengthIndex + 1], allocator);
|
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||||
foreach (ref e; this.data[])
|
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);
|
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
|
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
|
||||||
* elements.
|
* elements.
|
||||||
*/
|
*/
|
||||||
Range opIndex()
|
Range opIndex()
|
||||||
{
|
{
|
||||||
return typeof(return)(data[]);
|
return typeof(return)(this.data[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
ConstRange opIndex() const
|
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;
|
mixin DefaultAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user