/*****************************************************************************

  CSE 235 - Discrete Mathematics
   Fall 2008
   relation.h - header file for relation ADT

    You may add any member functions that you wish
    but DO NOT modify the naming conventions or method
    parameters already present here.  You may write
    additional classes/structures as you wish, but
    be sure to use good OOP style and include them in
    your makefile.


*****************************************************************************/

#include <iostream>
#include <fstream>
using namespace std;

class relation
{
   private:
   
    //-----------------------------------------------------------------------
    // Empty an existing relation, allocates memory for a relation 
    // on a set of size n
    void init_relation(int n);
    
   public:

    // The default constructor.  Constructs a relation on the empty set.
    relation(); 

    // The destructor.  deallocates dynamic memory.
    ~relation();

    // Constructor that generates an empty relation on a set of size n
    // A = {a_0, a_1, ..., a_n-1}
    // Note that we will be indexing from 0 to n-1
    relation(int n);

    // The copy constructor.
    relation(const relation &S);

    // Overloaded assignment operator.
    relation& operator =(const relation &S);

    //-----------------------------------------------------------------------
    // This should return n, the number of elements in the set the relation
    // is defined on (i.e. *not* the number of valid relations)
    int SetSize() const;

    //-----------------------------------------------------------------------
    // These functions return true if the relation has the 
    // respective properties
    bool isReflexive() const;
    bool isSymmetric() const;
    bool isAsymmetric() const;
    bool isAntisymmetric() const;
    bool isTransitive() const;
    bool isPartialOrder() const;
    bool isEquivalenceRelation() const;
    
    //-----------------------------------------------------------------------
    // This should return true if a_i is related to a_j and false otherwise
    // including if it is out of range.
    // 0 <= i,j <= n-1
    bool isRelated(int i, int j) const;

    //-----------------------------------------------------------------------
    // This makes a_i related to a_j they are not already
    // 0 <= i,j <= n-1
    void AddRelation(int i, int j);
    
    //-----------------------------------------------------------------------
    // This takes away the relation between a_i and a_j if they are
    void DeleteRelation(int i, int j);
    
    //-----------------------------------------------------------------------
    // This prints the relation to stdout
    void Print();

    //-----------------------------------------------------------------------
    // Overload the >> operator (Read a relation from a file)            
    // This method has been done for you, but you will need to implement    
    // a similar method for overloading the << operator         
    // The format of the flat file is:                      
    //      n                           
    //      0 (list)
    //      1 (list)
    //       ...
    //      n-1 (list)
    //
    // where :
    // 'n' is the size of the set (number of elements) which is followed by n lines
    // each line starts with an integer a representing a unique element in the set, 
    // {a_0, a_1, a_2, ..., a_n-1}
    // which is followed by a list of integers between 0 and n-1 that a_i is related to
    //
    // Example: the file
    // 5
    // 0 0 1 2
    // 1 3 1
    // 2 3 4
    // 3 3 4
    // 4  
    // represents the relation {(0,0), (0,1), (0,2), (1,1), (1,3), (2,3), (2,4), (3,3), (3,4)}
    // 

    friend istream& operator >>(istream &in, relation &S);

    //-----------------------------------------------------------------------
    // Similar to the overloaded >> above, but this takes as an argument    
    // the filename, so the details, like the filestreams, etc,         
    // are left to the class.                       
    // So if R is of type relation, a user can do:               
    //      R.read("blah").                         
    // and it will read a relation in from file "blah".                  

    void read(char *thefile);

    //-----------------------------------------------------------------------
    // Overload the << operator                     
    // Write a relation to a file in the same format as 'read' above.        

    friend ostream& operator <<(ostream &out, const relation &R);

    //-----------------------------------------------------------------------
    // Similar to the overloaded << above, but this takes as an argument    
    // the filename, so the details, like the filestreams, etc,         
    // are left to the class.                       

    void write(char *thefile) const;
    
};

// These return a (pointer to a) relation object that is the 
// Reflexive/Symmetric/Transitive Closure;Union/Intersection of the passed relation(s)
// IF AN OPERATION IS INVALID, RETURN AN EMPTY RELATION
relation *ReflexiveClosure(relation *A);
relation *SymmetricClosure(relation *A);
relation *TransitiveClosure(relation *A);
relation *Union(relation *A, relation *B);
relation *Intersection(relation *A, relation *B);

