#pragma implementation

#include "hpg.h"
#include "indiv.h"


void error( const char* str1 )
{
  cerr << str1 << '\n';
  exit(1);
}

double myrand(){
  //ensure returned value is below 1.
  return (double)(random() % (RAND_MAX))/((double)RAND_MAX);
}

void hpg::load_parameters(int argc, char **argv){

  char *filename;

  if (argc <= 1 )
    error("Please supply name of parameter file");

  argv++;
  filename = new char[100];
  strcpy(filename, *argv);
  params.load(filename);
  delete filename;

  n=params.intp("n");
  nodims=n;
  kmin=params.intp("kmin");
  kmax=params.intp("kmax");
  mmin=params.intp("mmin");
  mmax=params.intp("mmax");
  arity=params.intp("arity");
  if (kmin<2)
    error("k should be >= 2");
  fixedfitnesscontribution=params.boolp("fixedfitnesscontribution",false);
  randomorder=params.boolp("randomorder",true);
}

void hpg::vectorize(int *values, vector<int> &v, int n){
  v.clear();
  for (int i=0;i<n;i++)
    v.push_back(values[i]);
}

void hpg::appendvector(vector<int> &a,vector<int> &b){
  //append a to b
  vector<int>::iterator it=a.begin();
  while(it!=a.end()){
    b.push_back(*it);
    it++;
  }
}

void hpg::appendvector(vector<module*> &a,vector<module*> &b){
  //append a to b
  vector<module*>::iterator it=a.begin();
  while(it!=a.end()){
    b.push_back(*it);
    it++;
  }
}

void hpg::print_module(module* mod){
  cout<<"M"<<mod->uid<<": ";
  for (vector<int>::iterator it=mod->vars.begin();it!=mod->vars.end();it++)
    cout<<*it<<" ";
  cout<<endl;
  int i=0;
  for (vector<vector<int> >::iterator it=mod->optvalues.begin();it!=mod->optvalues.end();it++){
    cout<<"setting ";
    for(vector<int>::iterator it1=(*it).begin();it1!=(*it).end();it1++)
      cout<<*it1<<" ";
    if ((int)(mod->ftncontrib.size())>i)
      cout<<"contrib "<<*(mod->ftncontrib.begin()+i);
    cout<<endl;
    i++;
  }
}

void hpg::printvector(vector<int> &vals){
  vector<int>::iterator it;
  it=vals.begin();
  while(it!=vals.end()){
    cout<<*it<<" ";
    it++;
  }
  cout<<endl;
}

void hpg::printvector(vector<vector<int> > &vals){
  vector<vector<int> >::iterator it=vals.begin();
  while(it!=vals.end()){
    printvector(*it);
    it++;
  }
}

void hpg::printvector(vector<module*> &mods){
  for (vector<module*>::iterator it=mods.begin();it!=mods.end();it++)
    print_module(*it);
}

void hpg::printvector(vector<vector<module*> > &mods){
  int l=0;
  for (vector<vector<module*> >::iterator it=mods.begin();it!=mods.end();it++){
    cout<<"level "<<l<<": "<<endl;
    printvector(*it);
    cout<<endl;
    l++;
  }
}

module *hpg::create_module(){
  module *mod = new module();
  mod->uid=nextuid;
  nextuid++;
  return mod;
}

bool hpg::equals(vector<int> &v1,vector<int> &v2){
  bool ok=true;
  vector<int>::iterator it1,it2;
  it1=v1.begin();it2=v2.begin();
  if (v1.size()!=v2.size())    
    error("vector of unequal length!");
  
  while((it1!=v1.end())&&(it2!=v2.end())){
    if (*it1!=*it2)
      ok=false;
    it1++;it2++;
  }
  return ok;
}

void hpg::get_values(vector<int> &vars, vector<int> positions, vector<int> &vals){
  vals.clear();
  for (vector<int>::iterator it=positions.begin();it!=positions.end();it++){
    if ((*it)>=(int)vars.size())
      error("get vals: index out of bounds");
    vals.push_back(*(vars.begin()+*it));
  }
}

double hpg::fitness(vector<int> &vars){
  module *m;
  double ftn=0;
  vector<int> vals;
  for (vector<vector<module*> >::iterator it=mods.begin();it!=mods.end();it++)//for each level
    for (vector<module*>::iterator it1=(*it).begin();it1!=(*it).end();it1++){//for each mod
      m=*it1;
      get_values(vars,m->vars,vals);
      int ind=0;      
      for (vector<vector<int> >::iterator it2=m->optvalues.begin();it2!=m->optvalues.end();it2++){
	if (equals(*it2,vals))
	  ftn+=*(m->ftncontrib.begin()+ind);	
	ind++;
      }
    }
  return ftn;
}


void hpg::add_options(vector<vector<int> > &options, vector<vector<int> > &vals){
  vector<int> prefix, newvals;

  if (options.size()==0){
    for (vector<vector<int> >::iterator it=vals.begin();it!=vals.end();it++){      
      appendvector(*it,newvals);
      options.push_back(newvals);
      newvals.clear();
    }
  }
  else{//combine vals with all existing options
    if (vals.size()<2)
      error("add opt: vals");
    int orgnr=options.size();
    int i=0;
    for (vector<vector<int> >::iterator it=options.begin();i<orgnr;it++){
      it=options.begin()+i;//it may have changed due to reallocation -> must determine iterator anew!
      if (it==options.end())
	error("options reached end!");
      prefix=*it;

      for (vector<vector<int> >::iterator it1=vals.begin();it1!=vals.end();it1++){
	appendvector(prefix,newvals);
	appendvector(*it1,newvals);
	options.push_back(newvals);
	newvals.clear();
      }
      i++;
    }
    vector<vector<int> >::iterator it3=options.begin();
    for (int i=0; i<orgnr;i++)
      it3=options.erase(it3);
  }

}

void hpg::assign_fitness(){
  module *m;
  int level=0;
  for (vector<vector<module*> >::iterator it=mods.begin();it!=mods.end();it++){//for each level
    for (vector<module*>::iterator it1=(*it).begin();it1!=(*it).end();it1++){//for each mod
      m=*it1;
      for (int i=0;i<(int)m->optvalues.size();i++)
	if (fixedfitnesscontribution)
	  m->ftncontrib.push_back(1);//each module worth 1 extra point.
	else
	  m->ftncontrib.push_back(m->vars.size());	
    }
    level++;
  }
}

void hpg::select_options(vector<vector<int> > &options, module *mc, unsigned int m){
  //    if (options.size()==0)
  //  cout<<"warning: m too large"<<endl;
    while ((nosettings<m)&&(options.size()>0)){
    vals.clear();
    vector<vector<int> >::iterator it2;

    int ind=(int)floor(myrand()*options.size());
    it2=options.begin()+ind;
    appendvector(*it2,vals);
    mc->optvalues.push_back(vals);		
    options.erase(it2);
    nosettings++;
  }    

  if (mods.size()<=level)
    error("level not reserved");
  (*(mods.begin()+level)).push_back(mc);
}

void hpg::create_options(vector<vector<int> > &options, module *mc){

  for (vector<module*>::iterator it=mc->parts.begin();it!=mc->parts.end();it++){
    if ((*it)->optvalues.size()==0)
      error("module component has no opt values");
    add_options(options,(*it)->optvalues);
  }

  for (vector<vector<int> >::iterator it=mc->optvalues.begin();it!=mc->optvalues.end();it++){
    vector<vector<int> >::iterator it1=options.begin();
    while(!equals(*it1,*it)){
      it1++;
      if (it1==options.end())
	error("option not found!");
    }
    options.erase(it1);
  }
  
}

void hpg::create_remaining_settings(module *mc, unsigned int m){
  vector<vector<int> > options;

  //  if (nosettings>m)
  //cout<<"warning: m too small"<<endl;

  if (nosettings<m)
    create_options(options,mc);

  select_options(options, mc, m);
}

void hpg::use_prev_layer_settings(module *mc, unsigned int m){

  bool done=false; 
  for (vector<module*>::iterator it=mc->parts.begin();it!=mc->parts.end();it++)
    (*it)->vals=(*it)->optvalues;//make temporary copy of optvalues to track which ones used
  while (!done){//first ensure all values are used
    vals.clear();
    for (vector<module*>::iterator it=mc->parts.begin();it!=mc->parts.end();it++){
      vector<vector<int> >::iterator it1;
      if ((*it)->vals.size()>0){
	int index=(int)floor(myrand()*(*it)->vals.size());
	it1=(*it)->vals.begin()+index;//a value vector
      }
      else
	it1=(*it)->optvalues.begin()+(int)floor(myrand()*(*it)->optvalues.size());
      if ((*it)->optvalues.size()==0)
	error("no opt values!");
      appendvector(*it1,vals);
      if ((*it)->vals.size()>0){//no need to delete contents; shallow copy
	(*it)->vals.erase(it1);	   
      }
    }
    mc->optvalues.push_back(vals);

    done=true;//done?
    for (vector<module*>::iterator it=mc->parts.begin();it!=mc->parts.end();it++)
      if ((*it)->vals.size()>0)
	done=false;	    	
	
    nosettings++;
  }
}

void hpg::choose_parts(module *mc){

  vector<module*>::iterator it;

  if (!randomorder)
    it=todo.begin();

  unsigned int k=kmin+(int)floor(myrand()*(kmax+1-kmin));
  for (unsigned int j=0;((j<k) && (todo.size()>0));j++){       
    if (randomorder)
      it=todo.begin()+(int)floor(myrand()*todo.size());
    mc->parts.push_back(*it);
    appendvector((*it)->vars,mc->vars);
    it=todo.erase(it);
  }


}


void hpg::generate_problem(){
  module *mc;
  level=0;

  mods.push_back(modules);//create space for first layer of modules

  while ((*(mods.begin()+level)).size() >= kmin){
    todo=*(mods.begin()+level);//combine all modules of current level into larger ones
    level++;
    modules.clear();
    mods.push_back(modules);//new layer of modules

    while (todo.size() >= kmin){
      mc=create_module();//combine randomly selected modules into new combination
      choose_parts(mc);

      nosettings=0;//select context-optimal settings for the new composite module
      unsigned int m=mmin+(int)floor(myrand()*(mmax+1-mmin));

      use_prev_layer_settings(mc, m);//first ensure all co-settings of previous layer are used
      create_remaining_settings(mc, m);//randomly select remaining settings until m formed
    }
    appendvector(todo,*(mods.begin()+level));
    todo.clear();
  }

  assign_fitness(); //assign fitness contributions
}

void hpg::initialize(int argc, char **argv){
  nextuid=0;
  load_parameters(argc, argv);
}

indiv *hpg::draw_indiv(){
  module *mod;
  indiv *ind = new indiv(this);
  vector<int> covered;
  vector<module*>::iterator mit;

  for (mit=maxmods.begin();mit!=maxmods.end();mit++){
    mod=*mit;

    ind->modules.push_back(mod);
    ind->valindex.push_back((int)floor(myrand()*mod->optvalues.size()));//randomly choose setting
    appendvector(mod->vars,covered);
  }

    ind->getvalues();//find ordered m.vars and m.vals 
    return ind;
}

void hpg::initialize_modules(){
  module *mod;
  vector<int> vals;

  for (unsigned int i=0;i<n;i++){
    mod=create_module();    
    mod->vars.push_back(i);

    for (unsigned int j=0;j<arity;j++){
      vals.push_back(j);
      mod->optvalues.push_back(vals);
      vals.clear();
    }

    modules.push_back(mod);
    maxmods.push_back(mod);
  }
}


hpg::hpg(int argc, char **argv){
  indiv *ind;

  initialize(argc, argv);

  initialize_modules();

  cout<<endl<<"Generating problem"<<endl;
    
  generate_problem();

  cout<<"Created these modules:"<<endl;
  printvector(mods);
   
  cout<<"Some example evaluations:"<<endl;
  for (int i=0;i<10;i++){
    ind=draw_indiv();
    printvector(ind->vals);
    cout<<"fitness: "<<fitness(ind->vals)<<endl;       
    delete ind;
  }
          
}

int main(int argc, char **argv){                                                                                          
  new hpg(argc, argv);
  return 0;
}



