
#/*##########################################################################
# Copyright (C) 2001-2013 European Synchrotron Radiation Facility
#
#              PyHST2  
#  European Synchrotron Radiation Facility, Grenoble,France
#
# PyHST2 is  developed at
# the ESRF by the Scientific Software  staff.
# Principal author for PyHST2: Alessandro Mirone.
#
# This program 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 2 of the License, or (at your option) 
# any later version.
#
# PyHST2 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
# PyHST2; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307, USA.
#
# PyHST2 follows the dual licensing model of Trolltech's Qt and Riverbank's PyQt
# and cannot be used as a free plugin for a non-free program. 
#
# Please contact the ESRF industrial unit (industry@esrf.fr) if this license 
# is a problem for you.
#############################################################################*/
#include<math.h>
#include <stdlib.h>
#include<stdio.h>
#include <string.h>

#include <cuda.h>
// #include <cutil.h>


#define FROMCU
extern "C" {
#include<CCspace.h>
}


#  define CUDA_SAFE_CALL_NO_SYNC( call) {                                    \
    cudaError err = call;                                                    \
    if( cudaSuccess != err) {                                                \
        fprintf(stderr, "Cuda error in file '%s' in line %i : %s.\n",        \
                __FILE__, __LINE__, cudaGetErrorString( err) );              \
        exit(EXIT_FAILURE);                                                  \
    } }

#  define CUDA_SAFE_CALL( call)     CUDA_SAFE_CALL_NO_SYNC(call);                                            \

// -----------------------------------------------------------------------------------------------




//Round a / b to nearest higher integer value
int iDivUp(int a, int b){
    return (a % b != 0) ? (a / b + 1) : (a / b);
}

//Align a to nearest higher multiple of b
int iAlignUp(int a, int b){
    return (a % b != 0) ?  (a - a % b + b) : a;
}



texture<float, 2, cudaReadModeElementType> texSlice;

texture<float, 2, cudaReadModeElementType> texA;
texture<float, 2, cudaReadModeElementType> texB;
texture<float, 2, cudaReadModeElementType> texC;

__global__ static void    slicerot_kernel( int dimslice,
					  float *d_SLICE,
					  float dangle,
					  int duty_oversampling) {

  const int tidx = threadIdx.x;
  const int bidx = blockIdx.x;
  const int tidy = threadIdx.y;
  const int bidy = blockIdx.y;

  const float axis_position =  (dimslice-1 )/ 2.0f  ; 
  const float rx = (16* bidx  + tidx     ) - axis_position;
  const float ry = (16* bidy  + tidy     ) - axis_position;

  const float anglestep     =  dangle/(duty_oversampling)        ;
  const float angleshift    = -anglestep*(duty_oversampling-1)/2.0f ;
  
  float res=0;
  float rotangle;
  float x,y;
  for(int irot=0; irot<duty_oversampling ; irot++) {
    rotangle =  angleshift + irot *   anglestep  ; 
    float cc = cos(rotangle), ss = sin(rotangle)  ; 
    x   =   rx*cc  -  ry*ss  +   axis_position   ; 
    y   =   ry*cc  +  rx*ss  +   axis_position   ; 
    res =   res + tex2D(texSlice ,x + 1.5f, y  +1.5f ) ;
  }
  d_SLICE[ 16*gridDim.x*(bidy*16+tidy) + bidx*16 + tidx     ] = res/duty_oversampling  ;
}

__global__ static void    forward_kernel( float *d_Sino, 
					  int dimslice,
					  int num_bins,
					  float* angles_per_project ,
					  float axis_position,
					  float *d_axis_corrections,
					  int   *d_beginPos    ,
					  int   *d_strideJoseph,
					  int   *d_strideLine  ,
					  int num_projections,
					  int  dimrecx,
					  int  dimrecy,
					  float cpu_offset_x,
					  float cpu_offset_y,
					  int josephnoclip
					  ) {
  
  const int tidx = threadIdx.x;
  const int bidx = blockIdx.x;
  const int tidy = threadIdx.y;
  const int bidy = blockIdx.y;

  float angle;
  float cos_angle,sin_angle ; 


  __shared__  float   shared[7*16] ;   
  float   *corrections  = shared ;   
  int     *beginPos     =(int*)( &shared[16]) ;   
  int     *strideJoseph =(int*)( &shared[16+2*16]) ;   
  int     *strideLine   =(int*)( &shared[16+4*16]) ;   


  // thread will use corrections[tidy] 
  // All are read by first warp
  int offset, OFFSET;
  switch(tidy)
    {
    case 0:
      corrections[ tidx ]= d_axis_corrections[ bidy*16+tidx];
      break;
    case 1:
    case 2:
      offset = 16*(tidy-1); 
      OFFSET = dimrecy*(tidy-1); 
      beginPos    [offset + tidx ]=  d_beginPos[ OFFSET+ bidy*16+tidx]  ; 
      break;
    case 3:  
    case 4:
      offset = 16*(tidy-3); 
      OFFSET = dimrecy*(tidy-3); 
      strideJoseph[offset + tidx ]=  d_strideJoseph[OFFSET + bidy*16+tidx]  ; 
      break;
    case 5:  
    case 6:
      offset = 16*(tidy-5); 
      OFFSET = dimrecy*(tidy-5); 
      strideLine[offset + tidx ]=  d_strideLine[OFFSET + bidy*16+tidx]  ; 
      break;
    } 
  

  __syncthreads();

  angle = angles_per_project[ bidy*16+tidy ]   ;
  cos_angle = cos(angle);
  sin_angle = sin(angle);
  
  if(fabs(cos_angle) > 0.70710678f ) {
    if( cos_angle>0) {
      cos_angle = cos(angle);
      sin_angle = sin(angle);
    } else{
      cos_angle = -cos(angle);
      sin_angle = -sin(angle);
    }
  } else {
    if( sin_angle>0) {
      cos_angle =  sin(angle);
      sin_angle = -cos(angle);
    } else {
      cos_angle = -sin(angle);
      sin_angle =  cos(angle);
    }
  }
  float res=0.0f;
  
  float axis_corr = axis_position  + corrections[ tidy ]; 
  float axis      = axis_position ; 

  float xpix = ( bidx*16+tidx )-cpu_offset_x;
  float posx = axis*(1.0f-sin_angle/cos_angle ) +(xpix-(axis_corr) )/cos_angle ;
  
  float shiftJ = sin_angle/cos_angle;

  float x1 = fminf(-sin_angle/cos_angle ,0.f);
  float x2 = fmaxf(-sin_angle/cos_angle ,0.f);


  float  Area;

  Area=1.0f/cos_angle;
  int stlA, stlB , stlAJ, stlBJ ;

  stlA=strideLine[16+tidy];
  stlB=strideLine[tidy];
  stlAJ=strideJoseph[16+tidy];
  stlBJ=strideJoseph[tidy];

  int beginA = beginPos    [16+tidy ];
  int beginB = beginPos    [tidy ];
  float add;
  int l;
  // #pragma unroll 4

  if(josephnoclip) {
    for(int j=0; j<dimslice; j++) {  // j come Joseph
      
      x1 = beginA +(posx)*stlA + (j)*stlAJ+1.5f;
      x2 = beginB +(posx)*stlB + (j)*stlBJ+1.5f; 
      // int l = (x1>=0.0 )*(x1<dimslice+2)*( x2>=0.0)*( x2<dimslice+2 ) ;
      add = tex2D(texSlice, x1,x2); 
      res = res + add; // *l; 
      posx+=shiftJ;
    }
  } else {
    for(int j=0; j<dimslice; j++) {  // j come Joseph
      x1 = beginA +(posx)*stlA + (j)*stlAJ+1.5f;
      x2 = beginB +(posx)*stlB + (j)*stlBJ+1.5f; 
      l = (x1>=0.0f )*(x1<(dimslice+2))*( x2>=0.0f)*( x2<(dimslice+2) ) ;
      add = tex2D(texSlice, x1,x2); 
      res = res + add*l; // *l; 
      posx+=shiftJ;
    }
  }
  if(   ( bidy*16+tidy ) < num_projections  &&  ( bidx*16+tidx ) < num_bins ) {
    d_Sino[ dimrecx*( bidy*16+tidy ) +   ( bidx*16+tidx )  ] = Area*res *M_PI*0.5/num_projections;
    // d_Sino[ dimrecx*( bidy*16+tidy ) +   ( bidx*16+tidx )  ] = tidy*16+tidx ;
  }
}


__global__ static void    forward_kernel_fan( float *d_Sino, 
					      int dimslice,
					      int num_bins,
					      float* angles_per_project ,
					      float axis_position,
					      float *d_axis_corrections,
					      int   *d_beginPos    ,
					      int   *d_strideJoseph,
					      int   *d_strideLine  ,
					      int num_projections,
					      int  dimrecx,
					      int  dimrecy,
					      float cpu_offset_x,
					      float cpu_offset_y,
					      int josephnoclip,
					      float fan_factor,
					      float source_x
					      ) {
  
  const int tidx = threadIdx.x;
  const int bidx = blockIdx.x;
  const int tidy = threadIdx.y;
  const int bidy = blockIdx.y;

  float angle;
  float cos_angle,sin_angle ; 


  __shared__  float   shared[7*16] ;   
  float   *corrections  = shared ;   
  int     *beginPos     =(int*)( &shared[16]) ;   
  int     *strideJoseph =(int*)( &shared[16+2*16]) ;   
  int     *strideLine   =(int*)( &shared[16+4*16]) ;   


  // thread will use corrections[tidy] 
  // All are read by first warp
  int offset, OFFSET;
  switch(tidy)
    {
    case 0:
      corrections[ tidx ]= d_axis_corrections[ bidy*16+tidx];
      break;
    case 1:
    case 2:
      offset = 16*(tidy-1); 
      OFFSET = dimrecy*(tidy-1); 
      beginPos    [offset + tidx ]=  d_beginPos[ OFFSET+ bidy*16+tidx]  ; 
      break;
    case 3:  
    case 4:
      offset = 16*(tidy-3); 
      OFFSET = dimrecy*(tidy-3); 
      strideJoseph[offset + tidx ]=  d_strideJoseph[OFFSET + bidy*16+tidx]  ; 
      break;
    case 5:  
    case 6:
      offset = 16*(tidy-5); 
      OFFSET = dimrecy*(tidy-5); 
      strideLine[offset + tidx ]=  d_strideLine[OFFSET + bidy*16+tidx]  ; 
      break;
    } 
  

  __syncthreads();

  angle = angles_per_project[ bidy*16+tidy ]   ;
  cos_angle = cos(angle);
  sin_angle = sin(angle);
  
  
  float axis_corr = axis_position  + corrections[ tidy ]; 
  float axis      = axis_position ; 
  float xpix = ( bidx*16+tidx )-cpu_offset_x;


  // angle =  angle +  fan_factor * (   xpix  - axis_corr   )   ;
  angle =  angle +  fan_factor * (   xpix  - source_x   )   ;

  if(fabs(cos_angle) > 0.70710678f ) {
    if( cos_angle>0) {
      cos_angle = cos(angle);
      sin_angle = sin(angle);
    } else{
      cos_angle = -cos(angle);
      sin_angle = -sin(angle);
    }
  } else {
    if( sin_angle>0) {
      cos_angle =  sin(angle);
      sin_angle = -cos(angle);
    } else {
      cos_angle = -sin(angle);
      sin_angle =  cos(angle);
    }
  }
  float res=0.0f;


  float posx = axis*(1.0f-sin_angle/cos_angle ) +(xpix-(axis_corr) )/cos_angle ;
  
  float shiftJ = sin_angle/cos_angle;

  float x1 = fminf(-sin_angle/cos_angle ,0.f);
  float x2 = fmaxf(-sin_angle/cos_angle ,0.f);


  float  Area;

  Area=1.0f/cos_angle;
  int stlA, stlB , stlAJ, stlBJ ;

  stlA=strideLine[16+tidy];
  stlB=strideLine[tidy];
  stlAJ=strideJoseph[16+tidy];
  stlBJ=strideJoseph[tidy];

  int beginA = beginPos    [16+tidy ];
  int beginB = beginPos    [tidy ];
  float add;
  int l;
  // #pragma unroll 4

  if(josephnoclip) {
    for(int j=0; j<dimslice; j++) {  // j come Joseph
      
      x1 = beginA +(posx)*stlA + (j)*stlAJ+1.5f;
      x2 = beginB +(posx)*stlB + (j)*stlBJ+1.5f; 
      // int l = (x1>=0.0 )*(x1<dimslice+2)*( x2>=0.0)*( x2<dimslice+2 ) ;
      add = tex2D(texSlice, x1,x2); 
      res = res + add; // *l; 
      posx+=shiftJ;
    }
  } else {
    for(int j=0; j<dimslice; j++) {  // j come Joseph
      x1 = beginA +(posx)*stlA + (j)*stlAJ+1.5f;
      x2 = beginB +(posx)*stlB + (j)*stlBJ+1.5f; 
      l = (x1>=0.0f )*(x1<(dimslice+2))*( x2>=0.0f)*( x2<(dimslice+2) ) ;
      add = tex2D(texSlice, x1,x2); 
      res = res + add*l; // *l; 
      posx+=shiftJ;
    }
  }
  if(   ( bidy*16+tidy ) < num_projections  &&  ( bidx*16+tidx ) < num_bins ) {
    d_Sino[ dimrecx*( bidy*16+tidy ) +   ( bidx*16+tidx )  ] = Area*res *M_PI*0.5/num_projections;
    // d_Sino[ dimrecx*( bidy*16+tidy ) +   ( bidx*16+tidx )  ] = tidy*16+tidx ;
  }
}


// extern "C" {

// int C_HST_PROJECT_1OVER(
//    int  num_bins,        /* Number of bins in each sinogram  */
//    int  num_projections, /* Number of projections in  each sinogram */
//    float*  angles_per_project, 
//    float   axis_position,
//    float* SINOGRAMS ,
//    float *SLICE,
//    int   dimslice,
//    float * axis_corrections
//    );

// }
#include <cufft.h>
#define CUDA_SAFE_FFT(call){                                                   \
    cufftResult err = call;                                                    \
    if( CUFFT_SUCCESS != err) {                                                \
        fprintf(stderr, "Cuda error in file '%s' in line %i : %s.\n",          \
                __FILE__, __LINE__, err );                                     \
        exit(EXIT_FAILURE);                                                    \
    } }
#define CUFFT_SAFE_CALL(call, extramsg) {do {	\
       cufftResult err = call;    \
       if( CUFFT_SUCCESS  != err) {   \
	 if(err==   CUFFT_INVALID_PLAN   )				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_INVALID_PLAN"    ,  extramsg); \
	 if(err==  CUFFT_ALLOC_FAILED   )				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_ALLOC_FAILED"    ,  extramsg); \
	 if(err==  CUFFT_INVALID_TYPE   )				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__,  "CUFFT_INVALID_TYPE"  ,  extramsg); \
	 if(err==  CUFFT_INVALID_VALUE  )				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_INVALID_VALUE"   ,  extramsg); \
	 if(err==    CUFFT_INTERNAL_ERROR)				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_INTERNAL_ERROR"   ,  extramsg); \
	 if(err==  CUFFT_EXEC_FAILED )					\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_EXEC_FAILED"     ,  extramsg); \
	 if(err==    CUFFT_SETUP_FAILED )				\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_SETUP_FAILED"   ,  extramsg); \
	 if(err==  CUFFT_INVALID_SIZE)					\
	   sprintf(messaggio, "Error in file '%s' in line %i :%s -- %s \n", \
                   __FILE__, __LINE__, "CUFFT_INVALID_SIZE"   ,  extramsg); \
	 fprintf(stderr, messaggio );					\
	 exit(EXIT_FAILURE);						\
	 								\
       } } while (0) ;}




void  C_HST_PROJECT_1OVER_GPU(
   void *gpuctx,
   int  num_bins,        /* Number of bins in each sinogram  */
   int  num_projections, /* Number of projections in  each sinogram */
   float*  angles_per_projectA, 
   float   axis_position,
   float* SINOGRAMS ,
   float *SLICE,
   int   dimslice,
   float * axis_corrections,
   float cpu_offset_x,
   float cpu_offset_y,
   int josephnoclip,
   float    DETECTOR_DUTY_RATIO        ,
   int    DETECTOR_DUTY_OVERSAMPLING,
   int memisonhost,
   float fan_factor,
   float source_x
   )
{
  
  cuCtxSetCurrent ( *((CUcontext *) gpuctx  ))  ; 
  axis_position= (dimslice-1)/2.0f   ; 
  int NblocchiPerLinea= iDivUp(num_bins, 16 );
  int NblocchiPerColonna =  iDivUp(num_projections, 16 ) ; 
  
  
  int dimrecx, dimrecy;
  dimrecx = NblocchiPerLinea*16 ;
  dimrecy = NblocchiPerColonna*16 ; 
    
  float*  angles_per_project = (float*)malloc( sizeof(float)*dimrecy ) ;
  {
    memcpy(angles_per_project, angles_per_projectA,  sizeof(float)*num_projections   );
    int i;
    for(i=num_projections; i< dimrecy; i++) angles_per_project[i]=angles_per_project[num_projections-1 ];
  }

  int beginPos[2][ dimrecy     ];
  int strideJoseph[2][ dimrecy ];
  int strideLine  [2][ dimrecy ];


  // create the sinogram on device
  float * d_SINO ; 
  CUDA_SAFE_CALL(cudaMalloc((void**)&d_SINO , sizeof(float) * dimrecx* dimrecy      ));
  CUDA_SAFE_CALL(cudaMemset(d_SINO ,0, sizeof(float) * dimrecx* dimrecy    ));
  


  // create the texture with the slice  
  cudaChannelFormatDesc floatTex = cudaCreateChannelDesc<float>();
  cudaArray * a_Slice;
  CUDA_SAFE_CALL( cudaMallocArray(&a_Slice, &floatTex ,  (dimslice+2)   ,  (dimslice+2)) );


  {
    float zeri[dimslice+2];
    memset(zeri,0, (dimslice+2)*sizeof(float));
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 0, 0, zeri , (dimslice+2)*sizeof(float) ,(dimslice+2)*sizeof(float) , 1,cudaMemcpyHostToDevice) );
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 0, 0, zeri , (1)*sizeof(float) ,(1)*sizeof(float) ,(dimslice+2) ,cudaMemcpyHostToDevice) );
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, (dimslice+1)*4, 0, zeri , (1)*sizeof(float) ,(1)*sizeof(float) ,(dimslice+2) ,cudaMemcpyHostToDevice) );
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 0, dimslice+1, zeri , (dimslice+2)*sizeof(float) ,(dimslice+2)*sizeof(float) , 1,cudaMemcpyHostToDevice) );
  }

  if(memisonhost) {
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 1*4, 1, SLICE , (dimslice)*sizeof(float) ,(dimslice)*sizeof(float) , dimslice,
					cudaMemcpyHostToDevice) );
  } else {
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 1*4, 1, SLICE , (dimslice)*sizeof(float) ,(dimslice)*sizeof(float) , dimslice,
					cudaMemcpyDeviceToDevice) );
  }

  texSlice.addressMode[0] = cudaAddressModeClamp;
  texSlice.addressMode[1] = cudaAddressModeClamp;

  texSlice.filterMode = cudaFilterModeLinear;
  texSlice.normalized = false; 
  
  CUDA_SAFE_CALL( cudaBindTextureToArray(texSlice, a_Slice) );
  // nelle texture la prima dimensione e'la x!!!


  if(abs(DETECTOR_DUTY_OVERSAMPLING)>1){

    int NblocchiPerLinea= iDivUp(dimslice, 16 );
    int NblocchiPerColonna =  iDivUp(dimslice, 16 ) ; 
    int dimrecx; // dimrecy;
    dimrecx = NblocchiPerLinea  *16 ;
    // dimrecy = NblocchiPerColonna*16 ;

    float *d_slice;
    CUDA_SAFE_CALL(cudaMalloc((void**)&d_slice , sizeof(float) * dimrecx * dimrecx ) );
    float dangle = (angles_per_project[1]-angles_per_project[0])*DETECTOR_DUTY_RATIO; 
    dim3 dimGrid ( NblocchiPerLinea ,   NblocchiPerLinea );
    dim3      dimBlock         (  16  ,  16   );
    // printf("c_hst_project_1over.cu:  DOING slicerot_kernel with DETECTOR_DUTY_OVERSAMPLING=%d dangle = %e\n",DETECTOR_DUTY_OVERSAMPLING, dangle);
    slicerot_kernel<<<dimGrid,dimBlock>>>( dimslice,
					   d_slice,
					   dangle,
					   abs(DETECTOR_DUTY_OVERSAMPLING) );
    
    CUDA_SAFE_CALL( cudaMemcpy2DToArray(a_Slice, 1*4, 1, d_slice , ( dimrecx )*sizeof(float) ,(dimslice)*sizeof(float) , dimslice,
					cudaMemcpyDeviceToDevice) );
    
    if(DETECTOR_DUTY_OVERSAMPLING<0)  { //  test
      cudaMemcpy2D( SLICE,
		    (dimslice)*sizeof(float),
		    d_slice,
		    (dimrecx)*sizeof(float),
		    (dimslice)*sizeof(float),
		    (dimslice),
		    cudaMemcpyDeviceToHost	 
		    ); 
      return ; 
    }
    CUDA_SAFE_CALL(cudaFree(d_slice));
  }

  // create the axis_corrections on device
  float * d_axis_corrections ; 
  CUDA_SAFE_CALL(cudaMalloc((void**)&d_axis_corrections , sizeof(float) *   num_projections   ));
  CUDA_SAFE_CALL(cudaMemset( d_axis_corrections ,0, sizeof(float) *  num_projections ));
  cudaError_t last = cudaGetLastError();
  // printf("Last error-reset: %s \n", cudaGetErrorString( last));
  CUDA_SAFE_CALL( cudaMemcpy( d_axis_corrections , axis_corrections  , 
			      sizeof(float) * ( num_projections)  ,  cudaMemcpyHostToDevice) );

  // precalculations

  for(int iproj=0; iproj<dimrecy; iproj+=16) {
    // decide sum direction on the basis of the middle projection
    // float angle = (angle_offset) + (iproj +7.5 )* (angle_increment);
    // float angle =  angles_per_project[ iproj+7];
    // float cos_angle = cos(angle);
    // float sin_angle = sin(angle);


    for(int i=0; i<16; i++) {
      float angle =  angles_per_project[ iproj+i];
      float cos_angle = cos(angle);
      float sin_angle = sin(angle);
      
      if(fabs(cos_angle) > 0.70710678 ) {
	if( cos_angle>0) {
	  beginPos[0][ iproj+i]=0;  // vertical
	  beginPos[1][ iproj+i]=0;  // rapida
	  strideJoseph[0][iproj+i]=1;
	  strideJoseph[1][iproj+i]=0;
	  strideLine  [0][iproj+i]=0;
	  strideLine  [1][iproj+i]=1;
	} else {
	  beginPos[0][iproj+i]=dimslice-1;  // vertical
	  beginPos[1][iproj+i]=dimslice-1;  // rapida
	  
	  strideJoseph[0][iproj+i]=-1;
	  strideJoseph[1][iproj+i]=0;
	  strideLine  [0][iproj+i]=0;
	  strideLine  [1][iproj+i]=-1;
	}
      } else {
	if( sin_angle>0) {
	  beginPos[0][iproj+i]=dimslice-1;  // vertical
	  beginPos[1][iproj+i]=0;  // rapida

	  
	  strideJoseph[0][iproj+i]=0;
	  strideJoseph[1][iproj+i]=1;
	  strideLine  [0][iproj+i]=-1;
	  strideLine  [1][iproj+i]=0;
	} else {
	  beginPos[0][iproj+i]=0;  // vertical
	  beginPos[1][iproj+i]=dimslice-1;  // rapida

	  
	  strideJoseph[0][iproj+i]=0;
	  strideJoseph[1][iproj+i]=-1;
	  strideLine  [0][iproj+i]=1;
	  strideLine  [1][iproj+i]=0;
	}
      }

    }
  }

 
  // installing the precalculations on the device 
/*   int   *d_beginPos    ; */
/*   int   *d_strideJoseph; */
/*   int   *d_strideLine  ; */
/*   float *d_A; */
/*   float *d_B; */
/*   float *d_C; */

#define CREATECOPYH2D(nome,tipo,dimensione)     tipo * d_##nome ;		\
  CUDA_SAFE_CALL(cudaMalloc((void**)& d_##nome , sizeof(tipo) *   dimensione  )); \
  CUDA_SAFE_CALL( cudaMemcpy( d_##nome  , nome , sizeof(tipo) * dimensione ,  cudaMemcpyHostToDevice) );
  
  CREATECOPYH2D(beginPos,     int, 2*dimrecy);
  CREATECOPYH2D(strideJoseph, int, 2*dimrecy);
  CREATECOPYH2D(strideLine,   int, 2*dimrecy);

  
  CREATECOPYH2D(angles_per_project,     float, dimrecy);

  // calling kernel
  dim3 dimGrid ( NblocchiPerLinea ,   NblocchiPerColonna );
  dim3      dimBlock         (  16  ,  16   );



  {
    cudaError_t cudaError;
    cudaError = cudaGetLastError();
    if( cudaError != cudaSuccess )
      {
	fprintf(stderr, "CUDA Runtime API Error 1 reported : %s\n", cudaGetErrorString(cudaError));
	exit(0);
      }
  }
 
  if(fan_factor==0) {
    forward_kernel<<<dimGrid,dimBlock,7*16*sizeof(float)>>> (d_SINO, 
							     dimslice,
							     num_bins,
							     d_angles_per_project,
							     axis_position,
							     d_axis_corrections,
							     d_beginPos    ,
							     d_strideJoseph,
							     d_strideLine  ,
							     num_projections,
							     dimrecx,
							     dimrecy,
							     cpu_offset_x,
							     cpu_offset_y,
							     josephnoclip
							     );  
  } else {
    forward_kernel_fan<<<dimGrid,dimBlock,7*16*sizeof(float)>>> (d_SINO, 
							     dimslice,
							     num_bins,
							     d_angles_per_project,
							     axis_position,
							     d_axis_corrections,
							     d_beginPos    ,
							     d_strideJoseph,
							     d_strideLine  ,
							     num_projections,
							     dimrecx,
							     dimrecy,
							     cpu_offset_x,
							     cpu_offset_y,
							     josephnoclip,
								 fan_factor,
								 source_x
							     );  
  }


  {
    cudaError_t cudaError;
    cudaError = cudaGetLastError();
    if( cudaError != cudaSuccess )
      {
	fprintf(stderr, "CUDA Runtime API Error 1 reported : %s\n", cudaGetErrorString(cudaError));
	exit(0);
      }
  }
 
  // retrieve the sinogram from device

  if(memisonhost) {
    CUDA_SAFE_CALL( cudaMemcpy2D( SINOGRAMS,
				  (num_bins)*sizeof(float),
				  d_SINO,
				  (dimrecx)*sizeof(float),
				  (num_bins)*sizeof(float),
				  (num_projections),
				  cudaMemcpyDeviceToHost	 
				  ) 	
		    );
  } else {
    CUDA_SAFE_CALL( cudaMemcpy2D( SINOGRAMS,
				  (num_bins)*sizeof(float),
				  d_SINO,
				  (dimrecx)*sizeof(float),
				  (num_bins)*sizeof(float),
				  (num_projections),
				  cudaMemcpyDeviceToDevice	 
				  ) 	
		    );
  }

//   printf(" %f %f %f sum %f \n",SINOGRAMS[0] , SINOGRAMS[1],SINOGRAMS[2], sum );


  //  free memory 
  CUDA_SAFE_CALL(cudaFree(d_axis_corrections));

  CUDA_SAFE_CALL(cudaFreeArray(a_Slice));

  CUDA_SAFE_CALL(cudaFree(d_SINO));


  CUDA_SAFE_CALL(cudaFree(d_beginPos));
  CUDA_SAFE_CALL(cudaFree(d_strideJoseph));
  CUDA_SAFE_CALL(cudaFree(d_strideLine));
  CUDA_SAFE_CALL(cudaFree(d_angles_per_project));


}
