Implement lookups in the Set
This commit is contained in:
		@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user