! *****************************************************************************
!> \\brief provides specific implementations for common cases of xyz_to_vab routine.
!> 
!> \\note
!>     ____              _ _     __  __           _ _  __         _____ _     _       _____ _ _      _
!>    |  _ \  ___  _ __ ( ) |_  |  \/  | ___   __| (_)/ _|_   _  |_   _| |__ (_)___  |  ___(_) | ___| |
!>    | | | |/ _ \| '_ \|/| __| | |\/| |/ _ \ / _` | | |_| | | |   | | | '_ \| / __| | |_  | | |/ _ \ |
!>    | |_| | (_) | | | | | |_  | |  | | (_) | (_| | |  _| |_| |   | | | | | | \__ \ |  _| | | |  __/_|
!>    |____/ \___/|_| |_|  \__| |_|  |_|\___/ \__,_|_|_|  \__, |   |_| |_| |_|_|___/ |_|   |_|_|\___(_)
!>                                                        |___/
!>      ____ _                  ___                              _ _       _       _
!>     / ___| | ___  ___  ___  |_ _|_ __ ___  _ __ ___   ___  __| (_) __ _| |_ ___| |_   _
!>    | |   | |/ _ \/ __|/ _ \  | || '_ ` _ \| '_ ` _ \ / _ \/ _` | |/ _` | __/ _ \ | | | |
!>    | |___| | (_) \__ \  __/  | || | | | | | | | | | |  __/ (_| | | (_| | ||  __/ | |_| |
!>     \____|_|\___/|___/\___| |___|_| |_| |_|_| |_| |_|\___|\__,_|_|\__,_|\__\___|_|\__, |
!>                                                                                   |___/
!>     _____ _     _       _____ _ _      _
!>    |_   _| |__ (_)___  |  ___(_) | ___| |
!>      | | | '_ \| / __| | |_  | | |/ _ \ |
!>      | | | | | | \__ \ |  _| | | |  __/_|
!>      |_| |_| |_|_|___/ |_|   |_|_|\___(_)
!>
!>      This is a template
!>
!>      **** DO NOT MODIFY THIS .f90 FILE ****
!>      modify the .template instead
!>      The script to regenerate this file can be found at autotune_grid/xyz_to_vab/
!> \par History
!>      07.2012 Fixed a bug with the dimension of coef_xyz
!>      06.2012 Template cleanup 
!>      05.2012 created
!>
!> \\author Ruyman Reyes
! *****************************************************************************

! Caller routine
      SUBROUTINE call_to_xyz_to_vab(prefactor, coef_xyz, lp, &
                                    la_max_local, lb_max_local, &
                                    rp, ra, rab, vab, coset, & 
                                    la_min_local, lb_min_local, &
                                    maxl, lvab, hvab)
      
              USE kinds,                        ONLY: dp
      
             REAL(KIND=dp), INTENT(IN) :: prefactor
             REAL(KIND=dp), DIMENSION(((lp+1)*(lp+2)*(lp+3))/6), &
                                  INTENT(INOUT) :: coef_xyz
             INTEGER, INTENT(IN) :: lp, la_max_local, lb_max_local,&
                                    la_min_local, lb_min_local
             INTEGER, INTENT(IN) :: maxl, lvab, hvab
             REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: rp, ra, rab
             REAL(KIND=dp), DIMENSION(lvab,hvab), INTENT(INOUT)  :: vab
             INTEGER,  DIMENSION(-1:maxl,-1:maxl,-1:maxl), &
                       INTENT(IN)     :: coset

      
      	<$--(if cray_tracing)-->
      	    include "pat_apif.h"
      	    INTEGER :: istat
      	    CALL PAT_sampling_state(PAT_STATE_ON,istat)
      	    CALL PAT_region_begin(1,"XYZ_TO_VAB_X_X",istat)
      	<$--(end)-->   
          SELECT CASE(la_max_local)
           <$--(for la_value in range(0,la_max_local+1))--> 
               CASE (@<la_value>@)
                    SELECT CASE(lb_max_local)
                    <$--(for lb_value in range(0,lb_max_local+1))--> 
                             CASE(@<lb_value>@) 
                               CALL xyz_to_vab_@<la_value>@_@<lb_value>@(prefactor, coef_xyz, lp, &
                                                  la_max_local, lb_max_local, &
                                                  rp, ra, rab, vab, coset, la_min_local, & 
                                                  lb_min_local, maxl, lvab, hvab)
  
                    <$--(end)-->  
                             CASE DEFAULT
  			          CALL xyz_to_vab(prefactor, coef_xyz, lp, &
                                                  la_max_local, lb_max_local, &
                                                  rp, ra, rab, vab, coset, la_min_local, &
                                                  lb_min_local, maxl, lvab, hvab)
                     END SELECT
           <$--(end)-->  
               CASE DEFAULT
  			CALL xyz_to_vab(prefactor, coef_xyz, lp, &
  					 la_max_local, lb_max_local, &
  					  rp, ra, rab, vab, coset, la_min_local, lb_min_local, maxl, lvab, hvab)
  
           END SELECT
  	   
      	<$--(if cray_tracing)-->
      	    CALL PAT_region_end(1, istat)
      	    CALL PAT_sampling_state(PAT_STATE_OFF,istat)
      	<$--(end)-->
      
      	    END SUBROUTINE call_to_xyz_to_vab
      
!!! Cloned routines
<$--(for la_max_local in range(0,la_max_local+1))-->
 <$--(for lb_max_local in range(0,lb_max_local+1))-->
   <$--(if (la_max_local + lb_max_local) <= 4)-->
          ! Fully unrolled version
    <$--(include)--> xyz_to_vab.template <$--(end)-->
   <$--(else)-->
          ! Partial unrolled version
    <$--(include)--> xyz_to_vab_nonunroll.template <$--(end)-->
   <$--(end)-->
 <$--(end)-->
<$--(end)-->

!!! End of cloned routines

      ! Original routine 
      SUBROUTINE xyz_to_vab(prefactor, coef_xyz, lp, la_max_local, &
                             lb_max_local, rp, ra, rab, vab, coset, &
                             la_min_local, lb_min_local, maxl, &
                             lvab, hvab)
      
              USE kinds,                        ONLY: dp
              
         implicit none
      
             ! PARAMETERS
             REAL(KIND=dp), INTENT(IN) :: prefactor
             INTEGER, INTENT(IN) :: lp, la_max_local, lb_max_local, &
                                        la_min_local, lb_min_local, &
                                        maxl, lvab, hvab
             INTEGER,  DIMENSION(-1:maxl,-1:maxl,-1:maxl), &
                       INTENT(IN)     :: coset


             REAL(KIND=dp), DIMENSION(((lp+1)*(lp+2)*(lp+3))/6), &
                             INTENT(INOUT) :: coef_xyz
             REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: rp, ra, rab
             REAL(KIND=dp), DIMENSION(lvab,hvab), INTENT(INOUT) :: vab
      
             ! LOCAL
             INTEGER :: ico,jco,lxa,lxb,lxp,lxyz,lyp,lzp, iaxis, l, k, &
                        lya,lyb,lza,lzb
             REAL(KIND=dp) :: a,b,binomial_k_lxa,binomial_l_lxb
      
          REAL(KIND=dp), DIMENSION(:,:,:,:), ALLOCATABLE :: alpha
          REAL(KIND=dp), DIMENSION(:,:), ALLOCATABLE :: coef_ttz
          REAL(KIND=dp), DIMENSION(:,:,:,:), ALLOCATABLE :: coef_tyz
      
      
          coef_xyz=coef_xyz*prefactor
      
      !   *** initialise the coefficient matrix, we transform the sum
      !
      !   sum_{lxa,lya,lza,lxb,lyb,lzb} P_{lxa,lya,lza,lxb,lyb,lzb} (x-a_x)**lxa (y-a_y)**lya (z-a_z)**lza (x-b_x)**lxb (y-a_y)**lya (z-a_z)**lza
      !
      !   into
      !
      !   sum_{lxp,lyp,lzp} P_{lxp,lyp,lzp} (x-p_x)**lxp (y-p_y)**lyp (z-p_z)**lzp
      !
      !   where p is center of the product gaussian, and lp = la_max + lb_max
      !   (current implementation is l**7)
      !
      !
      !   compute polynomial expansion coefs -> (x-a)**lxa (x-b)**lxb -> sum_{ls} alpha(ls,lxa,lxb,1)*(x-p)**ls
      !
      !   *** make the alpha matrix ***
          ALLOCATE(alpha(0:lp,0:la_max_local,0:lb_max_local,3))
          alpha(:,:,:,:)=0.0_dp
          DO iaxis=1,3
          DO lxa=0,la_max_local
          DO lxb=0,lb_max_local
             binomial_k_lxa=1.0_dp
             a=1.0_dp
             DO k=0,lxa
              binomial_l_lxb=1.0_dp
              b=1.0_dp
              DO l=0,lxb
                 alpha(lxa-l+lxb-k,lxa,lxb,iaxis)=alpha(lxa-l+lxb-k,lxa,lxb,iaxis)+ &
                                   binomial_k_lxa*binomial_l_lxb*a*b
                 binomial_l_lxb=binomial_l_lxb*REAL(lxb-l,dp)/REAL(l+1,dp)
                 b=b*(rp(iaxis)-(ra(iaxis)+rab(iaxis)))
              ENDDO
              binomial_k_lxa=binomial_k_lxa*REAL(lxa-k,dp)/REAL(k+1,dp)
              a=a*(-ra(iaxis)+rp(iaxis))
             ENDDO
          ENDDO
          ENDDO
          ENDDO
      
          !
          !   compute v_{lxa,lya,lza,lxb,lyb,lzb} given v_{lxp,lyp,lzp} and alpha(ls,lxa,lxb,1)
          !   use a three step procedure
          !
      
          ALLOCATE(coef_ttz(0:la_max_local,0:lb_max_local))
          ALLOCATE(coef_tyz(0:la_max_local,0:lb_max_local,0:la_max_local,0:lb_max_local))
          lxyz=0
          DO lzp=0,lp
             coef_tyz=0.0_dp
             DO lyp=0,lp-lzp
                coef_ttz=0.0_dp
                DO lxp=0,lp-lzp-lyp
                   lxyz=lxyz+1
                   DO lxb=0,lb_max_local
                   DO lxa=0,la_max_local
                      coef_ttz(lxa,lxb)=coef_ttz(lxa,lxb)+coef_xyz(lxyz)*alpha(lxp,lxa,lxb,1)
                   ENDDO
                   ENDDO
      
                ENDDO
      
                DO lyb=0,lb_max_local
                DO lya=0,la_max_local
                   DO lxb=0,lb_max_local-lyb
                   DO lxa=0,la_max_local-lya
                      coef_tyz(lxa,lxb,lya,lyb)=coef_tyz(lxa,lxb,lya,lyb)+coef_ttz(lxa,lxb)*alpha(lyp,lya,lyb,2)
                   ENDDO
                   ENDDO
                ENDDO
                ENDDO
      
             ENDDO
      
             DO lzb=0,lb_max_local
             DO lza=0,la_max_local
                DO lyb=0,lb_max_local-lzb
                DO lya=0,la_max_local-lza
                   DO lxb=MAX(lb_min_local-lzb-lyb,0),lb_max_local-lzb-lyb
                   jco=coset(lxb,lyb,lzb)
                   DO lxa=MAX(la_min_local-lza-lya,0),la_max_local-lza-lya
                      ico=coset(lxa,lya,lza)
                      vab(ico,jco)=vab(ico,jco)+coef_tyz(lxa,lxb,lya,lyb)*alpha(lzp,lza,lzb,3)
                   ENDDO
                   ENDDO
                ENDDO
                ENDDO
             ENDDO
             ENDDO
      
          ENDDO
      
          DEALLOCATE(coef_tyz)
          DEALLOCATE(coef_ttz)
          DEALLOCATE(alpha)
      
      
      
            end subroutine xyz_to_vab
      
      
      
