// $Id: set.h,v 1.3 1999/01/25 20:00:31 shields Exp $
copyright notice

#ifndef set_INCLUDED
#define set_INCLUDED

#include "config.h"
#include "assert.h"
#include "symbol.h"

class ShadowSymbol
{ 
public:
    ShadowSymbol *next;
    Symbol *symbol;
    int pool_index;

    inline NameSymbol *Identity() { return symbol -> Identity(); }

    ShadowSymbol(Symbol *symbol_) : symbol(symbol_),
                                    conflict(NULL)
    {}

    ~ShadowSymbol() { delete conflict; }

    Symbol *Conflict(int i) { return (*conflict)[i]; }

    inline int NumConflicts()
    {
        return (conflict ? conflict -> Length() : 0);
    }

    inline void AddConflict(Symbol *conflict_symbol)
    {
        if ((symbol != conflict_symbol) && (! Find(conflict_symbol)))
            conflict -> Next() = conflict_symbol;
        return;
    }    

    inline void RemoveConflict(int k)
    {
        int last_index = conflict -> Length() - 1;
        if (k < 0) // when k is negative, it identifies the main symbol
             symbol = (*conflict)[last_index];
        else (*conflict)[k] = (*conflict)[last_index];
        conflict -> Reset(last_index);
    }

private:
    Tuple<Symbol *> *conflict;

    bool Find(Symbol *conflict_symbol)
    {
        if (! conflict)
            conflict = new Tuple<Symbol *>(4);
        for (int k = 0; k < conflict -> Length(); k++)
            if ((*conflict)[k] == conflict_symbol)
                return true;
        return false;
    }    
};


class SymbolSet
{
public:
    enum
    {
        DEFAULT_HASH_SIZE = 13,
        MAX_HASH_SIZE = 1021
    };

    SymbolSet(int hash_size_ = DEFAULT_HASH_SIZE) : symbol_pool(256),
                                                    main_index(0),
                                                    sub_index(0)
    {
        hash_size = (hash_size_ <= 0 ? 1 : hash_size_);

        prime_index = -1;
        do
        {
            if (hash_size < primes[prime_index + 1])
                break;
            prime_index++;
        } while (primes[prime_index] < MAX_HASH_SIZE);

        base = (ShadowSymbol **) memset(new ShadowSymbol *[hash_size], 0, hash_size * sizeof(ShadowSymbol *));
    }

    ~SymbolSet();

    //
    // Calculate the size of the set an return the value.
    //
    inline int Size()
    {
        int size = 0;

        for (int i = 0; i < symbol_pool.Length(); i++)
        {
            ShadowSymbol *shadow = symbol_pool[i];
            Symbol *symbol = shadow -> symbol;
            for (int k = 0; symbol; symbol = (Symbol *) (k < shadow -> NumConflicts() ? shadow -> Conflict(k++) : NULL))
                size++;
        }

        return size;
    }

    //
    // Empty out the set in question - i.e., remove all its elements
    //
    inline void SetEmpty()
    {
        for (int i = 0; i < symbol_pool.Length(); i++)
            delete symbol_pool[i];
        symbol_pool.Reset();
        base = (ShadowSymbol **) memset(base, 0, hash_size * sizeof(ShadowSymbol *));
    }

    //
    // Empty out the set in question - i.e., remove all its elements
    //
    bool IsEmpty() { return symbol_pool.Length() == 0; }

    //
    // Assignment of a set to another.
    //
    SymbolSet &operator=(SymbolSet &rhs)
    {
        if (this != &rhs)
        {
            this -> SetEmpty();
            this -> Union(rhs);
        }

        return *this;
    }

    //
    // Equality comparison of two sets
    //
    bool operator==(SymbolSet &);

    //
    // NonEquality comparison of two sets
    //
    inline bool operator!=(SymbolSet &rhs)
    {
        return ! (*this == rhs);
    }

    //
    // Union the set in question with the set passed as argument: "set"
    //
    void Union(SymbolSet &);

    //
    // Intersect the set in question with the set passed as argument: "set"
    //
    void Intersection(SymbolSet &);

    //
    // Return a bolean value indicating whether or not the set in question intersects the set passed as argument: "set"
    // i.e., is there at least one element of set that is also an element of "this" set.
    //
    bool Intersects(SymbolSet &);

    //
    // How many elements with this name symbol do we have?
    //
    inline int NameCount(Symbol *element)
    {
        NameSymbol *name_symbol = element -> Identity();
        for (ShadowSymbol *shadow = base[name_symbol -> index % hash_size]; shadow; shadow = shadow -> next)
        {
            if (shadow -> Identity() == name_symbol)
                return shadow -> NumConflicts() + 1;
        }

        return 0;
    }

    //
    // Is "element" an element of the set in question ?
    //
    inline bool IsElement(Symbol *element)
    {
assert(element);
        NameSymbol *name_symbol = element -> Identity();
        for (ShadowSymbol *shadow = base[name_symbol -> index % hash_size]; shadow; shadow = shadow -> next)
        {
            if (shadow -> Identity() == name_symbol)
            {
                Symbol *symbol = shadow -> symbol;
                for (int k = 0; symbol; symbol = (Symbol *) (k < shadow -> NumConflicts() ? shadow -> Conflict(k++) : NULL))
                {
                    if (symbol == element)
                        return true;
                }

                return false;
            }
        }

        return false;
    }

    //
    // Add element to the set in question if was not already there.
    //
    inline void AddElement(Symbol *element)
    {
        NameSymbol *name_symbol = element -> Identity();
        int i = name_symbol -> index % hash_size;

        ShadowSymbol *shadow;
        for (shadow = base[i]; shadow; shadow = shadow -> next)
        {
            if (shadow -> Identity() == name_symbol)
            {
                shadow -> AddConflict(element);
                return;
            }
        }

        shadow = new ShadowSymbol(element);
        shadow -> pool_index = symbol_pool.Length();
        symbol_pool.Next() = shadow;

        shadow -> next = base[i];
        base[i] = shadow;

        //
        // If the set is "adjustable" and the number of unique elements in it exceeds
        // 2 times the size of the base, and we have not yet reached the maximum
        // allowable size for a base, reallocate a larger base and rehash the elements.
        //
        if ((hash_size < MAX_HASH_SIZE) && (symbol_pool.Length() > (hash_size << 1)))
            Rehash();

        return;
    }


    void RemoveElement(Symbol *);

    Symbol* FirstElement()
    {
        main_index = 0;
        sub_index = 0;
        return (main_index < symbol_pool.Length() ? symbol_pool[main_index] -> symbol : (Symbol *) NULL);
    }

    Symbol* NextElement()
    {
        Symbol *symbol = NULL;

        if (main_index < symbol_pool.Length())
        {
             if (sub_index < symbol_pool[main_index] -> NumConflicts())
                  symbol = symbol_pool[main_index] -> Conflict(sub_index++);
             else
             {
                 main_index++;
                 sub_index = 0;
                 symbol = (main_index < symbol_pool.Length() ? symbol_pool[main_index] -> symbol : (Symbol *) NULL);
             }
        }

        return symbol;
    }

protected:

    Tuple<ShadowSymbol *> symbol_pool;

    int main_index,
        sub_index;

    ShadowSymbol **base;
    int hash_size;

    static int primes[];
    int prime_index;

    void Rehash();
};


//
// Single-value Mapping from a name_symbol into a symbol with that name.
//
class SymbolMap : public SymbolSet
{
public:
    SymbolMap(int hash_size_ = DEFAULT_HASH_SIZE) : SymbolSet(hash_size_)
    {}

    //
    // Is there an element with this name in the map ?
    //
    inline Symbol *Image(NameSymbol *name_symbol)
    {
assert(name_symbol);
        for (ShadowSymbol *shadow = base[name_symbol -> index % hash_size]; shadow; shadow = shadow -> next)
        {
            if (shadow -> Identity() == name_symbol)
                return shadow -> symbol;
        }

        return NULL;
    }

    //
    // Add element to the set in question if was not already there.
    //
    inline void AddElement(Symbol *element)
    {
assert(element);
        ShadowSymbol *shadow = NULL;
        for (shadow = base[element -> Identity() -> index % hash_size]; shadow; shadow = shadow -> next)
        {
            if (shadow -> Identity() == element -> Identity())
                break;
        }

        //
        // If an element was already mapped into that name, replace it.
        // Otherwise, add the new element.
        //
        if (shadow)
             shadow -> symbol = element;
        else SymbolSet::AddElement(element);

        return;
    }
};


//
// This Bitset template class can be used to construct sets of
// integers. The template takes as argument the address of an integer
// variable, set_size, which indicates that the universe of the sets
// is: {0..*set_size}.
//
class BitSet
{
    typedef unsigned CELL;

    CELL *s;
    const int set_size;

public:

    enum { EMPTY, UNIVERSE, cell_size = sizeof(CELL) * CHAR_BIT };

    //
    // Produce the empty set.
    //
    void SetEmpty()
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] = 0;
    }

    //
    // Produce the universe set.
    //
    void SetUniverse()
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] = ~((CELL) 0);
    }

    //
    // This function takes as argument the size of a hash table, table_size.
    // It hashes a bitset into a location within the range <1..table_size-1>.
    //
    int Hash(int table_size)
    {
        unsigned long hash_address = 0;

        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            hash_address += s[i];
 
        return hash_address % table_size;
    }

    //
    // Assignment of a bitset to another.
    //
    BitSet& operator=(const BitSet& rhs)
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] = rhs.s[i];

        return *this;
    }

    //
    // Constructor of an uninitialized bitset.
    //
    BitSet(int set_size_) : set_size(set_size_)
    {
        //
        // Note that we comment out the -1 because some C++ compilers
        // do not know how to allocate an array of size 0. Note that
        // we assert that set_size >= 0.
        //
        s = new CELL[(set_size + cell_size /* - 1 */) / cell_size];
    }

    //
    // Constructor of an initialized bitset.
    //
    BitSet(int set_size_, int init) : set_size(set_size_)
    {
        //
        // Note that we comment out the -1 because some C++ compilers
        // do not know how to allocate an array of size 0. Note that
        // we assert that set_size >= 0.
        //
        s = new CELL[(set_size + cell_size /* - 1 */) / cell_size];
        if (init == UNIVERSE)
             SetUniverse();
        else SetEmpty();
    }

    //
    // Constructor to clone a bitset.
    //
    BitSet(const BitSet& rhs) : set_size(rhs.set_size)
    {
        //
        // Note that we comment out the -1 because some C++ compilers
        // do not know how to allocate an array of size 0. Note that
        // we assert that set_size >= 0.
        //
        s = new CELL[(set_size + cell_size /* - 1 */) / cell_size];
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] = rhs.s[i];
    }

    //
    // Destructor of a bitset.
    //
    ~BitSet() { delete [] s; }

    //
    // Return size of a bit set.
    //
    int Size() { return set_size; }

    //
    // Return a boolean value indicating whether or not the element i
    // is in the bitset in question.
    //
    int operator[](const int i)
    {
assert(i >= 0 && i < set_size);
        //
        // Note that no check is made here to ensure that 0 <= i < set_size.
        // Such a check might be useful for debugging and a range exception
        // should be thrown if it yields TRUE.
        //
        return s[i / cell_size] &
               ((i + cell_size) % cell_size
                         ? (CELL) 1 << ((i + cell_size) % cell_size)
                         : (CELL) 1);
    }

    //
    // Insert an element i in the bitset in question.
    //
    void AddElement(int i)
    {
assert(i >= 0 && i < set_size);
        s[i / cell_size] |= ((i + cell_size) % cell_size
                                             ? (CELL) 1 << ((i + cell_size) % cell_size)
                                             : (CELL) 1);
    }

    //
    // Remove an element i from the bitset in question.
    //
    void RemoveElement(int i)
    {
assert(i >= 0 && i < set_size);
        s[i / cell_size] &= ~((i + cell_size) % cell_size
                                              ? (CELL) 1 << ((i + cell_size) % cell_size)
                                              : (CELL) 1);
    }

    //
    // Yield a boolean result indicating whether or not two sets are
    // identical.
    //
    int operator==(const BitSet& rhs)
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
        {
            if (s[i] != rhs.s[i])
                return 0;
        }

        return 1;
    }

    //
    // Yield a boolean result indicating whether or not two sets are
    // identical.
    //
    int operator!=(const BitSet& rhs)
    {
        return ! (*this == rhs);
    }

    //
    // Union of two bitsets.
    //
    BitSet operator+(const BitSet& rhs)
    {
        BitSet result(set_size);

        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            result.s[i] = s[i] | rhs.s[i];

        return result;
    }

    //
    // Union of an lvalue bitset and a rhs bitset.
    //
    BitSet& operator+=(const BitSet& rhs)
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] |= rhs.s[i];

        return *this;
    }

    //
    // Intersection of two bitsets.
    //
    BitSet operator*(const BitSet& rhs)
    {
        BitSet result(set_size);

        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            result.s[i] = s[i] & rhs.s[i];

        return result;
    }

    //
    // Intersection of an lvalue bitset and a rhs bitset.
    //
    BitSet& operator*=(const BitSet& rhs)
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] &= rhs.s[i];

        return *this;
    }

    //
    // Difference of two bitsets.
    //
    BitSet operator-(const BitSet& rhs)
    {
        BitSet result(set_size);

        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            result.s[i] = s[i] & (~ rhs.s[i]);

        return result;
    }

    //
    // Difference of an lvalue bitset and a rhs bitset.
    //
    BitSet& operator-=(const BitSet& rhs)
    {
        for (int i = (set_size - 1) / cell_size; i >= 0; i--)
            s[i] &= (~ rhs.s[i]);

        return *this;
    }
};

class DefiniteAssignmentSet
{
public:
    int set_size;

    BitSet true_set,
           false_set;

    DefiniteAssignmentSet(int set_size_) : set_size(set_size_),
                                           true_set(set_size),
                                           false_set(set_size)
    {}
};
#endif