//O(1) time complexity; worst case O(n)
        //add an item to this hash table
        public void Add(V value)
        {
            Grow();

            var hashCode = getHash(value);

            var index = hashCode % bucketSize;

            if (hashArray[index] == null)
            {
                hashArray[index] = new HashSetNode <V>(value);
            }
            else
            {
                var current = hashArray[index];
                //keep track of this so that we won't circle around infinitely
                var hitKey = current.Value;

                while (current != null)
                {
                    if (current.Value.Equals(value))
                    {
                        throw new Exception("Duplicate value");
                    }

                    index++;

                    //wrap around
                    if (index == bucketSize)
                    {
                        index = 0;
                    }

                    current = hashArray[index];

                    if (current != null && current.Value.Equals(hitKey))
                    {
                        throw new Exception("HashSet is full");
                    }
                }

                hashArray[index] = new HashSetNode <V>(value);
            }

            Count++;
        }
        //O(1) time complexity; worst case O(n)
        public void Remove(V value)
        {
            var hashCode = getHash(value);
            var curIndex = hashCode % bucketSize;

            if (hashArray[curIndex] == null)
            {
                throw new Exception("No such item for given value");
            }
            else
            {
                var current = hashArray[curIndex];

                //prevent circling around infinitely
                var hitKey = current.Value;

                HashSetNode <V> target = null;

                while (current != null)
                {
                    if (current.Value.Equals(value))
                    {
                        target = current;
                        break;
                    }

                    curIndex++;

                    //wrap around
                    if (curIndex == bucketSize)
                    {
                        curIndex = 0;
                    }

                    current = hashArray[curIndex];

                    if (current != null && current.Value.Equals(hitKey))
                    {
                        throw new Exception("No such item for given value");
                    }
                }

                //remove
                if (target == null)
                {
                    throw new Exception("No such item for given value");
                }
                else
                {
                    //delete this element
                    hashArray[curIndex] = null;

                    //now time to cleanup subsequent broken hash elements due to this emptied cell
                    curIndex++;

                    //wrap around
                    if (curIndex == bucketSize)
                    {
                        curIndex = 0;
                    }

                    current = hashArray[curIndex];

                    //until an empty cell
                    while (current != null)
                    {
                        //delete current
                        hashArray[curIndex] = null;

                        //add current back to table
                        Add(current.Value);
                        Count--;

                        curIndex++;

                        //wrap around
                        if (curIndex == bucketSize)
                        {
                            curIndex = 0;
                        }

                        current = hashArray[curIndex];
                    }
                }
            }

            Count--;

            Shrink();
        }