/*******************************************************************************
    OpenAirInterface 
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is 
   included in this distribution in the file called "COPYING". If not, 
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr
  
  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/
#include "defs.h"

int mult_cpx_matrix_h(short *x1[2][2], 
		      short *x2[2][2], 
		      short *y[2][2], 
		      unsigned int N, 
		      unsigned short output_shift,
		      short hermitian)
{

  if (hermitian) {
    // this computes x1^H*x2 and stores it in y
    mult_cpx_vector_h(x2[0][0],x1[0][0],y[0][0],N,output_shift,1);
    mult_cpx_vector_h(x2[0][1],x1[0][1],y[0][0],N,output_shift,1);
    mult_cpx_vector_h(x2[0][0],x1[1][0],y[1][0],N,output_shift,1);
    mult_cpx_vector_h(x2[0][1],x1[1][1],y[1][0],N,output_shift,1);
    mult_cpx_vector_h(x2[1][0],x1[0][0],y[0][1],N,output_shift,1);
    mult_cpx_vector_h(x2[1][1],x1[0][1],y[0][1],N,output_shift,1);
    mult_cpx_vector_h(x2[1][0],x1[1][0],y[1][1],N,output_shift,1);
    mult_cpx_vector_h(x2[1][1],x1[1][1],y[1][1],N,output_shift,1);
  } 
  else {
    // this computes x1*x2^H and stores it in y
    mult_cpx_vector_h(x1[0][0],x2[0][0],y[0][0],N,output_shift,1);
    mult_cpx_vector_h(x1[0][1],x2[0][1],y[0][0],N,output_shift,1);
    mult_cpx_vector_h(x1[0][0],x2[1][0],y[0][1],N,output_shift,1);
    mult_cpx_vector_h(x1[0][1],x2[1][1],y[0][1],N,output_shift,1);
    mult_cpx_vector_h(x1[1][0],x2[0][0],y[1][0],N,output_shift,1);
    mult_cpx_vector_h(x1[1][1],x2[0][1],y[1][0],N,output_shift,1);
    mult_cpx_vector_h(x1[1][0],x2[1][0],y[1][1],N,output_shift,1);
    mult_cpx_vector_h(x1[1][1],x2[1][1],y[1][1],N,output_shift,1);
  }
}

int mult_cpx_matrix_vector(int *x1[2][2], 
			   int *x2[2], 
			   int *y[2], 
			   unsigned int N, 
			   unsigned short output_shift)
{

  Zero_Buffer(y[0],N*8);
  Zero_Buffer(y[1],N*8);

  // this computes x1*x2 and stores it in y (32 bit)
  mult_cpx_vector_add32((short*)x2[0],(short*)x1[0][0],(short*)y[0],N);
  mult_cpx_vector_add32((short*)x2[1],(short*)x1[0][1],(short*)y[0],N);
  mult_cpx_vector_add32((short*)x2[0],(short*)x1[1][0],(short*)y[1],N);
  mult_cpx_vector_add32((short*)x2[1],(short*)x1[1][1],(short*)y[1],N);

  // shift and pack
  shift_and_pack((short*)y[0],N,output_shift);
  shift_and_pack((short*)y[1],N,output_shift);

}

#ifdef MAIN_MM
#include <stdio.h>
#include <stdlib.h>
main () 
{
  short x1_00[256] __attribute__((aligned(16)));
  short x1_10[256] __attribute__((aligned(16)));
  short x1_01[256] __attribute__((aligned(16)));
  short x1_11[256] __attribute__((aligned(16)));
  short x2_0[256] __attribute__((aligned(16)));
  short x2_1[256] __attribute__((aligned(16)));
  short y_0[256] __attribute__((aligned(16)));
  short y_1[256] __attribute__((aligned(16)));

  int *x1[2][2];
  int *x2[2];
  int *y[2];
  int i,m,n;

  x1[0][0] = (int*)x1_00;
  x1[0][1] = (int*)x1_01;
  x1[1][0] = (int*)x1_10;
  x1[1][1] = (int*)x1_11;
  x2[0] = (int*)x2_0;
  x2[1] = (int*)x2_1;
  y[0] = (int*)y_0;
  y[1] = (int*)y_1;
 
  for(m=0;m<2;m++){
    for(n=0;n<2;n++){
      for(i=0;i<256;i+=4) {
	((short*)x1[m][n])[i] = ((short) rand())/4;
	((short*)x1[m][n])[i+1] = ((short) rand())/4;
	((short*)x1[m][n])[i+2] = -((short*)x1[m][n])[i+1];
	((short*)x1[m][n])[i+3] = ((short*)x1[m][n])[i];
      }
    }
    for(i=0;i<256;i+=4) {
      ((short*)x2[m])[i] = ((short) rand())/4;
      ((short*)x2[m])[i+1] = ((short) rand())/4;
      ((short*)x2[m])[i+2] = ((short*)x2[m])[i];
      ((short*)x2[m])[i+3] = ((short*)x2[m])[i+1];
    }
    Zero_Buffer(y[m],512);
  }

  /*
  input[0] = 100;
  input[1] = 200;
  input[2] = -200;
  input[3] = 100;
  input[4] = 1000;
  input[5] = 2000;
  input[6] = -2000;
  input[7] = 1000;
  input[8] = 100;
  input[9] = 200;
  input[10] = -200;
  input[11] = 100;
  input[12] = 1000;
  input[13] = 2000;
  input[14] = -2000;
  input[15] = 1000;

  input2[0] = 2;
  input2[1] = 1;
  input2[2] = 2;
  input2[3] = 1;
  input2[4] = 20;
  input2[5] = 10;
  input2[6] = 20;
  input2[7] = 10;
  input2[8] = 2;
  input2[9] = 1;
  input2[10] = 2;
  input2[11] = 1;
  input2[12] = 2000;
  input2[13] = 1000;
  input2[14] = 2000;
  input2[15] = 1000;

  x1[0][0] = (int*)input;
  x1[0][1] = (int*)input;
  x1[1][0] = (int*)input;
  x1[1][1] = (int*)input;

  x2[0] = (int*)input2; 
  x2[1] = (int*)input2; 

  y[0] = (int*)output; 
  y[1] = (int*)output2; 
  */

  mult_cpx_matrix_vector(x1,x2,y,64,15);

  //mult_cpx_vector_add32(x2[0],x1[0][0],y[0],64);

  for (i=0;i<128;i+=2)
    printf("i=%d, x1 = [%d+1i*%d %d+1i*%d; %d+1i*%d %d+1i*%d]; x2 = [%d+1i*%d; %d+1i*%d]; y = [%d+1i*%d; %d+1i*%d]; y_m= round(x1*x2./pow2(15)); y-y_m \n",
	   i,
	   ((short*)x1[0][0])[2*i],  ((short*)x1[0][0])[2*i+2],
	   ((short*)x1[0][1])[2*i],  ((short*)x1[0][1])[2*i+2],
	   ((short*)x1[1][0])[2*i],  ((short*)x1[1][0])[2*i+2],
	   ((short*)x1[1][1])[2*i],  ((short*)x1[1][1])[2*i+2],
	   ((short*)x2[0])[2*i],  ((short*)x2[0])[2*i+1],
	   ((short*)x2[1])[2*i],  ((short*)x2[1])[2*i+1],
	   ((short*)y[0])[2*i],  ((short*)y[0])[2*i+1],
	   ((short*)y[1])[2*i],  ((short*)y[1])[2*i+1]);
	   //((int*)y[0])[i],  ((int*)y[0])[i+1],
	   //((int*)y[1])[i],  ((int*)y[1])[i+1]);

}

#endif
