//---------------------------------------------------------------------------
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//---------------------------------------------------------------------------
#include "main.h"
#include "conf.h"
#include "tpfnct.h"
#include "tboolfnct.h"
#include "boom.h"
#include "decomp.h"
#include "implcheck.h"
#include "bufferstruct.h"
#include "cd.h"
//---------------------------------------------------------------------------
int gtrck = 0;
//---------------------------------------------------------------------------
TPfnct * CD_MinInitTerm( TBoolFnct * ofn )
{
  TPfnct * aktterm;

  aktterm = ofn->AddEmptyTermToEnd();
  aktterm->iv = new char[ofn->inpvars + 1];
  setmem( aktterm->iv, ofn->inpvars, '-' );
  aktterm->dim = ofn->inpvars;
  aktterm->ov = new char[ofn->outvars + 1];
  aktterm->ov[ofn->outvars] = 0;
  aktterm->iv[ofn->inpvars] = 0;
  return aktterm;
}
//---------------------------------------------------------------------------
unsigned long SaturAdd( unsigned long a, unsigned long b )
{
  unsigned long d;

  d = LONG_MAX - a;
  if ( b > d ) return LONG_MAX;
  else
    return a + b;
}
//---------------------------------------------------------------------------
void RestoreOffset( TBoolFnct * fn, int ovr )
{
  TPfnct * ff;

  ff = fn->offset[ovr];
  while ( ff != 0 )
  {
    ff->cvd = 0;
    ff = ff->next;
  }
}
//---------------------------------------------------------------------------
TBoolFnct * CD_Min( TBoolFnct * fn, int ovr, CD_Opts * cdo, int IC_memory, int IC_type, int IE_type, TGauge gauge, TermTree * imp_test, TermTree * offtree, TermTree * i_buffer, TBoolFnct * e_buffer )
{
  unsigned long * numof = 0, * o_numof, * numof0 = 0, * o_numof0 = 0;
  TBoolFnct * ofn;
  TPfnct * aktterm = new TPfnct();
  TPfnct * ff = new TPfnct();
  int i, j, k;
  int icovs;
  int * cbuf; // buffer for candidates
  int cnum; // number of candidates
  int * cbuf2; // 2nd buffer for candidates
  int cnum2; // 2nd number of candidates
  int * tbuf; // temporary pointer
  int prom;
  int pol;
  unsigned long mcount;
  int implok;
  long wght;
  int cyc;
  double f1, f2;
  unsigned long f;
  TBoolFnct * fn2; //= new TBoolFnct();
  TBoolFnct * fn3;// = new TBoolFnct();
  TPfnct * ff1 = new TPfnct();
  TPfnct * ff2 = new TPfnct();
  int tnum;
  int nf1, nf2;
  unsigned long tn1;
  int block;
  int penalty;
  char * DFT_iusg;

  FILE * tf = 0;

#if (trck&1)
  tf = fopen( "track.txt", "a" );
  fprintf( tf, "CD-S\n" );
#endif

  ofn = fn->CreateChildFnct();
  ofn->type = 0;
  o_numof = new unsigned long[fn->inpvars * 2];
  o_numof0 = new unsigned long[fn->inpvars * 2];
  cbuf = new int[fn->inpvars * 2];
  cbuf2 = new int[fn->inpvars * 2];

  if ( cdo->DECOMP_CDenable == 1 ) block = fn->decomp->outs[ovr];

  if ( cdo->DFT_CDenable == 1 )
  {
    DFT_iusg = new char[fn->inpvars];
    setmem( DFT_iusg, fn->inpvars, 0 );
  }

  fn3 = fn->CreateChildFnct();
  ff = fn->onset[ovr];
  while ( ff != 0 )
  {
    ff1 = new TPfnct;
    if ( fn3->last == 0 ) fn3->last = ff1;
    ff1->iv = ff->iv;
    ff1->ov = 0;
    ff1->cov = 0;
    ff1->covfield = 0;
    ff1->dim = ff->dim;
    ff1->prime = 0;
    ff1->origin = 1;
    ff1->next = fn3->fnct;
    fn3->fnct = ff1;
    fn3->terms++;
    ff = ff->next;
  }

  fn2 = fn->CreateChildFnct();
  ff = fn3->fnct;
  while ( ff != 0 )
  {
    ff1 = new TPfnct;
    ff1->iv = ff->iv;
    ff1->ov = 0;
    ff1->cov = 0;
    ff1->prime = 0;
    ff1->origin = 1;
    ff1->covfield = 0;
    ff1->dim = ff->dim;
    ff1->next = 0;
    if ( fn2->last != 0 ) fn2->last->next = ff1;
    fn2->last = ff1;
    if ( fn2->fnct == 0 ) fn2->fnct = ff1;
    fn2->terms++;
    ff = ff->next;
  }

  icovs = fn2->terms;
  gauge.NewGauge( "CD-search", icovs, 1 );
  aktterm = CD_MinInitTerm( ofn );
  cyc = 0;
  while ( fn2->terms != 0 )
  {
    cyc++;
    gtrck++;
#if (trck&3)
    //fprintf(tf," cyc %i; %i\n",cyc,gtrck);
#endif
    mcount = 0; //hledani maxima
    tnum = 0;
    cnum = 0;
    nf1 = 0;
    nf2 = 0;

    numof0 = o_numof0;
    for ( i = 0; i < fn->inpvars * 2; i++ )
    {
      o_numof[i] = 0;
    }
    numof = o_numof;
    numof0 = o_numof0;

    // NUMOF computation
    for ( j = 0; j < fn->inpvars; j++ )
      if ( aktterm->iv[j] == '-' )
      {
        ff = fn2->fnct;
        for ( i = 0; i < fn2->terms; i++ )
        {
          if ( cdo->CD_weights == 1 )
          {
            if ( ff->dim > 30 ) wght = LONG_MAX;
            else
              wght = 2 * pow( 2, ff->dim );
          }
          else
            wght = 1;
          if ( ff->iv[j] == '0' ) numof[j] = SaturAdd( wght, numof[j] );
          else if ( ff->iv[j] == '1' ) numof[j + fn->inpvars] = SaturAdd( wght, numof[j + fn->inpvars] );
          else if ( cdo->CD_weights == 1 )
            if ( ff->iv[j] == '-' && false )
            {
              numof[j] = SaturAdd( wght / 2, numof[j] );
              numof[j + fn->inpvars] = SaturAdd( wght / 2, numof[j + fn->inpvars] );
            }
          ff = ff->next;
        }
      }
    // Selection function
    for ( j = 0; j < 2 * fn->inpvars; j++ )
    {
      f = numof[j];
      if ( cdo->DECOMP_CDenable == 1 )
      {
        if ( fn->decomp->inps[block] [j % fn->inpvars] != 'N' )
        {
          f *= ( cdo->DECOMP_CD_force + 1 );
          if ( cdo->DECOMP_CD_loadforce > 0 ) f *= cdo->DECOMP_CD_loadforce;
        }
        else if ( cdo->DECOMP_CD_loadforce > 0 )
        {
          penalty = 1;
          for ( i = 0; i < fn->decomp->blocks; i++ )
            if ( i != block && fn->decomp->inps[i] [j % fn->inpvars] != 'N' )
              penalty++;
          if ( penalty == 1 ) f *= cdo->DECOMP_CD_loadforce;
        }
      }
      if ( cdo->DFT_CDenable == 1 )
        if ( DFT_iusg[j % fn->inpvars] == 1 ) f *= ( cdo->DFT_CD_force + 1 );
      if ( f > 0 )
      {
        tnum++;
        if ( f == mcount && mcount != 0 ) cbuf[cnum++] = j;
        if ( f > mcount )
        {
          mcount = f;
          cnum = 0;
          cbuf[cnum++] = j;
        }
      }
    }

    i = random( 100 ); // mutace
    if ( i < cdo->CD_mutations )
    {
      prom = random( fn->inpvars * 2 );
      implok = 0;
      while ( numof[prom] == 0 )
      {
        prom++;
        prom = prom % ( 2 * fn->inpvars );
      }
      pol = prom / fn->inpvars;
      prom = prom % fn->inpvars;
    }
    else
    {

      //1. kolo - je/neni implikant

      implok = 0;
      cnum2 = 0;
      if ( cnum > 1 )
      {
        for ( i = 0; i < cnum; i++ )
        {
          prom = cbuf[i] % fn->inpvars;
          pol = cbuf[i] / fn->inpvars;
          aktterm->iv[prom] = pol + 48;
          j = ImplCheck( fn, aktterm, ovr, IC_memory, IC_type, imp_test, offtree );
#if (trck&3)
          //fprintf(tf,"  Trying: %s -> %i\n",aktterm->iv,j);
#endif
          aktterm->iv[prom] = '-';
          if ( j == 1 )
          {
            cbuf2[cnum2++] = cbuf[i];
            implok = 1;
          }
        }
        if ( cnum2 > 0 )
        {
          tbuf = cbuf;
          cbuf = cbuf2;
          cbuf2 = tbuf;
          cnum = cnum2;
        }
      }
#if (trck&1)
      if ( cnum2 == 0 ) fprintf( tf, "     " ); fflush( tf );
#endif

      // 3. kolo - finalni nahodny vyber promenne

      j = random( cnum );
      prom = cbuf[j] % fn->inpvars;
      pol = cbuf[j] / fn->inpvars;
    }

    ff = fn2->fnct; //zapis
    ff2 = 0;
    while ( ff != 0 )
    {
      if ( abs( ff->iv[prom] - ( pol + 48 ) ) == 1 || ff->iv[prom] == '-' )
      {
        if ( ff == fn2->fnct )
        {
          fn2->fnct = ff->next;
          delete ff;
          ff = fn2->fnct;
          if ( ff == 0 ) fn2->last = 0;
        }
        else
        {
          ff2->next = ff->next;
          delete ff;
          ff = ff2->next;
          if ( ff2->next == 0 ) fn2->last = ff2;
        }
        fn2->terms--;
      }
      else
      {
        ff2 = ff;
        ff = ff->next;
      }
    }
    ff = fn->offset[ovr]; //vyrazeni offsetu
    for ( i = 0; i < fn->offterms[ovr]; i++ )
    {
      if ( ( ff->cvd == 0 ) && ( abs( ff->iv[prom] - ( pol + 48 ) ) == 1 ) ) ff->cvd = 1;
      ff = ff->next;
    }

    aktterm->iv[prom] = pol + '0';
    aktterm->dim--;
    if ( cdo->DECOMP_CDenable == 1 && fn->decomp->inps[block] [prom] != 'F' ) fn->decomp->inps[block] [prom] = '1';
    if ( cdo->DFT_CDenable == 1 ) DFT_iusg[prom] = 1;
#if (trck&1)
    fprintf( tf, "  %i %s (%c)", prom + fn->inpvars * pol, aktterm->iv,
         ( nod->next[prom + fn->inpvars * pol] == 0 ) ? '-' : nod->next[prom + fn->inpvars * pol]->closed + '0' );
    fflush( tf );
#endif

#if (trck&1)
    fprintf( tf, "\n" ); fflush( tf );
#endif
#if (trck&3)
    //fprintf(tf,"  Select: %s\n",aktterm->iv);
#endif
    if ( implok == 0 ) implok = ImplCheck( fn, aktterm, ovr, IC_memory, IC_type, imp_test, offtree );
    if ( implok == 1 )
    {
      if ( IE_type > 0 ) k = i_buffer->AddToTreeHistory( aktterm );
      if ( ( IE_type > 0 && k == 0 ) || ( IE_type == 0 ) )
      {
        AddToHistory( e_buffer, aktterm );
        e_buffer->last->dvar = ovr;
        for ( j = 0; j < fn->inpvars; j++ ) e_buffer->last->ov[j] = '0';
        e_buffer->last->ov[j] = 0;
#if (trck&3)
        //fprintf(tf,"  Addd:   %s %s\n",buffers->e_buffer->last->iv,buffers->e_buffer->last->ov);
#endif
      }
      ff = fn3->fnct;
      ff1 = fn2->fnct;
      while ( ff != 0 )
      {
        if ( ff1 != 0 )
          if ( ff->iv == ff1->iv )
          {
            if ( ff == fn3->fnct )
            {
              fn3->fnct = ff->next;
              delete ff;
              ff = fn3->fnct;
              if ( ff == 0 ) fn3->last = 0;
            }
            else
            {
              ff2->next = ff->next;
              delete ff;
              ff = ff2->next;
              if ( ff2->next == 0 ) fn3->last = ff2;
            }
            fn3->terms--;
            ff1 = ff1->next;
            continue;
          }
        ff2 = ff;
        ff = ff->next;
      }
      ff = fn2->fnct;
      while ( ff != 0 )
      {
        ff1 = ff->next;
        delete ff;
        fn2->terms--;
        ff = ff1;
      }
      ff = fn3->fnct;
      fn2->terms = 0;
      fn2->fnct = 0;
      fn2->last = 0;
      while ( ff != 0 )
      {
        ff1 = new TPfnct;
        ff1->iv = ff->iv;
        ff1->ov = 0;
        ff1->cov = 0;
        ff1->covfield = 0;
        ff1->dim = ff->dim;
        ff1->prime = 0;
        ff1->origin = 1;
        ff1->next = 0;
        if ( fn2->last != 0 ) fn2->last->next = ff1;
        fn2->last = ff1;
        if ( fn2->fnct == 0 ) fn2->fnct = ff1;
        fn2->terms++;
        ff = ff->next;
      }
      RestoreOffset( fn, ovr );
      if ( fn2->terms != 0 )
      {
        aktterm = CD_MinInitTerm( ofn );
        cyc = 0;
      }
      if ( gauge.GaugeStep( icovs - fn3->terms, 0 ) == 2 ) break;
    }
  }
  if ( cdo->DFT_CDenable == 1 )
    delete[] DFT_iusg;
  delete fn2;
  delete fn3;
  delete[] cbuf;
  delete[] cbuf2;
  delete[] o_numof;
  delete[] numof0;
  gauge.CloseGauge();
#if (trck&1)
  fclose( tf );
#endif
  return ofn;
}
//---------------------------------------------------------------------------
