#include "util.h"

// Variáveis globais
extern ponto l, w;      // lados do retângulo

extern ponto nCombLin;  // número de combinações lineares de l's e w's

extern ponto *combLin;  // vetor de combinações lineares de l's e w's.

extern ponto *iCombLin; // indices do vetor combLin das combinações lineares.
                        // Seja n a combinação linear. Então iCombLin[n] é o indice
                        //   do vetor combLin que está n.
                        // Ou seja, combLin[ iCombLin [ n ] ] = n

ponto fechaEmZ( ponto p );

indice q2i( ponto q0, ponto q1, ponto q2, ponto q3 );

qtde R_lim_sup( ponto x, ponto y );
qtde L_lim_sup( ponto *q );
qtde R_lim_inf( ponto x, ponto y );
qtde L_lim_inf( ponto *q );

void normalizar( ponto *q );

void posicaoPadraoB1( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB2( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB3( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB4( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB5( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB6( ponto *i, ponto *q, ponto *q1, ponto *q2 );
void posicaoPadraoB7( ponto *i, ponto *q, ponto *q1, ponto *q2 );

/**
 *  devolve o maior z_h tq z_h <= a
 *  usado por causa da operação de subtração em Z
 */

ponto fechaEmZ( ponto p ) {

  return combLin[ iCombLin [ p ] ];
}

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

  Um L é uma quádupla. L = L(X, Y, x, y)
  Os L's são indexados de maneira que seus indices estejam no
  intervalo 0<=i<nL.
  As funções abaixo (i2q, q2i) servem para fazer a correspon-
  dencia entre o indice do L e sua quádupla.
  
    f: i   ->   (X, Y. x. y)  e  f^-1: (X, Y. x. y)   ->   i
    i2q()                                      q2i()
    
*******************************************************************/

/**
 *  Dado uma quádupla, devolve o número do L correspondente.
 *     -> quádupla TO indice
 */
indice q2i( ponto q0, ponto q1, ponto q2, ponto q3 ) {
  
  return ((( iCombLin[ q0 ]*nCombLin) +
                iCombLin[ q1 ])*nCombLin +
                    iCombLin[ q2 ])*nCombLin +
                        iCombLin[ q3 ];
}


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

  As funções abaixo (R_lim_sup, L_lim_sup, R_lim_inf, L_lim_inf),
  servem para calcular dois limites para v*(L).
  Limite superior é A(L) / lw.
  Limite inferior é o número máximo de retângulos (l, w)
  contido em um packing homogêneo de L
  
*******************************************************************/


/**
 *  Calcula o limite superior de um dado R (L degenerado)
 */
qtde R_lim_sup( ponto x, ponto y ) {
  
  return (x*y) / (l*w); // A(R) / lw
}


/**
 *  Calcula o limite superior de um dado L
 */
qtde L_lim_sup( ponto *q ) {
  
  return (q[0]*q[1] - (q[0]-q[2])*(q[1]-q[3])) / (l*w); // A(L) / lw
}


/**
 *  Calcula o limite inferior de um dado R (L degenerado)
 */
qtde R_lim_inf( ponto x, ponto y ) {
  qtde a, b;
  
  a = (x/l)*(y/w);
  b = (x/w)*(y/l);
  
  return MAX( a, b );
}


/**
 *  Calcula o limite inferior de um dado L
 */
qtde L_lim_inf( ponto *q ) {
  qtde a, b;
  
  // "quebra" o L em dois retângulos
  a = R_lim_inf( q[2], q[1] ) + R_lim_inf( q[0]-q[2], q[3] );
  b = R_lim_inf( q[2], q[1]-q[3] ) + R_lim_inf( q[0], q[3] );
  
  return MAX( a, b );
}

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

  A funções abaixo (normalizar) auxilia nas funções de divisão
    de um L.
  
*******************************************************************/

/*
 *  normaliza a quádupla para que pertença ao conjunto I
 */
void normalizar( ponto *q ) {

  int i, j, i1, j1;

  i = q[0]; j = q[1]; i1 = q[2]; j1 = q[3];

  // Estes são para eliminar retângulos "degenerados" pois
  // um retângulo sempre deve ser definido com i=i1 e j=j1.
  if( 0==i1 ) { i1=i; j=j1; }
  else if( 0==j1 ) { j1=j; i=i1; }
  else if ( i1 == i || j1 == j ) { i1=i; j1=j; }
  
  // Se a superfície for menor do que um retangulinho (em particular 
  // se for nula) também descarta.
  if ( i * j - ( i - i1 ) * ( j - j1 ) < l * w ) { q[0]=-1; return; }
  
  // E, se for necessário, isto deita o retângulo
  if ( i == i1 && j == j1 && i < j ) {SWAP(i,j); SWAP(i1,j1) }
  
  // Isto é para, se necessário, deitar Ls
  if( 0<i1  && i1<i  &&  0<j1  && j1<j  && i<j ) { SWAP(i,j); SWAP(i1,j1); }
  else if( 0<i1  && i1<i  &&  0<j1  && j1<j  && i==j  && i1<j1 ) { SWAP(i1,j1); }

  q[0]=i; q[1]=j; q[2]=i1; q[3]=j1;

}

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

  As funções abaixo (posicaoPadraoB1,..., posicaoPadraoB7) servem para
  dividir somente um L, segundo alguns critérios.
  
***********************************************************************/
/**
 *  Divide o L em 2 L's, segundo a subdivisão B1
 */
void posicaoPadraoB1( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = q[2];
  q1[1] = fechaEmZ( q[1]-i[1] );
  q1[2] = i[0];
  q1[3] = fechaEmZ( q[1]-q[3] );

  q2[0] = q[0];
  q2[1] = q[3];
  q2[2] = fechaEmZ( q[0]-i[0] );
  q2[3] = i[1];
}


/**
 *  Divide o L em 2 L's, segundo a subdivisão B2
 */
void posicaoPadraoB2( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = q[2];
  q1[1] = fechaEmZ( q[1]-q[3] );
  q1[2] = fechaEmZ( q[2]-i[0] );
  q1[3] = fechaEmZ( q[1]-i[1] );

  q2[0] = q[0];
  q2[1] = i[1];
  q2[2] = i[0];
  q2[3] = q[3];
}


/**
 *  Divide o L em 2 L's, segundo a subdivisão B3
 */
void posicaoPadraoB3( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = q[0];
  q1[1] = q[1];
  q1[2] = i[0];
  q1[3] = i[1];

  q2[0] = fechaEmZ( q[0]-i[0] );
  q2[1] = fechaEmZ( q[1]-i[1] );
  q2[2] = fechaEmZ( q[2]-i[0] );
  q2[3] = fechaEmZ( q[3]-i[1] );
}


/**
 *  Divide o L em 2 L's, segundo a subdivisão B4
 */
void posicaoPadraoB4( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = i[0];
  q1[1] = q[1];
  q1[2] = q[2];
  q1[3] = i[1];

  q2[0] = fechaEmZ( q[0]-q[2] );
  q2[1] = q[3];
  q2[2] = fechaEmZ( q[0]-i[0] );
  q2[3] = fechaEmZ( q[3]-i[1] );
}


/**
 *  Divide o L em 2 L's, segundo a subdivisão B5
 */
void posicaoPadraoB5( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = q[2];
  q1[1] = q[1];
  q1[2] = i[0];
  q1[3] = fechaEmZ( q[1]-i[1] );
  
  q2[0] = fechaEmZ( q[0]-i[0] );
  q2[1] = q[3];
  q2[2] = fechaEmZ( q[0]-q[2] );
  q2[3] = i[1];
}


/**
 *  Divide o R em 2 L's, segundo a subdivisão B6
 */
void posicaoPadraoB6( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = i[2];
  q1[1] = q[1];
  q1[2] = i[0];
  q1[3] = fechaEmZ( q[1]-i[1] );

  q2[0] = fechaEmZ( q[0]-i[0] );
  q2[1] = q[1];
  q2[2] = fechaEmZ( q[0]-i[2] );
  q2[3] = i[1];
}


/**
 *  Divide o R em 2 L's, segundo a subdivisão B7
 */
void posicaoPadraoB7( ponto *i, ponto *q, ponto *q1, ponto *q2 ) {
  
  q1[0] = q[0];
  q1[1] = fechaEmZ( q[1]-i[1] );
  q1[2] = i[0];
  q1[3] = fechaEmZ( q[1]-i[2] );
  
  q2[0] = q[0];
  q2[1] = i[2];
  q2[2] = fechaEmZ( q[0]-i[0] );
  q2[3] = i[1];
}

