/*******************************************************************************
 * 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 "ValueInt.hh"
#include "ValueReal.hh"
#include <stdint.h>
#include <sstream>
#include "../domains/PredefinedDomain.hh"
#include <boost/functional/hash.hpp>

using namespace epoch::altarica;



ValueInt::~ValueInt() {
  for (std::vector<IntInterval*>::iterator i = values_->begin();
       i!= values_->end(); ++i)
    delete *i;
  delete(values_);
}



ValueInt * ValueInt::clone() const {
  ValueInt* n;
  if (d_ != NULL)
    n = new ValueInt(d_);
  else
    n = new ValueInt();
  n->clear();
  for (std::vector<IntInterval*>::iterator vi = values_->begin(); vi != values_->end(); ++vi)
    n->addValues((*vi)->min, (*vi)->max);
  n->setConstrained(constrained_);
  return n;
}

void ValueInt::clear() {
  for (std::vector<IntInterval*>::iterator i = values_->begin(); i!=values_->end(); ++i)
    delete *i;
  values_->clear();
  // rehash_();
}

void ValueInt::inverse() {
  for (std::vector<IntInterval*>::iterator i = values_->begin(); i != values_->end(); ++i) {
    int b = (*i)->min;
    (*i)->min = -(*i)->max;
    (*i)->max = -b;
  }
  // rehash_();
}



ValueInt::ValueInt(): Value() {
  values_ = new std::vector<IntInterval*>();
  d_ = NULL; //new PredefinedDomain(false);
  IntInterval * ii = new IntInterval();
  //    PredefinedDomain * pd = new PredefinedDomain(false);
  ii->max = PredefinedDomain::max(false);
  ii->min = PredefinedDomain::min(false);
  //delete pd;
  values_->push_back(ii);
  // rehash_();
}
ValueInt::ValueInt(const DiscreteDomain *d): Value() {
  d_ =d;
  values_ = new std::vector<IntInterval*>();
  IntInterval * ii = new IntInterval();
  if (d_ != NULL) {
    ii->max = d->getMax();
    ii->min = d->getMin();
  } else {
    ii->max = PredefinedDomain::max(false);
    ii->min = PredefinedDomain::min(false);
  }

  values_->push_back(ii);
  // rehash_();
}

ValueInt::ValueInt(int i):Value() {
  d_ = NULL; //new PredefinedDomain(false);
  values_ = new std::vector<IntInterval*>();
  IntInterval * ii = new IntInterval();
  ii->max = i;
  ii->min = i;
  values_->push_back(ii);
  // rehash_();
}

void ValueInt::setValue (int i) {
  IntInterval * ii = new IntInterval();
  clear();
  ii->max = ii->min = i;
  values_->push_back(ii);
  // rehash_();
}



int ValueInt::toInt() const {
  if (values_->size() != 1)
    return INT32_MIN;
  if ((*values_)[0]->min != (*values_)[0]->max)
    return INT32_MIN;
  return (*values_)[0]->min;
}

double ValueInt::toDouble() const {
  return toInt();
}

// 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 ValueInt::toHash() const {
  std::size_t 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));
  }
  return (int)h_;
}




bool ValueInt::equals(const ValueInt*v) const {
  return v->includes(values_) && v->isIncluded(values_);
}



void ValueInt::merge(const ValueInt*v) {
  for (std::vector<IntInterval*>::const_iterator i = v->getValues()->begin(); i!= v->getValues()->end(); ++i)
    addValues((*i)->min, (*i)->max);
  // rehash_();
}

bool ValueInt::contains(const ValueInt *fi) const {
  int fii = fi->toInt();
  for (std::vector<IntInterval*>::const_iterator i = values_->begin(); i!= values_->end(); ++i) {
    if (fii > (*i)->min && fii<(*i)->max)
      return true;
  }
  return false;
}


bool ValueInt::includes(IntInterval *ii) const {
  for (std::vector<IntInterval*>::iterator iiv =values_->begin(); iiv != values_->end(); ++iiv) {
    if ((*iiv)->min <= ii->min && (*iiv)->max >= ii->max)
      return true;
  }
  return false;
}



bool ValueInt::isIncluded(IntInterval*ii) const {
  for (std::vector<IntInterval*>::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 ValueInt::includes(std::vector<IntInterval*>*v) const {
  for (std::vector<IntInterval*>::iterator vi = v->begin(); vi != v->end(); ++vi) {
    if (!includes(*vi))
      return false;
  }
  return true;
}


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

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

void ValueInt::addValues(int min, int max) {
  IntInterval * ii = new IntInterval();
  if (min <= max) {
    ii->min = min;
    ii->max = max;
  } else {
    ii->min = max;
    ii->max = min;
  }
  if (d_ != NULL) {
    if (ii->min<d_->getMin()) {      
      std::cerr << "WARNING: bound of domain changes affectation of model!!!!\n";
      std::cerr << "domain of variable: ["<<d_->getMin()<<","<<d_->getMax()<<"]\n";
      std::cerr << "some expression tries to add ["<<ii->min<<","<<ii->max<<"] as a possible value\n";      
      std::cerr << "changing it to  ["<<d_->getMin()<<","<<ii->max<<"] \n\n";      
      ii->min = d_->getMin();
    }    
    if (ii->min>d_->getMax()) {      
      std::cerr << "\nWARNING: bound of domain changes affectation of model!!!!\n";
      std::cerr << "domain of variable: ["<<d_->getMin()<<","<<d_->getMax()<<"]\n";
      std::cerr << "some expression tries to add ["<<ii->min<<","<<ii->max<<"] as a possible value\n";      
      std::cerr << "changing it to  ["<<d_->getMax()<<","<<ii->max<<"] \n\n";      
      ii->min = d_->getMax();    
    }
    if (ii->max>d_->getMax()) {      
      std::cerr << "\nWARNING: bound of domain changes affectation of model!!!!\n";
      std::cerr << "domain of variable: ["<<d_->getMin()<<","<<d_->getMax()<<"]\n";
      std::cerr << "some expression tries to add ["<<ii->min<<","<<ii->max<<"] as a possible value\n";      
      std::cerr << "changing it to  ["<<ii->min<<","<<d_->getMax()<<"] \n\n";
      ii->max = d_->getMax();
    }    
    if (ii->max<d_->getMin()) {      
      std::cerr << "\nWARNING: bound of domain changes affectation of model!!!!\n";
      std::cerr << "domain of variable: ["<<d_->getMin()<<","<<d_->getMax()<<"]\n";
      std::cerr << "some expression tries to add ["<<ii->min<<","<<ii->max<<"] as a possible value\n";      
      std::cerr << "changing it to  ["<<ii->min<<","<<d_->getMin()<<"] \n\n";
      ii->max = d_->getMin();
    }
    
    
  }
  if (includes(ii)) {
    delete ii;
    return;
  }
  if (isIncluded(ii)) {
    for (std::vector<IntInterval*>::iterator it = values_->begin(); it != values_->end(); ++it)
      delete *it;
    values_->push_back(ii);
    return;
  }
  for (std::vector<IntInterval*>::iterator iiv = values_->begin(); iiv != values_->end(); ) {
    if ( (  (*iiv)->min <= ii->min && (*iiv)->max >= ii->min)   ||
         ( (*iiv)->min <= ii->max  && (*iiv)->max >= ii->max ) ) {
      // intersection so do the union
      if ((*iiv)->min < ii->min)
        ii->min = (*iiv)->min;
      if( (*iiv)->max > ii->max)
        ii->max = (*iiv)->max;
      // and remove old interval
      delete *iiv;
      iiv = values_->erase(iiv);
    } else {
      ++iiv;
    }
  }
  bool inserted = false;
  for (std::vector<IntInterval*>::iterator iiv = values_->begin(); iiv != values_->end() && !inserted; ++iiv) {
    if ( (*iiv)->min > ii->min) {
      values_->insert(iiv, ii);
      inserted = true;
    }
  }
  // rehash_();

}

const std::vector<IntInterval*> * ValueInt::getValues() const {
  return values_;
}


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




void ValueInt::removeValues(int min, int max) {
  for (std::vector<IntInterval*>::iterator mvi = values_->begin(); mvi!=values_->end();) {
    if ( (*mvi)->max < min || (*mvi)->min > max) {
      // no intersection -> do nothing
      ++mvi;
    } else {
      if ( (*mvi)->max <= max) {
        if ((*mvi)->min >= min) {
          // remove mvi completely
          delete *mvi;
          mvi = values_->erase(mvi);
        } else {
          (*mvi)->max = min -1;
          ++mvi;
        }
      } else if ( (*mvi)->min >= min) {
        (*mvi)->min = max +1;
        ++mvi;
      } else {
        // split
        int old_max = (*mvi)->max;
        (*mvi)->max = min -1;
        IntInterval * nii = new IntInterval();
        nii->min = max+1;
        nii->max = old_max;
        ++mvi;
        mvi = values_->insert(mvi,nii);
        ++mvi;
      }

    }
  }
  // rehash_();
}

void ValueInt::intersectValue(const ValueInt* v) {
  ValueInt * vv = v->getComplement();
  const std::vector<IntInterval*> * values_to_remove = vv->getValues();
  for (std::vector<IntInterval*>::const_iterator tri = values_to_remove->begin(); tri!= values_to_remove->end(); ++tri) {
    removeValues((*tri)->min, (*tri)->max);
  }
  delete(vv);
  // rehash_();
}



void ValueInt::add(const ValueInt *i) {
  std::vector<IntInterval*> * myValues = values_;
  values_ = new std::vector<IntInterval*>();
  const std::vector<IntInterval*> * otherValues = i->getValues();

  for (std::vector<IntInterval*>::iterator i = myValues->begin(); i!= myValues->end(); ++i) {
    for (std::vector<IntInterval*>::const_iterator j = otherValues->begin(); j!= otherValues->end(); ++j) {
      addValues((*i)->min+(*j)->min, (*i)->max+(*j)->max);
    }
  }

  // int ii = i->toInt();
  // if (i->toInt() == INT32_MIN)
  //  BOOST_THROW_EXCEPTION(std::runtime_error("trying to add an openint that is not a simple int..."));
  // else {
  //  for (std::vector<IntInterval*>::iterator iii = values_->begin(); iii != values_->end(); ++iii) {
  //      (*iii)->min += ii;
  //      (*iii)->max += ii;
  //  }
  // }
  // rehash_();
}


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

  // int ii = i->toInt();
  // if (i->toInt() == INT32_MIN)
  //  BOOST_THROW_EXCEPTION(std::runtime_error("trying to minus an openint that is not a simple int..."));
  // else {
  //  for (std::vector<IntInterval*>::iterator iii = values_->begin(); iii != values_->end(); ++iii) {
  //      (*iii)->min -= ii;
  //      (*iii)->max -= ii;
  //  }
  // }
  // rehash_();
}





void ValueInt::times(const ValueInt *i) {
  int ii = i->toInt();
  if (i->toInt() == INT32_MIN)
    BOOST_THROW_EXCEPTION(std::runtime_error("trying to times by an openint that is not a simple int..."));
  else {
    for (std::vector<IntInterval*>::iterator iii = values_->begin(); iii != values_->end(); ++iii) {
      (*iii)->min *= ii;
      (*iii)->max *= ii;
    }
  }
  // rehash_();
}

void ValueInt::div(const ValueInt *i) {
  int ii = i->toInt();
  if (i->toInt() == INT32_MIN)
    BOOST_THROW_EXCEPTION(std::runtime_error("trying to div by  an openint that is not a simple int..."));
  else {
    for (std::vector<IntInterval*>::iterator iii = values_->begin(); iii != values_->end(); ++iii) {
      (*iii)->min /= ii;
      (*iii)->max /= ii;
    }
  }
  // rehash_();
}

void ValueInt::mod(const ValueInt *i) {
  int ii = i->toInt();
  if (i->toInt() == INT32_MIN)
    BOOST_THROW_EXCEPTION(std::runtime_error("trying to mod by  an openint that is not a simple int..."));
  else {
    for (std::vector<IntInterval*>::iterator iii = values_->begin(); iii != values_->end(); ++iii) {
      (*iii)->min %= ii;
      (*iii)->max %= ii;
    }
  }
  // rehash_();
}

int ValueInt::max() const {
  return ((*values_)[values_->size()-1])->max;
}

int ValueInt::min() const {
  return ((*values_)[0])->min;
}

int ValueInt::card() const {
  if (!constrained_)
    return d_->getArity();
  else {
    int card = 0;
    for (std::vector<IntInterval*>::iterator i = values_->begin(); i!= values_->end(); ++i)
      card += (*i)->max - (*i)->min +1;
    return card;
  }
  return -1;
}


void ValueInt::intUnion(ValueInt * o)  {
  const std::vector<IntInterval*> * ov = o->getValues();
  for (std::vector<IntInterval*>::const_iterator i = ov->begin(); i!=ov->end(); ++i) {
    addValues((*i)->min, (*i)->max);
  }
  // rehash_();
}




ValueInt* ValueInt::getComplement() const {
  ValueInt * n = new ValueInt(d_);
  for (std::vector<IntInterval*>::const_iterator ovi = values_->begin(); ovi != values_->end(); ++ovi) {
    n->removeValues((*ovi)->min, (*ovi)->max);
  }
  return n;
}


void ValueInt::setToInfThan(ValueInt*o) {
  removeValues(o->min(), INT32_MAX);
  // rehash_();
}

void ValueInt::setToSupThan(ValueInt*o) {
  removeValues(INT32_MIN, o->max());
  // rehash_();
}

void ValueInt::setToInfEqThan(ValueInt*o) {
  removeValues(o->min()+1, INT32_MAX);
  // rehash_();
}

void ValueInt::setToSupEqThan(ValueInt*o) {
  removeValues(INT32_MIN, o->max()-1);
  // rehash_();
}

std::vector<Value*> * ValueInt::getSingletons() const {
  std::vector<Value*> * ret = new std::vector<Value*>();
  for (std::vector<IntInterval*>::const_iterator i = values_->begin(); i!=values_->end(); ++i)
    for (int j = (*i)->min; j<=(*i)->max; ++j) {
      Value *fv = new ValueInt(j);
      fv->own();
      ret->push_back(fv);
    }
  return ret;
}

bool ValueInt::hasIntersection(const ValueInt*v) const {
  for (std::vector<IntInterval*>::const_iterator i = v->getValues()->begin(); i!= v->getValues()->end(); ++i)
    for (std::vector<IntInterval*>::const_iterator j = values_->begin(); j!= values_->end() ; ++j)
      if (! ((*i)->min>(*j)->max || (*i)->max<(*j)->min))
        return true;

  return false;
}

std::string ValueInt::toString() const {
  //    std::string s;
  std::stringstream ss;
  for (std::vector<IntInterval*>::iterator i = values_->begin(); i!= values_->end(); ++i) {
    if ((*i)->min == (*i)->max) {
      ss << (*i)->min;
    } else {
      ss << "[";
      ss<< (*i)->min;
      ss<< ",";
      ss<< (*i)->max;
      ss<< "]";
    }
  }
  // ss<< " in ";
  // ss<< d_->toString();
  return ss.str();
}

int ValueInt::getMin() const {
  if (values_ == NULL)
    return INT32_MIN;
  return (*values_)[0]->min;
}

int ValueInt::getMax() const {
  if (values_ == NULL)
    return INT32_MAX;
  return (*values_)[values_->size()-1]->max;
}


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

void ValueInt::removeValue(const ValueInt *v) {
  const std::vector<IntInterval*> * vals = v->getValues();
  for (std::vector<IntInterval*>::const_iterator i = vals->begin(); i != vals->end(); ++i)
    removeValues((*i)->min, (*i)->max);
}



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