#define EXTERN
#include <stdio.h>
#include <stdlib.h>
#include <libciomr/libciomr.h>
#include <math.h>
#include <libqt/qt.h>
#include "input.h"
#include "global.h"
#include "defines.h"


double xyz2lm_Coeff(int,int,int,int,int);
static double **bc;
static double *fac;

/*-----------------------------------------------------------------------------------------------------------------
  This function builds cartesian to pure angular momentum transformation matrix
 -----------------------------------------------------------------------------------------------------------------*/

void build_cart2pureang()
{
  int i,j,k,m;
  int n;
  int class,lmax,irr,ao,atom,l;
  int shell, shell_first, shell_last;
  int ao_max;
  int cnt, so_cnt;
  int ao_offset;
  int n_max;
  double min,tmp;


    /*------------------------
      Initialize global arrays
     -------------------------*/
  
    num_so_per_irrep = init_int_array(nirreps);
    cart2pureang = (double ***) malloc(sizeof(double **)*(max_angmom+1));
    for(l=0;l<=max_angmom;l++)
      cart2pureang[l] = init_matrix(2*l+1,ioff[l+1]);
    fac = init_array(2*max_angmom+1);
    fac[0] = 1.0;
    for(i=1;i<=2*max_angmom;i++)
      fac[i] = i*fac[i-1];
    bc = init_matrix(max_angmom+1,max_angmom+1);
    for(i=0;i<=max_angmom;i++)
      for(j=0;j<=i;j++)
	bc[i][j] = combinations(i,j);
  
    /*------------------------
      Initialize local arrays
     -------------------------*/
  
    
    /*--------------------------------------------------------------------------------------------------
      Compute number of pure angular momentum SOs in each symmetry block for each angular momentum type
     --------------------------------------------------------------------------------------------------*/

    num_pureang_so = init_int_matrix(max_angmom+1,nirreps);
    num_redun_so = init_int_matrix(max_angmom+1,nirreps);
    for(l=0;l<=max_angmom;l++)
      for(irr=0;irr<nirreps;irr++)
	num_pureang_so[l][irr] = num_cart_so[l][irr];
    for(i=2;i<=max_angmom;i++)
      for(l=i%2;l<i;l+=2)
	for(irr=0;irr<nirreps;irr++) {
	  num_redun_so[i][irr] += num_pureang_so[l][irr];
	  num_pureang_so[i][irr] -= num_pureang_so[l][irr];
	}


    /*--------------------------------------------------------------------------------------------------
      Compute cartesian to pure angular momentum transformation matrices for each angular momentum type
     --------------------------------------------------------------------------------------------------*/

    for(l=0;l<=max_angmom;l++) {
      ao_max = ioff[l+1];
      for(m=-l;m<=l;m++)
	for(ao=0;ao<ao_max;ao++)
	  cart2pureang[l][m+l][ao] = xyz2lm_Coeff(l,m,xexp_ao[l][ao],yexp_ao[l][ao],zexp_ao[l][ao]);
    }


    /*-----------------------------
      Remove after testing is over
     -----------------------------*/
    if (print_lvl >= DEBUGPRINT)
      for(l=0;l<=max_angmom;l++) {
	fprintf(outfile,"    -Cart2PureAng matrix for l=%d:\n",l);
	print_mat(cart2pureang[l],2*l+1,ioff[l+1],outfile);
	fprintf(outfile,"\n");
      }

    free(fac);
    free_matrix(bc,max_angmom+1);
    return;
}



/*---------------------------------------------------------------------------------------------
  Computes transformation coefficients from cartesian to real pure angular momentum functions.
  See IJQC 54, 83 (1995), eqn (15). If m is negative, imaginary part is computed, whereas
  a positive m indicates that the real part of spherical harmonic Ylm is requested.
 ---------------------------------------------------------------------------------------------*/

double xyz2lm_Coeff(int l, int m, int lx, int ly, int lz)
{
  int i,j,k,i_max;
  int k_min, k_max;
  int abs_m;
  int comp;
  double pfac, pfac1, sum, sum1;

  abs_m = abs(m);
  if ((lx + ly - abs(m))%2)
    return 0.0;
  else
    j = (lx + ly - abs(m))/2;

  if (j < 0)
    return 0.0;

  /*----------------------------------------------------------------------------------------
    Checking whether the cartesian polynomial contributes to the requested component of Ylm
   ----------------------------------------------------------------------------------------*/
  comp = (m >= 0) ? 1 : -1;
  i = abs_m-lx;
  if (comp != parity(i))
    return 0.0;

  pfac = sqrt(fac[2*lx]*fac[2*ly]*fac[2*lz]*fac[l-abs_m]/(fac[2*l]*fac[l]*fac[lx]*fac[ly]*fac[lz]*fac[l+abs_m]));
  pfac /= (1 << l);

  if (m < 0)
    pfac *= parity((i-1)/2);
  else
    pfac *= parity(i/2);
  
  i_max = (l-abs_m)/2;
  sum = 0.0;
  for(i=0;i<=i_max;i++) {
    pfac1 = bc[l][i]*bc[i][j];
    if (pfac1 == 0.0)
      continue;
    else
      pfac1 *= (parity(i)*fac[2*(l-i)]/fac[l-abs_m-2*i]);
    sum1 = 0.0;
    k_min = MAX((lx-abs_m)/2,0);
    k_max = MIN(j,lx/2);
    for(k=k_min;k<=k_max;k++)
      sum1 += bc[j][k]*bc[abs_m][lx-2*k]*parity(k);
    sum += pfac1*sum1;
  }
  if (m == 0)
    return pfac*sum;
  else
    return M_SQRT2*pfac*sum;
}

