// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: ffiguard.h 630 2010-04-27 13:52:24Z Sebastian Kupferschmid $
//
//////////////////////////////////////////////////////////////////////

#ifndef FF_IGUARD_H
#define FF_IGUARD_H

#include "ff_heuristic/ffvar.h"
#include <inttypes.h>
#include <climits>
#include <vector>
#include <iosfwd>

namespace ff {
    class VarConstraint;
    class ConstConstraint;
    class NullConstraint;
    class RelaxedSolution;

    //////////////////////////////////////////////////////////////////////
    //     
    // An IntegerConstraint is either x comp y or x comp c, where x, y
    // are variables, c is a constant and comp is <, <=, == ,>=, > or
    // !=.
    // 
    //////////////////////////////////////////////////////////////////////
    
    class IntegerConstraint {
    public:
	enum comp_t { LT = 0, LE, EQ, GE, GT, NEQ };
	const int32_t id;
	const Variable* lhs;
	const comp_t comp;
    protected:
	static const int32_t not_activated = -1;
	static const int32_t no_witness = INT_MAX;

	int32_t activationLevel;	
	
	IntegerConstraint(const Variable* lhs, comp_t comp, int32_t id) : 
	    id(id),
	    lhs(lhs),
	    comp(comp),
	    activationLevel(not_activated)
	    {}	
    public:
	virtual ~IntegerConstraint() {}
	virtual witness_t getWitness(const Value& lhs, const Value& rhs) const;

	virtual std::ostream& display(std::ostream& o) const = 0;
	virtual bool isSatisfied() const = 0;
	virtual void setActivationLevel(int32_t level) { 
	    if (activationLevel == not_activated) {
		activationLevel = level; 
	    }
	}
	virtual int32_t getActivationLevel() const { return activationLevel; }
	virtual void init() { activationLevel = not_activated; }
	virtual void accept(RelaxedSolution* solution) const = 0;

	virtual bool equals(const IntegerConstraint*) const = 0;
	virtual bool equals(const VarConstraint*) const = 0;
	virtual bool equals(const ConstConstraint*) const = 0;
	virtual bool equals(const NullConstraint*) const = 0;
    };

    inline std::ostream& operator<<(std::ostream& o, const IntegerConstraint& ic) {
	return ic.display(o);
    }

    ////////////////////////////////////////////////////////////////////

    class VarConstraint : public IntegerConstraint {
    public:
	const Variable* rhs;

	VarConstraint(const Variable* lhs, const Variable* rhs, comp_t comp, int32_t id);

	virtual std::ostream& display(std::ostream& o) const;
	virtual void accept(RelaxedSolution* solution) const;
	
	virtual bool isSatisfied() const;
	virtual bool equals(const IntegerConstraint* c) const { return c->equals(this); } 
	virtual bool equals(const VarConstraint* c) const { 
	    return lhs->getID() == c->lhs->getID() && rhs->getID() == c->rhs->getID() && comp == c->comp;
	}
	virtual bool equals(const ConstConstraint*) const { return false; }
	virtual bool equals(const NullConstraint*) const { return false; }	
    };

    inline bool VarConstraint::isSatisfied() const {
	switch (comp) {
	case LT:  return lhs->lt(rhs);
	case LE:  return lhs->le(rhs);
	case EQ:  return lhs->eq(rhs);
	case GE:  return lhs->ge(rhs);
	case GT:  return lhs->gt(rhs);
	case NEQ: return lhs->neq(rhs);
	}
	assert(false);
	return true;
    }

    ////////////////////////////////////////////////////////////////////

    class ConstConstraint : public IntegerConstraint {
    public:
	const int32_t rhs;

    	ConstConstraint(const Variable* lhs, int32_t rhs, comp_t comp, int32_t id);
	virtual std::ostream& display(std::ostream& o) const;
	virtual witness_t getWitness(const Value& lhs, const Value& rhs) const;
	virtual void accept(RelaxedSolution* solution) const;
	virtual bool isSatisfied() const;
	virtual bool equals(const IntegerConstraint* c) const { return c->equals(this); } 
	virtual bool equals(const VarConstraint*) const { return false; }
	virtual bool equals(const ConstConstraint*c) const {
	    return lhs->getID() == c->lhs->getID() && comp == c->comp && rhs == c->rhs;
	}
	virtual bool equals(const NullConstraint*) const { return false; }
    };

    inline bool ConstConstraint::isSatisfied() const {
	switch (comp) {
	case LT:  return lhs->lt(rhs);
	case LE:  return lhs->le(rhs);
	case EQ:  return lhs->eq(rhs);
	case GE:  return lhs->ge(rhs);
	case GT:  return lhs->gt(rhs);
	case NEQ: return lhs->neq(rhs);
	}
	assert(false);
	return true;
    }

    ////////////////////////////////////////////////////////////////////

    class NullConstraint : public IntegerConstraint {
    public:
    	NullConstraint(int32_t id);
	virtual std::ostream& display(std::ostream& o) const;
	virtual bool isSatisfied() const;
	virtual witness_t getWitnesses(const Value&, const Value&) const {
	    return make_pair(no_witness, no_witness); 
	}
	virtual void accept(RelaxedSolution* solution) const;
	virtual bool equals(const IntegerConstraint* ic) const { return ic->equals(this); } 
	virtual bool equals(const VarConstraint*) const { return false; }
	virtual bool equals(const ConstConstraint*) const { return false; }
	virtual bool equals(const NullConstraint*) const { return true; }
    }; 

    inline bool NullConstraint::isSatisfied() const { 
	return true; 
    }

    ////////////////////////////////////////////////////////////////////

    class IntegerGuard {
    private:
	mutable std::vector<IntegerConstraint*> constraints;
	mutable uint32_t firstUnsat;
    public:
	IntegerGuard();
	~IntegerGuard();
	
	void init() { firstUnsat = 0; }
	void addConstraint(IntegerConstraint* ic);
	bool isSatisfied(int32_t level) const {
	    while (firstUnsat < constraints.size() && constraints[firstUnsat]->isSatisfied()) {
		constraints[firstUnsat]->setActivationLevel(level);
		firstUnsat++;
	    }
	    return firstUnsat == constraints.size();
	}	
	bool empty() const { return constraints.empty(); }
	uint32_t size() const { return constraints.size(); }
	const IntegerConstraint* operator[](int i) const {
	    return constraints[i];
	}
	const std::vector<IntegerConstraint*>& getConstraints() const { return constraints; }
	std::ostream& display(std::ostream& o) const;
    };
       
    inline std::ostream& operator<<(std::ostream& o, const IntegerGuard& ig) {
	return ig.display(o);
    }

    std::ostream& operator<<(std::ostream& o, IntegerConstraint::comp_t c);
}

#endif /* FF_IGUARD_H */
