/*
	What's new (wrt. 2.4)
	---------------------
	
	bad GEs info in -info corrected

	What's new (wrt. 2.5)
	---------------------

	- -OW option
    - memory allocation bug in MakeCovField fixed (making BOOM crash)
    - error in VHDL export corrected
    - bug in -endcov fixed

	What's new (wrt. 2.6)
	---------------------

    - error in VHDL, Verilog and Blif export corrected
    - bug in -info fixed
    - report file output
    - noIR option
    - OVn, -OWn (VHDL export limit)

*/


//---------------------------------------------------------------------------
#include <stdio.h>  // printf
#include <stdlib.h> // atoi
#include <string.h>
#include <process.h>
#include <windows.h>
//---------------------------------------------------------------------------
 
#include "main.h"
#include "boom.h"
#include "checks.h"
#include "tboolfnct.h"
#include "decomp.h"
#include "cd.h"
#include "cover.h"

float GetMyCPUTime()
{
    HANDLE hProcess;	// specifies the process of interest
    FILETIME lpCreationTime;	// when the process was created
    FILETIME lpExitTime;	// when the process exited
    FILETIME lpKernelTime;	// time the process has spent in kernel mode
    FILETIME lpUserTime;
    LARGE_INTEGER t1, t2;


    hProcess = GetCurrentProcess();
    GetProcessTimes(hProcess, &lpCreationTime, &lpExitTime, &lpKernelTime, &lpUserTime);
    memcpy(&t1, &lpUserTime, sizeof(FILETIME));
    memcpy(&t2, &lpKernelTime, sizeof(FILETIME));
    return ((float)(t1.QuadPart+t2.QuadPart)/10000000);
}

//---------------------------------------------------------------------------
int NewGauge( char * name, int max, int context )
{
  return 0;
};
//---------------------------------------------------------------------------
void CloseGauge()
{
};
//---------------------------------------------------------------------------
int GaugeStep( int val, int context )
{
  return 0;
};
//---------------------------------------------------------------------------
void help()
{
  fprintf( stderr, "syntax: BOOM [options] source [destination] [report]\n" );
  fprintf( stderr, "Minimization Options:\n" );
  fprintf( stderr, "    -FRn  Define FC-Min:CD-search ratio n (0-100), 0 is default\n" );
  fprintf( stderr, "    -DFn  Define the FC-Min Depth Factor, 10 is default\n" );
  fprintf( stderr, "    -DDx  Define the FC-Min Depth Factor Direction\n" );
  fprintf( stderr, "             0   DF = 1:n (from -DFn)\n" );
  fprintf( stderr, "             1   DF = n:1 (from -DFn), default\n" );
  fprintf( stderr, "    -CMn  Define CD-search mutations ratio n (0-100)\n" );
  fprintf( stderr, "    -RMn  Define implicant reduction mutations ratio n (0-100)\n" );
  fprintf( stderr, "    -Ex   Select implicant expansion type:\n" );
  fprintf( stderr, "             0   Sequential search\n" );
  fprintf( stderr, "             1   Distributed multiple IE\n" );
  fprintf( stderr, "             2   Distributed exhaustive IE\n" );
  fprintf( stderr, "             3   Multiple IE (default)\n" );
  fprintf( stderr, "             4   Exhaustive IE\n" );
  fprintf( stderr, "    -CPx  Select the covering problem solution algorithm:\n" );
  fprintf( stderr, "             0   LCMC\n" );
  fprintf( stderr, "             1   Contributive selection (default)\n" );
  fprintf( stderr, "             2   Contributive removal\n" );
  fprintf( stderr, "             3   Exact Aura cover\n" );
  fprintf( stderr, "             4   Auto select cover procedure\n" );
  fprintf( stderr, "             5   Natural Heuristic cover\n" );
  fprintf( stderr, "             6   ContribAdd Heuristic cover\n" );
  fprintf( stderr, "             7   Roth Positiv Heuristic cover\n" );
  fprintf( stderr, "             8   Servit Heuristic cover\n" );
  fprintf( stderr, "    -tx Stop Aura after x seconds\n" );
  fprintf( stderr, "    -fx Effort level, 1-10, 1 - fastest cover & low quality solution, 10 - slowest cover & high quality solution\n" );
  fprintf( stderr, "    -Sxn  Define stopping criterion x of value n:\n" );
  fprintf( stderr, "             t   stop after n seconds\n" );
  fprintf( stderr, "             i   stop after n iterations (default is Si1)\n" );
  fprintf( stderr, "             n   stopping interval equal to n\n" );
  fprintf( stderr, "             q   stop when quality of solution meets n\n" );
  fprintf( stderr, "    -Qx   Define optimization criterion x:\n" );
  fprintf( stderr, "             t   terms\n" );
  fprintf( stderr, "             l   literals\n" );
  fprintf( stderr, "             o   output cost\n" );
  fprintf( stderr, "             b   literals+output cost (default)\n" );
  fprintf( stderr, "             g   number of gates\n" );
  fprintf( stderr, "             G   number of gate equivalents\n" );
  fprintf( stderr, "    -single   Single-output minimization\n" );
  fprintf( stderr, "    -endcov   Cover at the end only\n" );
  fprintf( stderr, "    -SD   Substitute output don't cares\n" );
  fprintf( stderr, "    -Ox[n]   Define output format:\n" );
  fprintf( stderr, "             P   PLA (default)\n" );
  fprintf( stderr, "             V[n]   VHDL. If n is specified, the function is split into max. n-input gates\n" );
  fprintf( stderr, "             W[n]   VHDL - inputs and outputs as vectors.  If n is specified, the function is split into max. n-input gates\n" );
  fprintf( stderr, "             v   Verilog\n" );
  fprintf( stderr, "             B   BLIF\n" );
  fprintf( stderr, "             E   Equations\n" );
  fprintf( stderr, "             H   HTML (table)\n" );
  fprintf( stderr, "    -nocheck      Don't check the input function for consistence\n" );
  fprintf( stderr, "    -noIR         Don't perform the implicant reduction\n" );
  fprintf( stderr, "    -namevars     Implicitly name the variables\n" );
  fprintf( stderr, "\n" );
  fprintf( stderr, "Special Functions (cannot be combined with minimization):\n" );
  fprintf( stderr, "    -info        Prints the information about the PLA function (for fd type only)\n" );
  fprintf( stderr, "    -Tcheck      Checks the input function for consistence\n" );
  fprintf( stderr, "    -Tsplit      Splits intersecting terms\n" );
  fprintf( stderr, "    -Tminterm    Splits function into minterms\n" );
}

void Save(TBoolFnct *srcf, char *fn, int of, TGauge gauge, Notation not, bool savevarnames, int limit)
{
    switch (of)
    {
        case 0: srcf->SaveFnct(fn, gauge, savevarnames);
                break;
        case 1: srcf->SaveFnctAsVHDL(fn, gauge, limit);
                break;
        case 2: srcf->SaveFnctAsVerilog(fn, gauge);
                break;
        case 3: srcf->SaveFnctAsBLIF(fn, gauge);
                break;
        case 4: srcf->SaveFnctAsSymbols(fn, gauge, not);
                break;
        case 5: srcf->SaveFnctAsHTML(fn, gauge);
                break;
        case 6: srcf->SaveFnctAsVHDL2(fn, gauge, limit);
                break;
    }
}
//---------------------------------------------------------------------------
int main( int argc, char * argv[] )
{
  int i;
  char * inm, * onm, * rnm;
  FILE * rpt;
  int op;
  int vhdllimit;

      // 0 = minimization
      // 1 = info
      // 2 = Tcheck
      // 3 = Tsplit
      // 4 = Tminterm

  int nocheck;
  float t1, t2;

  TBoolFnct * srcf, * minf;
  Boom * boominst;


  int of;       // output format
      // 0 = PLA
      // 1 = VHDL
      // 2 = Verilog
      // 3 = BLIF
      // 4 = Equations
      // 5 = HTML

  boominst = new Boom();
  boominst->gauge.NewGauge = NewGauge;
  boominst->gauge.NewGauge = NewGauge;
  boominst->gauge.CloseGauge = CloseGauge;
  boominst->gauge.GaugeStep = GaugeStep;

  inm = 0;
  onm = 0;
  rnm = 0;
  i = 1;
  op = 0;
  of = 0;
  srcf = 0;
  minf = 0;
  nocheck = 0;

  vhdllimit = -1;

  fprintf( stderr, "BOOM 2.7, (c) August 2006 by Petr Fiser\n\n" );
  while ( i != argc )
  {
    if ( argv[i] [0] == '-' )
    {
      switch ( argv[i] [1] )
      {
        case 'F':
          switch ( argv[i] [2] )
          {
            case 'R':
              boominst->mo->CD_FC = atoi( & argv[i] [3] );
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'D':
          switch ( argv[i] [2] )
          {
            case 'F':
              boominst->mo->FC_DF = atoi( & argv[i] [3] );
            break;
            case 'D':
              boominst->mo->FC_DF_dir = atoi( & argv[i] [3] );
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'S':
          switch ( argv[i] [2] )
          {
            case 't':
              boominst->mo->STOP_time = 1;
              boominst->mo->STOP_maxtime = atof( & argv[i] [3] );
            break;
            case 'i':
              boominst->mo->STOP_iterations = 1;
              boominst->mo->STOP_maxiterations = atoi( & argv[i] [3] );
            break;
            case 'n':
              boominst->mo->STOP_improvement = 1;
              boominst->mo->STOP_improvementint = atoi( & argv[i] [3] );
            break;
            case 'q':
              boominst->mo->STOP_optimization = 1;
              boominst->mo->STOP_optimizationval = atoi( & argv[i] [3] );
            break;

            case 'D':
              boominst->mo->covopts->COV_substDC = 1;
            break;

            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'Q':
          switch ( argv[i] [2] )
          {
            case 't':
              boominst->mo->OPT_crit = 0;
            break;
            case 'l':
              boominst->mo->OPT_crit = 1;
            break;
            case 'o':
              boominst->mo->OPT_crit = 2;
            break;
            case 'b':
              boominst->mo->OPT_crit = 3;
            break;
            case 'g':
              boominst->mo->OPT_crit = 4;
            break;
            case 'G':
              boominst->mo->OPT_crit = 5;
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'C':
          switch ( argv[i] [2] )
          {
            case 'M':
              boominst->mo->cdopts->CD_mutations = atoi( & argv[i] [3] );
            break;
            case 'P':
              boominst->mo->covopts->COV_type = atoi( & argv[i] [3] );
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'O':
          switch ( argv[i][2] )
          {
            case 'P':
              of = 0;
            break;
            case 'V':
              of = 1;
              if (strlen(argv[i]) > 3)
                vhdllimit = atoi( & argv[i] [3] );
            break;
            case 'v':
              of = 2;
            break;
            case 'B':
              of = 3;
            break;
            case 'E':
              of = 4;
            break;
            case 'H':
              of = 5;
            break;
            case 'W':
              of = 6;
              if (strlen(argv[i]) > 3)
                vhdllimit = atoi( & argv[i] [3] );
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;

        case 'R':
          switch ( argv[i] [2] )
          {
            case 'M':
              boominst->mo->IR_mutations = atoi( & argv[i] [3] );
            break;
            default:
              fprintf( stderr, "Unknown option %c\n", argv[i] [1] );
              exit( 1 );
          }
        break;
        case 'E':
          boominst->mo->IE_type = atoi( & argv[i] [2] );
        break;
        case 'e':
          if ( strcmp( argv[i], "-endcov" ) == 0 )
          {
            boominst->mo->covopts->endcov=1;
            boominst->mo->COV_end = 1;
          break;
          }
          else
          {
            fprintf( stderr, "Unknown parameter- %s\n", argv[i] );
            exit( 1 );
          }
        break;
        case 't':
            boominst->mo->covopts->MaxRunTime = atoi( & argv[i] [2] );
        break;
        case 'f':
            boominst->mo->covopts->effort = atoi( & argv[i] [2] );
        break;
        
        case 's':
          if ( strcmp( argv[i], "-single" ) == 0 )
          {
            boominst->mo->COV_somin = 1;
          break;
          }
        case 'i':
          if ( strcmp( argv[i], "-info" ) == 0 )
          {
            op = 1;
          break;
          }
        case 'n':
          if ( strcmp( argv[i], "-nocheck" ) == 0 )
          {
            nocheck = 1;
          break;
          }
          if ( strcmp( argv[i], "-noIR" ) == 0 )
          {
            boominst->mo->IR_no = 1;
          break;
          }
          if ( strcmp( argv[i], "-namevars" ) == 0 )
          {
            boominst->mo->savevarnames = true;
          break;
          }
        case 'T':
          if ( strcmp( argv[i], "-Tcheck" ) == 0 )
          {
            op = 2;
          break;
          }
          else if ( strcmp( argv[i], "-Tsplit" ) == 0 )
          {
            op = 3;
          break;
          }
          else if ( strcmp( argv[i], "-Tminterm" ) == 0 )
          {
            op = 4;
          break;
          }
          else
          {
            fprintf( stderr, "Unknown parameter %s\n", argv[i] );
          break;
          }

        default:
          fprintf( stderr, "Unknown parameter %s\n", argv[i] );
          exit( 1 );
      }
    }
    else if ( inm == 0 ) inm = argv[i];
    else if ( onm == 0 ) onm = argv[i];
    else rnm = argv[i];
    i++;
  }
  if ( inm == 0 ) help();
  else
  {
    if (rnm != 0) rpt = fopen(rnm, "wt");
    else rpt = stderr;
    fprintf( rpt, "Input function: %s\n", inm );
    srcf = srcf->LoadFnct( inm, boominst->gauge );
//    srcf->SwapOnOff();
//    srcf->SaveFnct("temp.pla");
    if ( srcf == 0 )
    {
      fprintf( rpt, "File not found or the PLA file is damaged\n" );
      return 1;
    }

//    InitDecompAss( srcf, boominst->mo->dopts->blocks );

    if ( srcf->type == 1 ) fprintf( rpt, "Function type: fr\n" );
    else
      fprintf( rpt, "Function type: fd\n" );
    fprintf( rpt, "Input variables: %i\n", srcf->inpvars );
    fprintf( rpt, "Output variables: %i\n", srcf->outvars );
    fprintf( rpt, "Defined terms: %i\n", srcf->terms );
    fprintf( rpt, "\n" );
    switch ( op )
    {
      case 0:
        if ( boominst->mo->CD_FC > 100 )
        {
          fprintf( rpt, "CD-Search:FC-Min ratio must be between 0-100\n" );
          exit( 1 );
        }
        if ( boominst->mo->FC_DF_dir > 1 )
        {
          fprintf( rpt, "FC-Min DF direction must be 0 or 1\n" );
          exit( 1 );
        }
        if ( boominst->mo->cdopts->CD_mutations > 100 )
        {
          fprintf( rpt, "Number of mutations must be less of equal to 100%\n" );
          exit( 1 );
        }
        if ( boominst->mo->IR_mutations > 100 )
        {
          fprintf( rpt, "Number of mutations must be less of equal to 100%\n" );
          exit( 1 );
        }
        if ( boominst->mo->covopts->COV_type > 100 )
        {
          fprintf( rpt, "Unknown CP solution method (%i)\n", boominst->mo->covopts->COV_type );
          exit( 1 );
        }
        if ( boominst->mo->covopts->effort > 10 || boominst->mo->covopts->effort < 1 )
        {
          fprintf( rpt, "Bad effort value (%i),effort must be in range 1 - 10 \n", boominst->mo->covopts->effort );
          exit( 1 );
        }
        if ( boominst->mo->IE_type > 100 )
        {
          fprintf( rpt, "Unknown IE method (%i)\n", boominst->mo->IE_type );
          exit( 1 );
        }
        if ( boominst->mo->OPT_crit > 5 )
        {
          fprintf( rpt, "Unknown optimization criterion (%i)\n", boominst->mo->OPT_crit );
          exit( 1 );
        }

        if ( boominst->mo->COV_end == 1 && ( boominst->mo->STOP_improvement == 1 || boominst->mo->STOP_optimization == 1 ) )
        {
          fprintf( rpt, "Cannot combine endcov parameter with optimization criteria based stopping condition\n" );
          exit( 0 );
        }
        if ( srcf->type != 1 )
        {
          fprintf( rpt, "Function must be fr type\n" );
          exit( 1 );
        }
        if ( boominst->mo->STOP_time == 0 && boominst->mo->STOP_iterations == 0 && boominst->mo->STOP_improvement == 0 && boominst->mo->STOP_optimization == 0 )
          boominst->mo->STOP_iterations = 1;

        if ( nocheck == 0 )
            if ( CheckConsist( srcf, boominst->gauge ) == 0 ) {
                fprintf( rpt, "Inconsistent function!\n" );
                return 1;
            }

        t1 = GetMyCPUTime();
        fprintf( rpt, "Minimizing...\n\n" );
        minf = boominst->Minimize( srcf );
        t2 = GetMyCPUTime();
        if ( minf == 0 )
        {
          fprintf( rpt, "Error occured during minimization\n" );
          return 1;
        }
        minf->GetInfo();
        fprintf( rpt, "Elapsed time: %.4f s\n", boominst->info.tm );
        fprintf( rpt, "Elapsed CPU time: %.4f s\n", t2-t1 );
        fprintf( rpt, "Iterations: %i\n", boominst->info.cyc );
        fprintf( rpt, "Terms: %i\n", minf->info.groupterms );
        fprintf( rpt, "Literals: %i\n", minf->info.liters );
        fprintf( rpt, "Output cost: %i\n", minf->info.ON_terms );
        fprintf( rpt, "Gates: %i\n", minf->info.gates );
        fprintf( rpt, "GEs: %.1f\n", (float)minf->info.GEs / 2 );
        fprintf( rpt, "\n" );
//        minf->SaveFnctAsBLIF_POS( onm );
//        srcf->SaveFnctAsBLIF( onm );
//        minf->SaveFnct( strcat(onm, ".pla"));
        Save(minf, onm, of, boominst->gauge, boominst->mo->not, boominst->mo->savevarnames, vhdllimit);
      break;
      case 1:
        srcf->GetInfo();
        fprintf( rpt, "Literals: %i\n", srcf->info.liters );
        fprintf( rpt, "Output cost: %i\n", srcf->info.ON_terms );
        if (srcf->type == 0) {
            fprintf( rpt, "Gates: %i\n", srcf->info.gates );
            fprintf( rpt, "GEs: %.1f\n", (float)srcf->info.GEs / 2);
        }
        printf( "\n" );
      break;
      case 2:
        if ( CheckConsist( srcf, boominst->gauge ) != 0 ) fprintf( rpt, "Function is consistent\n" );
        else
          fprintf( rpt, "Inconsistent function!\n" );
      break;
      case 3:
        if ( srcf->type != 1 )
        {
          fprintf( rpt, "Function must be fr type\n" );
          exit( 1 );
        }
        if ( NoIntersections( srcf, boominst->gauge ) == 0 )
        {
          fprintf( rpt, "Inconsistent function" );
          exit( 1 );
        }
        else
          fprintf( rpt, "OK\n" );
        Save(srcf, onm, of, boominst->gauge, boominst->mo->not, boominst->mo->savevarnames, vhdllimit);
      break;
      case 4:
        minf = SplitToMinterms( srcf, boominst->gauge);
        if ( minf == 0 )
        {
          fprintf( rpt, "Inconsistent function" );
          exit( 1 );
        }
        else
          fprintf( rpt, "OK\n" );
        Save(minf, onm, of, boominst->gauge, boominst->mo->not, boominst->mo->savevarnames, vhdllimit);
      break;
    }
  }
  delete srcf;
  delete minf;

  return 0;
}
