/*******************************************************************************
 * Copyright (c) 2015-2017 ONERA and IRT AESE (IRT Saint Exupéry).
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab) et de
 * l'IRT AESE (IRT Saint Exupéry).
 * Tous droits réservés.
 *
 * Ce programme et les éléments qui l'accompagnent sont mis à disposition
 * aux conditions définies par le contrat de licence logiciel CeCILL-C soumise
 * au droit français et respectant les principes de diffusion des logiciels libres.
 * Vous pouvez utiliser, modifier et/ou redistribuer ce programme
 * sous les conditions de la licence CeCILL-C (http://www.cecill.info).

 * This software is property of ONERA (the French Aerospace Lab) and of
 * the IRT AESE (IRT Saint Exupéry).
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under
 * the terms of the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.
 * You can  use, modify and/ or redistribute the software under the terms of
 * the CeCILL-C license (http://www.cecill.info).
 *
 * Contributeurs/contributors:
 *     Guillaume Infantes (ONERA - Centre de Toulouse) - initial API and implementation
 *     Alexandre Albore (IRT Saint-Exupéry) - Altarica/Fiacre translation
 *
 *******************************************************************************/

#include "ValueReal.hh"
#include "ValueInt.hh"
#include <limits>
#include <sstream>
#include "../domains/RealDomain.hh"
#include <boost/functional/hash.hpp>
#include <boost/icl/continuous_interval.hpp>
#include <boost/icl/interval_set.hpp>

using namespace epoch::altarica;
using namespace boost::icl;


ValueReal::~ValueReal() {
}





ValueReal * ValueReal::clone() const {
    return new ValueReal(d_, values_);
}

void ValueReal::clear() {
    values_.clear();
    if (d_ == NULL) {
	values_.insert(interval<double>::closed(std::numeric_limits<double>::min(), std::numeric_limits<double>::max()));
    } else {
	values_.insert(continuous_interval<double>(d_->getBI()));
    }
}

void ValueReal::inverse() {
    RealIntervals newValues;
    for (RealIntervals::iterator i = values_.begin(); i!= values_.end(); ++i) {
	if (i->bounds() == interval_bounds::open() || i->bounds() == interval_bounds::closed())
	    newValues += continuous_interval<double>(-upper(*i), -lower(*i), i->bounds());
	else 
	    newValues += continuous_interval<double>(-upper(*i), -lower(*i), i->bounds().reverse_right().reverse_left());
    }
    values_.clear();
    values_ = newValues;
    // rehash_();
}



ValueReal::ValueReal(): Value() {
    values_ += continuous_interval<double>(std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), interval_bounds::open());
    d_ = NULL; 
}

ValueReal::ValueReal(const RealDomain *d, const RealIntervals& ri) : Value() {
    values_ = RealIntervals(ri);
    d_ = d;
}

ValueReal::ValueReal(ValueInt *vi) : Value() {
    d_ = new RealDomain(vi->getDomain());
    for (std::vector<IntInterval*>::const_iterator i = vi->getValues()->begin(); i!= vi->getValues()->end(); ++i) {
	addValues((*i)->min, (*i)->max);
    }
}



ValueReal::ValueReal(const RealDomain *d): Value() {
    d_ =d;
    if (d_!= NULL)
	values_ = RealIntervals(d->getBI());
    else
	values_ += continuous_interval<double>(std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), interval_bounds::open());
}

ValueReal::ValueReal(double d):Value() {
    d_ = NULL; //new PredefinedDomain(false);
    values_ += continuous_interval<double>(d, d, interval_bounds::closed());
}

void ValueReal::setValue (double d) {
    values_.clear();
    values_ += continuous_interval<double>(d, d, interval_bounds::closed());
}



double ValueReal::toDouble() const {
    if (lower(values_) != upper(values_))
	return std::numeric_limits<double>::min();
    return lower(values_);
}

// void ValueInt::rehash_() {
//     h_ =0;
//     boost::hash<int> int_hash;
//     for (std::vector<IntInterval*>::const_iterator iii = values_->begin(); iii!= values_->end(); ++iii) {
// 	boost::hash_combine(h_, int_hash((*iii)->min));
// 	boost::hash_combine(h_, int_hash((*iii)->max));
//     }
// }

int ValueReal::toHash() const {   
    std::size_t h_ =0;
    boost::hash<double> double_hash;
    for (RealIntervals::iterator iii = values_.begin(); iii!= values_.end(); ++iii) {
	boost::hash_combine(h_, double_hash(iii->lower()));
	boost::hash_combine(h_, double_hash(iii->upper()));
    }
    return (int)h_;
}




bool ValueReal::equals(const ValueReal*v) const {
    return includes(v) && v->includes(this);
}



void ValueReal::merge(const ValueReal*v) {
    values_ += v->getValues();
}

bool ValueReal::contains(const ValueReal *fi) const {
    return ::boost::icl::contains(values_,fi->getValues());
}


bool ValueReal::includes(const ValueReal *ii) const {
    return ::boost::icl::contains(values_,ii->getValues());
}



// bool ValueReal::isIncluded(RealInterval*ii) const {
//     for (std::vector<RealInterval*>::iterator iiv =values_->begin(); iiv != values_->end(); ++iiv) {
// 	if ((*iiv)->min < ii->min)
// 	    return false;
// 	if ((*iiv)->max > ii->max)
// 	    return false;
//     }
//     return true;
// }

// bool ValueReal::includes(std::vector<RealInterval*>*v) const {
//     for (std::vector<RealInterval*>::iterator vi = v->begin(); vi != v->end(); ++vi) {
// 	if (!includes(*vi))
// 	    return false;
//     }
//     return true;
// }


// bool ValueReal::isIncluded(std::vector<RealInterval*>*v) const {
//     for (std::vector<RealInterval*>::iterator iiv = v->begin(); iiv!=v->end(); ++iiv) {
// 	if (isIncluded(*iiv))
// 	    return true;
//     }
//     return false;
// }

void ValueReal::setDomain (const Domain *d) {
    // if (d_ != NULL)
    // 	delete d_;
    d_ = (RealDomain*) d;
}

void ValueReal::addValues(double min, double max, bool openright, bool openleft) {
    if (openright) {
	if (openleft) {
	    values_ += continuous_interval<double>(min,max, interval_bounds::open());	    
	} else {
	    values_ += continuous_interval<double>(min,max, interval_bounds::right_open());	    	    
	}
    } else {
	if (openleft) {
	    values_ += continuous_interval<double>(min,max, interval_bounds::left_open());	    
	}  else {
	    values_ += continuous_interval<double>(min,max, interval_bounds::closed());	    
	}	
    }
}

// const std::vector<RealInterval*> * ValueReal::getValues() const {
//     return values_;
// }


// Value * ValueInt::intersectValue(Value *v) const {    
//      v->intersectValue(this);
//      return v;
// }




void ValueReal::removeValues(double min, double max, bool openright, bool openleft) {
    if (openright) {
	if (openleft) {
	    values_ -= continuous_interval<double>(min,max, interval_bounds::open());	    
	} else {
	    values_ -= continuous_interval<double>(min,max, interval_bounds::right_open());	    	    
	}
    } else {
	if (openleft) {
	    values_ -= continuous_interval<double>(min,max, interval_bounds::left_open());	    
	}  else {
	    values_ -= continuous_interval<double>(min,max, interval_bounds::closed());	    
	}	
    }    
}

void ValueReal::removeValue(const ValueReal * v) {
    values_ -= v->getValues();;
}


void ValueReal::intersectValue(const ValueReal* v) {
    values_ &= v->getValues();
}



void ValueReal::add(const ValueReal *v) {
    RealIntervals newValues;
    
    for (RealIntervals::iterator i = values_.begin(); i!= values_.end(); ++i) {
	for (RealIntervals::iterator j = v->getValues().begin(); j!= v->getValues().end(); ++j) {
	    bool lo = i->bounds() == interval_bounds::left_open() 
		||    j->bounds() == interval_bounds::left_open() 
		||    j->bounds() == interval_bounds::open() 
		||    i->bounds() == interval_bounds::open() ;
	    bool ro = i->bounds() == interval_bounds::right_open() 
		||    j->bounds() == interval_bounds::right_open() 
		||    j->bounds() == interval_bounds::open() 
		||    i->bounds() == interval_bounds::open() ;
	    interval_bounds b;
	    if (lo) {
		if (ro) {
		    b = interval_bounds::open();
		} else {
		    b = interval_bounds::left_open();
		}
	    } else {
		if  (ro) {
		    b = interval_bounds::right_open();
		} else {
		    b = interval_bounds::closed();
		}
	    }
		
	    newValues += continuous_interval<double>(i->lower()+j->lower(), i->upper()+j->upper(),  b);
	}	
    }
    values_ = newValues;
}


void ValueReal::minus(const ValueReal *i) {
    ValueReal *mi = i->clone();
    mi->inverse();
    add(mi);
    delete mi;
}





void ValueReal::times(const ValueReal *i) {
    double ii = i->toDouble();
    RealIntervals newValues;
    if (i->toDouble() ==  std::numeric_limits<double>::min())
	BOOST_THROW_EXCEPTION(std::runtime_error("trying to times by an openreal that is not a simple int..."));
    else {
	for (RealIntervals::iterator iii = values_.begin(); iii != values_.end(); ++iii) {
	    newValues += continuous_interval<double>(iii->lower() * ii, iii->upper() *ii, iii->bounds());
	}
    }    
    values_ = newValues;
    // rehash_();
}

void ValueReal::div(const ValueReal *i) {
    double  ii = i->toDouble();
    RealIntervals newValues;
    if (ii == std::numeric_limits<double>::min())
	BOOST_THROW_EXCEPTION(std::runtime_error("trying to div by  an openreal that is not a simple real..."));
    else {
	for (RealIntervals::iterator iii = values_.begin(); iii != values_.end(); ++iii) {
	    newValues += continuous_interval<double>(iii->lower() / ii, iii->upper() /ii, iii->bounds());
	}	
    }    
    values_ = newValues;
}

void ValueReal::mod(const ValueReal *i) {
    BOOST_THROW_EXCEPTION(std::runtime_error("trying to mod by realvalues..."));
}

double ValueReal::max() const {
    return upper(values_);
}

double ValueReal::min() const {
    return lower(values_);
}

int ValueReal::card() const {
    if (lower(values_) == upper(values_))
	return 1;
    return std::numeric_limits<int>::max();
}


void ValueReal::realUnion(ValueReal * o)  {
    values_ += o->getValues();
}




ValueReal* ValueReal::getComplement() const {
    ValueReal * n = new ValueReal(d_);
    n->removeValue(this);
    return n;
}


void ValueReal::setToInfThan(ValueReal*o) {   
    if (o->getValues().begin()->bounds() == interval_bounds::left_open() 
	|| o->getValues().begin()->bounds() == interval_bounds::open() )
	values_ -= continuous_interval<double>(o->min(), std::numeric_limits<double>::max(), interval_bounds::open());
    else
	values_ -= continuous_interval<double>(o->min(), std::numeric_limits<double>::max(), interval_bounds::right_open());
}

void ValueReal::setToSupThan(ValueReal*o) {
    if (o->getValues().end()->bounds() == interval_bounds::right_open() 
	|| o->getValues().end()->bounds() == interval_bounds::open() )
	values_ -= continuous_interval<double>(std::numeric_limits<double>::min(), o->max(), interval_bounds::open());
    else
	values_ -= continuous_interval<double>(std::numeric_limits<double>::min(), o->max(), interval_bounds::right_open());
}

void ValueReal::setToInfEqThan(ValueReal*o) {
    values_ -= continuous_interval<double>(o->min(), std::numeric_limits<double>::max(), interval_bounds::open());
}

void ValueReal::setToSupEqThan(ValueReal*o) {
    values_ -= continuous_interval<double>(std::numeric_limits<double>::min(), o->max(), interval_bounds::open());
}

std::vector<Value*> * ValueReal::getSingletons() const {
    std::vector<Value*> * v = new std::vector<Value*>();
    for (RealIntervals::iterator i = values_.begin(); i!=values_.end(); ++i) {
	if (i->lower() != i->upper() || i->bounds() != interval_bounds::closed())
	    BOOST_THROW_EXCEPTION(std::runtime_error("trying to div by  an openreal that is not a simple real..."));
	else
	    v->push_back(new ValueReal(i->lower()));
    }
    return v;
}
    
bool ValueReal::hasIntersection(const ValueReal*v) const {  
    intersects(values_,v->getValues());
}

std::string ValueReal::toString() const {
    //    std::string s;
    std::stringstream ss;
    ss<< values_;
    return ss.str();
}

std::string ValueReal::toFiacre() const {
    //    std::string s;
    std::stringstream ss;
    ss<< getMin() << "," << getMax();
    return ss.str();
}

int ValueReal::getMin() const {
    return lower(values_);
}


int ValueReal::getMax() const {
    return upper(values_);
}



bool ValueReal::less(const ValueReal *v) const {
    return getMin() < v->getMin();
}




bool ValueReal::pDifferent(const ValueReal *v) const {
    ValueReal * c = v->getComplement();
    bool b = hasIntersection(c);
    delete (c);
    return b;
}
