/* ga_optimizer.cc */

#include "ga_optimizer.h"
#include "EvStdMetric.hh"
#include "integer.h"
#include "root.h"
#include "function.h"
#include "element.h"

bool ga_optimizer::init( void ) {
  if( pMut + pCrossover > 1.0 ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- illegal pMut=" << pMut 
	   << " and pCrossover " << pCrossover 
	   << " sum to value greater than 1\n";
    }
    return true;
  }

  /**** 1 -- find steady_state and exit conditions, and nIndividuals. 
    check types */
  list<long unsigned int> ga_deps;
  steady_state = ( dc_element * )lookup_child( steady_state_label, true );
  if( steady_state && ( steady_state->type() != Element_t || 
			steady_state->get_rtype() != Boolean_t ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- " << steady_state->full_type() 
	   << " must be a boolean element\n";
    }
    return true;
  }
  
  exit_cond = ( dc_element * )lookup_child( exit_cond_label, true );
  if( exit_cond && ( exit_cond->type() != Element_t || 
		     exit_cond->get_rtype() != Boolean_t ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- " << exit_cond->full_type() 
	   << " must be a boolean element\n";
    }
    return true;
  }
    
  nIndividuals = ( dc_element * )lookup_child( nIndividuals_label, true );
  if( nIndividuals == nil || ( nIndividuals->type() != Element_t || 
			       nIndividuals->get_rtype() != Int_t ) ) {
    dc_trace( TRACE_ERROR ) {
      if( nIndividuals ) {
	cerr << "ga_optimizer::init -- " << nIndividuals->full_type() 
	     << " must be an integer element\n";
      } else {
	cerr << "ga_optimizer::init -- missing " << nIndividuals_label
	     << ".  must be an integer element\n";
      }
      return true;
    }
  }

  int nerrs;
  if( fitness == nil ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- missing performance function\n";
    }
    return true;
  }

  if( ( nerrs = fitness->rehash() ) != 0 ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- " << nerrs 
	   << "errors hashing performance function ( " << *fitness << " )\n";
    }
    return true;
  }
  if( fitness->get_rtype() != Real_t ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- performance function ( "
	   << *fitness << " ) does not have type real.  type is "
	   << dc_type_string[fitness->get_rtype()] << "\n";
    }
    return true;
  }

  /**** 2 -- set all children to active from dormant frozen */
  for_children( set_child_a, true );
  if( ( nerrs = rehash_root() ) != 0 ) {
    cerr << nerrs << "ga_optimizer::init -- errors resetting\n";
  }

  /**** 3 -- set up inputs */
  if( inputs.find_inputs( this ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- error linking inputs\n";
    }
    return true;
  }

  /**** 4 -- set up ga */
  gPerformanceFunction = this;
  gPerformanceType = -1;

  fSearchDimension = inputs.nBits();
  fBestPerformance = minPerf;
  dc_int *ps = ( dc_int * )nIndividuals->get();
  if( ps == nil ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::init -- failed to get valid integer from " 
	   << nIndividuals->full_type() << "\n";
    }
  }

  int popsize =( long int )ps->get();
  
  Algor = new EvMikeAlgor<EvBitGenome>( popsize, nGens, pCrossover, pMut, 
					mutVar );

  generation->set( 0 );

  dc_trace( TRACE_FEW ) {
    cout << "INITED " << full_type() << "\n";
    cout << "fitness = ( " << *fitness << " )\n";
    cout << "PopulationSize = " << popsize << "\n";
    cout << "NumGens = " << nGens << "\n";
    cout << "pCrossover = " << pCrossover << "\n";
    cout << "pMut = " << pMut << "\n";
    cout << "mutVar = " << mutVar << "\n";
    cout << "\nInputs: " << inputs.nBits() << "\n" << inputs << "\n";
  }

  return false;
}

bool ga_optimizer::run( void ) {
  best = Algor->ExecAndReturn();
  
  return false;
}

bool ga_optimizer::finish( void ) {
  Reset();
  
  if( inputs.set( best.Genome() ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::finish -- failed to set inputs\n";
    }
    return true;
  }
  dc_trace( TRACE_FEW ) {
    cout << "BEST GENOME\n" << inputs << "\n";
  }

  return false;
}

bool ga_optimizer::output( void ) {
  return false;
}

void ga_optimizer::cleanup( void ) {
  for_children( set_child_df, true );
  if( Algor ) delete( Algor );
  Algor = nil;
  gPerformanceFunction = nil;
}


/******************************************************************************/

double ga_optimizer::Evaluate( const StdBitString &bstring ) {
  Reset();

  //  cerr << "Evaluating (" << bstring << ")\n";
  if( inputs.set( bstring ) ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::Evaluate -- failed to set inputs with genome ( " 
	   << bstring << " )\n";
    }
    return minPerf;
  }

  double performanceValue = Simulate();
  if( performanceValue > fBestPerformance ) {
    fBestPerformance = performanceValue;
  }

  return( performanceValue );
}

double ga_optimizer::Simulate( void ) {
  double performance = 0;

  if( exit_cond == nil ) {
    performance = Step( time_step ) * time_step;

  } else {
    while( 1 ) {
      performance += Step( time_step ) * time_step;
      
      dc_data *e = exit_cond->get();
      if( !e || e->type() != Boolean_t ) {
	cerr << "ga_optimizer::Simulate -- exit condition ( " << *exit_cond
	     << " ) failed to evaluate to boolean\n";
	return minPerf;
      }
      bool exit_b = ( ( dc_boolean * )e )->get();
      if( exit_b ) break;
    }
  }

  dc_trace( TRACE_MANY ) {
    cout << "Perf = " << performance << " at time " << root.iter_counter->t()
	 << ".  Best = " << fBestPerformance << "\n";
  }

  return performance;
}

double ga_optimizer::Step( double stepSize ) {
  double t = 0;
  while( t < stepSize ) {
    t += advance_time( stepSize - t );
  }

  dc_data *d;
  d = fitness->evaluate();
  if( !d || d->type() != Real_t ) {
    dc_trace( TRACE_ERROR ) {
      cerr << "ga_optimizer::Simulate -- fitness function ( " << *exit_cond
	   << " ) failed to evaluate to boolean\n";
    }
    return minPerf;
  }
  double perf = ( ( dc_real * )d )->get();
  if( d->is_temporary() ) delete( d );

  return perf;
}

void ga_optimizer::Reset( void ) {
  root.iter_counter->reset();

  if( steady_state == nil ) return;

  /* advance time to steady state */
  double t = 0;
  while( 1 ) {
    dc_data *d = steady_state->get();
    if( d == nil /* || d->type() != Boolean_t */ ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "ga_optimizer::Reset -- evaluation of  steady state condition ("
	     << *steady_state << " ) failed\n";
      }
      return;// true;
    }
    
    if( ( ( ( dc_boolean * )d )->get() ) ) {
      dc_trace( TRACE_MANY ) {
	cout << "ga_optimizer::Reset -- steady state reached at time " << t
	     << "\n";
      }
      break;
    } else if( t >= steady_state_max_iters ) {
      dc_trace( TRACE_ERROR ) {
	cerr << "ga_optimizer::Reset -- exited after time " 
	     << steady_state_max_iters << " before steady state reached\n";
      }
      break;
    }
    
      t += advance_time( time_step );
  }
}

/******************************************************************************/


ga_optimizer::~ga_optimizer( void ) {
  clear_inputs();
}

void ga_optimizer::clear_inputs( void ) {
  inputs.clear();
}
