/*******************************************************************************
 * 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 <cfloat>
#include <iostream>
#include <map>
#include <sstream>
#include <utility>

#include "AltaricaInitInfoXML2.hh"
#include "AltaricaJson.hh"
#include "AltaricaModel.hh"
#include "AltaricaState.hh"
#include "Assignment.hh"
#include "Constant.hh"
#include "Event.hh"
#include "EventLaw.hh"
#include "MemberAccess.hh"
#include "MetaEvent.hh"
#include "Node.hh"
#include "Synchronisation.hh"
#include "Variable.hh"
#include "Values.hh"
#include "Transition.hh"
#include "Assertion.hh"
#include "domains/EnumDomain.hh"
#include "expressions/ExpressionInfoVar.hh"
#include "expressions/Expression.hh"

using namespace epoch::altarica;

bool AltaricaModel::debugSemantics = false;

AltaricaModel::AltaricaModel(bool continuous) {

        getEnabledMetaEventsMostPrioOnlyFunctor = &AltaricaModel::getEnabledMetaEventsMostPrioOnlyOnTheFly;
        getEnabledMetaEventsAllFunctor = &AltaricaModel::getEnabledMetaEventsAllOnTheFly;
        getEnabledMetaEventsDiracFunctor = &AltaricaModel::getEnabledMetaEventsDiracOnTheFly;

        initInfo = NULL;
        finalized_ = false;
        varStringCache = "";
        varCache = NULL;
        hasDirac_ = false;
        if (continuous) {
                discrete_ = false;
                probaComputer = &AltaricaModel::giveContinuousProbas;
        } else {
                probaComputer = &AltaricaModel::computeDiscreteProbas;
                discrete_ = true;
        }
        metaEventOrderer = &AltaricaModel::orderMetaEventNOOP;
        metaEvents_ = NULL;
        metaEventsByPrio_ = NULL;
        asserts_ = NULL;
}


void AltaricaModel::addConstant(Constant* c) {
        constants.push_back(c);
}

void AltaricaModel::addDomain(Domain* d) {
        domains.push_back(d);
}



void AltaricaModel::addNode(Node* n) {
        //    n->finalize(this);  // TOO EARLY!!!!
        nodes.push_back(n);
}

Node * AltaricaModel::findNodeByName(const std::string& s) {
        for (std::vector<Node*>::iterator ni = nodes.begin();
             ni != nodes.end(); ni++) {
                if ((*ni)->hasName(s))
                        return (*ni);
        }
        return NULL;
}

const Node * AltaricaModel::findNodeByFullName(const std::string& s) const {
        for (std::vector<Node*>::const_iterator ni = nodes.begin();
             ni != nodes.end(); ni++) {
                if ((*ni)->toString(false).compare(s)==0)
                        return (*ni);
        }
        return NULL;
}


std::vector<Node*> * AltaricaModel::findNodesByType(const std::string& s) {
        std::vector<Node*> *nodesByType = new std::vector<Node*>();
        for (std::vector<Node*>::iterator ni = nodes.begin(); ni != nodes.end(); ni++) {
                if ((*ni)->getParent() == NULL)
                        continue;
                const MemberAccess * mat = (*ni)->getTypeName();
                if (mat->toString().compare(s) == 0)
                        nodesByType->push_back(*ni);
        }
        if (nodesByType->size() == 0) {
                delete nodesByType;
                return NULL;
        }
        return nodesByType;
}


 Constant * AltaricaModel::findConstantByName(const std::string& s) const {
        for (std::vector<Constant*>::const_iterator constIt = constants.begin();
             constIt != constants.end(); ++constIt)
                if ((*constIt)->getName().compare(s) == 0)
                        return *constIt;
        return NULL;
}

Domain * AltaricaModel::findDomainByName(const std::string& s) {
        for (std::vector<Domain*>::iterator vdit = domains.begin();
             vdit != domains.end(); ++vdit)
                if ((*vdit)->alias(s))
                        return *vdit;
        return NULL;

}

void AltaricaModel::addVars(Node* n) {
        const std::map<std::string, Variable*, Node::ltstr> * v = n->getStateVariables();
        for (std::map<std::string, Variable*, Node::ltstr>::const_iterator i = v->begin(); i!= v->end(); ++i)
                stateVars.push_back(i->second);
        v = n->getFlowVariables();
        for (std::map<std::string, Variable*, Node::ltstr>::const_iterator i = v->begin(); i!= v->end(); ++i)
                flowVars.push_back(i->second);
}



// void AltaricaModel::addTrans(Node*n) {
//     const std::vector<Transition*>* v = n->getTransitions();
//     for (std::vector<Transition*>::const_iterator i = v->begin(); i!= v->end(); ++i) {
//  atomicTransitions->push_back(*i);
//     }
// }

void AltaricaModel::addEvents(Node*n) {
        const std::map<std::string, Event*, Node::ltstr>* v = n->getEvents();
        for (std::map<std::string, Event*,Node::ltstr>::const_iterator i = v->begin(); i!= v->end(); ++i) {
              //  std::cerr << "    event of node : " << i->second->toString() << i->second->getLaw()->toString() << std::endl;
                atomicEvents.push_back(i->second);
        }
}


void AltaricaModel::addSyncs(Node*n) {
        const std::vector<Synchronisation*>* v = n->getSyncs();
        for (std::vector<Synchronisation*>::const_iterator i = v->begin(); i!= v->end(); ++i) {
                syncs.push_back(*i);
        }
}

 void AltaricaModel::addAsserts(Node*n) {
     const std::vector<Assertion*>* v = n->getAsserts();
     for (std::vector<Assertion*>::const_iterator i = v->begin(); i!= v->end(); ++i) {
      asserts_->push_back(*i);
     }
 }


bool AltaricaModel::noProba(MetaEvent* mev) 
{
        const std::vector<Event*> * ev = mev->getEvents();
        for (std::vector<Event*>::const_iterator j= ev->begin(); j!= ev->end(); ++j)
                if ((*j)->getLaw() != NULL && !(*j)->getLaw()->isDisabled())
                        return false;
        return true;  
}


bool AltaricaModel::noProba(std::vector<MetaEvent*>* mev) {
        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) 
                if (!noProba(*i)) 
                        return false;
        return true;  
        //   const std::vector<Event*> * ev = (*i)->getEvents();
        //   for (std::vector<Event*>::const_iterator j= ev->begin(); j!= ev->end(); ++j)
        //     if ((*j)->getLaw() != NULL && !(*j)->isDisabled())
        //       return false;
        // }
        // return true;
}


// bool AltaricaModel::dirac(std::vector<MetaEvent*> * mev) {
//     for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i)
//  if (dirac(*i))
//      return true;
//     return false;
// }

// bool AltaricaModel::dirac(MetaEvent* mev) {
//     const std::vector<Event*> * ev = mev->getEvents();
//     for (std::vector<Event*>::const_iterator i = ev->begin(); i!= ev->end(); ++i) {
//  if ((*i)->getLaw() != NULL && (*i)->getLaw()->isDirac())
//      return true;
//     }
//     return false;
// }

bool AltaricaModel::discrete(std::vector<MetaEvent*>* mev) {


        bool discrete = false;
        bool cont = false;
        bool dirac = false;
        bool timed = false;

        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                const std::vector<Event*>* ev = (*i)->getEvents();
                for (std::vector<Event*>::const_iterator j = ev->begin(); j != ev->end(); ++j) {
                        if ((*j)->getLaw() != NULL &&  (*j)->getLaw()->isDisabled()) {
                                if ((*j)->getLaw()->isDiscrete()  )
                                        discrete = true;
                                else if ((*j)->getLaw()->isCont())
                                        cont = true;
                                else if ((*j)->getLaw()->isDirac())
                                        dirac = true;
                                else if ((*j)->getLaw()->isTimed())
                                        timed = true;
                            //    std::cerr << "event with law: " << (*j)->toString() << std::endl;

                        } else {
                            //          std::cerr << "event with no law: " << (*j)->toString() << std::endl;
                        }
                }
        }
        if (discrete && cont)
                BOOST_THROW_EXCEPTION(std::runtime_error(std::string("you are mixing discrete and continuous law in enabled events")));
        if (discrete)
                return true;
        if (cont || dirac || timed)
                return false;



        // std::cerr << "size of mevec: " << mev->size() << std::endl;
        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                const std::vector<Event*>* ev = (*i)->getEvents();
                for (std::vector<Event*>::const_iterator j = ev->begin(); j != ev->end(); ++j) {
                        //    std::cerr << "event with no law: " << (*j)->toString() << std::endl;
                }
        }

        if (mev->size() == 1) {
                (*(mev->begin()))->setProba(1.0);
                (*(mev->begin()))->setReward(1.0);
                return true;
        }


        //BOOST_THROW_EXCEPTION(std::runtime_error(std::string("unable to find any event law; that should not happen here....")));
        return false;
}

bool AltaricaModel::hasDirac(std::vector<MetaEvent*> * mev) {
        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                if ((*i)->isDirac())
                        return true;
        }
        return false;
}

void AltaricaModel::giveContinuousProbas(std::vector<MetaEvent*>* mev, bool mayHaveDirac, AltaricaModel *am, AltaricaState *as) {
        if (mev->size() == 0)
                return;
        if (noProba(mev)) {
                std::cerr<<" FATAL ERROR: no proba in MEV for continuous time model....\n";
                BOOST_THROW_EXCEPTION(std::runtime_error(std::string("no proba in MEV for continuous time model....")));
        }

        if (mayHaveDirac && hasDirac(mev)) {
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();) {
                        if ((*i)->isDirac()) {
                                (*i)->setProba(1);
                                ++i;
                        }  else {
                                delete (*i);
                                i = mev->erase(i);
                        }
                }
        }

        if (debugSemantics && discrete(mev)) {
                BOOST_THROW_EXCEPTION(std::runtime_error(std::string("discrete proba in MEV for continuous time model....")));
        }

        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();++i)  {
                (*i)->setMaxContProba(am,as);
        }
}

void AltaricaModel::computeDiscreteProbas(std::vector<MetaEvent*>* mev, bool mayHaveDirac, AltaricaModel *am, AltaricaState *as) {
        if (noProba(mev)) {
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                        (*i)->setProba(1.0/mev->size());
                        (*i)->setReward(1.0);
                }
                return;
        }
        if (mayHaveDirac && hasDirac(mev)) {
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();) {
                        if ((*i)->isDirac()) {
                                (*i)->setProba(1);
                                ++i;
                        }  else {
                                delete (*i);
                                i = mev->erase(i);
                        }
                }
                if (mev->size() == 1) {
                        (*mev)[0]->setReward(1.0);
                        return;
                }
        }

        double sum = 0;
  
        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) 
                (*i)->setReward(1.0);  
  
        if (discrete(mev)) {
    
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();++i) 
                        sum += (*i)->timesDiscreteProba(am,as);
    
                if (sum > 1 + DBL_EPSILON) { // case where probas are stated as
                        // "if alone"
                        if (debugSemantics)
                                std::cerr << "sum of probas > 1 : " << sum << " ; considering probas if alone (doing times by proba of not other events enabled\n";

                        std::vector<const Event*> allEvents;
                        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();++i) {
                                for (std::vector<Event*>::const_iterator j = (*i)->getEvents()->begin(); j!= (*i)->getEvents()->end(); ++j) {
                                        bool isIn = false;
                                        for (std::vector<const Event*>::iterator k = allEvents.begin(); k!=allEvents.end() && !isIn; ++k)
                                                if (*k == *j)
                                                        isIn = true;
                                        if (!isIn)
                                                allEvents.push_back(*j);
                                }
                        }
      
                        sum = 0;      
                        for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();++i) 
                                sum += (*i)->timesDiscreteProba((*i)->complementOthersProba(&allEvents,am,as));

                        if (sum < 1+DBL_EPSILON && sum > 1-DBL_EPSILON)
                                return;      
                        if (sum > 1+DBL_EPSILON)        {        
                                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) 
                                        (*i)->setProba((*i)->getProba()/sum);
                        } else {
                                MetaEvent * noop = new MetaEvent();    
                                noop->setProba(1.0 - sum);
                                mev->push_back(noop);        
                        }        
        
                } else if (sum < 1+DBL_EPSILON && sum > 1-DBL_EPSILON) {
                        // normalization unneeded, we stop here
                        if (debugSemantics) 
                                std::cerr << "sum of probas == 1 : " << sum << " ; considering pure MDP semantics\n";
                        return ;

                } else {
                        if (debugSemantics)    
                                std::cerr << "sum of probas < 1 : " << sum << " ; considering pure MDP semantics + adding noop\n";
                        MetaEvent * noop = new MetaEvent();    
                        noop->setProba(1.0 - sum);
                        mev->push_back(noop);
      
                }
    
    
        } else {
                // here special case for synchronizing continuous chain over
                // discrete state change
    
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end();++i)
                        (*i)->setMaxContProba(am,as);
                sum = 0;    
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) 
                        sum += (*i)->getProba();
    
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) 
                        (*i)->setProba((*i)->getProba()/sum); 
    
        }  
  

}





std::vector<MetaEvent*> * AltaricaModel::computeDiracMetaEvents(AltaricaState *state) {
        std::vector<MetaEvent*> * metaEvents = new  std::vector<MetaEvent*>();

        //    std::cerr << "\nMETA EVENTS BUILDING START..." << std::endl;

        std::vector<Event*> multiSyncEvents;
        std::vector<MetaEvent*> * noSyncMeta = new std::vector<MetaEvent*>();

        for (std::vector<Event*>::iterator evi = atomicEvents.begin(); evi != atomicEvents.end(); ++evi) {
                if ((*evi)->nSynchros()==0) {
                        // good case: *evi is not synchronised
                        if ((*evi)->isDirac() && (*evi)->isEnabled(this, state)) {
                                MetaEvent *noSync = new MetaEvent();
                                noSync->push_back(*evi);
                                noSyncMeta->push_back(noSync);
                        }
                } else if ((*evi)->nSynchros() > 1) {
                        // in this case we will have to merge simple synchros
                        if ((*evi)->isEnabled(this, state))
                                multiSyncEvents.push_back(*evi);
                }
        }

        // now generate simple synchros
        int j = 0;
        for (std::vector<Synchronisation*>::iterator si = syncs.begin(); si != syncs.end(); ++si) {
                std::vector<MetaEvent*>* ss = (*si)->buildSynchronisedEvents(this, state);
                if (ss != NULL) {
                        for (std::vector<MetaEvent*>::iterator i = ss->begin(); i!= ss->end(); ++i)
                                if ((*i)->isDirac())
                                        metaEvents->push_back(*i);
                                else
                                        delete *i;
                }
                delete ss;
                ++j;
        }

        // now merge synchros if needed
        for (std::vector<Event*>::iterator ie = multiSyncEvents.begin(); ie != multiSyncEvents.end(); ++ie) {
                std::vector<MetaEvent*>* metas = findAndRemoveMetas(*ie, metaEvents);
                MetaEvent * ume = MetaEvent::unionMetaEvents(metas);
                if (ume->isDirac())
                        metaEvents->push_back(ume);
                else
                        delete ume;
                for (std::vector<MetaEvent*>::iterator i = metas->begin(); i!=  metas->end(); ++i)
                        delete *i;
                delete(metas);
        }

        metaEvents->insert(metaEvents->end(), noSyncMeta->begin(), noSyncMeta->end());
        // for (std::vector<MetaEvent*>::iterator i = metaEvents->begin(); i!=metaEvents->end(); ) {
        //  if (dirac(*i))
        //      ++i;
        //  else {
        //      delete(*i);
        //      i = metaEvents->erase(i);
        //  }
        // }



        delete(noSyncMeta);

//  purgeMetaEvents(metaEvents);
  

        if (!hasDirac_)
                (*probaComputer)(metaEvents,false,this,state);
        else
                (*probaComputer)(metaEvents,true,this,state);
        return metaEvents;
}



std::vector<MetaEvent*> * AltaricaModel::computeMetaEventsNoDirac(AltaricaState *state) {
        std::vector<MetaEvent*> * metaEvents = new  std::vector<MetaEvent*>();

        //    std::cerr << "\nMETA EVENTS BUILDING START..." << std::endl;

        std::vector<Event*> * multiSyncEvents = new std::vector<Event*>();
        std::vector<MetaEvent*> * noSyncMeta = new std::vector<MetaEvent*>();

        for (std::vector<Event*>::iterator evi = atomicEvents.begin(); evi != atomicEvents.end(); ++evi) {
                if ((*evi)->nSynchros()==0) {
                        // good case: *evi is not synchronised
                        if ((*evi)->isEnabled(this, state)&&!(*evi)->isDirac()) {
                                MetaEvent *noSync = new MetaEvent();
                                noSync->push_back(*evi);
                                noSyncMeta->push_back(noSync);
                        }
                } else if ((*evi)->nSynchros() > 1) {
                        // in this case we will have to merge simple synchros
                        if ((*evi)->isEnabled(this, state) && !(*evi)->isDirac())
                                multiSyncEvents->push_back(*evi);
                }
        }

        // now generate simple synchros
        int j = 0;
        for (std::vector<Synchronisation*>::iterator si = syncs.begin(); si != syncs.end(); ++si) {
                std::vector<MetaEvent*>* ss = (*si)->buildSynchronisedEvents(this, state);
                if (ss != NULL) {
                        metaEvents->insert(metaEvents->end(), ss->begin(), ss->end());
                        delete ss;
                        ++j;
                }

        }

        // now merge synchros if needed
        for (std::vector<Event*>::iterator ie = multiSyncEvents->begin(); ie != multiSyncEvents->end(); ++ie) {
                std::vector<MetaEvent*>* metas = findAndRemoveMetas(*ie, metaEvents);
                if (metas->size() > 0) {
                        std::vector<MetaEvent*> * combinedMEV = MetaEvent::combineMetaEvents(metas);
                        metaEvents->insert(metaEvents->end(), combinedMEV->begin(), combinedMEV->end());
                        delete combinedMEV;      
                }
    
//      metaEvents->push_back(MetaEvent::combineMetaEvents(metas));
      
                for (std::vector<MetaEvent*>::iterator i = metas->begin(); i!=  metas->end(); ++i)
                        delete *i;
                delete(metas);
        }

        metaEvents->insert(metaEvents->end(), noSyncMeta->begin(), noSyncMeta->end());

        delete(multiSyncEvents);
        delete(noSyncMeta);

//  purgeMetaEvents(metaEvents);
  

        (*probaComputer)(metaEvents,false,this,state);
        return (*metaEventOrderer)(metaEvents);
}


std::vector<MetaEvent*> * AltaricaModel:: orderMetaEventMostProbableFirst(std::vector<MetaEvent*>* mev) {
        std::vector<MetaEvent*> * orderedMetaEvents = new  std::vector<MetaEvent*>();
        std::vector<MetaEvent*>::iterator maxpos;
        while (mev->size() > 0) {
                double maxProba = -1;
                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                        if ((*i)->getProba() > maxProba) {
                                maxProba = (*i)->getProba();
                                maxpos = i;
                        }
                }
                orderedMetaEvents->push_back(*maxpos);
                mev->erase(maxpos);
        }
        delete(mev);
        return orderedMetaEvents;
}


std::vector<MetaEvent*> * AltaricaModel:: orderMetaEventLessProbableFirst(std::vector<MetaEvent*>* mev) {
        std::vector<MetaEvent*> * orderedMetaEvents = new  std::vector<MetaEvent*>();
        std::vector<MetaEvent*>::iterator minpos;
        while (mev->size() > 0) {
                double minProba = 2;

                for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
                        if ((*i)->getProba() < minProba) {
                                minProba = (*i)->getProba();
                                minpos = i;
                        }
                }
                orderedMetaEvents->push_back(*minpos);
                mev->erase(minpos);
        }
        delete(mev);
        return orderedMetaEvents;
}


std::vector<MetaEvent*> * AltaricaModel:: orderMetaEventNOOP(std::vector<MetaEvent*>* mev) {
        return mev;
}



std::vector<MetaEvent*> * AltaricaModel::findAndRemoveMetas(Event*e,std::vector<MetaEvent*> * metaEvents) {
        std::vector<MetaEvent*> * metasOfE = new std::vector<MetaEvent*>();
        for (std::vector<MetaEvent*>::iterator i = metaEvents->begin(); i!= metaEvents->end();) {
                if ((*i)->containsE(e)) {
                        metasOfE->push_back(*i);
                        i = metaEvents->erase(i);
                } else
                        ++i;
        }
        return metasOfE;
}

MetaEvent* AltaricaModel::popMorePrio(std::vector<MetaEvent*> * prioClass) {
        std::vector<MetaEvent*>::iterator morePrioI = prioClass->begin();
        MetaEvent* morePrio = *morePrioI;
        for (std::vector<MetaEvent*>::iterator i = ++prioClass->begin(); i != prioClass->end(); ++i) {
                if ((*i)->comparePrioTo(morePrio) == 1) {
                        morePrio = *i;
                        morePrioI = i;
                }
        }
        prioClass->erase(morePrioI);
        return morePrio;
}

MetaEvent* AltaricaModel::popSamePrio(std::vector<MetaEvent*> *prioClass, MetaEvent* p) {
        for (std::vector<MetaEvent*>::iterator i = prioClass->begin(); i != prioClass->end(); ++i) {
                if ( (*i)->comparePrioTo(p) == 0 ) {
                        MetaEvent* samePrio = *i;
                        prioClass->erase(i);
                        return samePrio;
                }
        }
        return NULL;
}

bool AltaricaModel::hasEventDAG(std::vector<MetaEvent*>* prioClass) {
        for (std::vector<MetaEvent*>::iterator mai = prioClass->begin(); mai != prioClass->end(); ++mai) {
                const std::vector<Event*>  * ev = (*mai)->getEvents();
                for (std::vector<Event*>::const_iterator ei = ev->begin(); ei != ev->end(); ++ei) {
                        if ((*ei)->getUppers()->size() != 0)
                                return true;
                }
        }
        return false;
}

std::vector<std::vector<MetaEvent*>*>* AltaricaModel::splitPrioClass(std::vector<MetaEvent*>* prioClass) {
        std::vector<std::vector<MetaEvent*>*>* newClasses = new std::vector<std::vector<MetaEvent*>*>();

        if (!hasEventDAG(prioClass)) {
                newClasses->push_back(prioClass);
        } else {
                while (prioClass->size() > 0) {
                        std::vector<MetaEvent*> * newMetas = new std::vector<MetaEvent*>();
                        MetaEvent * morePrio = popMorePrio(prioClass);
                        newMetas->push_back(morePrio);
                        MetaEvent * p;
                        while ((p = popSamePrio(prioClass, morePrio)) != NULL) {
                                newMetas->push_back(p);
                        }
                        newClasses->push_back(newMetas);
                }
                delete prioClass;
        }

        return newClasses;
}

std::vector<std::vector<MetaEvent*> *> * AltaricaModel::buildPrioritizedMetaEvents(std::vector<MetaEvent*> * metaEvents) {

        std::vector<std::vector<epoch::altarica::MetaEvent*> *> *metaEventsByPrio
                = new  std::vector<std::vector<epoch::altarica::MetaEvent*> *>();
        std::multimap<unsigned int, MetaEvent*> metaEventsBySize;
        std::vector<std::vector<MetaEvent*>*> metaEventsByPrio2;


        unsigned int maxSize = 0;
        for (std::vector<MetaEvent*>::iterator i = metaEvents->begin(); i!= metaEvents->end(); ++i)  {
                metaEventsBySize.insert(std::pair<int, MetaEvent*>((*i)->size(), *i));
                if ((*i)->size() > maxSize)
                        maxSize = (*i)->size();
        }
        //  first larger size

        for (unsigned int i = 0; i<=maxSize; ++i) {
                std::pair<std::multimap<unsigned int, MetaEvent*>::iterator,
                          std::multimap<unsigned int, MetaEvent*>::iterator> ppp;
                ppp = metaEventsBySize.equal_range(i);

                if (ppp.first != ppp.second) {
                        std::vector<MetaEvent*>* newPrioClass = new std::vector<MetaEvent*>();

                        for(std::multimap<unsigned int,MetaEvent*>::iterator mai = ppp.first;
                            mai!= ppp.second; ++mai)
                                newPrioClass->push_back(mai->second);

                        // if size is one, insert directly
                        if (newPrioClass->size() == 1)
                                metaEventsByPrio2.push_back(newPrioClass);
                        //if size > 1, we have to check if splitting is needed, based on inside-node dag event priority (....sigh..)
                        else {
                                std::vector<std::vector<MetaEvent*>*>* newPrioClasses = splitPrioClass(newPrioClass);
                                metaEventsByPrio2.insert(metaEventsByPrio2.end(),
                                                         newPrioClasses->begin(),
                                                         newPrioClasses->end());
                                delete (newPrioClasses);
                        }
                        metaEventsBySize.erase(ppp.first, ppp.second);
                }
        }


        for (std::vector<std::vector<MetaEvent*>*>::reverse_iterator i = metaEventsByPrio2.rbegin(); i != metaEventsByPrio2.rend(); ++i ) {
                metaEventsByPrio->push_back(*i);
        }
        return metaEventsByPrio;
}



void AltaricaModel::finalize(std::string  mainNodeName, bool onTheFlyMeta) {
        //    onTheFly_ = onTheFlyMeta;
        if (!onTheFlyMeta) {
                getEnabledMetaEventsMostPrioOnlyFunctor = &AltaricaModel::getEnabledMetaEventsMostPrioOnlyOnTheFly;
                getEnabledMetaEventsAllFunctor  = &AltaricaModel::getEnabledMetaEventsAllOnTheFly;
                getEnabledMetaEventsDiracFunctor  = &AltaricaModel::getEnabledMetaEventsDiracOnTheFly;
        }
        // first find main node
        //    mainNode_->setParent(NULL);
        mainNode_ = findNodeByName(mainNodeName);
        if (mainNode_ == NULL){
        	std::cerr  << "ERROR, no main node" << std::endl;
        	exit( EXIT_FAILURE );
        }
        if (asserts_ == NULL)
      	  asserts_ = new  std::list<Assertion*>();

        finalizeRec(mainNode_);
        // compactSimpleAssertsRec(mainNode_); // AA: deletes assertions relative to aliases. not needed
        addGlobalDataRec(mainNode_);

        // now compute sync events
        if (!onTheFlyMeta) {
      	  metaEvents_ = computeMetaEventsNoDirac(NULL);
      	  metaEventsByPrio_ = buildPrioritizedMetaEvents(metaEvents_);
        }

        std::map<std::string, Variable*> m;
        for (std::vector<Variable*>::iterator j = stateVars.begin(); j!= stateVars.end(); ++j)
                m[(*j)->toString()] = *j;
        stateVars.resize(0);
        for (std::map<std::string, Variable*>::iterator mi = m.begin(); mi != m.end(); ++mi)
                stateVars.push_back(mi->second);

        std::map<std::string, Variable*> m2;
        for (std::vector<Variable*>::iterator j = flowVars.begin(); j!= flowVars.end(); ++j)
                m2[(*j)->toString()] = *j;
        flowVars.resize(0);
        for (std::map<std::string, Variable*>::iterator mi = m2.begin(); mi != m2.end(); ++mi)
                flowVars.push_back(mi->second);



        unsigned int i = 0;
        for (std::vector<Variable*>::iterator j = stateVars.begin(); j!= stateVars.end(); ++j)
                (*j)->setIndex(i++);
        i = 0;
        for (std::vector<Variable*>::iterator j = flowVars.begin(); j!= flowVars.end(); ++j)
                (*j)->setIndex(i++);

        i = 0;
        for (std::vector<Node*>::iterator j = nodes.begin(); j != nodes.end(); ++j)
                (*j)->setIndex(i++);

        //    if (removeDiracs)
        for (std::vector<Event*>::iterator j = atomicEvents.begin(); j!=atomicEvents.end(); ++j) {
                if ((*j)->isDirac()) {
                        hasDirac_ = true;
                        atomicDiracEvents.push_back(*j);
                }
        }
        if (debugSemantics)
        {
      	  std::cout << "\nAssertions before ordering"<< std::endl;
      	  for(auto &e : getAsserts()) {
      		  size_t et = (size_t)(e);
      		  std::cout << et << " "<< e->getExpr()->toString()<< std::endl;
      	  }
        }
        orders_assertions();


        bool hasLaws = false;
        for (std::vector<Event*>::iterator j = atomicEvents.begin(); j!=atomicEvents.end()&&!hasLaws; ++j)
                if ((*j)->getLaw() != NULL)
                        hasLaws = true;

        if (hasLaws) { //get rid of phantom events
                for (std::vector<Event*>::iterator j = atomicEvents.begin(); j!=atomicEvents.end();) {
                        if ((*j)->nSynchros()==0 && (*j)->getLaw()==NULL) {
                                std::cerr << "ERASING event with no law because other have one: " << (*j)->toString() << std::endl;
                                std::cerr << "please double check and if it is the only possible transition add law <event xx> = [constant(1.0)|Dirac(0)];  \n";
                                j = atomicEvents.erase(j);
                        }  else
                                j++;
                }
        }

        finalized_ = true;
}


void AltaricaModel::finalizeRec(Node *n) {
	n->finalize(this);
	//    const std::vector<Node*> * sn = n->getSubNodes();
	const std::map<std::string, Node*, Node::ltstr> * sn = n->getSubNodes();
	//    for (std::vector<Node*>::const_iterator i = sn->begin(); i!= sn->end(); ++i) {
	for (std::map<std::string, Node*, Node::ltstr>::const_iterator i = sn->begin(); i!= sn->end(); ++i) {
		finalizeRec(i->second);
	}

}

void AltaricaModel::compactSimpleAssertsRec(Node *n) {
      n->compactSimpleAsserts(this);
        //    const std::vector<Node*> * sn = n->getSubNodes();
        const std::map<std::string, Node*, Node::ltstr> * sn = n->getSubNodes();
        //    for (std::vector<Node*>::const_iterator i = sn->begin(); i!= sn->end(); ++i) {
        for (std::map<std::string, Node*, Node::ltstr>::const_iterator i = sn->begin(); i!= sn->end(); ++i) {
                compactSimpleAssertsRec(i->second);
        }

}

void AltaricaModel::addGlobalDataRec(Node *n) {
        //    std::cerr << "\n\n\nbuilding node: " << n->toString() << std::endl;
        addVars(n);
        //    addTrans(n);
        addEvents(n);
        addSyncs(n);
        addAsserts(n);
        //n->finalize(this);
        const std::map<std::string, Node*, Node::ltstr> * sn = n->getSubNodes();

        for (std::map<std::string,Node*,Node::ltstr>::const_iterator i = sn->begin(); i!= sn->end(); ++i) {
                addGlobalDataRec(i->second);
                addNode(i->second);
        }
        n->populateVarCache();
}


AltaricaState *AltaricaModel::initialState() {
        if (!finalized_) {
                std::cerr << "!!!!!!!!!! WARNING: no explicit finalization of the model, assuming main node name is \"main\" and assuming on-the-fly building of enabled synchros" << std::endl;
                finalize();
        }
        AltaricaState * init = new AltaricaState(true,this,initInfo);


        //    std::cerr<< "FF diracs initial state" << std::endl;
        init = forwardDirac(init);
        //    std::cerr<< "FF diracs initial state END" << std::endl;
        init->rehash_();


        //    evaluateFlowVars(init); // fills caches in expressions -> useless because caches are filled during first diracstuff
        return init;
}


std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsMostPrioOnlyOnTheFly(AltaricaState *state) {
        std::vector<MetaEvent*> * mev = computeMetaEventsNoDirac(state);
        std::vector<std::vector<MetaEvent*> *> * metaEventsByPrio = buildPrioritizedMetaEvents(mev);
        delete mev;
        for (std::vector<std::vector<MetaEvent*>*>::iterator i = ++metaEventsByPrio->begin(); i!=
                     metaEventsByPrio->end(); ++i) {
                for (std::vector<MetaEvent*>::iterator j = (*i)->begin(); j!= (*i)->end(); ++j) {
                        delete *j;
                }
                delete *i;
        }
        std::vector<MetaEvent*> * ret = (*metaEventsByPrio)[0];
        delete metaEventsByPrio;
        return ret;
}

std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsMostPrioOnlyOffLine(AltaricaState *state) {
        bool inPrio = false;
        std::vector<MetaEvent*> *enabledMetaEvents = new std::vector<MetaEvent*>();
        for (std::vector<std::vector<MetaEvent*> *>::iterator prioIt = metaEventsByPrio_->begin();
             prioIt != metaEventsByPrio_->end() && !inPrio; ++prioIt) {
                for (std::vector<MetaEvent*>::iterator meit = (*prioIt)->begin();
                     meit != (*prioIt)->end(); ++meit) {
                        if ((*meit)->isEnabled(this, state)) {
                                inPrio = true;
                                enabledMetaEvents->push_back(*meit);
                        }
                }
        }
        if (enabledMetaEvents->size() != 0)
                return enabledMetaEvents;

        delete(enabledMetaEvents);

        return NULL;
}


std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsMostPrioOnly(AltaricaState *state) {
        return (this->*getEnabledMetaEventsMostPrioOnlyFunctor)(state);
}


std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsAllOnTheFly(AltaricaState *state) {
        std::vector<MetaEvent*> * mev = computeMetaEventsNoDirac(state);
        //  std::cerr << "number of meta events: " << mev->size() << std::endl;
        // for (std::vector<MetaEvent*>::iterator i = mev->begin(); i!= mev->end(); ++i) {
        //     std::cerr << (*i)->toString() << std::endl;
        // }
        return mev;
}


std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsAllOffLine(AltaricaState *state) {
        std::vector<MetaEvent*> *enabledMetaEvents = new std::vector<MetaEvent*>();
        for (std::vector<MetaEvent*>::iterator meit = metaEvents_->begin(); meit != metaEvents_->end(); ++meit)
                if ((*meit)->isEnabled(this, state))
                        enabledMetaEvents->push_back(*meit);
        return enabledMetaEvents;
}



std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsAll(AltaricaState *state) {
        return (this->*getEnabledMetaEventsAllFunctor)(state);
}


std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsDiracOnTheFly(AltaricaState *state) {
        std::vector<MetaEvent*> * mev = computeDiracMetaEvents(state);
        //  std::cerr << "number of meta events: " << mev->size() << std::endl;
        return mev;
}

std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsDiracOffLine(AltaricaState *state) {
        std::vector<MetaEvent*> *enabledMetaEvents = new std::vector<MetaEvent*>();
        for (std::vector<MetaEvent*>::iterator meit = metaEvents_->begin(); meit != metaEvents_->end(); ++meit)
                if ((*meit)->isEnabled(this, state) && (*meit)->isDirac())
                        enabledMetaEvents->push_back(*meit);
        return enabledMetaEvents;
}

std::vector<MetaEvent*> * AltaricaModel::getEnabledMetaEventsDirac(AltaricaState *state) {
        return (this->*getEnabledMetaEventsDiracFunctor)(state);
}


bool AltaricaModel::diracEnabled(AltaricaState*s) {
        for (std::vector<Event*>::iterator i = atomicDiracEvents.begin(); i!= atomicDiracEvents.end(); ++i) {
                if ((*i)->isEnabled(this, s))
                        return true;
        }
        return false;
}

AltaricaState * AltaricaModel::forwardDirac(AltaricaState *ns, std::vector<const Variable*> *alreadyAssignedInDirac) {
        if (!hasDirac_) {
                delete alreadyAssignedInDirac;
                return ns;
        }

        std::vector<MetaEvent*> * mens = getEnabledMetaEventsDirac(ns);
        if (mens->size() == 0) {
                delete mens;
                delete alreadyAssignedInDirac;
                return ns;
        }
        // we need to work on transitions themselves, and not events
        // because transitions can be firable, but not the whole event
        // this is okay for general case, but we have to decompose it here

        // build list of assigned and necessary vars for each transition
        // assigned is the effect of the transitions
        // necessary haveto be computed before

        std::vector<Transition*> enabledTransitions;
        std::vector<std::vector<const Variable*>* >assignedVars;
        std::vector<std::vector<Variable*>* > necessaryVars;

        for (std::vector<MetaEvent*>::iterator i = mens->begin();i!=mens->end(); ++i) {
                for (std::vector<Event*>::const_iterator k = (*i)->getEvents()->begin(); k!= (*i)->getEvents()->end(); ++k) {
                        for (std::vector<Transition*>::const_iterator j = (*k)->getTrans()->begin(); j!= (*k)->getTrans()->end(); ++j) {
                                if ((*j)->precondOK(this, ns)) {

                                        std::vector<const Variable*> *assigned = new std::vector<const Variable*>();
                                        std::vector<Variable*> *necessary = new std::vector<Variable*>();

                                        for (std::vector<Assignment*>::const_iterator l = (*j)->getAssigns()->begin();
                                             l!= (*j)->getAssigns()->end(); ++l) {
                                                // get vars assigned by this transition
                                                assigned->push_back((*l)->getVar());
                                                // get vars necessary to do this assignment
                                                ExpressionInfoVar * eiv = new ExpressionInfoVar(false, false, this, ns, (*l)->getNode());
                                                (*l)->getExpr()->accept(eiv);
                                                // if var not  already computed, put it into necessary list
                                                for (std::vector<Variable*>::iterator m = eiv->getVars()->begin(); m!= eiv->getVars()->end(); ++m)
                                                        if (!isInAlreadyAssignedInDirac(*m, alreadyAssignedInDirac))
                                                                necessary->push_back(*m);
                                                //      necessary->insert(necessary->end(), eiv->getVars()->begin(), eiv->getVars()->end());
                                                delete eiv->getVars();
                                                delete eiv;
                                        }

                                        if (!isInAlreadyAssignedInDirac(assigned, alreadyAssignedInDirac)) {
                                                assignedVars.push_back(assigned);
                                                necessaryVars.push_back(necessary);
                                                enabledTransitions.push_back(*j);
                                        }  else {
                                                delete assigned;
                                                delete necessary;
                                        }
                                }
                        }
                }
        }

        for (std::vector<MetaEvent*>::iterator i = mens->begin();i!=mens->end(); ++i)
                delete *i;
        delete mens;


        //sometimes we have metaevents enabled but no transition
        if (enabledTransitions.size() == 0) {
                delete alreadyAssignedInDirac;
                return ns;
        }

        // now we clean the necessary lists
        // remove vars that are not in assigned (external vars not involved in the order)
        // vars that have already been computed while firing the current order are not added in previous loop
        // detect first  transition that have no necessary var

        int indexFirable = -1;
        int k = 0;
        for (std::vector<std::vector<Variable*>*>::iterator i = necessaryVars.begin();
             indexFirable == -1 && i!= necessaryVars.end(); ++i, ++k) {
                if ((*i)->size() == 0) {
                        indexFirable = k;
                        break;
                }
                for (std::vector<Variable*>::iterator j = (*i)->begin(); j!=(*i)->end(); ) {
                        if (isInAssigned(*j, assignedVars) )
                                //    && !isInAlreadyAssignedInDirac(*j, alreadyAssignedInDirac)
                                ++j;
                        else
                                j = (*i)->erase(j);
                        if ((*i)->size() == 0)
                                indexFirable = k;
                }
        }

        for (std::vector<std::vector<Variable*>*>::iterator i = necessaryVars.begin(); i!= necessaryVars.end(); ++i)
                delete *i;

        if (indexFirable == -1) {
                std::cerr<< "cannot determine order in diracs..." << std::endl;
                exit(1);
                // we will get around this later by choosing the least necessary stuff
        }

        // we have at last a transition to fire...
        AltaricaState * newState = ns->clone();
        enabledTransitions[indexFirable]->doTransition(this, ns, newState);
        if (alreadyAssignedInDirac == NULL)
                alreadyAssignedInDirac = assignedVars[indexFirable];
        else {
                alreadyAssignedInDirac->insert(alreadyAssignedInDirac->end(),
                                               assignedVars[indexFirable]->begin(),
                                               assignedVars[indexFirable]->end());
                delete assignedVars[indexFirable];
        }

        int j = 0;
        for (std::vector<std::vector<const Variable*>*>::iterator i = assignedVars.begin(); i!= assignedVars.end(); ++i, ++j)
                if (j != indexFirable)
                        delete *i;
        delete ns;
        return forwardDirac(newState, alreadyAssignedInDirac);
}

bool AltaricaModel::isInAssigned(Variable* v, std::vector<std::vector<const Variable*>* >& assigned) {
        for (std::vector<std::vector<const Variable*>* >::iterator i = assigned.begin(); i!= assigned.end(); ++i)
                for (std::vector<const Variable*>::iterator j = (*i)->begin(); j!= (*i)->end(); ++j)
                        if (v == *j)
                                return true;
        return false;
}

bool AltaricaModel::isInAlreadyAssignedInDirac(const Variable* v, std::vector<const Variable*>* assigned) {
        if (assigned == NULL)
                return false;
        for (std::vector<const Variable*>::iterator j = assigned->begin(); j!= assigned->end(); ++j)
                if (v == *j)
                        return true;
        return false;
}

bool AltaricaModel::isInAlreadyAssignedInDirac(std::vector<const Variable*> *vv, std::vector<const Variable*>* assigned) {
        if (assigned == NULL)
                return false;
        for (std::vector<const Variable*>::iterator i = vv->begin(); i!= vv->end(); ++i) {
                if (!isInAlreadyAssignedInDirac(*i, assigned))
                        return false;
        }
        return true;
}



AltaricaState * AltaricaModel::getNextState(AltaricaState *s, MetaEvent* me) {

        AltaricaState *ns = s->clone();
        if (me != NULL) {
    
                const std::vector<Event*> * ev = me->getEvents();
                for (std::vector<Event*>::const_iterator evi = ev->begin(); evi != ev->end(); ++evi) {
                        (*evi)->doEvent(this, s,ns);
                }
        }
  
  
        // std::cerr<< "FF diracs" << std::endl;
        ns = forwardDirac(ns);
        // std::cerr<< "FF diracs END" << std::endl;
        ns->rehash_();


        return ns;

}


void AltaricaModel::evaluateFlowVars(AltaricaState *s) const {
        for (std::vector<Variable*>::const_iterator i = flowVars.begin();
             i!= flowVars.end(); ++i)
                s->removeValue(*i);
        for (std::vector<Variable*>::const_iterator i = flowVars.begin();
             i!= flowVars.end(); ++i) {
                (*i)->evaluate(this, s);
        }
}

AltaricaModel::~AltaricaModel() {
        std::vector<Node*> toDelete;
        for (std::vector<Constant*>::iterator i = constants.begin(); i!= constants.end(); ++i)
                delete *i;
        //    delete(constants);
        for (std::vector<Node*>::iterator i = nodes.begin(); i!= nodes.end(); ++i) {
                if ((*i)->getParent() == NULL)
                        (*i)->deleteExprs();
        }
        for (std::vector<Node*>::iterator i = nodes.begin(); i!= nodes.end(); ++i) {
                if ((*i)->getParent() == NULL)
                        toDelete.push_back(*i);
        }
        for (std::vector<Node*>::iterator i = toDelete.begin(); i!= toDelete.end(); ++i) {
                delete (*i);
        }

        for (std::vector<Domain*>::iterator i = domains.begin(); i!= domains.end(); ++i)
                delete *i;
        if (metaEventsByPrio_ != NULL) {
                metaEventsByPrio_->clear();
                delete(metaEventsByPrio_);
                delete(metaEvents_);
                delete(asserts_);
        }
}


bool AltaricaModel::existsVar(std::string n) {
        MemberAccess *ma = new MemberAccess(n);
        ma->setGlobal(true);
        Variable * v = mainNode_->findVariableByName(ma);
        delete ma;
        if (v == NULL)
                return false;
        return true;
}

Value * AltaricaModel::getFlowVarValueAndRelease(AltaricaState *s, std::string n) {
        if (varStringCache.compare("") == 0) {
                varStringCache = n;
                MemberAccess *ma = new MemberAccess(n);
                ma->setGlobal(true);
                varCache = mainNode_->findFlowVariableByName(ma);
                delete ma;
                Value * vv = s->getCachedValue();
                if (vv == NULL) {
                        varCache->evaluate(this, s);
                        vv = s->getValue(varCache)->clone();
                        s->setCachedValue(vv->clone());
                        //      s->releaseFlowVars();
                } else
                        vv = vv->clone();
                return  vv;
        }
        if (varStringCache.compare(n) == 0) {
                Value *vv = s->getCachedValue();
                if (vv == NULL) {
                        varCache->evaluate(this, s);
                        vv = s->getValue(varCache)->clone();
                        s->setCachedValue(vv->clone());
                        //      s->releaseFlowVars();
                } else
                        vv = vv->clone();
                return vv;
        }

        MemberAccess *ma = new MemberAccess(n);
        ma->setGlobal(true);
        Variable * v = mainNode_->findFlowVariableByName(ma);
        delete ma;
        v->evaluate(this, s);
        Value *vv = s->getValue(v)->clone();
        //    s->releaseFlowVars();
        return vv ;
}

Value * AltaricaModel::getStateVarValue(AltaricaState *s, std::string n) {
        Variable * v = mainNode_->findStateVariableByName(n);
        return  s->getValue(v);
}

Value * AltaricaModel::getVarValueAndRelease(AltaricaState *s, std::string n) {
        Variable *v;
        if (varStringCache.compare("") == 0) {
                varStringCache = n;
                varCache = mainNode_->findFlowVariableByName(n);
                if (varCache == NULL) {
                        varCache = mainNode_->findStateVariableByName(n);
                }
        }
        if (varStringCache.compare(n) == 0)
                v = varCache;
        else {
                v = mainNode_->findFlowVariableByName(n);
                if (v == NULL)
                        v = mainNode_->findStateVariableByName(n);
        }

        if (v->isState())
                return s->getValue(v)->clone();
        else  {
                v->evaluate(this, s);
                Value * vv =   s->getValue(v)->clone();
                s->releaseFlowVars();
                return vv;
        }

        return NULL;
}



/* Orders the assertions, s.t. the heads never compare before the
* assertions where they are in the tail.
* E.g. (b = a) goes after (a = c)  */
void AltaricaModel::orders_assertions()
{
	bool changed = false;
	std::vector<size_t> already_seen;
	do {
		std::list<epoch::altarica::Assertion*>::iterator idx = getAsserts().begin();
		changed = false;

		for(auto &e : getAsserts()) {

			already_seen.push_back((size_t)e);

			// std::cout << "checking: " << e << " " << (size_t)(e) << std::endl;
			const std::vector<epoch::altarica::Variable*> * fv =   (*e).infoVars(this, true, false);

			for (std::vector<epoch::altarica::Variable*>::const_iterator j = fv->begin(); j!= fv->end(); ++j) {  // (*e).getVars()

				const std::vector<epoch::altarica::Assertion*> *ast = (*j)->getAsserts();
if (ast == NULL &&  debugSemantics)
 {
	std::cerr << "No assertions here " << std::endl;
	continue;
}
				for (std::vector<epoch::altarica::Assertion*>::const_iterator vv = ast->begin(); vv!= ast->end(); ++vv) {
					// Inserts vvt before et
					if (std::find(already_seen.begin(), already_seen.end(), (size_t)(*vv)) == already_seen.end()) {
						// std::cout << "  changed" << (size_t)e << " vs. " << (size_t)(*vv) << std::endl; ;
						getAsserts().remove(*vv);
						getAsserts().insert(idx,*vv);
						changed = true;
						break;
					}
				}
				if (changed)
					break;
			}
			if (changed)
				break;
			++idx;
			delete fv;
		}
		/* DEBUG */
//	        std::cout << "\nAssertions"<< std::endl;
//	        for(auto &e : getAsserts()) {
//	      	  size_t et = (size_t)(e);
//	      	  std::cout << et << " "<< e->getExpr()->toString()<< std::endl;
//	        }
	} while (changed);
}

Variable* AltaricaModel::getStateVar(unsigned int i) const {
        for (std::vector<Variable*>::const_iterator j = stateVars.begin(); j!= stateVars.end(); ++j)
                if ((*j)->getIndex() == i)
                        return *j;
        return NULL;
}
Variable* AltaricaModel::getFlowVar(unsigned int i) const {
        for (std::vector<Variable*>::const_iterator j = flowVars.begin(); j!= flowVars.end(); ++j)
                if ((*j)->getIndex() == i)
                        return *j;
        return NULL;
}


void AltaricaModel::setInitInfoXML(std::string initFile) {
        initInfo = new AltaricaInitInfoXML2(initFile,this);
}


AltaricaJson * AltaricaModel::setInitInfoJSON(std::string initFile) {
        return (AltaricaJson*) (initInfo = new AltaricaJson(initFile,this));
}


void AltaricaModel::purgeMetaEvents(std::vector<MetaEvent*> *metaEvents) 
{
        for (std::vector<MetaEvent*>::iterator i = metaEvents->begin(); i!=metaEvents->end(); ) {
                if (noProba(*i))
                        i = metaEvents->erase(i);
                else
                        i++;    
        }
}

