/*****************************************************************************/
/*!
 * \file theorem.h
 * 
 * Author: Sergey Berezin
 * 
 * Created: Dec 10 00:37:49 GMT 2002
 *
 * <hr>
 *
 * License to use, copy, modify, sell and/or distribute this software
 * and its documentation for any purpose is hereby granted without
 * royalty, subject to the terms and conditions defined in the \ref
 * LICENSE file provided with this distribution.
 * 
 * <hr>
 * 
 */
/*****************************************************************************/
// CLASS: Theorem
//
// AUTHOR: Sergey Berezin, 07/05/02
//
// Abstract:
//
// A class representing a proven fact in CVC.  It stores the theorem
// as a CVC expression, and in the proof prodicing mode also its
// proof.
//
// The idea is to allow only a few trusted classes to create values of
// this class.  If all the critical computations in the decision
// procedures are done through the use of Theorems, then soundness of
// these decision procedures will rely only on the soundness of the
// methods in the trusted classes (the inference rules).
//
// Thus, proof checking can effectively be done at run-time on the
// fly.  Or the soundness may be potentially proven by static analysis
// and many run-time checks can then be optimized away.
//
// This theorem.h file should be used by the decision procedures that
// use Theorem.
//
////////////////////////////////////////////////////////////////////////

// expr.h Has to be included outside of #ifndef, since it sources us
// recursively (read comments in expr_value.h).
#ifndef _cvc3__expr_h_
#include "expr.h"
#endif

#ifndef _cvc3__theorem_h_
#define _cvc3__theorem_h_

#include "os.h"
#include "proof.h"
#include "assumptions.h"

namespace CVC3 {

  // Declare the data holding classes but hide the definitions
  class TheoremManager;
  class TheoremValue;
  class Assumptions;

  // Theorem is basically a wrapper around a pointer to a
  // TheoremValue, so that we can pass this class around by value.
  // All the constructors of this class are private, so do not inherit
  // from it and do not try to create a value directly.  Only
  // TheoremProducer can create new Theorem instances.
  //
  // Theorems, unlike expressions, are NOT made unique, and it is
  // possible to have the same theorem in different scopes with
  // different assumptions and proofs.  It is a deliberate feature,
  // since natural deduction sometimes requires proving the same
  // conclusion from different assumptions independently, e.g. in
  // disjunction elimination rule.
  class CVC_DLL Theorem {
  private:
    // Make a theorem producing class our friend.  No definition is
    // exposed here.
    friend class TheoremProducer;
    // Also allow our 3-valued cousin to create us
    friend class Theorem3;
    // Also TheoremValue classes for assumptions
    friend class RegTheoremValue;
    friend class RWTheoremValue;

    // Optimization: reflexivity theorems just store the exprValue pointer
    // directly.  Also, the lowest bit is set to 1 to indicate that its
    // a reflexivity theorem.  This really helps performance!
    union {
      intptr_t d_thm;
      ExprValue* d_expr;
    };

    //! Compare Theorems by their expressions.  Return -1, 0, or 1.
    friend int compare(const Theorem& t1, const Theorem& t2);
    //! Compare a Theorem with an Expr (as if Expr is a Theorem)
    friend int compare(const Theorem& t1, const Expr& e2);
    //! Compare Theorems by TheoremValue pointers.  Return -1, 0, or 1.
    friend int compareByPtr(const Theorem& t1, const Theorem& t2);
    //! Equality is w.r.t. compare()
    friend bool operator==(const Theorem& t1, const Theorem& t2) 
      { return (compare(t1, t2)==0); }
    //! Disequality is w.r.t. compare()
    friend bool operator!=(const Theorem& t1, const Theorem& t2)
      { return (compare(t1, t2) != 0); }

    //! Constructor only used by TheoremValue for assumptions
    Theorem(TheoremValue* thm) :d_thm(((intptr_t)thm) | 0x1) {}

    //! Constructor for a new theorem 
    Theorem(TheoremManager* tm, const Expr &thm, const Assumptions& assump,
            const Proof& pf, bool isAssump = false, int scope = -1);

    //! Constructor for rewrite theorems.
    /*! These use a special efficient subclass of TheoremValue for
     * theorems which represent rewrites: A |- t = t' or A |- phi<=>phi'
     */
    Theorem(TheoremManager* tm, const Expr& lhs, const Expr& rhs,
	    const Assumptions& assump, const Proof& pf, bool isAssump = false,
            int scope = -1);

    //! Constructor for a reflexivity theorem: |-t=t or |-phi<=>phi
    Theorem(const Expr& e);

    void recursivePrint(int& i) const;
    void getAssumptionsRec(std::set<Expr>& assumptions) const;

    ExprValue* exprValue() const { return d_expr; }
    TheoremValue* thm() const { return (TheoremValue*)(d_thm & (~(0x1))); }

  public:
    // recusive function to print theorems and all assumptions recursively
    // important: this function will corrupt all flags!! so exercise 
    // caution when using in any graph traversals 
    // (probably more useful to call it only before a crash)
    void printDebug() const { 
      clearAllFlags();
      setCachedValue(0);
      setFlag();
      int i = 1; 
      recursivePrint(i);
    }

    // Default constructor creates Null theorem to allow untrusted
    // code declare local vars without initialization (vector<Theorem>
    // may need that, for instance).
    Theorem(): d_thm(0) { }
    // Copy constructor
    Theorem(const Theorem &th);
    // Assignment operator
    Theorem& operator=(const Theorem &th);

    // Destructor
    ~Theorem();

    // Test if we are running in a proof production mode and with assumptions
    bool withProof() const;
    bool withAssumptions() const;

    bool isNull() const { return d_thm == 0; }

    // True if theorem is of the form t=t' or phi iff phi'
    bool isRewrite() const;
    // True if theorem was created using assumpRule
    bool isAssump() const;
    // True if reflexivity theorem
    bool isRefl() const { return d_thm && !(d_thm & 0x1); }
    
    // Return the theorem value as an Expr
    Expr getExpr() const;
    const Expr& getLHS() const;
    const Expr& getRHS() const;

    // Return the assumptions.  a should be empty and uninitialized
    //    void getAssumptions(Assumptions& a) const;
    // Recurse to get actual assumptions
    void getLeafAssumptions(std::vector<Expr>& assumptions,
                            bool negate = false) const;
    const Assumptions& getAssumptionsRef() const;
    // Return the proof of the theorem.  If running without proofs,
    // return the Null proof.
    Proof getProof() const;
    // Return the lowest scope level at which this theorem is valid.
    // Value -1 means no information is available.
    int getScope() const;
    //! Return quantification level for this theorem
    unsigned getQuantLevel() const;
    //! Set the quantification level for this theorem
    void setQuantLevel(unsigned level);

    // hash
    size_t hash() const;

    // Printing
    std::string toString() const;

    // For debugging
    void printx() const;
    void printxnodag() const;
    void pprintx() const;
    void pprintxnodag() const;
    
    void print() const;

    /*! \name Methods for Theorem Attributes
     *
     * Several attributes used in conflict analysis and assumptions
     * graph traversals.
     * @{
     */
    //! Check if the flag attribute is set
    bool isFlagged() const;
    //! Clear the flag attribute in all the theorems
    void clearAllFlags() const;
    //! Set the flag attribute
    void setFlag() const;

    //! Set the "expand" attribute
    void setExpandFlag(bool val) const;
    //! Check the "expand" attribute
    bool getExpandFlag() const;
    //! Set the "literal" attribute
    /*! The expression of this theorem will be added as a conflict
     * clause literal */
    void setLitFlag(bool val) const;
    //! Check the "literal" attribute
    /*! The expression of this theorem will be added as a conflict
     * clause literal */
    bool getLitFlag() const;
    //! Check if the theorem is a literal
    bool isAbsLiteral() const;

    bool refutes(const Expr& e) const
    {
      return
	(e.isNot() && e[0] == getExpr()) ||
	(getExpr().isNot() && getExpr()[0] == e);
    }

    bool proves(const Expr& e) const
    {
      return getExpr() == e;
    }

    bool matches(const Expr& e) const
    {
      return proves(e) || refutes(e);
    }

    void setCachedValue(int value) const;
    int getCachedValue() const;
    
    /*!@}*/ // End of Attribute methods

    //! Printing a theorem to a stream, calling it "name".
    std::ostream& print(std::ostream& os, const std::string& name) const;
    
    friend std::ostream& operator<<(std::ostream& os, const Theorem& t) {
      return t.print(os, "Theorem");
    }

    static bool TheoremEq(const Theorem& t1, const Theorem& t2) 
    { 
      DebugAssert(!t1.isNull() && !t2.isNull(), 
                  "AssumptionsValue() Null Theorem passed to constructor");
      return t1 == t2;
    }
  };  // End of Theorem

/*****************************************************************************/
/*!
 *\class Theorem3
 *\brief Theorem3
 *
 * Author: Sergey Berezin
 *
 * Created: Tue Nov  4 17:57:07 2003
 *
 * Implements the 3-valued theorem used for the user assertions and
 * the result of query.  It is simply a wrapper around class Theorem,
 * but has a different semantic meaning: the formula may have partial
 * functions and has the Kleene's 3-valued interpretation.  The fact
 * that a Theorem3 value is derived means that the TCCs for the
 * formula and all of its assumptions are valid in the current
 * context, and the proofs of TCCs contribute to the set of
 * assumptions.
 */
/*****************************************************************************/
  class Theorem3 {
  private:
    // Make a theorem producing class our friend.  No definition is
    // exposed here.
    friend class TheoremProducer;

    Theorem d_thm;

    friend bool operator==(const Theorem3& t1, const Theorem3& t2) {
      return t1.d_thm == t2.d_thm;
    }
    friend bool operator!=(const Theorem3& t1, const Theorem3& t2) {
      return t1.d_thm != t2.d_thm;
    }


    // Private constructors for a new theorem 
    Theorem3(TheoremManager* tm, const Expr &thm, const Assumptions& assump,
             const Proof& pf, bool isAssump = false, int scope = -1)
      : d_thm(tm, thm, assump, pf, isAssump, scope) { }

    // Constructors for rewrite theorems.  These use a special efficient
    // subclass of TheoremValue for theorems which represent rewrites:
    // A |- t = t' or A |- phi iff phi'
    Theorem3(TheoremManager* tm, const Expr& lhs, const Expr& rhs,
	     const Assumptions& assump, const Proof& pf)
      : d_thm(tm, lhs, rhs, assump, pf) { }

  public:
    // recusive function to print theorems and all assumptions recursively
    // important: this function will corrupt all flags!! so exercise 
    // caution when using in any graph traversals 
    // (probably more useful to call it only before a crash)
    void printDebug() const { d_thm.printDebug(); }

    // Default constructor creates Null theorem to allow untrusted
    // code declare local vars without initialization (vector<Theorem>
    // may need that, for instance).
    Theorem3() { }

    // Destructor
    virtual ~Theorem3() { }

    bool isNull() const { return d_thm.isNull(); }

    // True if theorem is of the form t=t' or phi iff phi'
    bool isRewrite() const { return d_thm.isRewrite(); }
    bool isAssump() const { return d_thm.isAssump(); }
    
    // Return the theorem value as an Expr
    Expr getExpr() const { return d_thm.getExpr(); }
    const Expr& getLHS() const { return d_thm.getLHS(); }
    const Expr& getRHS() const { return d_thm.getRHS(); }

    // Return the assumptions.
    // It's an error if called while running without assumptions.
    //    void getAssumptions(Assumptions& a) const { d_thm.getAssumptions(a); }
    const Assumptions& getAssumptionsRef() const {
      return d_thm.getAssumptionsRef();
    }
    // Return the proof of the theorem.  If running without proofs,
    // return the Null proof.
    Proof getProof() const { return d_thm.getProof(); }

    // Return the lowest scope level at which this theorem is valid.
    // Value -1 means no information is available.
    int getScope() const { return d_thm.getScope(); }

    // Test if we are running in a proof production mode and with assumptions
    bool withProof() const { return d_thm.withProof(); }
    bool withAssumptions() const { return d_thm.withAssumptions(); }

    // Printing
    std::string toString() const;

    // For debugging
    void printx() const { d_thm.printx(); }
    void print() const { d_thm.print(); }

    //! Check if the theorem is a literal
    bool isAbsLiteral() const { return d_thm.isAbsLiteral(); }

    friend std::ostream& operator<<(std::ostream& os, const Theorem3& t) {
      return t.d_thm.print(os, "Theorem3");
    }
  };  // End of Theorem3

  //! "Less" comparator for theorems by TheoremValue pointers
  class TheoremLess {
  public:
    bool operator()(const Theorem& t1, const Theorem& t2) const {
      return (compareByPtr(t1, t2) < 0);
    }
  };
  typedef std::map<Theorem,bool, TheoremLess> TheoremMap;

  inline std::string Theorem::toString() const {
    std::ostringstream ss;
    ss << (*this);
    return ss.str();
  }

  inline std::string Theorem3::toString() const {
    std::ostringstream ss;
    ss << (*this);
    return ss.str();
  }

  // Merge assumptions from different theorems
//   inline Assumptions merge(const Theorem& t1, const Theorem& t2) {
//     return Assumptions(t1, t2);
//   }
//   inline void merge(Assumptions& a, const Theorem& t) {
//     a.add(t);
//   }
//   inline Assumptions merge(const std::vector<Theorem>& t) {
//     return Assumptions(t);
//   }

  inline bool operator<(const Theorem& t1, const Theorem& t2)
    { return compare(t1, t2) < 0; }
  inline bool operator<=(const Theorem& t1, const Theorem& t2)
    { return compare(t1, t2) <= 0; }
  inline bool operator>(const Theorem& t1, const Theorem& t2)
    { return compare(t1, t2) > 0; }
  inline bool operator>=(const Theorem& t1, const Theorem& t2)
    { return compare(t1, t2) >= 0; }

} // end of namespace CVC3

#include "hash_fun.h"
namespace Hash
{

template<> struct hash<CVC3::Theorem>
{
  size_t operator()(const CVC3::Theorem& e) const { return e.hash(); }
};

}

#endif
