#include "PLAtab.h"

/*---------------------------------------------------------------------------*/
/* --- konstruktor s poctem radku, sloupcu a implicitni hodnotou vyplne --- */
/* --- alokuje se pamet pro matici r*s --- */

    PLAtab::PLAtab ( int r, int s, int load )
  {
    rad = r;
    sl = s;
    data = new int * [rad];
    for (int i = 0; i < rad; i ++ ) data[i] = new int [sl];

    for (int i = 0; i < rad; i ++ )
       for (int j = 0; j < sl; j ++ )
       {
         data[i][j] = load;
       }
  }

/*---------------------------------------------------------------------------*/
/* --- kopirujici konstruktor --- */
/* --- alokuje pamet pro novou matici --- */

     PLAtab::PLAtab ( const PLAtab & src )
  {
    rad = src.rad;
    sl = src.sl;
    data = new int * [rad];
    for (int i = 0; i < rad; i ++ ) data[i] = new int [sl];

    for (int i = 0; i < rad; i ++ )
       for (int j = 0; j < sl; j ++ )
       {
         data[i][j] = src.data[i][j];
       }

  }

/*---------------------------------------------------------------------------*/
/* --- destruktor dealokujici pamet --- */

     PLAtab::~PLAtab ()
  {
    for (int i = 0; i < rad; i ++ ) delete [] data[i];

    delete [] data;
    data = NULL;
    rad = 0;
    sl = 0;

  }


/*---------------------------------------------------------------------------*/
/* --- pretizeny implicitni operator prizazeni --- */
/* --- zrusi starou matici rad*sl a vytvori novou podle src --- */

 PLAtab    & PLAtab::operator= ( const PLAtab & src )
  {
    for (int i = 0; i < rad; i ++ )
       delete [] data[i];
    delete [] data;


    rad = src . rad;
    sl  = src . sl;
    data = new int * [rad];
    for (int i = 0; i < rad; i ++ ) data[i] = new int [sl];

    for (int i = 0; i < rad; i ++ )
       for (int j = 0; j < sl; j ++ )
       {
         data[i][j] = src.data[i][j];
       }

    return  ( *this );
  }

/*---------------------------------------------------------------------------*/
/* --- funkce pro pridani radku do matice --- */
/* --- vraci novou PLAtab, ale nemeni stavajici --- */

    PLAtab     PLAtab::addR( )
{
  PLAtab nova(rad+1,sl);
  for (int i = 0; i < rad; i ++ )
     for (int j = 0; j < sl; j ++ )
        {
          nova.data[i][j] = data[i][j];
        }

  return nova;

}
/*---------------------------------------------------------------------------*/
/* --- funkce na mazani rakdu, jehoz cislo je v parametru radek --- */
/* --- vraci novou PLAtab a tu stavajici nemeni --- */

PLAtab     PLAtab::delR ( int radek ){

  PLAtab pomM(rad-1,sl);
  int pomRadek = -1;
  for (int i = 0; i < rad; i ++ )
       if(i!=radek) {
                       pomRadek++;
                       for (int j = 0; j < sl; j ++ )
                          pomM.data[pomRadek][j] = data[i][j];
                    }


  return pomM;
 }

/*---------------------------------------------------------------------------*/
/* --- funkce pro mazani sloupce, jehoz cislo je v parametru sloupec --- */
/* --- vytvari novou PLAtab a stavajici nemeni --- */

 PLAtab     PLAtab::delS ( int sloupec ){

  PLAtab pomM(rad,sl-1);
  int pomSloupec = -1;
  for (int i = 0; i < rad; i ++ )
    {
     for (int j = 0; j < sl; j ++ )
         if(j!=sloupec) {
                         pomSloupec++;
                         pomM.data[i][pomSloupec] = data[i][j];
                        }

     pomSloupec = -1;
    }

   return pomM;
 }
/*---------------------------------------------------------------------------*/
/* --- funkce vytvori BDD z jednoho radku PLAtab --- */
 void     PLAtab::bdd_z_termu(BDD * dest_bdd){

   Node * parent,*TmpParent;
   Node ** Tmp = NULL;
  bool first_node = 0;
   for(int i=0; i<sl; i++)
    {
      if(data[0][i] == 1 ) {
                            // ctu 1 => Low-potomek jde do zeroT
                            parent = new Node(i);
                            if(!first_node) {first_node = 1; dest_bdd->root = parent;}
                            (dest_bdd->arrayLevel[i]).push_front(parent);
                            if(Tmp!=NULL){
                                          *Tmp = parent;
                                          parent->parentList.push_front(TmpParent);
                                         }
                            TmpParent = parent;
                            Tmp = &parent->High;
                            parent->Low = dest_bdd->zeroT;

                           }
        else if(data[0][i] == 0)  {
                                    // ctu 0 => High-potomek jde do zeroT
                                    parent = new Node(i);
                                    if(!first_node) {first_node = 1; dest_bdd->root = parent;}
                                    (dest_bdd->arrayLevel[i]).push_front(parent);
                                    if(Tmp!=NULL){
                                                  *Tmp = parent;
                                                  parent->parentList.push_front(TmpParent);
                                                 }
                                    TmpParent = parent;
                                    Tmp = &parent->Low;
                                    parent->High = dest_bdd->zeroT;
                                  }


    }


if(!first_node) {dest_bdd->root = dest_bdd->oneT;}
   else {*Tmp = dest_bdd->oneT;}
   }


/*---------------------------------------------------------------------------*/
/* --- funkce pro jednickovou expanzi podle PLAtabulky --- */
/* --- vytvari novou PLAtabulku a s tou pracuje, stavajici nemeni --- */
/* --- vraci upravenou nove vztvorenou PLAtabulku --- */

 PLAtab     PLAtab::expanse1 (Node * parent, BDD * dest_bdd){

  PLAtab pomM = *this;

  int pomRadek = 0;
  for (int i = 0; i < rad; i ++ )
      if(data[i][0] == 0) pomM = pomM.delR(pomRadek);
        else pomRadek++;
//kdyz uz je tabulka bez radku
  if(pomM.rad==0) {parent->High = dest_bdd->zeroT;}
    else {
          //jeste nejaky radek zbyva
          //vytvorim novy uzel a pridam ho do BDD
          parent->High = new Node(parent->varId+1);
          (dest_bdd->arrayLevel[parent->varId+1]).push_front(parent->High);
          parent->High->parentList.push_front(parent);

          //smazu 1.sloupec
          pomM = pomM.delS(0);
          if(pomM.sl!=1){
                         //radku zbylo vice nez jeden
                         pomM.expanse1(parent->High,dest_bdd);
                         pomM.expanse0(parent->High,dest_bdd);
                        }

          else {
                //zbyva posledni radek - volam posledni expanze
                pomM.expEnd1(parent->High,dest_bdd);
                pomM.expEnd0(parent->High,dest_bdd);
               }
    }
  return pomM;
 }

/*---------------------------------------------------------------------------*/
/* --- funkce pro jednickovou posledni expanzi podle PLAtabulky --- */
/* --- vytvari novou PLAtabulku a s tou pracuje, stavajici nemeni --- */
/* --- nevraci uz upravenou nove vztvorenou PLAtabulku!!!!!!!!!!!!!!!! --- */

void     PLAtab::expEnd1 (Node * parent, BDD * dest_bdd){

  PLAtab pomM = *this;

  int pomRadek = 0;
  for (int i = 0; i < rad; i ++ )
      if(data[i][0] == 0) pomM = pomM.delR(pomRadek);
        else pomRadek++;

  if(pomM.rad!=0) parent->High = dest_bdd->oneT;
    else parent->High = dest_bdd->zeroT;

 }

/*---------------------------------------------------------------------------*/
/* --- funkce pro nulovou expanzi podle PLAtabulky --- */
/* --- vytvari novou PLAtabulku a s tou pracuje, stavajici nemeni --- */
/* --- vraci upravenou nove vztvorenou PLAtabulku --- */

 PLAtab     PLAtab::expanse0 (Node * parent, BDD *dest_bdd){

  PLAtab pomM = *this;
  int pomRadek = 0;
  for (int i = 0; i < rad; i ++ )
      if(data[i][0] == 1) pomM = pomM.delR(pomRadek);
          else pomRadek++;
//kdyz uz je tabulka bez radku
if(pomM.rad==0) parent->Low = dest_bdd->zeroT;
  else {
         //jeste nejaky radek zbyva
         //vytvorim novy uzel a pridam ho do BDD
         parent->Low = new Node(parent->varId+1);
         (dest_bdd->arrayLevel[parent->varId+1]).push_front(parent->Low);
         parent->Low->parentList.push_front(parent);

          //smazu 1.sloupec
         pomM = pomM.delS(0);

         if(pomM.sl!=1){
                        //radku zbylo vice nez jeden
                        pomM.expanse1(parent->Low,dest_bdd);
                        pomM.expanse0(parent->Low,dest_bdd);
                       }

              else {
                    //zbyva posledni radek - volam posledni expanzi
                    pomM.expEnd1(parent->Low,dest_bdd);
                    pomM.expEnd0(parent->Low,dest_bdd);
                   }

      }

return pomM;
}

/*---------------------------------------------------------------------------*/
/* --- funkce pro nulovou posledni expanzi podle PLAtabulky --- */
/* --- vytvari novou PLAtabulku a s tou pracuje, stavajici nemeni --- */
/* --- nevraci uz upravenou nove vztvorenou PLAtabulku!!!!!!!!!!!!!!!! --- */

void     PLAtab::expEnd0 (Node * parent, BDD * dest_bdd){

  PLAtab pomM = *this;

  int pomRadek = 0;
  for (int i = 0; i < rad; i ++ )
      if(data[i][0] == 1) pomM = pomM.delR(pomRadek);
        else pomRadek++;

  if(pomM.rad!=0) parent->Low = dest_bdd->oneT;
    else parent->Low = dest_bdd->zeroT;

 }

/*---------------------------------------------------------------------------*/
/* --- pretizeny operator porovnani dvou PLAtabulek --- */
/* --- porovnava kazdou hodnotu [x,y] --- */

 int          PLAtab::operator == ( const PLAtab & x ) const
  {
    if(sl!=x.sl || rad!=x.rad) return 0;
    for (int i = 0; i < rad; i ++ )
       for (int j = 0; j < sl; j ++ )
       {
         if(data[i][j] != x.data[i][j]) return 0;
       }

    return 1;
  }

/*---------------------------------------------------------------------------*/
/* --- pretizeny operator [] pro PLAtabulku --- */
/* --- osetreno preskoceni indexu, vyhodi vyjimku --- */

 int *      PLAtab ::  operator[]  ( int ind  )
{
if(ind<0 || ind>=rad) throw "nedovoleny index";
return data[ind];

}

/*---------------------------------------------------------------------------*/
/* --- pretizeny operator [] pro PLAtabulku(pro konstantni objekty) --- */
/* --- osetreno preskoceni indexu, vyhodi vyjimku --- */


const   int *     PLAtab ::   operator[]  ( int ind  )  const
{
  if(ind<0 || ind>=rad) throw "nedovoleny index";
return data[ind];

}

/*---------------------------------------------------------------------------*/
/* --- pretizeny operator nerovna se pro PLAtabulku --- */

 int          PLAtab::operator != ( const PLAtab & x ) const
  {
    return (operator==(x))==0;
  }

/*---------------------------------------------------------------------------*/
/* --- pretizeny operator pro vystup do proudu pro PLAtabulku --- */

 ostream & operator << ( ostream & out, const PLAtab & x )
  {
    for (int i = 0; i < x.rad; i ++ )
       {
         for (int j = 0; j < x.sl; j ++ )
          {
           out << x . data[i][j] << "  ";
          }
        out << endl;
       }

    return ( out );
  }

/*---------------------------------------------------------------------------*/
