
#/*##########################################################################
# 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<string.h>
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

#ifdef __SSE__
#include<emmintrin.h>
#endif

#include<semaphore.h>
#include"cpu_main.h"
#include<mpi.h>
#include<omp.h>


//#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION


#ifdef __SSE__
#define FLOAT_TO_INT(in,out)  \
    out=_mm_cvtss_si32(_mm_load_ss(&(in)));
#else
#define FLOAT_TO_INT(in,out)  \
  out = (int) in ;
#endif




#define  PEZZO 64

void   cpu_inner(   float *SLICE ,   int num_proj, int  num_bins, float *WORK_perproje , 
		    float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
		    float cpu_offset_x,  
		    float cpu_offset_y,
		    int oversampling, int X1, float dive,
		    int npj_offset, int * proj_num_list, int npjs_file) ;

void   cpu_inner_checkrange(   float *SLICE ,   int num_proj, int  num_bins, float *WORK_perproje , 
			       float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
			       float cpu_offset_x,  
			       float cpu_offset_y,
			       int oversampling, int X1, float dive,
			       int npj_offset, int * proj_num_list, int npjs_file,
			       int measurerangenotnan,
			       float *angles_per_proj) ;


void 	    pro_cpu_inner_conicity(   float *SLICE ,   int P1, int P2, int nslices_data, int  num_bins,
				      int nslices,  int num_y, int num_x, 
				      float * WORK_perproje, 
				       int Z1, int Z2, 
				      int X1, int X2, 
				      float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
				      float cpu_offset_x,  
				      float cpu_offset_y,
				      int Nfirstslice, 
				      float  source_distance, float detector_distance,
				      float	v2x , float   v2z, float v_size,
				      float	SOURCE_XA , float SOURCE_ZA) ;


int       pro_cpu_main_conicity( int num_y, int num_x,  float * SLICE, int num_proj, int num_bins, float *data , 
				 float axis_position, float * axis_position_s, float * cos_s, float *sin_s , float cpu_offset_x, float cpu_offset_y,
				 int Nfirstslice, int nslices, int data_start, int nslices_data,
				 float source_distance, float detector_distance, 
				 float v2x ,   float v2z,float voxel_size, 
				 float SOURCE_X , float SOURCE_Z , sem_t sema){
  

#define  PEZZO_proj 4
#define  PEZZO_z 8
#define  PEZZO_x 256

  int nx,nz,npro;
  
  nx = ( (int ) (num_bins*1.0/PEZZO_x+0.99999));
  nz = ( (int ) (nslices_data*1.0/PEZZO_z+0.99999));
  npro = ( (int ) (num_proj*1.0/PEZZO_proj+0.99999));
  
  {

    int  X1,X2,Z1,Z2,P1,P2;

    int ipro,ix,iz;
    float * pezzoSINO;

    pezzoSINO = (float *) malloc( PEZZO_proj*PEZZO_z*PEZZO_x *sizeof(float) );

    for( ipro=0; ipro<npro; ipro++) {
      P1= PEZZO_proj*ipro;
      P2=P1+PEZZO_proj;
      if(P2>num_proj) P2=num_proj;
      for( iz=0; iz<nz; iz++) {
	Z1= PEZZO_z*iz;
	  for( ix=0; ix<nx; ix++) {
	    X1= PEZZO_x*ix;
	    memset(pezzoSINO,0,PEZZO_proj*PEZZO_z*PEZZO_x  *sizeof(float));

	    Z2=Z1+PEZZO_z;
	    X2=X1+PEZZO_x;
	    
	    if(Z2>nslices_data) Z2=nslices_data;
	    if(X2>num_bins) X2=num_bins;


	    pro_cpu_inner_conicity(  SLICE  ,   P1, P2,
				     PEZZO_z , PEZZO_x ,
				     nslices,   num_y, num_x, 
				     pezzoSINO , 
				      Z1, Z2, X1, X2, 
				     axis_position, axis_position_s,  cos_s, sin_s , 
				     cpu_offset_x, cpu_offset_y ,
				     Nfirstslice,
				     source_distance,  detector_distance,
				     v2x ,    v2z,voxel_size,
				     SOURCE_X ,  SOURCE_Z - data_start);
	    
	    

	    sem_wait(&sema);
	    int jp, jZ, jx;
	    for(jp=P1; jp<P2; jp++) {
	      for(jZ=Z1; jZ<Z2; jZ++) {
		for(jx=X1; jx<X2; jx++) {
		  data[jZ*num_proj*num_bins+jp*num_bins+jx ] += pezzoSINO[ PEZZO_z* PEZZO_x*(jp-P1) + PEZZO_x*(jZ-Z1) + (jx-X1)];
		}
	      }
	    }
	    sem_post(&sema);
	    /* if(data_start==170 && Z1==0) { */
	    /*   FILE *f; */
	    /*   f=fopen("dat0.dat","w" ) ; */
	    /*   printf(" BLOCCO CON %d %d %d   P1 %d  P2 %d  Z1 %d  Z2 %d \n", (P2-P1),PEZZO_z, PEZZO_x , P1,P2, Z1, Z2   ) ;  */
	    /*   fwrite(pezzoSINO, (P2-P1)*PEZZO_z* PEZZO_x   ,  4  ,f) ;  */
	    /*   fclose(f); */
	    /*   exit(0); */
	    /* } */
	    
	  }
      }
    }
    free(pezzoSINO);
  }
  return 1;
}

void   cpu_inner_conicity(   float *SLICE ,   int num_proj, int nslices_data, int  num_bins, float *WORK_perproje , 
			     float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
			     float cpu_offset_x,  
			     float cpu_offset_y,
			     int Z1, int Z2,
			     float  source_distance, float detector_distance,
			     float	v2x , float   v2z,float voxel_size, 
			     float	SOURCE_X , float SOURCE_Z, int ZZ00);


int       cpu_main_conicity( int num_y, int num_x,  float * SLICE, int num_proj, int num_bins, float *WORK_perproje , 
			     float axis_position, float * axis_position_s, float * cos_s, float *sin_s , float cpu_offset_x, float cpu_offset_y,
			     int Nfirstslice, int nslices, int data_start, int nslices_data,
			     float source_distance, float detector_distance, 
			     float v2x ,   float v2z,float voxel_size, 
			     float SOURCE_X , float SOURCE_Z ){
  
  int nx,ny,nz;
  nx = ( (int ) (num_x*1.0/PEZZO+0.99999))+1;
  ny = ( (int ) (num_y*1.0/PEZZO+0.99999))+1;
  nz = ( (int ) (nslices*1.0/PEZZO+0.99999))+1;
  {
    // int tid;
    int  X1,X2,Y1,Y2,Z1,Z2;
    // int ce;
    int iz,jz,ix,iy,jx,jy;//i, pos,pos_a;
    float * pezzoSLICE;

    pezzoSLICE = (float *) malloc( PEZZO*PEZZO*PEZZO *sizeof(float) );

    for( iz=0; iz<nz; iz++) {
      Z1= PEZZO*iz;
      Z2=Z1+PEZZO;
      if(Z2>nslices) Z2=nslices;
      for( ix=0; ix<nx; ix++) {
	X1= PEZZO*ix;
	for( iy=0; iy<ny; iy++) {
	  Y1= PEZZO*iy;
	  memset(pezzoSLICE,0, PEZZO*PEZZO*PEZZO*sizeof(float));
	  // printf("chiamo cpu_inner_conicity per z da %d a %d  ix %d  iy %d\n", Nfirstslice+Z1,Nfirstslice+Z2, ix,iy);



	  printf( " iz %d iy %d  ix %d nz, ny nz %d %d %d \n" , iz,ix, iy, nz, ny ,nx) ; 

	  
	  // printf(" CHIAMO cpu_inner_conicity axis_position %20.10e  cpu_offset_x %20.10e  cpu_offset_x+ix*PEZZO %20.10e SOURCE_X  %e\n", axis_position, cpu_offset_x, cpu_offset_x+ix*PEZZO,SOURCE_X );
	  // printf(" CHIAMO cpu_inner_conicity axis_position  SOURCE_Z  %e  data_start %d\n", SOURCE_Z,  data_start );


	  
	  cpu_inner_conicity(   pezzoSLICE ,   num_proj, nslices_data,  num_bins, WORK_perproje , 
				axis_position, axis_position_s,  cos_s, sin_s , 
				cpu_offset_x+ix*PEZZO,  
				cpu_offset_y +iy*PEZZO,
				Nfirstslice+Z1,Nfirstslice+Z2,
				source_distance,  detector_distance,
				v2x ,    v2z,voxel_size,
				SOURCE_X ,  SOURCE_Z - data_start,  X1);


	  X2=X1+PEZZO;
	  Y2=Y1+PEZZO;

	  if(Y2>num_y) Y2=num_y;
	  if(X2>num_x) X2=num_x;

	  for(jz=Z1; jz<Z2; jz++) {
	    for(jy=Y1; jy<Y2; jy++) {
	      for(jx=X1; jx<X2; jx++) {
	
		SLICE[jz*num_y*num_x+jy*num_x+jx ] = pezzoSLICE[ PEZZO* PEZZO*(jz-Z1) + PEZZO*(jy-Y1) + (jx-X1)];
	      }
	    }
	  }
	}
      }
    }
    free(pezzoSLICE);
  }
  return 1;
}

void 	    pro_cpu_inner_conicity(   float *SLICE ,   int P1, int P2
				      , int nslices_data, int  num_bins,
				      int nslices,  int num_y, int num_x, 
				      float * pezzoSINO, 
				      int Z1, int Z2, 
				      int X1, int X2, 
				      float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
				      float cpu_offset_x,  
				      float cpu_offset_y,
				      int Nfirstslice, 
				      float  source_distance, float detector_distance,
				      float	v2x , float   v2z, float v_size,
				      float	SOURCE_X , float SOURCE_Z) {

  long  projection;
  float cos_angle ,sin_angle;


  float axis_position_v;    // -------------------------
  int iz,ix;



  float w00,w01,w10,w11;
  int ipx0,  ipz0 ; 
  float dx,dz;


  int ivx, ivy;
  float fvx, fvy, fvz;
  double F;

  float magnicenter =  (source_distance+detector_distance)/ (source_distance )  ; 
  axis_position_v = (axis_position-SOURCE_X)/(magnicenter*v2x) ; 
  cpu_offset_x -=  SOURCE_X+axis_position_v; 
  cpu_offset_y -=  SOURCE_X+axis_position_v; 

  for(projection=P1; projection < P2; projection++) {
    cos_angle = cos_s[projection];
    sin_angle = sin_s[projection];

    float Dnumex = (source_distance+detector_distance)*v2x;
    float Dnumez = (source_distance+detector_distance)*v2z;
    float Xcorner = (axis_position_s[projection]-SOURCE_X)/(magnicenter*v2x) + (   cpu_offset_x *cos_angle    -     cpu_offset_y * sin_angle ) ; 
    float Ycorner =  axis_position_v+( cpu_offset_x)* sin_angle + ( cpu_offset_y )* cos_angle;
    float Ddeno0 = source_distance +     ( Ycorner  ) *1.0e-6*v_size;
    float Ddeno ; 
    float res = 0 ;  
    res=0.0f;
    float Area;
    float sum=0.0f;
    // int ipos;
    for( iz = Z1 ; iz <Z2; iz++) { 
      for( ix = X1  ; ix <   X2  ; ix++) { 
	res=0.0;
	if(fabs(sin_angle)>fabs(cos_angle)) {
	  if(1) {
	    Area = 1.0/ fabs(sin_angle);
	    for(ivx=0; ivx<num_x; ivx++) {
	      Ddeno = Ddeno0    +   ivx*sin_angle * v_size *1.0e-6;       ; 
	      F  = (sin_angle-(SOURCE_X-ix)*cos_angle*v_size*1.0e-6/Dnumex); 
	      fvy   =( ( Xcorner+ivx*cos_angle ) + (SOURCE_X-ix)*(Ddeno)/Dnumex   );
	      fvy = fvy / F ;
	      Ddeno +=   fvy*cos_angle * v_size *1.0e-6;      
	      fvz  = (iz-SOURCE_Z)*Ddeno    / Dnumez  -Nfirstslice; 

/* axis_position_v = (axis_position-SOURCE_X)/(magnicenter*v2x) ;  */
/* cpu_offset_x -=  SOURCE_X- axis_position_v;  */
/* cpu_offset_y -=  SOURCE_X- axis_position_v;  */
/* Xcorner = (axis_position_s[projection]-SOURCE_X)/(magnicenter*v2x) +( cpu_offset_x )* cos_angle - ( cpu_offset_y )* sin_angle;   */
/* Ycorner = axis_position_v+( cpu_offset_x)* sin_angle + ( cpu_offset_y )* cos_angle;   */
/*   (px-SOURCE_X) /(source_distance+detector_distance)*(source_distance +  (Ycorner  +ix*sin_angle )* v_size*1.0e-6 )-(Xcorner+(ix)*cos_angle)*v2x */
/*  =  */
/*   - iy*( sin_angle *v2x   +cos_angle* (px-SOURCE_X) /(source_distance+detector_distance) * v_size*1.0e-6 )  */
/*   (px-SOURCE_X) /(source_distance+detector_distance)*(source_distance +  (Ycorner  + iy*cos_angle)* v_size*1.0e-6 )-(Xcorner- iy*sin_angle)*v2x */
/*  =  */
/*   + ix*( cos_angle *v2x   -sin_angle* (px-SOURCE_X) /(source_distance+detector_distance) * v_size*1.0e-6 )  */

	      if( fvz>-1 && fvz< nslices && fvy>0 && fvy< num_y-1  ) {
		// printf(" %e %e   %e  %e \n" ,  fvy, fvz     );
		fvy = fvy  - 0.499999f; 
		fvz = fvz  - 0.499999f; 
		
		FLOAT_TO_INT(fvy,ipx0);
		FLOAT_TO_INT(fvz,ipz0);
		
		dx = fvy-ipx0 + 0.499999f; 
		dz = fvz-ipz0 + 0.499999f; 

		w00  = SLICE[  (    (ipz0+0)*num_y  +(ipx0+0)  ) *num_x +ivx    ];
		w01  = SLICE[  (    (ipz0+0)*num_y  +(ipx0+1)  ) *num_x +ivx    ];
		w10  = SLICE[  (    (ipz0+1)*num_y  +(ipx0+0)  ) *num_x +ivx    ];
		w11  = SLICE[  (    (ipz0+1)*num_y  +(ipx0+1)  ) *num_x +ivx    ];
		res  +=  ( (1.0f-dz)*(   w00 *(1.0f-dx)   + w01*dx    )+dz*( w10*(1.0f-dx)    + w11 *dx      ) )  ; 
	      }
	    }
	  }
	} else {
	  Area = 1.0/ fabs(cos_angle);
	  for(ivy=0; ivy<num_y; ivy++) {
	    Ddeno = Ddeno0    +   ivy*cos_angle * v_size *1.0e-6;      
	    F  = (cos_angle+(SOURCE_X-ix)*sin_angle*v_size*1.0e-6/Dnumex); 
	    fvx   =( -( Xcorner-ivy*sin_angle ) + (ix-SOURCE_X)*(Ddeno)/Dnumex   );
	    fvx = fvx / F ;
	    Ddeno +=   fvx*sin_angle * v_size *1.0e-6;      
	    fvz  = (iz-SOURCE_Z)*Ddeno    / Dnumez -Nfirstslice; 

	    if( fvz>-1 && fvz< nslices && fvx>0 && fvx< num_x-1  ) {
	      
	      fvx = fvx  - 0.499999f; 
	      fvz = fvz  - 0.499999f; 
	      
	      FLOAT_TO_INT(fvx,ipx0);
	      FLOAT_TO_INT(fvz,ipz0);
	      
	      dx = fvx-ipx0 + 0.499999f; 
	      dz = fvz-ipz0 + 0.499999f; 
	      
	      w00  = SLICE[  (    (ipz0+0)*num_y   +ivy  ) *num_x  +(ipx0+0)  ];
	      w01  = SLICE[  (    (ipz0+0)*num_y   +ivy  ) *num_x  +(ipx0+1)  ];
	      w10  = SLICE[  (    (ipz0+1)*num_y   +ivy  ) *num_x  +(ipx0+0)  ];
	      w11  = SLICE[  (    (ipz0+1)*num_y   +ivy  ) *num_x  +(ipx0+1)  ];	      	      
	      res  +=  ( (1.0f-dz)*(   w00 *(1.0f-dx)   + w01*dx    )+dz*( w10*(1.0f-dx)    + w11 *dx      ) )  ; 
	    }
	  }
	}
	pezzoSINO[ ((projection-P1)*nslices_data +(iz-Z1) )*num_bins +(ix-X1)] += res*Area;  	

	sum  +=  res*Area  ;  
      }
    }
  }	
}

#define FLOAT float
void   cpu_inner_conicity(   float *SLICE ,   int num_proj, int nslices_data, int  num_bins, float *WORK_perproje , 
			     float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
			     float cpu_offset_x,  
			     float cpu_offset_y,
			     int Z1, int Z2,
			     float  source_distance, float detector_distance,
			     float	v2x , float   v2z, float v_size,
			     float	SOURCE_XA , float SOURCE_ZA, int XX00) {

  long  projection;
  float cos_angle ,sin_angle;
  FLOAT VX0, VY0, vy,vz;
  float vx;
  float axis_position_v;    // -------------------------
  // float source_x = 0, source_z=0; // ----------------------------------------
  FLOAT	SOURCE_X = SOURCE_XA ;
  FLOAT SOURCE_Z = SOURCE_ZA ;
  int iz,iy,ix;

  float px,pz;

  float *ww ;
  float w00,w01,w10,w11;
  int ipx0,  ipz0 ; 
  float dx,dz;
  FLOAT magni  ; 


  float magnicenter =  (source_distance+detector_distance)/ (source_distance )  ; 
  axis_position_v = (axis_position-SOURCE_X)/(magnicenter*v2x) ; 
  cpu_offset_x -=  SOURCE_X; 
  cpu_offset_y -=  SOURCE_X; 

  for(projection=0; projection < num_proj; projection++) {
    
    // if(projection%1000 ==0 ) printf(" projection %d   \n" , projection ) ; 


    
    ww =  WORK_perproje  +(projection*nslices_data/**3+1*/  )*num_bins ; 

    cos_angle = cos_s[projection];
    sin_angle = sin_s[projection];

    VX0 = (axis_position_s[projection]-SOURCE_X)/(magnicenter*v2x)   ;
    VX0+=+( cpu_offset_x - axis_position_v )* cos_angle ;
    VX0+=- ( cpu_offset_y - axis_position_v )* sin_angle;  
    
    VY0 = axis_position_v+( cpu_offset_x - axis_position_v )* sin_angle ;
    VY0+= ( cpu_offset_y - axis_position_v )* cos_angle;  
    
    int vpos = 0;
    for( iz = Z1 ; iz <Z2; iz++) { 
      
      vz = iz ; 
      vpos = (iz-Z1)*PEZZO*PEZZO; 
      
      for( iy = 0  ; iy <   PEZZO  ; iy++) { 
	vx = VX0 - iy*sin_angle ;
	vy = VY0 + iy*cos_angle  ;
	
	for( ix = 0  ; ix <   PEZZO  ; ix++) { 

	  magni =  (source_distance+detector_distance)/ (source_distance +  (vy+ix*sin_angle )* v_size*1.0e-6 )  ; 
	  
	  px = SOURCE_X - 0.499999f +  magni*(vx+(ix)*cos_angle) *v2x ;
	  pz = SOURCE_Z - 0.499999f +  magni*vz *v2z  ;
	  /* if(projection==0 && iy==0 && ix==0) { */

	  /*   printf( " pz %e  \n" ,  pz + 0.499999f ) ; */
	  /*   float magni = (source_distance+detector_distance)/ (source_distance  )  ;   */
	  /*   float pz2 = SOURCE_Z - 0.49999f +  magni*vz *v2z  ; */
	  /*   printf( " pz2 %e  \n" ,  pz2 + 0.499999f ) ; */

	  /* } */

	  FLOAT_TO_INT(px,ipx0);
	  FLOAT_TO_INT(pz,ipz0);

	  dx = px-ipx0 + 0.499999f; 
	  dz = pz-ipz0 + 0.499999f; 
	  
	  w00  = ww[  ipz0    *(/*3**/num_bins) +ipx0    ];
	  w01  = ww[  ipz0    *(/*3**/num_bins) +ipx0+1    ];
	  w10  = ww[  (ipz0+1)*(/*3**/num_bins) +ipx0    ];
	  w11  = ww[  (ipz0+1)*(/*3**/num_bins) +ipx0+1    ];
	  
	  SLICE[vpos  ] +=  ( (1.0f-dz)*(   w00 *(1.0f-dx)   + w01*dx    )+dz*( w10*(1.0f-dx)    + w11 *dx      ) )  ; 
	  vpos++;
	}
      }
    }
  }
  
}





int cpu_main(int num_y, int num_x,  float * SLICE, int num_proj, int num_bins, float *WORK_perproje , 
	     float axis_position, float * axis_position_s, float * cos_s, float *sin_s , float cpu_offset_x, float cpu_offset_y,
	     int *minX, int*maxX,
	     int oversampling, int ncpus, float dive,
	     int npj_offset, int * proj_num_list, int npjs_file,
	     int is_helicoidal, int measurerangenotnan,
	     float *angles_per_proj // da usarsi con (is_helicoidal)&&(measurerangenotnan)
	     )
{
  
  // int dimx, dimy;
  int nx,ny;

  nx = ( (int ) (num_x/PEZZO+0.99999))+1;
  ny = ( (int ) (num_y/PEZZO+0.99999))+1;
  // dimx=PEZZO*nx;
  // dimy=PEZZO*ny;

// #pragma	omp parallel num_threads (ncpus)  
  {
    int tid;
    int  X1,X2,Y1,Y2;
    // int ce;
    int ix,iy,jx,jy,i, pos;
    float * pezzoSLICE;
    tid = omp_get_thread_num();

    pezzoSLICE = (float *) malloc( PEZZO*PEZZO *sizeof(float) );
    int count=0; 

    for( ix=0; ix<nx; ix++) {
      X1= PEZZO*ix;
      X2=X1+PEZZO;
      if(X2>num_x) X2=num_x;
      for( iy=0; iy<ny; iy++) {
	Y1= PEZZO*iy;
	Y2=Y1+PEZZO;
	if(Y2>num_y) Y2=num_y;
	// ce=0;
	for(i=Y1; i<Y2; i++) {
	  if( minX[i]<X2 && maxX[i]>X1) {
	    // ce=1;
	    break;
	  }
	}
	// printf(" %d %d %d \n", ix,iy, ce ); 
	/* if(ce==0) continue;*/ 
	
	count+=1;
	if(count%ncpus==tid) {
	  memset(pezzoSLICE,0, PEZZO*PEZZO*sizeof(float));


	  // is_helicoidal, int measurerangenotnan,
	  //  float *angles_per_proj // da usarsi con (is_helicoidal)&&(measurerangenotnan)
		
	  if(!is_helicoidal) {
	    cpu_inner(   pezzoSLICE ,   num_proj,  num_bins, WORK_perproje , 
			 axis_position, axis_position_s,  cos_s, sin_s , 
			 cpu_offset_x+ix*PEZZO,  
			 cpu_offset_y   + iy * PEZZO ,
			 oversampling, X1, dive, npj_offset, proj_num_list,  npjs_file);
	  } else {
	    cpu_inner_checkrange(   pezzoSLICE ,   num_proj,  num_bins, WORK_perproje , 
				    axis_position, axis_position_s,  cos_s, sin_s , 
				    cpu_offset_x+ix*PEZZO,  
				    cpu_offset_y   + iy * PEZZO ,
				    oversampling, X1, dive, npj_offset, proj_num_list,  npjs_file,
				    measurerangenotnan,angles_per_proj);
	  }

	  for(jy=Y1; jy<Y2; jy++) {
	    pos= jy*num_x+X1;
	    for(jx=X1; jx<X2; jx++) {
	      SLICE[ pos] = pezzoSLICE[ PEZZO*(jy-Y1) + (jx-X1)];
	      pos++;
	    }
	  }
	}
      }
    }
    free(pezzoSLICE);
  }
  return 1;
}



void   cpu_inner(   float *SLICE ,   int num_proj, int  num_bins, float *WORK_perproje , 
		    float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
		    float cpu_offset_x,  
		    float cpu_offset_y,
		    int oversampling, int X1, float dive,
		    int npj_offset, int * proj_num_list, int npjs_file) {
  float  DY;
  float *OVERSAMPLE;
  int j;
  float cos_angle ,sin_angle;
  float increment_position, increment_position_2, increment_position_3;
  float increment_position_4, increment_position_5, increment_position_6;
  float increment_position_7, increment_position_8;
  float slice_positionA, slice_position;
  float axis_position_corr ;  
  int y=0;
  // int x_start, x_end; // ,num_xelem ;
  long start_address;
  long  projection, address;
  long  bin,bin1,bin2,bin3;
  long  bin4,bin5,bin6,bin7;
  float  fbin ,fbin1,fbin2,fbin3;
  float  fbin4,fbin5,fbin6,fbin7;
  if( dive!=0.0f) {
    fprintf(stderr, "ERROR :  the option CONICITY_FAN is implemented only for GPUs  ");
    fprintf(stdout, "ERROR :  the option CONICITY_FAN is implemented only for GPUs  ");
    exit(1);
  }
  OVERSAMPLE = WORK_perproje + oversampling*num_bins;
  for(projection=0; projection < num_proj; projection++) {
    /* printf(" pro %ld \n", projection); */
    int npj_absolute = proj_num_list[projection ] + npj_offset ;
    if(npj_absolute<0 || npj_absolute >=  npjs_file) {      
      // printf( " npj_absolute %d  npjs_file %d  projection %d  num_proj  %d proj_num_list %d\n",
	//      npj_absolute,  npjs_file, projection,  num_proj,  proj_num_list  ) ; 
      continue;
    }
    
  

    cos_angle = cos_s[npj_absolute];
    sin_angle = sin_s[npj_absolute];

    increment_position = (float)( cos_angle * (oversampling) );
    increment_position_2 =(float) ( increment_position*2);
    increment_position_3 =(float) ( increment_position*3);
    increment_position_4 = (float)( increment_position*4);
    increment_position_5 = (float)( increment_position*5);
    increment_position_6 = (float)( increment_position*6);
    increment_position_7 = (float)( increment_position*7);
    increment_position_8 = (float)( increment_position*8);
    	
    axis_position_corr = axis_position_s[npj_absolute] ; 
    slice_positionA =
      ( (float) ( ( (oversampling) * ( axis_position_corr + 
				       (     cpu_offset_x     - axis_position ) * cos_angle - 
				       (     cpu_offset_y     - axis_position ) * sin_angle  ) 
		    )));
    DY = -oversampling *sin_angle;
      
      start_address = 0;      
      for(y   = 0  ; y < PEZZO  ; y++) {

	address = start_address ; 
	j=0;
	
	slice_position=slice_positionA;

	for(; j < PEZZO - 7; j += 8) {
	  
	  fbin = slice_position;
	  fbin1 = slice_position+ increment_position;
	  fbin2 = slice_position+ increment_position_2;
	  fbin3 = slice_position+ increment_position_3;

	  FLOAT_TO_INT(fbin,bin);
	  FLOAT_TO_INT(fbin1,bin1);
	  FLOAT_TO_INT(fbin2,bin2);
	  FLOAT_TO_INT(fbin3,bin3);
	  
	  SLICE[address  ] = SLICE[address  ] + OVERSAMPLE[bin]; 
	  SLICE[address+1] = SLICE[address+1] + OVERSAMPLE[bin1]; 
	  SLICE[address+2] = SLICE[address+2] + OVERSAMPLE[bin2]; 
	  SLICE[address+3] = SLICE[address+3] + OVERSAMPLE[bin3];
	  
	  fbin4 = slice_position+ increment_position_4;
	  fbin5 = slice_position+ increment_position_5;
	  fbin6 = slice_position+ increment_position_6;
	  fbin7 = slice_position+ increment_position_7;
	  
	  FLOAT_TO_INT(fbin4,bin4);
	  FLOAT_TO_INT(fbin5,bin5);
	  FLOAT_TO_INT(fbin6,bin6);
	  FLOAT_TO_INT(fbin7,bin7);
	  
	  SLICE[address+4  ] = SLICE[address+4] + OVERSAMPLE[bin4]; 
	  SLICE[address+5  ] = SLICE[address+5] + OVERSAMPLE[bin5]; 
	  SLICE[address+6  ] = SLICE[address+6] + OVERSAMPLE[bin6]; 
	  SLICE[address+7  ] = SLICE[address+7] + OVERSAMPLE[bin7];
	  
	  slice_position = slice_position + increment_position_8;
	  address += 8;
	}      
	slice_positionA=slice_positionA+DY;
	start_address=start_address+PEZZO;
      }
      OVERSAMPLE += 3*oversampling*num_bins;
  }
  
  
  
}












void   cpu_inner_checkrange(   float *SLICE ,   int num_proj, int  num_bins, float *WORK_perproje , 
			       float axis_position, float *axis_position_s,  float *cos_s, float *sin_s , 
			       float cpu_offset_x,  
			       float cpu_offset_y,
			       int oversampling, int X1, float dive,
			       int npj_offset, int * proj_num_list, int npjs_file,
			       int measurerangenotnan,
			       float *angles_per_proj) {
  float  DY;
  float *OVERSAMPLE;
  int j;
  float cos_angle ,sin_angle;
  float increment_position, increment_position_2, increment_position_3;
  float increment_position_4, increment_position_5, increment_position_6;
  float increment_position_7, increment_position_8;
  float slice_positionA, slice_position;
  float axis_position_corr ;  
  int y=0;
  // int x_start, x_end; // ,num_xelem ;
  long start_address;
  long  projection, address;
  long  bin,bin1,bin2,bin3;
  long  bin4,bin5,bin6,bin7;
  float  fbin ,fbin1,fbin2,fbin3;
  float  fbin4,fbin5,fbin6,fbin7;
  if( dive!=0.0f) {
    fprintf(stderr, "ERROR :  the option CONICITY_FAN is implemented only for GPUs  ");
    fprintf(stdout, "ERROR :  the option CONICITY_FAN is implemented only for GPUs  ");
    exit(1);
  }
  OVERSAMPLE = WORK_perproje + oversampling*num_bins;
  
  
  int startpro=-1;
  int endpro   =-1;
  
  float startangle=-1;
  int endpro_angle =-1;
  int npis=0;
  float fattore_estremi=1;

  for(projection=0; projection < num_proj; projection++) {
    
    int npj_absolute = proj_num_list[projection ] + npj_offset ;
    
    int isgood=0;
    if( npj_absolute<0 || npj_absolute >=  npjs_file) {

      //printf( "  NOGOOD npj_absolute %d  npjs_file %d  projection %d  num_proj  %d proj_num_list %d\n",
	//      npj_absolute,  npjs_file, projection,  num_proj,  proj_num_list[projection ]  ) ; 


      isgood=0;
    } else {
      cos_angle = cos_s[npj_absolute];
      sin_angle = sin_s[npj_absolute];
      increment_position = (float)( cos_angle * (oversampling) );
      axis_position_corr = axis_position_s[npj_absolute] ; 
      slice_positionA =
	( (float) ( ( (oversampling) * ( axis_position_corr + 
					 (     cpu_offset_x     - axis_position ) * cos_angle - 
					 (     cpu_offset_y     - axis_position ) * sin_angle  ) 
		      )));
      start_address = 0;      
      address = start_address ; 
      j=0;
      slice_position=slice_positionA;
      fbin = slice_position;
      FLOAT_TO_INT(fbin,bin);   
      // printf(" npj_absolute %d bin %d OVERSAMPLE[bin] %e\n", npj_absolute,  bin, OVERSAMPLE[bin] );
      if( OVERSAMPLE[bin]!= OVERSAMPLE[bin]) {
	isgood=0;
      } else {
	isgood=1;
      }
    }
    if(isgood) {
      if(startpro == -1) {
	startpro = projection; 
	if(measurerangenotnan) {
	  startangle = angles_per_proj[npj_absolute] ; 
	}
      } else {
	if(measurerangenotnan) {
	  if( fabs(-startangle + 2*angles_per_proj[npj_absolute]-angles_per_proj[npj_absolute-1]   ) > (npis+1.00001)*M_PI) {
	    if( fabs(fabs(-startangle + angles_per_proj[npj_absolute]) - (npis+1)*M_PI )<1.0e-6 *M_PI  ) {
	      fattore_estremi=0.5;
	    } else {
	      fattore_estremi=3.0/4;
	    }
	    endpro_angle = projection+1;
	    npis++;
	  }
	}
      }
    } else {
      if(startpro != -1 && endpro == -1) endpro = projection; 
    }
    /* { */
    /*   float sum=0; */
    /*   int k; */
    /*   for(k=0;k<num_bins*oversampling; k++) { */
    /* 	sum+=OVERSAMPLE[k] ;  */
    /*   } */
    /*   printf("%d SUM %e  is nana %d\n" , projection, sum, sum!=sum); */
    /* } */
    OVERSAMPLE += 3*oversampling*num_bins;
  }
  if(measurerangenotnan) {
    endpro =  endpro_angle  ;
  }
  
  int inizio=0 , fine=0;
  if(measurerangenotnan) {
    inizio = startpro+1;
    fine   = endpro  -1;
  } else {
    inizio = startpro;
    fine   = endpro  ;    
  }
  
  OVERSAMPLE = WORK_perproje + oversampling*num_bins +inizio*3*oversampling*num_bins;

  // printf("INIZIO FINE %d %d %d\n", inizio, fine, measurerangenotnan );

  for(projection=inizio; projection < fine; projection++) {
    // printf(" pro %ld \n", projection); 
    int npj_absolute = proj_num_list[projection ] + npj_offset ;
    if(npj_absolute<0 || npj_absolute >=  npjs_file) {


      
      //printf( " npj_absolute %d  npjs_file %d  projection %d  num_proj  %d  fine %d   proj_num_list[projection ] %d\n",
	//      npj_absolute,  npjs_file, projection,  num_proj,  fine,  proj_num_list[projection ]  ) ; 




      continue;
    }
    
    cos_angle = cos_s[npj_absolute];
    sin_angle = sin_s[npj_absolute];
    
    increment_position = (float)( cos_angle * (oversampling) );
    increment_position_2 =(float) ( increment_position*2);
    increment_position_3 =(float) ( increment_position*3);
    increment_position_4 = (float)( increment_position*4);
    increment_position_5 = (float)( increment_position*5);
    increment_position_6 = (float)( increment_position*6);
    increment_position_7 = (float)( increment_position*7);
    increment_position_8 = (float)( increment_position*8);
    	
    axis_position_corr = axis_position_s[npj_absolute] ; 
    slice_positionA =
      ( (float) ( ( (oversampling) * ( axis_position_corr + 
				       (     cpu_offset_x     - axis_position ) * cos_angle - 
				       (     cpu_offset_y     - axis_position ) * sin_angle  ) 
		    )));
    DY = -oversampling *sin_angle;
      
      start_address = 0;      
      for(y   = 0  ; y < PEZZO  ; y++) {

	address = start_address ; 
	j=0;
	
	slice_position=slice_positionA;

	for(; j < PEZZO - 7; j += 8) {
	  
	  fbin = slice_position;
	  fbin1 = slice_position+ increment_position;
	  fbin2 = slice_position+ increment_position_2;
	  fbin3 = slice_position+ increment_position_3;

	  FLOAT_TO_INT(fbin,bin);
	  FLOAT_TO_INT(fbin1,bin1);
	  FLOAT_TO_INT(fbin2,bin2);
	  FLOAT_TO_INT(fbin3,bin3);
	  
	  SLICE[address  ] = SLICE[address  ] + OVERSAMPLE[bin]; 
	  SLICE[address+1] = SLICE[address+1] + OVERSAMPLE[bin1]; 
	  SLICE[address+2] = SLICE[address+2] + OVERSAMPLE[bin2]; 
	  SLICE[address+3] = SLICE[address+3] + OVERSAMPLE[bin3];
	  
	  fbin4 = slice_position+ increment_position_4;
	  fbin5 = slice_position+ increment_position_5;
	  fbin6 = slice_position+ increment_position_6;
	  fbin7 = slice_position+ increment_position_7;
	  
	  FLOAT_TO_INT(fbin4,bin4);
	  FLOAT_TO_INT(fbin5,bin5);
	  FLOAT_TO_INT(fbin6,bin6);
	  FLOAT_TO_INT(fbin7,bin7);
	  
	  SLICE[address+4  ] = SLICE[address+4] + OVERSAMPLE[bin4]; 
	  SLICE[address+5  ] = SLICE[address+5] + OVERSAMPLE[bin5]; 
	  SLICE[address+6  ] = SLICE[address+6] + OVERSAMPLE[bin6]; 
	  SLICE[address+7  ] = SLICE[address+7] + OVERSAMPLE[bin7];
	  
	  slice_position = slice_position + increment_position_8;
	  address += 8;
	}      
	slice_positionA=slice_positionA+DY;
	start_address=start_address+PEZZO;
      }
      OVERSAMPLE += 3*oversampling*num_bins;
  }
  

  if(measurerangenotnan) {
    

    for(projection=startpro; projection < endpro; projection+= (endpro-1)-startpro) {

      OVERSAMPLE = WORK_perproje + oversampling*num_bins +projection*3*oversampling*num_bins;

      /* printf(" pro %ld \n", projection); */
      int npj_absolute = proj_num_list[projection ] + npj_offset ;
      if(npj_absolute<0 || npj_absolute >=  npjs_file) {
	continue;
      }
      
      cos_angle = cos_s[npj_absolute];
      sin_angle = sin_s[npj_absolute];
      
      increment_position = (float)( cos_angle * (oversampling) );
      increment_position_2 =(float) ( increment_position*2);
      increment_position_3 =(float) ( increment_position*3);
      increment_position_4 = (float)( increment_position*4);
      increment_position_5 = (float)( increment_position*5);
      increment_position_6 = (float)( increment_position*6);
      increment_position_7 = (float)( increment_position*7);
      increment_position_8 = (float)( increment_position*8);
      
      axis_position_corr = axis_position_s[npj_absolute] ; 
      slice_positionA =
	( (float) ( ( (oversampling) * ( axis_position_corr + 
					 (     cpu_offset_x     - axis_position ) * cos_angle - 
					 (     cpu_offset_y     - axis_position ) * sin_angle  ) 
		      )));
      DY = -oversampling *sin_angle;
      
      start_address = 0;      
      for(y   = 0  ; y < PEZZO  ; y++) {
	
	address = start_address ; 
	j=0;
	
	slice_position=slice_positionA;
	
	for(; j < PEZZO - 7; j += 8) {
	  
	  fbin = slice_position;
	  fbin1 = slice_position+ increment_position;
	  fbin2 = slice_position+ increment_position_2;
	  fbin3 = slice_position+ increment_position_3;
	  
	  FLOAT_TO_INT(fbin,bin);
	  FLOAT_TO_INT(fbin1,bin1);
	  FLOAT_TO_INT(fbin2,bin2);
	  FLOAT_TO_INT(fbin3,bin3);
	  
	  SLICE[address  ] = SLICE[address  ] + OVERSAMPLE[bin] *fattore_estremi; 
	  SLICE[address+1] = SLICE[address+1] + OVERSAMPLE[bin1]*fattore_estremi; 
	  SLICE[address+2] = SLICE[address+2] + OVERSAMPLE[bin2]*fattore_estremi; 
	  SLICE[address+3] = SLICE[address+3] + OVERSAMPLE[bin3]*fattore_estremi;
	  
	  fbin4 = slice_position+ increment_position_4;
	  fbin5 = slice_position+ increment_position_5;
	  fbin6 = slice_position+ increment_position_6;
	  fbin7 = slice_position+ increment_position_7;
	  
	  FLOAT_TO_INT(fbin4,bin4);
	  FLOAT_TO_INT(fbin5,bin5);
	  FLOAT_TO_INT(fbin6,bin6);
	  FLOAT_TO_INT(fbin7,bin7);
	  
	  SLICE[address+4  ] = SLICE[address+4] + OVERSAMPLE[bin4]*fattore_estremi; 
	  SLICE[address+5  ] = SLICE[address+5] + OVERSAMPLE[bin5]*fattore_estremi; 
	  SLICE[address+6  ] = SLICE[address+6] + OVERSAMPLE[bin6]*fattore_estremi; 
	  SLICE[address+7  ] = SLICE[address+7] + OVERSAMPLE[bin7]*fattore_estremi;
	  
	  slice_position = slice_position + increment_position_8;
	  address += 8;
	}      
	slice_positionA=slice_positionA+DY;
	start_address=start_address+PEZZO;
      }
    }
    
    start_address = 0;      
    for(y   = 0  ; y < PEZZO  ; y++) {
      
      address = start_address ; 
      for(; j < PEZZO - 7; j += 8) {
	
	
	SLICE[address  ] = SLICE[address  ]   /npis; 
	SLICE[address+1] = SLICE[address+1]   /npis; 
	SLICE[address+2] = SLICE[address+2]   /npis; 
	SLICE[address+3] = SLICE[address+3]   /npis;

	SLICE[address+4  ] = SLICE[address+4] /npis; 
	SLICE[address+5  ] = SLICE[address+5] /npis; 
	SLICE[address+6  ] = SLICE[address+6] /npis; 
	SLICE[address+7  ] = SLICE[address+7] /npis;
	
	
	address += 8;
      }      
      start_address=start_address+PEZZO;
    }
  }
  
}


