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


void build_so_classes()
{
  int i,j,l,tmp;
  int atom, ua;
  int kfirst, klast;
  int class,lmax,irr,ao,symop,char_i;
  int ao_first, ao_last, ao_max, ao_irr;
  int class_first, class_last, uc;
  int shell, shell_first, shell_last;
  int **class_num_angmom;
  int **coeff_irr;
  int so_cnt, so_blk_lim, so_blk_off, cnt;
  int ao_offset;
  int basisfn_num_in_symblk;
  int *first_basisfn_in_symblk_offset;
  int *first_basisfn_in_symblk;


    /*----------------------------------------------------------------
      Compute maximum angular momentum number for each class of atoms
     ----------------------------------------------------------------*/

    max_angmom_class = init_int_array(num_classes);
    class_num_angmom = init_int_matrix(num_classes,max_angmom+1);
    for(i=0;i<num_atoms;i++) {
      kfirst = first_shell_on_atom[i];
      klast = kfirst + nshells_per_atom[i];
      class = atom_class[i];
      for(j=kfirst;j<klast;j++) {
	max_angmom_class[class] = (max_angmom_class[class] > shell_ang_mom[j]) ? max_angmom_class[class] : shell_ang_mom[j];
	class_num_angmom[class][shell_ang_mom[j]]++;
      }
    }
    
    /*-----------------------
      Allocate global arrays
     -----------------------*/

    num_cart_so_in_class = (int ***) malloc(num_classes*sizeof(int **));
    for(class=0;class<num_classes;class++)
      num_cart_so_in_class[class] = init_int_matrix(max_angmom_class[class]+1,nirreps);
    class_so_coeff = (double ****) malloc(num_classes*sizeof(double ***));
    for(i=0;i<num_classes;i++)
      class_so_coeff[i] = (double ***) malloc((max_angmom_class[i]+1)*sizeof(double **));
    if (puream) {
      num_pureang_so_in_class = (int ***) malloc(num_classes*sizeof(int **));
      for(class=0;class<num_classes;class++)
	num_pureang_so_in_class[class] = init_int_matrix(max_angmom_class[class]+1,nirreps);
    }
    

    /*----------------------
      Allocate local arrays
     -----------------------*/

    coeff_irr = init_int_matrix(nirreps,ioff[max_angmom+1]);

    
    /*------------------------------------------------------------------------------------------------------------------------
      Loop over each unique class, each angular momentum number within class, irreps, and basis function types
      and 1) count number of SO in each representation num_cart_so_in_class[class][l][irr] generated by a shell of ang momentum l
      on an atom of a unique class "uc" or its symmetry equivalent classes;
      2) form matrices of coefficients and labels for classes equivalent to the unique class "uc"
     ------------------------------------------------------------------------------------------------------------------------*/
    for(uc=0;uc<num_unique_classes;uc++) {
      class = uc2c[uc];
      class_first = class;
      class_last = class + unique_class_degen[uc];
      lmax = max_angmom_class[class];
      for(l=0;l<=lmax;l++) {
	ao_max = ioff[l+1];
        for(symop=0;symop<nirreps;symop++)
	  if (class_orbit[class][symop] == class)
	    for(irr=0;irr<nirreps;irr++)
	      for(ao=0;ao<ao_max;ao++)
		coeff_irr[irr][ao] += irr_char[irr][symop]*ao_type_transmat[l][symop][ao];
	for(irr=0;irr<nirreps;irr++)
	  for(ao=0;ao<ao_max;ao++)
	    if (coeff_irr[irr][ao] > 0)
	      for(i=class_first;i<class_last;i++)
		num_cart_so_in_class[i][l][irr] += 1;

	if (puream) {
	  for(irr=0;irr<nirreps;irr++)
	    for(i=class_first;i<class_last;i++)
	      num_pureang_so_in_class[i][l][irr] = num_cart_so_in_class[i][l][irr];
	  for(ao_irr=0;ao_irr<nirreps;ao_irr++)
	    if (num_cart_so[l][ao_irr] != 0)
	      for(ao=0;ao<ao_max;ao++)
		if (ao_type_irr[l][ao] == ao_irr) {
		  for(irr=0;irr<nirreps;irr++)
		    if (coeff_irr[irr][ao] > 0)
		      for(i=class_first;i<class_last;i++)
			num_pureang_so_in_class[i][l][irr] -= num_redun_so[l][ao_irr];
		  break;
		}
	}

	/*---------------------------------
	  Remove after testing is complete
	 ---------------------------------*/
	if (print_lvl >= DEBUGPRINT) {
	  fprintf(outfile,"    Class = %d  l = %d\n     Number of cartesian SOs in symmetry blocks:",class+1,l);
	  for(irr=0;irr<nirreps;irr++)
	    fprintf(outfile," %d",num_cart_so_in_class[class][l][irr]);
	  fprintf(outfile,"\n\n");
	  if (puream) {
	    fprintf(outfile,"    Class = %d  l = %d\n     Number of pure angular momentum SOs in symmetry blocks:",class+1,l);
	    for(irr=0;irr<nirreps;irr++)
	      fprintf(outfile," %d",num_pureang_so_in_class[class][l][irr]);
	    fprintf(outfile,"\n\n");
	  }
	}

	/*Compute SO coefficients and labels for ALL classes - children of a unique class "class"*/
	for(i=class_first;i<class_last;i++) {
	  class_so_coeff[i][l] = init_matrix(ao_max*unique_class_degen[uc],ao_max);
	  for(irr=0;irr<nirreps;irr++)
	    bzero((char *)coeff_irr[irr],sizeof(int)*ao_max);
	  for(symop=0;symop<nirreps;symop++)
	    if (class_orbit[class_first][symop] == i)
	      for(irr=0;irr<nirreps;irr++)
		if (num_cart_so_in_class[class][l][irr] != 0) /*There are SOs in this class and ang. momentum type*/
		  for(ao=0;ao<ao_max;ao++)
		    coeff_irr[irr][ao] += irr_char[irr][symop]*ao_type_transmat[l][symop][ao];
	  so_cnt = 0;
	  for(irr=0;irr<nirreps;irr++)
	    if (num_cart_so_in_class[class][l][irr] != 0)
	      for(ao=0;ao<ao_max;ao++)
		if (coeff_irr[irr][ao] != 0)
		  class_so_coeff[i][l][so_cnt++][ao] = sign(coeff_irr[irr][ao]);
	}
	for(irr=0;irr<nirreps;irr++)
	  bzero((char *)coeff_irr[irr],sizeof(int)*ao_max);
      }
    }

    /*---------------------------------
      Remove after testing is complete
     ---------------------------------*/
    if (print_lvl >= DEBUGPRINT)
      for(uc=0;uc<num_unique_classes;uc++) {
	class = uc2c[uc];
	class_first = class;
	class_last = class + unique_class_degen[uc];
	lmax = max_angmom_class[class];
	for(i=class_first;i<class_last;i++) {
	  fprintf(outfile,"\n    Class = %d\n",i+1);
	  for(l=0;l<=lmax;l++) {
	    ao_max = ioff[l+1];
	    fprintf(outfile,"     l = %d\n",l);
	    for(j=0;j<ao_max*unique_class_degen[uc];j++) {
	      fprintf(outfile,"     ");
	      for(ao=0;ao<ao_max;ao++)
		fprintf(outfile,"    %4.1lf",class_so_coeff[i][l][j][ao]);
	      fprintf(outfile,"\n");
	    }
	  }
	}
      }

    return;
}



