 MODULE FPSM2D_DERIV
 
  USE FPSM2D_PINPUT
  USE DFOURIER
  
  IMPLICIT NONE 

  TYPE FPSM2D_DERIV_TYPE 
   PRIVATE
   
   TYPE (DFOURIER_TYPE) :: DF
   LOGICAL :: LROTATE
   INTEGER :: NBUF,NPES,N1,N2
   REAL, DIMENSION(:,:), POINTER :: BUF1
   REAL, DIMENSION(:,:), POINTER :: BUF2
   REAL, DIMENSION(:,:), POINTER :: AUXARRAY
	 
  END TYPE FPSM2D_DERIV_TYPE
  
  PRIVATE :: ROTATEANDAPPLY,BUF2U,U2BUF
 
 CONTAINS
!==================================================

 SUBROUTINE FPSM2D_DERIV_NEW(THIS,INPUT,I,D,OK)
  TYPE(FPSM2D_DERIV_TYPE), INTENT(INOUT) :: THIS
  TYPE(FPSM2D_PINPUT_TYPE), INTENT(IN) :: INPUT
  INTEGER, INTENT(IN) :: I
  REAL, INTENT(IN) :: D
  LOGICAL, INTENT(OUT) :: OK
  !------------------------------
  INTEGER, DIMENSION(2) :: NN,ND
  INTEGER :: NPES,IERR
  REAL, DIMENSION(2) :: DD
  
  CALL FPSM2D_PINPUT_GETSPACE(INPUT,NN,ND)
  
  CALL DFOURIER_NEW(THIS%DF,NN(I),ND(3-I),D,OK)
  IF(OK) THEN
    THIS%LROTATE= (I.GT.1) !DERIVATIVE IN DIRECTION OTHER THAN 1ST REQUIRES ROTATION
    IF(THIS%LROTATE) THEN
	 
	 CALL MPI_COMM_SIZE(MPI_COMM_WORLD,THIS%NPES,IERR)
	 !THIS%NPES=1
	 
	 THIS%NBUF=PRODUCT(ND)
	 THIS%N1=ND(1)
	 THIS%N2=ND(2)
	 ALLOCATE(THIS%BUF1(THIS%NBUF,THIS%NPES))
	 ALLOCATE(THIS%BUF2(THIS%NBUF,THIS%NPES))
	 ALLOCATE(THIS%AUXARRAY(NN(2),ND(1)))
	ENDIF
  ELSE
   PRINT*, 'ERROR IN FPSM2D_DERIV_NEW' 
  ENDIF
 
 END SUBROUTINE FPSM2D_DERIV_NEW
!===========================================

 SUBROUTINE FPSM2D_DERIV_KILL(THIS)
  TYPE(FPSM2D_DERIV_TYPE), INTENT(INOUT) :: THIS
 
  CALL DFOURIER_KILL(THIS%DF)
  IF (THIS%LROTATE) THEN
   DEALLOCATE(THIS%BUF1)
   DEALLOCATE(THIS%BUF2)
   DEALLOCATE(THIS%AUXARRAY)
  ENDIF
 
 END SUBROUTINE FPSM2D_DERIV_KILL
 !===========================================
 
 SUBROUTINE FPSM2D_DERIV_APPLY(THIS,U,DU)
  TYPE(FPSM2D_DERIV_TYPE), INTENT(INOUT) :: THIS
  REAL, DIMENSION(:,:), INTENT(IN) :: U
  REAL, DIMENSION(:,:), INTENT(OUT) :: DU
  !-----------------------------------------
  
  IF(THIS%LROTATE) THEN
    CALL ROTATEANDAPPLY(THIS,U,DU)
  ELSE
	CALL DFOURIER_APPLY(THIS%DF,U,DU)
  ENDIF  

 
 END SUBROUTINE FPSM2D_DERIV_APPLY
 !===========================================

 SUBROUTINE ROTATEANDAPPLY(THIS,U,DU)
   TYPE(FPSM2D_DERIV_TYPE), INTENT(INOUT) :: THIS
  REAL, DIMENSION(:,:), INTENT(IN) :: U
  REAL, DIMENSION(:,:), INTENT(OUT) :: DU
  !-----------------------------------------
  INTEGER :: IERR
  
    CALL U2BUF(U,THIS%BUF1,THIS%NPES,THIS%N1,THIS%N2) !U -> BUF1
	
	CALL MPI_ALLTOALL(THIS%BUF1,THIS%NBUF,MPI_REAL,&
                      THIS%BUF2,THIS%NBUF,MPI_REAL,&
                      MPI_COMM_WORLD,IERR)
	
	CALL BUF2U(THIS%BUF2,THIS%AUXARRAY,THIS%NPES,THIS%N2,THIS%N1)
	
	CALL DFOURIER_APPLY(THIS%DF,THIS%AUXARRAY,THIS%AUXARRAY)
	
	CALL U2BUF(THIS%AUXARRAY,THIS%BUF1,THIS%NPES,THIS%N2,THIS%N1)
	
	CALL MPI_ALLTOALL(THIS%BUF1,THIS%NBUF,MPI_REAL,&
                      THIS%BUF2,THIS%NBUF,MPI_REAL,&
                      MPI_COMM_WORLD,IERR)
	!THIS%BUF2=THIS%BUF1 !SCALAR VERSION)
    
	CALL BUF2U(THIS%BUF2,DU,THIS%NPES,THIS%N1,THIS%N2) !BUF2 -> DU
			  
 END SUBROUTINE ROTATEANDAPPLY
 !====================================================== 
 
 SUBROUTINE U2BUF(U,BUF,NPES,N1,N2)
  REAL, DIMENSION(:,:), INTENT(IN) :: U
  REAL, DIMENSION(:,:), INTENT(OUT) :: BUF
  INTEGER, INTENT(IN) :: NPES,N1,N2
  !----------------------------------------
  INTEGER :: IP,IX1,IX2
  
  IX2=0
  DO IP=1,NPES
   IX1=IX2+1
   IX2=IX2+N1
   BUF(:,IP)=PACK(U(IX1:IX2,:),.TRUE.)
  ENDDO
 
 END SUBROUTINE U2BUF
 !======================================================
 
 SUBROUTINE BUF2U(BUF,U,NPES,N1,N2)
  REAL, DIMENSION(:,:), INTENT(IN) :: BUF
  REAL, DIMENSION(:,:), INTENT(OUT) :: U
  INTEGER, INTENT(IN) :: NPES,N1,N2
  !----------------------------------------
  INTEGER :: IP,IY1,IY2
  
  IY2=0
  DO IP=1,NPES
   IY1=IY2+1
   IY2=IY2+N1
   U(IY1:IY2,:)=RESHAPE(BUF(:,IP),(/N2,N1/),ORDER=(/2,1/))
  ENDDO
  
 END SUBROUTINE BUF2U
 !======================================================
 
 
 END MODULE FPSM2D_DERIV