//---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h> // random
#include <string.h>
//---------------------------------------------------------------------------
#include "main.h"
#include "conf.h"
#include "boom.h"
#include "tboolfnct.h"
#include "decomp.h"
#include "tpfnct.h"
//---------------------------------------------------------------------------
int SaveBDF( char * fn, DecompositionOptions * dopts )
{
  int i;
  FILE * f;

  if ( ( f = fopen( fn, "wt" ) ) == 0 ) return 0;
  fprintf( f, "%i\n", dopts->blocks );
  for ( i = 0; i < dopts->blocks; i++ )
    fprintf( f, "%i %i\n", dopts->inps[i], dopts->outs[i] );
  fclose( f );
  return 1;
}
//---------------------------------------------------------------------------
int LoadBDF( char * fn, DecompositionOptions * dopts )
{
  int i;
  FILE * f;

  if ( ( f = fopen( fn, "rt" ) ) == 0 ) return 0;
  delete[] dopts->inps;
  delete[] dopts->outs;
  fscanf( f, "%i", & dopts->blocks );
  dopts->inps = new int[dopts->blocks];
  dopts->outs = new int[dopts->blocks];
  for ( i = 0; i < dopts->blocks; i++ )
    fscanf( f, "%i %i\n", & dopts->inps[i], & dopts->outs[i] );
  fclose( f );
  return 1;
}
//---------------------------------------------------------------------------
void CountInpOuts( TBoolFnct * fn )
{
  int i, j;

  for ( i = 0; i < fn->decomp->blocks; i++ )
  {
    fn->decomp->icnt[i] = 0;
    for ( j = 0; j < fn->inpvars; j++ )
      if ( fn->decomp->inps[i] [j] == 'F' || fn->decomp->inps[i] [j] == '1' )
        fn->decomp->icnt[i] ++;
    fn->decomp->ocnt[i] = 0;
  }
  for ( j = 0; j < fn->outvars; j++ )
    fn->decomp->ocnt[fn->decomp->outs[j]] ++;
}
//---------------------------------------------------------------------------
void CleanInpAssignment( TBoolFnct * fn )
{
  int i, j;

  for ( i = 0; i < fn->decomp->blocks; i++ )
  {
    for ( j = 0; j < fn->inpvars; j++ )
    {
      if ( fn->decomp->inps[i] [j] == '1' ) fn->decomp->inps[i] [j] = 'N';
      if ( fn->decomp->inps[i] [j] == 'R' ) fn->decomp->inps[i] [j] = 'F';
    }
  }
}
//---------------------------------------------------------------------------
void CleanOutAssignment( TBoolFnct * fn )
{
  int j;

  for ( j = 0; j < fn->outvars; j++ )
    if ( fn->decomp->outforced[j] != 'F' ) fn->decomp->outs[j] = 0;
}
//---------------------------------------------------------------------------
void CleanAssignment( TBoolFnct * fn )
{
  CleanInpAssignment( fn );
  CleanOutAssignment( fn );
}
//---------------------------------------------------------------------------
int CountAssignment( TBoolFnct * fn )
{
  int i, j, b;
  TPfnct * ff;

  CleanInpAssignment( fn );
  for ( i = 0; i < fn->decomp->blocks; i++ )
    for ( j = 0; j < fn->inpvars; j++ )
      if ( fn->decomp->inps[i] [j] == 'F' ) fn->decomp->inps[i] [j] = 'R';
  for ( i = 0; i < fn->outvars; i++ )
  {
    b = fn->decomp->outs[i];
    ff = fn->fnct;
    while ( ff != 0 )
    {
      if ( ff->ov[i] == '1' )
      {
        for ( j = 0; j < fn->inpvars; j++ )
          if ( ff->iv[j] != '-' )
            if ( fn->decomp->inps[b] [j] == 'R' ) fn->decomp->inps[b] [j] = 'F';
            else if ( fn->decomp->inps[b] [j] != 'F' ) fn->decomp->inps[b] [j] = '1';
      }
      ff = ff->next;
    }
  }
  CountInpOuts( fn );
  return 1;
}
//---------------------------------------------------------------------------
void InitDecompAss( TBoolFnct * fn, int blocks )
{
  int i, j;

  if ( fn->decomp != 0 )
  {
    fn->decomp = new DecompAss;
    fn->decomp->blocks = blocks;
    fn->decomp->inps = new char * [blocks];
    fn->decomp->outs = new int[fn->outvars];
    fn->decomp->outforced = new char[fn->outvars];
    fn->decomp->icnt = new int[blocks];
    fn->decomp->ocnt = new int[blocks];
    for ( i = 0; i < blocks; i++ )
    {
      fn->decomp->icnt[i] = 0;
      fn->decomp->ocnt[i] = 0;
      fn->decomp->inps[i] = new char[fn->inpvars];
      for ( j = 0; j < fn->inpvars; j++ ) fn->decomp->inps[i] [j] = 'N';
    }
    for ( j = 0; j < fn->outvars; j++ )
    {
      fn->decomp->outs[j] = 0;
      fn->decomp->outforced[j] = 'N';
    }
  }
}
//---------------------------------------------------------------------------
DecompAss * DuplicateDecompAss( TBoolFnct * fn, DecompositionOptions * dopts )
{
  int i, j;
  DecompAss * da;

  if ( fn == 0 ) return 0;
  if ( fn->decomp == 0 ) return 0;
  da = new DecompAss;
  da->blocks = dopts->blocks;
  da->inps = new char * [dopts->blocks];
  da->outs = new int[fn->outvars];
  da->outforced = new char[fn->outvars];
  da->icnt = new int[dopts->blocks];
  da->ocnt = new int[dopts->blocks];
  for ( i = 0; i < dopts->blocks; i++ )
  {
    da->inps[i] = new char[fn->inpvars];
    for ( j = 0; j < fn->inpvars; j++ ) da->inps[i] [j] = ( fn->decomp->blocks <= i ) ? 'N' : fn->decomp->inps[i] [j];
    da->icnt[i] = ( fn->decomp->blocks <= i ) ? 0 : fn->decomp->icnt[i];
    da->ocnt[i] = ( fn->decomp->blocks <= i ) ? 0 : fn->decomp->ocnt[i];
  }
  for ( j = 0; j < fn->outvars; j++ )
  {
    da->outs[j] = fn->decomp->outs[j];
    da->outforced[j] = fn->decomp->outforced[j];
  }
  return da;
}
//---------------------------------------------------------------------------
void UpdateDecompAss( TBoolFnct * fn, DecompositionOptions * dopts ) // for updating assignment after # of blocks change
{
  DecompAss * da;

  if ( fn == 0 ) return;
  if ( fn->decomp == 0 ) return;
  if ( fn->decomp->blocks != dopts->blocks )
  {
    da = DuplicateDecompAss( fn, dopts );
    DestroyDecompAss( fn );
    fn->decomp = da;
  }
}
//---------------------------------------------------------------------------
void SaveDecompAss( TBoolFnct * fn, char * filn )
{
  FILE * f;
  int i, j;

  f = fopen( filn, "wt" );
  fprintf( f, "%i ", fn->decomp->blocks );
  fprintf( f, "%i ", fn->inpvars );
  fprintf( f, "%i ", fn->outvars );
  for ( i = 0; i < fn->decomp->blocks; i++ )
    for ( j = 0; j < fn->inpvars; j++ )
      fprintf( f, "%c", ( fn->decomp->inps[i] [j] == 'F' ) ? 'F' : 'N' );
  fprintf( f, "\n" );
  for ( j = 0; j < fn->outvars; j++ )
    fprintf( f, "%i %c ", fn->decomp->outs[j], fn->decomp->outforced[j] );
  fclose( f );
}
//---------------------------------------------------------------------------
int LoadDecompAss( TBoolFnct * fn, char * filn, DecompositionOptions * dopts )
{
  FILE * f;
  int i, j;
  char s[10];

  f = fopen( filn, "rt" );
  fscanf( f, "%i", & i );
  if ( i != dopts->blocks )
  {
    fclose( f );
    return 1;
  }
  fscanf( f, "%i", & i );
  if ( i != fn->inpvars )
  {
    fclose( f );
    return 2;
  }
  fscanf( f, "%i", & i );
  if ( i != fn->outvars )
  {
    fclose( f );
    return 3;
  }
  DestroyDecompAss( fn );
  InitDecompAss( fn, dopts->blocks );
  for ( i = 0; i < fn->decomp->blocks; i++ )
    for ( j = 0; j < fn->inpvars; j++ )
      fscanf( f, "%c", & fn->decomp->inps[i] [j] );
  for ( j = 0; j < fn->outvars; j++ )
  {
    fscanf( f, "%i", & fn->decomp->outs[j] );
    fscanf( f, "%s", & s );
    fn->decomp->outforced[j] = s[0];
  }
  fclose( f );
  return 0;
}
//---------------------------------------------------------------------------
void DestroyDecompAss( TBoolFnct * fn )
{
  int i;

  if ( fn->decomp != 0 )
  {
    for ( i = 0; i < fn->decomp->blocks; i++ )
    {
      delete[] fn->decomp->inps[i];
    }
    delete[] fn->decomp->inps;
    delete[] fn->decomp->outs;
    delete[] fn->decomp->outforced;
    delete[] fn->decomp->icnt;
    delete[] fn->decomp->ocnt;
    delete fn->decomp;
    fn->decomp = 0;
  }
}
//---------------------------------------------------------------------------
int AssignValidOutputs( TBoolFnct * fn, DecompositionOptions * dopts ) // returns 1 when failed
{
  int i, b;

  for ( i = 0; i < fn->decomp->blocks; i++ ) fn->decomp->ocnt[i] = 0;
  for ( i = 0; i < fn->outvars; i++ )
    if ( fn->decomp->outforced[i] == 'F' )
      fn->decomp->ocnt[fn->decomp->outs[i]] ++;
  b = 0;
  for ( i = 0; i < fn->outvars; i++ )
    if ( fn->decomp->outforced[i] != 'F' )
    {
      if ( fn->decomp->ocnt[b] == dopts->outs[b] )
      {
        b++;
        if ( b >= fn->decomp->blocks ) return 1;
      }
      fn->decomp->outs[i] = b;
      fn->decomp->ocnt[b] ++;
    }
  return 0;
}
//---------------------------------------------------------------------------
int RandomAssignValidOutputs( TBoolFnct * fn, DecompositionOptions * dopts ) // returns 1 when failed
{
  int i, b;
  int b1;

  for ( i = 0; i < fn->decomp->blocks; i++ ) fn->decomp->ocnt[i] = 0;
  for ( i = 0; i < fn->outvars; i++ )
    if ( fn->decomp->outforced[i] == 'F' )
      fn->decomp->ocnt[fn->decomp->outs[i]] ++;
  for ( i = 0; i < fn->outvars; i++ )
    if ( fn->decomp->outforced[i] != 'F' )
    {
      b = random( fn->decomp->blocks );
      b1 = ( b + fn->decomp->blocks - 1 ) % fn->decomp->blocks;
      while ( fn->decomp->ocnt[b] == dopts->outs[b] && b != b1 )
      {
        b++;
        b %= fn->decomp->blocks;
      }
      if ( b == b1 && fn->decomp->ocnt[b] == dopts->outs[b] ) return 1;
      fn->decomp->outs[i] = b;
      fn->decomp->ocnt[b] ++;
    }
  return 0;
}
//---------------------------------------------------------------------------
TBoolFnct * ExtractBlock( TBoolFnct * srcf, int b ) // extract block b from srcf as a new function
{
  TBoolFnct * fn;
  TPfnct * ff;
  int inp, o, d;
  char * iv, * ov;
  int i, j;

  j = 0;
  inp = 0;
  for ( i = 0; i < srcf->inpvars; i++ )
    if ( srcf->decomp->inps[b] [i] == '1' || srcf->decomp->inps[b] [i] == 'F' )
      inp++;
  o = 0;
  for ( i = 0; i < srcf->outvars; i++ )
    if ( srcf->decomp->outs[i] == b ) o++;

  fn = new TBoolFnct( inp, o, 0, 0 );

  iv = new char[inp + 1];
  ov = new char[o + 1];

  ff = srcf->fnct;
  while ( ff != 0 )
  {
    j = 0;
    for ( i = 0; i < srcf->outvars; i++ )
      if ( srcf->decomp->outs[i] == b && ff->ov[i] == '1' )
      {
        j = 1;
        break;
      }
    if ( j == 1 )
    {
      j = 0;
      d = 0;
      for ( i = 0; i < srcf->inpvars; i++ )
        if ( srcf->decomp->inps[b] [i] == '1' || srcf->decomp->inps[b] [i] == 'F' )
        {
          if ( srcf->inames[i] != 0 )
          {
            fn->inames[j] = new char[strlen( srcf->inames[i] ) + 1];
            strcpy( fn->inames[j], srcf->inames[i] );
          }
          iv[j++] = ff->iv[i];
          if ( ff->iv[i] == '-' ) d++;
        }
      iv[j] = 0;
      j = 0;
      for ( i = 0; i < srcf->outvars; i++ )
        if ( srcf->decomp->outs[i] == b )
        {
          if ( srcf->onames[i] != 0 )
          {
            fn->onames[j] = new char[strlen( srcf->onames[i] ) + 1];
            strcpy( fn->onames[j], srcf->onames[i] );
          }
          ov[j++] = ff->ov[i];
        }
      ov[j] = 0;
      fn->AddTermToEnd( iv, ov, d );
    }
    ff = ff->next;
  }
  delete[] iv;
  delete[] ov;
  return fn;
}
//---------------------------------------------------------------------------

PartInfo GetPartinfo( TBoolFnct * fn, DecompositionOptions * dopts )
{
  PartInfo inf;
  int i, j;
  char * inpusg;

  inf.inpsum = 0;
  CountAssignment( fn );
  for ( i = 0; i < dopts->blocks; i++ )
    inf.inpsum += fn->decomp->icnt[i];
  inpusg = new char[fn->inpvars];
  setmem( inpusg, fn->inpvars, 0 );
  inf.inpusage = 0;
  inf.branchinps = 0;
  inf.maxload = 0;
  inf.branchings = 0;
  inf.avgload = 0;
  for ( i = 0; i < dopts->blocks; i++ )
    for ( j = 0; j < fn->inpvars; j++ )
      if ( fn->decomp->inps[i] [j] != 'N' ) inpusg[j] ++;
  for ( j = 0; j < fn->inpvars; j++ )
  {
    if ( inpusg[j] != 0 ) inf.inpusage++;
    if ( inpusg[j] > 1 )
    {
      inf.branchinps++;
      inf.branchings += inpusg[j] - 1;
    }
    inf.avgload += inpusg[j];
    if ( inpusg[j] > inf.maxload ) inf.maxload = inpusg[j];
  }
  inf.avgload /= ( double )fn->inpvars;
  delete[] inpusg;
  return inf;
}
//---------------------------------------------------------------------------

