# datastructures/set.js

    'use strict';

    /**
     * Creates a Set instance.
     * @constructor
     */
    function Set() {
        this.set = {};
        this.numElems = 0;
    }

    /**
     * Get the number of elements in the set.
     * @returns {number} Number of elements in the set.
     */
    Set.prototype.size = function() {
        return this.numElems;
    };

    /**
     * Returns whether the set is empty.
     * @returns {boolean} Whether or not the set is empty.
     */
    Set.prototype.isEmpty = function() {
        return this.numElems === 0;
    };

    /**
     * Clears the elements in the set.
     * Results in a set equal to a newly constructed instance.
     */
    Set.prototype.clear = function() {
        this.set = {};
        this.numElems = 0;
    };

    /**
     * Extract a key from an object for the set dictionary.
     * @param {elem} elem - A graphics object to get a key for.
     * @returns {string} A string representing the elen.
     */
    Set.prototype.getKey = function(elem) {
        var name = elem;
        if(typeof elem == "object")
            name = elem.toString();
        return name;
    };

    /**
     * Add an object to a set.
     * @param {elem} elem - A graphics object to be added.
     */
    Set.prototype.add = function(elem) {
        if(elem === null) {
            throw new TypeError('Cannot add a null to a set.');
        }
        var key = this.getKey(elem);
        if(!this.containsKey(key)){
            this.numElems++;
        }
        this.set[key] = elem;
    };

    /**
     * Remove an object from a set.
     * @param {elem} elem - A graphics object to be removed.
     */
    Set.prototype.remove = function(elem) {
        if(elem === null) {
            throw new TypeError('Cannot remove null from a set.');
        }
        var key = this.getKey(elem);
        if (this.containsKey(key)) {
            delete this.set[key];
            this.numElems--;
        } else {
            throw new Error('Set does not contain ' + key);
        }
    };

    /**
     * Check if a set contains a key.
     * @return {boolean} Whether or not the set contains the key.
     */
    Set.prototype.containsKey = function(key) {
        return typeof this.set[key] != 'undefined';
    };

    /**
     * Check if a set contains an elem.
     * @param {elem} elem - A graphics object to be checked.
     * @return {boolean} Whether or not the set contains the elem.
     */
    Set.prototype.contains = function(elem) {
        return typeof this.find(elem) != "undefined";
    };

    /**
     * Get the items in the set.
     * @returns {dictionary} Dictionary of all items in the set
     */
    Set.prototype.elems = function() {
        return this.set;
    };

    /**
     * Finds an elem in the set.
     * @param {elem} elem - An element to look for.
     * @returns {elem|undefined} Either the element (if found) or undefined.
     */
    Set.prototype.find = function(elem) {
        var key = this.getKey(elem);
        return this.set[key];
    };

    /**
     * Creates a union between two sets.
     * @param {Set} otherSet - A set with which a union should be created.
     */
    Set.prototype.union = function(otherSet) {
        for(var key in otherSet.elems()){
            this.add(otherSet.find(key));
        }
    };

    /**
     * Remove items from the set if they are not contained in otherSet.
     * @param {Set} otherSet - A set with which an intersection should be created.
     */
    Set.prototype.intersect = function(otherSet) {
        var newElems = {};
        var newSize = 0;

        for(var key in otherSet.elems()){
            if(this.containsKey(key)){
                newSize++;
                newElems[key] = this.find(key);
            }
        }

        this.set = newElems;
        this.numElems = newSize;
    };

    /**
     * Create a string representation of the set.
     * @returns {string} String representation of the set
     * Follows the syntax 'Set: {elem, elem, elem}'
     */
    Set.prototype.toString = function() {

        var result = "Set: {";
        var i = 0;
        for(var key in this.set){
            var cur = this.set[key];
            result += cur;
            if(i < this.size() - 1){
                result += ', ';
            }
            i++;
        }
        result += "}";
        return result;
    };

    module.exports = Set;