
#/*##########################################################################
# 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.
#############################################################################*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include<libgen.h>
#include<string.h>
#include"CCspace.h"
#include"edftools.h"
#include<mpi.h>
#include<time.h>
#include<mpi.h>
#include<omp.h>
#include <fftw3.h>
#include<complex.h>

#include"cpu_main.h"
#include<time.h>

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

#include <dirent.h> 


#include <sys/types.h>
#include <unistd.h>

#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>

#include"edftools.h"


#undef NDEBUG
#include<assert.h>
#undef I

#define max(a,b) ( (a)>(b)? (a):(b) )
#define min(a,b) ( (a)<(b)? (a):(b) )

#define myread( a,b,c  ) {int res=read(a,b,c); if(res<-1) printf(" gcc casse les pieds\n");}
#define mywrite( a,b,c  ) {int res=write(a,b,c); if(res<-1) printf(" gcc casse les pieds\n");}

//-------------
#define NO_DATA_WEIGHT_MASK
//-------------

#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



// inline int omp_get_thread_num() { return 0; }
inline void   omp_set_nested(int i) {};

void qdplot(float* arr, int L) {

  FILE* fid = fopen("tmp_plot.dat","wb");
  if (fid == NULL) return;
  fwrite(arr,L*sizeof(float),1,fid);
  fclose(fid);
  int res=system("./plotbinary.sh tmp_plot.dat");
  printf("%d\n", res);

}

int nextpow2(int v) {
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  return v;
}
int nextpow2_padded(int v) {
  // REVERTED TO OLD PADDING
  int vold=v;
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  if(v<vold*1.5) v*=2;
  return vold;
  return v;
}

int roundfft(const long int N) ;

void * getLibNameHandle(       const char *dirname,   const char *prefix) {
  DIR *d;
  struct dirent *dir;
  void *lib_handle;
  
  d = opendir(dirname);
  char fname[2000];
  char fname2[4000];
  if (d) {
    while ((dir = readdir(d)) != NULL) {
      printf(" spulciando %s , %s \n",dir->d_name , prefix);
      if(     strstr(  dir->d_name, prefix  )!=NULL     ) {
	if(  strncmp(   dir->d_name, prefix,   strlen(  prefix)     )==0      ) {
	  // closedir(d);
	  memcpy( fname , dir->d_name, strlen(  dir->d_name)+1  );
	  sprintf(fname2,"%s/%s"   ,   dirname , fname );
	  lib_handle = dlopen(fname2, RTLD_LAZY);
	  return lib_handle;
	}
      }
    }
    closedir(d);
  }
  return NULL;
}



/**
 * @brief gaussian_2d : compute a two dimensional (square) Gaussian kernel
 * @param ksize : size : number of rows and columns of the kernel
 * @param sigma : standard deviation
 */
float* gaussian_2d(int ksize, float sigma) {
  float* kernel = (float*) calloc(ksize*ksize,sizeof(float));
  int hksize = (ksize-1)/2;
  float sigma2 = sigma*sigma;
  float x2, y2;
  float sum = 0.0f;
  int i,j;
  for (i=0; i<ksize; i++) {
    for (j=0; j<ksize; j++) {
      x2 = (float) ((i-hksize)*(i-hksize));
      y2 = (float) ((j-hksize)*(j-hksize));
      kernel[i*ksize+j] = expf(-(x2+y2)/(2*sigma2));
      sum += kernel[i*ksize+j];
    }
  }
  for (i=0; i< ksize*ksize; i++) kernel[i] /= sum;
  return kernel;
}

/**
 * @brief (OBSOLETE) gaussian_second_derivative_2d : compute the second derivative of a 2D gaussian
 * @param ksize : kernel size
 * @param sigma : standard deviation
 * @param axis : derivative with respect to the dimension "axis"
 */
float* gaussian_second_derivative_2d(int ksize, float sigma, int axis) {
  float* kernel = (float*) calloc(ksize*ksize,sizeof(float));
  int hksize = (ksize-1)/2;
  float sigma2 = sigma*sigma;
  float one_sigma2 = 1.0f/sigma2;
  float x2, y2;
  float* d2 = ((axis == 0) ? &x2 : &y2);
  int i,j;
  for (i=0; i<ksize; i++) {
    for (j=0; j<ksize; j++) {
      x2 = (float) ((i-hksize)*(i-hksize));
      y2 = (float) ((j-hksize)*(j-hksize));
      kernel[i*ksize+j] = one_sigma2 * ((*d2)*one_sigma2 -1) * expf(-(x2+y2)/(2*sigma2));
    }
  }
  return kernel;
}
/**
 * @brief gaussian_second_derivative_2d : compute the second derivative of a 1D gaussian
 * @param ksize : kernel size
 * @param sigma : standard deviation
 */
float* gaussian_second_derivative_1d(int ksize, float sigma) {
  float* kernel = (float*) calloc(ksize,sizeof(float));
  int hksize = (ksize-1)/2;
  float one_sigma2 = 1.0f/(sigma*sigma);
  float x2;
  int i;
  for (i=0; i<ksize; i++) {
    x2 = (float) ((i-hksize)*(i-hksize));
    kernel[i] = one_sigma2 * (x2*one_sigma2 -1) * expf(-0.5*x2*one_sigma2);
  }
  return kernel;
}

/**
 * @brief convolve_1d : in-place one dimensional convolution of an image with a kernel
 * @param image : image to be convolved with a kernel
 * @param nrows : number of rows of the image
 * @param ncols : number of columns of the image
 * @param kernel : convolution kernel
 * @param ksize : length of the convolution kernel
 * @param axis : if axis == 0 : vertical convolution (i.e convolution along rows) ; horizontal convol. (i.e convol. along columns) otherwise
 */

void convolve_1d(float* image, int nrows, int ncols, float* kernel, int ksize, int axis) {
  int rkern = (ksize-1)/2;
  int i,j,k,L;
  float* line_tmp;
  if (axis == 1) {//horizontal convolutions
    L = ncols;
    line_tmp = (float*) malloc(L*sizeof(float));
    for (k=0; k < nrows; k++) {
      memset(line_tmp,0,L*sizeof(float));
      for(i = 0; i <  L; i++) {
        for(j = max(0, i-rkern); j <= min(L-1, i+rkern); j++) {
          line_tmp[i] += kernel[rkern + i-j] * image[k*ncols+j];
        }
      }
      for (i=0; i< L; i++) {
        image[k*ncols+i] = line_tmp[i];
      }
    }
  }
  else {//vertical convolutions
    L = nrows;
    line_tmp = (float*) malloc(L*sizeof(float));
    for (k=0; k < ncols; k++) {
      memset(line_tmp, 0, L*sizeof(float));
      for(i = 0; i <  L; i++) {
//        line_tmp[i] = 0;
        for(j = max(0, i-rkern); j <= min(L-1, i+rkern); j++) {
          line_tmp[i] += kernel[rkern + i-j] * image[j*ncols+k];
        }
      }
      for (i=0; i< L; i++) {
        image[i*ncols+k] = line_tmp[i];
      }
    }
  }
  free(line_tmp);
}


/**
 * @brief LoG_kernel : in-place Laplacian of Gaussian convolution of an image, using the separable convolution formula
 *   (I * g_xx)*g(y) + (I * g_yy)*g(x)
 * @param image : image to be convolved
 * @param nrows : number of rows of the image
 * @param ncols : number of columns of the image
 * @param sigma : standard deviation of the gaussian filter
 */
void LoG_kernel(float* image, int nrows, int ncols, float sigma) {
  int ksize = (int) (8*sigma+1);
  if ((ksize & 1) == 0) ksize++;
  float* G = gaussian_2d(ksize, sigma);
  float* dG = gaussian_second_derivative_1d(ksize, sigma);
  float* image_cpy, *image_cpy2;
  size_t imsize_t = nrows*ncols*sizeof(float);
  image_cpy = (float*) malloc(imsize_t);
  memcpy(image_cpy, image, imsize_t);
  image_cpy2 = (float*) malloc(imsize_t);
  memcpy(image_cpy2, image, imsize_t);
  // (I * g_xx)*G(y)
  convolve_1d(image_cpy, nrows, ncols, dG, ksize, 0);
  convolve_1d(image_cpy, nrows, ncols, G, ksize, 1);
  // (I * g_yy)*G(x)
  convolve_1d(image_cpy2, nrows, ncols, dG, ksize, 1);
  convolve_1d(image_cpy2, nrows, ncols, G, ksize, 0);
  int i;
  for (i=0; i<nrows*ncols; i++) image[i] = image_cpy[i] + image_cpy2[i];
  free(image_cpy);
  free(image_cpy2);
  free(G);
  free(dG);
}
/**
 * @brief compute_histogram : Compute the histogram of a data
 * @param data : Data which we want to compute the histogram from
 * @param size : number of elements of the data
 * @param nbins : number of bins of the resulting histogram
 * @param minv : (result, given by address if not NULL) minimum value
 * @param maxv : (result, given by address if not NULL) maximum value
 * @return hist : the histogram of size "nbins"
 */
int* compute_histogram(float* data, int size, int nbins, float* minv, float* maxv) {
  float vmin = data[0], vmax = data[0];
  int i;
  for (i=0; i<size; i++) {
    if (data[i] < vmin) vmin = data[i];
    if (data[i] > vmax) vmax = data[i];
  }
  int* hist = (int*) calloc(nbins,sizeof(int));
  float binsize = (vmax-vmin)/nbins;
  float offset = abs(vmin);
  for (i=0;i<size;i++) {
    hist[(int) ((data[i]+offset)/binsize)]++;
  }
  if (minv) *minv = vmin;
  if (maxv) *maxv = vmax;
  return hist;
}

float* create_mask_threshold(float* data, int size, float pc) {
  float* mask = (float *) calloc(size, sizeof(float));
  float vmin = data[0], vmax = data[0];
  int i;
  for (i=0; i<size; i++) {
    if (data[i] < vmin) vmin = data[i];
    if (data[i] > vmax) vmax = data[i];
  }
  int cnt = 0;
  for (i=0; i<size; i++) {
    if (data[i] > pc*vmax) {cnt++; mask[i] = 1; }
  }
  printf("found %d values greater than threshold (min = %f, max = %f)\n\n",cnt,vmin,vmax);
  return mask;
}
#ifdef DATA_WEIGHT_MASK
float* load_weight_mask(char* fname, int numrows, int numcols) {
  FILE* fid = fopen(fname,"rb");
  if (fid == NULL) {
    printf("ERROR in load_weight_mask: could not open %s\n",fname);
    return NULL;
  }
  float* arr = (float*) calloc(numrows*numcols,sizeof(float));
  if (arr == NULL) {
    puts("ERROR in load_weight_mask: out of memory");
    fclose(fid);
    return NULL;
  }
  fread(arr, sizeof(float), numrows*numcols, fid);
  fclose(fid);
  return arr;
}
#endif


 /* --------------------*/
/*      CCarraylist    */

void CCarraylist_initialise(CCarraylist * self) {
  self->ntokens=0;
}

/* the pointers contained in ccarraylist are supposed to be allocated
externally and not be freed. Cspace already Incref the Pyobject */
void  CCarraylist_appendarray(CCarraylist* self, float * array) {
  if(self->ntokens==MAXNTOKENS) {
    fprintf(stderr, " ntokens==MAXNTOKENS in file  %s line %d  \n", __FILE__, __LINE__);
    fprintf(stderr, " MAXNTOKENS is set to %d in file CCspace.h \n", MAXNTOKENS);
    fprintf(stderr, " you should either increase it in Cspace.h or decrease it in pyhst input file\n");
    exit(1);
  }
  self->datatokens[self->ntokens]=array;
  self->ntokens+=1;
}


void     Filter_and_Trim(CCspace *self, float * buffer, float * Tptr  ,float * Rawptr,
       int Pos0, int Pos1, int  Size0, int Size1,
       int pos0, int pos1, int size0, int size1,
       float axis_correctionsL,
       int ncpus
       );

void     Filter_CCD(CCspace *  self, float * buffer, float * Rawptr,
        int Pos0, int Pos1, int  Size0, int Size1,
        int pos0, int pos1, int size0, int size1,
        int CCD_FILTER_KIND ,
        void *CCD_FILTER_PARA,
        // float axis_correctionsL,
        int ncpus
        );

void CCD_AXIS_LONGITUDINAL_CORRECTION_Implementation(float *Tptr, float *Rawptr,
                 int Pos0, int  Pos1, int  Size0, int  Size1 ,
                 int pos0, int  pos1, int  size0, int  size1,
                 float axis_correctionsL  , int ncpus );

/* --------------------*/
/*   CCreading_infos   */

void CCreading_infos_PrereadHeaders(CCreading_infos* self,  int tryconstheader, char ** file_list, int FILE_INTERVAL) {
  long int hsize_first, hsize_last , i;
  double stime, etime;
  int iproc;
  
  static int commonDim1=-1, commonDim2=-1;
  
  int*  datatype_ptr ;
  int* byteorder_ptr;
  int* sizeImag_ptr;
  int list_lenght;
  long int **headerSizes_ptr;
  int multiframe=0;
  int sizeofdatatype[3];

  char * mycurrentname = NULL;
  float cur_tmp;
  
  sizeofdatatype[FloatValue]   =4;
  sizeofdatatype[UnsignedShort]=2;
  sizeofdatatype[SignedInteger]=4;


  if(file_list==self->proj_file_list) {
    sizeImag_ptr  = &(self->sizeImage);
    byteorder_ptr = &(self->byteorder);
    datatype_ptr  = &(self->datatype) ;
    list_lenght=self->proj_file_list_lenght;
    headerSizes_ptr =  &(self->headerSizes) ;
    multiframe=self->MULTIFRAME;

    if ( strlen( self->CURRENT_NAME  )==0 ) {
      mycurrentname = NULL ;
    } else {
      mycurrentname = self->CURRENT_NAME ;
    }


    self->currents = (float *) malloc(list_lenght* sizeof(float)      ) ; 
    self->currents_lenght =  list_lenght ; 
    for(i=0; i<list_lenght; i++) {
      self->currents[i]=1.0;
    }
    
  }  else   if(file_list==self->ff_file_list) {
    sizeImag_ptr  = &(self->ff_sizeImage);
    byteorder_ptr = &(self->ff_byteorder);
    datatype_ptr  = &(self->ff_datatype) ;
    list_lenght=self->ff_file_list_lenght;
    headerSizes_ptr =  &(self->ff_headerSizes) ;
    
    mycurrentname = NULL ;
    
  }  else   if(file_list==self->corr_file_list) {
    self->corr_restrained=0;
    
    sizeImag_ptr  = &(self->corr_sizeImage);
    byteorder_ptr = &(self->corr_byteorder);
    datatype_ptr  = &(self->corr_datatype) ;
    list_lenght=self->corr_file_list_lenght;
    headerSizes_ptr =  &(self->corr_headerSizes) ;

    
    mycurrentname = NULL ;

    
  } else {
    printf(" internal error in  CCreading_infos_PrereadHeaders\n");
    exit(1);
  }
  
  MPI_Comm_rank(MPI_COMM_WORLD,&(iproc));
  
  if( strcmp(self->proj_reading_type , "edf")==0   || file_list==self->corr_file_list ) {
    (*headerSizes_ptr) =(long int*) malloc(list_lenght*sizeof(long int));
    prereadEdfHeader( sizeImag_ptr,
                      byteorder_ptr,
                      datatype_ptr,
                      &hsize_first,
                      &(self->Dim_1),
                      &(self->Dim_2),
                      file_list[0] ,
		      mycurrentname,
		      &cur_tmp
		      );
    if(file_list==self->corr_file_list)  {
      prereadEdfHeader_restrained(
				  &(self->corr_start_2),
				  &(self->corr_real_2),
				  file_list[0] );
      if(   self->corr_start_2 >0  || self->corr_real_2!=  self->Dim_2     ){
        self->corr_restrained=1;
      }
    }
    
    prereadEdfHeader( sizeImag_ptr,
                      byteorder_ptr,
                      datatype_ptr,
                      &hsize_last      ,
                      &(self->Dim_1),
                      &(self->Dim_2),
                      file_list[ list_lenght-1],
		      mycurrentname,
		      &cur_tmp );
    if(commonDim1!=-1) {
      if (self->Dim_1 != commonDim1 || self->Dim_2 != commonDim2  ) {
        printf(" Input File CCreading_infos_PrereadHeaders \n");
        printf(" Actual scheme expects projections and Flat Fields having the same dimensions ( they can have different datatypes though)\n");
        exit(1);
      }
      commonDim1=self->Dim_1;
      commonDim2=self->Dim_2;
    }
    
    
    if( multiframe  || (tryconstheader &&  hsize_last==hsize_first) ) {
      if(multiframe==0) {
        if(iproc==0) {
          printf("Detected constant size headers. tryconstheader option on\n");
        }
        for(i=0; i<list_lenght; i++) {
	  
	  // printf("DEBUGH mettendo header %i a %d \n", i, hsize_first);
	  
          (*headerSizes_ptr)[i]=hsize_first;
        }
      } else if(multiframe==2){
        if(iproc==0) {
          printf("Applying constant size headers with MULTIFRAME=2\n");
        }
        for(i=0; i<list_lenght; i++) {
          /* int ip = self->file_proj_num_list[i]; */
          /* (*headerSizes_ptr)[i]=hsize_first+(FILE_INTERVAL*ip+self->NUM_FIRST_IMAGE)*(hsize_first+ self->Dim_1*self->Dim_2*sizeofdatatype[*datatype_ptr] ); */
          int ip = self->file_proj_num_list[self->my_proj_num_list[i]];
          (*headerSizes_ptr)[i]=hsize_first+(ip)*(hsize_first+ self->Dim_1*self->Dim_2*sizeofdatatype[*datatype_ptr] );
        }
      } else if(multiframe==1){
        self->Dim_2=1;
        if(iproc==0) {
          printf("Applying only initial header with MULTIFRAME=1\n");
        }
        for(i=0; i<list_lenght; i++) {
          /* int ip = self->file_proj_num_list[i]; */
          /* (*headerSizes_ptr)[i]=hsize_first+(FILE_INTERVAL*ip+self->NUM_FIRST_IMAGE)*( self->Dim_1*self->Dim_2*sizeofdatatype[*datatype_ptr] ); */
          int ip = self->file_proj_num_list[self->my_proj_num_list[i]];
          (*headerSizes_ptr)[i]=hsize_first+(ip)*( self->Dim_1*self->Dim_2*sizeofdatatype[*datatype_ptr] );
        }
      }   else {
	printf(" the MULTIFRAME value you gave is not yet implemented in the new PyHST\n");
	exit(1);
      }
    } else {
      stime = ((double) clock())/CLOCKS_PER_SEC ;
      
      for(i=0; i<list_lenght; i++) {
        if(iproc==0 && i%100==0 && i) {
          etime=((double) clock())/CLOCKS_PER_SEC;
          printf( " PRE-READING Header projection # %ld  of %d spent %e seconds.  Total  approx.: %e seconds \n", i ,
                  list_lenght, etime-stime,
                  list_lenght*(etime-stime)/i );
        }
	
	
        prereadEdfHeader( sizeImag_ptr,
                          byteorder_ptr,
                          datatype_ptr,
                          &(   (*headerSizes_ptr) [i]        ),
                          &(self->Dim_1),
                          &(self->Dim_2),
                          file_list[i] ,
			  mycurrentname,
			  &cur_tmp);
	
	if(  mycurrentname  )  {
	  self->currents[i] = cur_tmp ; 
	}
	
      }
    }
  } else {
    fprintf(stderr, "WARNING lettura .h5 in test phase!! \n");
  }
  
}
/* ------------*/
/* CCspace     */

void CCspace_initialise(CCspace * self  ) {
  self->rawdatatokens  =   (CCarraylist*) malloc( sizeof(CCarraylist));
  self->ff_rawdatatokens  =   (CCarraylist*) malloc( sizeof(CCarraylist));
  self->datatokens     =   (CCarraylist*) malloc( sizeof(CCarraylist));
  self->transposeddatatokens     =   (CCarraylist*) malloc( sizeof(CCarraylist));
  
  CCarraylist_initialise(self->rawdatatokens);
  CCarraylist_initialise(self->ff_rawdatatokens);
  CCarraylist_initialise(self->datatokens);
  CCarraylist_initialise(self->transposeddatatokens);

  self->snXtoken       = NULL;
  self->ff_read_status = NULL ;
  sem_init(&(self->ff_sem  ),  0,  1);
  sem_init(&(self->fftw_sem),  0,  1);
  sem_init(&(self->slicesrequest_sem),  0,  1);
  sem_init(&(self->savgol_sem),  0,  1);
  sem_init(&(self->fbp_sem),  0,  1);
  sem_init(&(self->gpustat_sem),  0,  1);
  sem_init(&(self->gpustat_pag_sem),  0,  1);
  sem_init(&(self->gpudones_pag_sem),  0,  1);
  sem_init(&(self->gpustat_med_sem),  0,  1);
  sem_init(&(self->islicetracker_sem),  0,  1);
  sem_init(&(self->proupdate_sem),  0,  1);

  sem_init(&(self->filereading_sem),0,10) ;  
  sem_init(&(self->hdf5sequence_sem),0,1) ;  
  
  MPI_Comm_rank(MPI_COMM_WORLD,&(self->iproc));
  MPI_Comm_size(MPI_COMM_WORLD,&(self->nprocs));

  self->shared_h5id = -1; 
  self->shared_h5DSid = -1; 
  
  self->slicesrequest=NULL;


  self->Coeff_fil=NULL;

  self->fbp_precalculated.FILTER=0;
  self->fbp_precalculated.NEURALFILTER=0;

  self->sharedHandle=NULL;

  self->gpu_is_apriori_usable=1;

  self->gpu_context=NULL;
  self->gpu_pag_context=NULL;
  self->gpu_med_context=NULL;


  self->aMin=1;
  self->aMax=0;

  sem_init(&(self->minmax_sem), 0, 1);
  self->histovalues = (long int *) malloc(1000001*sizeof(long int));
  memset(self->histovalues,0, 1000001*sizeof(long int));

  self->invertedsteam_output_created=0;

  self->packet_has_flown = (int*) malloc(1000000*sizeof(float));
  memset(      self->packet_has_flown, 0,   1000000*4        ) ;


  for(int i=0; i< MAXALLOCATIONS; i++) {
    self->allocations[i]       = NULL;
    self->allocations_sizes[i] = 0   ;
  }

  
}

void CCspace_set_nchunks( CCspace * self   ,int   nchunks )  {
  int i;
  self->reading_infos.nchunks = nchunks;

  self->packet_completion = (int *) malloc( nchunks * sizeof(int) ) ;
  for( i=0; i<self->reading_infos.nchunks ; i++ ) {
    self->packet_completion[i]=0;
  }
}


void CCspace_add2DataSpace(CCspace * self , float *token,const char *key ) {
  if(strcmp(key,"rawdatatokens" )==0)       {  CCarraylist_appendarray( self->rawdatatokens, token); }
  else if(strcmp(key,"ff_rawdatatokens" )==0)  {  CCarraylist_appendarray( self->ff_rawdatatokens, token); }
  else if(strcmp(key,"datatokens" )==0)  {  CCarraylist_appendarray( self->datatokens, token); }
  else if(strcmp(key,"transposeddatatokens" )==0)  {  CCarraylist_appendarray( self->transposeddatatokens, token); }
  else  {    printf("internal error routine CCspace_add2DataSpace\n ");exit(1);
  }
}
void   CCspace_prepare_concurrent_ff_reading(CCspace * self) {
  int k;
  CCspace_ffstatus_dealloc( self );
  self->snXtoken       =  (int* ) malloc(  sizeof(int  )* self-> ff_rawdatatokens-> ntokens ) ;
  self->ff_read_status =  (int**) malloc(  sizeof(int* )* self-> ff_rawdatatokens-> ntokens ) ;
  for( k = 0; k < self->ff_rawdatatokens->ntokens  ; k++  ) {
    self->snXtoken[k] =-1; // reinitialise ff_read_status when it changes
    self->ff_read_status[k] = (int* ) malloc( sizeof(int  )*  self->reading_infos . NFF) ;
  } 
}

void   CCspace_ffstatus_dealloc(CCspace * self ) {
  int k;
  if(self->snXtoken ) free(self->snXtoken);
  self->snXtoken=NULL;

  if(self->ff_read_status ) {
    for(k=0; k<  self->ff_rawdatatokens->ntokens; k++) {
      free( self->ff_read_status[k]   ) ;
    }
    free(self->ff_read_status );
  }
  self->ff_read_status =NULL;
}


void   CCspace_end(CCspace * self ) {
  /* printf("finalize\n"); */
  /* MPI_Finalize(); */
  /* printf("finalize OK\n"); */
  /* exit(0); */
}




void     dimmi_somma(   char * msg   , float *x  , int n   )  {
  int i;
  double sum=0.0;
  for (i=0; i<n; i++) {
    sum+=x[i]*x[i];
  }
  printf(" %s    %e \n", msg, sum);
}




void CCspace_reconstruct(CCspace *  self ,int sn, int npbunches,int ncpus, int  STEAM_DIRECTION ) {
  int sn_nb ;
  int num_slices;
  int nslicestodo;
  int completed;
  SlicesRequest request;
  int hasposted=0;
  int hasbeenset=0;
  struct timespec delay = {0,5000000}; //500,000 = 1/2 milliseconds
  struct timespec delayrem;

  sn_nb = sn/npbunches ;

  long int size1 = self->reading_infos.size_s_[2*sn_nb +1];

  int cmar = self->params.CONICITY_MARGIN;

  sn_nb = sn/npbunches ;

  // num_slices         = self->reading_infos.slices_mpi_numslices[sn_nb*self->nprocs + self->iproc] ;

  int prima, ultima , base;
  base = self->params.first_slices_2r[0];
  prima  =  self->params.first_slices_2r[sn_nb*self->nprocs + self->iproc] ;
  ultima =  self->params.last_slices_2r[sn_nb*self->nprocs + self->iproc] ;
  num_slices = ultima - prima + 1;

  int *correspondance = self->params.corr_slice_slicedect ;
  nslicestodo = (int) ( num_slices*1.0/npbunches+0.9999999 );

  {
    int ns2req = correspondance[prima +nslicestodo-1 -base ] - correspondance[prima -base]+1;
    ns2req = max(correspondance[ultima -base ] - correspondance[ultima - nslicestodo+1 -base]+1, ns2req    ) ;
    request.data =  (float*) malloc((ns2req+2*cmar+2)*self->params.nprojs_span * size1*sizeof(float)) ; // aggiunto un altro 2 per sicurezza
  }

  while(1) {
    nanosleep(&delay, &delayrem);
    sem_wait( &(self->slicesrequest_sem));
    completed =  self->packet_completion[ sn_nb ] ;
    sem_post( &(self->slicesrequest_sem));
    if(completed) break;
    hasposted=0;
    sem_wait( &(self->slicesrequest_sem));
    if(self->slicesrequest==NULL ||  self->slicesrequest->has_been_set_flag ) {
      request.nslices_2r=nslicestodo;
      request.has_been_set_flag=0;
      self->slicesrequest=&request;
      hasposted=1;
    }
    sem_post( &(self->slicesrequest_sem));
    if(hasposted) {
      while(1) {
        nanosleep(&delay, &delayrem);
        sem_wait( &(self->slicesrequest_sem));
        completed =  self->packet_completion[ sn_nb ] ;
        hasbeenset =  request.has_been_set_flag  ;
        sem_post( &(self->slicesrequest_sem));
        if(hasbeenset) {
          if (self->params.verbosity>0) printf(" Ho %d slices da fare  completed %d Nfirstslice %d \n", request.nslices_2r, completed, request.Nfirstslice);
          if (self->params.verbosity>0) printf("self->params.DO_RING_FILTER %d \n", self->params.DO_RING_FILTER   );
          if( self->params.DO_RING_FILTER ) {
            if(   self->params.RING_FILTER_KIND  == RING_Filter_ID  ) {
              if (self->params.verbosity>0) printf(" ring_filter  \n" );
              CCspace_RING_Filter_implementation(self, request.data,
                                                 (RING_Filter_PARA_struct*) self->params.RING_FILTER_PARA ,
                                                 request.nslices_data,
                                                 self->params.nprojs_span, size1 , ncpus, NULL) ;  // no more used

            } else  if(  self->params.RING_FILTER_KIND  == RING_Filter_SG_ID  ) {
              CCspace_RING_Filter_SG_implementation(self, request.data,
                                                    (RING_Filter_SG_PARA_struct*) self->params.RING_FILTER_PARA ,
                                                    request.nslices_data,
                                                    self->params.nprojs_span, size1, ncpus, NULL  ) ; // no more used
            }
          }


          CCspace_Sino_2_Slice(self, request.data, request.nslices_2r, request.nslices_data,     request.Nfirstslice, ncpus,
                               request.data_start,1,   self->reading_infos. proj_num_offset_list[ sn_nb ]     , sn%npbunches  , npbunches );
          {
            sem_wait( &(self->slicesrequest_sem));
            self->slicesrequest=NULL ;
            sem_post( &(self->slicesrequest_sem));
          }
        };
        if(hasbeenset || completed ) break;
      }
    }
  }

  free(request.data);
}

void CCspace_reconstructSHARED(CCspace *  self ,int sn, int npbunches,int ntoktransposed, int ncpus, int STEAM_DIRECTION ) {
  int sn_nb, inb ;
  long int num_slices;
  long int nslicestodo; //firstslice;

  sn_nb = sn/npbunches ;
  inb   = sn%npbunches ;

  long int start_slice_2r = self->params.first_slices_2r[ sn_nb*self->nprocs + self->iproc]  ;
  long int top_slice_2r   = self->params.last_slices_2r [ sn_nb*self->nprocs +self->iproc ] +1 ;

  // int total_slices_2r =  top_slice_2r - start_slice_2r  ;
  // long int  base = self->params.first_slices_2r[0];
  int *correspondance = self->params.corr_slice_slicedect ;

  long int size1 = self->reading_infos.size_s_[2*sn_nb +1];

  int try_2by2 = 0;
  if( STEAM_DIRECTION==1 && self->params.VECTORIALITY ==1 && !self->params.OUTPUT_SINOGRAMS &&
      !(self->params.DO_RING_FILTER &&  self->params.RING_FILTER_KIND  == RING_Filter_THRESHOLDED_ID) &&
      self->params.CONICITY==0 &&
      !self->params.ITERATIVE_CORRECTIONS  &&
      !self->params.LT_pars &&
      self->params.FBFILTER != 10 &&
      self->gpu_context
      ) {
    try_2by2 = 1;
  }
    
  num_slices         =  top_slice_2r  -  start_slice_2r ;
  
  nslicestodo = (int) ( num_slices*1.0/npbunches+0.9999999 );
  if( try_2by2 ) {
    if(num_slices>1) {
      if( nslicestodo%2) nslicestodo++;
    }
  }
  
  int N_tmp =  max( (nslicestodo*inb -self->params.patch_ep )   , 0 ) ;
  
  long int slice_ptr =N_tmp +  start_slice_2r ;
  long int nslices_2r  = max(min( num_slices-N_tmp,  nslicestodo*(inb+1)-N_tmp +self->params.patch_ep  ),0);

  if(nslices_2r==0 ) return;

  int my_wished_margine        = self->params.CONICITY_MARGIN_DOWN_wished[sn_nb*self->nprocs + self->iproc ] ;
  int my_wished_margine_up     = self->params.CONICITY_MARGIN_UP_wished[sn_nb*self->nprocs + self->iproc ] ;
  if(self->params.CONICITY ) { 
    int md, mu;
    md =  my_wished_margine ;
    mu =  my_wished_margine_up ;
    
    float n_md =   (  (float) inb* mu     +(npbunches-inb)* md)  /(npbunches)   ; // per l' ultimo spessore non si arriva mai a npbunches
    float n_mu =   ((float) (inb+1)* mu     +(npbunches-inb-1)* md ) /(npbunches)   ; // per l' ultimo spessore non si arriva mai a npbunches
    
    my_wished_margine    = ceil(n_md);
    my_wished_margine_up = ceil(n_mu);
  }
  
  long int mydata_start  ;
  if(self->params.DZPERPROJ==0)  {
    mydata_start =  correspondance[ slice_ptr + (int) self->params.SOURCE_Y  /* -base  */] - self->params.first_slices[sn_nb ]  ;
    mydata_start = mydata_start - my_wished_margine ;
    if(mydata_start<0) {
      mydata_start=0;
    }
  } else {
    // per definizione il bordo inferiore del detector passa davanti a start_slice_2r
    // quando il numero della proiezione non shiftata est zero ( cioe' il vero numero di
    //  proiezione est offsetted )
    if (self->params.verbosity>1)  printf("DEBUG slice_ptr %ld start_slice_2r %ld , self->params.first_slices[sn_nb ]  %d  my_wished_margine %d \n",  slice_ptr ,
	   start_slice_2r  , self->params.first_slices[sn_nb ], my_wished_margine );
    // mydata_start =  correspondance[ slice_ptr - start_slice_2r ] - self->params.first_slices[sn_nb ]  ;
    mydata_start =  slice_ptr - self->params.first_slices[sn_nb ]  ;//      self->reading_infos.pos_s_[sn_nb]      dovrebbe essere uguale  ;
    mydata_start = mydata_start - my_wished_margine ;
  }

  int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*self->nprocs + self->iproc]  ;
  int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*self->nprocs + self->iproc]  ;
  long int start_slice_2s = self->reading_infos.slices_mpi_offsets[ sn_nb*self->nprocs + self->iproc] -md ;
  long int top_slice_2s   = self->reading_infos.slices_mpi_offsets[ sn_nb*self->nprocs +self->iproc ]
               +self->reading_infos.slices_mpi_numslices[ sn_nb*self->nprocs + self->iproc]
               + mu;
  long int total_slices_2s =  top_slice_2s - start_slice_2s ;


  long int mydata_end;
  if(self->params.DZPERPROJ==0)  {
    mydata_end =  correspondance[slice_ptr + (nslices_2r-1) + (int) self->params.SOURCE_Y /* -base   */ ] +1 ; // il +1 !!!!
    mydata_end -= self->params.first_slices[sn_nb]  ;
    // mydata_end +=   mu ;
    mydata_end +=   my_wished_margine_up ;
  } else {
    mydata_end =        slice_ptr + (nslices_2r-1) +1 ;       // correspondance[slice_ptr + (nslices_2r-1)  - start_slice_2r ] +1 ; // il +1 !!!!
    mydata_end -= self->params.first_slices[sn_nb]  ;
    mydata_end +=  my_wished_margine_up;  
  }


  if(!self->params.CONICITY ) { //  && self->params.DZPERPROJ==0 ) {
    assert( (mydata_end-mydata_start) ==  (md+ nslices_2r+mu)   );
  }
  
  /*
    memcpy( self->slicesrequest->data,
    self-> transposeddatatokens->datatokens[ntoktransposed] +
    (mydata_start - start_slice_2s)* self->params.nprojs_span * size1,
    (mydata_end-mydata_start)*self->params.nprojs_span * size1 *sizeof(float)
    );
    self->slicesrequest->Nfirstslice  =  slice_ptr ;
    self->slicesrequest->nslices_2r   =  nslices_2r ;
    self->slicesrequest->nslices_data =  (mydata_end-mydata_start);
    self->slicesrequest->data_start   =   mydata_start + self->params.first_slices[sn_nb ] ;
    self->slicesrequest->has_been_set_flag=1;
    slice_ptr              +=   nslices_2r ;
  */


  if (self->params.verbosity>2)  printf(" DEBUG mydata_end  %ld, mydata_start  %ld  self->params.first_slices[sn_nb ] %d  \n",
	 mydata_end , mydata_start,  self->params.first_slices[sn_nb ]) ; 
  
  
  long int Nfirstslice = slice_ptr;
  long int nslices_data =  (mydata_end-mydata_start);
  long int data_start   =   mydata_start + self->params.first_slices[sn_nb ] ;

  if (self->params.verbosity>0) printf(" Ho %ld slices da fare   Nfirstslice %ld \n", nslices_2r,  Nfirstslice);
  if (self->params.verbosity>0) printf("self->params.DO_RING_FILTER %d \n", self->params.DO_RING_FILTER   );

  if(STEAM_DIRECTION == -1 ) {
    sem_wait( &(self->fftw_sem));
    if(self->packet_has_flown[sn_nb]==0) {
      memset(  self->transposeddatatokens->datatokens[ntoktransposed],
               0,
               sizeof(float)*self->params.nprojs_span*size1*total_slices_2s ) ;
      self->packet_has_flown[sn_nb]=1;
    }
    sem_post( &(self->fftw_sem));
  }
  
  if (self->params.verbosity>1)  printf(" data %p  mydata_start %ld start_slice_2s %ld  \n",  self->transposeddatatokens->datatokens[ntoktransposed],
	 mydata_start, start_slice_2s) ; 
  
  float *data = self->transposeddatatokens->datatokens[ntoktransposed]+(mydata_start-start_slice_2s)*self->params.nprojs_span*size1;

  if(STEAM_DIRECTION==1) {
    if( self->params.DO_RING_FILTER ) {
      // data =  (float*) malloc((mydata_end-mydata_start)*self->params.nprojs_span * size1 *sizeof(float)) ;

      if(   self->params.RING_FILTER_KIND  == RING_Filter_ID  ) {
        if (self->params.verbosity>0) printf(" ring_filter  \n" );
        CCspace_RING_Filter_implementation(self, data,
                                           (RING_Filter_PARA_struct*) self->params.RING_FILTER_PARA ,
                                           nslices_data,
                                           self->params.nprojs_span, size1 , ncpus,
                                           self->transposeddatatokens->islice_tracker[ntoktransposed]+(mydata_start-start_slice_2s)) ;
      } else  if(  self->params.RING_FILTER_KIND  == RING_Filter_SG_ID  ) {
        CCspace_RING_Filter_SG_implementation(self, data,
                                              (RING_Filter_SG_PARA_struct*) self->params.RING_FILTER_PARA ,
                                              nslices_data,
                                              self->params.nprojs_span, size1, ncpus,
                                              self->transposeddatatokens->islice_tracker[ntoktransposed] +(mydata_start-start_slice_2s)) ;
      }
    }
  }
  // printf(" qua calcolo %d nslices_data  a partire da data_start %d \n", nslices_data,data_start);
  CCspace_Sino_2_Slice(self,data,nslices_2r,nslices_data,Nfirstslice, ncpus,data_start, STEAM_DIRECTION,
		       self->reading_infos. proj_num_offset_list[ sn_nb ] , sn%npbunches  , npbunches);

  /* if(STEAM_DIRECTION==1) {  */
  /*   if( self->params.DO_RING_FILTER ) { */
  /*     free(data); */
  /*   } */
  /* } */

  if (self->params.verbosity>0) printf(" ESCO DA SHARED \n");

}



void get_min_max( float *  SLICEcalc, int num_y,  int num_x,
                  float *ima_min, float *ima_max   ) {
  *ima_min=1.0e30;
  *ima_max=-1.0e30;
  int y,x;
  for(y=0; y< num_y; y++) {
    for(x=0; x< num_x; x++) {
      *ima_min=min(SLICEcalc[y*(num_x) +x],*ima_min);
      *ima_max=max(SLICEcalc[y*(num_x) +x],*ima_max);
    }
  }
}


void spills_over( float *  SLICEcalc, int num_y,  int num_x,
                  float  threshold , float * SLICE    ) {
  int y,x;
  for(y=0; y< num_y; y++) {
    for(x=0; x< num_x; x++) {
      if( SLICEcalc[y*(num_x) +x]  > threshold  ) {
        // SLICE[y*(num_x) +x]+=( SLICEcalc[y*(num_x) +x]-threshold);
        SLICE[y*(num_x) +x] =  SLICEcalc[y*(num_x) +x]-threshold;
      } else {
        // SLICE[y*(num_x) +x] = -100;
      }
    }
  }
}

void put_histo(long int *new_histo, double aMin, double aMax,
               float * SLICE, int size ) {
  int i,j;
  float f,d, F;
  d=(aMax-aMin)/(1000000-1);
  // printf(" AMIN AMAX %e %e %e \n", aMin, aMax, d);
  if(d<=0) d=1;
  for(i=0; i<size; i++) {
    f=SLICE[i];
    F = (f - aMin)/d -0.49999f ;
    FLOAT_TO_INT(F, j ) ;
    if(j>0)    new_histo[j]++;
  }
}


void replace_histo( long int *histovalues, double SaMin, double SaMax,
                    long int *new_histo  , double aMin, double aMax     )
{
  int i,j;
  float f,d,d05,D, F;

  d=(aMax-aMin)/(1000000-1);
  D=(SaMax-SaMin)/(1000000-1);
  if(D<=0) D=1;
  d05=d/2.0f;
  for(i=0; i<1000000; i++) {
    f=aMin+i*d+d05;
    F = (f - SaMin)/D -0.49999f ;
    FLOAT_TO_INT(F, j ) ;
    if(j>=0 && j<=1000000-1)
      histovalues[j]+= new_histo[i];
  }
}

void   CCspace_getSaturations(CCspace * self, double aMin,double aMax,double *sat1,double *sat2 ,
                              double *Sat1,double *Sat2  ) {

  long int * replacement_histo=(long int *) malloc(1000001*sizeof(long int));
  memset(replacement_histo , 0 , 1000001*sizeof(long int));

  replace_histo(replacement_histo,aMin, aMax,
                self->histovalues       , self->aMin, self->aMax   );

  int nels=1000000;
  MPI_Allreduce(MPI_IN_PLACE, replacement_histo, nels , MPI_LONG , MPI_SUM, MPI_COMM_WORLD );

  MPI_Barrier(MPI_COMM_WORLD);

  double sum = 0.0;
  int i;
  for(i=0; i<1000000; i++) {
    sum+= replacement_histo[i];
  }
  double y = 0.0;
  for(i=0; i<1000000; i++) {
    y+= replacement_histo[i];
    if(y>0.00001*sum) {
      *sat1 = aMin+i*(aMax-aMin)/(1000000-1) ;
      break;
    }
  }
  y = 0.0;
  for(i=1000000-1; i>=0; i--) {
    y+= replacement_histo[i];
    if(y>0.00001*sum) {
      *sat2 = aMin+i*(aMax-aMin)/(1000000-1) ;
      break;
    }
  }
  y = 0.0;
  for(i=0; i<1000000; i++) {
    y+= replacement_histo[i];
    if(y>0.002*sum) {
      *Sat1 = aMin+i*(aMax-aMin)/(1000000-1) ;
      break;
    }
  }
  y = 0.0;
  for(i=1000000-1; i>=0; i--) {
    y+= replacement_histo[i];
    if(y>0.002*sum) {
      *Sat2 = aMin+i*(aMax-aMin)/(1000000-1) ;
      break;
    }
  }


  if(self->iproc==0) {
    char nomeout[10000];
    char s[4000];
    sprintf(nomeout,"%s",self->params.OUTPUT_FILE_HISTOGRAM);

    FILE *output = fopen(nomeout,"w") ;
    if(!output) {
      printf(" error opening output file for histogram now stopping\n");
      fprintf(stderr, " error opening output file for histogram now stopping\n");
      exit(1);
    }
    if( byteorder()== LowByteFirst ) {

      sprintf(s,"{\nHeaderID       = EH:000001:000000:000000 ;\nImage          = 1 ;\nByteOrder = LowByteFirst ;\nSize = %ld ;\nDim_1 = %d ;\nDim_2 = %d ;\nDataType = Signed64 ;\nMaxVal = %14.8e ;\nMinVal = %14.8e ;\n",((long int )1000000 *sizeof(long int)),1,1000000,aMax,aMin);
    } else {
      sprintf(s,"{\nHeaderID        =  EH:000001:000000:000000 ;\nImage           =  1 ;\nByteOrder = HighByteFirst ;\nSize = %ld ;\nDim_1 = %d ;\nDim_2 = %d ;\nDataType = Signed64 ;\nMaxVal = %14.8e ;\nMinVal = %14.8e ;\n",((long int )1000000 *sizeof(long int) ),1,1000000,aMax,aMin);
    }

    int len=strlen(s);
    fwrite(s,1,len,output);
    for(i=len; i<1022; i++) {
      fwrite(" ",1,1,output);
    }
    fwrite("}\n",1,2,output);

    fwrite(replacement_histo , sizeof(long int),1000000  , output);
    fclose(output);
  }
  free(replacement_histo);

}

void free_LT_infos(LT_infos *p, CCspace *self) {
  if (p==NULL) return;
  if( p->  C   ) free( p->  C    );
  if( p->  II   ) free( p->  II    );
  if( p->  JJ   ) free( p->  JJ    );
  if( p-> C2s    ) free( p->  C2s    );
  if( p->  I2s   ) free( p->  I2s    );
  if( p->  J2s   ) free( p->  J2s    );
  if( p->   sigmas  ) free( p->  sigmas    );

  if( p-> gaussian_coeffs)   free(  p-> gaussian_coeffs   )  ; 

  if( p->corre)  free(p->corre);
  if( p->slice_comp)  free(p->slice_comp);
  
  if(p->fftwR ) {
    /* fftw_free(p->fftwR); */
    /* fftw_free(p->fftwC); */
    
    free(p->fftwR);
    free(p->fftwC);
    
    sem_wait( &(self->fftw_sem));
    fftwf_destroy_plan(p->planr2c);
    fftwf_destroy_plan(p->planc2r);
    sem_post( &(self->fftw_sem)); 
  }
  if( p ) free( p    );
}


int lt_cmpr(const void *a, const void *b, void *arg)
{
  int A = *((int *)a);
  int B = *((int *)b);
  int *w = (int *)arg;
 
  return w[A] - w[B];
}



void   lt_reorder(int N, int *pos, float *C, int *II, int *JJ){
  int i,j;
  for(i=0; i< N; i++) {
    int inizio, fine;
    inizio =   pos[i  ] ; 
    fine   =   pos[i+1] ; 
    int nel = fine-inizio;
    if(nel>0) {
      int order[nel];
      int itmp[nel];
      int jtmp[nel];
      float tmp[nel];
      
      for(j=0; j<nel; j++) order[j]=inizio+j;
      qsort_r( order, nel, sizeof(int),  lt_cmpr,     JJ    ) ;
      
      for(j=0; j<nel; j++) {
	tmp [j] = C [order[j]];
	itmp[j] = II[order[j]];
	jtmp[j] = JJ[order[j]];
      }
      for(j=0; j<nel; j++) {
	C [inizio+j] = tmp [j]  ;
	II[inizio+j] = itmp[j]  ;
	JJ[inizio+j] = jtmp[j]  ;
      }
    }
  }
}


double     lt_calculate_grad_sino(int  Ng,float * grad, float *  coeffs,  int   Ns , float * data, int  N,
				  int *csc_posC,  int * csc_II , float *csc_C ,
				  int *csr_posR,  int * csr_JJ , float *csr_C ,
				  float **auxs, float *multisino, int lsd , int nsigma, float * sigmas,
				  LT_infos * lt,  LT_infos * lt2s )  ;


int     gpu_lt_nitems_atonce( int N ,  int *itos  ,  int mnd4t  )  {
  int *isdiffprev_cumsum = (int *)calloc(  N , sizeof(int) ) ;
  int i;
  int restricted=0;
  int result=0;
  for(i=1; i<N; i++) {
    isdiffprev_cumsum[i] =  isdiffprev_cumsum[i-1] + (itos[i]>itos[i-1]);
    if(restricted==0) {
      result=i+1;
      if(isdiffprev_cumsum[i] -isdiffprev_cumsum[i-(result-1)]>=mnd4t) {
	restricted=1;
      }
    }
    if(restricted==1) {
      while(isdiffprev_cumsum[i] -isdiffprev_cumsum[i-(result-1)]>=mnd4t) {
	result--;
      }
    }
  }
  free( isdiffprev_cumsum ) ;
  return result;
}


void  LT_fit_sino(CCspace * self,  LT_infos *lt,LT_infos *lt2s, float *data , int nprojs, int num_bins   )  {
  
  lt->lt_reb = self->params.LT_pars->LT_REBINNING;
  lt2s->lt_reb = self->params.LT_pars->LT_REBINNING;
  float *multisino = (float*) calloc(  lt->Nsigmas*lt-> SLD+ lt2s->N2s, sizeof(float)       );
  int Ng = lt->Ng;
  int Ns = lt->Nsigmas*lt-> SLD ;

  int * count_csr   = (int *) calloc(Ns + lt2s->dim2s_s , sizeof(int) ) ;
  int * count_csc   = (int *) calloc(Ng, sizeof(int) ) ;
  int i;
  for(i=0; i< lt->N; i++) {
    count_csr[lt->II[i]]++;
    count_csc[lt->JJ[i]]++;
  }
  
  for(i=0; i< lt2s->N2s; i++) {
    count_csr[  Ns+ lt2s->I2s[i]]++;
    count_csc[lt2s->J2s[i]]++;
  }

  
  int * csr_posR   = (int * ) calloc(Ns + lt2s->dim2s_s +1, sizeof(int) ) ;
  int * csc_posC   = (int * ) calloc(Ng+1, sizeof(int) ) ;
  for(i=0; i< Ns + lt2s->dim2s_s ; i++) {
    csr_posR[i+1] = csr_posR[i]   +  count_csr[i]  ;
    count_csr[i] = 0 ; 
  }
  for(i=0; i< Ng; i++) {
    csc_posC[i+1] = csc_posC[i]   +  count_csc[i]  ;
    count_csc[i] = 0 ; 
  }
 
  float *csr_C, *csc_C;
  int   *csr_II, *csc_II;
  int   *csr_JJ, *csc_JJ;

  
  csr_C     = (float*) calloc(lt->N+lt2s->N2s, sizeof(float) ) ;
  csr_II    = (int *) calloc (lt->N+lt2s->N2s, sizeof(int) ) ;
  csr_JJ    = (int *) calloc (lt->N+lt2s->N2s, sizeof(int) ) ;
  csc_C     = (float*) calloc(lt->N+lt2s->N2s, sizeof(float) ) ;
  csc_II    = (int *) calloc (lt->N+lt2s->N2s, sizeof(int) ) ;
  csc_JJ    = (int *) calloc (lt->N+lt2s->N2s, sizeof(int) ) ;

  int pos;
  for(i=0; i< lt->N; i++) {
    pos = csr_posR[  lt->II[i] ] + count_csr[lt->II[i]];
    csr_C [pos] = lt->C [i];
    csr_II[pos] = lt->II[i];
    csr_JJ[pos] = lt->JJ[i];
    count_csr[lt->II[i]]++;

    pos = csc_posC[  lt->JJ[i] ] + count_csc[lt->JJ[i]];
    csc_C [pos] = lt->C [i];
    csc_II[pos] = lt->II[i];
    csc_JJ[pos] = lt->JJ[i];
    count_csc[lt->JJ[i]]++;
  }

  for(i=0; i< lt2s->N2s; i++) {
    pos = csr_posR[  lt2s->I2s[i]+ Ns ] + count_csr[lt2s->I2s[i]+ Ns];
    csr_C [pos] = lt2s->C2s [i]*sqrt(  lt-> force_per_gaussian/lt2s->force_per_gaussian  );
    csr_II[pos] = lt2s->I2s[i] + Ns;
    csr_JJ[pos] = lt2s->J2s[i];
    count_csr[lt2s->I2s[i]+ Ns]++;

    pos = csc_posC[  lt2s->J2s[i] ] + count_csc[lt2s->J2s[i]];
    csc_C [pos] = lt2s->C2s [i]  *sqrt(  lt-> force_per_gaussian/lt2s->force_per_gaussian  ) ;
    csc_II[pos] = lt2s->I2s[i]+Ns;
    csc_JJ[pos] = lt2s->J2s[i];
    count_csc[lt2s->J2s[i]]++;
  }


  lt_reorder(Ns+ lt2s->dim2s_s, csr_posR, csr_C, csr_II, csr_JJ);
  lt_reorder(Ng, csc_posC, csc_C, csc_JJ, csc_II);

  int usa_gpu=0;
  sem_wait( &(self->gpustat_sem));
  if(self->gpu_context){
    if(self->gpu_context->inuse==0) {
      self->gpu_context->inuse=1;
      usa_gpu=1 ;
    }
    if(  self->params.ALLOWBOTHGPUCPU) sem_post( &(self->gpustat_sem));
  } else {
    printf(" the context was null\n" );

    
    sem_post( &(self->gpustat_sem));
  }
  if(usa_gpu && 0) { // DISABLED
    int csc_nitems_atonce, csr_nitems_atonce;
    csr_nitems_atonce = min(128,gpu_lt_nitems_atonce( lt->N + lt2s->N2s ,  csr_II  ,  GPU_LT_MAX_NDIFF4thread  ) ); 
    csc_nitems_atonce = min(128,gpu_lt_nitems_atonce( lt->N + lt2s->N2s ,  csc_JJ  ,  GPU_LT_MAX_NDIFF4thread  )) ; 


    
    self->gpu_context->gpu_lt_fit_sino(self->gpu_context, Ng,  lt2s->gaussian_coeffs,     Ns ,  data, lt->N + lt2s->N2s,
				       csc_II ,  csc_JJ ,csc_C , csc_nitems_atonce,
				       csr_II ,  csr_JJ ,csr_C , csr_nitems_atonce,
				       lt->SLD,  lt->Nsigmas,   lt->sigmas,  lt, lt2s
				       );
    sem_post( &(self->gpustat_sem));    
  } else  {
    time_t tstart = clock(); // profiling
    printf("Ng = %d\n", Ng); // DEBUG
    float *auxs[6];
    int i;
    for( i=0; i<6; i++) auxs[i] = (float*) calloc(   max(Ns/lt->Nsigmas, Ng), sizeof( float)) ; 
    printf("Ns = %d, Nsigmas = %d, Ng = %d, aux = %d\n",Ns, lt->Nsigmas, Ng, max(Ns/lt->Nsigmas, Ng));
    puts("------------");
    float *grad=auxs[0];
    float *grad_old=auxs[1];
    float *p=auxs[2];
    
    float err;
    memset( lt2s->gaussian_coeffs, 0 , sizeof(float) * lt-> Ng ) ; 

    err=lt_calculate_grad_sino( Ng,grad, lt2s->gaussian_coeffs,     Ns ,  data, lt->N + lt2s->N2s,
				csc_posC,  csc_II ,csc_C ,
				csr_posR,  csr_JJ ,csr_C ,
				auxs+3,
				multisino, lt->SLD,  lt->Nsigmas,   lt->sigmas,  lt, lt2s) ;

    for( i=0; i<Ng; i++) p[i]=grad[i];
    double rrold=0.0,rr;
    for( i=0; i<Ng; i++) {
      rrold += grad[i]*grad[i];
    }
    rr=rrold;
    int iter;
    for ( iter=0; iter<1000; iter++) {
      for( i=0; i<Ng; i++) {
	grad_old[i] = grad[i];
      }
      lt_calculate_grad_sino( Ng,grad, p ,     Ns ,  NULL , lt->N+ lt2s->N2s,
			      csc_posC,  csc_II ,csc_C ,
			      csr_posR,  csr_JJ ,csr_C ,
			      auxs+3,
			      multisino, lt->SLD,  lt->Nsigmas,   lt->sigmas,  lt, lt2s) ; 

      double pap = 0.0;
      for(i=0; i < Ng; i++) {
	pap += p[i]*grad[i];
      }
      
      for( i=0; i<Ng; i++) {
	  lt2s->gaussian_coeffs[i] -=  p[i]*rr/pap   ;
      }
      
      
      err=lt_calculate_grad_sino( Ng,grad, lt2s->gaussian_coeffs,     Ns ,  data, lt->N+ lt2s->N2s,
				  csc_posC,  csc_II ,csc_C ,
				  csr_posR,  csr_JJ ,csr_C ,
				  auxs+3,
				  multisino, lt->SLD,  lt->Nsigmas,   lt->sigmas,  lt, lt2s) ; 
      // rrold=rr;
      rrold = 0 ; 
      rr=0;
      for( i=0; i<Ng; i++) {
	// rr+=  grad[i]*(grad[i]-grad_old[i]);
	rr+=  grad[i]*(grad[i]);
        rrold +=  grad_old[i]*grad_old[i] ;
      }
      float beta = max(rr/rrold,0)  ; 
      for( i=0; i<Ng; i++) {
  p[i]=  grad[i]+  p[i]*beta;
      }
      if ((iter % 10) == 0) printf( "[%d] errore est %e  mod_grad est  %e  beta %e\n",  iter, err,rr, beta) ;

    }

    // LT_DEBUG
//    write_data_to_edf(lt2s->gaussian_coeffs, 1, lt->Ng, "gaussians.edf");


    for( i=0; i<6; i++) free( auxs[i]) ;
    // profiling
    double el_s = ((double) (clock()-tstart))/CLOCKS_PER_SEC;
    printf("Ellapsed time for correction: %lf secs\n", el_s);
    printf("N2 = %d, Ng = %d\n", lt->SLD/self->params.nprojs_span,Ng); // P.numpjs_span * (num_bins +6*P.LT_MAX_STEP)
  }
  free(multisino);
  free( count_csr  ) ;
  free( count_csc  ) ;
  free( csr_posR  ) ;
  free( csc_posC  ) ;
  free(  csr_C      ) ;
  free(  csr_II     ) ;
  free(  csr_JJ     ) ;
  free(  csc_C      ) ;
  free(  csc_II     ) ;
  free(  csc_JJ     ) ;
}



void  LT_apodize( float *SLICE,int  dia, int num_x, int margin  )  {
  int i,j;
  float d, center, x,y, t;
  center = (num_x-1)/2.0;
  for (i=0; i< num_x; i++) {
    y = i-center;
    for (j=0; j< num_x; j++) {
      x = j -center;
      if( fabs(y)<dia/2*0.7 && fabs(x)<dia/2*0.7 ) continue;
      
      // printf(" x,y,center %e %e %e \n",x,y,center  );
      
      d = sqrt(x*x+y*y );
      
      // printf(" d dia/2 %e %d %d \n",d, dia/2 , margin);
      
      if(d< dia/2) continue;
      
      t = ( d-dia/2.0  )/ margin;
      // printf(" T %e %e \n", t,  1 -  t*t*t*(t*(t*6 - 15) + 10)  );
      
      if (t<1.0) {
	SLICE [ i*num_x  +j ] *=    1 -  t*t*t*(t*(t*6 - 15) + 10)   ; 
      } else {
	SLICE [ i*num_x  +j ] = 0 ;
      }
    }
  }
}

void LT_correct_image( LT_infos *lt,LT_infos *lt2s, float *SLICE   ) {
  int i;
  for(i=0; i< lt->N2s; i++ ) {
    SLICE[ lt->I2s[i] ] +=  lt->C2s[i]*lt2s->gaussian_coeffs[ lt->J2s[i] ]   ;  
  }
}

void  LT_convoluzione(float *convolved, float * multisino, int  SLD,
		      int  nsigma, float *sigmas, LT_infos *lt , int direct)  {

//  printf("memseting... (direct = %d, nsigma=%d)\n", direct, nsigma);
  if(direct==1) {
    memset(convolved, 0, SLD*sizeof(float));
  } else {
    memset(multisino , 0, nsigma*SLD*sizeof(float));
  }

  int is, ipro,i,j;

  int nprojs = lt->nprojs;
  int nprojs_reb = nprojs/lt->lt_reb;

  // SLD = nprojs_reb * (nbins_reb + 6*LT_MAX_STEP_REBIN)
  int nbins_reb = lt->SLD/nprojs_reb - 6*lt->lt_max_step_reb;
  int nbins = nbins_reb * lt->lt_reb; // TODO: not accurate if nbins is not a multiple of rebinning


  //~ printf("nbins = %d, nbins_reb = %d, nprojs = %d, reb = %d\n", nbins, nbins_reb, nprojs, lt->lt_reb);
  //getchar();
  //exit(1);

  float         gkern[nbins_reb] ;
  double sum=0.0;
  
  
  for(is=0; is< nsigma; is++) {

    memset( gkern, 0, sizeof(float)*nbins_reb  );
    sum=0.0;
    float sig;
    for(i=0; i<3*(sigmas[is]/lt->lt_reb) && i < nbins_reb; i++) {
      sig = sigmas[is];
      sig /= lt->lt_reb;
      gkern[i] = exp(  -2*i*i/sig/ sig  );
      if(i) {
  gkern[nbins_reb-i]  = gkern[i];
	sum+=  gkern[i];
      }
      sum+=  gkern[i];
    }
    for(i=0 ; i<nbins_reb; i++) gkern[i] *=  M_PI/(2*nprojs)/ (sum*lt->lt_reb);
    memcpy(lt->fftwR ,   gkern,   sizeof(float) * nbins_reb ) ;
    
    fftwf_execute_dft_r2c(lt->planr2c ,  lt->fftwR     , (fftwf_complex*) lt->fftwC );
    
    for(i=0; i<nbins_reb; i++) {
      gkern[i] = creal(lt->fftwC[i])  ; 
    }
    if(1) {
      for(ipro=0; ipro< nprojs_reb; ipro++) {
	if(direct==1) {
    memcpy(lt->fftwR ,   multisino+is*SLD + ipro*nbins_reb ,   sizeof(float) * nbins_reb ) ;
	} else {
    for(i=0; i< nbins_reb; i++) {
      lt->fftwR [i] = convolved[ ipro*nbins_reb+i    ];
	  }
	}
	
	fftwf_execute_dft_r2c(lt->planr2c ,  lt->fftwR ,    (fftwf_complex*) lt->fftwC );
	
  for(i=0; i<nbins_reb; i++) {
    lt->fftwC[i] *=   gkern[i]/nbins_reb   ;
	  
	}
	
	fftwf_execute_dft_c2r(lt->planc2r , (fftwf_complex*)  lt->fftwC,  lt->fftwR      );
	if(direct==1) {
    for(i=0; i<nbins_reb; i++) {
      convolved[  ipro*nbins_reb +i    ] += lt->fftwR[i] ;
	  }
	} else {
    for(i=0; i<nbins_reb; i++) {
      multisino[ is*SLD +  ipro*nbins_reb +i    ] += lt->fftwR[i] ;
	  }
	}
      }
    } else {
      for(ipro=0; ipro< nprojs; ipro++)
	{
	if(direct==1) {
    for(i=0; i<nprojs*nbins; i++) {
	    for(j=-sigmas[is]; j<sigmas[is]; j++) {
	      convolved[  ipro*nbins +i    ] += multisino[ is*SLD +   ipro*nbins +(i+j)%nbins    ]/(2*sigmas[is]) ;
	    }
	  }
	} else {
	  for(i=0; i<nbins; i++) {
	    for(j=-sigmas[is]; j<sigmas[is]; j++) {
	      multisino[ is*SLD +  ipro*nbins +i    ] += convolved[   ipro*nbins +(i+j)%nbins    ]/(2*sigmas[is]) ;
	    }
	  }
	}
      }
    }
  }
}



double     lt_calculate_grad_sino(int  Ng,float * grad, float *  coeffs,  int   Ns , float * data, int  N,
				  int *csc_posC,  int * csc_II , float *csc_C ,
				  int *csr_posR,  int * csr_JJ , float *csr_C ,
				  float **auxs, float *multisino, int SLD , int nsigma, float *sigmas,
				  LT_infos * lt,  LT_infos * lt2s)  {
  int i,II,JJ, pos1, pos2, iproj;
  double err=0.0;
  float *diff      = auxs[0];
  float *convolved = auxs[1];
  // float *kzone     = auxs[2];

  int LT_REBINNING = lt->lt_reb;
  int nprojs_rebin = lt->nprojs / LT_REBINNING;
  int NBins  =  Ns/nsigma/nprojs_rebin           ;
  int nbins  =  NBins - 6*  lt->lt_max_step_reb    ;


  memset(diff,0, Ns/nsigma*sizeof(float));

  memset(multisino, 0, sizeof(float)* (nsigma* SLD  + lt2s->dim2s_s ) );
  
  if (data ==NULL) {
  } else {
    for(iproj=0; iproj<nprojs_rebin; iproj++) {
      for(i = 3*lt->lt_max_step_reb ; i < NBins - 3*lt->lt_max_step_reb    ; i++) {
  diff[  i + iproj*NBins ] = data[   i -  3*lt->lt_max_step_reb     + iproj*nbins];
      }
    }
    memcpy(multisino + nsigma* SLD, lt2s->slice_comp , lt2s->dim2s_s*sizeof(float)  );
  }

  for(II=0; II<Ns+lt2s->dim2s_s; II++) {
    pos1 = csr_posR[II]  ; 
    pos2 = csr_posR[II+1]  ; 
    for(i=pos1; i<pos2; i++) {
      multisino[ II] -=    csr_C[i]*coeffs[ csr_JJ[ i] ]  ; 
    }
  }

  /* if(1) { */
  /* } else { */
  /*   for(i=0; i<lt->N; i++) { */
  /*     multisino[ lt->II[i] ] +=    lt->C[i]*coeffs[ lt->JJ[ i] ]  ;  */
  /*   } */
  /* } */

  LT_convoluzione(convolved, multisino,  SLD, nsigma, sigmas, lt2s ,1) ;


  for(iproj=0; iproj<nprojs_rebin; iproj++) {
    for(i = 3*lt->lt_max_step_reb ; i < NBins - 3*lt->lt_max_step_reb    ; i++) {
      diff[  i + iproj*NBins ] += convolved[   i    + iproj*NBins];
      // deplace en LT_convoluzione :  * M_PI/(2*nprojs_rebin);
    }
  }

  for(i=0; i<Ns/nsigma; i++) err += diff[i]*diff[i]/2.0;
  for(i=+ nsigma* SLD; i<+ nsigma* SLD + lt2s->dim2s_s ; i++) err += multisino[i]*multisino[i]/2.0;
  
  LT_convoluzione(diff , multisino,  SLD, nsigma, sigmas, lt2s ,-1) ;

  memset(grad,0, sizeof(float)*Ng);
  for(JJ=0; JJ<Ng; JJ++) {
    pos1 = csc_posC[JJ]  ; 
    pos2 = csc_posC[JJ+1]  ;
    grad[JJ]=0.0;
    for(i=pos1; i<pos2; i++) {
      grad[ JJ ] -=    csc_C[i]*multisino[ csc_II[ i] ] ;
      // deplace' en LT_convoluzione : * M_PI/(2*lt->nprojs) ; 
    }
  }
  
  return err;
}

void get_customized_LTmatrices(LT_infos *lt_infos_coarse,
			       LT_infos ** lt_merged_infos__ ,
			       float * SLICE) {

  LT_infos* lt_merged_infos = (LT_infos*) calloc(1, sizeof(*lt_merged_infos));

  
  lt_merged_infos->SLD  = lt_infos_coarse->SLD  ; 
  lt_merged_infos->nprojs  = lt_infos_coarse->nprojs  ; 
  lt_merged_infos->Nsigmas  = lt_infos_coarse->Nsigmas  ; 
  lt_merged_infos->lt_max_step  = lt_infos_coarse->lt_max_step  ; 
  lt_merged_infos->Nslice  = lt_infos_coarse->Nslice  ; 
  
  int i;
  int II,JJ;


  int Nslice = lt_infos_coarse-> Nslice ;
  printf(" NSLICE SLICE in customized Nslice %d \n", Nslice);

  int totel = 0;
  for(i=0; i<  lt_infos_coarse-> N2s     ; i++ ) {
    II= lt_infos_coarse->I2s[i];
    if (  !isnan( SLICE[II]) ) totel+=1;
  } 

  lt_merged_infos->N2s     =   totel ; 
  lt_merged_infos->C2s     =  (float*) malloc(  sizeof(float) * totel )  ; 
  lt_merged_infos->I2s     =  (int*)   malloc(  sizeof(int) * totel )  ;
  lt_merged_infos->J2s     =  (int*)   malloc(  sizeof(int) * totel )  ;
    
  {
    lt_merged_infos->corre   = (int *) calloc(Nslice, sizeof(int) );
    for(i=0; i<  Nslice   ; i++ ) {
      if (  !isnan( SLICE[i]) ) {
	lt_merged_infos->corre[i]=1;
      }
    }
    int tots = 0;
    for(i=0; i< Nslice; i++) {
      tots = tots+lt_merged_infos->corre[i];
      lt_merged_infos->corre[i]=tots*lt_merged_infos->corre[i];
    }
    lt_merged_infos->slice_comp  = (float *) calloc(tots, sizeof(float) );

    for(i=0; i< Nslice; i++) {
      if( lt_merged_infos->corre[i] )  {
	lt_merged_infos->slice_comp [ lt_merged_infos->corre[i]-1 ] =     SLICE[i]  ;
      }
    }
    
    lt_merged_infos->dim2s_s =  tots  ; 
    lt_merged_infos->dim2s_g =  0     ; 

    float * forces = (float*) calloc( lt_infos_coarse->Ng, sizeof(float));

    totel = 0;
    lt_merged_infos->force_per_gaussian = 0.0;
    double de=0;
    for(i=0; i<  lt_infos_coarse-> N2s     ; i++ ) {
      II = lt_infos_coarse->I2s[i];
      JJ = lt_infos_coarse->J2s[i];
      if (  !isnan( SLICE[II]) ) {
	de = lt_infos_coarse->C2s[i] ;
	lt_merged_infos->C2s[totel]     =  de  ; 
	lt_merged_infos->I2s[totel]     =  lt_merged_infos->corre[II]-1   ;
	lt_merged_infos->J2s[totel]     =  JJ   ;
	forces[JJ] += de*de ; 
	if(  JJ  > lt_merged_infos->dim2s_g ) lt_merged_infos->dim2s_g = JJ;
	totel+=1;
      }
    }

    printf("> %d\n", lt_infos_coarse->lt_reb);
    //getchar();
    lt_merged_infos->dim2s_g += 1 ;
    for(i=0; i<  lt_infos_coarse-> Ng     ; i++ ) {
      lt_merged_infos->force_per_gaussian = max( lt_merged_infos->force_per_gaussian , forces[i]/* /(lt_infos_coarse->lt_reb  * lt_infos_coarse->lt_reb )*/ ) ;

    }
    lt_merged_infos->force_per_gaussian /= lt_infos_coarse->lt_reb; /* * lt_infos_coarse->lt_reb ; */ // <===

    free(forces);
  }

  lt_merged_infos->gaussian_coeffs = (float * ) calloc( lt_infos_coarse->Ng , sizeof(float) ) ;  

  
  printf(" FINE MERGE\n");
  
  *lt_merged_infos__ = lt_merged_infos;


  for(i=0; i<lt_merged_infos->dim2s_s ; i++) {
    lt_merged_infos->slice_comp [ i ] *= sqrt(  lt_infos_coarse-> force_per_gaussian/lt_merged_infos->force_per_gaussian  )    ;
    //~ lt_merged_infos->slice_comp [ i ] = 0.1 * sqrt(  lt_infos_coarse-> force_per_gaussian/lt_merged_infos->force_per_gaussian  )    ;
  }

  
  
}

void read_vol_slice(char *nomeout,  float * SLICEptr, long int l_start, int slice_size, int nslice_step) {
  int fd; 
  fd = open(nomeout, O_RDONLY );
  if(fd==-1) {
    fprintf(stderr, " ERROR : could not open : %s  \n", nomeout );
    exit(1);
  }
  
  int gslice;
  for(gslice=0; gslice< nslice_step; gslice++) {
    lseek( fd  ,l_start + gslice*sizeof(float)*slice_size  , SEEK_SET);
    // remarque: si iv>0 nous serons dans le cas vectoriel ce qui exclude la conicity(gslice sera 0)
    myread( fd,  SLICEptr+gslice* slice_size,   sizeof(float)*slice_size );
  }
  close(fd);
}



void read_edf_slice(char * nomeout, float * SLICEptr, int num_y, int num_x, sem_t *semaforo_ptr )  {
  int Dim_1, Dim_2;
  long int hsize;
  int datatype;
  int byteorder;
  int sizeImage;

  float cur_tmp;
  char *currentName = NULL ; 
  prereadEdfHeader( &sizeImage ,
		    &byteorder ,
		    &datatype  ,
		    &hsize     ,
		    &Dim_1     ,
		    &Dim_2     ,
		    nomeout ,
		    currentName,
		    &cur_tmp
		    
		    ) ;
  float *buffer=NULL;
  if( datatype!=FloatValue)  {
    buffer = (float*) malloc(   Dim_1* Dim_2  *sizeof(float) ) ;
  }
  read_data_from_edf( nomeout, SLICEptr,
		      NULL, FloatValue, 1, hsize,
		      0, 0,num_y,  num_x,
		      4,num_y,num_x,
		      byteorder,1, semaforo_ptr ) ;
  if(buffer) free(buffer);
}


void CCspace_precalculations( CCspace *    self,   int ncpus ) {
  int two_power;
  int dim_fft;
  float *dumf[ncpus]  ;
  fcomplex *dumfC[ncpus] ;
  
  int i;
  int num_bins ;
  int projection ;
  
  num_bins = self->reading_infos.size_s[0 +1];
  
  two_power = (int)(log((2. * num_bins - 1)) / log(2.0) + 0.9999);
  dim_fft = 1; for(i=0;i<two_power; i++) dim_fft*=2;
  /* this for extra symmetric padding with fai360 */
  dim_fft*=2;
  self->fbp_precalculated.dim_fft = dim_fft ;
  for(i=0; i<ncpus; i++) {
    sem_wait( &(self->fftw_sem));
    dumf [i]      = (float *)         fftwf_malloc( dim_fft  * sizeof(float)  );
    dumfC[i]      = (fcomplex *) fftwf_malloc( dim_fft  * sizeof(fftwf_complex )  );
    sem_post( &(self->fftw_sem) );

  }
  
  // printf( "AAAA in precalculation \n"  ) ;
  
  sem_wait(& (self->fbp_sem) );
  if( self->fbp_precalculated.FILTER==0) {
    self->fbp_precalculated.prec_gamma_is_set = 0;
    
    int y_start=0, y_end=0 ;
    int x_start, x_end ;
    int *X_STARTS, *X_ENDS;
    fftwf_plan planr2c, planc2r;
    
    X_STARTS = (int*) malloc(num_bins * sizeof(int));
    X_ENDS   = (int*) malloc(num_bins * sizeof(int));
    
    self->fbp_precalculated.Lipschitz_fbdl=-1.0f;


        
    sem_wait( &(self->fftw_sem));
    
    self->fbp_precalculated.FILTER    = (float *) fftwf_malloc((2*dim_fft) * sizeof(float));
    
    fftwf_plan_with_nthreads(1 );
    planr2c = fftwf_plan_dft_r2c_1d(  dim_fft  ,  dumf[0]  , (fftwf_complex*) dumfC[0]    ,  FFTW_MEASURE );
    planc2r = fftwf_plan_dft_c2r_1d(  dim_fft  , (fftwf_complex*) dumfC[0],    dumf[0]      ,  FFTW_MEASURE );
    sem_post( &(self->fftw_sem) );

    if( self->params.FBFILTER ==-1  || self->params.FBFILTER ==10 ) {
      for(i=0; i< dim_fft; i++ ) {
        self->fbp_precalculated.FILTER[i]=1.0;
      }
    } else if (  self->params.FBFILTER == 0   ) {
      for(i=0; i<= dim_fft/2; i++ ) {
        self->fbp_precalculated.FILTER[i]=i*2.0/dim_fft ;
      }
      for(i=(dim_fft-1);  i>(dim_fft)/2   ;   i--   ) {
        self->fbp_precalculated.FILTER[  i] = (dim_fft -  i)*2.0/dim_fft ;
      }
    }else if (  self->params.FBFILTER == 3   ) {

      for(i=0; i<= dim_fft/2; i++ ) {
        self->fbp_precalculated.FILTER[i]= fabs(  sin(i*M_PI/dim_fft) ) *2.0/M_PI;
      }
      for(i=(dim_fft-1);  i>(dim_fft)/2   ;   i--   ) {
        self->fbp_precalculated.FILTER[  i] = fabs(sin((dim_fft -  i)*M_PI/dim_fft))*2.0/M_PI ;
      }


    } else if (  self->params.FBFILTER > 4 &&  self->params.FBFILTER <=5  ) {
      float alpha = self->params.FBFILTER-4; 
      for(i=0; i<= dim_fft/2; i++ ) {
	float x = i*2.0/dim_fft ; 
        self->fbp_precalculated.FILTER[i] =  fabs(x) - alpha*x*x ;	
      }
      for(i=(dim_fft-1);  i>(dim_fft)/2   ;   i--   ) {
	float x = (dim_fft -  i)*2.0/dim_fft ; 
        self->fbp_precalculated.FILTER[  i] =   fabs(x) - alpha*x*x ;
      }
    } else if (  self->params.FBFILTER == 1  ||  self->params.FBFILTER == 2    ) {
      for(i=0; i<dim_fft; i+=1) {
        dumf[0][i]=0;
      }
      for(i=0; i<dim_fft/2; i+=2) {
        dumf[0][i]=0;
        if(i) dumf[0][dim_fft-i]=0;
      }
      for(i=1; i<dim_fft/2; i+=2) {
        dumf[0][i]= -1.0/i/i/M_PI/M_PI ;
        dumf[0][dim_fft-i]=-1.0/i/i/M_PI/M_PI ;
      }
      dumf[0][0]=1.0/4;
      fftwf_execute_dft_r2c( planr2c ,  dumf[0] , (fftwf_complex*)  dumfC[0]  );
      for(i=0; i<=dim_fft/2; i++) {
        self->fbp_precalculated.FILTER[i] = crealf(dumfC[0][i])*2 ;
        // printf("%e \n",self->fbp_precalculated.FILTER[i]  );
      }
      for(i=(dim_fft)/2+1   ;  i<(dim_fft)   ;   i++  ) {
        self->fbp_precalculated.FILTER[  i]  = crealf(dumfC[0][ dim_fft-i ])*2   ;
      }
      /* for(i=1; i<=dim_fft/2; i++) { */
      /* 	self->fbp_precalculated.FILTER[dim_fft-i]  =  dumfC[0][i]  ;       */
      /* } */
      // ( http://www.mathematica-journal.com/issue/v6i2/article/murrell/murrell.pdf)
    }
    if (  self->params.FBFILTER == 2   ) {
      /* for(i=0; i<dim_fft; i+=1) { */
      /* 	self->fbp_precalculated.FILTER[i] = 1.0/2/3.1415;  i*2.0/dim_fft   */
      /* } */
      self->fbp_precalculated.FILTER[0]= self->fbp_precalculated.FILTER[dim_fft/2]=0;
    }

    {     
      for(i=0; i<= dim_fft/2; i++ ) {
	self->fbp_precalculated.FILTER[i]   *= self->params.FBFACTORS[i];
      }
      for(i=(dim_fft-1);  i>(dim_fft)/2   ;   i--   ) {
	self->fbp_precalculated.FILTER[  i]   *=  self->params.FBFACTORS[dim_fft-i];
      }
    }


    if(self->params.SF_ITERATIONS) {
      if (self->params.verbosity > 1) puts("importing SIRT-Filter");
      // The SIRT-Filter is angle dependent. The buffer has to be re-allocated.
      free(self->fbp_precalculated.FILTER);
      self->fbp_precalculated.FILTER = (float *) calloc(dim_fft * self->reading_infos.numpjs, sizeof(float));
      // memcpy ?
      int proj;
      for (proj = 0; proj < self->reading_infos.numpjs; proj++) {
        for (i = 0; i < dim_fft; i++) {
          self->fbp_precalculated.FILTER[proj*dim_fft + i] =  self->params.SF_FILTER[proj*dim_fft + i];
        }
      }
//       FILE* fid = fopen("tmp.dat","wb");
//       fwrite(self->fbp_precalculated.FILTER, sizeof(float), dim_fft * self->reading_infos.numpjs, fid);
//       fclose(fid);
    }



    self->fbp_precalculated.planr2c =  planr2c ; 
    self->fbp_precalculated.planc2r =  planc2r ; 
    

    // r0 , r1 , r2 , . . . , rn/2 , i(n+1)/2−1 , . . . , i2 , i1


    self->fbp_precalculated.minX = (int*) malloc(self->params.num_y *sizeof(int)     );
    self->fbp_precalculated.maxX = (int*) malloc(self->params.num_y *sizeof(int)     );
    for(i=0; i< self->params.num_y   ; i++) {
      self->fbp_precalculated.maxX[i]=0;
      self->fbp_precalculated.minX[i]=1000000;
    }
    self->fbp_precalculated.axis_position_corr_s = (float *) malloc( self->reading_infos.numpjs * sizeof(float) );
    self->fbp_precalculated.cos_s                = (float *) malloc( self->reading_infos.numpjs * sizeof(float) );
    self->fbp_precalculated.sin_s                = (float *) malloc( self->reading_infos.numpjs * sizeof(float) );

    /* printf("AAAA  (self->params.nprojs_span  )*( self->params.ANGLE_BETWEEN_PROJECTIONS) -2*M_PI)  %e\n", */
    /* 	   ((self->params.nprojs_span  )*( self->params.ANGLE_BETWEEN_PROJECTIONS) -2*M_PI) ) ; */

    if((self->params.AVOIDHALFTOMO!=-1 ) && (self->params.AVOIDHALFTOMO==1  ||( self->params.DZPERPROJ==0 &&fabs(fabs((self->params.nprojs_span  )*( self->params.ANGLE_BETWEEN_PROJECTIONS)) -2*M_PI)>0.001 ))){
      int  trim=1;
      float radiusleft =  self->params.ROTATION_AXIS_POSITION ;
      float  radiusright = ((float ) num_bins ) - self->params.ROTATION_AXIS_POSITION ;


      if(self->params.ANGLE_BETWEEN_PROJECTIONS<0) {
	float tmp = radiusleft;
	radiusleft = radiusright;
	radiusright = tmp;
      }


      
      float radiussmall, x_diff ; ;
      int y;
      
      // int *minX, *maxX;
	
      if (radiusleft < radiusright) {
	radiussmall = radiusleft;
      } else {
	radiussmall = radiusright;
      }
      y_start =   self->params.ROTATION_AXIS_POSITION  -  radiusright + 1;
      y_end   =   self->params.ROTATION_AXIS_POSITION  +  radiusleft     ;
      if (y_start<0 ) y_start=0 ;
      if ( y_end >  num_bins  ) y_end = num_bins ;
	
      for (y = y_start  ; y <  y_end ; ++y) {
	if ((float ) y <    self->params.ROTATION_AXIS_POSITION     ) {
	  x_diff = sqrt(radiusright * radiusright -
			(y -self->params.ROTATION_AXIS_POSITION )*(y-self->params.ROTATION_AXIS_POSITION));
	} else {
	  x_diff = sqrt(radiusleft * radiusleft -
			(y -self->params.ROTATION_AXIS_POSITION ) *(y -self->params.ROTATION_AXIS_POSITION ));
	}
	if (x_diff> radiussmall) {
	  x_diff = radiussmall;
	}
	  
	X_STARTS[y ] = max(0 , ((int )(self->params.ROTATION_AXIS_POSITION - x_diff))  )+trim;
	X_ENDS[y ]   = min( num_bins  ,  ((int )(self->params.ROTATION_AXIS_POSITION + x_diff))    ) - trim;
      }
      self->fbp_precalculated.fai360=0;
      if (self->params.verbosity>0)  printf(" self->fbp_precalculated.fai360 non attivato \n");
    } else {
      self->fbp_precalculated.fai360=1;
      if (self->params.verbosity>0) printf(" self->fbp_precalculated.fai360 attivato \n");
    }
	
    if( self->params.AXIS_TO_THE_CENTER == 1 ) {
      self->fbp_precalculated.MOVE_X=  ( ( ((self->params.start_x) + (self->params.num_x-1)/2.0 ) ) - ( self->params.ROTATION_AXIS_POSITION ));
      self->fbp_precalculated.MOVE_Y=  ( ( ((self->params.start_y) + (self->params.num_y-1)/2.0  )) - ( self->params.ROTATION_AXIS_POSITION ));
    } else {
      self->fbp_precalculated.MOVE_X= 0;
      self->fbp_precalculated.MOVE_Y= 0;
    }
    self->fbp_precalculated.angles_per_proj=(float *) malloc( self->reading_infos.numpjs *sizeof(float) );
	
	
    for(projection=0; projection <   self->reading_infos.numpjs      ; projection++) {
      float angle ;
      int yoristart, yoriend;
      int yorigin ;
      float  axis_position_corr;
	  
      //  \**@@**/
      // -axis_corrections  angles_per_proj cos_s  sin_s   self->fbp_precalculated.axis_position_corr_s -correctionsL
      //  

  
      ////  define DXPERPROJ (3.0/2999)

      
      float shift=0;
      if( self->params.DZPERPROJ!=0)      shift= projection*self->params.DXPERPROJ;

      float corre = self->axis_corrections[projection] + shift;
      float EXT1, EXT2, XH ;
      if( self->params.do_custom_angles) {
	angle = self->params.custom_angles[projection];
      } else {
	angle =  self->params.ANGLE_OFFSET  +  projection * ( self->params.ANGLE_BETWEEN_PROJECTIONS )  ;
      }
      self->fbp_precalculated.angles_per_proj[projection]= angle;
      float cos_angle = cos(angle);
      float sin_angle = sin(angle);
	  
      self->fbp_precalculated.cos_s[projection]= cos_angle;
      self->fbp_precalculated.sin_s[projection]= sin_angle;
	  
      if( self->fbp_precalculated.fai360) {
	yoristart = 0;
	yoriend   = self->params.num_y ;
      } else {
	yoristart = y_start  ;
	yoriend   = y_end ;
      }
	  
      for(yorigin  = yoristart ; yorigin < yoriend  ; yorigin++) {
	int y;
	    
	if( self->fbp_precalculated.fai360) {
	  y=yorigin;
	} else {
	  /* y_start e y_end sono calcolati per una maschera massimale */
	  /* y e relativo alla  finestra dell user */
	  y = (yorigin + self->fbp_precalculated.MOVE_Y +corre - self->params.start_y )+100000;   /* per evitare che -0.1 sia tagliato a 0 come 0.9 */
	  y=y-100000;
	}
	    
	/* si potrebbe eventualmente togliere il 2 se tutto va bene :  ps 2 ->1*/
	if( y <0 || y >= self->params.num_y -0 ) continue;

	x_start=0;

	if( self->fbp_precalculated.fai360) {

	  float rradius ;
	  rradius = max(  self->params.ROTATION_AXIS_POSITION  ,  num_bins -   self->params.ROTATION_AXIS_POSITION    )  ;
	  {
	    float yy  = ((y + self->params.start_y    -self->fbp_precalculated.MOVE_Y     - 1.0f) + 0.5f -   self->params.ROTATION_AXIS_POSITION   );
	    if(fabs(yy)>rradius) {
	      rradius=0.0;
	    } else {
	      rradius = sqrt(rradius*rradius-yy*yy);
	    }
	  }

	  axis_position_corr = corre+( self->params.ROTATION_AXIS_POSITION ) ;
	  if( fabs(cos_angle)>1.0e-6) { /* s puo lasciare solo la parte in y */
	    EXT1 = -( (float) ( (  ( axis_position_corr +
				     ((   self->params.start_x  -self->fbp_precalculated.MOVE_X   - 1)
				      + 0.5 - self->params.ROTATION_AXIS_POSITION  ) * cos_angle -
				     ((y +  self->params.start_y    -self->fbp_precalculated.MOVE_Y     - 1)
				      + 0.5 -  self->params.ROTATION_AXIS_POSITION) * sin_angle -0.5 )  ) ))/cos_angle ;
	    EXT2 = ( num_bins- (float) ( (  ( axis_position_corr +
					      ((  self->params.start_x  -self->fbp_precalculated.MOVE_X   - 1)
					       + 0.5 -  self->params.ROTATION_AXIS_POSITION ) * cos_angle -
					      ((y +  self->params.start_y    -self->fbp_precalculated.MOVE_Y- 1)
					       + 0.5 -self->params.ROTATION_AXIS_POSITION )*sin_angle -0.5 )  ) ))/cos_angle ;
	  } else {
	    XH = ( (float) ( (  ( axis_position_corr  -
				  ((y +  self->params.start_y    -self->fbp_precalculated.MOVE_Y     - 1)
				   + 0.5 -   self->params.ROTATION_AXIS_POSITION   ) * sin_angle -0.5 )  ) )
		   );
	    if( XH<=0 || XH  >=  num_bins) continue;
	    EXT1=0;
	    EXT2=   self->params.num_x;
	  }
	  x_start = min(EXT1,EXT2)+1;
	  x_end   = max(EXT1,EXT2)-1;

	  /********************************************************************************************/
	  x_start = max(x_start ,  self->params.ROTATION_AXIS_POSITION   -rradius   - self->params.start_x +self->fbp_precalculated.MOVE_X );/* SOSPETTO */
	  x_end   = min(x_end   ,  self->params.ROTATION_AXIS_POSITION   +rradius   - self->params.start_x +self->fbp_precalculated.MOVE_X );
	  /********************************************************************************************/


	} else {
	  x_start = (X_STARTS[yorigin]-1) - self->params.start_x  + self->fbp_precalculated.MOVE_X+corre ;
	  x_end   = (X_ENDS[yorigin]-1)   - self->params.start_x  + self->fbp_precalculated.MOVE_X+corre ;
	}

	{
	  if( x_start<0) x_start=0;
	  if( x_end>=  self->params.num_x-0) x_end=  self->params.num_x -0;
	  /* anche qui se tutto va bene si puo provare ad essere piu larghi
	     x_start+=2;
	     x_end-=2; */
	}
	if(self->params.zerooffmask) {
	  self->fbp_precalculated.minX[y] = min(self->fbp_precalculated.minX[y], x_start);
	  self->fbp_precalculated.maxX[y] = max(self->fbp_precalculated.maxX[y], x_end);

	  // printf("y %d  minX %d maxX %d \n", y, self->fbp_precalculated.minX[y],self->fbp_precalculated.maxX[y]);

	}
      }

      self->fbp_precalculated.axis_position_corr_s[projection] = corre+(self->params.ROTATION_AXIS_POSITION  ) ;
    }
    /* { */
    /*   int i; */
    /*   for(i=0; i<self->params.num_y; i++) { */
    /* 	printf(" y %d min  %d max %d    \n" , i,      self->fbp_precalculated.minX[i]  ,  self->fbp_precalculated.maxX[i]     ); */
    /*   } */
    /* } */

    free(X_STARTS);
    free(X_ENDS);

    if( 2* self->params.ROTATION_AXIS_POSITION   >  num_bins ) {
      self->fbp_precalculated.prof_fact=1;
      self->fbp_precalculated.prof_shift=0;
      self->fbp_precalculated.overlapping = ( num_bins- self->params.ROTATION_AXIS_POSITION );
      if(self->fbp_precalculated.overlapping>1) self->fbp_precalculated.overlapping-=1 ;
    } else {
      self->fbp_precalculated.overlapping = (self->params.ROTATION_AXIS_POSITION );
      self->fbp_precalculated.prof_fact=-1;
      self->fbp_precalculated.prof_shift=1;
    }
    if(self->fbp_precalculated.overlapping>1) self->fbp_precalculated.overlapping-=1 ;
    {
      float corrmax=0;
      int i;
      for(i=0; i< self->reading_infos.numpjs  ; i++) {
	if(fabs(self->axis_corrections[i])>corrmax) corrmax = fabs(self->axis_corrections[i]) ;
      }
      self->fbp_precalculated.overlapping-=corrmax ;
      if(self->fbp_precalculated.overlapping<0  ) self->fbp_precalculated.overlapping=0.1 ;
    }

    self->fbp_precalculated.pente_zone  = min(   self->params.PENTEZONE , self->fbp_precalculated.overlapping);
    self->fbp_precalculated.flat_zone   = self->fbp_precalculated.overlapping - self->fbp_precalculated.pente_zone;

    if (self->params.verbosity>0)  printf(" pente_zone %e flat_zone %e overlapping %e   \n" ,  self->fbp_precalculated.pente_zone,  self->fbp_precalculated.flat_zone, self->fbp_precalculated.overlapping  ) ;

    sem_wait( &(self->gpustat_sem));
    //   self->gpu_is_apriori_usable=0;
    if( self->gpu_is_apriori_usable) {
      if(self->gpu_context==NULL && self->params.TRYGPU){
	if (self->params.verbosity>0) printf(" MO SETTO PER GPU %d\n",self->params.TRYGPU);
	void *lib_handle=NULL;
//         void* libw_handle;

	{


	  

	  lib_handle = getLibNameHandle(self->params.nome_directory_distribution   ,   "libgputomo" ) ;

          // wavelets
//           sprintf(fname,"%s/%s"   ,   self->params.nome_directory_distribution, "libwavelets.so");
//           printf(">>> %s\n", fname);
//           libw_handle = dlopen(fname, RTLD_LAZY);
	}
// 	if (libw_handle) {
//             wavelets_driver_Symbol fn_wavelets_driver;
//             fn_wavelets_driver = dlsym(libw_handle, "wavelets_driver");
//             self->gpu_context->wavelets_driver = fn_wavelets_driver;
//
//             sinofilter_driver_Symbol fn_sinofilter_driver;
//             fn_sinofilter_driver = dlsym(libw_handle, "sinofilters_driver");
//             self->gpu_context->sinofilter_driver = fn_sinofilter_driver;
//             self->wavelets_ok = 1;
//
//             if ((error = dlerror()) != NULL)  {
//                 printf("Failed to find symbol wavelets_driver or sinofilter_driver.  No Wavelets will be used \n");
//                 printf("> %s\n", error);
//             }
//         }
//         else {
//             printf("Failed to load libwavelets.so. No Wavelets utilities can be used \n");
//             self->wavelets_ok = 0;
//         }
	if (lib_handle)	{
	  char *error;
	  gpu_main_Symbol fn;
	  gpu_main_2by2_Symbol fn_2by2;
	  dfi_gpu_main_Symbol dfi_fn;
	  gpu_main_conicity_Symbol fn_coni;
	  pro_gpu_main_conicity_Symbol fn_proconi;
	  fb_dl_Symbol fn_fb_dl;
	  search_lipschitz_Symbol fn_search_lipschitz;
	  tv_denoising_fistagpu_Symbol fn_denois;
	  tv_denoising_patches_L1_Symbol fn_denois_patches_L1;
	  tv_denoising_patches_OMP_Symbol fn_denois_patches_OMP;
	  nonlocalmeans_Symbol fn_nonlocalmeans;
	  gpu_mainInit_Symbol fnInit;
	  gpu_project_Symbol  fnproject;
	  gpu_lt_fit_sino_Symbol    fn_gpu_lt_fit_sino ; 

	    
	  gpu_backproject_Symbol fn_gpu_backproj;
	  cp_driver_Symbol fn_cp_driver;
	  //    conjgrad_driver_Symbol fn_conjgrad_driver;

	  //    sirtfilter_driver_Symbol fn_sirtfilter_driver;
	  // gpu_backproject_dfi_Symbol fn_gpu_backproj_dfi;


	  fn_denois                     =  (tv_denoising_fistagpu_Symbol )  dlsym(lib_handle, "tv_denoising_fistagpu");
	  fn_denois_patches_L1          =  (tv_denoising_patches_L1_Symbol)  dlsym(lib_handle, "tv_denoising_patches_L1");
	  fn_denois_patches_OMP         =  (tv_denoising_patches_OMP_Symbol)  dlsym(lib_handle, "tv_denoising_patches_OMP");
	  fn_nonlocalmeans              =  (nonlocalmeans_Symbol)  dlsym(lib_handle, "nonlocalmeans");
	  fn                            =  (gpu_main_Symbol )  dlsym(lib_handle, "gpu_main");
	  fn_2by2                       =  (gpu_main_2by2_Symbol )  dlsym(lib_handle, "gpu_main_2by2");
	  dfi_fn                        =  (dfi_gpu_main_Symbol)  dlsym(lib_handle, "dfi_gpu_main");
	  fn_coni                       =  (gpu_main_conicity_Symbol)  dlsym(lib_handle, "gpu_main_conicity");
	  fn_proconi                    =  (pro_gpu_main_conicity_Symbol)  dlsym(lib_handle, "pro_gpu_main_conicity");
	  fn_fb_dl                      =  (fb_dl_Symbol)  dlsym(lib_handle, "fb_dl");
	  fn_search_lipschitz           =  (search_lipschitz_Symbol)  dlsym(lib_handle,"search_lipschitz");
	  fnInit                        =  (gpu_mainInit_Symbol)  dlsym(lib_handle, "gpu_mainInit");
	  fn_gpu_lt_fit_sino            =  (gpu_lt_fit_sino_Symbol)  dlsym(lib_handle, "gpu_lt_fit_sino");
	  
	  fn_gpu_backproj               =  (gpu_backproject_Symbol)  dlsym(lib_handle, "gpu_fbdl");
	  fn_cp_driver                  =  (cp_driver_Symbol) dlsym(lib_handle, "chambolle_pock_driver");
	  
	  
	  //    fn_conjgrad_driver = dlsym(lib_handle, "conjgrad_driver");

          wavelets_driver_Symbol fn_wavelets_driver;
          fn_wavelets_driver = (wavelets_driver_Symbol) dlsym(lib_handle, "wavelets_driver");
          sinofilter_driver_Symbol fn_sinofilter_driver;
          fn_sinofilter_driver = (sinofilter_driver_Symbol) dlsym(lib_handle, "sinofilters_driver");

	  // fn_gpu_backproj_dfi = dlsym(lib_handle, "dfi_gpu_main");
	  //    fn_sirtfilter_driver = dlsym(lib_handle, "sf_driver");
	  
	  
	  
	  if ((error = dlerror()) != NULL)  {
	    printf("Failed to find symbol gpu_main or gpu_mainInit or tv_denoising_fistagpu or nonlocalmeans or  tv_denoising_patches_L1  or  tv_denoising_patches_OMP  or something else .. in libgputomo.so. No Gpu will be used \n");
            printf("> %s\n", error);
	  }
	  AllocPinned_Symbol  fnAllocPinned;
	  FreePinned_Symbol   fnFreePinned  ;
	  fnAllocPinned = (AllocPinned_Symbol) dlsym(lib_handle, "AllocPinned");
	  fnFreePinned  = (FreePinned_Symbol) dlsym(lib_handle, "FreePinned");

	  {
	    lib_handle = getLibNameHandle(    self->params.nome_directory_distribution    , "libprojection"  )  ; 
	    
	  }

	  if (lib_handle)	{
	    fnproject= (gpu_project_Symbol) dlsym(lib_handle, "C_HST_PROJECT_1OVER_GPU");
	  } else {
	    printf("COULD NOT OPEN libprojection.so, forward projections, if needed will be made on CPU\n");
	    fnproject=NULL;
	  }

	  if ((error = dlerror()) != NULL)  {
	    printf("Failed to find symbol C_HST_PROJECT_1OVER_GPU in  libprojection.so. No Gpu will be used \n");
	  } else {
	    self->gpu_context = ( Gpu_Context*) malloc(sizeof(Gpu_Context));
	    self->gpu_context->void_ccspace_ptr =  (void *) self;
	    self->gpu_context->inuse=0;
	    
	    self->gpu_context->gpuctx = NULL ; 
	    
	    self->gpu_context->tv_denoising_fistagpu =  fn_denois  ;
	    self->gpu_context->tv_denoising_patches_L1  =  fn_denois_patches_L1  ;
	    self->gpu_context->tv_denoising_patches_OMP =  fn_denois_patches_OMP  ;
	    self->gpu_context->nonlocalmeans=  fn_nonlocalmeans  ;
	    
	    self->gpu_context->gpu_main_conicity =  fn_coni  ;
	    self->gpu_context->pro_gpu_main_conicity =  fn_proconi  ;
	    self->gpu_context->dfi_gpu_main =  dfi_fn  ;
	    self->gpu_context->gpu_main =  fn  ;
	    self->gpu_context->gpu_main_2by2 =  fn_2by2  ;
	    self->gpu_context->gpu_lt_fit_sino =  fn_gpu_lt_fit_sino ;
	    self->gpu_context->fb_dl =  fn_fb_dl  ;
	    self->gpu_context->gpu_backproj = fn_gpu_backproj; //PP.add
	    self->gpu_context->cp_driver = fn_cp_driver;
	    //      self->gpu_context->conjgrad_driver = fn_conjgrad_driver;
	    //      self->gpu_context->sirtfilter_driver = fn_sirtfilter_driver;


            // --
            self->gpu_context->wavelets_driver = fn_wavelets_driver;
            self->gpu_context->sinofilter_driver = fn_sinofilter_driver;
            // ----

	    self->gpu_context->search_lipschitz =  fn_search_lipschitz  ;
	    self->gpu_context->fb_dl_Lipschitz = -1.0;
	    
	    self->gpu_context->gpu_mainInit =  fnInit  ;
	    self->gpu_context->gpu_project  = fnproject;
	    
	    self->gpu_context->AllocPinned=fnAllocPinned;
	    self->gpu_context->FreePinned =fnFreePinned ;
	    
	    self->gpu_context->dim_fft = dim_fft ;
	    self->gpu_context->nprojs_span = self->params.nprojs_span;
//	    self->gpu_context->num_bins =  num_bins + self->params.patch_ep; // ???
      self->gpu_context->num_bins =  num_bins ;

	    self->gpu_context->axis_position_s  = self->fbp_precalculated.axis_position_corr_s;
	    self->gpu_context->axis_corrections    =  self->axis_corrections   ;
	    self->gpu_context->cos_s           = self->fbp_precalculated.cos_s ;
	    self->gpu_context->sin_s           = self->fbp_precalculated.sin_s ;

	    self->gpu_context->overlapping =      self->fbp_precalculated.overlapping  ;
	    self->gpu_context->pente_zone  =	self->fbp_precalculated.pente_zone   ;
	    self->gpu_context->flat_zone   =	self->fbp_precalculated.flat_zone    ;
	    self->gpu_context->prof_shift  =	self->fbp_precalculated.prof_shift   ;
	    self->gpu_context->prof_fact   =      self->fbp_precalculated.prof_fact    ;

	    self->gpu_context->fai360   =self->fbp_precalculated.fai360;

	    self->gpu_context->num_x = self->params.num_x  ;
	    self->gpu_context->num_y = self->params.num_y  ;

	    self->gpu_context->axis_position   = self->params.ROTATION_AXIS_POSITION ;


	    self->gpu_context->gpu_offset_x =  self->params.start_x  -self->fbp_precalculated.MOVE_X  ;
	    self->gpu_context->gpu_offset_y =  self->params.start_y  -self->fbp_precalculated.MOVE_Y  ;

	    self->gpu_context->FBFILTER = self->params.FBFILTER  ;
	    self->gpu_context->DFI_KERNEL_SIZE = self->params.DFI_KERNEL_SIZE  ;
	    self->gpu_context->DFI_NOFVALUES = self->params.DFI_NOFVALUES  ;
	    self->gpu_context->DFI_OVERSAMPLING_RATE = self->params.DFI_OVERSAMPLING_RATE  ;
	    self->gpu_context->DFI_R2C_MODE = self->params.DFI_R2C_MODE  ;
	    self->gpu_context->DO_PRECONDITION = self->params.DO_PRECONDITION;
	    self->gpu_context->precond_params_dl = self->params.precond_params_dl;
            self->gpu_context->USE_DFI = self->params.USE_DFI;
            self->gpu_context->USE_DFP = self->params.USE_DFP;

	    self->gpu_context->MYGPU = self->params.MYGPU ;
	    if (self->params.verbosity>0)  printf("ho settato gpu_context \n");


	    self->gpu_context->VECTORIALITY = self->params.VECTORIALITY ;
	    self->gpu_context->peso_overlap = self->params.peso_overlap ;
	    self->gpu_context->STEPFORPATCHES = self->params.STEPFORPATCHES ;
	    self->gpu_context->ITERATIVE_CORRECTIONS = self->params.ITERATIVE_CORRECTIONS ;
	    self->gpu_context->SMOOTH_PENALTY_PARAM = self->params.SMOOTH_PENALTY_PARAM ;
	    self->gpu_context->LINE_SEARCH_INIT = self->params.LINE_SEARCH_INIT ;
	    self->gpu_context->PENALTY_TYPE = self->params.PENALTY_TYPE ;
	    self->gpu_context->OPTIM_ALGORITHM = self->params.OPTIM_ALGORITHM ;
	    self->gpu_context->angles_per_proj  = self->fbp_precalculated.angles_per_proj  ;

	    self->gpu_context->JOSEPHNOCLIP  =  self->params.JOSEPHNOCLIP  ;

	    self->gpu_context->CONICITY   =  self->params.CONICITY   ;
	    self->gpu_context->CONICITY_FAN   =  self->params.CONICITY_FAN   ;
	    self->gpu_context->SOURCE_DISTANCE   =  self->params.SOURCE_DISTANCE  ;
	    if(self->params.CONICITY_FAN )  {
	      self->gpu_context->FAN_FACTOR  = 1.0/( self->params.SOURCE_DISTANCE *1.0e6/ self->params.IMAGE_PIXEL_SIZE_1 ) ;
	    } else {
	      self->gpu_context->FAN_FACTOR  = 0;
	    }
	    self->gpu_context->DETECTOR_DISTANCE   =  self->params.DETECTOR_DISTANCE   ;
	    
	    self->gpu_context->SOURCE_X    =  self->params.SOURCE_X   ;
	    self->gpu_context->SOURCE_Y    =  self->params.SOURCE_Y   ;
	    
	    self->gpu_context->tilt_psi    =  self->params.DECT_PSI   *M_PI/180.0 ;
	    self->gpu_context->tilt_tilt   =  self->params.DECT_TILT  *M_PI/180.0 ;

	    
	    self->gpu_context->CONICITY_MARGIN_UP   =  self->params.CONICITY_MARGIN_UP   ;
	    self->gpu_context->CONICITY_MARGIN_DOWN   =  self->params.CONICITY_MARGIN_DOWN    ;

	    self->gpu_context->ITER_RING_HEIGHT   =  self->params.ITER_RING_HEIGHT    ;
	    self->gpu_context->ITER_RING_SIZE   =  self->params.ITER_RING_SIZE    ;
	    self->gpu_context->LIPSCHITZFACTOR   =  self->params.LIPSCHITZFACTOR    ;
	    self->gpu_context->LIPSCHITZ_ITERATIONS   =  self->params.LIPSCHITZ_ITERATIONS    ;  //PP.add
	    self->gpu_context->RING_ALPHA   =  self->params.RING_ALPHA ;
	    self->gpu_context->RING_BETA   =  self->params.ITER_RING_BETA ;
	    self->gpu_context->NUMBER_OF_RINGS = self->params.NUMBER_OF_RINGS;
	    // Wavelets
	    self->gpu_context->W_LEVELS = self->params.W_LEVELS;
	    self->gpu_context->W_CYCLE_SPIN = self->params.W_CYCLE_SPIN;
	    self->gpu_context->W_FISTA_PARAM = self->params.W_FISTA_PARAM;
	    self->gpu_context->W_WNAME = self->params.W_WNAME;
	    self->gpu_context->W_SWT = self->params.W_SWT;
	    self->gpu_context->W_NORMALIZE = self->params.W_NORMALIZE;
      self->gpu_context->W_THRESHOLD_APPCOEFFS = self->params.W_THRESHOLD_APPCOEFFS;
      self->gpu_context->W_THRESHOLD_COUSINS = self->params.W_THRESHOLD_COUSINS;
      // Munch destriper
      self->gpu_context->FW_LEVELS = self->params.FW_LEVELS;
      self->gpu_context->FW_SIGMA = self->params.FW_SIGMA;
      self->gpu_context->FW_WNAME = self->params.FW_WNAME;
      self->gpu_context->FW_SCALING = self->params.FW_SCALING;
      self->gpu_context->FW_FILTERPARAM = self->params.FW_FILTERPARAM;
      //
      self->gpu_context->ESTIMATE_BETA = self->params.ESTIMATE_BETA;
      // Ignore angles
      self->gpu_context->do_ignore_projections= self->params.do_ignore_projections;
      self->gpu_context->ignore_angles = self->params.ignore_angles;


      // Fluo
      self->gpu_context->FLUO_SINO = self->params.FLUO_SINO;
      self->gpu_context->FLUO_ITERS = self->params.FLUO_ITERS;
	    // Sirt Filter
	    self->gpu_context->SF_ITERATIONS = self->params.SF_ITERATIONS;
	    self->gpu_context->SF_SAVEDIR = self->params.SF_SAVEDIR;
	    self->gpu_context->SF_LAMBDA = self->params.SF_LAMBDA;
	    //
	    self->gpu_context->DATA_IS_FILTERED = self->params.DATA_IS_FILTERED;
	    // TV+L2
	    self->gpu_context->BETA_L2 = self->params.BETA_L2;
      // positivity constraint
      self->gpu_context->ITER_POS_CONSTRAINT = self->params.ITER_POS_CONSTRAINT;
	    	    
//_______
	    self->gpu_context->verbosity = self->params.verbosity;


	    self->gpu_context->tot_proj_num_list  = self->reading_infos.tot_proj_num_list  ; 
	    self->gpu_context->numpjs   =   self->reading_infos.numpjs;
	    self->gpu_context->DZPERPROJ       =  self-> params.DZPERPROJ;

	    if ( self->params.LT_pars  )  {
	      self->gpu_context->lt_infos_coarse    = self->params.LT_pars->lt_infos_coarse ;
	    } else {
	      self->gpu_context->lt_infos_coarse =  NULL  ; 
	    }
	    

	    self->gpu_context->gpu_mainInit(	self->gpu_context,
						self->fbp_precalculated.FILTER
						)  ;

	    if (self->params.verbosity>1) printf("gpu_mainInit OK  \n");

	  }
	} else {
	  printf("Failed to load libgputomo.so. No Gpu will be used \n");
	  self->gpu_is_apriori_usable=0;
	}
      } else {
	// printf("GPU will not be used \n");
      }
    }
    sem_post( &(self->gpustat_sem));
  }

  sem_post( &(self->fbp_sem));
  if (self->params.verbosity>1) printf(" precalculation OK  \n");


  for(i=0; i<ncpus; i++) {
    sem_wait( &(self->fftw_sem));
    fftwf_free(dumf[i]);
    fftwf_free(dumfC[i]);
    sem_post( &(self->fftw_sem) );
  }
}


void *CCspace_memoryCache( CCspace *self,  int aid, int   ibunch, int  npbunches,  long int nmemb, long int sizem) {
  int ai = aid*npbunches+ibunch;
  if( self->allocations[ai]) {
    long int new_size =  nmemb * sizem ;
    void *ptr;
    if( new_size > self->allocations_sizes[ai]) {
      ptr = self->allocations      [ai];

      free(ptr);
      ptr = calloc(   nmemb,  sizem      ) ;
      assert(ptr);
      self->allocations      [ai] = ptr               ;
      self->allocations_sizes[ai] = nmemb * sizem     ;
      return ptr;
    } else {
      ptr = self->allocations      [ai];
    }
    return ptr;
  } else {
    void *ptr = calloc(   nmemb,  sizem      ) ;
    assert(ptr);
    self->allocations      [ai] = ptr               ;
    self->allocations_sizes[ai] = nmemb * sizem     ;
    return ptr;
  }
  

}




void CCspace_Sino_2_Slice( CCspace *    self, float * dataA, int nslices, int nslices_data,int Nfirstslice, 
			   int ncpus , int data_start, int STEAM_DIRECTION,
			   int npj_offset, int ibunch, int npbunches) {

  // printf( "AAAA chiamo precalculation \n"  ) ;

  CCspace_precalculations(  self,   ncpus ) ;

  // si on utilise des dictionnaires epais ( plus d' une slice ) on en tient compte
  long int patch_w = 2*self->params.patch_ep +1;
  long int nslice_data_granularity =  patch_w;
  long int nslice_granularity      =  patch_w;
  long int nslice_workppj_granularity      =  1;
  long int nslice_step                    = 1;

  long int VECTORIALITY = self->params.VECTORIALITY ;


    int try_2by2 = 0;
    if( STEAM_DIRECTION==1 && VECTORIALITY==1 && !self->params.OUTPUT_SINOGRAMS &&
	!(self->params.DO_RING_FILTER &&  self->params.RING_FILTER_KIND  == RING_Filter_THRESHOLDED_ID) &&
	self->params.CONICITY==0 &&
	!self->params.ITERATIVE_CORRECTIONS  &&
	!self->params.LT_pars &&
	self->params.FBFILTER != 10 &&
	self->gpu_context
	) {
      try_2by2 = 1;
    }
    

  
  // Aussi si on est en conicity
  if(self->params.CONICITY) {

    nslice_granularity      =   nslices; // la routine conique procede par blocks 3D
                                         // pour maximiser la cache hit
    nslice_step =   nslices ;

    nslice_data_granularity =   nslices_data ;
    // il n' y a pas de raison de se soucier de  patch_w
    // parce que la reg. sera fait en exterieur

    nslice_workppj_granularity = nslice_data_granularity;

    // toutes les routines coniques font de l' interpolation bilineaire
    self->params.OVERSAMPLING_FACTOR=1;

    if( VECTORIALITY==2) {
      printf("VECTORIALITY==2 not yet allowed with conicity\n");
      exit(1);
    }
    /* if(self->fbp_precalculated.fai360) { */
    /*   printf("HALF TOMO  ------- not yet allowed with conicity\n"); */
    /*   exit(0); */
    /* } */

    /* CCspace_Sino_2_Slice_conicity(   self,  dataA,  nslices, nslices_data,  Nfirstslice,  ncpus , data_start,  STEAM_DIRECTION ); */
    /* return; */
  }
  // printf( " QUI 00 \n") ;
  char nomeout[10000];
  //  char nomeoutbis[10000];
  int two_power;
  long int dim_fft;
  float *dumf  ;
  fcomplex *dumfC ;

  int i;
  long int num_bins ;
  long int projection ;
  float Sino_Sum=0;
  // int blockslice=      self->params.num_y * self->params.num_x   ;

  num_bins = self->reading_infos.size_s[0 +1];

  two_power = (int)(log((2. * num_bins - 1)) / log(2.0) + 0.9999);
  dim_fft = 1; for(i=0;i<two_power; i++) dim_fft*=2;
  /* this for extra symmetric padding with fai360 */
  dim_fft*=2;

  sem_wait( &(self->fftw_sem));
  dumf       = (float *)         fftwf_malloc( dim_fft  * sizeof(float)  );
  dumfC     = (fcomplex *) fftwf_malloc( dim_fft  * sizeof(fftwf_complex )  );
  sem_post( &(self->fftw_sem) );

  LT_infos  *lt_merged_infos=NULL;
  LT_infos  *lt_infos_coarse=NULL;

  float * SLICE_lt = NULL ;
  int lt_dsq = 0;

  float * SINOGRAMMA=NULL, *SLICEold=NULL,  * SINOGRAMMA_LT = NULL ;

  
  if (self->params.LT_pars==NULL   ) {
  } else {
    SINOGRAMMA_LT = (float *) calloc(  self->params.nprojs_span *num_bins, sizeof(float)    );

    assert( VECTORIALITY==1 ) ;
    assert( self->params.CONICITY==0) ; 

    lt_dsq = self->params.num_x -2*self->params.LT_pars->LT_MARGIN ;


    printf(" LT ALLOC SLICE_lt con dsq %d nslice %d \n", lt_dsq, lt_dsq*lt_dsq);
    SLICE_lt = (float *) calloc(   lt_dsq*lt_dsq  , sizeof(float)  ) ;
    
    if(self->params.EDFOUTPUT ) {
      sprintf(nomeout,self->params.LT_pars->LT_KNOWN_REGIONS_FILE,   Nfirstslice+ ((int)self->params.SOURCE_Y) );
      printf(" LEGGO %d %d \n", lt_dsq, lt_dsq); // debug
      read_edf_slice( nomeout, SLICE_lt , lt_dsq, lt_dsq , &(self->filereading_sem) );
      printf(" LEGGO %d %d OK\n", lt_dsq, lt_dsq);
    }  else {
      sprintf(nomeout,"%s",self->params.LT_pars->LT_KNOWN_REGIONS_FILE);
      read_vol_slice(nomeout,  SLICE_lt,
		     ((long int)(Nfirstslice - self->params.first_slices_2r[ 0 ]   ))*sizeof(float)*lt_dsq*lt_dsq,
		     lt_dsq*lt_dsq , 1) ;
    }
  }
  
  {
    float *WORK_perproje,  *SLICE,  *SLICEcalc, *data, *data_orig;
    // int SLICEpinned;
    float *WORK[self->params.nprojs_span*(1+try_2by2) ], *OVERSAMPLE, *WORKbis ;
    
    float cpu_offset_x, cpu_offset_y;
    long int islice ;
    
    if(self->params.CONICITY==0) {
      // WORK_perproje =   (float*)calloc( (nslice_workppj_granularity+try_2by2)  * (self->params.nprojs_span  *3+3)*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))  ;

      WORK_perproje =   (float*) CCspace_memoryCache( self,  7,   ibunch, npbunches,  (nslice_workppj_granularity+try_2by2)  * (self->params.nprojs_span  *3+3)*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))  ;
      
      memset(  WORK_perproje ,  0 ,nslice_workppj_granularity*   3*(self->params.nprojs_span+1)*(num_bins)*self->params.OVERSAMPLING_FACTOR*sizeof(float )         );
      WORK_perproje=WORK_perproje+(1+try_2by2)*(num_bins)*self->params.OVERSAMPLING_FACTOR;
      
    } else {
      // WORK_perproje =   (float*)calloc( nslice_workppj_granularity  * (self->params.nprojs_span  )*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))  ;
      WORK_perproje =   (float*) CCspace_memoryCache( self,  8,   ibunch, npbunches,   nslice_workppj_granularity  * (self->params.nprojs_span  )*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))  ;


      memset(  WORK_perproje ,  0 ,nslice_workppj_granularity*   (self->params.nprojs_span)*(num_bins)*self->params.OVERSAMPLING_FACTOR*sizeof(float )         );
    }
    
    // WORK OVERSAMPLE WORKbis
    // WORK[0 ] =   (float*)  calloc( (1+try_2by2)* self->params.nprojs_span*  (dim_fft), sizeof(float )  )  ;
    WORK[0 ] = (float*) CCspace_memoryCache( self,  9,   ibunch, npbunches, (1+try_2by2)* self->params.nprojs_span*  (dim_fft), sizeof(float )  )  ;
    
    // OVERSAMPLE  =   (float*)calloc( 3*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))   ;
    OVERSAMPLE = (float*) CCspace_memoryCache( self,  10, ibunch, npbunches,  3*(num_bins)*self->params.OVERSAMPLING_FACTOR, sizeof(float ))   ;

    
    memset(  OVERSAMPLE   , 0,   3*(num_bins)*self->params.OVERSAMPLING_FACTOR * sizeof(float )  );
    
    // WORKbis  =   (float*)  calloc((1+try_2by2)*(num_bins), sizeof(float )  ) ;
    WORKbis  =  (float*) CCspace_memoryCache( self,  11, ibunch, npbunches,(1+try_2by2)*(num_bins), sizeof(float )  ) ;
    
    // memset(  WORKbis   , 0,   (num_bins)* sizeof(float ) );
    for(projection=0; projection <    (1+try_2by2)*self->params.nprojs_span       ; projection++) {
      WORK   [projection]  =   WORK[0 ]  + projection*dim_fft ;
    }
    
    /* ----------------------------------------------------------------------------------- */
    
    // SLICEcalc =  (float*)calloc( (nslice_granularity+try_2by2) * VECTORIALITY* self->params.num_x * self->params.num_y , sizeof(float )) ;

    SLICEcalc = (float*) CCspace_memoryCache( self,  0,   ibunch, npbunches,   (nslice_granularity+try_2by2) * VECTORIALITY* self->params.num_x * self->params.num_y , sizeof(float ));
    
    if(self->params.CONICITY==1  && STEAM_DIRECTION==-1 )  {
      //  SLICE     =  (float*)calloc((nslice_granularity+2) * VECTORIALITY* self->params.num_x * self->params.num_y ,sizeof(float )) ;

      SLICE     = (float*) CCspace_memoryCache( self,  1,   ibunch, npbunches,
						(nslice_granularity+2) * VECTORIALITY* self->params.num_x * self->params.num_y ,sizeof(float ));
    
      
      memset(SLICE, 0, (nslice_granularity+2) * VECTORIALITY* self->params.num_x * self->params.num_y * sizeof(float )   );
      SLICE = SLICE +   VECTORIALITY* self->params.num_x * self->params.num_y  ;
    } else {
      // SLICE     =  (float*)calloc((nslice_granularity+try_2by2) * VECTORIALITY* self->params.num_x * self->params.num_y , sizeof(float )) ;
      SLICE     = (float*) CCspace_memoryCache( self,  2,   ibunch, npbunches,
						(nslice_granularity+try_2by2) * VECTORIALITY* self->params.num_x * self->params.num_y , sizeof(float )) ;
						
    }
    
    // memset(SLICEcalc,0, self->params.num_x * self->params.num_y  *sizeof(float) ); // sara scritta per pezzi dentro
    // memset(SLICE,0, self->params.num_x * self->params.num_y  *sizeof(float) ); // sara scritta per pezzi dentro

    // data =  (float*)calloc(       (nslice_data_granularity+try_2by2) * VECTORIALITY* self->params.nprojs_span  * num_bins , sizeof(float )) ;

    data = (float*) CCspace_memoryCache( self,  3,   ibunch, npbunches, (nslice_data_granularity+try_2by2) * VECTORIALITY* self->params.nprojs_span  * num_bins , sizeof(float )) ;
    // memset(data, 0,  nslice_data_granularity * VECTORIALITY* self->params.nprojs_span  * num_bins * sizeof(float )  );


    // {
    // FILE *f=fopen("sino.dat","w");
    //  fwrite( dataA , self->params.nprojs_span  * num_bins ,4   , f);
    //  fclose(f);
    // }

    if( self->params.CONICITY==0 )  {
      // data_orig =  (float*)calloc(  (nslice_data_granularity+try_2by2) *  VECTORIALITY* self->params.nprojs_span  * num_bins ,sizeof(float )) ;
      data_orig = (float*) CCspace_memoryCache( self,  4, ibunch, npbunches,  (nslice_data_granularity+try_2by2) *  VECTORIALITY* self->params.nprojs_span  * num_bins ,sizeof(float )) ;
    } else {
      data_orig = data ; 
    }
    
    cpu_offset_x=  self->params.start_x  -self->fbp_precalculated.MOVE_X ;
    cpu_offset_y=  self->params.start_y  -self->fbp_precalculated.MOVE_Y ;
    int oversampling = self->params.OVERSAMPLING_FACTOR ;


    if(self->params.ITERATIVE_CORRECTIONS   ||
       (self->params.DO_RING_FILTER  &&   self->params.RING_FILTER_KIND  == RING_Filter_THRESHOLDED_ID)
       || STEAM_DIRECTION==-1
       ){
      if( self->params.num_y!= self->params.num_x || self->params.AXIS_TO_THE_CENTER != 1 ) {
        printf("ERROR : when using forward projection  self->params.num_y== self->params.num_x is required, and also  params.AXIS_TO_THE_CENTER=='Y'   \n"  ) ;
        printf("self->params.num_y,  self->params.num_x %d %d\n",self->params.num_y,  self->params.num_x  );
        exit(1);
      }
      
      // SINOGRAMMA = (float *) calloc( max(patch_w, nslice_data_granularity)* VECTORIALITY* self->params.nprojs_span *num_bins, sizeof(float )    );
      SINOGRAMMA = (float*) CCspace_memoryCache( self,  5, ibunch, npbunches,max(patch_w, nslice_data_granularity)* VECTORIALITY* self->params.nprojs_span *num_bins, sizeof(float )    );
      
      int dimslice= self->params.num_x ;
      // SLICEold = (float *) calloc(  nslice_granularity * VECTORIALITY*  dimslice * dimslice , sizeof(float )  );

      SLICEold =   (float*) CCspace_memoryCache( self,  6,   ibunch, npbunches,nslice_granularity * VECTORIALITY*  dimslice * dimslice , sizeof(float )  );
      memset(SLICEold, 0, nslice_granularity * VECTORIALITY* sizeof(float ) * dimslice * dimslice  );

    }
  
    
    // ------------------------------------------------------------------------------------

    int ISLICE_end = (nslices- (patch_w-1));

    int use2by2 = 0;
    for(islice=0; islice< ISLICE_end; islice+=nslice_step+try_2by2* use2by2) {

      int do_2by2 = 0;
      if(STEAM_DIRECTION==1) {

        if(VECTORIALITY==1) {
	  
	  if (self->params.verbosity > 10) printf(" copio %ld %ld  npjs %d %p %p\n",
						  islice*self->params.nprojs_span*num_bins,
						  nslice_data_granularity * self->params.nprojs_span*num_bins,
						  self->params.nprojs_span, data_orig, dataA);

	  if(use2by2 && try_2by2 && islice+1 < ISLICE_end) {
	    memcpy(data_orig , dataA +  islice*self->params.nprojs_span*num_bins,    // nslice_data_granularity *
		   (nslice_data_granularity+try_2by2) * self->params.nprojs_span*num_bins*sizeof(float) );
	    do_2by2 = 1;
	  } else {
	    memcpy(data_orig , dataA +  islice*self->params.nprojs_span*num_bins,    // nslice_data_granularity *
		   nslice_data_granularity * self->params.nprojs_span*num_bins*sizeof(float) );
	  }
	  
	  
        } else {
          assert(self->params.CONICITY==0); // avec conicity les regularisations sont faites en externe
          long int iangle, ibin;
          for(iangle=0; iangle<self->params.nprojs_span; iangle++) {
	    // int npj_absolute  =self->reading_infos.proj_num_list[iangle] + npj_offset ;
	    // int npj_absolute  =self->reading_infos.proj_num_list[iangle]  ;
	    // if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
	    // continue;
	    // }
	    //   printf(">> self->fbp_precalculated.angles_per_proj[npj_absolute]  %d %e \n" , npj_absolute, self->fbp_precalculated.angles_per_proj[npj_absolute] );

	    // float cc = cos( self->fbp_precalculated.angles_per_proj[npj_absolute] );
	    //float ss = sin( self->fbp_precalculated.angles_per_proj[npj_absolute] );
            float cc = cos( self->fbp_precalculated.angles_per_proj[ iangle  ] );
            float ss = sin( self->fbp_precalculated.angles_per_proj[ iangle ] );
            long int jslice;
            long int offset1=0;
            long int offset2=0;
            for(jslice=islice; jslice<islice+patch_w; jslice++) {

              offset1 =   (jslice-islice) *num_bins*VECTORIALITY * self->params.nprojs_span;
              offset2 =  offset1 +  patch_w *num_bins*(VECTORIALITY-1) * self->params.nprojs_span;

              for(ibin=0; ibin<num_bins; ibin++) {

                data_orig[offset1 + iangle*num_bins+ibin] = -dataA [ (jslice*self->params.nprojs_span+iangle)*num_bins +ibin ]*cc;
                data_orig[offset2 + iangle*num_bins+ibin] =  dataA [ (jslice*self->params.nprojs_span+iangle)*num_bins +ibin ]*ss;
              }
            }
          }
        }
      } else {
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        memset(data_orig , 0, nslice_data_granularity*self->params.nprojs_span*num_bins*sizeof(float) );
        if(VECTORIALITY==2) {
          assert(self->params.CONICITY==0); // avec conicity les regularisations sont faites en externe
          memset(data_orig +self->params.nprojs_span*num_bins, 0, self->params.nprojs_span*num_bins*sizeof(float) );
        }
        if(self->params.EDFOUTPUT ) {
          if(VECTORIALITY==1) {
            int gslice=0;
            for(gslice=0; gslice<nslice_step; gslice++) {
	      sprintf(nomeout,self->params.OUTPUT_FILE,   Nfirstslice+islice+gslice  + ((int)self->params.SOURCE_Y) ); 
	      read_edf_slice( nomeout, SLICE+gslice*self->params.num_y*self->params.num_x, self->params.num_y, self->params.num_x , &(self->filereading_sem) );
            }
          } else {
            assert(self->params.CONICITY==0);

            sprintf(nomeout,self->params.OUTPUT_FILE, "c",  Nfirstslice+islice);
	    read_edf_slice( nomeout, SLICE , self->params.num_y, self->params.num_x , &(self->filereading_sem) );

            sprintf(nomeout,self->params.OUTPUT_FILE, "s",  Nfirstslice+islice);
	    read_edf_slice( nomeout, SLICE+ self->params.num_y*self->params.num_x , self->params.num_y, self->params.num_x , &(self->filereading_sem) );
          }
        } else {
          int iv;
          for(iv=0; iv<VECTORIALITY; iv++ ){
            if( VECTORIALITY==1) {
              sprintf(nomeout,"%s",self->params.OUTPUT_FILE);
            } else {
              assert(self->params.CONICITY==0);
              if(iv==0) sprintf(nomeout,self->params.OUTPUT_FILE,"c");
              if(iv==1) sprintf(nomeout,self->params.OUTPUT_FILE,"s");
            }
	    
	    
	    read_vol_slice(nomeout,  SLICE+(iv)* self->params.num_y*self->params.num_x,
			   ((long int)(Nfirstslice+islice - self->params.first_slices_2r[ 0 ]   ))*sizeof(float)*self->params.num_y*self->params.num_x,
			   self->params.num_y*self->params.num_x , nslice_step) ;
          }
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      }
    
      if(self->params.OUTPUT_SINOGRAMS) {
        assert(self->params.CONICITY==0); // on ne peut pas isoler un sinogramme de son contexte volumetrique
        sprintf(nomeout,self->params.OUTPUT_FILE,  Nfirstslice+islice );
        write_data_to_edf(data_orig , self->params.nprojs_span, num_bins  , nomeout);
        continue;
      } 
      int ivolta;
      
      if(STEAM_DIRECTION==1) {

        memset(SLICEcalc,0,(nslice_granularity+try_2by2)* VECTORIALITY*self->params.num_x * self->params.num_y  *sizeof(float) ); // sara scritta per pezzi dentro
	memset(SLICE    ,0,(nslice_granularity+try_2by2)* VECTORIALITY*self->params.num_x * self->params.num_y  *sizeof(float) ); // sara scritta per pezzi dentro
	
	if( self->params.CONICITY==0 )  {
	  memcpy(data,data_orig  , (nslice_data_granularity+try_2by2)* VECTORIALITY*self->params.nprojs_span  * num_bins * sizeof(float ));
	}


	// printf( " QUI 2 %d  %d %d\n", self->params.DO_RING_FILTER ,  self->params.RING_FILTER_KIND, RING_Filter_THRESHOLDED_ID  ) ;
        if( self->params.DO_RING_FILTER &&  self->params.RING_FILTER_KIND  == RING_Filter_THRESHOLDED_ID  ) {
          assert(self->params.CONICITY==0); // ce type d 'operations, pour conicity, doivent etre faite a l 'exterieur
          if(VECTORIALITY==2) {
            printf("ERROR : VECTORIALITY==2 CANNOT BE USED WITH RING_Filter_THRESHOLDED\n");
            fprintf(stderr, "ERROR : VECTORIALITY==2 CANNOT BE USED WITH RING_Filter_THRESHOLDED\n");
            exit(1);
          }

          int gslice;  // la boucle sur gslice ca sert dans le cas de patch_w>1
          for(gslice=0; gslice<nslice_granularity; gslice++)   {
            int offset =  gslice*self->params.nprojs_span  * num_bins ;
            memset(SLICEcalc,0, self->params.num_x * self->params.num_y  *sizeof(float) );
            Sino_Sum = rec_driver(self,  WORK,WORKbis,    SLICEcalc    ,  num_bins,  dim_fft,
                                  dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
                                  data + offset,
                                  ncpus   , cpu_offset_x, cpu_offset_y ,1 ,0 , NULL,
				  npj_offset,0);  // 1 = precondition

            float threshold =   ((RING_Filter_PARA_struct*) self->params.RING_FILTER_PARA)->threshold;
            int dimslice= self->params.num_x ;
            memset(SLICE,0, self->params.num_x * self->params.num_y  *sizeof(float) );
            memset(SINOGRAMMA,0,self->params.nprojs_span  * num_bins *sizeof(float) );
            spills_over(  SLICEcalc, self->params.num_y,  self->params.num_x,
                          threshold , SLICE    );

            pro_driver(self, num_bins,self->fbp_precalculated.angles_per_proj,
                       SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y );

            for( i=0; i< self->params.nprojs_span *num_bins ; i++) {
              data[i+offset]=data_orig[i+offset]-SINOGRAMMA[i];
            }

            CCspace_RING_Filter_implementation(self, data+offset,
                                               (RING_Filter_PARA_struct*) self->params.RING_FILTER_PARA ,
                                               1 , self->params.nprojs_span, num_bins , ncpus, NULL ) ;

            for( i=0; i< self->params.nprojs_span *num_bins ; i++) {
              data_orig[i+offset] += data[i+offset]-  (data_orig[i+offset]-SINOGRAMMA[i]);
            }
            memcpy(data+offset,data_orig+offset  ,self->params.nprojs_span  * num_bins * sizeof(float ));
          }
        }
      }
      
      if (self->params.FW_LEVELS > 0 || fabsf(self->params.FW_SCALING) > 1e-7) { // Fourier-Wavelets Sinogram de-striper (Munch filter)
        sem_wait( &(self->gpustat_sem));
	//         self->gpu_context->fw_plans_ok = 0;

	{
	  int k=0;
	  for( k=0; k< max(1+try_2by2, nslice_data_granularity) ; k++) {
	    self->gpu_context->sinofilter_driver(self->gpu_context, data + k*self->params.nprojs_span  * num_bins);
	  }
	}
        sem_post( &(self->gpustat_sem));
      }
      
      if(!self->params.ITERATIVE_CORRECTIONS  || STEAM_DIRECTION==-1) { //Filtered backprojection
        // non iterative case or forward projection ( STEAM_DIRECTION == -1 )
        if(STEAM_DIRECTION==1) {
          if(self->params.CONICITY==0) {
            // and this will be all
            rec_driver(self,  WORK,WORKbis,    SLICE    ,  num_bins,  dim_fft,
                       dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
                       data, ncpus   , cpu_offset_x, cpu_offset_y ,1+1  // du_precondition==2 signifie que si nous sommes
                                                                        // en geometrie helicoidal il faudra surveiller le range 
                                                                        // valable ou il n 'y a pas des NANs
		       ,0 , NULL,
		       npj_offset , do_2by2 );  // 1 = precondition (fbp)


	    
	    if (self->params.LT_pars   ) { // Local tomo

	      int LT_REBINNING = self->params.LT_pars->LT_REBINNING;
	      assert( VECTORIALITY==1 ) ;
	      assert( self->params.CONICITY==0) ; 
	     

	      
	      LT_apodize( SLICE, lt_dsq, self->params.num_x, self->params.LT_pars->LT_MARGIN  ) ;

	      printf("LT apodize OK \n");

	      
	      /* write_data_to_edf( SLICE ,// debug */
	      /* 			 self->params.num_y, self->params.num_x   , */
	      /* 			 "prima_rec_apodized.edf"); */

	      /* write_data_to_edf( SLICE_lt , */
	      /* 			 lt_dsq, lt_dsq  , */
	      /* 			 "known.edf"); */
	
	      printf("LT estraggo errore \n");
	      int i,j;
	      for(i=0; i< lt_dsq; i++) {
		for(j=0; j< lt_dsq; j++) {
		  SLICE_lt[i*lt_dsq+j]  -=  SLICE[ (  i + self->params.LT_pars->LT_MARGIN )*self->params.num_x      +(j+ self->params.LT_pars->LT_MARGIN) ] ; 
		}
	      }
	      
	      write_data_to_edf( SLICE_lt ,// debug
				 lt_dsq, lt_dsq  ,
				 "known_error.edf");
	      
	      printf(" faccio proiezione %p \n", SINOGRAMMA_LT);
	      
	      int dimslice= self->params.num_x ;
	      memset(SINOGRAMMA_LT,0,(self->params.nprojs_span)  * (num_bins) *sizeof(float) ); // Larger than necessary ; it is then rebinned after projection and data substraction

	      printf("LT settata memoria \n");
	      
	      pro_driver(self, num_bins,self->fbp_precalculated.angles_per_proj,
			 SINOGRAMMA_LT, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y );


	      write_data_to_edf( SINOGRAMMA_LT ,// debug
				 self->params.nprojs_span , num_bins  ,
				 "SINOGRAMMA_LT.edf");

	      write_data_to_edf( data ,// debug
				 self->params.nprojs_span , num_bins  ,
				 "data.edf");
	      
	      printf("LT proiezione fatta \n");

	      printf("LT vado in customized \n");

	      self->params.LT_pars->lt_infos_coarse->lt_reb = LT_REBINNING;
	      get_customized_LTmatrices(self->params.LT_pars->lt_infos_coarse,
					&lt_merged_infos ,  SLICE_lt );
	      
	      printf("LT calcolo plans \n");


	      lt_infos_coarse = self->params.LT_pars->lt_infos_coarse;
	      
	      sem_wait( &(self->fftw_sem));
	      if(lt_merged_infos->fftwR == NULL) {
		fftwf_plan_with_nthreads(1 );
		lt_merged_infos->fftwR     = (float *)         fftwf_malloc(  lt_merged_infos->SLD/ (lt_merged_infos->nprojs/LT_REBINNING)  * sizeof(float)  );
		lt_merged_infos->fftwC     = (fcomplex *) fftwf_malloc(  lt_merged_infos->SLD/ (lt_merged_infos->nprojs/LT_REBINNING)  * sizeof(fftwf_complex )  );
		lt_merged_infos->planr2c = fftwf_plan_dft_r2c_1d(   lt_merged_infos->SLD/ (lt_merged_infos->nprojs/LT_REBINNING)          , lt_merged_infos->fftwR    , (fftwf_complex*) lt_merged_infos->fftwC    ,  FFTW_MEASURE );
		lt_merged_infos->planc2r = fftwf_plan_dft_c2r_1d(   lt_merged_infos->SLD/ (lt_merged_infos->nprojs/LT_REBINNING)   , (fftwf_complex*)lt_merged_infos->fftwC ,   lt_merged_infos->fftwR  ,  FFTW_MEASURE );
	      }
	      sem_post( &(self->fftw_sem) );

	      for( i=0; i< self->params.nprojs_span *num_bins ; i++) {
		SINOGRAMMA_LT[i] = data[i]- SINOGRAMMA_LT [i];
	      }

	      // SINOGRAMMA_LT "rebinning" (subsampling for now)
	      int num_bins_reb = num_bins / LT_REBINNING;
	      {
		int rebin = LT_REBINNING;
		//float tmp = 0.0f;
		int x, y;//, kx, ky;
		for (y = 0; y < self->params.nprojs_span/rebin; y++) {
		  for (x = 0; x < num_bins_reb; x++) {

		    /*
		    // sum on (rebin)^2 square
		    for (ky = 0; ky < rebin; ky++) {
		    for (kx = 0; kx < rebin; kx++) {
		    tmp += SINOGRAMMA_LT[(y*rebin + ky)*num_bins + (x*rebin + kx)];
		    }
		    }
		    tmp /= (rebin*rebin);
		    SINOGRAMMA_LT[y*num_bins_reb + x] = tmp;
		    */
		    SINOGRAMMA_LT[y*num_bins_reb + x] = SINOGRAMMA_LT[(rebin*y)*num_bins + (x*rebin)];
		  }
		}

		/*
		  for (int i = 0; i < num_bins_reb * (self->params.nprojs_span/rebin); i++) {
		  SINOGRAMMA_LT[i] = SINOGRAMMA_LT[rebin*i];
		  }
		*/
		// memset the other elements of the sinogram
		for (i = num_bins_reb * (self->params.nprojs_span/rebin); i < num_bins*self->params.nprojs_span; i++) {
		  SINOGRAMMA_LT[i] = 0;
		}
	      }

	                            
	      LT_fit_sino(self, lt_infos_coarse, lt_merged_infos, SINOGRAMMA_LT , self->params.nprojs_span ,  num_bins  ) ;

	      printf("LT correct image \n");

	      

	      for(i=0; i< lt_dsq; i++) {
		for(j=0; j< lt_dsq; j++) {
		  SLICE_lt[i*lt_dsq+j]=SLICE[(i+self->params.LT_pars->LT_MARGIN)*self->params.num_x+(j+self->params.LT_pars->LT_MARGIN)]; 
		}
	      }

	      LT_correct_image( lt_infos_coarse,  lt_merged_infos,   SLICE_lt   );
	      
	      free_LT_infos(lt_merged_infos,self) ; 
	    }
          } else {
            if (self->params.verbosity>0)  printf( "CHIAMO CONIC DRIVER  \n") ;

            /* { */
            /*   int i; */
            /*   int n = nslices_data*self->params.nprojs_span  * num_bins; */
            /*   double sum; */
            /*   float * t=data; */
            /*   for(i=0; i< n ; i++) { */
            /* 	sum+= t[i];  */
            /*   } */
            /*   printf("  sum %e\n", sum); */
            /* } */


	    // da estendere alle altre recostruzioni  in seguito
	    // memset(  WORK_perproje ,  0 ,nslice_workppj_granularity*   (self->params.nprojs_span)*(num_bins)*self->params.OVERSAMPLING_FACTOR*sizeof(float )   );

            conic_driver(   self,  data, SLICE, nslices,
                            nslices_data,
                            Nfirstslice,
                            data_start,
                            dumf,
                            dumfC,
                            WORK_perproje,
                            WORK,
                            WORKbis,
                            cpu_offset_x,
                            cpu_offset_y
                            );
          }
        } else {
          int dimslice= self->params.num_x ;
          assert( self->params.num_x==self->params.num_y);

          if(self->params.CONICITY==0) {
            pro_driver(self, num_bins, self->fbp_precalculated.angles_per_proj,
                       SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y);

          } else {

            // printf(" CHIAMO PRO CONIC DRIVER %d %d \n",nslices_data, nslices   ) ;

            pro_conic_driver(   self, SINOGRAMMA , SLICE, nslices,
                                nslices_data,
                                Nfirstslice,
                                data_start,
                                cpu_offset_x,
                                cpu_offset_y
                                );
          }

          {
            float micron_in_cm = 1.0e-4;
            float tmp = 1.0f /   (M_PI*0.5f/self->params.nprojs_span)* (self->params.VOXEL_SIZE * micron_in_cm )  ;
            for( i=0; i< self->params.nprojs_span *num_bins*nslice_data_granularity ; i++) {
              SINOGRAMMA[i]*=tmp  ;
            }
          }
          if(VECTORIALITY==1) {
            if(self->params.CONICITY==0) {
              for( i=0; i< self->params.nprojs_span *num_bins ; i++) {
                dataA [ islice*self->params.nprojs_span*num_bins+i] =SINOGRAMMA[i]  ;
              }
            } else {
              sem_wait( &(self->proupdate_sem));
              long int i;
              for( i=0; i< self->params.nprojs_span *num_bins * nslices_data ; i++) {
                dataA [ 0*self->params.nprojs_span*num_bins+i] += SINOGRAMMA[i]  ;
              }
              /* if(data_start==170) { */
              /* 	FILE *f; */
              /* 	f=fopen("dat.dat","w" ) ; */
              /* 	printf(" FINE CON %d %d %d data_start %d \n",   self->params.nprojs_span , num_bins , nslices_data , data_start ) ;  */
              /* 	fwrite(SINOGRAMMA,  self->params.nprojs_span *num_bins * nslices_data   ,  4  ,f) ;  */
              /* 	fclose(f); */
              /* 	exit(0); */
              /* } */
              sem_post( &(self->proupdate_sem));
            }
          } else {
            assert(self->params.CONICITY==0);
            int iangle, ibin;
            for(iangle=0; iangle<self->params.nprojs_span; iangle++) {


	      // int npj_absolute  =self->reading_infos.proj_num_list[iangle] + npj_offset ;
	      // int npj_absolute  =self->reading_infos.proj_num_list[iangle] ;
	      // if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
	      // continue;
	      // }

	      // printf(" self->fbp_precalculated.angles_per_proj[npj_absolute]  %d %e \n" , npj_absolute, self->fbp_precalculated.angles_per_proj[npj_absolute] );
              // float cc = cos( self->fbp_precalculated.angles_per_proj[npj_absolute] );
              // float ss = sin( self->fbp_precalculated.angles_per_proj[npj_absolute] );
              float cc = cos( self->fbp_precalculated.angles_per_proj[iangle] );
              float ss = sin( self->fbp_precalculated.angles_per_proj[iangle] );
              for(ibin=0; ibin<num_bins; ibin++) {
                dataA [ (islice*self->params.nprojs_span+iangle)*num_bins +ibin ] =
		  -cc*SINOGRAMMA[iangle*num_bins+ibin]+
		  ss*SINOGRAMMA[(iangle+self->params.nprojs_span)*num_bins+ibin];
              }
            }
          }
        }
        /* if(self->params.DETECTOR_DUTY_OVERSAMPLING<0) { */
        /*   int dimslice= self->params.num_x ; */
        /*   pro_driver(self, num_bins,angles_per_proj, */
        /* 	     SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y); */
        /* } */
      } else {
        int dimslice= self->params.num_x, iter ;
        float beta = self->params.BETA_TV;
        float gamma = 1.0/self->params.LIPSCHITZFACTOR/ ( dimslice * self->params.nprojs_span  );

        //  x = x0
        //   u_old = np.zeros((l, l))
        float t_old = 1, t_new ;
        double norma=1.0e30;
        //----------------------
        // Initialisations...
        memset(SLICE,0, self->params.num_x * self->params.num_y  *sizeof(float) * VECTORIALITY * patch_w );
        //float alpha_rings = self->params.nprojs_span; //*pi/2 ?
        float alpha_rings = self->params.RING_ALPHA;
        float rings    [VECTORIALITY*num_bins];
        float drings   [VECTORIALITY*num_bins];
        float rings_old[VECTORIALITY*num_bins];
        float rings_tmp[VECTORIALITY*num_bins];
        memset( rings      , 0,  VECTORIALITY*num_bins*sizeof(float) ) ;
        memset( rings_old  , 0,  VECTORIALITY*num_bins*sizeof(float) ) ;
        float* energies = (float*) malloc(abs(self->params.ITERATIVE_CORRECTIONS)*sizeof(float)); //FIXME

	
#ifdef DATA_WEIGHT_MASK
        float* data_weight = load_weight_mask("weight.dat", self->params.nprojs_span, num_bins);
        if (data_weight == NULL) { puts("Could not find data weight mask ! Aborting."); return; }
#endif

        //For Fourier filtering of the rings variables
        fftwf_plan planr2c=NULL, planc2r=NULL;

	float *dumfftwf=NULL;
	int dimfft_preco_rings = nextpow2_padded(num_bins);
	int dimfft_rings = dimfft_preco_rings/2+1; 

        fftwf_complex* drings_fourier=NULL;

        float* ramp_filter;
        fftwf_complex* ramp_filter_fourier=NULL;
        if (self->params.DO_PRECONDITION) {
          sem_wait( &(self->fftw_sem)); //CAUTION : FFTW plan creation is not thread-safe
          ramp_filter = (float*) fftwf_malloc(dimfft_preco_rings*sizeof(float));
          dumfftwf = (float*) fftwf_malloc(dimfft_preco_rings*sizeof(float));
          drings_fourier = (fftwf_complex*) fftwf_malloc((  dimfft_preco_rings/2+1  )*sizeof(fftwf_complex));
          planr2c = fftwf_plan_dft_r2c_1d(dimfft_preco_rings,ramp_filter , drings_fourier, FFTW_ESTIMATE);
          planc2r = fftwf_plan_dft_c2r_1d(dimfft_preco_rings, drings_fourier, ramp_filter, FFTW_ESTIMATE);

          //Fourier transform of discrete ramp filter
          ramp_filter = (float*) fftwf_malloc(dimfft_preco_rings*sizeof(float));
	  sem_post( &(self->fftw_sem)); //----
	  
	  memset( ramp_filter,0, dimfft_preco_rings*sizeof(float)   );
          ramp_filter[0] = 0.25f;
          float val;
          for (i = 1; i <=dimfft_preco_rings /2; i++) {
            val = ((i & 1) ? (-1.0f/M_PI/M_PI/i/i) : (0.0f));
            ramp_filter[i] = val;
            ramp_filter[dimfft_preco_rings-i] = val;
          }
	  sem_wait( &(self->fftw_sem));
          ramp_filter_fourier = (fftwf_complex*) fftwf_malloc(dimfft_rings*sizeof(fftwf_complex));
	  sem_post( &(self->fftw_sem)); //----
	  
          fftwf_execute_dft_r2c(planr2c, ramp_filter, ramp_filter_fourier);
	  
          sem_wait( &(self->fftw_sem)); //CAUTION : FFTW plan creation is not thread-safe
          fftwf_free(ramp_filter);
	  sem_post( &(self->fftw_sem)); //----
	  

        }
        //-----------------



        clock_t startTime2=0;
        if(self->params.OPTIM_ALGORITHM != 1 || self->params.DENOISING_TYPE==5 || self->params.DENOISING_TYPE==6 || self->params.DENOISING_TYPE==7 || self->params.DENOISING_TYPE==8 || self->params.DENOISING_TYPE==9) {
          // we dont need extra factors
        } else if(self->params.ITER_RING_SIZE==0  && self->params.DO_PRECONDITION == 0) {
          gamma = 1.0/self->params.LIPSCHITZFACTOR/ ( dimslice * self->params.nprojs_span  ); // /3 ;
          /* { */
          /*   int i; */
          /*   for(i=0; i<100; i++)  */
          /*     printf(" WARNING !!!! ricordati di togliere questa \n"); */
          /* } */
        }
        else if(self->params.DENOISING_TYPE!=4 ) {
          sem_wait(& (self->fbp_sem) );
	  
          float factorifprec = 1.0;
          int doprec = 1;
          if( self->params.DO_PRECONDITION == 0) {
            // factorifprec=1.0f/(M_PI*0.5f/ self->params.nprojs_span);
            doprec=0;
          }
	  
          if(self->fbp_precalculated.prec_gamma_is_set==0) { //calculate Lipschitz constant with power method
	    startTime2 = clock();          
	    /*
	     *  The Hessian with respect to the variables (x,r) [x : image variables, r : rings variables] reads :
	     *
	     *  | Ht*H   -Ht |  (num_bins**2 rows)
	     *  | -H     Id  |  (num_bins rows)
	     *
	     *  After a multiplication by vector [x r]t :
	     *
	     *  | Ht(H*x-r) | : "SLICE"  [component wrt "x" = backproj(projection corrected by "r")]
	     *  | -(H*x-r)  | : "comp_r" [component wrt "r" = projection corrected by "r"]
	     *
	     * */
	    
	    
	    //x0 = SLICE = Ht * data [initial vector for image variables]
            Sino_Sum = rec_driver(self,  WORK,WORKbis,    SLICE    ,  num_bins,  dim_fft,
				  dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
				  data, ncpus   , cpu_offset_x, cpu_offset_y ,doprec ,1 , NULL,
				  npj_offset , 0);  // (do_precondition,is_tomolocal,&fidelity)
	    
            //r0 : initial vector for rings variables
            float* r0 = (float*) malloc(VECTORIALITY* num_bins*sizeof(float));
            float norma_r = 0.0f;
            for (i = 0; i < VECTORIALITY* num_bins; i++) r0[i] = (float) (rand() % 5);

	    float renorm = (M_PI*0.5f)/self->params.nprojs_span; // voir explication dans gputomo.cu

	    
            for(iter=0; iter < self->params.LIPSCHITZ_ITERATIONS; iter++) {
	      
              for(norma=0.0, i=0; i< dimslice *dimslice * VECTORIALITY ; i++)  norma  += SLICE[i]*SLICE[i];
              for(norma_r=0.0f, i=0; i< num_bins*VECTORIALITY; i++)  norma_r  += r0[i]*r0[i];
              norma = sqrtf(norma + norma_r);
              for(i=0; i< dimslice *dimslice* VECTORIALITY  ; i++)  SLICE[i]/=norma;
              for(i=0; i< num_bins*VECTORIALITY; i++)  r0[i] /= norma;
              if((iter % 1) == 0  /*&& self->iproc==0*/) printf("Searching Lipschitz Constants at iteration %d norma %e %d norma_r %e\n", iter, norma, Nfirstslice, norma_r);
	      
              //H*x0            [SLICE->SINOGRAMMA]
	      
              pro_driver(self, num_bins,self->fbp_precalculated.angles_per_proj,
                         SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y);
	      
              //H*x0-r0  for each angle
              for( i=0; i< VECTORIALITY*self->params.nprojs_span *num_bins ; i++) {
                int iring =  i % (  num_bins   ) + num_bins *(i/(self->params.nprojs_span *num_bins)) ;
		//                SINOGRAMMA[i] -= alpha_rings* r0[ iring  ];
                SINOGRAMMA[i] =   (SINOGRAMMA[i]*factorifprec- alpha_rings* r0[ iring  ]) ;
              }
              //update rings by accumulating sinogram values
              for (i = 0; i < num_bins*VECTORIALITY; i++) r0[i] = 0;
              for( i=0; i< VECTORIALITY*self->params.nprojs_span *num_bins ; i++) {
                int iring =  i % (  num_bins   ) + num_bins *(i/(self->params.nprojs_span *num_bins)) ;
                r0[iring] += -alpha_rings *SINOGRAMMA[i];
		if (self->params.DO_PRECONDITION==0) { SINOGRAMMA[i] *=renorm ; }
              }
              //Filter the rings coefficients by the same filter used for back-projection
              if (self->params.DO_PRECONDITION) {
                int j;
                for (j = 0; j < VECTORIALITY; j++){
		  memcpy(  dumfftwf , r0+j*num_bins,  num_bins*sizeof(float) ) ;
		  if(1){ 
		    int k;
		    for( k=0; k<dimfft_preco_rings-num_bins; k++) {
		      if(k>(dimfft_preco_rings-num_bins)/2) {
			dumfftwf[num_bins+k]  =  dumfftwf[0]   ; 
		      } else {
			dumfftwf[num_bins+k]  =  dumfftwf[num_bins-1]   ; 
		      }
		    }
		  }
                  fftwf_execute_dft_r2c(planr2c,dumfftwf , drings_fourier);
		  {
		    int i;
		    for (i = 0; i < dimfft_rings; i++) {
		      drings_fourier[i][0] *= ramp_filter_fourier[i][0]/(2*(dimfft_rings-1));
		      drings_fourier[i][1] *= ramp_filter_fourier[i][0]/(2*(dimfft_rings-1));
		    }
		  }
		  fftwf_execute_dft_c2r(planc2r, drings_fourier, dumfftwf );
		  memcpy(  r0+j*num_bins,  dumfftwf , num_bins*sizeof(float) ) ; 
                }
              }
              //---
              //Ht * (H*x0 - r0)        [SINOGRAMMA->SLICE]
              rec_driver(self,  WORK,WORKbis,SLICE,  num_bins,  dim_fft,
                         dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
                         SINOGRAMMA , ncpus   , cpu_offset_x, cpu_offset_y  ,doprec ,1, NULL,
			 npj_offset ,0 );
	      
            }
            free(r0);
            norma=self->params.LIPSCHITZFACTOR*norma ;
            self->fbp_precalculated.prec_gamma = 1.0/self->params.LIPSCHITZFACTOR/norma;
	    
            self->fbp_precalculated.prec_gamma_is_set=1;
          }
          gamma =   self->fbp_precalculated.prec_gamma ;
          sem_post(& (self->fbp_sem) );
          float data_fidelity_err = 0, tv_norm=0;
          //Initial (filtered) back-projection : SLICE
          rec_driver(self,  WORK,WORKbis,SLICE,  num_bins,  dim_fft,
                     dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
                     data , ncpus   , cpu_offset_x, cpu_offset_y  ,
                     doprec ,1 ,  &data_fidelity_err,
		     npj_offset,0);

	  
	  

          if(beta>0)  tv_norm=denoising_driver(self,dimslice,dimslice,SLICE, SLICE, beta );

          float energy = data_fidelity_err/2.0 + tv_norm*beta;
          printf("  at iteration %d  energy = %e   data_fidelity_err %e   L1 norm %E \n", 0, energy, data_fidelity_err,tv_norm*beta);
          energies[0] = energy; //FIXME
          memcpy(SLICEold, SLICE, VECTORIALITY*dimslice*dimslice*sizeof(float) );
        } //end "denoising_type != 4"


        if(self->params.DENOISING_TYPE==6 || self->params.DENOISING_TYPE==5){
          /* // 6 -> NN-FBP Training set calculation */
          /* // 5 -> NN-FBP Reconstruct with trained weights */
	  
          /* // Create a handle to NNFBP library */
          /* sem_wait( &(self->fftw_sem)); */
          /* if(self->sharedHandle==NULL){ */
          /*   char fname[2000]; */
          /*   // sprintf(fname,"%s/%s"   ,   self->params.nome_directory_distribution, "libpyhst_nnfbp.so"); */
          /*   sprintf(fname,"%s"   ,    "libpyhst_nnfbp.so"); */
          /*   self->sharedHandle = dlopen(fname, RTLD_LAZY); */
          /*   if(self->sharedHandle==NULL){ */
          /*     sprintf(fname,"%s/%s"   ,   self->params.nome_directory_distribution, "libpyhst_nnfbp.so"); */
          /*     self->sharedHandle = dlopen(fname, RTLD_LAZY); */
          /*     if(self->sharedHandle==NULL){ */
          /*       fprintf(stderr,"NNFBP plugin should be installed somewhere to use DENOISING_TYPE 5 or 6\n"); */
          /*       fprintf(stderr, "ERROR: %s\n", dlerror()); */
          /*       exit(EXIT_FAILURE); */
          /*     } */
          /*   } */
          /* } */
          /* sem_post( &(self->fftw_sem)); */

          /* dlerror();    /\* Clear any existing error *\/ */
          /* char *error; */

          if(self->params.DENOISING_TYPE==6){
	    // int i;
            // Perform training set calculation
            nnfbp_train(self,dim_fft,num_bins, data_orig, dimslice,SLICEcalc, SINOGRAMMA, WORK, WORKbis, dumf, dumfC, WORK_perproje, OVERSAMPLE, oversampling, ncpus,cpu_offset_x,cpu_offset_y, Nfirstslice, islice, nomeout);
            continue;
          }else{
            // NN-FBP Reconstruction function handle
            /* void (*nnfbp_reconstruct)(CCspace*,int,int,float*,int,float*,float*,float*,float**,float*,float*,fcomplex*,float*,float*,int,int,float,float,rec_driver_func); */
            /* *(void **) (&nnfbp_reconstruct) = dlsym(self->sharedHandle, "nnfbp_reconstruct"); */
            /* if ((error = dlerror()) != NULL)  { */
	    /*   fprintf(stderr,"NNFBP plugin not loaded successfully\n"); */
	    /*   fprintf(stderr, "%s\n", error); */
	    /*   exit(EXIT_FAILURE); */
	    /* } */
	    
            // Perform NN-FBP reconstruction
            nnfbp_reconstruct(self,dim_fft,num_bins, data_orig, dimslice,SLICEcalc,SLICE, SINOGRAMMA,
			      WORK, WORKbis, dumf, dumfC, WORK_perproje, OVERSAMPLE, oversampling, ncpus,cpu_offset_x, cpu_offset_y);
          }
        }else if(self->params.DENOISING_TYPE==7){
          /* // MR-FBP Reconstruction */
	  
          /* // Create a handle to MRFBP library */
          /* sem_wait( &(self->fftw_sem)); */
          /* if(self->sharedHandle==NULL){ */
          /*   self->sharedHandle = dlopen("libpyhst_mrfbp.so", RTLD_LAZY); */
          /*   if(self->sharedHandle==NULL){ */
          /*     fprintf(stderr,"MRFBP plugin should be installed to use DENOISING_TYPE 7\n"); */
          /*     fprintf(stderr, "ERROR: %s\n", dlerror()); */
          /*     exit(EXIT_FAILURE); */
          /*   } */
          /* } */
          /* sem_post( &(self->fftw_sem)); */
	  
          /* dlerror();    /\* Clear any existing error *\/ */
          /* char *error; */

          /* // MR-FBP Reconstruction function handle */
          /* void (*mrfbp_reconstruct)(CCspace*,int,int,float*,int,float*,float*,float*,float**,float*,float*,fcomplex*,float*,float*,int,int,float,float,rec_driver_func, pro_driver_func); */
          /* *(void **) (&mrfbp_reconstruct) = dlsym(self->sharedHandle, "mrfbp_reconstruct"); */
          /* if ((error = dlerror()) != NULL)  { */
          /*   fprintf(stderr,"MRFBP plugin not loaded successfully\n"); */
          /*   fprintf(stderr, "%s\n", error); */
          /*   exit(EXIT_FAILURE); */
          /* } */
          /* // Perform reconstruction */
          /* mrfbp_reconstruct(self,dim_fft,num_bins, data_orig, dimslice,SLICEcalc,SLICE, SINOGRAMMA, WORK, WORKbis, dumf, dumfC, WORK_perproje, OVERSAMPLE, oversampling, ncpus,cpu_offset_x, cpu_offset_y, &rec_driver, &pro_driver); */

        }
        else if (self->params.DENOISING_TYPE == 8) { // Wavelets
          clock_t startTime = clock();
          sem_wait( &(self->gpustat_sem));
          if(!self->gpu_context ){
            printf(" ERROR : THE Wavelets RECONSTRUCTION CAN BE USED ONLY WITH GPU \n" );
            exit(1);
          }
          //---
          self->gpu_context->wavelets_driver(self->gpu_context,
					     data, SLICE,
					     self->params.DETECTOR_DUTY_RATIO,
					     self->params.DETECTOR_DUTY_OVERSAMPLING,
					     self->params.BETA_TV,
					     self->params.ITER_RING_BETA,
					     self->params.ITER_RING_HEIGHT,
					     self->params.RING_ALPHA);
          //---
          sem_post( &(self->gpustat_sem));
          printf("Wavelets took %f seconds\n\n", (double)( clock() - startTime ) / (double)CLOCKS_PER_SEC);
        }

	//        else if (self->params.DENOISING_TYPE == 9) { // SIRT-Filter - DISABLED, done in the Python level
	//          clock_t startTime = clock();
	//          sem_wait( &(self->gpustat_sem));
	//          if(!self->gpu_context ){
	//            printf(" ERROR : THE SIRT-Filter RECONSTRUCTION CAN BE USED ONLY WITH GPU \n" );
	//            exit(1);
	//          }
	//          //---
	//          self->gpu_context->sirtfilter_driver(self->gpu_context,
	//                                       data, SLICE,
	//                                       self->params.DETECTOR_DUTY_RATIO,
	//                                       self->params.DETECTOR_DUTY_OVERSAMPLING);
	//          //---
	//          sem_post( &(self->gpustat_sem));
	//          printf("SIRT-Filter took %f seconds\n\n", (double)( clock() - startTime ) / (double)CLOCKS_PER_SEC);
	//        }

        else if(self->params.DENOISING_TYPE>=1 && self->params.DENOISING_TYPE<=3 && self->params.OPTIM_ALGORITHM == 1) { // fast gradient tv  denoising et fista
          clock_t startTime = clock(); //FIXME

          float old_data_fidelity_err = 1.0e30;

          int rkern = (int) (  3*self->params.ITER_RING_SIZE  +1);
          assert(rkern<1000);
          float kernel1[2*rkern+1];
          float kernel2[4*rkern+1];
          {
            int i,j;
            float x;
            float s=3.0;
            float sum=0;
            for(i=0; i< 2*rkern+1  ; i++) {
              x = (rkern-i)/s;
              kernel1[i]=exp(-x*x/2.0);
              sum+=kernel1[i] ;
            }
            for(i=0; i<2*rkern+1; i++) {
              kernel1[i]/=sum;
            }
            for(i=0; i<4*rkern+1; i++) {
              kernel2[i]=0.0;
            }
            kernel1[rkern]-=1.0;
            for(i=0; i<2*rkern+1; i++) {
              for(j=0; j<2*rkern+1; j++) {
                kernel2[i+j] += kernel1[i]*kernel1[j];
              }
            }
          }
          float renorm = (M_PI*0.5f)/self->params.nprojs_span; // voir explication dans gputomo.cu
          float alpha_rings = self->params.RING_ALPHA;
          //printf("gamma = %.2f - 1/gamma = %.2f",gamma,1.0/gamma);

          // if angles are to be ignored, pre-compute an array if projs to ignore
          int* is_ignored = NULL ;
          if (self->params.do_ignore_projections) {
            is_ignored = (int*) calloc(self->params.nprojs_span, sizeof(int));
            int currproj_ign = 0;
            for (projection = 0; projection < self->params.nprojs_span; projection++) { // dans la suite quand on voudra
	  // faire ignore aussi en elicoidal il faudra ajouter, dans la comparaison : +npj_offset a' projection
              if (projection == self->params.ignore_angles[currproj_ign]) {
                is_ignored[projection] = 1;
                currproj_ign++;
              }
            }
          }
          // -----

          for(ivolta=1; ivolta<    abs(self->params.ITERATIVE_CORRECTIONS) ; ivolta++) {
            // err = H * x - y
            pro_driver(self, num_bins,self->fbp_precalculated.angles_per_proj,
                       SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y);

            memset( drings     , 0,  VECTORIALITY*num_bins*sizeof(float) ) ;

            int i;
            float  data_fidelity_err =  0.0;


            if(	self->params.DO_PRECONDITION == 0) {

              for( i=0; i< VECTORIALITY*self->params.nprojs_span *num_bins ; i++) {

                int iring =  i % (  num_bins   ) + num_bins *(i/(self->params.nprojs_span *num_bins)) ;

                SINOGRAMMA[i] += alpha_rings * rings[ iring  ];
                SINOGRAMMA[i]=(SINOGRAMMA[i]-data_orig[i] ) ;
                drings[iring] += -alpha_rings * SINOGRAMMA[i] ;

                SINOGRAMMA[i] *= renorm;

                // ignore projections ?
                int curr_proj = (i / num_bins) % self->params.nprojs_span;
                if (self->params.do_ignore_projections && is_ignored[curr_proj]) SINOGRAMMA[i] = 0;
                // -----

              }
	      // SINOGRAMMA[i] *=  renorm;  commentato per combatibilita con CP
	      
            } else {

              for( i=0; i< VECTORIALITY*self->params.nprojs_span *num_bins ; i++) {

                SINOGRAMMA[i]=(SINOGRAMMA[i]-data_orig[i])  ;
                int iring =  i % (  num_bins   ) +  num_bins *(i/(self->params.nprojs_span *num_bins)) ;
                SINOGRAMMA[i] += alpha_rings* rings[ iring  ];
                drings[iring] += -alpha_rings * SINOGRAMMA[i] ;
                // SINOGRAMMA[i] *=  renorm;  commentato per combatibilita con CP


                // ignore projections ?
                if (self->params.do_ignore_projections) {
                  int curr_proj = (i / num_bins) % self->params.nprojs_span;
                  if (is_ignored[curr_proj]) SINOGRAMMA[i] = 0;
                }
                // -----

              }
            }

            //Filter the rings coefficients by the same filter used for back-projection
            if (self->params.DO_PRECONDITION) {
              int j=0;
              for (j = 0; j < VECTORIALITY; j++){
		memcpy(  dumfftwf , drings+j*num_bins,  num_bins*sizeof(float) ) ;
		if(1){ 
		  int k;
		  for( k=0; k<dimfft_preco_rings-num_bins; k++) {
		    if(k>(dimfft_preco_rings-num_bins)/2) {
		      dumfftwf[num_bins+k]  = dumfftwf[0]    ; 
		    } else {
		      dumfftwf[num_bins+k]  =    dumfftwf[num_bins-1]  ; 
		    }
		  }
		}
                fftwf_execute_dft_r2c(planr2c, dumfftwf   , drings_fourier);
                for (i = 0; i < dimfft_rings; i++) {
                  drings_fourier[i][0] *= ramp_filter_fourier[i][0]/(2*(dimfft_rings-1));
                  drings_fourier[i][1] *= ramp_filter_fourier[i][0]/(2*(dimfft_rings-1));
                }
                fftwf_execute_dft_c2r(planc2r, drings_fourier, dumfftwf);
		memcpy(  drings+j*num_bins, dumfftwf ,   num_bins*sizeof(float) ) ;
              }
            }

            //---

            if(0&&self->params.ITER_RING_HEIGHT) {//convolution of drings by kernel2 //FIXME : deleted
              int id,i,j;
              for(id=0; id<VECTORIALITY; id++) {
                for(i=0; i< num_bins; i++) rings_tmp[i]=0;
                for(i=0; i< num_bins; i++){
                  for(j=max(0,(i-2*rkern)); j<=min((num_bins-1), (i+2*rkern)) ; j++   ) {
                    rings_tmp[i] +=   kernel2[2*rkern + i-j] * drings[id*num_bins + j]  ;
                  }
                }
                for(i=0; i< num_bins; i++)  {
                  //printf("   %e %e \n",  drings[id*num_bins + i] ,   rings_tmp[i] );
                  drings[id*num_bins + i]   =   rings_tmp[i]  ;
                }
              }
            }

            // back_proj = Ht * err
            rec_driver(self,  WORK,WORKbis,SLICEcalc,  num_bins,  dim_fft,
                       dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
                       SINOGRAMMA , ncpus   , cpu_offset_x, cpu_offset_y  ,
                       self->params.DO_PRECONDITION==1 ,1 , &data_fidelity_err,
		       npj_offset ,0);
	    

	    {
              if( self->params.DO_PRECONDITION==0) {
	      	data_fidelity_err /=  renorm;
	      	data_fidelity_err /=  renorm;
	      }
            }



            for( i=0; i< VECTORIALITY*dimslice*dimslice ; i++) {
              SLICE[i]=SLICE[i]-SLICEcalc[i]  * gamma;
              SLICEcalc[i]= SLICE[i];
            }
            // printf(" chiamo denoising \n");



            float tv_norm=0;
            if(beta>0) {
              if( self->params.DENOISING_TYPE!=3)
                tv_norm = denoising_driver(self,dimslice,dimslice,SLICE, SLICE, gamma*beta );
              else {
                if(ivolta!=abs(self->params.ITERATIVE_CORRECTIONS)-1) {
                  tv_norm = denoising_driver(self,dimslice,dimslice,SLICE, SLICE, beta );
                }  else {
                  tv_norm = denoising_driver(self,dimslice,dimslice,SLICE, SLICE, -beta );
                }
              }
            }

            t_new = (1.0 + sqrt(1 + 4 * t_old*t_old))/2.;
            if( !self->params.FISTA)   t_new=1;

            // x = u_n + (t_old - 1)/t_new * (u_n - u_old)
            {
              float dum;
              for( i=0; i< VECTORIALITY*dimslice*dimslice ; i++) {
                dum = SLICE[i];
                SLICE[i]=SLICE[i] + (t_old-1)/t_new * ( SLICE[i]- SLICEold[i]  ) ;
                SLICEold[i]=dum;
              }
            }



	    
	    //float gamma_rings = 1.0f/(self->params.LIPSCHITZFACTOR*self->params.nprojs_span);
	    float rings_L1_norm = 0.0f;
	    {//apply fista step to rings
	      float  rold;
	      int i;
	      for( i=0; i<VECTORIALITY* num_bins; i++) {
                rings_tmp[i]  = rings[i] +  drings[i]* gamma ;
	      }
	      proietta_drings( self, rings_tmp,   VECTORIALITY , 1.0/gamma) ;  // prende Lipschitz in argomento

	      for( i=0; i< VECTORIALITY* num_bins; i++) {
		rold  = rings_old[i];
		rings_old[i] =  rings_tmp[i];
		rings[i]=rings_tmp[i] +  ((t_old-1)/t_new) * (rings_tmp[i]-rold) ;
		rings_L1_norm += fabsf(rings[i]);
	      }
	    }
	    //    if (ivolta % 100 == 0) qdplot(rings,num_bins);


            t_old = t_new ;


            float energy = data_fidelity_err/2.0 + tv_norm*beta + self->params.ITER_RING_BETA*rings_L1_norm ;
            energies[ivolta] = energy;
            if ((ivolta % 10) == 0) printf("  iteration %d  energy = %e , data_fidelity_err = %e L1 norm = %e  rings = %e \n", ivolta, energy, data_fidelity_err, tv_norm, rings_L1_norm);
	    if(self->params.DENOISING_TYPE==3) {
	      if( old_data_fidelity_err*1.1 < data_fidelity_err && ivolta<abs(self->params.ITERATIVE_CORRECTIONS)-2) {
		ivolta=abs(self->params.ITERATIVE_CORRECTIONS)-2;
	      }
	    }
	    if( old_data_fidelity_err >  data_fidelity_err  )          old_data_fidelity_err = data_fidelity_err ;
          } //end of iterative corrections loop for TV
          //
          if (self->params.do_ignore_projections) free(is_ignored);
          //---


          if (false && self->params.DO_PRECONDITION) {
	    sem_wait( &(self->fftw_sem));
            fftwf_free(dumfftwf);
            fftwf_free(drings_fourier);
            fftwf_free(ramp_filter_fourier);
	    
            fftwf_destroy_plan(planr2c);
            fftwf_destroy_plan(planc2r);
	    sem_post( &(self->fftw_sem));
	    
          }
	  
          // FILE* fid = fopen("energy.dat","wb");
          // fwrite(energies, sizeof(float), abs(self->params.ITERATIVE_CORRECTIONS), fid);
          // fclose(fid);
          free(energies); //FIXME
          //-----
          //for (i = 0; i < VECTORIALITY*dimslice*dimslice; i++) SLICE[i] += SLICE_delta[i];
          //-----
          printf("TV denoiser took %f seconds (%f in total)\n\n", (double) ( clock() - startTime ) / (double)CLOCKS_PER_SEC, (double) ( clock() - startTime2 ) / (double)CLOCKS_PER_SEC); //FIXME
        } else if(self->params.DENOISING_TYPE==4) {

          // ARRANGIARSI QUI

          fb_dl_driver(self,
                       data, num_bins, SLICE,
                       self->params.DO_PRECONDITION==0 ,VECTORIALITY,  self->params.BETA_TV, Nfirstslice+islice
                       );

        }
        else if (self->params.DENOISING_TYPE == 1 &&  self->params.OPTIM_ALGORITHM  == 3) { //Chambolle-Pock
          sem_wait( &(self->gpustat_sem));
          if(!self->gpu_context ){
            printf(" ERROR : THE Chambolle-Pock ROUTINE CAN BE USED ONLY WITH GPU \n" );
            exit(1);
          }
          clock_t startTime = clock();
          //---
          self->gpu_context->cp_driver(self->gpu_context,
                                       data, SLICE,
                                       self->params.DETECTOR_DUTY_RATIO,
                                       self->params.DETECTOR_DUTY_OVERSAMPLING,
                                       self->params.BETA_TV,
                                       self->params.ITER_RING_BETA,
                                       self->params.ITER_RING_HEIGHT,
                                       self->params.RING_ALPHA);
          //---
          sem_post( &(self->gpustat_sem));
          printf("CP took %f seconds\n\n", (double)( clock() - startTime ) / (double)CLOCKS_PER_SEC);
        }
        /*
	  else if (self->params.DENOISING_TYPE == 1 &&  self->params.OPTIM_ALGORITHM  == 4) { // Conjugate gradient
          sem_wait( &(self->gpustat_sem));
          if(!self->gpu_context ){
	  printf(" ERROR : THE Conjugate gradient ROUTINE CAN BE USED ONLY WITH GPU \n" );
	  exit(1);
          }
          //---
          self->gpu_context->conjgrad_driver(self->gpu_context,
	  data, SLICE,
	  self->params.DETECTOR_DUTY_RATIO,
	  self->params.DETECTOR_DUTY_OVERSAMPLING,
	  self->params.BETA_TV);
          //---
          sem_post( &(self->gpustat_sem));
	  }
        */

        else if(self->params.DENOISING_TYPE==30) { // nlm

          if(VECTORIALITY==2) {
            printf("ERROR : VECTORIALITY==2 CANNOT BE USED with NLM\n");
            fprintf(stderr, "ERROR : VECTORIALITY==2 CANNOT BE USED WITH NLM\n");
            exit(1);
          }

          // float ima_min, ima_max;

	  Sino_Sum = rec_driver(self,  WORK,WORKbis,    SLICE   ,  num_bins,  dim_fft,
				dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling,
				data, ncpus   , cpu_offset_x, cpu_offset_y ,1 ,1, NULL ,
				npj_offset, 0);  // 1 = precondition

	  /* for(k=0;k<2;k++) { */
	  /*   Sino_Sum = rec_driver(self,  WORK,WORKbis,    SLICEcalc    ,  num_bins,  dim_fft,  */
	  /* 			  dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling, */
	  /* 			  data, ncpus   , cpu_offset_x, cpu_offset_y ,1 ,1, NULL   );  // 1 = precondition */

	  /*     get_min_max(  SLICEcalc, self->params.num_y,  self->params.num_x, &ima_min, &ima_max   ); */
	  /*     printf(" MIN MAX %e %e \n", ima_min, ima_max); */

	  /*   if(k==0) { */
	  /*     threshold = 0.5f*(ima_min+ima_max); */
	  /*     spills_over(  SLICEcalc, self->params.num_y,  self->params.num_x, */
	  /* 		    threshold , SLICE    ); */

	  /*     pro_driver(self, num_bins,self->fbp_precalculated.angles_per_proj, */
	  /* 		 SINOGRAMMA, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y); */

	  /*     for( i=0; i< self->params.nprojs_span *num_bins ; i++) { */
	  /* 	data[i]=data_orig[i]-SINOGRAMMA[i];  */
	  /*     } */
	  /*   } else { */
	  /*     for( i=0; i< self->params.num_y*self->params.num_x ; i++) { */
	  /* 	if(SLICE[i]==0.0f) SLICE[i] =SLICEcalc[i];  */
	  /*     } */
	  /*   } */
	  /* } */


	  float bruit=FindNoise(self, SLICE, self->params.CALM_ZONE_LEN,dimslice,dimslice)*self->params.NLM_NOISE_INITIAL_FACTOR;
	  printf("  NOISE %e \n", bruit/self->params.NLM_NOISE_INITIAL_FACTOR);
	  for(ivolta=0; ivolta< abs(self->params.ITERATIVE_CORRECTIONS) ; ivolta++) {
	    nlm_driver(self,dimslice,dimslice,SLICE, SLICE, bruit);
	    bruit = bruit/self->params.NLM_NOISE_GEOMETRIC_RATIO ;
	  }
        }
        if(self->params.zerooffmask) {
          int x,y, iv , offset;
          for(iv=0; iv<VECTORIALITY; iv++) {
            offset = iv*(self->params.num_x*self->params.num_y)   ;
            for(y=0; y< self->params.num_y; y++) {
              for(x=0; x<min(( self->fbp_precalculated.minX[y]),(self->params.num_x) ) ; x++) {
                SLICE[offset+y*(self->params.num_x) +x]=0;
              }
              for(x=self->fbp_precalculated.maxX[y]; x<   self->params.num_x; x++) {
                SLICE[offset+y*( self->params.num_x) +x]=0;
              }
            }
          }
        }
      }//end of "iterative corrections" case


      if(STEAM_DIRECTION==1) {

        if(VECTORIALITY==2) {
          // rotational2zero(self,SLICE,SLICE);
        }
        float Slice_Sum=0.0;
        {
          int i;
          for(i=0; i< (  self->params.num_x)* ( self->params.num_y); i++) {
            Slice_Sum+= SLICE[i];
          }
        }
        //	printf("ALLA FINE %e\n",Slice_Sum );

        if( self->params.SUMRULE==1  || self->params.CONICITY) {
          if(VECTORIALITY==2) {
            printf("ERROR : VECTORIALITY==2 or self->params.CONICITY CANNOT BE USED with SUMRULE==1\n");
            fprintf(stderr, "ERROR : VECTORIALITY==2 CANNOT BE USED WITH SUMRULE==1\n");
            exit(1);
          }
        }
        if( self->params.SUMRULE==1  ) {

          Slice_Sum=0.0;
          int i;
          for(i=0; i< (self->params.num_x)* (self->params.num_y); i++) {
            Slice_Sum+= SLICE[i];
          }
          Sino_Sum*=  2.0/M_PI ;
          for(i=0; i< (self->params.num_x)* (self->params.num_y); i++) {
            SLICE[i]+=(Sino_Sum-Slice_Sum)/((self->params.num_x)* (self->params.num_y));
          }
        }

        int inizio_slice, fine_slice;
        if(self->params.CONICITY) {
          inizio_slice =  0  ;
          fine_slice =    nslices * self->params.num_y*self->params.num_x   ;
        } else {
          inizio_slice =  self->params.patch_ep*self->params.num_y*self->params.num_x   ;
          fine_slice =    (self->params.patch_ep+1)*self->params.num_y*self->params.num_x   ;
        }

        if(1){
          float aMin,aMax;
          aMin=aMax=SLICE[0];


          for(i=inizio_slice ;i< fine_slice  ; i++) {
            if(SLICE[i]>aMax ) aMax = SLICE[i];
            if(SLICE[i]<aMin ) aMin = SLICE[i];
          }
          // long int * new_histo=(long int *) malloc(1000001*sizeof(long int));
	  long int * new_histo =  (long int *) CCspace_memoryCache( self,  12, ibunch, npbunches,1000001,sizeof(long int));
	  
          memset(new_histo , 0 , 1000001*sizeof(long int));

          put_histo(new_histo, aMin, aMax, SLICE+inizio_slice, self->params.num_y*self->params.num_x );
          sem_wait(&(self->minmax_sem)) ;
          {
            if(self->aMin > self->aMax ) {
              self->aMin = aMin;
              self->aMax = aMax;
              memcpy(self->histovalues, new_histo, 1000000*sizeof(long int));
            } else {
              int cambio=0;
              double oldMin, oldMax;

              oldMin= self->aMin;
              oldMax= self->aMax;
              if(aMin < self->aMin)  { self->aMin=aMin;cambio=1; }
              if(aMax > self->aMax)  { self->aMax=aMax;cambio=1; }

              if(cambio) {
                // long int * replacement_histo=(long int *) malloc(1000000*sizeof(long int));
		long int * replacement_histo= (long int*) CCspace_memoryCache( self,  13, ibunch, npbunches,1000001,sizeof(long int));
		 
                memset(replacement_histo , 0 , 1000000*sizeof(long int));
                replace_histo(replacement_histo,self->aMin, self->aMax,
                              self->histovalues       , oldMin, oldMax     );
                // free(self->histovalues );
                memcpy( self->histovalues ,  replacement_histo,    1000000*sizeof(long int)  ) ; 
              }
              replace_histo(self->histovalues,self->aMin, self->aMax,
                            new_histo        , aMin, aMax     );
            }
          }
          sem_post(&(self->minmax_sem)) ;
          // free(new_histo);
        }
        if(self->params.EDFOUTPUT ) {
          if(VECTORIALITY==1) {
            // sprintf(nomeout,self->params.OUTPUT_FILE,   Nfirstslice+islice+self->reading_infos.pos_s[0]);


            if( self->params.patch_ep==0  )  {
              int gslice;
              for(gslice=0; gslice<nslice_step+do_2by2; gslice++)   {
		sprintf(nomeout,self->params.OUTPUT_FILE,   Nfirstslice+islice +
			self->params.patch_ep + gslice+ ((int)self->params.SOURCE_Y) ); 
                if (self->params.verbosity>0)  printf("writing file %s \n", nomeout );

		if(self->params.LT_pars==NULL) {
		  write_data_to_edf( SLICE + inizio_slice  + gslice*self->params.num_y*self->params.num_x ,
				     self->params.num_y, self->params.num_x   ,
				     nomeout);
		} else {
		  write_data_to_edf( SLICE_lt ,
				     lt_dsq, lt_dsq  ,
				     nomeout);
		  free(SLICE_lt);
		}
              }
            } else  {
              int inizio, fine;
              inizio = self->params.patch_ep-self->params.patch_ep/2;
              fine   = self->params.patch_ep+self->params.patch_ep/2;
              if ( Nfirstslice+islice==0  ){
                inizio=0;
              };
              if ( Nfirstslice+islice+2*self->params.patch_ep==self->params.NUM_IMAGE_2-1 ){
                fine=2*self->params.patch_ep;
              };
              int gslice;
              for(gslice=inizio; gslice<=fine; gslice++)   {
                sprintf(nomeout,self->params.OUTPUT_FILE,   Nfirstslice+islice +  gslice );
                if (self->params.verbosity>0) printf("writing file %s \n", nomeout );
                write_data_to_edf( SLICE +  gslice*self->params.num_y*self->params.num_x ,
                                   self->params.num_y, self->params.num_x   ,
                                   nomeout);
              }
            }



          } else {
            // printf("writing file %s \n", nomeout );
            sprintf(nomeout,self->params.OUTPUT_FILE, "c",  Nfirstslice+islice +  self->params.patch_ep );
            // printf("writing file %s \n", nomeout );
            write_data_to_edf( SLICE  + inizio_slice, self->params.num_y, self->params.num_x   ,
                               nomeout);
            sprintf(nomeout,self->params.OUTPUT_FILE, "s",  Nfirstslice+islice+  self->params.patch_ep);
            // printf("writing file %s \n", nomeout );
            write_data_to_edf( SLICE+ inizio_slice + self->params.num_y*self->params.num_x*patch_w   , self->params.num_y, self->params.num_x   ,
                               nomeout);
          }
        } else {

          int iv;
          for(iv=0; iv<VECTORIALITY; iv++ ){
            if( VECTORIALITY==1) {
              sprintf(nomeout,"%s",self->params.OUTPUT_FILE);
            } else {
              if(iv==0) sprintf(nomeout,self->params.OUTPUT_FILE,"c");
              if(iv==1) sprintf(nomeout,self->params.OUTPUT_FILE,"s");
            }

            struct flock fl;
            int fd;
	    int SLICE_size = self->params.num_y*self->params.num_x;
	    float *SLICE_ptr = SLICE;
	    if(self->params.LT_pars) {
	      SLICE_size = lt_dsq*lt_dsq;
	      SLICE_ptr = SLICE_lt ; 
	    }

	    
            fl.l_type   = F_WRLCK;  /* F_RDLCK, F_WRLCK, F_UNLCK    */
            fl.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
            int first0 = self->params.first_slices_2r[ 0 ];
            fl.l_len    = sizeof(float)*SLICE_size; /* length, 0 = to EOF           */
            fl.l_pid    = getpid(); /* our PID                      */

            // printf(" opening file %s for lock \n", nomeout);
            fd = open(nomeout, O_RDWR | O_CREAT , 640);
            if(fd==-1) {
              fprintf(stderr, " ERROR : could not open : %s  \n", nomeout );
              exit(1);
            }
            int gslice;
            for(gslice=0; gslice<nslice_step+do_2by2; gslice++)   {

              if (self->params.verbosity>0)  printf(" locking slice %ld  in file %s \n",(Nfirstslice+islice+gslice +  self->params.patch_ep -  first0), nomeout );
	      /* { */
	      /* 	int i = (Nfirstslice+islice+gslice +  self->params.patch_ep -  first0); */
	      /* 	if(i>0) continue; */
	      /* } */

	      
              fl.l_start  =  ((long int)(Nfirstslice+islice + gslice+ self->params.patch_ep  -  first0  ))*sizeof(float)*SLICE_size;
              fcntl(fd, F_SETLKW, &fl);  /* F_GETLK, F_SETLK, F_SETLKW */
              lseek( fd  ,fl.l_start , SEEK_SET);

              mywrite(fd, SLICE_ptr  + inizio_slice +(gslice+ iv)*SLICE_size*patch_w  ,   sizeof(float)*SLICE_size  );
              fl.l_type   = F_UNLCK;  /* tell it to unlock the region */
              fcntl(fd, F_SETLK, &fl); /* set the region to unlocked */
              // printf("unlocked \n");
            }
	    if(self->params.LT_pars) {
	      free(SLICE_lt);
	    }
            close(fd);
          }
        }
        // t2 = clock();
        // printf( "islice,Nfirstslice =%d,%d   t=%f \n", islice,Nfirstslice,  ( ((long)t2)-((long)t1)   )*1.0f/CLOCKS_PER_SEC);
      }
    }

    if(SINOGRAMMA){
      // free(SINOGRAMMA);
      // free(SLICEold);
    }
    if(SINOGRAMMA_LT) {
      free(SINOGRAMMA_LT);
    }

    
    // int tid;
    //   free(SLICEcalc);


    if(self->params.CONICITY==1  && STEAM_DIRECTION==-1 )  {
      // free(SLICE-   VECTORIALITY* self->params.num_x * self->params.num_y  );
    } else {
      // free(SLICE);
    }
    // free(data);
    if( self->params.CONICITY==0 )  {
      // free(data_orig);
    }

    // free(OVERSAMPLE);
    // free( WORKbis ) ;

    // free( WORK[0]  ) ;

    // free( WORK_perproje - (self->params.CONICITY==0 )*(1+try_2by2)*(num_bins)*self->params.OVERSAMPLING_FACTOR) ;
    sem_wait( &(self->fftw_sem));
    fftwf_free(dumf);
    fftwf_free(dumfC);
    sem_post( &(self->fftw_sem)); //----

  }
  //free_LT_infos(lt_merged_infos);

}

void pro_conic_driver(  CCspace *   self, float *  data , float *SLICE, int  nslices,
			int nslices_data,
			int Nfirstslice,
			int data_start,
			float cpu_offset_x,
			float cpu_offset_y
			){


  int num_bins = self->reading_infos.size_s[0 +1];
  float voxel_size = self->params.VOXEL_SIZE;

  int usa_gpu=0;
  sem_wait( &(self->gpustat_sem));
  if(self->gpu_context ){
    if(self->gpu_context->inuse==0) {
      self->gpu_context->inuse=1;
      usa_gpu=1 ;
    }
  } else {
    sem_post( &(self->gpustat_sem));
  }



  if(usa_gpu) {

    self->gpu_context->pro_gpu_main_conicity(  self->gpu_context,       SLICE ,
					       data,
					       Nfirstslice, nslices, data_start, nslices_data,
					       self->params.SOURCE_DISTANCE, self->params.DETECTOR_DISTANCE,
					       voxel_size/self->params.IMAGE_PIXEL_SIZE_1,   voxel_size/self->params.IMAGE_PIXEL_SIZE_2,
					       voxel_size,
					       self->params.SOURCE_X , self->params.SOURCE_Y );


    self->gpu_context->inuse=0;
    sem_post( &(self->gpustat_sem));
  } else {



    pro_cpu_main_conicity( self->params.num_y, self->params.num_x,      SLICE , self->params.nprojs_span, num_bins , data,
			   self->params.ROTATION_AXIS_POSITION , self->fbp_precalculated.axis_position_corr_s ,
			   self->fbp_precalculated.cos_s, self->fbp_precalculated.sin_s , cpu_offset_x, cpu_offset_y,
			   Nfirstslice, nslices, data_start, nslices_data,
			   self->params.SOURCE_DISTANCE, self->params.DETECTOR_DISTANCE,
			   voxel_size/self->params.IMAGE_PIXEL_SIZE_1,   voxel_size/self->params.IMAGE_PIXEL_SIZE_2,
			   voxel_size,
			   self->params.SOURCE_X , self->params.SOURCE_Y ,   self->proupdate_sem);



    if (self->params.verbosity>1)  printf(" CHIAMO pro_cpu_main_conicity OK\n");

  }
}

void CCspace_Sino_2_Slice_conicity( CCspace *    self, float * dataA, int nslices, int nslices_data,
            int Nfirstslice, int ncpus , int data_start,  int STEAM_DIRECTION) {
}

void haar(float *rings, int n)
{
    int i=0;
    int w=n/2;
    float tmp[n] ;
    while(w>=1) {
        for(i=0;i<w;i++) {
            tmp[i]   = (rings[2*i] + rings[2*i+1])*0.7071067811865475;
            tmp[i+w] = (rings[2*i] - rings[2*i+1])*0.7071067811865475;
        }
        for(i=0;i<(w*2);i++) rings[i] = tmp[i];
        w/=2;
    }
}
void haar_inv(float *rings, int n)
{
    int i=0;
    int w=1;
    float tmp[n] ;
    while(w<=n/2) {
        for(i=0;i<w;i++) {
            tmp[2*i]   = (rings[i] + rings[i+w])*0.7071067811865475;
            tmp[2*i+1] = (rings[i] - rings[i+w] )*0.7071067811865475;
        }
        for(i=0;i<(w*2);i++) rings[i] = tmp[i];
        w*=2;
    }
}

void  proietta_drings(void * void_ccspace_ptr, float* rings_tmp, int doppio , float lip )  {
  CCspace *self = (CCspace *) void_ccspace_ptr ;
  
  int  num_bins = self->reading_infos.size_s[0 +1];
  int i,k, icount;
 
  //if multirings : num_bins *= 4; !
  int rings_size = doppio*num_bins*self->params.NUMBER_OF_RINGS;
  
  for(i=0; i<rings_size; i++) {
    icount=0; 
    for(k=0; k< self->params.N_intervals_hrings; k+=1) {
      if( (i%num_bins)>= self->params.intervals_hrings[k]   )  icount++;
    }
    if(icount%2==0) rings_tmp[i] =0;
  } 

  if( self->params.do_dump_hrings) {
    FILE* filedebug = fopen("rings.dat","w");
    int wrote = fwrite(rings_tmp,doppio *self->params.NUMBER_OF_RINGS*num_bins*sizeof(float),1,filedebug);if(0) printf("%d", wrote);
    fclose(filedebug);
  }

//  float *  dumf      = (float *)         malloc( self->fbp_precalculated.dim_fft  * sizeof(float)  );
//  int dim_fft   = self->fbp_precalculated.dim_fft ;
  /*
  if(0) {
      for(k=0; k<doppio ; k++) {
          for( i=0; i< num_bins; i++) {
              dumf[i] = rings_tmp[k*num_bins+i];
          }
          for(; i<dim_fft/2; i++) {
              dumf[i]=dumf[num_bins-1];
          }
          for(i=dim_fft-1; i>=dim_fft/2; i--) {
              dumf[i]=dumf[0];
          }
          haar( dumf  , dim_fft );
          int  nps = dim_fft;
          int ic=0;
          int ics=2;
          while(nps>=2) {
              // float limit =  nps*1.0f/self->params.ITER_RING_SIZE  ;
              // limit       =  self->params.ITER_RING_HEIGHT *exp(-limit*limit)* sqrt( 1.0 * nps);
              float limit       =   (log(nps)/log(2.0)-1.0)*0.3 ;
              limit       =  self->params.ITER_RING_HEIGHT *exp(-limit*log(2.0))* sqrt( 1.0 * nps);
              for(; ic<ics; ic++) {
                  dumf[ic] = copysignf( min(fabs(dumf[ic]),limit) , dumf[ic]) ;
              }
              nps/=2;
              ics*=2;
          }
          haar_inv( dumf  , dim_fft );
          for( i=0; i< num_bins; i++) {
              rings_tmp[k*num_bins+i] = dumf[i];
          }
      }
  }

  else
  */
  {
      for(k=0; k<doppio ; k++) {
          for( i=0; i< rings_size/doppio; i++) {
              float a,b;
              a = min(fabs(rings_tmp[k*num_bins+i]),self->params.ITER_RING_HEIGHT);
              b = max(fabs(rings_tmp[k*num_bins+i])  -  self->params.ITER_RING_BETA/lip,  0);
              // rings_tmp[k*num_bins+i]=copysignf(a,rings_tmp[k*num_bins+i]);
              rings_tmp[k*num_bins+i]=copysignf(min(a,b),rings_tmp[k*num_bins+i]);
          }
      }
  }
//  free(dumf);
}



void conic_driver(  CCspace *   self,  float *  data, float *SLICE, int  nslices,
		    int nslices_data,
		    int Nfirstslice,
		    int data_start,
		    float *dumf,
		    fcomplex *dumfC,
		    float *WORK_perproje,
		    float **WORK,
		    float *WORKbis,
		    float cpu_offset_x,
		    float cpu_offset_y
		    ) {


  int two_power;
  int dim_fft;

  int i;
  long int num_bins ;
  // int projection ;
  int do_precondition = 1;
  // int blockslice=      self->params.num_y * self->params.num_x   ;



  long int nprojs_span = self->params.nprojs_span;

  /* if(self->fbp_precalculated.fai360) { */
  /*   printf("HALF TOMO ---  not yet allowed with conicity\n"); */
  /*   exit(0); */
  /* } */

  num_bins = self->reading_infos.size_s[0 +1];

  two_power = (int)(log((2. * num_bins - 1)) / log(2.0) + 0.9999);
  dim_fft = 1; for(i=0;i<two_power; i++) dim_fft*=2;
  /* this for extra symmetric padding with fai360 */
  dim_fft*=2;


  long int islice;

  {// ricostruzione slices

    
    for(islice=0; islice<(nslices_data); islice++) {
      long int projection;


      int currproj_ign = -1;
      if (self->params.do_ignore_projections) currproj_ign = 0;

      
      for(projection=0; projection <    nprojs_span       ; projection++) {


	int ignore_this_projection = 0;
	if (currproj_ign != -1) {
	  if (projection == self->params.ignore_angles[currproj_ign]) {
	    ignore_this_projection = 1;
	    currproj_ign++;
	  }
	}
	if (ignore_this_projection) {
	  memset(WORK[projection], 0 , num_bins * sizeof(float)  );
	} else  {
	  memcpy(WORK[projection],
		 data + (islice)*nprojs_span*num_bins + (projection) * num_bins,
		 num_bins * sizeof(float));
	}
	
	
	if(self->fbp_precalculated.fai360)      {      // this means halftomo

	  if(  self->params.PADDING[0]=='0' ) {
	    memset(WORK[projection]  + num_bins,0, ( dim_fft  - num_bins) * sizeof(float));
	  } else {
	    if( 2* self->params.ROTATION_AXIS_POSITION   >  num_bins ) {

	      memcpy(WORKbis,data
		     +(islice)*nprojs_span*num_bins
		     + (
			+((projection+(( self->params.nprojs_span))/2)% ( self->params.nprojs_span))
			)* num_bins
		     ,  num_bins * sizeof(float));

	      int kpad, ipoint ;
	      for ( kpad =  num_bins  ,ipoint= 2*self->params.ROTATION_AXIS_POSITION - num_bins ;
		    kpad <2*self->params.ROTATION_AXIS_POSITION -1      ;
		    kpad++, ipoint-- ) {
		WORK[projection][kpad] =  WORKbis[ ipoint ]      ;
	      }
	      for ( ; kpad < ( +dim_fft + 2*self->params.ROTATION_AXIS_POSITION )/2;
		    kpad++) {
		WORK[projection][kpad] =  WORKbis[ 0   ]      ;
	      }
	      for ( ;  kpad < dim_fft ;
		    kpad++ ) {
		WORK[projection][kpad] =  WORK[projection][ 0   ]      ;
	      }

	    } else {
	      memcpy(WORKbis ,
		     data +(islice)*nprojs_span*num_bins
		     +(
		       ((projection+(( self->params.nprojs_span))/2)% ( self->params.nprojs_span))
		       )*num_bins,
		     num_bins * sizeof(float));
	      int kpad, ipoint ;
	      for ( kpad=  0 ,  ipoint= 2*self->params.ROTATION_AXIS_POSITION- kpad;
		    kpad >  2*self->params.ROTATION_AXIS_POSITION -    num_bins  +1 ;
		    kpad--, ipoint++ ) {
		WORK[projection][ dim_fft -1 + kpad] =  WORKbis[ ipoint ]      ;
	      }
	      for ( ; kpad > ( -(dim_fft)+ (num_bins)+   2*self->params.ROTATION_AXIS_POSITION -   num_bins )/2   + 1 ;
		    kpad-- ) {
		WORK[projection][(dim_fft)-1 + kpad] =  WORKbis[ num_bins -1  ]      ;
	      }
	      for ( ;  kpad > -(dim_fft) + (num_bins) ;
		    kpad-- ) {
		WORK[projection][(dim_fft)-1 + kpad ] =  WORK[projection][  num_bins -1  ]      ;
	      }
	    }
	  }
	} else {
	  if( self->params.PADDING[0]=='0' ) {
	    memset(WORK[projection] + (num_bins),0,((dim_fft) - (num_bins)) * sizeof(float));
	  } else {
	    int kpad;
	    for ( kpad = (num_bins)  ; kpad < ((num_bins)+(dim_fft))/2 ; kpad++ ) {
	      WORK[projection][kpad] = WORK[projection][(num_bins) -1];
	    }
	    for ( kpad = ((num_bins)+(dim_fft))/2 ; kpad <  (dim_fft) ; kpad++ ) {
	      WORK[projection][kpad] = WORK[projection][0];
	    }
	  }
	}
      }
      // qui sotto si potrebbe chiamare fft many
      for(projection=0; projection <    nprojs_span       ; projection++) {
	int  i;
	if( (self->params.FBFILTER >=0 && do_precondition) || self->params.FBFILTER ==2 ) {
	  for(i=0; i<dim_fft; i++) {
	    dumf[i] =   WORK[projection][i] ;
	  }
	  fftwf_execute_dft_r2c( self->fbp_precalculated.planr2c , dumf , (fftwf_complex*) dumfC  );
	  if(do_precondition) {
	    for(i=0;i<=(dim_fft/2);i++) {
	      dumfC[i] = dumfC[i] * self->fbp_precalculated.FILTER[i]/ dim_fft ;
	    }
	  } else {
	    for(i=0;i<=(dim_fft/2);i++) {
	      dumfC[i] = dumfC[i] /  dim_fft ;
	    }
	  }
	  if(self->params.FBFILTER ==2) {
	    for(i=1;i<=(dim_fft/2);i++) {
	      dumfC[i] = ( ( +cimag(dumfC[i]) - _Complex_I  *  crealf(dumfC[i]) ))/(i*M_PI*2.0*2.0/dim_fft);
	    }
	  }
	  fftwf_execute_dft_c2r( self->fbp_precalculated.planc2r, (fftwf_complex*) dumfC   ,  dumf );
	  for(i=0; i<dim_fft; i++) WORK[projection][i]=dumf[i];
	}
  
  
	float ax_pos_corr = self->fbp_precalculated.axis_position_corr_s[ projection ];
	// printf("ax_pos_corr  %e  overlapping   %e  pente_zone   %e\n",ax_pos_corr , self->fbp_precalculated.overlapping, self->fbp_precalculated.pente_zone );
	if(self->fbp_precalculated.fai360) {
	  float peso, dx ;
    
	  for(i=0; i< (num_bins);i++) {
	    /* if ( removelight360) { */
	    /*   WORK[i] = -log( exp(-WORK[i]/normaliselight360) +correction360 )* normaliselight360;  */
	    /* } */
	    if(  fabs(i-  ax_pos_corr ) <= self->fbp_precalculated.overlapping ) {
	      if(  fabs(i- ax_pos_corr ) <= self->fbp_precalculated.flat_zone) {
		WORK[projection][i]*=0.5;
	      } else {
		if( i> ax_pos_corr  ) {
		  dx = ax_pos_corr  - i + self->fbp_precalculated.flat_zone;
		} else {
		  dx = ax_pos_corr - i - self->fbp_precalculated.flat_zone;
		}
		peso = self->fbp_precalculated.prof_shift +
		  self->fbp_precalculated.prof_fact * (0.5*(1.0+ (        dx      )/self->fbp_precalculated.pente_zone));
		WORK[projection][i] *= peso; // peso means weight
	      }
	    } else if (i>ax_pos_corr ) {
	      WORK[projection][i] *= self->fbp_precalculated.prof_shift-0.0;
	    } else {
	      WORK[projection][i] *= self->fbp_precalculated.prof_shift + self->fbp_precalculated.prof_fact*1.0 ;
	    }
	    WORK[projection][i] *=2;
	  }
	}
  

	/* memcpy( WORK_perproje+(islice*nprojs_span*3  + projection*3+1)*(num_bins), */
	/* 	  WORK [projection] , */
	/* 	  (num_bins)* sizeof(float)); */
	memcpy( WORK_perproje+((nslices_data) *projection  + islice)*(num_bins),
		WORK [projection] ,
		(num_bins)* sizeof(float));
      }
    }

    int usa_gpu=0;
    sem_wait( &(self->gpustat_sem));
    if(self->gpu_context ){
      if(self->gpu_context->inuse==0) {
	self->gpu_context->inuse=1;
	usa_gpu=1 ;
      }
    } else {
      sem_post( &(self->gpustat_sem));
    }

    float voxel_size = self->params.VOXEL_SIZE;

    if(usa_gpu) {
      self->gpu_context->gpu_main_conicity(self->gpu_context,     SLICE ,
					   WORK_perproje ,
					   Nfirstslice, nslices, data_start, nslices_data,
					   self->params.SOURCE_DISTANCE, self->params.DETECTOR_DISTANCE,
					   voxel_size/self->params.IMAGE_PIXEL_SIZE_1,   voxel_size/self->params.IMAGE_PIXEL_SIZE_2,
					   voxel_size,
					   self->params.SOURCE_X , self->params.SOURCE_Y );
      self->gpu_context->inuse=0;



      sem_post( &(self->gpustat_sem));
    } else {


      // printf(" chiamo cpu_main_conicity  con self->params.SOURCE_X   %e \n", self->params.SOURCE_X);

      cpu_main_conicity( self->params.num_y, self->params.num_x,      SLICE , nprojs_span, num_bins ,
			 WORK_perproje , self->params.ROTATION_AXIS_POSITION , self->fbp_precalculated.axis_position_corr_s ,
			 self->fbp_precalculated.cos_s, self->fbp_precalculated.sin_s , cpu_offset_x, cpu_offset_y,
			 Nfirstslice, nslices, data_start, nslices_data,
			 self->params.SOURCE_DISTANCE, self->params.DETECTOR_DISTANCE,
			 voxel_size/self->params.IMAGE_PIXEL_SIZE_1,   voxel_size/self->params.IMAGE_PIXEL_SIZE_2,
			 voxel_size,
			 self->params.SOURCE_X , self->params.SOURCE_Y );
    }

    // printf(" self->params.zerooffmask  est %d \n",self->params.zerooffmask   ) ;
    if(self->params.zerooffmask ) {
      int x,y,iv;

      int blockslice =  self->params.num_y * self->params.num_x ;
      for(iv=0; iv<nslices; iv++) {
	for(y=0; y< self->params.num_y; y++) {



	  for(x=0; x<min(( self->fbp_precalculated.minX[y]),(self->params.num_x) ) ; x++) {
	    SLICE[ iv*blockslice + y*(self->params.num_x) +x]=0;
	  }
	  for(x=self->fbp_precalculated.maxX[y]; x<   self->params.num_x; x++) {
	    SLICE[ iv*blockslice + y*( self->params.num_x) +x]=0;
	  }
	}
      }
    }
  }
}




void CCspace_dispense_chunk   ( CCspace * self  , int sn,  int  ntoktransposed , int   npbunches  ) {
  int sn_nb ;
  int  iproc;
  // int num_slices ;
  int richieste[self->nprocs];
  int doni[self->nprocs];
  int nslices_2r;
  long int size1;
  int maxdono, maxrichiesta;// imaxdono, imaxrichiesta ;
  // MPI_Status status;
  // int Nfirstslice ;
  struct timespec delay = {0,5000000}; //500,000 = 1/2 milliseconds
  struct timespec delayrem;
  int niente;

  sn_nb = sn/npbunches ;

  size1 = self->reading_infos.size_s[2*sn_nb +1];

  int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*self->nprocs + self->iproc]  ;
  int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*self->nprocs + self->iproc]  ;

  int my_wished_margine        = self->params.CONICITY_MARGIN_DOWN_wished[sn_nb*self->nprocs + self->iproc ] ;
  int my_wished_margine_up     = self->params.CONICITY_MARGIN_UP_wished[sn_nb*self->nprocs + self->iproc ] ;

  int start_slice_2s = self->reading_infos.slices_mpi_offsets[ sn_nb*self->nprocs + self->iproc] -md ;
  int top_slice_2s   = self->reading_infos.slices_mpi_offsets[ sn_nb*self->nprocs +self->iproc ]
               +self->reading_infos.slices_mpi_numslices[ sn_nb*self->nprocs + self->iproc]
               + mu;

  int total_slices_2s =  top_slice_2s - start_slice_2s ;

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  int start_slice_2r = self->params.first_slices_2r[ sn_nb*self->nprocs + self->iproc]  ;
  int top_slice_2r   = self->params.last_slices_2r [ sn_nb*self->nprocs +self->iproc ] +1 ;

  // int total_slices_2r =  top_slice_2r - start_slice_2r  ;
  // int  base = self->params.first_slices_2r[0];

  int *correspondance = self->params.corr_slice_slicedect ;
  int slice_ptr = start_slice_2r;
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


  while(1) {
    niente=0;
    sem_wait( &(self->slicesrequest_sem));
    if(self->slicesrequest && self->slicesrequest->has_been_set_flag==0 ) {
      if(slice_ptr<top_slice_2r) {
	
	nslices_2r = min( (top_slice_2r -slice_ptr), (self->slicesrequest->nslices_2r)  );
	
	int mydata_start     =  correspondance[ slice_ptr /* -base */ + (int) self->params.SOURCE_Y ] - self->params.first_slices[sn_nb ]  ;
	
	mydata_start = mydata_start - my_wished_margine ;
	
	if(mydata_start<0) {
	  mydata_start=0;
	}
	
	int mydata_end =  correspondance[slice_ptr + (nslices_2r-1)  +  (int) self->params.SOURCE_Y  /* -base   */ ] +1 ; // il +1 !!!!
	mydata_end -= self->params.first_slices[sn_nb]  ;
	
	// mydata_end +=  nslices_2r + my_wished_margine_up ;
	mydata_end +=   my_wished_margine_up ;
	
	if( mydata_end - mydata_start>  total_slices_2s  ) {
	  mydata_end = total_slices_2s+mydata_start ;
	}
	
	
	if(!self->params.CONICITY) {
	  assert( (mydata_end-mydata_start) ==  (md+ nslices_2r+mu)   );
	}
	
	memcpy( self->slicesrequest->data,
		self-> transposeddatatokens->datatokens[ntoktransposed] +
		(mydata_start - start_slice_2s)* self->params.nprojs_span * size1,
		(mydata_end-mydata_start)*self->params.nprojs_span * size1 *sizeof(float)
		);
	
	if (self->params.verbosity>1)  printf(" memcopy done di %ld elementi da %d a %d   \n", (mydata_end-mydata_start)*self->params.nprojs_span * size1,
	       mydata_start, mydata_end );
	
	self->slicesrequest->Nfirstslice  =  slice_ptr ;
	self->slicesrequest->nslices_2r   =  nslices_2r ;
	self->slicesrequest->nslices_data =  (mydata_end-mydata_start);
	self->slicesrequest->data_start   =   mydata_start + self->params.first_slices[sn_nb ] ;
	
	self->slicesrequest->has_been_set_flag=1;
	
	slice_ptr              +=   nslices_2r ;
	
	richieste[self->iproc]    =   0;
	
      } else {
	printf(" richiedo altrove \n");
	richieste[self->iproc]    =  self->slicesrequest->nslices_2r ;
      }
    } else {
      richieste[self->iproc]    =   0;
      niente=1;
    }
    doni[self->iproc]         = top_slice_2r-slice_ptr  ;
    sem_post( &(self->slicesrequest_sem));
    
    if(niente) {
      // printf(" niente \n");
      
      nanosleep(&delay, &delayrem);
    }
    
    
    MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,richieste ,
		  1 , MPI_INT , MPI_COMM_WORLD );
    MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,doni   ,
		  1 , MPI_INT , MPI_COMM_WORLD );
    
    maxdono=0;
    // imaxdono=0;
    maxrichiesta=0;
    // imaxrichiesta=0;
    // se poi funziona sara meglio apparegliare una a una tante piu richeste con doni possibile
    for(iproc=0; iproc<self->nprocs ; iproc++) {
      // printf(" per iproc %d dono %d   self->iproc  %d     \n", iproc, doni[iproc],  self->iproc );
      if( doni[iproc]>maxdono ) {
	maxdono  = doni [iproc]    ;
	// imaxdono = iproc ;
      }
      if(richieste [iproc]>maxrichiesta ) {
	maxrichiesta  =  richieste[iproc]    ;
	// imaxrichiesta = iproc ;
      }
    }
    if(maxdono==0) {
      sem_wait( &(self->slicesrequest_sem));
      self->packet_completion[ sn_nb    ]=1;
      sem_post( &(self->slicesrequest_sem));
      break;
    }
    
    
    /* if(maxrichiesta>0) { // non funziona ancora  */
    /*   if( self->params.CONICITY) { */
    /* 	printf("A COMMUNICATION BETWEEN process %d AND %d WOULD BE POSSIBLE BUT WITH CONICITY IS ON INTERCOMMUNICATION ARE NOT IMPLEMENTED\n",imaxrichiesta,imaxdono );  */
    /*   } else { */
    /* 	printf(" =============================== MAXRICHIESTA %d MAXDONO %d \n", maxrichiesta,maxdono);  */
    
    /* 	if(imaxdono == self->iproc) { */
    /* 	  nslices_2r = min(maxdono , maxrichiesta  ); */
    /* 	  Nfirstslice  =  nslicesdone + num_slices_offset ;  */
    /* 	  MPI_Send(&Nfirstslice, */
    /* 		   1, */
    /* 		   MPI_INT, imaxrichiesta, */
    /* 		   19284, */
    /* 		   MPI_COMM_WORLD); */
    /* 	  MPI_Send(self-> transposeddatatokens->datatokens[ntoktransposed] + nslicesdone* self->params.nprojs_span * size1, */
    /* 		   nslices_2r*self->params.nprojs_span * size1, */
    /* 		   MPI_FLOAT, imaxrichiesta, */
    /* 		   19285, */
    /* 		   MPI_COMM_WORLD); */
    /* 	  nslicesdone              +=   nslices_2r ;  */
    /* 	} */
    /* 	if(imaxrichiesta == self->iproc) { */
    /* 	  nslices_2r = min(maxdono , maxrichiesta  );	 */
    /* 	  MPI_Recv(&(Nfirstslice), */
    /* 		   1, */
    /* 		   MPI_INT,imaxdono , */
    /* 		   19284, */
    /* 		   MPI_COMM_WORLD, &status); */
    /* 	  MPI_Recv(self->slicesrequest->data, */
    /* 		   nslices_2r*self->params.nprojs_span * size1, */
    /* 		   MPI_FLOAT,imaxdono , */
    /* 		   19285, */
    /* 		   MPI_COMM_WORLD, &status); */
    /* 	  sem_wait( &(self->slicesrequest_sem)); */

    /* 	  self->slicesrequest-> my_m_down =  0 ;   //  l' inteprocesso non e' ancora adattato alla conicity */
    /* 	  self->slicesrequest-> my_m_up   =  0 ;  */

    /* 	  self->slicesrequest->Nfirstslice = Nfirstslice;  */
    /* 	  self->slicesrequest->nslices =  nslices_2r ;  */
    /* 	  self->slicesrequest->has_been_set_flag=1; */
    /* 	  sem_post( &(self->slicesrequest_sem)); */
    /* 	} */
    /*   } */
    // }
  }

}

void CCspace_tranpose_chunk    (CCspace * self , int sn, int  ntoktreated, int  ntoktransposed , int npbunches, int STEAM_DIRECTION) {
  int sn_nb ;
  int * size_s;
  // int * pos_s ;
  long int size0, size1 ;
  // int pos0 ;  //   pos1  ;
  long int  num_my_proj;
  float *Tptr, *Tra_ptr;
  long int jproc ;
  int n_myslices=0;
  int nprocs ;
  
  static float * transmit_buffer = NULL ;
  static float * receive_buffer = NULL ;
  
  static long int transmit_size = 0;
  static long int receive_size = 0;

  
  int start_slice, top_slice;
  int islice, ix;
  long int iproj;
  int sendcnts[self->nprocs];
  int recvcnts[self->nprocs];
  int rdispls[self->nprocs];
  int sdispls[self->nprocs];
  char nomeout[10000];

  long int tot;
  int numpjs_offset,numpjs_jproc, numprojs ;
  sn_nb = sn/npbunches ;
  nprocs = self->nprocs  ;


  size_s = self->reading_infos.size_s  ;
  // pos_s = self->reading_infos.pos_s  ;
  size0 = size_s[2*sn_nb +0];
  size1 = size_s[2*sn_nb +1];
  // pos0 = pos_s[2*sn_nb +0];
  // pos1 = pos_s[2*sn_nb +1];


  MPI_Datatype AcTypeMPI;
  {
    int block_lengths[1];                        // # of elt. in each block
    MPI_Aint displacements[1];                   // displac.
    MPI_Datatype typelist[1];                    // list of types
    block_lengths[0] = size1 ;
    typelist[0] = MPI_FLOAT;
    displacements[0] = 0;
#if  MPI_VERSION >1
    MPI_Type_create_struct(1, block_lengths, displacements, typelist, &AcTypeMPI);
#else
    MPI_Type_struct(1, block_lengths, displacements, typelist, &AcTypeMPI);
#endif
    MPI_Type_commit(&AcTypeMPI);
  }




  num_my_proj = self->reading_infos. proj_mpi_numpjs[self->iproc];

  Tptr =  self-> datatokens     ->datatokens[ntoktreated] ;
  Tra_ptr = self-> transposeddatatokens     ->datatokens[ntoktransposed]  ;

  n_myslices = self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + self->iproc ]   ;
  
  int cmar = self->params.CONICITY_MARGIN;


  if(! transmit_buffer) {
    transmit_size = num_my_proj * (size0 +(nprocs)*2*cmar) *  size1*sizeof(float)              ;
    receive_size  =  (n_myslices +2*cmar ) * self->params.nprojs_span   *  size1*sizeof(float) ; 
    transmit_buffer = (float*) malloc( transmit_size  ) ;
    receive_buffer  = (float*) malloc( receive_size   ) ;
  } else {
    long int new_transmit_size = num_my_proj * (size0 +(nprocs)*2*cmar) *  size1*sizeof(float)              ;
    long int new_receive_size  =  (n_myslices +2*cmar ) * self->params.nprojs_span   *  size1*sizeof(float) ; 
    if( new_transmit_size > transmit_size) {
      
      transmit_buffer = (float*) realloc( transmit_buffer, new_transmit_size  ) ;
      transmit_size = new_transmit_size ; 
    } 
    if( new_receive_size > receive_size) {
      receive_buffer = (float*) realloc( receive_buffer, new_receive_size  ) ;
      receive_size = new_receive_size ; 
    } 
  }
  

  // memset(transmit_buffer, 0, num_my_proj * (size0 +(nprocs)*2*cmar) *  size1*sizeof(float) );
  // memset(receive_buffer, 0,  (n_myslices +2*cmar ) * self->params.nprojs_span   *  size1*sizeof(float)  );
  
  if (self->params.verbosity>0)  printf(" processo %d alloca %ld per t and %ld per r \n", self->iproc,
					num_my_proj * size0  *  size1,
					n_myslices*self->params.nprojs_span*size1
					);
  
  if(STEAM_DIRECTION==1) {
    printf("  PREPARO COPIE\n");

    
    tot=0;

    for(jproc=0; jproc<self->nprocs; jproc++) {
      sdispls[jproc]=tot;
      
      int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + jproc]  ;
      int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + jproc]  ;

      start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc] - md  ;
      top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc]  +
	self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + jproc]+
	mu;

#pragma	omp parallel  num_threads(npbunches)
      {
	int myid =  omp_get_thread_num();
	long int mytot = tot;
	for(int  iproj=0 ;iproj<num_my_proj; iproj++) {

	  if(iproj%npbunches == myid ) {
	    memcpy( transmit_buffer+mytot*size1, Tptr +  iproj*size0*size1 + (start_slice)*size1 ,(top_slice-start_slice)* size1*sizeof(float)  );
	  }
	  mytot = mytot + (top_slice-start_slice) ; // * size1 ;
	}
	#pragma omp barrier  
	if(myid==0) {
	  tot = mytot;
	}
	#pragma omp barrier  
      }
      sendcnts[jproc] =  tot-sdispls[jproc];
    }
    printf("  PREPARO COPIE OK \n");
    
    tot=0;
    
    int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + self->iproc]  ;
    int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + self->iproc]  ;
    
    start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + self->iproc] -md ;
    top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs +self->iproc ]
      +self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + self->iproc]
      + mu;
    
    for(jproc=0; jproc<self->nprocs; jproc++) {
      rdispls[jproc]=tot;
      tot = tot + (top_slice-start_slice) * self->reading_infos. proj_mpi_numpjs[jproc]; // * size1
      recvcnts[jproc] =  tot-rdispls[jproc];
    }

    printf("  VADO in MPI \n");
    MPI_Alltoallv(   transmit_buffer , sendcnts, sdispls,
		     AcTypeMPI   ,
		     receive_buffer, recvcnts, rdispls,
		     AcTypeMPI , MPI_COMM_WORLD);
    printf("  VADO in MPI OK\n");

    

    {
      int my_wished_margine        = self->params.CONICITY_MARGIN_DOWN_wished[sn_nb*self->nprocs + self->iproc ] ;
      int my_wished_margine_up     = self->params.CONICITY_MARGIN_UP_wished[sn_nb*self->nprocs + self->iproc ] ;

      self->transposeddatatokens->islice_tracker[ntoktransposed] = (int *) malloc(  (top_slice-start_slice  + my_wished_margine+ my_wished_margine_up )*sizeof(int)) ;
      memset(  self->transposeddatatokens->islice_tracker[ntoktransposed]   ,0,     (top_slice-start_slice+ + my_wished_margine+ my_wished_margine_up   )*sizeof(int)     );
    }
    numprojs =  self->params.nprojs_span ;
    
    int tot_npj = 0;
    for(jproc=0; jproc<self->nprocs; jproc++) {
      tot_npj +=  self->reading_infos. proj_mpi_numpjs [jproc];
    }
    
    /* int LATO = 128; */
    /* printf(" ============================= QUI NB %d \n",npbunches );  */
    // #pragma	omp parallel   num_threads(npbunches)
    {
      
      // int myid =  omp_get_thread_num();
      
      // printf(" ============================= QUI  myid %d \n",myid );
      
      // int Isl =0; 
      // for(int Sslicing = 0; Sslicing*LATO<(top_slice-start_slice) ; Sslicing ++) {
      
      // int totproj = 0;
      // 	for(int Pslicing = 0; Pslicing*LATO<  tot_npj ; Pslicing ++) {
      // 	  if( myid==npbunches-1) {
      /*   printf(" transpose ibunch %d S,P %d %d \n",  myid, Sslicing,Pslicing  ) ; */
      /* } */
      /* Isl++ ; */
      /* if(  Isl%npbunches !=  myid   ) { */
      /*   continue ;  */
      /* } */
      for(jproc=0; jproc<self->nprocs; jproc++) {
	
	numpjs_jproc  =  self->reading_infos. proj_mpi_numpjs [jproc] ;
	numpjs_offset =  self->reading_infos. proj_mpi_offsets[jproc] ;
	
#pragma omp parallel  for private(islice,ix)     num_threads(npbunches)
	for(iproj=  0  ; iproj<  numpjs_jproc ;  iproj++) { // if( iproj+totproj  >= Pslicing*LATO && iproj+totproj <  ( Pslicing+1 ) * LATO )
	  for(islice= 0 ; islice<   (top_slice-start_slice); islice++) {  // if( islice>= Sslicing*LATO & islice <(Sslicing+1)*LATO )
 	    for(ix=0; ix<size1; ix++) {
	      Tra_ptr[(islice*numprojs  +  (iproj+numpjs_offset))*size1 + ix   ] =
		receive_buffer[ rdispls[jproc]*size1  + (iproj*(top_slice-start_slice) + islice)*size1 +ix   ] ;
	    }
	  }
	}
	// totproj   +=   numpjs_jproc  ;
	
	//}
	// }
      }
    }
  } else {
    
    tot=0;
    for(jproc=0; jproc<self->nprocs; jproc++) {
      sdispls[jproc]=tot;

      int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + jproc]  ;
      int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + jproc]  ;

      start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc] - md  ;
      top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc]  +
	self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + jproc]+
	mu;

      #pragma	omp parallel  num_threads(npbunches)
      {
	int myid =  omp_get_thread_num();

	int mytot = tot;
	for(int  iproj=0 ;iproj<num_my_proj; iproj++) {
	  if(iproj%npbunches == myid ) {
	    memset(  Tptr +  iproj*size0*size1 + (start_slice)*size1 ,0, (top_slice-start_slice)* size1*sizeof(float)  );
	  }
	  mytot = mytot + (top_slice-start_slice); // * size1 ;
	}
#pragma omp barrier  
	if(myid==0) {
	  tot = mytot;
	}
#pragma omp barrier  
      }      
      sendcnts[jproc] =  tot-sdispls[jproc];
    }
    

    int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + self->iproc]  ;
    int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + self->iproc]  ;

    start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + self->iproc] -md ;
    top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs +self->iproc ]
      +self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + self->iproc]
      + mu;



    tot=0;
    for(jproc=0; jproc<self->nprocs; jproc++) {
      rdispls[jproc]=tot;
      tot = tot + (top_slice-start_slice)* self->reading_infos. proj_mpi_numpjs[jproc]; //  * size1
      recvcnts[jproc] =  tot-rdispls[jproc];
    }


    numprojs =  self->params.nprojs_span ;

    for(jproc=0; jproc<self->nprocs; jproc++) {

      numpjs_jproc  =  self->reading_infos. proj_mpi_numpjs [jproc] ;
      numpjs_offset =  self->reading_infos. proj_mpi_offsets[jproc] ;
      
#pragma omp parallel  for private(islice,ix)     num_threads(npbunches)
      for(iproj=0 ; iproj< numpjs_jproc  ;  iproj++) {
	
	for(islice=0; islice<(top_slice-start_slice); islice++) {
	  for(ix=0; ix<size1; ix++) {

	    
	    receive_buffer[ rdispls[jproc] *size1  + (iproj*(top_slice-start_slice) + islice)*size1 +ix   ] =
	      Tra_ptr[(islice*numprojs  +  (iproj+numpjs_offset))*size1 + ix   ]  ;
	  }
	}
      }
      
    }


    MPI_Alltoallv(  receive_buffer, recvcnts, rdispls,
		    AcTypeMPI  ,
		    transmit_buffer , sendcnts, sdispls,
		    AcTypeMPI , MPI_COMM_WORLD);

    tot=0;
    for(jproc=0; jproc<self->nprocs; jproc++) {

      int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + jproc]  ;
      int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + jproc]  ;

      start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc] - md  ;
      top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + jproc]  +
	self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + jproc]+
	mu;

#pragma	omp parallel  num_threads(npbunches)
      {
	int myid =  omp_get_thread_num();
	int mytot = tot;
	for(int  iproj=0 ;iproj<num_my_proj; iproj++) {
	  if(iproj%npbunches == myid ) {
	    int k;
	    for(k=0; k < (top_slice-start_slice)* size1; k++) {
	      Tptr [iproj*size0*size1 + (start_slice)*size1 +k] += transmit_buffer[mytot+k];
	    }
	  }
	  mytot = mytot + (top_slice-start_slice)* size1 ;
	}
#pragma omp barrier  
	if(myid==0) {
	  tot = mytot;
	}
#pragma omp barrier  
      }
    }
    
    int iproc;
    int prevnprojs =0;
    for(iproc=0; iproc<self->iproc; iproc++) {
      prevnprojs+= self->reading_infos. proj_mpi_numpjs[iproc];
    }
    if(!self->invertedsteam_output_created){
      
      if(self->params.PROJ_OUTPUT_RESTRAINED==0 ) {
	
	float *dum = (float *) malloc(self->params.NUM_IMAGE_2 * self->params.NUM_IMAGE_1 *sizeof(float)) ;
	memset(dum,0, self->params.NUM_IMAGE_2 * self->params.NUM_IMAGE_1 *sizeof(float));
	for( iproj=0 ;iproj<num_my_proj; iproj++) {
	  sprintf(nomeout,self->params.PROJ_OUTPUT_FILE, prevnprojs+  iproj );
	  write_data_to_edf(dum ,self->params.NUM_IMAGE_2 , self->params.NUM_IMAGE_1   , nomeout);
	}
	free(dum);
      } else {
	
	int nchuncks = self->reading_infos.nchunks;
	int sizeV = self->reading_infos.pos_s[ 2*(nchuncks-1) +0]-self->reading_infos.pos_s[ 2*0  +0]  +
	  self->reading_infos.slices_mpi_offsets[ (nchuncks-1)* nprocs + nprocs-1]-
	  self->reading_infos.slices_mpi_offsets[ (nchuncks-1)* nprocs + 0]+
	  self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + nprocs-1 ];
	// qui ci si e' dimenticati dei margin up e down. Per ora questa opzione restrained
	// sara utilizzata solamente si  geometria parallela. Sara comunque interessante vedere
	// se i down e up estremali sono nulli. Questa cosa semplificherebbe il meccanismo quando lo
	// si volesse applicare anche al caso conico
	
	float *dum = (float *) malloc(sizeV * self->params.NUM_IMAGE_1 *sizeof(float)) ;
	memset(dum,0, sizeV  * self->params.NUM_IMAGE_1 *sizeof(float));
	
	for( iproj=0 ;iproj<num_my_proj; iproj++) {
	  sprintf(nomeout,self->params.PROJ_OUTPUT_FILE, prevnprojs+  iproj );
	  
	  write_data_to_edf_restrained(dum ,self->params.NUM_IMAGE_2 , self->params.NUM_IMAGE_1   ,
				       self->reading_infos.pos_s[ 2*0  +0],
				       sizeV,
				       nomeout);
	}
	free(dum);
      }
      self->invertedsteam_output_created=1;
    }
    for( iproj=0 ;iproj<num_my_proj; iproj++) {
      int mu = self->params.CONICITY_MARGIN_UP   [ sn_nb*nprocs + nprocs-1]  ;
      int md = self->params.CONICITY_MARGIN_DOWN [ sn_nb*nprocs + 0]  ;

      start_slice = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + 0] - md  ;
      top_slice   = self->reading_infos.slices_mpi_offsets[ sn_nb*nprocs + nprocs-1]  +
	self->reading_infos.slices_mpi_numslices[ sn_nb*nprocs + nprocs-1 ]+
	mu;
      
      sprintf(nomeout,self->params.PROJ_OUTPUT_FILE, prevnprojs+  iproj );
      
      if(self->params.PROJ_OUTPUT_RESTRAINED==0 ) {
	
	write_add_data_to_edf(Tptr + iproj*size0*size1 + (start_slice)*size1  ,self->params.NUM_IMAGE_2 , self->params.NUM_IMAGE_1   , nomeout,
			      self->reading_infos.pos_s[ 2*sn_nb +0], top_slice-start_slice);
	
      } else {
	
	int nchuncks = self->reading_infos.nchunks;
	int sizeV = self->reading_infos.pos_s[ 2*(nchuncks-1) +0]-self->reading_infos.pos_s[ 2*0  +0]  +
	  self->reading_infos.slices_mpi_offsets[ (nchuncks-1)* nprocs + nprocs-1]-
	  self->reading_infos.slices_mpi_offsets[ (nchuncks-1)* nprocs + 0];
	// qui ci si e' dimenticati dei margin up e down. Per ora questa opzione restrained
	// sara utilizzata solamente si  geometria parallela. Sara comunque interessante vedere
	// se i down e up estremali sono nulli. Questa cosa semplificherebbe il meccanismo quando lo
	// si volesse applicare anche al caso conico
	write_add_data_to_edf(Tptr + iproj*size0*size1 + (start_slice)*size1  , sizeV , self->params.NUM_IMAGE_1   , nomeout,
			      self->reading_infos.pos_s[ 2*sn_nb +0]-self->reading_infos.pos_s[ 2*0  +0] ,
			      top_slice-start_slice);
	
      }
      
    }
  }
  /* for(islice=0; islice<(top_slice-start_slice); islice++) { */
  /*   char nome[1000] ;  */
  /*   sprintf( nome, "sino_slice_%04d" , islice+ start_slice  ); */
  /*   FILE * output = fopen(nome,"w") ;  */
  /*   fwrite( Tra_ptr + islice*numprojs*size1, sizeof(float), numprojs*size1 , output ); */
  /*   fclose(output); */
  /* } */
  /* printf(" free\n"); */
  /* free(transmit_buffer); */
  /* free(receive_buffer); */
  /* printf(" free OK \n"); */
}

float bh_lut( float u , float *F , float *U,  int N ) {

  int l,m,h;
  l=-1;
  h=N;
  while(h>l+1) {
    m=(l+h)/2;
    if(   u>U[m]     ) {
      l=m;
    } else {
      h=m;
    }
  }
  if(l==-1) l=0;
  if(l==N-1) l=N-2;
  float d = u-U[l];
  float a0,a1;
  a0 = F[l];
  a1 = F[l+1];

  return u*(a0*(1-d)+d*a1);
}


void CCspace_preprocess_chunk(CCspace *  self , int sn, int  ntok, int  ntokt , int npbunches, int ncpus , int doff2){
  int sn_nb ;
  int *size_s_, *pos_s_;
  int *size_s, *pos_s;
  float *Rawptr , *FFptrA, *FFptrB, *Tptr, *Bptr,  *DFptr;;
  int ibunch , num_my_proj;
  long int npj;
  int pstart, pend ;
  int Size0, Size1;
  int Pos0, Pos1;
  int  gdim1 ; //   gdim0 ;
  int i,j;
  float * buffer ;
  float * correction ;
  float normalise;
  int sizeofdatatype[2];
  sizeofdatatype[FloatValue]   =4;
  sizeofdatatype[UnsignedShort]=2;

  if (self->params.verbosity>1)  printf( " packet %d in CCspace_preprocess_chunk\n", sn);

  // normalise = M_PI/ 2.0  *  1.0 / self->params.nprojs_span   *    1.0e6*0.01 / self->params.IMAGE_PIXEL_SIZE_1 ;
  normalise = M_PI/ 2.0  *  1.0 / self->params.nprojs_span   *    1.0e6*0.01 / self->params.VOXEL_SIZE ;
  if(self->params.ITERATIVE_CORRECTIONS==0 && self->params.DZPERPROJ!=0) {
    int nps = M_PI/  self->params.ANGLE_BETWEEN_PROJECTIONS   ;
    normalise = M_PI/ 2.0  *  1.0 / nps  *    1.0e6*0.01 / self->params.VOXEL_SIZE ;
  }
 
  omp_set_nested(0);

  size_s_= self->reading_infos.size_s_ ;
  pos_s_ = self->reading_infos.pos_s_  ;

  size_s = self->reading_infos.size_s  ;
  pos_s  = self->reading_infos.pos_s   ;

  sn_nb = sn/npbunches ;


  int num_previous_projs=0;  
  for(i=0; i< self->iproc; i++) {
    num_previous_projs +=  self->reading_infos. proj_mpi_numpjs[ i ] ; 
  }
  
  
  num_my_proj = self->reading_infos. proj_mpi_numpjs[self->iproc];
  // printf("E  QUI CI SONO %d \n", num_my_proj );
  
  ibunch = sn % npbunches ;

  pstart = (int) (   ((num_my_proj*1.0)/ npbunches)  *ibunch  +0.49999999999 ) ;
  pend   = (int)(     ((num_my_proj*1.0)/ npbunches)  *(ibunch+1)   +0.4999999999 );



  int  num_myprojs_reduced_forthisproc = (int)( num_my_proj *1.0/ self->params.RAWDATA_MEMORY_REUSE+0.99999999999);
  if (num_myprojs_reduced_forthisproc%npbunches > 0)  {
    num_myprojs_reduced_forthisproc = num_myprojs_reduced_forthisproc + ( npbunches -  num_myprojs_reduced_forthisproc%npbunches);
  }
  int  num_myprojs_reduced_forAllThreads = num_myprojs_reduced_forthisproc/npbunches ;

  // @@@@@@@
  Size0 = size_s_[2*sn_nb +0];
  Size1 = size_s_[2*sn_nb +1];
  Pos0 = pos_s_[2*sn_nb +0];
  Pos1 = pos_s_[2*sn_nb +1];

  {
    int size0, size1 ;
    size0 = size_s[2*sn_nb +0] ;
    size1 = size_s[2*sn_nb +1] ;
    int size_pa0, size_pa1 ;
    size_pa0 =   max(size0 +2*self->params.PAGANIN_MARGE,Size0);
    size_pa1 =   max(size1 +2*self->params.PAGANIN_MARGE,Size1);
    size_pa0 = (int ) ( exp(log(2.0)* ( (int) ( log(size_pa0*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );  // always 2**n > roundfft
    size_pa1 = (int ) ( exp(log(2.0)* ( (int) ( log(size_pa1*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );
    buffer = (float * ) malloc( sizeof(float)*(size_pa0)*(size_pa1) );
    correction = (float * ) malloc( sizeof(float)*(size_pa0)*(size_pa1) );

  }
  // gdim0 = self->params.NUM_IMAGE_2 ;
  gdim1 = self->params.NUM_IMAGE_1 ;

  for(npj=pstart; npj<pend  ; npj++) {
    
    if(npj%100==0)
      if (self->params.verbosity>1)
	printf( " packet %d in CCspace_preprocess_chunk %ld \n", sn, npj);
    
    /* int size0, size1 ;  */
    /* size0 = size_s[2*sn_nb +0] ;  */
    /* size1 = size_s[2*sn_nb +1] ;  */
    Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj*Size0*Size1;
    if(self->params.DZPERPROJ==0) {
      
      long int posff_a = (long int) self->reading_infos.ff_indexes_coefficients[ 6*npj+0 ];
      long int posff_b = (long int) self->reading_infos.ff_indexes_coefficients[ 6*npj+1 ];
      float coeff_a    = self->reading_infos.ff_indexes_coefficients[ 6*npj+2 ];
      float coeff_b    = self->reading_infos.ff_indexes_coefficients[ 6*npj+3 ];
      
      FFptrA = self->ff_rawdatatokens->datatokens[ntok] +posff_a*Size0*Size1 ;
      if(posff_b!=-1)
	FFptrB = self->ff_rawdatatokens->datatokens[ntok] +posff_b*Size0*Size1 ;
      else
	FFptrB = FFptrA ;
      
      Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];
      Bptr =  self->background  ;
      DFptr = self->ffcorr  ;



      float current=1.0 ;
      
      if( strcmp(self->reading_infos.proj_reading_type , "edf")==0 ){
	
	if (self->params.CORRECT_FLATFIELD )  {
	  
	  // int iread =    self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
	  int iread  =  self->reading_infos.my_proj_num_list[npj]  + self->reading_infos.proj_num_offset_list[sn_nb] ;
	  
	  assert( iread< self->reading_infos.currents_lenght) ;
	  current = self->reading_infos.currents[iread];
	} else {
	  current = 1.0; 
	}


	
	if(self->params.SUBTRACT_BACKGROUND )    {
	  // #pragma	omp parallel for private(i,j) num_threads (ncpus)
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] =  (Rawptr[i*Size1  +j] -  Bptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ])/current;
	    }
	  }
	} else {
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] =  (Rawptr[i*Size1  +j] ) / current;
	    }
	  }
	}
      }
      

      if(self->params.CORRECT_FLATFIELD )    {
	if(posff_b ==-1) {
	  // #pragma	omp parallel for private(i,j) num_threads (ncpus)
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] = (Rawptr[i*Size1  +j]+self->params.OFFSETRADIOETFF/current ) / (FFptrA[i*Size1  +j] )      ;
	    }
	  }
	} else {
	  // #pragma	omp parallel for private(i,j) num_threads (ncpus)
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] = (Rawptr[i*Size1  +j]+self->params.OFFSETRADIOETFF/current ) /
		(  coeff_a*  FFptrA[i*Size1  +j] + coeff_b*  FFptrB[i*Size1  +j] )      ;
	    }
	  }
	}
      }
    }
    
    if(self->params.TAKE_LOGARITHM)    {
      float SUM=0.0;
      // #pragma	omp parallel for private(i,j) num_threads (ncpus)
      for(i=0; i<Size0; i++) {
	for(j=0; j< Size1; j++) {
	  SUM+= Rawptr[i*Size1  +j]; 
	  Rawptr[i*Size1  +j] = max(Rawptr[i*Size1  +j], self->params.ZEROCLIPVALUE );
	  Rawptr[i*Size1  +j] = min(Rawptr[i*Size1  +j], self->params.ONECLIPVALUE );
	}
      }
      // printf("DEBUG SUM  %d %e %d %d\n", npj,   SUM , Size0,Size1 ) ; 
    }
    if(self->params.DZPERPROJ==0) {   
      if(self->params.DOUBLEFFCORRECTION )    {
	DFptr = self->ffcorr  ;
	if(self->params.TAKE_LOGARITHM )    {
	  // printf( " applying double ff\n");
	  // #pragma	omp parallel for private(i,j) num_threads (ncpus)
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j] *  DFptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ];
	    }
	  }
	} else {
	  // #pragma	omp parallel for private(i,j) num_threads (ncpus)
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j] +  DFptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ];
	    }
	  }
	}
      }
    }
  }
  
  if(self->params.CCD_FILTER_KIND!= CCD_FILTER_NONE_ID) {
    if (self->params.verbosity>1) printf(" vado a filtrare ccd sn=%d \n",sn);
    for(npj=pstart; npj<pend  ; npj++) {
      // int npj_absolute = npj+     self->reading_infos. proj_mpi_offsets[self->iproc] ;
      
      /* int size0, size1 ;  */
      /* size0 = size_s[2*sn_nb +0] ;  */
      /* size1 = size_s[2*sn_nb +1] ;  */
      Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj*Size0*Size1;
      
      Filter_CCD(self,  buffer,  Rawptr,
		 Pos0, Pos1, Size0, Size1,
		 pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
		 size_s[2*sn_nb +0], size_s[2*sn_nb +1],
		 self->params.CCD_FILTER_KIND ,   // ok
		 self->params.CCD_FILTER_PARA,    // void *
		 // self->axis_correctionsL[npj_absolute],
		 ncpus
		 );
    }
    if (self->params.verbosity>1) printf(" vado a filtrare ccd OK sn=%d\n",sn);
  }
  
  
  if( self->reading_infos.corr_file_list_lenght ){
    if(self->params.DZPERPROJ!=0) {
      printf(" ERROR :   multipaganin not yet adapted to  elicoidal scan \n");
      exit(1);
    }
    /* double corrmin, corrmax ;  */
    /* corrmin=-1000000; */
    /* corrmax = 1000000; */
    // double sum=0.0;
    for(npj=pstart; npj<pend  ; npj++) {

      long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch  ;
      int npj_absolute =self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
      if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
	continue;
      }
      
      int start_line=0;
      int Dim_2 = self->reading_infos.Dim_2  ;
      if(self->reading_infos.corr_restrained) {
	start_line = self->reading_infos.corr_start_2   ;
	Dim_2  =    self->reading_infos.corr_real_2  ;
      }


      int iread;
      if(1) {
	iread = self->reading_infos.my_proj_num_list[npj]  + self->reading_infos.proj_num_offset_list[sn_nb] ;
      } else{
	iread = npj;
      }
      if(   !(iread>=0 && iread< self->reading_infos.numpjs) ) {
	continue;
      }
      printf (" iread npj %d %ld\n", iread, npj);
 
      read_data_from_edf( self->reading_infos.corr_file_list[ iread ],
			  correction    , // target_float @@@@@@@@@@@@@@@@@@@@@
			  (char*) buffer  ,   // work buffer
			  self->reading_infos.corr_datatype, //
			  self->params.ROTATION_VERTICAL,
			  self->reading_infos.corr_headerSizes[  iread  ],
			  pos_s_[2*sn_nb]-start_line,  pos_s_[2*sn_nb+1],
			  size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
			  sizeofdatatype[ self->reading_infos.corr_datatype],
			  Dim_2,self->reading_infos.Dim_1,
			  self->reading_infos.corr_byteorder,
			  self->params.BINNING, &(self->filereading_sem)
			  );

      
      Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted*Size0*Size1;
      if(1) {
	
	// double sum=0.0;
	float d;
	int II;
	for(i=0; i<Size0; i++) {
	  II = i + pos_s_[2*sn_nb] ; 
	  if( II >= start_line && (II-start_line)<Dim_2) {
	    for(j=0; j< Size1; j++) {
	      d = correction[(II-start_line)*Size1  +j] ;
	      Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j] *exp( d );
	      // sum+= d;
	      /* if ( d>corrmax) corrmax = d;  */
	      /* if(d<corrmin) corrmin = d ;  */
	    }
	  }
	}
	// printf(" LEGGENDO %s SUM %e  \n", self->reading_infos.corr_file_list[ npj  ] , sum );
      }
    }
    // printf(" LEGGENDO  SUM %e  corrmin %e corrmax %e\n",  sum , corrmin, corrmax);
  }
  
  
  
  if(self->params.DO_PAGANIN )    {
    Rawptr  = self-> rawdatatokens->datatokens[ntok] ;
    if (self->params.verbosity>1)
      printf(" chiamo paganin  packet %d\n", sn);

    // int mystart =  self->reading_infos.tot_proj_num_list[  num_previous_projs +  pstart] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
    int mystart = 0;
    if( pend>pstart) {
      mystart = self->reading_infos.tot_proj_num_list[  num_previous_projs +  pstart] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
    }

    
    Paganin( self,  Rawptr,
	     Pos0, Pos1, Size0, Size1,
	     pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
	     size_s[2*sn_nb +0], size_s[2*sn_nb +1],
	     &(self->params) , ncpus,   &(self->fftw_sem ),
	     pstart, pend, self->reading_infos. proj_mpi_offsets[self->iproc],
	     self->reading_infos. proj_num_offset_list[ sn_nb ] ,
	     mystart,
	     npbunches,
	     ibunch
	     );
  }
  
 



  for(npj=pstart; npj<pend  ; npj++) {
    int npj_absolute = self->reading_infos.tot_proj_num_list[  num_previous_projs +  npj] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
    // *@@* inutile continuare qui se npj_absolute non nei limiti
    if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
      Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];
      int k;
      // printf(" METTO A NAN la Treated  \n");
      for(k=0; k< size_s[2*sn_nb +0]*size_s[2*sn_nb +1]; k++) {
	Tptr[k]=NAN;
      }
      continue;
    }
    int size0, size1 ;
    size0 = size_s[2*sn_nb +0] ;
    size1 = size_s[2*sn_nb +1] ;
    Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj*Size0*Size1;
    
    Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];


    

    // memset( Tptr, 0,  size_s[2*sn_nb +0]*size_s[2*sn_nb +1]*sizeof(float)   );

    
    if(self->params.TAKE_LOGARITHM )    {
      /* // #pragma	omp parallel for private(i,j) num_threads (ncpus) */
      for(i=0; i<Size0; i++) {
        for(j=0; j< Size1; j++) {
          Rawptr[i*Size1  +j] = max(Rawptr[i*Size1  +j], self->params.ZEROCLIPVALUE );
        }
      }
    }
     
    // printf("ABSOLUTE %d\n",npj_absolute );

    float corrL=0;
    
    if(npj_absolute>=0 && npj_absolute< self->reading_infos.numpjs ) {
      corrL = self->axis_correctionsL[npj_absolute]; 
    }

    // printf("(interleaved )  PER PROJ %d %ld offset sn_nb %e %d CORRE %e \n",npj_absolute, npj,  corrL , sn_nb, self->reading_infos. proj_num_offset_list[sn_nb]  );

    Filter_and_Trim( self, buffer, Tptr  , Rawptr,
		     Pos0, Pos1, Size0, Size1,
		     pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
		     size_s[2*sn_nb +0], size_s[2*sn_nb +1],
		     corrL,
		     ncpus
		     );
    
    
    
    if(self->params.TAKE_LOGARITHM )    {

	if(self->params.BH_LUT_F )    {
	  for(i=0; i<size0; i++) {
	    for(j=0; j< size1; j++) {
	      Tptr[i*size1  +j] =  bh_lut( (-log( Tptr [i*size1  +j]))  , self->params.BH_LUT_F , self->params.BH_LUT_U,  self->params.BH_LUT_N ) *normalise;
	    }
	  }
	} else {
	  for(i=0; i<size0; i++) {
	    for(j=0; j< size1; j++) {
	      Tptr[i*size1  +j] = -log( Tptr [i*size1  +j] )  *normalise;
	    }
	  }
	}



      
    } else {
      // #pragma	omp parallel for private(i,j) num_threads (ncpus)
      for(i=0; i<size0; i++) {
	for(j=0; j< size1; j++) {
	  Tptr[i*size1  +j] =   Tptr [i*size1  +j]  *normalise;
	}
      }
    }
    if(self->params.STRAIGTHEN_SINOS) {
      for(i=0; i<size0; i++) {
	float ff0,ff1,ff2, f0,f1,f2,x;
	ff0=0.0f;
	ff1=0.0f;
	ff2=0.0f;
	float sum0=0, sum1=0, sum2=0;
	for(j=0; j< size1; j++) {
	  x = 2.0f*(j+0.5f-size1/2.0f)/size1;
	  f0=1.0f;
	  f1= x;
	  f2= (3.0f*x*x-1.0f);
	  ff0=ff0+f0*Tptr [i*size1  +j];
	  ff1=ff1+f1*Tptr [i*size1  +j];
	  ff2=ff2+f2*Tptr [i*size1  +j];
	  sum0+=f0*f0 ; 
	  sum1+=f1*f1 ; 
	  sum2+=f2*f2 ; 
	}
	for(j=0; j< size1; j++) {
	  x = 2.0f*(j+0.5f-size1/2.0f)/size1;
	  f0=1.0f;
	  f1= x;
	  f2= (3.0f*x*x-1.0f);
	  //	  Tptr [i*size1  +j]=Tptr [i*size1  +j]-ff0*f0-ff1*f1-ff2*f2;
	  Tptr [i*size1  +j]=Tptr [i*size1  +j]- (ff0*f0/sum0+ff1*f1/sum1+ff2*f2/sum2);
	}
      }
    }
    // printf(" G MIDDLE %e \n", Tptr [(size0*size1)/2  ]     );
  }
  free(buffer);
  free(correction);
}


void CCspace_InterleavedReadingPreProcessing_chunk(CCspace *  self ,int sn,int  ntok, int  ntokt , int npbunches,int ncpus, int do_ff2 ){
  int sn_nb ;
  int *size_s_, *pos_s_;
  int *size_s, *pos_s;
  float *Rawptr , *FFptrA, *FFptrB, *Tptr, *Bptr,  *DFptr;;
  long int ibunch , num_my_proj;
  long int npj;
  int pstart, pend ;
  int Size0, Size1;
  int Pos0, Pos1;
  int  gdim1 ; //   gdim0 ;
  int i,j;
  float * buffer ;
  float *correction;
  float normalise;
  int sizeofdatatype[2];
  sizeofdatatype[FloatValue]   =4;
  sizeofdatatype[UnsignedShort]=2;
  
  if (self->params.verbosity>1)
    printf( " packet %d in InterleavedReadingPreProcessing\n", sn);
  
  // normalise = M_PI/ 2.0  *  1.0 / self->params.nprojs_span   *    1.0e6*0.01 / self->params.IMAGE_PIXEL_SIZE_1 ;
  normalise = M_PI/ 2.0  *  1.0 / self->params.nprojs_span   *    1.0e6*0.01 / self->params.VOXEL_SIZE ;
  
  omp_set_nested(0);
  
  size_s_= self->reading_infos.size_s_ ;
  pos_s_ = self->reading_infos.pos_s_  ;
  
  size_s = self->reading_infos.size_s  ;
  pos_s  = self->reading_infos.pos_s   ;
  
  sn_nb = sn/npbunches ;


  int num_previous_projs=0;  
  for(i=0; i< self->iproc; i++) {
    num_previous_projs +=  self->reading_infos. proj_mpi_numpjs[ i ] ; 
  }


  
  num_my_proj = self->reading_infos. proj_mpi_numpjs[self->iproc];
  ibunch = sn % npbunches ;
  
  Size0 = size_s_[2*sn_nb +0];
  Size1 = size_s_[2*sn_nb +1];
  Pos0 = pos_s_[2*sn_nb +0];
  Pos1 = pos_s_[2*sn_nb +1];
  
  {
    int size0, size1 ;
    size0 = size_s[2*sn_nb +0] ;
    size1 = size_s[2*sn_nb +1] ;
    int size_pa0, size_pa1 ;
    size_pa0 =   max(size0 +2*self->params.PAGANIN_MARGE,Size0);
    size_pa1 =   max(size1 +2*self->params.PAGANIN_MARGE,Size1);
    size_pa0 = (int ) ( exp(log(2.0)* ( (int) ( log(size_pa0*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );
    size_pa1 = (int ) ( exp(log(2.0)* ( (int) ( log(size_pa1*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );
    buffer = (float * ) malloc( sizeof(float)*(size_pa0)*(size_pa1) );

    printf(" ALLOCO %d %d \n", (size_pa0), (size_pa1)  );
    correction  = (float * ) malloc( sizeof(float)*(size_pa0)*(size_pa1) );
    printf(" ALLOCO p= %p \n", correction  );
    
  }
  // gdim0 = self->params.NUM_IMAGE_2 ;
  gdim1 = self->params.NUM_IMAGE_1 ;
  
  
  int Pstart = (int) (   ((num_my_proj*1.0)/ npbunches)  *ibunch  +0.49999999999 ) ;
  int Pend   = (int)(     ((num_my_proj*1.0)/ npbunches)  *(ibunch+1)   +0.4999999999 );
  
  int  num_myprojs_reduced_forthisproc = (int)( num_my_proj *1.0/ self->params.RAWDATA_MEMORY_REUSE+0.99999999999);
  
  if (num_myprojs_reduced_forthisproc%npbunches > 0)  {
    num_myprojs_reduced_forthisproc = num_myprojs_reduced_forthisproc + ( npbunches -  num_myprojs_reduced_forthisproc%npbunches);
  }
  int  num_myprojs_reduced_forAllThreads = num_myprojs_reduced_forthisproc/npbunches ;
  
  int ivolta=0;
  for(ivolta=0; ivolta< self->params.RAWDATA_MEMORY_REUSE; ivolta++) {
    pstart = Pstart + ivolta*num_myprojs_reduced_forAllThreads;
    pend   = pstart + num_myprojs_reduced_forAllThreads;
    if(pend>Pend) pend=Pend;
    
    // int npj_absolute = npj+     self->reading_infos. proj_mpi_offsets[self->iproc] ;
    {
      // qua bisogna leggere
      // pstart +     self->reading_infos. proj_mpi_offsets[self->iproc]
      // pend   +     self->reading_infos. proj_mpi_offsets[self->iproc]
      
      CCspace_read_chunk    (self ,  sn,   ntok ,  npbunches,
			     self->params.ROTATION_VERTICAL ,
			     self->params.BINNING ,  // !!!!!!!!!!!!!!
			     1,pstart , pend,
			     (-pstart) + num_myprojs_reduced_forAllThreads*ibunch );
    }


    if (do_ff2 == 1) {
      // Processes (bound to CPUs) are handled by MPI.
      // Threads (bound to cores) are handled by Python.
      sem_wait( &(self->ff_sem) );
      // I am the first thread to arrive here - allocate memory
      // As the next chunk will not necessarily be of the same size, we use this mechanism.
      // Chunk 1 has status [0, ..., sn_nb[, chunk 2 has status [sn_nb, ..., 2*sn_nb[, ...
      if (self->ff2_status < sn_nb) {
        // If memory is already allocated, and status < sn_nb,
        // this means that it was allocated for a previous chunk,
        // which has not necessarily the good size. Re-allocate it
        if (self->ff2_localmean_threads != NULL) {
          free(self->ff2_localmean_threads);
          free(self->ff2_localmean_process);
          free(self->ff2_globalmean);
        }
        self->ff2_localmean_threads = (float*) calloc(Size0*Size1*npbunches, sizeof(float));
        self->ff2_localmean_process = (float*) calloc(Size0*Size1, sizeof(float));
        self->ff2_globalmean = (float*) calloc(Size0*Size1, sizeof(float));
        // number of projections handled by one thread
        self->ff2_nels_threads = (int*) calloc(npbunches, sizeof(int));
        // number of projections handled by one process
        self->ff2_nels_process = (int*) calloc(self->nprocs, sizeof(int));
        self->ff2_done = 0;
      }
      self->ff2_status += 1;
      self->ff2_localmean_current_thread = self->ff2_localmean_threads + Size0*Size1*ibunch;
      sem_post( &(self->ff_sem) );
    }

    for(npj=pstart; npj<pend  ; npj++) {
      long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch  ;  // chaque thread
      // se limite a son domaine d' utilisation de l' array RawData. Nous sommes dans le cas reduit
      // qui traite les cas encombrantes
      
      if(npj%100==0)
	if (self->params.verbosity>1)
	  printf( " packet %d in CCspace_preprocess_chunk %ld \n", sn, npj);
      
      /* int size0, size1 ;  */
      /* size0 = size_s[2*sn_nb +0] ;  */
      /* size1 = size_s[2*sn_nb +1] ;  */
      Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted*Size0*Size1;

//	{
//	  int i,j;
//	  double sum=0;
//	  for(i=0; i<Size0; i++) {
//	    for(j=0; j< Size1; j++) {
//	      sum+=(Rawptr[i*Size1  +j]);
//	    }
//	  }
//	  printf("SUSUM  INIZIO  di ff %e\n", sum ) ;
//	}	

      
      if(self->params.DZPERPROJ==0) {
	long int posff_a = (long int) self->reading_infos.ff_indexes_coefficients[ 6*npj+0 ];
	long int posff_b = (long int) self->reading_infos.ff_indexes_coefficients[ 6*npj+1 ];
	float coeff_a    = self->reading_infos.ff_indexes_coefficients[ 6*npj+2 ];
	float coeff_b    = self->reading_infos.ff_indexes_coefficients[ 6*npj+3 ];
	
	FFptrA = self->ff_rawdatatokens->datatokens[ntok] +posff_a*Size0*Size1 ;
	if(posff_b!=-1)
	  FFptrB = self->ff_rawdatatokens->datatokens[ntok] +posff_b*Size0*Size1 ;
	else
	  FFptrB = FFptrA ;
	
	Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];
	Bptr =  self->background  ;


	float current =1.0;


	// @@back
	if( strcmp(self->reading_infos.proj_reading_type , "edf")==0 ){
	  if (self->params.CORRECT_FLATFIELD )  {
	    // int iread  =self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
	    
	    int iread  =  self->reading_infos.my_proj_num_list[npj]  + self->reading_infos.proj_num_offset_list[sn_nb] ;
	    
	    // printf( "   IREAD CLENGHT %d %d iproc %d\n",   iread,  self->reading_infos.currents_lenght, self->iproc    ) ;  
	    assert( iread< self->reading_infos.currents_lenght) ;
	    current = self->reading_infos.currents[iread];
	  } else {
	    current = 1.0; 
	  }
	  
	  
	  if(self->params.SUBTRACT_BACKGROUND )    {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] =  (Rawptr[i*Size1  +j] -  Bptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ])/current;
		// et les FF sont deja renormalise par le courant et dark
	      }
	    }
	  } else {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] =  (Rawptr[i*Size1  +j] )/current;
		// et les FF sont deja renormalise par le courant et dark
	      }
	    }
	  }
	}


	
	if(self->params.CORRECT_FLATFIELD )    {
	  if(posff_b ==-1) {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] = (Rawptr[i*Size1  +j]+self->params.OFFSETRADIOETFF/current ) / (FFptrA[i*Size1  +j] )      ;
	      }
	    }
	  } else {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] = (Rawptr[i*Size1  +j]+self->params.OFFSETRADIOETFF/current ) /
		  (  coeff_a*  FFptrA[i*Size1  +j] + coeff_b*  FFptrB[i*Size1  +j] )      ;
	      }
	    }
	  }
	}
      }


      if(self->params.TAKE_LOGARITHM)    {
	for(i=0; i<Size0; i++) {
	  for(j=0; j< Size1; j++) {
	    Rawptr[i*Size1  +j] = max(Rawptr[i*Size1  +j], self->params.ZEROCLIPVALUE );
	    Rawptr[i*Size1  +j] = min(Rawptr[i*Size1  +j], self->params.ONECLIPVALUE );
	  }
	}
      }

      if (do_ff2 == 1) {
        // Each thread  computes a mean on (ff2_cnt+1) projections into ff2_mean
        // Then, ff2_mean  will have to be reduced and weighted by (ff2_cnt+1)
//        int cnt = self->ff2_nels_threads[ibunch];
        for(i=0; i<Size0; i++) {
          for(j=0; j< Size1; j++) {
            // TODO: running mean
            self->ff2_localmean_current_thread[i*Size1+j] += Rawptr[i*Size1  +j];
//            self->ff2_localmean_current_thread[i*Size1+j] = cnt/(cnt + 1.0)*self->ff2_localmean_current_thread[i*Size1+j] + Rawptr[i*Size1  +j]/(cnt+1.0);
          }
        }
        self->ff2_nels_threads[ibunch]++;
      }

      if(self->params.DZPERPROJ==0) {   
	if(self->params.DOUBLEFFCORRECTION )    {
	  DFptr = self->ffcorr  ;
	  if(self->params.TAKE_LOGARITHM )    {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j] *  DFptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ];
	      }
	    }
	  } else {
	    for(i=0; i<Size0; i++) {
	      for(j=0; j< Size1; j++) {
		Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j] -  DFptr[ (i+Pos0)*gdim1  +(j+Pos1 )      ];
	      }
	    }
	  }
	}  
      }
    } // end "for npj"



    if (do_ff2 == 2) {
      sem_wait( &(self->ff_sem) );

      if (self->ff2_done == 0) {
        self->ff2_done = 1;

        // Thread 0 reduces ff2_localmean_threads   into  ff2_localmean_process
        self->ff2_nels_process[self->iproc] = 0;
        int k, numels_tot_threads = 0;
        float mean = 0;
        for(i=0; i<Size0; i++) {
          for(j=0; j< Size1; j++) {
            mean = 0;
            for (k = 0; k < npbunches; k++) {
              mean += self->ff2_localmean_threads[k*Size0*Size1 + (i*Size1  +j)];// TODO: running mean
              //              mean += self->ff2_localmean_threads[k*Size0*Size1 + (i*Size1  +j)] * self->ff2_nels_threads[k];
              numels_tot_threads += self->ff2_nels_threads[k];
            }
            self->ff2_localmean_process[i*Size1  +j] = mean; // TODO: running mean
            self->ff2_nels_process[self->iproc] = numels_tot_threads;
          }
        }
        // Scale the mean with the number of projections in the current local chunk
        //      for(i=0; i<Size0*Size1; i++) ff2_localmean[i] *= ff2_nelems[self->iproc];

        // Reduce  ff2_localmean_process  into  ff2_globalmean
//        MPI_Barrier(MPI_COMM_WORLD);
        MPI_Allreduce(self->ff2_localmean_process, self->ff2_globalmean, Size0*Size1 , MPI_FLOAT , MPI_SUM, MPI_COMM_WORLD);
        if (self->iproc == 0) {
          // Divide this sum by the sum of the local number of elements
          //          int nelems_total = 0;
          //          for (i = 0; i < self->nprocs; i++) nelems_total += self->ff2_nels_process[i];
          for(i=0; i<Size0*Size1; i++) self->ff2_globalmean[i] /= self->params.nprojs_span; // TODO: running mean
	  if(self->params.TAKE_LOGARITHM ) {
	    for(i=0; i<Size0*Size1; i++) self->ff2_globalmean[i]  = -log( self->ff2_globalmean[i] ) ;
	  }
          // Write this in edf
	  {
	    char name[10000];
	    sprintf(name,"%s/pyhst_dff_%.2f.edf", dirname(self->params.OUTPUT_FILE) , self->params.FF2_SIGMA     ) ; 
	    write_data_to_edf(self->ff2_globalmean, Size0, Size1, name);
	  }
	  
	  
          if (self->params.verbosity >= 10) puts("wrote projectionsmean.edf");

          float sigma = self->params.FF2_SIGMA;
          if (fabsf(sigma) > 1e-7) {
            // High-pass version of the projections mean
            // ------------------------------------------

            /*
          // Allocate memory and plans
          int Size0_fft = 2*Size0; // TODO: consider using nextpow2() for speed
          int Size1_fft = 2*Size1;

          fftwf_complex* ff2_projmean_f = (fftwf_complex*) calloc(Size0_fft*(Size1_fft/2+1), sizeof(fftwf_complex));
          float sigma = self->params.FF2_SIGMA;
          int gsize = (int) ceilf(8*sigma +1); if ((gsize & 1) == 0) gsize++;
          float* ff2_gaussian = (float*) calloc(gsize*gsize, sizeof(float));
          fftwf_complex* ff2_gaussian_f = (fftwf_complex*) calloc(Size0_fft*(Size1_fft/2+1), sizeof(fftwf_complex));
          float* ff2_projmean_old = (float*) calloc(Size0*Size1, sizeof(float));
          memcpy(ff2_projmean_old, self->ff2_globalmean, Size0*Size1*sizeof(float));

          fftwf_plan ff2_planr2c = fftwf_plan_dft_r2c_2d(Size0_fft, Size1_fft, self->ff2_globalmean, ff2_projmean_f, FFTW_ESTIMATE);
          fftwf_plan ff2_planc2r = fftwf_plan_dft_c2r_2d(Size0_fft, Size1_fft, ff2_projmean_f, self->ff2_globalmean, FFTW_ESTIMATE);
          fftwf_plan ff2_planr2c_g = fftwf_plan_dft_r2c_2d(Size0_fft, Size1_fft, ff2_gaussian, ff2_gaussian_f, FFTW_ESTIMATE);

          // Compute real gaussian function
          float ti, tj;
          float gsum = 0;
          for(i=0; i<gsize; i++) {
            ti = i - ((gsize-1)/2.0);
            for(j=0; j< gsize; j++) {
              tj = j - ((gsize-1)/2.0);
              ff2_gaussian[i*Size1  +j] = expf(-((tj*tj + ti*ti)/(2.0*sigma*sigma)));
              gsum += ff2_gaussian[i*Size1  +j];
            }
          }
          for(i=0; i<gsize*gsize; i++) ff2_gaussian[i] /= gsum;

          // Fourier Transform
          fftwf_execute(ff2_planr2c);
          fftwf_execute(ff2_planr2c_g);

          // Multiply...
          if (0) for (i = 0; i < Size0_fft*(Size1_fft/2+1); i++) {
            ff2_projmean_f[i][0] *= ff2_gaussian_f[i][0];
            ff2_projmean_f[i][1] *= ff2_gaussian_f[i][1];
          }

          // Inverse Fourier transform
          fftwf_execute(ff2_planc2r);
          */

            float* ff2_projmean_old = (float*) calloc(Size0*Size1, sizeof(float));
            memcpy(ff2_projmean_old, self->ff2_globalmean, Size0*Size1*sizeof(float));
            // Compute 1D Gaussian function
            int gsize = (int) ceilf(8*sigma +1); if ((gsize & 1) == 0) gsize++;
            float* ff2_gaussian = (float*) calloc(gsize, sizeof(float));
            float ti;
            float gsum = 0;
            for(i=0; i<gsize; i++) {
              ti = i - ((gsize-1)/2.0);
              ff2_gaussian[i] = expf(-((ti*ti)/(2.0*sigma*sigma)));
              gsum += ff2_gaussian[i];
            }
            for(i=0; i<gsize; i++) ff2_gaussian[i] /= gsum;

            int halfsize = gsize/2;
            float acc;
            // Convolution along dimension 1. TODO: not entirely accurate
            for(i=0; i<Size0; i++) {
              for(j=0; j< Size1; j++) {
                // conv along "j"
                acc = 0;
                for (k = max(0, halfsize-j); k <= min(gsize-1, Size1-1+halfsize-j); k++) {
                  acc += ff2_gaussian[k]*(self->ff2_globalmean)[i*Size1  + (j-halfsize+k)];
                }
                (self->ff2_globalmean)[i*Size1  +j] = acc;
              }
            }

            // Convolution along dimension 0
            if (Size0 > 1) {
              for(j=0; j< Size1; j++) {
                for(i=0; i<Size0; i++) {
                  // conv along "i"
                  acc = 0;
                  for (k = max(0, halfsize-i); k <= min(gsize-1, Size0-1+halfsize-i); k++) {
                    acc += ff2_gaussian[k]*(self->ff2_globalmean)[(i-halfsize+k)*Size1  +j];
                  }
                  (self->ff2_globalmean)[i*Size1  +j] = acc;
                }
              }
            }

            // High-pass
            for (i = 0; i < Size0*Size1; i++) self->ff2_globalmean[i] = ff2_projmean_old[i] - (self->ff2_globalmean)[i]; //self->ff2_globalmean[i] -= ff2_projmean_old[i];

            // Write
	    {
	      
	      char name[10000];
	      sprintf(name,"%s/pyhst_dff_%.2f.edf", dirname(self->params.OUTPUT_FILE) , self->params.FF2_SIGMA     ) ; 

	      write_data_to_edf(self->ff2_globalmean, Size0, Size1, name);
	      if (self->params.verbosity >= 10) puts("wrote projectionsmean.edf");
	    }

	    

            free(ff2_projmean_old);
            free(ff2_gaussian);

          } // high-pass filtering of the projections average

          /*
          free(ff2_projmean_f);
          free(ff2_projmean_old);
          free(ff2_gaussian);
          free(ff2_gaussian_f);
          */

          //~ free(self->ff2_localmean_threads); ?
          //~ free(self->ff2_localmean_process); ?
//           free(self->ff2_globalmean); // not here !
          //~ free(self->ff2_nels_process); ?
          //~ free(self->ff2_nels_threads); ?
        }
      }
      sem_post( &(self->ff_sem) );
    } // (do_ff2 == 2)


    // Apply the "on-the-fly double flat-field correction", if requested
    // Now apply the "double flat-field" correction
    if (do_ff2 == 2) {
      for(npj=pstart; npj<pend  ; npj++) {
        long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch;
        if(npj%100==0) if (self->params.verbosity>1) printf( " packet %d in CCspace_preprocess_chunk %ld \n", sn, npj);
        Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted*Size0*Size1;
        if(self->params.TAKE_LOGARITHM ) {
          for(i=0; i<Size0; i++) {
            for(j=0; j< Size1; j++) {
              Rawptr[i*Size1 +j] = Rawptr [i*Size1 +j] * self->ff2_globalmean[i*Size1 +j];
            }
          }
        } else {
          for(i=0; i<Size0; i++) {
            for(j=0; j< Size1; j++) {
              Rawptr[i*Size1 +j] = Rawptr [i*Size1 +j] - self->ff2_globalmean[i*Size1 +j];
            }
          }
        } // no take_logarithm
      } // for npj
    } // do_ff2 == 2
//     free(self->ff2_globalmean);


    if(self->params.CCD_FILTER_KIND!= CCD_FILTER_NONE_ID) {
      if (self->params.verbosity>1) printf(" vado a filtrare ccd sn=%d \n",sn);
      for(npj=pstart; npj<pend  ; npj++) {
	long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch  ;
	// int npj_absolute = npj+     self->reading_infos. proj_mpi_offsets[self->iproc] ;
	
	Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted*Size0*Size1;
	
	Filter_CCD(self,  buffer,  Rawptr,
		   Pos0, Pos1, Size0, Size1,
		   pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
		   size_s[2*sn_nb +0], size_s[2*sn_nb +1],
		   self->params.CCD_FILTER_KIND ,   // ok
		   self->params.CCD_FILTER_PARA,    // void *
		   // self->axis_correctionsL[npj_absolute],
		   ncpus
		   );
      }
      if (self->params.verbosity>1)printf(" vado a filtrare ccd OK sn=%d\n",sn);
    }

    if( self->reading_infos.corr_file_list_lenght ){
       if(self->params.DZPERPROJ!=0) {
	 printf(" ERROR :   multipaganin not yet adapted to  elicoidal scan \n");
	 exit(1);
 
       }
       for(npj=pstart; npj<pend  ; npj++) {
	 long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch  ;
	 int npj_absolute =self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
	 if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
	   continue;
	 }

	int start_line=0;
	int Dim_2 = self->reading_infos.Dim_2  ;
	printf(" GUARDO se e restrained \n");
	
	if(self->reading_infos.corr_restrained) {
	  printf(" OUI     \n" );

	  start_line = self->reading_infos.corr_start_2   ;
	  Dim_2  =    self->reading_infos.corr_real_2  ;
	}
	printf(" start_line %d  Dim_2 %d \n", start_line  , Dim_2 );
	printf(" LEGGO SU  p= %p \n", correction  );



	int iread;
	if(1) {
	  iread = self->reading_infos.my_proj_num_list[npj]  + self->reading_infos.proj_num_offset_list[sn_nb] ;
	} else{
	  iread = npj;
	}
	if(   !(iread>=0 && iread< self->reading_infos.numpjs) ) {
	  continue;
	}
 	printf (" iread npj %d %ld\n", iread, npj);
	
	read_data_from_edf( self->reading_infos.corr_file_list[  iread ],
			    correction    , // target_float @@@@@@@@@@@@@@@@@@@@@
			    (char*) buffer  ,   // work buffer
			    self->reading_infos.corr_datatype, //
			    self->params.ROTATION_VERTICAL,
			    self->reading_infos.corr_headerSizes[  iread  ],
			    pos_s_[2*sn_nb]-start_line,  pos_s_[2*sn_nb+1],
			    size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
			    sizeofdatatype[ self->reading_infos.corr_datatype],
			    Dim_2,self->reading_infos.Dim_1,
			    self->reading_infos.corr_byteorder,
			    self->params.BINNING, &(self->filereading_sem)
			    );

	//printf(" LETTURA OK   Size0  Size1 %d %d p= %p \n",Size0, Size1, correction );
	Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted*Size0*Size1;

	// double sum=0.0;
	int II;
	for(i=0; i<Size0; i++) {
	  II = i + pos_s_[2*sn_nb] ; 
	  if( II >= start_line && (II-start_line)<Dim_2) {
	    for(j=0; j< Size1; j++) {
	      
	      // printf("COO %d %d %d    \n" ,  (i-start_line),   Size1, j    );
	      
	      Rawptr[i*Size1  +j] =  Rawptr [i*Size1  +j]  * exp(  correction[(II-start_line)*Size1  +j] );
	      // sum += correction[(II-start_line)*Size1  +j] ; 
	      
	    }
	  }
	}
	printf(" Trascrittura OK \n");
	
      }
    }    

    if( self->params.DO_PAGANIN &&  (!do_ff2) )    {
      Rawptr  = self-> rawdatatokens->datatokens[ntok] ;
      if (self->params.verbosity>1) printf(" chiamo paganin  packet %d  do_ff2 %d\n", sn, do_ff2);


      int mystart = 0;
      if( pend>pstart) {
	mystart = self->reading_infos.tot_proj_num_list[  num_previous_projs +  pstart] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
      }
      
      Paganin( self,  Rawptr  +   num_myprojs_reduced_forAllThreads*ibunch*Size0*Size1 ,
	       Pos0, Pos1, Size0, Size1,
	       pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
	       size_s[2*sn_nb +0], size_s[2*sn_nb +1],
	       &(self->params) , ncpus,   &(self->fftw_sem ),
	       pstart-pstart,   // pstart,
	       pend-pstart,      // pend,
	       self->reading_infos. proj_mpi_offsets[self->iproc],
	       self->reading_infos. proj_num_offset_list[ sn_nb ] ,
	       mystart,
	       npbunches*self->params.RAWDATA_MEMORY_REUSE,
	       ibunch
	       );
    }

    if(  !do_ff2 ) {
      for(npj=pstart; npj<pend  ; npj++) {
	long int npj_reshifted = (npj - pstart) + num_myprojs_reduced_forAllThreads*ibunch  ;
	int npj_absolute =self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
	// *@@* inutile continuare qui se npj_absolute non nei limiti

	if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
	  Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];
	  int k;
	  // printf(" METTO A NAN la Treated  \n");
	  for(k=0; k< size_s[2*sn_nb +0]*size_s[2*sn_nb +1]; k++) {
	    Tptr[k]=NAN;
	  }
	  continue;
	}

	int size0, size1 ;
	size0 = size_s[2*sn_nb +0] ;
	size1 = size_s[2*sn_nb +1] ;
	Rawptr  = self-> rawdatatokens->datatokens[ntok] +npj_reshifted  *Size0*Size1;
      
	Tptr =  self-> datatokens->datatokens[ntokt] +npj*size_s[2*sn_nb +0]*size_s[2*sn_nb +1];
	// DFptr = self->ffcorr  ;

	// memset( Tptr, 0,  size_s[2*sn_nb +0]*size_s[2*sn_nb +1]*sizeof(float)   );
      

      
	if(self->params.TAKE_LOGARITHM )    {
	  for(i=0; i<Size0; i++) {
	    for(j=0; j< Size1; j++) {
	      Rawptr[i*Size1  +j] = max(Rawptr[i*Size1  +j], self->params.ZEROCLIPVALUE );
	    }
	  }
	}

	Filter_and_Trim( self, buffer, Tptr  , Rawptr,
			 Pos0, Pos1, Size0, Size1,
			 pos_s [2*sn_nb +0], pos_s [2*sn_nb +1],
			 size_s[2*sn_nb +0], size_s[2*sn_nb +1],
			 self->axis_correctionsL[npj_absolute],
			 ncpus
			 );
	float SUM=0.0;
	if(self->params.TAKE_LOGARITHM )    {
	  if(self->params.BH_LUT_F )    {
	    for(i=0; i<size0; i++) {
	      for(j=0; j< size1; j++) {
		Tptr[i*size1  +j] =  bh_lut( (-log( Tptr [i*size1  +j]))  , self->params.BH_LUT_F , self->params.BH_LUT_U,  self->params.BH_LUT_N ) *normalise;
	      }
	    }
	  } else {
	    for(i=0; i<size0; i++) {
	      for(j=0; j< size1; j++) {
		Tptr[i*size1  +j] = -log( Tptr [i*size1  +j] )  *normalise;
		SUM += Tptr[i*size1  +j] ;
	      }
	    }
	  }
	} else {
	  for(i=0; i<size0; i++) {
	    for(j=0; j< size1; j++) {
	      Tptr[i*size1  +j] =   Tptr [i*size1  +j]  *normalise;
	      SUM += Tptr[i*size1  +j] ;
	    }
	  }
	}
	// if (self->params.verbosity>1) printf(" SUM %e\n", SUM);

	if(self->params.STRAIGTHEN_SINOS) {
	  for(i=0; i<size0; i++) {
	    float ff0,ff1,ff2, f0,f1,f2,x;
	    ff0=0.0f;
	    ff1=0.0f;
	    ff2=0.0f;

	    float sum0=0, sum1=0, sum2=0;
	    for(j=0; j< size1; j++) {
	      x = 2.0f*(j+0.5f-size1/2.0f)/size1;
	      f0=1.0f;
	      f1= x;
	      f2= (3.0f*x*x-1.0f);
	      ff0=ff0+f0*Tptr [i*size1  +j];
	      ff1=ff1+f1*Tptr [i*size1  +j];
	      ff2=ff2+f2*Tptr [i*size1  +j];
	      sum0+=f0*f0 ; 
	      sum1+=f1*f1 ; 
	      sum2+=f2*f2 ; 

	    }
	    for(j=0; j< size1; j++) {
	      x = 2.0f*(j+0.5f-size1/2.0f)/size1;
	      f0=1.0f;
	      f1= x;

	      f2= (3.0f*x*x-1.0f);
	      //	  Tptr [i*size1  +j]=Tptr [i*size1  +j]-ff0*f0-ff1*f1-ff2*f2;
	      Tptr [i*size1  +j]=Tptr [i*size1  +j]- (ff0*f0/sum0+ff1*f1/sum1+ff2*f2/sum2);

	    }
	  }
	}

	/* if(self->params.STRAIGTHEN_SINOS) { */
	/* 	for(i=0; i<size0; i++) { */
	/* 	  float ff0,ff1,ff2, f0,f1,f2,x,deno; */
	/* 	  ff0=0.0f; */
	/* 	  ff1=0.0f; */
	/* 	  ff2=0.0f; */
	/* 	  for(j=0; j< size1; j++) { */
	/* 	    x = 2.0f*(j+0.5f-size1/2.0f)/size1; */
	/* 	    deno=sqrt(1.0f-x*x)*size1; */
	/* 	    f0=1.0f; */
	/* 	    f1= x; */
	/* 	    f2= (2.0f*x*x-1.0f); */
	/* 	    ff0=ff0+f0*Tptr [i*size1  +j]/deno; */
	/* 	    ff1=ff1+f1*Tptr [i*size1  +j]/deno; */
	/* 	    ff2=ff2+f2*Tptr [i*size1  +j]/deno; */
	/* 	  } */
	/* 	  for(j=0; j< size1; j++) { */
	/* 	    x = 2.0f*(j+0.5f-size1/2.0f)/size1; */
	/* 	    f0=1.0f; */
	/* 	    f1= x; */
	/* 	    f2= (2.0f*x*x-1.0f); */
	/* 	    Tptr [i*size1  +j]=Tptr [i*size1  +j]-ff0*f0-ff1*f1-ff2*f2; */
	/* 	  } */
	/* 	} */
	/* } */
      }
    }
  }
  free(buffer);
  free(correction);
}

/**
 *  PROVA COMMENTO PER DOXYGEN SPHINX
 */
void  CCspace_read_chunk    (CCspace * self , int sn, int  ntok , int npbunches, int rotation_vertical, int binning,
           int reduced_case, int red_start, int red_end, int reshift) {
  float *Aptr , *FFptr;
  long int npj;
  char **proj_file_list;
  int * my_proj_num_list;
  int * proj_num_offset_list;
  int * proj_mpi_numpjs;
  char *buffer;
  int *size_s_, *pos_s_;
  int smax2, smax1;
  int  sizeofdatatype[3];
  int num_my_proj , pstart, pend, ibunch;
  double float_npj, float_pstart , float_pend ;
  int sn_nb ;
  int k;
  int posff, iff;
  int takeit, isok, status;
  float *Bptr;
  // int gdim0, gdim1;
  int  gdim1;
  // long int offset ;
  int Size0, Size1;
  int Pos0, Pos1;
  int i,j ;


  long int last_posFF []={-1,-1};
  float *FF[2]={NULL,NULL};

  omp_set_nested(0);
  
  sem_wait( &(self->ff_sem) );
  if( (sn/npbunches) != self->snXtoken[ntok] ) {
    self->snXtoken[ntok]  =  (sn/npbunches) ;
    for(k=0; k<self->reading_infos.NFF; k++) {
      self->ff_read_status[ntok][k] = NOT_ACQUIRED;
    }
  }
  sem_post( &(self->ff_sem) );
  size_s_= self->reading_infos.size_s_ ;
  pos_s_ = self->reading_infos.pos_s_  ;
  sn_nb = sn/npbunches ;
  buffer=NULL;

  sizeofdatatype[FloatValue]   =4;
  sizeofdatatype[UnsignedShort]=2;
  sizeofdatatype[SignedInteger]=4;
  Aptr  = self->   rawdatatokens->datatokens[ntok];
  FFptr = self->ff_rawdatatokens->datatokens[ntok];
  if( 1 ) {
    smax2= self->reading_infos.size_s_[ sn_nb *2+0  ] ;
    smax1= self->reading_infos.size_s_[ sn_nb *2+1  ] ;
    buffer= (char*) malloc(max(  self->reading_infos.Dim_2*self->reading_infos.Dim_1  , binning*binning*smax2*smax1)
		   *max( sizeof(float),  sizeofdatatype[  self->reading_infos.datatype ] ) );

    if(  rotation_vertical==0 ) {
      /* for trasposition */
    }
    proj_file_list =self->reading_infos. proj_file_list;
    my_proj_num_list= self->reading_infos.my_proj_num_list;
    proj_mpi_numpjs= self->reading_infos.proj_mpi_numpjs  ;
    proj_num_offset_list= self->reading_infos.proj_num_offset_list;

    num_my_proj = proj_mpi_numpjs[self->iproc];
    ibunch = sn % npbunches ;

    if(!reduced_case) {
      float_npj = (num_my_proj*1.0)/ npbunches ;
      float_pstart = float_npj*ibunch ;
      float_pend   = float_pstart + float_npj ;
      pstart = (int)(float_pstart+0.499999999) ;
      pend = (int)(float_pend+0.499999999);
    } else {
      pstart = red_start;
      pend = red_end  ;
    }

    int num_previous_projs=0;  
    for(i=0; i< self->iproc; i++) {
      num_previous_projs +=  self->reading_infos. proj_mpi_numpjs[ i ] ; 
    }
    int Ninterval = 0;
    
    if(pend>pstart   ){
      int iold = my_proj_num_list[pstart]  + proj_num_offset_list[sn_nb] ;;  
      for(npj=pstart; npj<pend  ; npj++) {
	int iread = my_proj_num_list[npj]  + proj_num_offset_list[sn_nb] ;
	if( iread>iold+1) {
	  Ninterval++;
	}
	iold = iread;
      }
      Ninterval++;
    }
    int nprojs_interval[2*Ninterval];
    
    if(pend>pstart   ){
      Ninterval = 0; 
      int iold = my_proj_num_list[pstart]  + proj_num_offset_list[sn_nb] ;;
      int istart = iold;
      int iread;
      for(npj=pstart; npj<pend  ; npj++) {
	iread = my_proj_num_list[npj]  + proj_num_offset_list[sn_nb] ;
	if( iread>iold+1) {
	  nprojs_interval[2*Ninterval]= istart ; 
	  nprojs_interval[2*Ninterval+1]= iold+1 ;
	  	  printf( " NINTERVAL %d %d   \n"  ,    nprojs_interval[2*Ninterval], nprojs_interval[2*Ninterval+1]) ; 
	  Ninterval++;
	  istart = iread;
	}
	iold = iread;
      }
      {
	nprojs_interval[2*Ninterval]= istart ; 
	nprojs_interval[2*Ninterval+1]= iread+1 ;
		  printf( " NINTERVAL %d %d   \n"  ,    nprojs_interval[2*Ninterval], nprojs_interval[2*Ninterval+1]) ; 
	Ninterval++;
      }
    }
    


    int hfd5stack_done = 0;
    hid_t h5file_id = -1 ; 
    hid_t h5ds_id = -1 ; 
    
    for(npj=pstart; npj<pend  ; npj++) {
      // printf(" NPJ %d  %d %d\n", npj,size_s_[2*sn_nb +0],size_s_[2*sn_nb +1]);
      float *target;
      if(!reduced_case) {
	
	target =  (Aptr+ npj*size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1])	   ;

	//if(self->params.CONICITY ) memset(target,0, size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1]*sizeof(float) );

	
      } else {
	target =  (Aptr+ (npj+reshift)*size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1])	   ;
	// if(self->params.CONICITY ) memset(target,0, size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1]*sizeof(float) );
      }

      
      // printf(" leggo da edf %d %d %d %d \n",  pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],size_s_[2*sn_nb +0], size_s_[2*sn_nb +1]);

      //  pos_s_[2*sn_nb+1] is supposed to be 0
      // printf("DEBUGC SONO PRIMA CONFRONTO npj %d  proj_num_list[npj] %d proj_num_offset_list[sn_nb] %d\n", npj,proj_num_list[npj],  proj_num_offset_list[sn_nb]   );
     
      // printf("DEBUGC %d  SONO PRIMA CONFRONTO %d  %d %p %p  pstart %d pend %d\n", ibunch, npj,sn_nb,
      //     proj_num_list,  proj_num_offset_list , pstart, pend  );

      int iread = my_proj_num_list[npj]  + proj_num_offset_list[sn_nb] ;
     // printf("SONO A CONFRONTO %d  %d \n", iread, self->reading_infos.numpjs);
      if(iread>=0 && iread< self->reading_infos.numpjs ) {

	if(strcmp(self->reading_infos.proj_reading_type , "edf")==0) {
	  if(self->params.DZPERPROJ==0) {

	    read_data_from_edf( proj_file_list[iread],
				target   , // target_float
				buffer  ,   // work buffer
				self->reading_infos.datatype, //
				rotation_vertical,
				self->reading_infos.headerSizes[iread],
				pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],
				size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
				sizeofdatatype[ self->reading_infos.datatype],
				self->reading_infos.Dim_2,self->reading_infos.Dim_1,
				self->reading_infos.byteorder,
				binning, &(self->filereading_sem)
				);
	  } else {
	    float *Bptr;
	    if(self->params.SUBTRACT_BACKGROUND )    {
	      Bptr =  self->background  ;
	    } else {
	      Bptr = NULL ; 
	    }
	    int iff;
	    long int posff;

	    for(iff=0; iff<2; iff++) {
	      posff  = (long int) self->reading_infos.ff_indexes_coefficients[ 6*iread+iff ];
	      if(posff!=-1 && posff!=last_posFF[iff]) {
		last_posFF[iff]=posff;
		if( FF[iff]==NULL) {
		  FF[iff]=  (float *)  malloc( self->reading_infos.Dim_2*self->reading_infos.Dim_1 * sizeof(float));
		}
		read_data_from_edf( self->reading_infos.ff_file_list[ posff  ],
				    FF[iff]        , // target_float
				    buffer  ,   // work buffer
				    self->reading_infos.ff_datatype, //
				    rotation_vertical,
				    self->reading_infos.ff_headerSizes[posff],
				    0,  0,
				    self->reading_infos.Dim_2,self->reading_infos.Dim_1 ,
				    sizeofdatatype[ self->reading_infos.ff_datatype],
				    self->reading_infos.Dim_2,self->reading_infos.Dim_1,
				    self->reading_infos.ff_byteorder,
				    binning, &(self->filereading_sem)
				    );
	      }
	    }
	    memset(target, 0,  size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1]*sizeof(float)  ) ; 
	    int npj_absolute =self->reading_infos.tot_proj_num_list[npj+num_previous_projs] +     self->reading_infos. proj_num_offset_list[sn_nb] ;
	    read_data_from_edf_eli( proj_file_list[iread],
				    target   , // target_float
				    self->reading_infos.datatype, //
				    rotation_vertical,
				    self->reading_infos.headerSizes[iread],
				    pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],
				    size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
				    sizeofdatatype[ self->reading_infos.datatype],
				    self->reading_infos.Dim_2,self->reading_infos.Dim_1,
				    self->reading_infos.byteorder,
				    binning,
				    iread,
				    self->params.DZPERPROJ,
				    Bptr,
				    self->params.CORRECT_FLATFIELD,
				    self->reading_infos.ff_indexes_coefficients + 6*iread,
				    FF,
				    self->params.DOUBLEFFCORRECTION!=NULL ,
				    self->params.TAKE_LOGARITHM,
				    self->ffcorr,
				    self->reading_infos.currents,
				    self->reading_infos.currents[npj_absolute] , &(self->filereading_sem)
				    );
	  }
	} else if (strcmp(self->reading_infos.proj_reading_type , "h5")==0){
	  if(self->params.DZPERPROJ==0) {
	    if(     hfd5stack_done++ == 0) {
	      sem_wait( &(self->hdf5sequence_sem));

	      float *bptr_tmp = NULL;
	      if(self->params.SUBTRACT_BACKGROUND )    {
		// ptr_tmp = self->background + ( (i+Pos0)*gdim1  +(j+Pos1 ) ) ;
		bptr_tmp = self->background + ( (0+  pos_s_[2*sn_nb] )* size_s_[2*sn_nb +1]   + 0 ) ;
		assert(  pos_s_[2*sn_nb+1] == 0   ); 
	      }
	      read_projSequence_from_h5( proj_file_list[ 0 ],
					 self->reading_infos.proj_h5_dsname,
					 Ninterval,
					 nprojs_interval,
					 target   , 
					 pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],
					 size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
					 rotation_vertical,
					 binning,
					 &(self->shared_h5id),
					 &(self->shared_h5DSid),
					 &(self->shared_h5DSid_current),
					 bptr_tmp
					 ) ;
	      sem_post( &(self->hdf5sequence_sem));
	    }
	  } else {// ==================================
	    float *Bptr = NULL;
	    /// @@back
	    if(self->params.SUBTRACT_BACKGROUND )    { Bptr =  self->background  ; }
	    int iff;
	    long int posff;

	    float *bptr_tmp = NULL;
	    if(self->params.SUBTRACT_BACKGROUND )    {
	      bptr_tmp = self->background + ( (0+  pos_s_[2*sn_nb] )* size_s_[2*sn_nb +1]   + 0 ) ;
	      assert(  pos_s_[2*sn_nb+1] == 0   ); 
	    }
	    
	    for(iff=0; iff<2; iff++) {
	      posff  = (long int) self->reading_infos.ff_indexes_coefficients[ 6*iread+iff ];
	      if(posff!=-1 && posff!=last_posFF[iff]) {
		last_posFF[iff]=posff;
		if( FF[iff]==NULL) {
		  FF[iff]=  (float *)  malloc(   self->params.NUM_IMAGE_1* self->params.NUM_IMAGE_2  * sizeof(float) );
		}
		int file_seq_pos = min(self->reading_infos.ff_file_list_lenght-1, posff ) ;
		int multiplo = 1 ;
		if( self->reading_infos.ff_file_list_lenght == 1 ) {
		  multiplo = 0 ; 
		}

		read_data_from_h5(self->reading_infos.ff_file_list[file_seq_pos] ,
				  self->reading_infos.ff_h5_dsname,
				  posff - file_seq_pos  ,
				  FF[iff]  , // target_float
				  0 ,
				  0 ,
				  self->params.NUM_IMAGE_2,
				  self->params.NUM_IMAGE_1,
				  rotation_vertical,
				  binning,
				  multiplo,
				  self->params.REFERENCES_HOTPIXEL_THRESHOLD ,
				  bptr_tmp
				  );
	      }
	    }
	    
	    memset(target, 0,  size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1]*sizeof(float)  ) ; 
	    
	    read_data_from_h5_eli( proj_file_list[ 0 ],
				   self->reading_infos.proj_h5_dsname,
				   iread ,
				   target   , // target_float
				   pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],
				   size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
				   rotation_vertical,
				   binning,
				   self->params.DZPERPROJ,
				   Bptr,
				   self->params.CORRECT_FLATFIELD,
				   self->reading_infos.ff_indexes_coefficients + 6*iread,
				   FF,
				   self->params.DOUBLEFFCORRECTION!=NULL ,
				   self->params.TAKE_LOGARITHM,
				   self->ffcorr,
				   &h5file_id,
				   &h5ds_id,
				   &(self->shared_h5DSid_current)
				   );
	    
	  }// fine conico con H5	  
	} else {
	  fprintf(stderr, "file type can be either edf or h5 reading projs but it was %s \n",self->reading_infos.proj_reading_type );
	  exit(1);
	}   
	
      } else {
	// printf(" metto a NAN\n");
	int k;
	for(k=0; k< pos_s_[2*sn_nb]*pos_s_[2*sn_nb+1]; k++) {
	  target[k]=NAN;
	}
      }
      if(self->params.DZPERPROJ==0) {
	for(iff=0; iff<2; iff++) {
	  posff = (long int) self->reading_infos.ff_indexes_coefficients[ 6*npj+iff ];
	  if(posff==-1) continue;
	  takeit=0;
	  sem_wait( &(self->ff_sem) );
	  status = self->ff_read_status[ntok][posff] ;
	  if(status ==  NOT_ACQUIRED )  {
	    self->ff_read_status[ntok][posff]= IN_ACQUISITION ;
	    takeit=1;
	  }
	  sem_post( &(self->ff_sem) );

	  if(takeit) {
	    //  pos_s_[2*sn_nb+1] is supposed to be 0
	    if(strcmp(self->reading_infos.ff_reading_type , "edf")==0) {
	      read_data_from_edf( self->reading_infos.ff_file_list[ posff  ],
				  (FFptr+ posff*size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1])	       , // target_float
				  buffer  ,   // work buffer
				  self->reading_infos.ff_datatype, //
				  rotation_vertical,
				  self->reading_infos.ff_headerSizes[posff],
				  pos_s_[2*sn_nb],  pos_s_[2*sn_nb+1],
				  size_s_[2*sn_nb +0], size_s_[2*sn_nb +1],
				  sizeofdatatype[ self->reading_infos.ff_datatype],
				  self->reading_infos.Dim_2,self->reading_infos.Dim_1,
				  self->reading_infos.ff_byteorder,
				  binning, &(self->filereading_sem)
				  );






	      
	    }   else if (strcmp(self->reading_infos.ff_reading_type , "h5")==0){

	      int file_seq_pos = min(self->reading_infos.ff_file_list_lenght-1, posff ) ;

	      int multiplo = 1 ;
	      
	      if( self->reading_infos.ff_file_list_lenght == 1 ) {
		multiplo = 0 ; 
	      }

	      
	      // @@back ff
	      float *bptr_tmp = NULL;
	      if(self->params.SUBTRACT_BACKGROUND )    {
		bptr_tmp = self->background + ( (0+  pos_s_[2*sn_nb] )* size_s_[2*sn_nb +1]   + 0 ) ;
		assert(  pos_s_[2*sn_nb+1] == 0   ); 
	      }
	      read_data_from_h5(self->reading_infos.ff_file_list[  file_seq_pos    ] ,
				self->reading_infos.ff_h5_dsname,
				posff - file_seq_pos  ,
				(FFptr+ posff*size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1]), // target_float
				pos_s_[2*sn_nb],
				pos_s_[2*sn_nb+1],
				size_s_[2*sn_nb +0],
				size_s_[2*sn_nb +1],
				rotation_vertical,
				binning,
				multiplo,
				self->params.REFERENCES_HOTPIXEL_THRESHOLD,
				bptr_tmp
				);

	      
	    } else {
	      fprintf(stderr, "file type MUST be either edf or h5, reading ffs it was %s\n", self->reading_infos.ff_reading_type);
	      
	      exit(1);
	    }

	    
	    float current=1 ;
	    // @@back
	    if( strcmp(self->reading_infos.proj_reading_type , "edf")==0 ){
		    
	      if (self->params.CORRECT_FLATFIELD )  {
		
		
		if ( strlen( self->reading_infos.CURRENT_NAME  )>0   && strcmp(self->reading_infos.proj_reading_type , "edf")==0  ) {
		  
		  int iread =    self->reading_infos.ff_indexes_coefficients[ 6*npj+iff +4 ] ;
		  if(iread<0) iread=0;
		  if(iread>=self->reading_infos.NUM_LAST_IMAGE) iread=self->reading_infos.NUM_LAST_IMAGE;
		  {
		    
		    int  datatype , byteorder, sizeImag, list_lenght;
		    long int headerSizes;
		    int  dim1, dim2;
		    // char name[10000] ;
		    prereadEdfHeader( &sizeImag,
				      &byteorder,
				      &datatype,
				      &headerSizes,
				      &(dim1),
				      &(dim2),
				      self->reading_infos.allfile_list[iread] ,
				      self->reading_infos.CURRENT_NAME,
				      &current);
		  }
		}
	      } else {
		current = 1.0; 
	      }
	      
	      if(self->params.SUBTRACT_BACKGROUND )    {
		// printf(" levo background \n");
		Bptr =  self->background  ;
		// gdim0 = self->params.NUM_IMAGE_2 ;
		gdim1 = self->params.NUM_IMAGE_1 ;
		// offset = posff*size_s_[2*sn_nb +0]*size_s_[2*sn_nb +1];
		
		Size0 = size_s_[2*sn_nb +0];
		Size1 = size_s_[2*sn_nb +1];
		Pos0  = pos_s_[2*sn_nb +0];
		Pos1  = pos_s_[2*sn_nb +1];
		
		for(i=0; i<Size0; i++) {
		  for(j=0; j< Size1; j++) {
		    FFptr[posff*Size0*Size1+i*Size1+j]  = (FFptr[posff*Size0*Size1+i*Size1+j] -  Bptr[ (i+Pos0)*gdim1  +(j+Pos1 )]   + self->params.OFFSETRADIOETFF)/current ;
		  }
		}
	      } else {
		Size0 = size_s_[2*sn_nb +0];
		Size1 = size_s_[2*sn_nb +1];
		
		for(i=0; i<Size0; i++) {
		  for(j=0; j< Size1; j++) {
		    FFptr[posff*Size0*Size1+i*Size1+j]  = (FFptr[posff*Size0*Size1+i*Size1+j] )/current ;
		  }
		}	      
	      }
	    }

	    
	    sem_wait( &(self->ff_sem) );
	    self->ff_read_status[ntok][posff] =  ACQUIRED;
	    sem_post( &(self->ff_sem) );
	  } else if(  status ==  IN_ACQUISITION  ) {
	    isok=0;
	    while(!isok) {
	      sem_wait( &(self->ff_sem) );
	      if( self->ff_read_status[ntok][posff]>=  ACQUIRED  )  isok=1;
	      sem_post( &(self->ff_sem) );
	    }
	  }
	}
      }
    }
    if( h5file_id >= 0 ) {
      H5Dclose( h5ds_id   );
      H5Fclose( h5file_id );
    }
  }
  
  if(buffer) free(buffer);
  {
    int iff;
    for(iff=0; iff<2; iff++) {
      if(FF[iff])    free(FF[iff]);
    }
  }
}


float FindNoise(CCspace *  self,float *SLICE_a, int CALM_ZONE_LEN, int size_pa0, int  size_pa1 ) {

  fcomplex *gn0, *gn1;
  fftwf_plan plan2D_forward, plan2D_backward ;

  float *SLICE = (float *) malloc( size_pa0*size_pa1 *sizeof(float)    ) ;
  memset(SLICE,0, size_pa0*size_pa1 *sizeof(float));
  float diff1,diff2;
  int i=0,j=0;
  // int count=0;
  for(i=0; i<size_pa0-1; i++) {
    for(j=0; j<size_pa1-1; j++) {
      diff1  = SLICE_a[ (i+1)*size_pa1 +j     ] -SLICE_a[ (i)*size_pa1 +j ]  ;
      diff2  = SLICE_a[ (i  )*size_pa1 +(j+1) ] -SLICE_a[ (i)*size_pa1 +j ]  ;
      SLICE[ i*size_pa1 +j ] = diff1*diff1 + diff2*diff2  ;
      // count++;
      // if(count%1000==0) printf(" %e \n", SLICE[ i*size_pa1 +j ] );
    }

  }

//     // --------------------------- test ---------------------------------------------------
//     for(i=0; i<size_pa0-1; i++) {
//       for(j=0; j<size_pa1-1; j++) {
//         diff1  = min(abs(i-300),20) ;
//         diff2  = min(abs(j-600),20) ;
//         SLICE[ i*size_pa1 +j ] = diff1*diff1 + diff2*diff2  ;
//       }
//     }
//     // -------------------------------------------------------------------------------------

  sem_wait( &(self->fftw_sem));
  gn0 = (fcomplex*) fftwf_malloc(sizeof(fftwf_complex) *size_pa0 );
  gn1 = (fcomplex*) fftwf_malloc(sizeof(fftwf_complex) *size_pa1 );
  sem_post( &(self->fftw_sem) );

  for(i=0; i< size_pa0; i++) {
    ((float*)gn0)[2*i]=0;
    ((float*)gn0)[2*i+1]=0;
    if(i<=CALM_ZONE_LEN/2 || i>(size_pa0-(CALM_ZONE_LEN+1)/2)  ) {
      ((float*)gn0)[2*i]=1.0;
      ((float*)gn0)[2*i+1]=0;
    }
  }
  for(i=0; i< size_pa1; i++) {
    ((float*)gn1)[2*i]=0;
    ((float*)gn1)[2*i+1]=0;
    if(i<=CALM_ZONE_LEN/2 || i>(size_pa1-(CALM_ZONE_LEN+1)/2) ) {
      ((float*)gn1)[2*i]=1.0;
      ((float*)gn1)[2*i+1]=0;
    }
  }
  {
    fftwf_plan plan0, plan1;
    sem_wait( &(self->fftw_sem));
    fftwf_plan_with_nthreads(1 );
    plan0 = fftwf_plan_dft_1d(size_pa0,(fftwf_complex* ) gn0 ,(fftwf_complex* )gn0 , FFTW_FORWARD, FFTW_ESTIMATE);
    plan1 = fftwf_plan_dft_1d(size_pa1,(fftwf_complex* )gn1 ,(fftwf_complex* )gn1 , FFTW_FORWARD, FFTW_ESTIMATE);
    sem_post( &(self->fftw_sem) );
    fftwf_execute(plan0);
    fftwf_execute(plan1);
    sem_wait( &(self->fftw_sem));
    fftwf_destroy_plan(plan0);
    fftwf_destroy_plan(plan1);
    sem_post( &(self->fftw_sem) );
  }
  float  * auxbuffer ;
  sem_wait( &(self->fftw_sem));
  auxbuffer     = (float *) fftwf_malloc(sizeof(fftwf_complex) *size_pa0*size_pa1 );
  fftwf_plan_with_nthreads(1 );
  plan2D_forward = fftwf_plan_dft_2d(size_pa0,size_pa1 ,
             (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
             FFTW_FORWARD         ,FFTW_ESTIMATE );
  plan2D_backward = fftwf_plan_dft_2d(size_pa0,size_pa1 ,
              (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
              FFTW_BACKWARD         ,FFTW_ESTIMATE );
  sem_post( &(self->fftw_sem) );

  for(i=0; i<size_pa0; i++) {
    for(j=0; j< size_pa1 ; j++) {
      ( auxbuffer) [ (i*size_pa1+j)*2  ] =  SLICE[(i*size_pa1+j)  ];
      ( auxbuffer) [ (i*size_pa1+j)*2 +1 ] =0 ;
    }
  }
  free(SLICE);

  fftwf_execute(plan2D_forward );
  for(i=0; i<size_pa0; i++) {
    for(j=0; j< size_pa1 ; j++) {
      ((fcomplex *)auxbuffer)[ (i*size_pa1+j)]=
  ((fcomplex *)auxbuffer)[(i*size_pa1+j)]*
  gn0[i]*gn1[j]
  ;
    }
  }
  fftwf_execute(plan2D_backward );
  sem_wait( &(self->fftw_sem));
  fftwf_free(gn0);
  fftwf_free(gn1);

  fftwf_destroy_plan(plan2D_forward);
  fftwf_destroy_plan(plan2D_backward);
  sem_post( &(self->fftw_sem));


  assert(CALM_ZONE_LEN <size_pa0 );
  assert(CALM_ZONE_LEN <size_pa1 );
  int minI=CALM_ZONE_LEN/2 , minJ= CALM_ZONE_LEN/2  ;
  float minG = 1.0e100;
  if (self->params.verbosity>1) printf(" minG = %e \n", minG);
  for(i= size_pa0/4+ CALM_ZONE_LEN/2; i<(3*size_pa0)/4-CALM_ZONE_LEN/2; i++) {
    for(j= size_pa1/4+CALM_ZONE_LEN/2 ; j< 3*size_pa1/4 -CALM_ZONE_LEN/2 ; j++) {
      if(auxbuffer[ 2*(i*size_pa1+j)]<minG) {
  minG = auxbuffer[ 2*(i*size_pa1+j)];
  minI=i;
  minJ=j;
      }
    }
  }
  if (self->params.verbosity>1) {
    printf(" minG = %e \n", minG);
    printf(" MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n");
    printf(" MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n");
    printf("minI %d minJ %d  size_pa0 %d  size_pa1  %d \n",minI,minJ, size_pa0  , size_pa1);
    printf(" MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n");
    printf(" MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n");
  }
  double mean=0.0;
  double res=0.0;
  for(i= minI-CALM_ZONE_LEN/2; i<minI+(CALM_ZONE_LEN+1)/2; i++) {
    for(j=minJ -CALM_ZONE_LEN/2; j<minJ+(CALM_ZONE_LEN+1)/2; j++) {
      mean=mean +  SLICE_a[(i*size_pa1+j)] ;
    }
  }
  mean=mean/( CALM_ZONE_LEN*CALM_ZONE_LEN  );

  float diff;
  for(i= minI -CALM_ZONE_LEN/2; i< minI+(CALM_ZONE_LEN+1)/2; i++) {
    for(j= minJ -CALM_ZONE_LEN/2; j< minJ +(CALM_ZONE_LEN+1)/2; j++) {
      diff = SLICE_a[(i*size_pa1+j)] -mean  ;
      res=res + diff*diff ;
    }
  }

  free(auxbuffer);

  res = res/( CALM_ZONE_LEN*CALM_ZONE_LEN  )  ;


  return (float) sqrt(res);

};

void risolvipuntimedi( float *B ,float *A, int dimx, float *C ) {
  int ivolta,j;

  memset(B,0, sizeof(float)*dimx);
  for(ivolta=0; ivolta<30; ivolta++) {
    for(j=0 ; j<dimx-1; j++) {
      C[j] = A[j]-0.5*(B[j ]+ B[j+1]   );
    }
    C[dimx-1] = A[dimx-1]-0.5*(B[dimx-1]+ B[0] );

    for(j=0 ; j<dimx-1; j++) {
      B[j ] += C[j]*0.5f ;
       B[j+1] += C[j]*0.5f ;
    }
    B[0 ] += C[dimx-1]*0.5f ;
    B[dimx-1 ] += C[dimx-1]*0.5f ;
  }
}

void rotational2zero(CCspace *self,float *SLICE,float *SLICEres) {

  int dimy, dimx;

  dimy =  self->params.num_y ;
  dimx =  self->params.num_x ;

  float *X = SLICE;
  float *Y = SLICE +  dimy * dimx  ;

  float  * auxbuffer;

  sem_wait( &(self->fftw_sem));
  auxbuffer     = (float *) fftwf_malloc(sizeof(fftwf_complex) * dimy * dimx );
  sem_post( &(self->fftw_sem) );

  
  memset( auxbuffer, 0, sizeof(fftwf_complex) * dimy * dimx);
  {
    double sum=0,d;
    int i,j;
    for(i=0; i<dimy; i++) {
      for(j=0; j<dimx; j++) {
  d=auxbuffer[2*(i*dimx +j)] =  (Y[   i*dimx           +  ((j+1)%dimx) ] - Y[ i*dimx  +  (j-1+dimx)%dimx ]  +
             X[  ((i-1+dimy)%dimy)*dimx  + j  ]      - X[((i+1)%dimx )*dimx   +   j  ])/2;
  sum += d*d ;

      }
    }
    if (self->params.verbosity>1) printf(" ROT2ZERO sum %e \n", sum);
  }

  fftwf_plan plan2D_forward, plan2D_backward ;

  sem_wait( &(self->fftw_sem));

  fftwf_plan_with_nthreads(1 );
  plan2D_forward = fftwf_plan_dft_2d(dimy, dimx,
             (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
             FFTW_FORWARD         ,FFTW_MEASURE );
  plan2D_backward = fftwf_plan_dft_2d(dimy, dimx,
              (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
              FFTW_BACKWARD         ,FFTW_MEASURE );
  sem_post( &(self->fftw_sem) );


  fftwf_execute(plan2D_forward);
  int i,j;
  float kernx[dimx];
  for( i=0; i<=(dimx)/2; i++) {
    kernx[i] = sin(  ((float)i)/dimx *2*M_PI  ) ;
    kernx[i] *= kernx[i] ;
  }
  for(i= (dimx)/2+1 ; i< dimx ; i++) {
    kernx[i] = sin(  ((float)( i -dimx ) )/dimx*2*M_PI ) ;
    kernx[i] *= kernx[i] ;
  }
  float kerny[dimy];
  for( i=0; i<=(dimy)/2; i++) {
    kerny[i] = sin(  ((float)i)/dimy *2*M_PI  ) ;
    kerny[i] *= kerny[i] ;
  }
  for(i= (dimy)/2+1 ; i< dimy ; i++) {
    kerny[i] = sin(  ((float)( i - dimy) )/dimy*2*M_PI ) ;
    kerny[i] *= kerny[i] ;
  }


#define auxbuffer2(k) auxbuffer[2*(k)]

  X = SLICEres;
  Y = SLICEres +  dimy * dimx  ;

  auxbuffer[0] = auxbuffer[1] = 0.0f;
  for(i=0; i<dimy; i++) {
    for(j=0 ; j<dimx; j++) {
      if(i+j) {
  auxbuffer[2*(i*dimx +j)  ] /=  1.0f*(kerny[i]+kernx[j] );
  auxbuffer[2*(i*dimx +j)+1] /=  1.0f*(kerny[i]+kernx[j] );
      }
    }
  }

  fftwf_execute(plan2D_backward);

  for(i=0; i<dimy; i++) {
    for(j=0 ; j<dimx; j++) {
      X[ i*dimx +j]-=   auxbuffer2(((i+1)%dimy)*dimx +  j ) - auxbuffer2( ((i-1+dimy)%dimy)*dimx +j );    // componente X
    }

  }

  for(j=0 ; j<dimx; j++) {
    for(i=0; i<dimy; i++) {
      Y[i*dimx + j ]-= - auxbuffer2(i*dimx +((j+1)%dimx)) + auxbuffer2(i*dimx +((j-1+dimx)%dimx));   // componente Y
    }
  }

    {
    double sum=0,d;
    int i,j;
    for(i=0; i<dimy; i++) {
      for(j=0; j<dimx; j++) {
  d =  (Y[   i*dimx           +  ((j+1)%dimx) ] - Y[ i*dimx  +  (j-1+dimx)%dimx ]  +
        X[  ((i-1+dimy)%dimy)*dimx  + j  ]      - X[((i+1)%dimx )*dimx   +   j  ])/2;
  sum += d*d ;

      }
    }
    if (self->params.verbosity>1)printf(" ROT2ZERO DOPO , sum %e \n", sum);
  }



#undef auxbuffer2
   sem_wait( &(self->fftw_sem));
   fftwf_free(auxbuffer);
   fftwf_destroy_plan(plan2D_forward);
   fftwf_destroy_plan(plan2D_backward);
   sem_post( &(self->fftw_sem));
   
}

void reset2nan( int *znan, float *Rawptr, int Size0, int Size1, int marge    )  {
  int k;
  int I0,I1;
  
  for(k=0; k<4; k+=2) {
    for(I0 = min(       max(znan[k] -marge,0)    ,Size0) ;I0 <  min( znan[k+1]+ marge,Size0) ; I0++) {
      for(I1 = 0 ;I1 < Size1 ; I1++) {
	Rawptr[I0*Size1+I1]=NAN;
      }
    }
  }
}

void	get_znan_andset2zero(int *znan, float *Rawptr, int Size0, int Size1   ) 	{
  int k;
  for (k=0; k<4; k++) {
    znan[k] = -1  ; 
  }
  int item=0;
  int I0,I1;
  for(I0=0; I0<Size0; I0++) {
    float tmp;
    tmp = Rawptr[I0*Size1+Size1/2];
    if(tmp!=tmp) {
      if( item==0 || item==2) {
	znan[item]=I0;
	item++;
      }
    } else {
      if( item==1 || item==3) {
	znan[item]=I0;
	item++;
      }
    }
  }
  for(k=item; k<4; k++) {
    znan[k]=10000000;
  }
  for(k=0; k<4; k+=2) {
    for(I0 = min(znan[k],Size0) ;I0 < min(znan[k+1],Size0) ; I0++) {
      for(I1 = 0 ;I1 < Size1 ; I1++) {
	Rawptr[I0*Size1+I1]=0;
      }
    }
  }
}
  

/* int check_roundfft() { */
/*   for(int i=1; i<4000; i++) { */
/*     int n= roundfft(i); */
/*     int f[] = {2,3,5,7,11,13 }; */
/*     int r = n; */
/*     for(int i=0; i< 6; i++) { */
/*       while(r%f[i] == 0) { */
/* 	r = r/f[i]; */
/*       } */
/*     } */
/*     printf(" %d %d %d  \n", i,n, r); */
/*   } */
/* } */


int roundfft(const long int N) {
  // long int MA=0,MB=0,MC=0,MD=0,ME=0,MF=0 ;
  const long int FA=2,FB=3,FC=5,FD=7,FE=11,FFF=13;
  long int DIFF=9999999999;
  long int   RES=1;

  long int   AA=1;
  long int BB,CC,DD,EE,FF;
  int A,B,C,D,E,F;
  for ( A=0; A <   (int) (log(1.0*N)/log(1.0*FA)+2); A++ ) {
    BB=AA ;
    for ( B=0; B <   (int) (log(1.0*N)/log(1.0*FB)+2); B++ ) {
      CC=BB ;
      for ( C=0; C <   (int) (log(1.0*N)/log(1.0*FC)+2); C++ ) {
	DD=CC ;
	for ( D=0; D <   (int) (log(1.0*N)/log(1.0*FD)+2); D++ ) {
	  EE=DD ;
	  for( E = 0; E<2; E++)  {
	    FF = EE; 
	    for( F = 0; F<2-E; F++) {
	      if(FF>N && DIFF>abs(N-FF)) {
		/* MA = A; */
		/* MB = B; */
		/* MC = C; */
		/* MD = D; */
		/* ME = E; */
		/* MF = F; */
		
		DIFF = abs(N-FF) ; 
		RES = FF;
		//printf("%ld %ld %ld %ld %ld %ld   %ld %ld %ld\n", A,B,C,D,E,F , DIFF, FF,N) ;
	      }
	      if(FF>N) break;
	      FF *= FFF;
	    }
	    if(EE>N) break;
	    EE *= FE;
	  }
	  if(DD>N) break;
	  DD *= FD;
	}
	if(CC>N) break;
	CC *= FC;
      }
      if(BB>N) break;
      BB *= FB;
    }
    if(AA>N) break;
    AA *= FA;
  }
  return RES;
}






void       Paganin(CCspace *  self,  float * RawptrA,
		   int Pos0, int  Pos1, int  Size0, int  Size1,
		   int pos0, int  pos1, int  size0, int  size1,
		   Cparameters *P , int ncpus,   sem_t* fftw_sem,
		   int pstart, int pend, int poffset,
		   int npj_offset,
		   int mystart,
		   int npbunches,
		   int ibunch
		   )  {
  
  int pos_pa0,   pos_pa1,   size_pa0,   size_pa1;
  int posEnd_pa0,  posEnd_pa1;
  int siz, siz2n, diff ;
  int i,j, I0, I1;
  float *f0 , *f1;
  fcomplex *gn0, *gn1;
  float sum_gn0, sum_gn1;
  float  * auxbuffer ;
  float  * kernelbuffer ;
  float *fgn0, *fgn1;
  float dum;
  fftwf_plan plan0, plan1;
  fftwf_plan plan2D_forward, plan2D_backward ;
  long int npj;
  float * Rawptr;
  CCspace *  selfP;

  printf("  IN PAGANIN ROUTINE\n");
  
  selfP =  self;

  omp_set_nested(0);

  auxbuffer = NULL ;
  kernelbuffer = NULL ;

  size_pa0 =   max(size0 +2*P->PAGANIN_MARGE,Size0);
  size_pa1 =   max(size1 +2*P->PAGANIN_MARGE,Size1);

  pos_pa0    =  pos_pa1    = 0 ;
  posEnd_pa0 =  posEnd_pa1 = 0 ;

  pos_pa0 = pos0-(max( pos0 -Pos0,P->PAGANIN_MARGE) );
  posEnd_pa0 = (pos0+size0) + ( max ((Pos0+Size0) -(pos0+size0 ),     P->PAGANIN_MARGE) );
  siz = posEnd_pa0 - pos_pa0 ;
  // siz2n = (int ) ( exp(log(2.0)* ( (int) ( log(siz*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );
  siz2n = roundfft(siz);
  diff=siz2n-siz;
  pos_pa0    =  pos_pa0 - diff/2 ;
  posEnd_pa0 =  posEnd_pa0 + ( diff-diff/2) ;
  size_pa0 =  siz2n ;

  pos_pa1 = pos1-(max( pos1 -Pos1,P->PAGANIN_MARGE) );
  posEnd_pa1 = (pos1+size1) + ( max ((Pos1+Size1) -(pos1+size1 ),     P->PAGANIN_MARGE) );
  siz = posEnd_pa1 - pos_pa1 ;
  // siz2n = (int ) ( exp(log(2.0)* ( (int) ( log(siz*1.0)/log(2.0)+0.9999999) ) )  +0.999999 );
  siz2n =  roundfft(siz); 
  diff=siz2n-siz;
  pos_pa1    =  pos_pa1 - diff/2 ;
  posEnd_pa1 =  posEnd_pa1 + ( diff-diff/2) ;
  size_pa1 =  siz2n ;

  f0  =(float*) malloc(sizeof(float)*size_pa0);
  f1  =(float*) malloc(sizeof(float)*size_pa1);

  
  sem_wait( &(self->fftw_sem));
  gn0 = (fcomplex*) fftwf_malloc(sizeof(fftwf_complex) *size_pa0 );
  gn1 = (fcomplex*) fftwf_malloc(sizeof(fftwf_complex) *size_pa1 );
  sem_post( &(self->fftw_sem) );

  fgn0 =(float*) malloc(sizeof(float)*size_pa0);
  fgn1 =(float*) malloc(sizeof(float)*size_pa1);


  for( i=0; i<=(size_pa0)/2; i++) {
    f0[i] = ((float)i)/size_pa0/P->IMAGE_PIXEL_SIZE_2 *P->PAGANIN_Lmicron   ;
    fgn0[i] = ((float)i)/size_pa0 *2*M_PI;
  }

  for( i=0; i<=(size_pa1)/2; i++) {
    f1[i] = ((float)i)/size_pa1/P->IMAGE_PIXEL_SIZE_1 *P->PAGANIN_Lmicron   ;
    fgn1[i] = ((float)i)/size_pa1 *2*M_PI;
  }

  for(i= (size_pa0)/2+1 ; i< size_pa0 ; i++) {
    f0[i] = ((float)( i - size_pa0) )/size_pa0/P->IMAGE_PIXEL_SIZE_2 *P->PAGANIN_Lmicron ;
    fgn0[i] = ((float)( i - size_pa0) )/size_pa0*2*M_PI  ;
  }

  for(i= (size_pa1)/2+1 ; i< size_pa1 ; i++) {
    f1[i] = ((float)( i - size_pa1) )/size_pa1/P->IMAGE_PIXEL_SIZE_1 *P->PAGANIN_Lmicron ;
    fgn1[i] = ((float)( i - size_pa1) )/size_pa1*2*M_PI  ;
  }

  if ( P->PUS >0.0)   {
    for(i=0; i<size_pa0; i++) {
      dum = i*i/(2*P->PUS*P->PUS) ;
      ((float*)gn0)[2*i] = exp(  - min(  dum, 100  ) ) ;
      ((float*)gn0)[2*i+1] =  0    ;
    }
    sum_gn0=((float*)gn0)[0];
    for(i=1; i<size_pa0; i++) {
      ((float*)gn0)[2*i] = ((float*)gn0)[2*i] + ((float*)gn0)[2*(size_pa0- i)]  ;
      sum_gn0+= ((float*)gn0)[2*i] ;
    }
    for(i=0; i<size_pa1; i++) {
      dum = i*i/(2*P->PUS*P->PUS) ;
      ((float*)gn1)[2*i] = exp(  - min(  dum, 100  ) ) ;
      ((float*)gn1)[2*i+1] =  0    ;
    }
    sum_gn1=((float*)gn1)[0];
    for(i=1; i<size_pa1; i++) {
      ((float*)gn1)[2*i] = ((float*)gn1)[2*i] + ((float*)gn1)[2*(size_pa1- i)]  ;
      sum_gn1+= ((float*)gn1)[2*i] ;
    }

    for(i=0; i<size_pa0; i++) {
      gn0[i]/= sum_gn0;
    }
    for(i=0; i<size_pa1; i++) {
      gn1[i]/= sum_gn1;
    }

    // .......  loccare tutto fuorche execute .....
    sem_wait( &(self->fftw_sem));
    fftwf_plan_with_nthreads(ncpus ); // controllare che sia 1 per default dove deve esserlo
    // nor is there an FFTW_IN_PLACE flag. You simply pass the same pointer for both the input and output arguments.
    plan0 = fftwf_plan_dft_1d(size_pa0,(fftwf_complex* ) gn0 ,(fftwf_complex* )gn0 , FFTW_FORWARD, FFTW_ESTIMATE);
    plan1 = fftwf_plan_dft_1d(size_pa1,(fftwf_complex* ) gn1 ,(fftwf_complex* )gn1 , FFTW_FORWARD, FFTW_ESTIMATE);
    sem_post( &(self->fftw_sem) );

    fftwf_execute(plan0);
    fftwf_execute(plan1);


    sem_wait( &(self->fftw_sem));
    fftwf_destroy_plan(plan0);
    fftwf_destroy_plan(plan1);
    sem_post( &(self->fftw_sem) );
  }

  sem_wait( &(self->fftw_sem));
  auxbuffer     = (float *) fftwf_malloc(sizeof(fftwf_complex) *size_pa0*size_pa1 );
  kernelbuffer  = (float *) fftwf_malloc(sizeof(fftwf_complex) *size_pa0*size_pa1 );
  fftwf_plan_with_nthreads(ncpus ); // controllare che sia 1 per default dove deve esserlo

  plan2D_forward = fftwf_plan_dft_2d(size_pa0,size_pa1 ,
       (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
                   FFTW_FORWARD         ,FFTW_MEASURE );
  plan2D_backward = fftwf_plan_dft_2d(size_pa0,size_pa1 ,
       (fftwf_complex*)auxbuffer,(fftwf_complex*)auxbuffer,
                   FFTW_BACKWARD         ,FFTW_MEASURE );
  sem_post( &(self->fftw_sem) );

  {
// #pragma	omp parallel for private(i, j) num_threads (ncpus)


    // f0[i] = ((float)i)/size_pa0/P->IMAGE_PIXEL_SIZE_2 *P->PAGANIN_Lmicron   ;
    // fgn0[i] = ((float)i)/size_pa0 *2*M_PI;

    float pfact = P->PAGANIN_Lmicron /P->IMAGE_PIXEL_SIZE_1/2.0/M_PI ;
    pfact = 2*pfact*pfact ; 
    
    for(i=0; i<size_pa0; i++) {
      for(j=0; j< size_pa1 ; j++) {
	// ( auxbuffer) [ (i*size_pa1+j)*2  ] =  1.0/((1.0  + f0[i]*f0[i] +  f1[j]*f1[j]         )* size_pa0* size_pa1* size_pa0* size_pa1);
	( auxbuffer) [ (i*size_pa1+j)*2  ] =  1.0/((1.0  + (2-cos(fgn0[i])-cos(fgn1[j]))*pfact)* size_pa0* size_pa1* size_pa0* size_pa1);
	( auxbuffer) [ (i*size_pa1+j)*2 +1 ] =0 ;
      }
    }
    
    fftwf_execute(plan2D_backward );
    
    // #pragma	omp parallel for private(i, j) num_threads (ncpus)
    for(i= P->PAGANIN_MARGE; i<size_pa0- P->PAGANIN_MARGE; i++) {
      for(j=0; j< size_pa1 ; j++) {
	( auxbuffer) [ (i*size_pa1+j)*2  ]   =  0;
	( auxbuffer) [ (i*size_pa1+j)*2 +1 ] =  0 ;
      }
    }
    
    /* for(j=0; j< size_pa0 ; j++) { */
    /*   for(i= P->PAGANIN_MARGE; i<size_pa1- P->PAGANIN_MARGE; i++) { */
    /* ( auxbuffer) [ (j*size_pa1+i)*2  ]   =  0; */
    /* ( auxbuffer) [ (j*size_pa1+i)*2 +1 ] =  0 ; */
    /*   } */
    /* } */
    
    fftwf_execute(plan2D_forward );
    
    
    if ( P->PUS >0.0)   {
      if (self->params.verbosity>1) printf( " applico UNSHARP al kernel buffer \n");
      if(P->UNSHARP_LoG){
	for(i=0; i<size_pa0; i++) {
	  for(j=0; j< size_pa1 ; j++) {
	    ((fcomplex *)auxbuffer)[ (i*size_pa1+j)  ]*=(1.0 +P->PUC*((2-2*cos(fgn0[i]))+(2-2*cos(fgn1[j])))*gn0[i]*gn1[j]);
	  }
	}
      } else {
	for(i=0; i<size_pa0; i++) {
	  for(j=0; j< size_pa1 ; j++) {
	    ((fcomplex *)auxbuffer)[ (i*size_pa1+j)  ] *= ((1.0+ P->PUC) -P->PUC *((fcomplex *) gn0 )[i]  * ((fcomplex *) gn1 )[j] )    ;
	  }
	}
      }
    }
    
    
    // #pragma	omp parallel for private(i, j) num_threads (ncpus)
    for(i=0; i<size_pa0; i++) {
      for(j=0; j< size_pa1 ; j++) {
	(kernelbuffer ) [ (i*size_pa1+j)*2  ]   = ( auxbuffer) [ (i*size_pa1+j)*2  ] ;
	(kernelbuffer ) [ (i*size_pa1+j)*2 +1 ] = ( auxbuffer) [ (i*size_pa1+j)*2 +1 ]  ;
      }
    }
  }
  
  // -------------------------------------------segna libro --------------------------------------------
  
  // quando il contesto e proprio zero  se c' el a libreria
  /*       inizializza contesto con sole funzioni */
  /* lascia semaforo */
  
  sem_wait( &(self->gpustat_pag_sem));
  if( self->params.MYGPU ==-1    )
    self->gpu_is_apriori_usable = 0;
  
  if( self->gpu_is_apriori_usable) {
    if(selfP->gpu_pag_context==NULL && self->params.TRYGPU){
      void *lib_handle;
      {

	lib_handle = getLibNameHandle(    self->params.nome_directory_distribution    ,  "libgputomo" )  ; 
	
      }
      if (lib_handle)	{
	char *error;
	gpu_pagCtxCreate_Symbol fncreate;
	gpu_pagCtxDestroy_Symbol fndestroy;
	gpu_pag_Symbol fn;
	gpu_pagInit_Symbol fnInit;
	gpu_pagFree_Symbol fnFree;
	
	fncreate  = (gpu_pagCtxCreate_Symbol) dlsym(lib_handle, "gpu_pagCtxCreate");
	fndestroy = (gpu_pagCtxDestroy_Symbol) dlsym(lib_handle, "gpu_pagCtxDestroy");
	fn        = (gpu_pag_Symbol) dlsym(lib_handle, "gpu_pag");
	fnInit    = (gpu_pagInit_Symbol) dlsym(lib_handle, "gpu_pagInit");
	fnFree    = (gpu_pagFree_Symbol) dlsym(lib_handle, "gpu_pagFree");
	
	if ((error = dlerror()) != NULL)  {
	  printf("Failed to find symbol gpu_pag in libgputomo.so. No Gpu will be used \n");
	  self->gpu_is_apriori_usable=0;
	} else {
	  selfP->gpu_pag_context = ( Gpu_pag_Context*) malloc(sizeof(Gpu_pag_Context));
	  selfP->gpu_pag_context->gpuctx = NULL; 
	  selfP->gpu_pag_context->inuse=0;
	  selfP->gpu_pag_context->gpu_pagCtxCreate =  fncreate  ;
	  selfP->gpu_pag_context->gpu_pagCtxDestroy =  fndestroy  ;
	  selfP->gpu_pag_context->gpu_pag =  fn  ;
	  selfP->gpu_pag_context->gpu_pagInit =  fnInit  ;
	  selfP->gpu_pag_context->gpu_pagFree =  fnFree  ;
	  selfP->gpu_pag_context->gpu_pagCtx_initialised = 0;

	  /* selfP->gpu_pag_context->size_pa0 = size_pa0 ;  */
	  /* selfP->gpu_pag_context->size_pa1 = size_pa1 ;  */
	  selfP->gpu_pag_context->MYGPU    = self->params.MYGPU ;
	  selfP->gpu_pag_context->gpu_pagCtxCreate( selfP->gpu_pag_context	)  ;
	  
	}
      } else {
	printf("Failed to load libgputomo.so. No Gpu will be used \n");
	self->gpu_is_apriori_usable=0;
      }
    } else {
      
    }
  } else {

    selfP->gpu_pag_context = ( Gpu_pag_Context*) malloc(sizeof(Gpu_pag_Context));
    selfP->gpu_pag_context->gpuctx = NULL; 
    selfP->gpu_pag_context->inuse=0;
    selfP->gpu_pag_context->gpu_pagCtx_initialised = 0;
    selfP->gpu_pag_context->MYGPU    = self->params.MYGPU ;
    // selfP->gpu_pag_context->gpu_pagCtxCreate( selfP->gpu_pag_context	)  ;
 }
  
  sem_post( &(self->gpustat_pag_sem));
  
  for(npj=pstart; npj<pend  ; npj+=2) {
    
    /* if((npj-pstart)%200==0) */
    /*   if (self->params.verbosity>0) */
    /* 	printf(" Paganin processing proje %ld from %d to %d\n",  mystart + npj - pstart , mystart  , mystart + pend - pstart ); */
    
    Rawptr = RawptrA + npj*Size0*Size1 ;


    int znan[4];
    if(self->params.DZPERPROJ)   get_znan_andset2zero(znan,Rawptr, Size0, Size1   ) ;

    long int proj_step = Size0*Size1;
    if(npj+1>pend-1 ) {
      proj_step = 0; 
    }
    
    for(i=0; i< size_pa0 ; i++) {
      I0 = i -(Pos0 -pos_pa0) ;
      I0=I0%(2*Size0) ;
      if(I0<0) {
	I0 = I0 +  2*Size0 ;
      }
      if(I0>=Size0)  {
	I0=2*(Size0)-1 - I0;
      }
      for(j=0; j< size_pa1 ; j++) {
	I1 = j -(Pos1-pos_pa1) ;
	I1=I1%(2*Size1) ;
	if(I1<0) {
	  I1 = I1 +  2*Size1 ;
	}
	if(I1>=Size1)  {
	  I1=2*(Size1)-1 - I1;
	}
	( auxbuffer) [ (i*size_pa1+j)*2  ] =  Rawptr[I0*Size1+I1]  ;
	( auxbuffer) [ (i*size_pa1+j)*2+1] =  Rawptr[I0*Size1+I1 + proj_step] ;
      }
    }

    {
      sem_wait( &(self->gpudones_pag_sem));
      if(selfP->gpu_pag_context->gpu_pagCtx_initialised==0) {
	selfP->gpu_pag_context-> NBunches_todo = npbunches;
	{
	  int k=0;
	  for( k=0; k< npbunches ; k++) {
	    selfP->gpu_pag_context->dones[k]=0;
	  }
	}
	selfP->gpu_pag_context->gpu_pagCtx_initialised=-1;
      }
      sem_post( &(self->gpudones_pag_sem));
    }
    
    int dowithcpu=1 ;
    {
      int isNotavailable = sem_trywait( &(self->gpustat_pag_sem));
      if(isNotavailable==0){
	if( self->gpu_is_apriori_usable) {
	  if(selfP->gpu_pag_context!=NULL && self->params.TRYGPU){
	    dowithcpu=0; ///////////////////
	    if(selfP->gpu_pag_context->gpu_pagCtx_initialised==-1) {
	      selfP->gpu_pag_context->size_pa0 = size_pa0 ;
	      selfP->gpu_pag_context->size_pa1 = size_pa1 ;
	      
	      selfP->gpu_pag_context->kernelbuffer =  kernelbuffer;
	      selfP->gpu_pag_context->gpu_pagInit( selfP->gpu_pag_context	)  ;
	      selfP->gpu_pag_context->gpu_pagCtx_initialised = 1;
	    }
	    
	    {
	      sem_wait( &(self->gpudones_pag_sem));
	      {
		int k=0;
		for( k=0; k < npbunches/self->params.RAWDATA_MEMORY_REUSE; k++) {
		  if( selfP->gpu_pag_context->dones[ ibunch ]  >  selfP->gpu_pag_context->dones[ k ] + 2         ) {
		    dowithcpu=1; 
		    break;
		  }
		}
	      }
	      sem_post( &(self->gpudones_pag_sem));
	    }
	    
	    if(!dowithcpu) {
	      
	      printf(" Paganin GPU FFT  for %ld\n",   mystart + npj - pstart );
	      
	      selfP->gpu_pag_context->gpu_pag( selfP->gpu_pag_context,  auxbuffer	)  ;
	      
	      sem_post( &(self->gpustat_pag_sem) );
	      
	      if(npj+1 > pend-1 ) {
		for(i=   ( Pos0 - pos_pa0  )  ; i<( Pos0 - pos_pa0  ) + Size0; i++) {
		  for(j=( Pos1 - pos_pa1  ); j< ( Pos1 - pos_pa1) + Size1 ; j++) {
		    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1)  ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2  ] ;
		  }
		}
	      } else {
		for(i=   ( Pos0 - pos_pa0  )  ; i<( Pos0 - pos_pa0  ) + Size0; i++) {
		  for(j=( Pos1 - pos_pa1  ); j< ( Pos1 - pos_pa1) + Size1 ; j++) {
		    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1)  ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2  ] ;
		    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1) + proj_step ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2 +1 ] ;
		  }
		}
	      }
	      if(self->params.DZPERPROJ) reset2nan( znan,Rawptr, Size0, Size1 , P->PAGANIN_MARGE   ) ;
	    }
	  }
	}
	if( dowithcpu) sem_post( &(self->gpustat_pag_sem) );
      } else{
	dowithcpu = 1 ; 
      }
    }
    {
      sem_wait( &(self->gpudones_pag_sem));
      if(selfP->gpu_pag_context!=NULL ){
	selfP->gpu_pag_context->dones[ ibunch ] += 1;
      }
      sem_post( &(self->gpudones_pag_sem));
    }

    if(dowithcpu) {
      fftwf_execute(plan2D_forward );
      
      for(i=0; i<size_pa0; i++) {
	for(j=0; j< size_pa1 ; j++) {
	  ((fcomplex *)auxbuffer)[ (i*size_pa1+j)]=((fcomplex *)auxbuffer)[(i*size_pa1+j)]*((fcomplex *)kernelbuffer)[(i*size_pa1+j)];
	}
      }
      printf(" Paganin CPU FFT  for %ld  \n",   mystart + npj - pstart );

      
      fftwf_execute(plan2D_backward );

      		  
      if(npj+1>pend-1 ) {
	for(i=   ( Pos0 - pos_pa0  )  ; i<( Pos0 - pos_pa0  ) + Size0; i++) {
	  for(j=( Pos1 - pos_pa1  ); j< ( Pos1 - pos_pa1) + Size1 ; j++) {
	    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1)  ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2  ] ;
	  }
	}
      } else {
	for(i=   ( Pos0 - pos_pa0  )  ; i<( Pos0 - pos_pa0  ) + Size0; i++) {
	  for(j=( Pos1 - pos_pa1  ); j< ( Pos1 - pos_pa1) + Size1 ; j++) {
	    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1)  ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2  ] ;
	    Rawptr [ (i-(Pos0-pos_pa0))*Size1 +j-(Pos1-pos_pa1) + proj_step ]= ((float *)  auxbuffer)[ (i*size_pa1+j)*2  +1] ;
	  }
	}
      }
      if(self->params.DZPERPROJ) reset2nan( znan,Rawptr, Size0, Size1 , P->PAGANIN_MARGE   ) ;
    } 

    
    if(self->params.DO_OUTPUT_PAGANIN) {
      char   output_name[1000];
      sprintf(output_name, "%s_%04ld.edf",self->params.OUTPUT_PAGANIN_FILE, (int) mystart + npj - pstart );
      if (self->params.verbosity>0) printf(" writing projection on %s \n", output_name);
      write_data_to_edf(Rawptr  , Size0, Size1   , output_name);
      if(npj+1 <= pend-1 ) {
          char   output_name[1000];
          sprintf(output_name, "%s_%04ld.edf",self->params.OUTPUT_PAGANIN_FILE, (int) mystart + npj+1 - pstart );
          if (self->params.verbosity>0) printf(" writing projection on %s \n", output_name);
          write_data_to_edf(Rawptr  + proj_step  , Size0, Size1   , output_name);
      }

    }
  }

  if(1){
    sem_wait( &(self->gpustat_pag_sem));
    if(selfP->gpu_pag_context && selfP->gpu_pag_context->gpu_pagCtx_initialised) {
      printf(" STO PER USCIRE %d \n" ,selfP->gpu_pag_context-> NBunches_todo  );

      selfP->gpu_pag_context-> NBunches_todo-- ;
      if( selfP->gpu_pag_context-> NBunches_todo   ==0) {
	selfP->gpu_pag_context->gpu_pagFree( selfP->gpu_pag_context	)  ;
	selfP->gpu_pag_context->gpu_pagCtx_initialised = 0 ;
      }
    }
    sem_post( &(self->gpustat_pag_sem));
  }
  
  if (self->params.verbosity>0) printf("finito  pag\n");
  
 
  free(f0);
  free(f1);
  free(fgn0);
  free(fgn1);
  
  sem_wait( &(self->fftw_sem));
  fftwf_free(gn0);
  fftwf_free(gn1);
  if(auxbuffer) fftwf_free(auxbuffer);
  if(kernelbuffer) fftwf_free(kernelbuffer);
  fftwf_destroy_plan(plan2D_forward);
  fftwf_destroy_plan(plan2D_backward);
  sem_post( &(self->fftw_sem));
  
  if (self->params.verbosity>0)printf("esco  pag\n");

}

float raw_interp(   float  *Rawptr  , int Size0 , int Size1, float fi , float fj    ) {
  if (fi<0) fi=0;
  if (fj<0) fj=0;
  if(fi>=Size0-1) fi=Size0-1.0001;
  if(fj>=Size1-1) fj=Size1-1.0001;

  int fi_0, fj_0;
  float dv, dh;

  fi_0 = (int) fi;
  fj_0 = (int) fj;

  dv = fi-fi_0;
  dh = fj-fj_0;
  
  float a00,a01,a10,a11;
  a00 =  Rawptr[(fi_0   )*Size1+(fj_0  )]  ; 
  a01 =  Rawptr[(fi_0   )*Size1+(fj_0+1)] ;
  a10 =  Rawptr[(fi_0+1 )*Size1+(fj_0  )]; 
  a11 =  Rawptr[(fi_0+1 )*Size1+(fj_0+1)] ;
    
  float res =
    (1-dv)*((1-dh)* a00  +  (dh)*a01)
    +(dv) *((1-dh)* a10  +  (dh)*a11);
  return res;
};

void correct_distortion(	CCspace *self,  float *Tptr  , int size0, int size1, int pos0, int pos1,
				float *Rawptr, int Size0, int Size1, int Pos0, int Pos1	 ) {
  float sh, sv;
  float *dist_H =  self->params.dist_h ; 
  float *dist_V =  self->params.dist_v ;
  
  int    dist_ncol =  self->params.dist_ncol ;
  assert( dist_ncol ==   Size1    );
  int i;
  for(i=0; i<size0; i++) {
    int ipos = pos0+i;

    int j;
    for(j=0; j< size1; j++) {
      int jpos = pos1+j;

      sh =   dist_H[ipos*Size1 +  jpos ] ;
      sv =   dist_V[ipos*Size1 +  jpos ] ;
	
      Tptr[i*size1  +j] = raw_interp(    Rawptr  , Size0 , Size1, (i+( pos0-Pos0 )) + sv ,  (j+( pos1-Pos1 ))  +sh  );
    }
  }  
}

void     Filter_and_Trim(CCspace *self, float * buffer, float * Tptr  ,float * Rawptr,
			 int Pos0, int Pos1, int  Size0, int Size1,
			 int pos0, int pos1, int size0, int size1,
			 float axis_correctionsL,
			 int ncpus
			 ) {
  
  int i,j;
  
  // printf("chiamo  ???  CCD_AXIS_LONGITUDINAL_CORRECTION_Implementation  %e  %d %d %d %d \n", axis_correctionsL, Pos0,   Size0, pos0, size0  );
  
  if ( fabs(axis_correctionsL)>0.0) {
    
    if( self->params.dist_h == NULL && self->params.dist_v == NULL ) {
      CCD_AXIS_LONGITUDINAL_CORRECTION_Implementation(Tptr,   Rawptr,
						      Pos0,  Pos1,   Size0,  Size1 ,
						      pos0,  pos1,  size0,  size1,
						      axis_correctionsL , ncpus  );
    } else {
      float *tmp = (float *) malloc(sizeof(float)*Size0*Size1);
      correct_distortion(
			 self, 
			 tmp  , Size0, Size1, Pos0, Pos1,
			 Rawptr, Size0, Size1, Pos0, Pos1
			 );		       
      CCD_AXIS_LONGITUDINAL_CORRECTION_Implementation(Tptr,   tmp   ,
						      Pos0,  Pos1,   Size0,  Size1 ,
						      pos0,  pos1,  size0,  size1,
						      axis_correctionsL , ncpus  );
    }
  } else {
    
    if( self->params.dist_h == NULL && self->params.dist_v == NULL ) {
      for(i=0; i<size0; i++) {
	for(j=0; j< size1; j++) {
	  Tptr[i*size1  +j] =  Rawptr[ (i  +( pos0-Pos0 )    ) *Size1 + (j+( pos1-Pos1 ))    ]  ;
	}
      }
    } else {
      correct_distortion(
			 self, 
			 Tptr  , size0, size1, pos0, pos1,
			 Rawptr, Size0, Size1, Pos0, Pos1
			 );		       
    }
  }
}



void     Filter_CCD(CCspace *  self, float * buffer, float * Rawptr,
		    int Pos0, int Pos1, int  Size0, int Size1,
		    int pos0, int pos1, int size0, int size1,
		    int CCD_FILTER_KIND ,
		    void *CCD_FILTER_PARA,
		    // float axis_correctionsL,
		    int ncpus
		    ) {
  
  CCspace *  selfP;
  
  
  int onectx4thread=0;
  if(onectx4thread) {
    selfP = (CCspace*) malloc(sizeof(CCspace));
    selfP->gpu_med_context=NULL ;
  }
  else selfP =  self;
  
  
  if( CCD_FILTER_KIND ==  CCD_FILTER_NONE_ID) {
    // pass ;
  } else if(  CCD_FILTER_KIND == CCD_Filter_ID )  {
    
    float threshold = ( (CCD_Filter_PARA_struct *) CCD_FILTER_PARA )->threshold ;
    
    if(!onectx4thread ) sem_wait( &(self->gpustat_med_sem));
    if( self->gpu_is_apriori_usable) {
      if(selfP->gpu_med_context==NULL && self->params.TRYGPU){
	void *lib_handle;
	{
	  
	  lib_handle = getLibNameHandle(    self->params.nome_directory_distribution    ,  "libgputomo" )  ; 
	  
	  
	}
	if (lib_handle)	{
	  char *error;
	  gpu_medCtxCreate_Symbol fncreate;
	  gpu_medCtxDestroy_Symbol fndestroy;
	  gpu_med_Symbol fn;
	  
	  fncreate = (gpu_medCtxCreate_Symbol)dlsym(lib_handle, "gpu_medCtxCreate");
	  fndestroy = (gpu_medCtxDestroy_Symbol)dlsym(lib_handle, "gpu_medCtxDestroy");
	  fn = (gpu_med_Symbol) dlsym(lib_handle, "gpu_med");
	  
	  if ((error = dlerror()) != NULL)  {
	    printf("Failed to find med symbols  in libgputomo.so. No Gpu will be used \n");
	    self->gpu_is_apriori_usable=0;
	  } else {
	    selfP->gpu_med_context = ( Gpu_med_Context*) malloc(sizeof(Gpu_med_Context));
	    selfP->gpu_med_context->inuse=0;
	    selfP->gpu_med_context->gpu_medCtxCreate =  fncreate  ;
	    selfP->gpu_med_context->gpu_medCtxDestroy =  fndestroy  ;
	    selfP->gpu_med_context->gpu_med =  fn  ;
	    selfP->gpu_med_context->MYGPU    = self->params.MYGPU ;
	    selfP->gpu_med_context->gpu_medCtxCreate( selfP->gpu_med_context	)  ;
	  }
	} else {
	  printf("Failed to load libgputomo.so. No Gpu will be used \n");
	  self->gpu_is_apriori_usable=0;
	}
      } else {
      }
    } else {
    }
    if(!onectx4thread ) sem_post( &(self->gpustat_med_sem));
    
    int dowithcpu=1 ;
    /* if(0)  { */
    /*   if(!onectx4thread ) sem_wait( &(self->gpustat_med_sem)); */
    /*   if( self->gpu_is_apriori_usable) { */
    /* 	if(selfP->gpu_med_context!=NULL && self->params.TRYGPU){ */
    /* 	  dowithcpu=0; */
    /* 	  selfP->gpu_med_context->gpu_med( selfP->gpu_med_context,Size0,  Size1 ,Rawptr,Rawptr,1,1,threshold)  ; */
    /* 	} */
    /* 	if(onectx4thread)  selfP->gpu_med_context->gpu_medCtxDestroy( selfP->gpu_med_context	)  ; */
    /*   } */
    /*   if(!onectx4thread ) sem_post( &(self->gpustat_med_sem   )); */
      
    /*   if( !self->params.TRYGPUCCDFILTER) { */
    /* 	dowithcpu=1; */
    /*   } */
    /* } */
    if(dowithcpu) {
      CCD_Filter_Implementation( buffer ,  Rawptr,   Size0,  Size1 , threshold  ,   ncpus );
    }
  } else {
    fprintf(stderr, " CCD_FILTER_KIND has an unknown value  in file  %s line %d  \n", __FILE__, __LINE__);
    exit(1);
  }
  if(onectx4thread) {
    free(selfP ) ;
  }



}


void CCD_AXIS_LONGITUDINAL_CORRECTION_Implementation(float *Tptr, float *Rawptr,
                 int Pos0, int  Pos1, int  Size0, int  Size1 ,
                 int pos0, int  pos1, int  size0, int  size1,
                 float axis_correctionsL  , int ncpus )
{



  int x0,x1;
  int X0,X1 ;
  int Dim0, Dim1;
  int dim0, dim1;

  int c0,c1;


  float *a_data, *m_data ;


  float shift ;
  double fact1, fact2;
  int ishift1, ishift2 ;

  shift = axis_correctionsL;

  a_data = Rawptr;
  m_data = Tptr;

  X0 = Pos0 ;
  X1 = Pos1 ;
  x0  = pos0 ;
  x1  = pos1 ;


  Dim0 = Size0;
  Dim1 = Size1;
  dim0 = size0 ;
  dim1 = size1 ;
#define AA(i,j)  a_data[(i)*Dim1+(j)    ]
#define MM(i,j)  m_data[(i)*dim1+(j)    ]
  ishift1= (int) shift  ;
  if(shift<0  &&  ishift1 != shift ) ishift1--;
  ishift2= ishift1+1;
  fact1 = ishift2-shift;
  fact2 = shift-ishift1;

// #pragma	omp parallel for private(c0,c1) num_threads (ncpus)

  for(c0=x0  ; c0< x0+dim0 ; c0++) {

    if(  c0+ishift1 < X0   ) {
      for(c1=x1; c1< x1+dim1 ; c1++) {
  MM((c0-x0),(c1-x1) ) =  AA( 0 ,c1) ;
      }
    } else if(  c0+ishift2 >= X0 +Dim0 ) {
      for(c1=x1; c1< x1+dim1 ; c1++) {
  MM((c0-x0),(c1-x1) ) = AA( Dim0-1,c1) ;
      }
    } else {
      for( c1=x1; c1< x1+dim1 ; c1++   ) {
  MM((c0-x0),(c1-x1) ) = fact1 * AA( (c0+ishift1-X0) ,  (c1-X1) ) + fact2 * AA( (c0+ishift2-X0), (c1-X1) );
      }
    }
  }
#undef AA
#undef MM
}



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


void  CCD_Filter_Implementation(float * Tptr, float * Rawptr,
        int Size0, int Size1 ,
        float threshold, int ncpus) {

  int x0_,x1_,  s0_,s1_;
  int X0_, X1_;
  int dim0, dim1;
  int nsum;
  int ds;
  // char msg[400];

  int c0,c1;
  int c0_,c1_;
  //  float temp[9];
  float swap;
  int boolV;
  int count;

  int shift_x[] = { -1,-1,-1, 0, 0,  0, 1,  1, 1  };
  int shift_y[] = { -1, 0, 1,-1, 0, 1,-1,  0, 1  };
  int si;
  float * a_data;
  float * m_data;

  a_data = Rawptr ;
  m_data = Tptr ;


  s0_ = Size0 ;
  s1_ = Size1 ;

  /* setting better the limits */

  x0_ = 0 ;
  x1_ = 0 ;

  X0_ = x0_ + s0_ ;
  X1_ = x1_ + s1_ ;

  dim0 = s0_;
  dim1 = s1_;

  memcpy(m_data ,a_data  , dim0*dim1*sizeof(float) );

#define AA(i,j)  a_data[(i)*dim1+(j)    ]
#define MM(i,j)  m_data[(i)*dim1+(j)    ]


  {
    float temp[9];
    
    for(c0=x0_+1 ; c0< X0_ -1; c0++) {
      for(c1=x1_+1; c1< X1_ -1; c1++) {
	
	temp[4] = AA(c0,c1);
	
	
	temp[0] = AA(c0-1,c1-1);
	temp[1] = AA(c0-1,c1);
	temp[2] = AA(c0-1,c1+1);
	temp[3] = AA(c0,c1-1);
	
	int count =0;
	count += ((  temp[4]    > temp[0] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[1] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[2] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[3] + threshold)?  1:0 );  
	
	
	temp[5] = AA(c0,c1+1);
	temp[6] = AA(c0+1,c1-1);
	temp[7] = AA(c0+1,c1);
	temp[8] = AA(c0+1,c1+1);
	
	count += ((  temp[4]    > temp[5] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[6] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[7] + threshold)?  1:0 );  
	count += ((  temp[4]    > temp[8] + threshold)?  1:0 );  
	
	
	if(count>4) {
	  
	  do
	    {
	      boolV = 0;
	      for (count = 0; count < 8; count++)
		{
		  if (temp[count] > temp[count+1])
		    {
		      swap = temp[count];
		      temp[count] = temp[count+1];
		      temp[count+1] = swap;
		      boolV = 1;
		    }
		}
	    } while(boolV!=0);
	  if (  AA(c0,c1) -temp[4] >= threshold ) MM(c0,c1) = temp[4];
	  else MM(c0,c1) = AA(c0,c1);
	}
      }
    }
  }
  float temp[9];
  ds=(s0_-1);
  if(ds==0) ds=1;
  for(c0= x0_ ; c0<X0_ ; c0 +=ds) {
    for(c1=x1_; c1<X1_; c1 ++ ) {
      nsum=0;
      
      for(si=0; si<9; si++) {
	
	c0_ = c0+ shift_x[si];
	c1_ = c1+ shift_y[si];
		
	if( c0_>=x0_ &&  c0_ <s0_ &&    c1_>=x1_ &&  c1_ <X1_  ) {
	  temp[nsum ] = AA(c0_,c1_); ;
	  nsum++;
	}
      }
      
      //Bubble-Sort
      do
	{
	  boolV = 0;
	  for (count = 0; count < nsum-1; count++)
	    {
	      if (temp[count] > temp[count+1])
		{
		  swap = temp[count];
		  temp[count] = temp[count+1];
		  temp[count+1] = swap;
		  boolV = 1;
		}
	    }
	} while(boolV!=0);
      
      if (  AA(c0,c1) -temp[nsum/2] >= threshold ) MM(c0,c1) = temp[nsum/2];
      else MM(c0,c1) = AA(c0,c1);
      
    }
    
  }
  for(c0= x0_ ; c0<X0_ ; c0 ++) {
    ds=(s1_-1);
    if(ds==0) ds=1;
    
    for(c1=x1_; c1<X1_; c1 += ds) {
      nsum=0;
      
      for(si=0; si<9; si++) {
	
        c0_ = c0+ shift_x[si];
        c1_ = c1+ shift_y[si];
	
	
	
        if( c0_>=x0_ &&  c0_ <s0_ &&    c1_>=x1_ &&  c1_ <X1_  ) {
          temp[nsum ] = AA(c0_,c1_); ;
          nsum++;
        }
      }
      
      //Bubble-Sort
      do
        {
          boolV = 0;
          for (count = 0; count < nsum-1; count++)
            {
              if (temp[count] > temp[count+1])
                {
                  swap = temp[count];
                  temp[count] = temp[count+1];
                  temp[count+1] = swap;
                  boolV = 1;
                }
            }
        } while(boolV!=0);
      
      if (  AA(c0,c1) -temp[nsum/2] >= threshold ) MM(c0,c1) = temp[nsum/2];
      else MM(c0,c1) = AA(c0,c1);
      
    }
  }
  
  memcpy( a_data, m_data, dim0*dim1*sizeof(float) );
  
#undef AA
#undef MM

}


void 	CCspace_RING_Filter_implementation(CCspace *   self, float *data,
             RING_Filter_PARA_struct*  RING_FILTER_PARA ,
             int nslices,
             int nprojs_span,
             int size1 ,
             int ncpus,
             int *islicetracker)  {
  int i;
  int dim_3,dim2,dim_1;
  float * a_data;
  float * sum_line;
  fcomplex *temp;

 int dim_fft, two_power;
  int slice_count, proj_count;
  fftwf_plan planr2c, planc2r;
  int tid;

  a_data = (float *) data;
  dim_3 =  nslices           ;
  dim2  =  nprojs_span   ;
  dim_1 =  size1             ;
  two_power = (int)(log((2. * dim_1 - 1)) / log(2.0) + 0.9999);
  dim_fft = 1; for(i=1;i<two_power; i++) dim_fft*=2;
#define AA(i,j,k)  a_data[(k)+dim_1*((j)+dim2*(i))  ]
  sum_line = (float*) malloc( ncpus*dim_fft*sizeof(float)  );
  temp     = (fcomplex *) malloc( ncpus*dim_fft*sizeof(fcomplex)  );

  sem_wait( &(self->fftw_sem));
  fftwf_plan_with_nthreads(1 );
  planr2c = fftwf_plan_dft_r2c_1d(  dim_fft  , sum_line, (fftwf_complex*) temp, FFTW_ESTIMATE    |  FFTW_UNALIGNED);
  planc2r = fftwf_plan_dft_c2r_1d(  dim_fft  , (fftwf_complex*)temp, sum_line, FFTW_ESTIMATE    |  FFTW_UNALIGNED);
  sem_post( &(self->fftw_sem) );

// #pragma	omp parallel for private(slice_count,tid,i,proj_count) num_threads (ncpus)
  for(slice_count=0; slice_count< dim_3; slice_count++) {
    if(islicetracker ) {
      int doit=1;
      sem_wait( &(self->islicetracker_sem));
      if ( islicetracker[slice_count] ) {
	doit=0;
      }
      islicetracker[slice_count]=1;
      sem_post( &(self->islicetracker_sem) );
      if (doit==0)  continue;
    }

    tid=  0 ; // omp_get_thread_num();
    memset(sum_line+tid*dim_fft ,0,dim_fft*sizeof(float) );
    for( proj_count=0 ; proj_count< dim2; proj_count++) {
      for(i=0; i< dim_1; i++) {
  sum_line[i+tid*dim_fft ] += AA(  slice_count, proj_count, i) ;
      }
    }
    for(i=0; i< dim_1; i++) {
      sum_line[i+tid*dim_fft ] /= dim2 ;
    }

    /* Filtering */

    fftwf_execute_dft_r2c( planr2c ,  (sum_line+tid*dim_fft), (fftwf_complex*) (temp +tid*dim_fft) );


    /******************************************************
     * Multiply the Fourier transformed array with filter *
     ******************************************************/


    temp[tid*dim_fft]=0.0;
    for(i=0;i<size1/2 ;i++) {
      temp[tid*dim_fft+i] *=  RING_FILTER_PARA->FILTER[2*i] ;
      // temp[(tid+1)*dim_fft-i]=0.0; // questo non serve perche r2c usa solo meta dei complex
    }

    /***************************************************
     * Inverse Fourier transform to give convolution   *
     ***************************************************/

    fftwf_execute_dft_c2r( planc2r ,  (fftwf_complex*)(temp +tid*dim_fft) , (sum_line+tid*dim_fft) );

    /* Subtraction */

    for( proj_count=0 ; proj_count< dim2; proj_count++) {
      for(i=0; i< dim_1; i++) {
  AA(  slice_count, proj_count, i) -=  sum_line[i+tid*dim_fft]/dim_fft  ;
      }
    }
  }

  free(sum_line);
  free(temp );
}
#undef AA


void 	CCspace_RING_Filter_SG_implementation(CCspace *   self, float *data,
                RING_Filter_SG_PARA_struct*  RING_FILTER_PARA ,
                int nslices,
                int nprojs_span,
                int size1 ,
                int ncpus,
                int *islicetracker)  {

  int i,k, j;
  int dim_3,dim2,dim_1;
  float * a_data;
  int LF, I_Slope, Lfen;
  float Eps1, Eps2, Axe_Rot, RProt;
  int tid;
  // float *Coeff_fil ;
  float   * sum_line ;
  float   * liss_line ;
  int  dist_from_corr ;
  float   * Coeff_Corr ;
  float   * Corr_Rayon ;
  int I_beg, I_end,   NL ;
  float Erreur ;
  // int dist_c;
  int IRd, IRf ;
  float Pas ;
  int slice_count, proj_count ;
  // ALLOCATE(Coeff_fil(0:LF-1,LF))
#define AA(i,j,k)  a_data[(k)+dim_1*((j)+dim2*(i))  ]
  a_data = (float *) data;
  dim_3 =  nslices           ;
  dim2  =  nprojs_span   ;
  dim_1 =  size1             ;

  sum_line = (float*) malloc( ncpus*size1*sizeof(float)  );
  liss_line = (float*) malloc( ncpus*size1*sizeof(float)  );
  // dist_from_corr  = (int *) malloc( ncpus*size1*sizeof(float)  );
  Coeff_Corr       = (float*) malloc( ncpus*size1*sizeof(float)  );
  Corr_Rayon      = (float*) malloc( dim_1*sizeof(float)  );

  Axe_Rot =  self->params.ROTATION_AXIS_POSITION ;   // will not take into account  axis_corrections

  LF = RING_FILTER_PARA->LF ;
  I_Slope =  RING_FILTER_PARA->I_Slope ;
  Lfen =  RING_FILTER_PARA->Lfen ;
  Eps1 =  RING_FILTER_PARA->Eps1 ;
  Eps2 =  RING_FILTER_PARA->Eps2 ;
  RProt   =  RING_FILTER_PARA->RProt ;

  sem_wait( &(self->savgol_sem));
  if(self->Coeff_fil==NULL) {
    self->Coeff_fil = (float *) malloc(LF*LF * sizeof(float )  ) ;
    SAVGOL(self->Coeff_fil,LF);
  }
  sem_post( &(self->savgol_sem));

  int Jlf, Lfens2 , Delta1; //, Delta2;
  Jlf = (LF-1)/2;
  Lfens2 = (Lfen-1)/2;
  Delta1 = Lfen-Lfens2-1;
  // Delta2 = Lfens2+1;
  {
    IRd = (int) (  Axe_Rot - RProt - I_Slope +0.499999  ) ;
    IRf = (int) (  Axe_Rot + RProt + I_Slope +0.499999  ) ;
    if(IRd<0) IRd=0;
    if(IRf>dim_1-1) IRf=dim_1-1;

    Pas = 1.0/I_Slope ;
    j = 0 ;
    for(i=0; i< dim_1; i++) {Corr_Rayon[i] = 1.0; };
    for(i=IRd ; i< IRd+I_Slope ; i++) {
      j = j+1;
      Corr_Rayon[i] = Corr_Rayon[i] - j*Pas ;
    }
    j = I_Slope;
    for(i=IRf-I_Slope+1 ; i<= IRf ; i++) {
      j = j-1;
      Corr_Rayon[i] = j*Pas;
    }
    for(i=IRd+I_Slope ; i< IRf-I_Slope+1 ; i++) {
      Corr_Rayon[i] = 0 ;
    }
  }

// #pragma	omp parallel for private(slice_count,tid, I_beg, I_end,i, proj_count,NL,k, Erreur, dist_from_corr,j) num_threads (ncpus)
  for(slice_count=0; slice_count< dim_3; slice_count++) {

    if(islicetracker ) {
      int doit=1;
      sem_wait( &(self->islicetracker_sem));
      if ( islicetracker[slice_count] ) {
  doit=0;
      }
      islicetracker[slice_count]=1;
      sem_post( &(self->islicetracker_sem) );
      if (doit==0)  continue;
    }

    tid= 0 ; // omp_get_thread_num();
    memset(sum_line+tid*dim_1 ,0,dim_1*sizeof(float) );
    for( proj_count=0 ; proj_count< dim2; proj_count++) {
      for(i=0; i< dim_1; i++) {
  sum_line[ i + tid*dim_1 ] += AA(  slice_count, proj_count, i) ;
      }
    }
    for(i=0; i< dim_1; i++) {
      sum_line[i + tid*dim_1 ] /= dim2 ;
    }
    for(I_beg = 0       ; I_beg < dim_1-1  && sum_line[I_beg + tid*dim_1 ] <= Eps1 ; I_beg++) ;
    for(I_end = dim_1-1 ; I_end > 0        && sum_line[I_end + tid*dim_1 ] <= Eps1 ; I_end--) ;
    for(i=0; i< dim_1; i++) {
      liss_line[i + tid*dim_1]=sum_line[i + tid*dim_1];
    }
    Erreur=0.0;
    for(i=I_beg; i<I_beg+Jlf; i++) {
      NL = i-I_beg;
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
      if( Erreur <fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ) Erreur = fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ;
    }
    NL = Jlf ;
    for(i=I_beg+Jlf; i<=I_end-Jlf; i++) {
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
      if( Erreur <fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ) Erreur = fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ;
    }
    for(i=I_end-Jlf+1; i<=I_end; i++) {
      NL = 2*Jlf -I_end +i;
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
      if( Erreur <fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ) Erreur = fabs(liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1]) ;
    }
    if(Erreur > Eps2 ) {
      {
  dist_from_corr = 100000 ;
  for(i=0; i<dim_1; i++) {
    Coeff_Corr[i + tid*dim_1]=2000000.0;
  }
  for(i=0; i<dim_1; i++) {
    if( fabs( liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1] ) <= Eps2 ) {
      dist_from_corr++;
    } else {
      dist_from_corr=0 ;
    }
    Coeff_Corr[i + tid*dim_1] = min( dist_from_corr,  Coeff_Corr[i + tid*dim_1]     ) ;
  }
  dist_from_corr = 100000 ;
  for(i=dim_1-1; i>=0; i--) {
    if( fabs( liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1] ) <= Eps2 ) {
      dist_from_corr++;
    } else {
      dist_from_corr=0 ;
    }
    Coeff_Corr[i + tid*dim_1] = min( dist_from_corr,  Coeff_Corr[i + tid*dim_1]     ) ;
  }
  for(i=0; i<dim_1; i++) {
    if( Coeff_Corr[i + tid*dim_1]  > I_Slope  ) {
      Coeff_Corr[i + tid*dim_1]=0;
    } else {
      Coeff_Corr[i + tid*dim_1]= (I_Slope+1 -Coeff_Corr[i + tid*dim_1])/(I_Slope+1)  ;
    }
  }
      }
    }
    if(Lfen==0 ) {
      for( proj_count=0 ; proj_count< dim2; proj_count++) {
  for(i=0; i< dim_1; i++) {
    AA(  slice_count, proj_count, i)  +=   Coeff_Corr[i + tid*dim_1]*
      (liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1])     ;  // Corr_Rayon[i + tid*dim_1]*
  }
      }

    } else {

      for( proj_count=0 ; proj_count< dim2; proj_count++) {
  for(i=0; i< dim_1; i++) {
    AA(  slice_count, proj_count, i)  +=   (1.0-Corr_Rayon[i + tid*dim_1])*
      (liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1])     ;
  }
      }

      memset(sum_line+tid*dim_1 ,0,dim_1*sizeof(float) );
      for( proj_count=0 ; proj_count< Lfen; proj_count++) {
  for(i=0; i< dim_1; i++) {
    sum_line[ i + tid*dim_1 ] += AA(  slice_count, proj_count, i) ;
  }
      }
      for(i=0; i< dim_1; i++) {
  sum_line[i + tid*dim_1 ] /= Lfen ;
      }

      for(proj_count=0; proj_count<dim2; proj_count++) {
  if(proj_count<=Lfens2) {
    // utilizza sum_line come e'
  } else   if(proj_count>=Lfens2+1 &&  proj_count<= dim2-Lfen+Lfens2) { // se Lfen non e' 0 Lfen-Lfen2 e' almeno 1
    for(i=0; i< dim_1; i++) {
      sum_line[ i + tid*dim_1 ] += (AA(slice_count,proj_count+Delta1 , i)-AA(slice_count,proj_count-Delta1 , i))/Lfen ;
    }
  } else {
    // utilizza sum_line come e'
  }
  {
    for(i=0; i< dim_1; i++) {
      liss_line[i + tid*dim_1]=sum_line[i + tid*dim_1];
    }
    for(i=I_beg; i<I_beg+Jlf; i++) {
      NL = i-I_beg;
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
    }
    NL = Jlf ;
    for(i=I_beg+Jlf; i<=I_end-Jlf; i++) {
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
    }
    for(i=I_end-Jlf+1; i<=I_end; i++) {
      NL = 2*Jlf -I_end +i;
      liss_line[i + tid*dim_1]= 0.0;
      for(k=0; k<LF; k++) liss_line[i + tid*dim_1] += self->Coeff_fil[NL*LF+k]*sum_line[ i-NL+k + tid*dim_1]   ;
    }
  }

  dist_from_corr = 100000 ;
  for(i=0; i<dim_1; i++) {
    Coeff_Corr[i + tid*dim_1]=2000000.0;
  }
  for(i=0; i<dim_1; i++) {
    if( fabs( liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1] ) <= Eps2 ) {
      dist_from_corr++;
    } else {
      dist_from_corr=0 ;
    }
    Coeff_Corr[i + tid*dim_1] = min( dist_from_corr,  Coeff_Corr[i + tid*dim_1]     ) ;
  }
  dist_from_corr = 100000 ;
  for(i=dim_1-1; i>=0; i--) {
    if( fabs( liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1] ) <= Eps2 ) {
      dist_from_corr++;
    } else {
      dist_from_corr=0 ;
    }
    Coeff_Corr[i + tid*dim_1] = min( dist_from_corr,  Coeff_Corr[i + tid*dim_1]     ) ;
  }
  for(i=0; i<dim_1; i++) {
    if( Coeff_Corr[i + tid*dim_1]  > I_Slope  ) {
      Coeff_Corr[i + tid*dim_1]=0;
    } else {
      Coeff_Corr[i + tid*dim_1]= (I_Slope+1 -Coeff_Corr[i + tid*dim_1])/(I_Slope+1)  ;
    }
  }
  for(i=0; i< dim_1; i++) {
    AA(  slice_count, proj_count, i)  +=   Coeff_Corr[i + tid*dim_1]*
      (liss_line[i + tid*dim_1] -sum_line[i + tid*dim_1])    *  Corr_Rayon[i + tid*dim_1] ;  // *
  }
      }
    }
  }
  free(sum_line);
  free(liss_line);
  free(Coeff_Corr);
  free(Corr_Rayon);
#undef AA
}



void SAVGOL(float *Coeff_filtre, int LF  ) {

  void LUBKSB(float *A, int N, int NP, int *INDX, float *B) ;
  void LUDCMP(float *A, int N, int NP, int *INDX);

  int LD,M,NL,NR, L,i ;
  // float C[1000] ;
#define NMAX 6
#define SHIFT_FORTRAN 1
#define AA(I,J) A[((I)-SHIFT_FORTRAN)*(NMAX+1)  +  ((J)-SHIFT_FORTRAN) ]
#define BB(I) B[(I) - SHIFT_FORTRAN ]

  int IMJ,IPJ,J,K,KK,MM,INDX[NMAX+1];
  float FAC,SOM,A[(NMAX+1)*(NMAX+1)],B[NMAX+1], kpowers[LF+1],Mkpowers[LF+1];
  // NP=NMAX+1;


  for(L=1; L<=LF; L++) {
    memset(B,0, (NMAX+1)*sizeof(float)             ); //B = 0.;
    memset(A,0, (NMAX+1)*(NMAX+1)*sizeof(float)    );// A = 0.;
    M = 4;
    LD = 0;
    NL = L-1;
    NR = LF-NL-1;
    // NP = NL + NR + 1; // LF
    for( i=0; i<=LF; i++) kpowers[i]=1;
    for( i=0; i<=LF; i++) Mkpowers[i]=1;
    for(IPJ=0; IPJ<=2*M; IPJ++) {
      SOM = 0;
      if(IPJ==0) SOM = 1;
      for(K=1;K<=NR; K++) {
  SOM = SOM + kpowers[K] ;
      }
      for( i=0; i<=LF; i++) kpowers[i]=kpowers[i]*i ;

      for(K=1;K<=NL; K++) {
  SOM = SOM + Mkpowers[K] ;
      }
      for( i=0; i<=LF; i++) Mkpowers[i]=-Mkpowers[i]*i ;

      MM = min(IPJ, 2*M-IPJ);
      for(IMJ=-MM; IPJ<=MM; IPJ+=2) {
  AA(   1+(IPJ+IMJ)/2     ,   1+(IPJ-IMJ)/2 )  = SOM;
      }
    }
    LUDCMP(A,M+1,NMAX+1,INDX);
    for( J=1; J<=M+1; J++ ) {
      BB(J)=0;
    }
    BB(LD+1) = 1;
    LUBKSB(A,M+1,NMAX+1,INDX,B);
    /* for(KK=1; KK<=NP; KK++) { */
    /*   C[KK]=0; */
    /* } */
    KK=0;
    for(K=-NL; K<=NR; K++ ) {
      SOM=BB(1);
      FAC = 1.0 ;
      for(MM=1; MM<=M; MM++) {
  FAC=FAC*K;
  SOM=SOM+BB(MM+1)*FAC;
      }
      KK=KK+1;
      Coeff_filtre[(L-1)*LF + (KK-1) ] = SOM;
    }
  }
}

#undef AA
#undef BB
void LUDCMP(float *A, int N, int NP, int *INDX) {
  printf("PROBLEM : LUDCMP AND LUBKSB HAVE BEEN REMOVED FROM DOMINIQUE BERNARD FILTER BECAUSE WERE COMING FROM NUMERICAL RECIPES book\n");
  printf("PROBLEM : IF YOU ARE INTERESTED IN THAT FILTER PLEASE PROVIDE THE SOURCES OF PYHST WITH YOUR OWN LU INVERSION\n\n");
  fprintf(stderr, "PROBLEM : LUDCMP AND LUBKSB HAVE BEEN REMOVED FROM DOMINIQUE BERNARD FILTER BECAUSE WERE COMING FROM NUMERICAL RECIPES book\n");
  fprintf(stderr, "PROBLEM : IF YOU ARE INTERESTED IN THAT FILTER PLEASE PROVIDE THE SOURCES OF PYHST WITH YOUR OWN LU INVERSION\n");
  exit(1);
}
 void LUBKSB(float *A, int N, int NP, int *INDX, float *B) {
  printf("PROBLEM : LUDCMP AND LUBKSB HAVE BEEN REMOVED FROM DOMINIQUE BERNARD FILTER BECAUSE WERE COMING FROM NUMERICAL RECIPES book\n");
  printf("PROBLEM : IF YOU ARE INTERESTED IN THAT FILTER PLEASE PROVIDE THE SOURCES OF PYHST WITH YOUR OWN LU INVERSION\n\n");
  fprintf(stderr, "PROBLEM : LUDCMP AND LUBKSB HAVE BEEN REMOVED FROM DOMINIQUE BERNARD FILTER BECAUSE WERE COMING FROM NUMERICAL RECIPES book\n");
  fprintf(stderr, "PROBLEM : IF YOU ARE INTERESTED IN THAT FILTER PLEASE PROVIDE THE SOURCES OF PYHST WITH YOUR OWN LU INVERSION\n");
  exit(1);
}
#undef BB
#undef AA
#undef IINDX
#undef NMAXVV
#undef TINY
#undef SHIFT_FORTRAN
#undef NMAX



void  C_HST_PROJECT_1OVER(int num_bins,        /* Number of bins in each sinogram  */
        int nprojs_span, /* Number of projections in  each sinogram */
        float * angles ,
        float  axis_position,
        float* SINOGRAMS ,
        float *SLICE,
        int   dimslice,
        float * axis_corrections,
        float cpu_offset_x,
        float cpu_offset_y
        )
{
  /* float * auxbuffer; */
  /*   int NblocchiPerLinea= iDivUp(*num_bins, 16 ); */
  /*   int NblocchiPerColonna =  iDivUp(*nprojs_span, 16 ) ;  */


   int dimrecx, dimrecy;
  /*   dimrecx = NblocchiPerLinea*16 ; */
  /*   dimrecy = NblocchiPerColonna*16 ;  */

  dimrecx = num_bins ;
  dimrecy = nprojs_span;

  // for(int iproj=0; iproj<dimrecy; iproj+=16) {
  int iproj ;

// #pragma	omp parallel for private(iproj) num_threads (4)
  for(iproj=0; iproj<dimrecy; iproj+=1) {

    int beginPos[2];
    int strideJoseph[2];
    int strideLine  [2];
    if(iproj && iproj%200==0) printf(" iproj 1over %d \n", iproj);

    float angle =  angles[iproj] ;
    float cos_angle = cos(angle);
    float sin_angle = sin(angle);

    // float anglei, cosi, sini;

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

    strideJoseph[0]=-1;
    strideJoseph[1]=0;
    strideLine  [0]=0;
    strideLine  [1]=-1;
  }
      }
    } else {
      int i;
      if( sin_angle>0) {
  for( i=0; i<16; i++) {
    beginPos[0]=dimslice-1;  // vertical
    beginPos[1]=0;  // rapida

    strideJoseph[0]=0;
    strideJoseph[1]=1;
    strideLine  [0]=-1;
    strideLine  [1]=0;

  }
      } else {
  for( i=0; i<16; i++) {
    beginPos[0]=0;  // vertical
    beginPos[1]=dimslice-1;  // rapida

    strideJoseph[0]=0;
    strideJoseph[1]=-1;
    strideLine  [0]=1;
    strideLine  [1]=0;
  }
      }
    }

    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 axis_corr = axis_position<  + axis_corrections[iproj  ];  */
    /* float axis      = axis_position ;  */
    float axis_corr = (dimslice-1)/2.0  + axis_corrections[iproj  ];
    float axis      = (dimslice-1)/2.0 ;

    float  Area;
    Area=1.0f/cos_angle;

    int ipix;
    for(ipix=0; ipix<num_bins ; ipix++) {
      float xpix = ipix-cpu_offset_x; //+1.5 ; // ( bidx*16+tidx ); // perche axis e' misurato da angolinocentro pixel
      float posx = (axis)*(1.0f-sin_angle/cos_angle ) +(xpix-(axis_corr) )/cos_angle ;

      float shiftJ = sin_angle/cos_angle;
      // float shiftL = 1.0f/cos_angle;

      float res=0.0;
      int j;
      if(1)
      for( j=0; j<dimslice; j++) {  // j come Joseph
  float y = beginPos    [0 ] +(posx + shiftJ*j )*strideLine[0] + (j)*strideJoseph[0] ;
  float x = beginPos    [1 ] +(posx + shiftJ*j )*strideLine[1] + (j)*strideJoseph[1];
  if( y >=0 && y < dimslice-1 &&
      x >=0 && x < dimslice-1
      ) {
    int ix, iy;
    y=y-0.49999;
    x=x-0.49999;
    FLOAT_TO_INT(y,iy);
    FLOAT_TO_INT(x,ix);
    y=y+0.49999;
    x=x+0.49999;
    float by= y-iy;
    float bx= x-ix;
    float ay= 1.0-by;
    float ax= 1.0-bx;

    res=res+
      SLICE[dimslice*(iy)+(ix)    ]*ay*ax +
      SLICE[dimslice*(iy+1)+(ix)  ]*by*ax +
      SLICE[dimslice*(iy)+(ix+1)  ]*ay*bx +
      SLICE[dimslice*(iy+1)+(ix+1)]*by*bx   ;
  }
      }
      SINOGRAMS[ dimrecx*( iproj ) +   (ipix  )  ] = Area*res*  M_PI *  0.5 /nprojs_span  ;
      // SINOGRAMS[ dimrecx*( iproj ) +   (ipix  )  ] =  SLICE[dimslice*( iproj ) +   (ipix  ) ]  ;
    }
  }
}


void fb_dl_driver(CCspace *self,
      float *data, int num_bins,  float *SLICEcalc,
      int do_precondition, int VECTORIALITY, float weight, int num_slice) {


  sem_wait( &(self->gpustat_sem));
  if(self->gpu_context ){
    /* if(self->fbp_precalculated.fai360) { */
    /*   printf(" ERROR : THE FB_DL ROUTINE HAS NOT YET HALFTOMO IMPLEMENTATION \n" ); */
    /*   exit(1); */
    /* } */
  } else {
    printf(" ERROR : THE FB_DL ROUTINE CAN BE USED ONLY WITH GPU \n" );
    exit(1);
  }

  int patch_w = 2*self->params.patch_ep +1;

  int iv;
  int blocksino    =  num_bins* self->params.nprojs_span  * patch_w;

  float *WORK = (float *) malloc(patch_w * VECTORIALITY* self->params.nprojs_span*num_bins*sizeof(float) ) ;

  for(iv=0; iv<VECTORIALITY; iv++) {
    int projection;
    for(projection=0; projection <   patch_w * self->params.nprojs_span       ; projection++) {

      memcpy(WORK +iv*blocksino + (projection)*num_bins ,data +iv*blocksino + (projection) * num_bins, num_bins * sizeof(float));
    }
  }

  //  int ivolta, nvolte;
  //nvolte=1+(self->fbp_precalculated.Lipschitz_fbdl<0);
  //for(ivolta=0; ivolta<nvolte; ivolta++)
    if (self->fbp_precalculated.Lipschitz_fbdl<0){
      self->gpu_context->num_slice =    num_slice ;
      self->gpu_context->fb_dl(self->gpu_context, WORK , SLICEcalc  , do_precondition ,
             self->params.DETECTOR_DUTY_RATIO,
             self->params.DETECTOR_DUTY_OVERSAMPLING,
             weight,
             self->params.patches,self->params.patches_N, self->params.patches_dim ,
             patch_w *VECTORIALITY,
             &(self->fbp_precalculated.Lipschitz_fbdl),
             proietta_drings);
    }
    self->gpu_context->num_slice =    num_slice ;
    self->gpu_context->fb_dl(self->gpu_context, WORK , SLICEcalc  , do_precondition ,
           self->params.DETECTOR_DUTY_RATIO,
           self->params.DETECTOR_DUTY_OVERSAMPLING,
           weight,
           self->params.patches,self->params.patches_N, self->params.patches_dim ,
           patch_w *VECTORIALITY,
           &(self->fbp_precalculated.Lipschitz_fbdl),
           proietta_drings);
    free(WORK);
    sem_post( &(self->gpustat_sem));
}



float rec_driver(CCspace *self,  float **WORK,float *WORKbis,float *SLICEcalc, int num_bins, int dim_fft,
     float * dumf, fcomplex *dumfC , float *WORK_perproje,float *OVERSAMPLE,
     int oversampling, float * data,int ncpus,float cpu_offset_x,float  cpu_offset_y, int do_precondition,
		 int is_tomolocal, float *fidelity, int npj_offset,  int do_2by2  )
{// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  //puts("entering rec_driver...");

  int VECTORIALITY = self->params.VECTORIALITY;
  int iv;
  int blocksino =  num_bins* self->params.nprojs_span;
  int blockslice=      self->params.num_y * self->params.num_x   ;
  float Sino_Sum = 0 ;
  
  for(iv=0; iv<VECTORIALITY; iv++) {
    int projection;
    // printf(" PADDING %c \n", self->params.PADDING[0]);
    // // #pragma	omp parallel for private(projection) reduction(+:Sino_Sum)  num_threads(ncpus)


    int currproj_ign = -1;
    if (self->params.do_ignore_projections) currproj_ign = 0;

    for(projection=0; projection <    (1+do_2by2)*self->params.nprojs_span       ; projection++) {
      // int tid=omp_get_thread_num();
      int  i;

      
      int ignore_this_projection = 0;
      if (currproj_ign != -1) {
	if( projection == self->params.nprojs_span) currproj_ign = 0 ; 
        if ( (projection% self->params.nprojs_span )  == self->params.ignore_angles[currproj_ign]) {   // dans la suite quand on voudra
	  // faire ignore aussi en elicoidal il faudra ajouter, dans la comparaison : +npj_offset a' projection
          ignore_this_projection = 1;
          currproj_ign++;
        }
      }

      if (ignore_this_projection) memset(WORK[projection], 0 , num_bins * sizeof(float));
      else memcpy(WORK[projection],data +iv*blocksino + (projection) * num_bins, num_bins * sizeof(float));
      for(i=0; i< (num_bins);i++) {
	Sino_Sum += WORK[projection][i];
      }
      if(self->fbp_precalculated.fai360)      {      // this means halftomo
	
	if(  self->params.PADDING[0]=='0' ) {
	  memset(WORK[projection]  + num_bins,0, ( dim_fft  - num_bins) * sizeof(float));
	}
	if( 2* self->params.ROTATION_AXIS_POSITION   >  num_bins ) {
	  if(  self->params.PADDING[0]!='0' ) {
	    memcpy(WORKbis,data +iv*blocksino + (0*self->params.nprojs_span +((projection+(( self->params.nprojs_span))/2)% ( self->params.nprojs_span)) )*
		   num_bins ,  num_bins * sizeof(float));
	    int kpad, ipoint ;
	    for ( kpad =  num_bins  ,ipoint= 2*self->params.ROTATION_AXIS_POSITION - num_bins ;
		  kpad <2*self->params.ROTATION_AXIS_POSITION -1      ;
		  kpad++, ipoint-- ) {
	      WORK[projection][kpad] =  WORKbis[ ipoint ]      ;
	    }
	    for ( ; kpad < ( +dim_fft + 2*self->params.ROTATION_AXIS_POSITION )/2;
		  kpad++) {
	      WORK[projection][kpad] =  WORKbis[ 0   ]      ;
	    }
	    for ( ;  kpad < dim_fft ;
		  kpad++ ) {
	      WORK[projection][kpad] =  WORK[projection][ 0   ]      ;
	    }
	  }
	} else {
	  if(  self->params.PADDING[0]!='0' ) {
	    memcpy(WORKbis ,data +iv*blocksino + (0*self->params.nprojs_span + ((projection+(( self->params.nprojs_span))/2)% ( self->params.nprojs_span)) )*
		   num_bins, num_bins * sizeof(float));
	    int kpad, ipoint ;
	    for ( kpad=  0 ,  ipoint= 2*self->params.ROTATION_AXIS_POSITION- kpad;
		  kpad >  2*self->params.ROTATION_AXIS_POSITION -    num_bins  +1 ;
		  kpad--, ipoint++ ) {
	      WORK[projection][ dim_fft -1 + kpad] =  WORKbis[ ipoint ]      ;
	    }
	    for ( ; kpad > ( -(dim_fft)+ (num_bins)+   2*self->params.ROTATION_AXIS_POSITION -   num_bins )/2   + 1 ;
		  kpad-- ) {
	      WORK[projection][(dim_fft)-1 + kpad] =  WORKbis[ num_bins -1  ]      ;
	    }
	    for ( ;  kpad > -(dim_fft) + (num_bins) ;
		  kpad-- ) {
	      WORK[projection][(dim_fft)-1 + kpad ] =  WORK[projection][  num_bins -1  ]      ;
	    }
	  }
	}
      } else {
	/* PADDING */
	if( self->params.PADDING[0]=='0' ) {
	  // printf(" padding 0 \n");
	  memset(WORK[projection] + (num_bins),0,
		 ((dim_fft) - (num_bins)) * sizeof(float));
	} else {
	  // printf(" padding E %s\n",self->params.PADDING );
	  int kpad;
	  for ( kpad = (num_bins)  ; kpad < ((num_bins)+(dim_fft))/2 ; kpad++ ) {
	    WORK[projection][kpad] = WORK[projection][(num_bins) -1];
	  }
	  for ( kpad = ((num_bins)+(dim_fft))/2 ; kpad <  (dim_fft) ; kpad++ ) {
	    WORK[projection][kpad] = WORK[projection][0];
	  }
	}
      }
    }

    float * fptr = NULL;
    if(do_2by2) {
      int nseg = self->params.nprojs_span * dim_fft ;
      fptr =(float*)malloc(  sizeof(float)*2*  nseg ) ;
      float *wptr = WORK[0];
      {
	int i=0;
	for( i=0; i< nseg; i++) {
	  fptr[2*i ]  = wptr[i     ];
	  fptr[2*i+1] = wptr[nseg+i];
	}
      }
    }
    // printf(" ASPETTO SEM GPU\n");
    
    int usa_gpu=0;
    sem_wait( &(self->gpustat_sem));
    if(self->gpu_context){
      if(self->gpu_context->inuse==0) {
	self->gpu_context->inuse=1;
	usa_gpu=1 ;
      }
      if(  self->params.ALLOWBOTHGPUCPU) sem_post( &(self->gpustat_sem));
    } else {
      printf(" the context was null\n" );
      sem_post( &(self->gpustat_sem));
    }
    if(usa_gpu) {
      // clock_t t1, t2;
      //  t1 = clock();
      if(self->params.FBFILTER==10) {
	self->gpu_context->dfi_gpu_main(self->gpu_context,  WORK[0] , SLICEcalc + iv*blockslice  ); //TODO : what is inside WORK[0]
      } else {
	//          if (fidelity) printf("Before gpu_main, fidelity = %f\n",*fidelity);

	if(do_2by2) {
	  // printf(" DO2BY2\n");
	  self->gpu_context->gpu_main_2by2(self->gpu_context,  fptr, SLICEcalc + iv*blockslice  , do_precondition ,
				      npj_offset, (do_precondition==2)  );
	  free(fptr);

	} else {
	  self->gpu_context->gpu_main(self->gpu_context,  WORK[0] , SLICEcalc + iv*blockslice  , do_precondition ,
				      self->params.DETECTOR_DUTY_RATIO,
				      self->params.DETECTOR_DUTY_OVERSAMPLING,
				      fidelity,
				      npj_offset, (do_precondition==2)  );
	}
	

	//          if (fidelity) printf("After gpu_main, fidelity = %f\n",*fidelity);
      }
      if(  self->params.ALLOWBOTHGPUCPU) sem_wait( &(self->gpustat_sem));
      self->gpu_context->inuse=0;
      sem_post( &(self->gpustat_sem));
      // t2 = clock();
      // printf( "GPUMAIN OK t=%f \n",  ( ((long)t2)-((long)t1)   )*1.0f/CLOCKS_PER_SEC);
    }else {
      if(self->params.FBFILTER==10) {
	fprintf(stderr,"DFI CAN BE USED ONLY WITH GPU\n");
	exit(1);
      }

      // riprendi qua il loop
      double accu=0;

      // #pragma	omp parallel for private(projection) reduction(+:Sino_Sum)  num_threads(ncpus)
      for (projection=0; projection < self->params.nprojs_span; projection++) {
        int npj_absolute = self->reading_infos.tot_proj_num_list[projection] + npj_offset ;
        if(npj_absolute<0 || npj_absolute >= self->reading_infos.numpjs ) {
          int k;
          for(k=0; k< 3*(num_bins)*oversampling ; k++) {
            WORK_perproje[(projection*3)*(num_bins)*oversampling+k]=NAN;
          }
          continue;
        }
        int  i,j;

        if(do_precondition==0 && fidelity!=NULL) {
          for(i=0; i<dim_fft; i++) {
            accu +=   WORK[projection][i]*WORK[projection][i] ;
          }
        }

        // int tid=omp_get_thread_num();
        if( (self->params.FBFILTER >=0 && do_precondition) || self->params.FBFILTER ==2 || self->params.SF_ITERATIONS) {
          for(i=0; i<dim_fft; i++) {
            dumf[i] =   WORK[projection][i] ;
          }
          fftwf_execute_dft_r2c( self->fbp_precalculated.planr2c , dumf , (fftwf_complex*) dumfC  );

          int filter_idx = 0;
          if(do_precondition) {
              for(i=0;i<=(dim_fft/2);i++) {
                filter_idx = i;
                // Sirt-Filter : angle dependent filtering
                if (self->params.SF_ITERATIONS) filter_idx = projection*dim_fft + i;
                dumfC[i] = dumfC[i] * self->fbp_precalculated.FILTER[filter_idx]/ dim_fft ;
                //  dumfC[i] = dumfC[tid][i]*(i*2.0/ dim_fft/dim_fft) ;
              }

            if(fidelity!=NULL) {
              float rr,ii ;
              rr =  crealf ( dumfC[0] ) ;  ii =  cimagf ( dumfC[0] ) ;
              accu += ((  rr*rr + ii*ii  ) * self->fbp_precalculated.FILTER[filter_idx - i])/2 ;
              for(i=1;i<=(dim_fft/2);i++) {
                rr =  crealf ( dumfC[i] ) ;ii =  cimagf ( dumfC[i] ) ;
                accu+= (  rr*rr + ii*ii  ) * self->fbp_precalculated.FILTER[filter_idx] ;
              }
            }
          } else {
            for(i=0;i<=(dim_fft/2);i++) {
              dumfC[i] = dumfC[i] /  dim_fft ;
            }
          }
          if(self->params.FBFILTER ==2) {
            for(i=1;i<=(dim_fft/2);i++) {
              dumfC[i] = ( ( +cimag(dumfC[i]) - _Complex_I  *  crealf(dumfC[i]) ))/(i*M_PI*2.0*2.0/dim_fft);
            }
            // 1.0/2/3.1415;  i*2.0/dim_fft
          }


          /* if(FILTER[3]<0) {  // integrazione sin->-cos   cos -> sin */
          /*   for(i=1;i<ARG(dim_fft)/2;i+=1) { */
          /*     dum=WORK[2*i]; */
          /*     WORK[2*i]  =  WORK[2*i+1] ; */
          /*     WORK[2*i+1]=  dum; */
          /*   } */
          /* } */
          /***************************************************
     * Inverse Fourier transform to give convolution   *
     ***************************************************/
          fftwf_execute_dft_c2r( self->fbp_precalculated.planc2r, (fftwf_complex*) dumfC   ,  dumf );
          for(i=0; i<dim_fft; i++) WORK[projection][i]=dumf[i];
        }

        float ax_pos_corr = self->fbp_precalculated.axis_position_corr_s[ npj_absolute];
        // printf("ax_pos_corr  %e  overlapping   %e  pente_zone   %e\n",ax_pos_corr , self->fbp_precalculated.overlapping, self->fbp_precalculated.pente_zone );
        if(self->fbp_precalculated.fai360) {
          float peso, dx ;

          for(i=0; i< (num_bins);i++) {
            /* if ( removelight360) { */
            /*   WORK[i] = -log( exp(-WORK[i]/normaliselight360) +correction360 )* normaliselight360;  */
            /* } */
            if(  fabs(i-  ax_pos_corr ) <= self->fbp_precalculated.overlapping ) {
              if(  fabs(i- ax_pos_corr ) <= self->fbp_precalculated.flat_zone) {
                WORK[projection][i]*=0.5;
              } else {
                if( i> ax_pos_corr  ) {
                  dx = ax_pos_corr  - i + self->fbp_precalculated.flat_zone;
                } else {
                  dx = ax_pos_corr - i - self->fbp_precalculated.flat_zone;
                }
                peso = self->fbp_precalculated.prof_shift +
                    self->fbp_precalculated.prof_fact * (0.5*(1.0+ (        dx      )/self->fbp_precalculated.pente_zone));
                WORK[projection][i] *= peso; // peso means weight
              }
            } else if (i>ax_pos_corr ) {
              WORK[projection][i] *= self->fbp_precalculated.prof_shift-0.0;
            } else {
              WORK[projection][i] *= self->fbp_precalculated.prof_shift + self->fbp_precalculated.prof_fact*1.0 ;
            }
            WORK[projection][i] *=2;
          }
        }
        {
          // float sumtidb =0.0;
          // float sumtid =0.0;
          WORK[projection][(num_bins)]=WORK[projection][(num_bins)-1];

          for(i=0; i<(num_bins); i++) {
            // sumtidb+=  WORK[projection][i] ;
            for(j=0; j<  oversampling; j++) {
              OVERSAMPLE[i*(oversampling) + j] =
                  (WORK[projection][i]*((oversampling)-j) + WORK[projection][i+1]*j) / ((oversampling));
              // sumtid+= OVERSAMPLE[tid][i*(oversampling) + j];
            }
          }
          // printf(" dopo sumtid %e %e tid %d  \n",sumtid,sumtidb  , tid);
        }
        memcpy( WORK_perproje+(projection*3+1)*(num_bins)*oversampling, OVERSAMPLE,   (num_bins)*oversampling * sizeof(float));
        // metti qua cpu_main e chiudi il loop che era stato riaperto
      } // end of loop on projections





      if(fidelity) *fidelity=2*accu;
      /* { */
      /* 	char nome[1000] ; */
      /* 	sprintf( nome, "sino_slice_new"  ); */
      /* 	FILE * output = fopen(nome,"w") ; */
      /* 	fwrite(WORK_perproje , sizeof(float) , self->params.nprojs_span*num_bins *3*oversampling, output ); */
      /* 	fclose(output); */
      /* } */
      printf( " CHIAMO CPU_MAIN \n");
      
      float dive=0.0;
      if(self->params.CONICITY_FAN )  {
        float  SOURCE_DISTANCE_pixels  =  self->params.SOURCE_DISTANCE *1.0e6/ self->params.IMAGE_PIXEL_SIZE_1  ;
        dive = 1.0/ SOURCE_DISTANCE_pixels;
      }

      cpu_main( self->params.num_y, self->params.num_x,      SLICEcalc + iv*blockslice , self->params.nprojs_span, num_bins ,
		  WORK_perproje , self->params.ROTATION_AXIS_POSITION , self->fbp_precalculated.axis_position_corr_s ,
		  self->fbp_precalculated.cos_s, self->fbp_precalculated.sin_s , cpu_offset_x, cpu_offset_y,
		  self->fbp_precalculated.minX, self->fbp_precalculated.maxX, oversampling, ncpus, dive,
		  npj_offset,self->reading_infos.tot_proj_num_list,  self->reading_infos.numpjs,
		  (self->params.DZPERPROJ!=0),(do_precondition==2),
		  self->fbp_precalculated.angles_per_proj // da usarsi con (self->params.DZPERPROJ!=0) &&(do_precondition==2)
		  );
     

      printf(" after  cpu_main\n");
    } // end of not(usa_gpu)


    if(do_2by2) {
      long int size =  blockslice ; 
      float *tmp = (float *)malloc( sizeof(float)*size*2   ) ;
      {
	long int i=0;
	for(i=0; i< size; i++) {
	  tmp[i] = SLICEcalc[2*i];
	  tmp[size+i] = SLICEcalc[2*i+1];
	}
      }
      memcpy(SLICEcalc,tmp,   sizeof(float)*size*2    ) ; 
      free(tmp);
    }

    
    // printf(" self->params.zerooffmask  est %d \n",self->params.zerooffmask   ) ;
    if(self->params.zerooffmask && ! is_tomolocal) {
      int x,y;
      int i2by2 = 0;
      for(i2by2=0; i2by2<(1+do_2by2); i2by2++) {
	for(y=0; y< self->params.num_y; y++) {
	  // printf( " %d %d %d \n", y,  self->fbp_precalculated.minX[y]   ,  self->fbp_precalculated.maxX[y]   ) ;
	  for(x=0; x<min(( self->fbp_precalculated.minX[y]),(self->params.num_x) ) ; x++) {
	    SLICEcalc[ (iv+i2by2)*blockslice + y*(self->params.num_x) +x]=0;
	  }
	  for(x=self->fbp_precalculated.maxX[y]; x<   self->params.num_x; x++) {
	    SLICEcalc[ (iv+i2by2)*blockslice + y*( self->params.num_x) +x]=0;
	  }
	}
      }
    }
    // printf(" mask applied \n");
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  }
  return Sino_Sum;
}

void  nlm_driver(CCspace *self,int dim0,int dim1,float  *img,float  *result , float bruit)  {
  sem_wait( &(self->gpustat_sem));
  if(self->gpu_context  &&  self->gpu_context->nonlocalmeans){
    if(self->gpu_context->inuse==0) {
      self->gpu_context->inuse=1;
    }
  } else {
    sem_post( &(self->gpustat_sem));
    printf (" ERROR: nonlocalmeans denoising works only with GPU but context not set\n" );
    fprintf(stderr, " ERROR:nonlocalmeans  denoising works only with GPU but context not set\n" );
    exit(1);
  }

  self->gpu_context->nonlocalmeans(self->gpu_context, img, result ,dim0, dim1,   bruit,0.85) ;
  self->gpu_context->inuse=0;
  printf("chiamo nonlocalmeans denoising  OK\n" );
  sem_post( &(self->gpustat_sem));
}


float  denoising_driver(CCspace *self,int dim0,int dim1,float  *img,float  *result , float weight )  {


  if(self->params.DENOISING_TYPE!=2) {
    sem_wait( &(self->gpustat_sem));
    if(self->gpu_context  &&  self->gpu_context->tv_denoising_fistagpu      ){
      if(self->gpu_context->inuse==0) {
	self->gpu_context->inuse=1;
      }
    } else {
      sem_post( &(self->gpustat_sem));
      
      printf (" ERROR: the selected denoising works only with GPU but context not set\n" );
      fprintf(stderr, " ERROR: denoising works only with GPU but context not set\n" );
      exit(1);
    }
  }
  
  
  if(self->params.VECTORIALITY==2 && (self->params.DENOISING_TYPE!=2  ) ) {
    printf("ERROR : VECTORIALITY==2 CANNOT BE USED WITH denoising other than patches_l1   (type=2) \n");
    fprintf(stderr, "ERROR : VECTORIALITY==2 CANNOT BE USED WITH denoising other than patches_l1   (type=2) \n");
    exit(1);
  }
  
  
  float res=0.0f;
  if(self->params.DENOISING_TYPE==1) {
    // float eps = 0.5e-6 ;
    float eps = self->params.DUAL_GAP_STOP ;
    // int n_iter_max = 500 ;
    int n_iter_max = self->params.N_ITERS_DENOISING ;
    int check_gap_frequency = 3 ;
    res = self->gpu_context->tv_denoising_fistagpu(self->gpu_context, dim0, dim1,  img, result ,  weight,
               eps ,  n_iter_max,    check_gap_frequency ) ;


  } else   if(self->params.DENOISING_TYPE==2) {


    int i;
    float tmp;
    for(i=0; i< dim0*dim1; i++) {
      tmp = fabs(result[i])-weight ;
      if(tmp<0) tmp=0;
      result[i]= copysignf( tmp, result[i]);
    }


    /* res = self->gpu_context->tv_denoising_patches_L1(self->gpu_context, dim0, dim1,  img, result ,  weight, */
    /* 						     self->params.patches,self->params.patches_N, self->params.patches_dim , */
    /* 						     self->params.N_ITERS_DENOISING, */
    /* 						     self->params.VECTORIALITY) ; */



  } else   if(self->params.DENOISING_TYPE==3) {


    int last=0;
    if(weight<0) {
      last=1;
      weight=-weight;
    }
    //printf(" chiamo denoising con beta %e self->params.patches_dim %d\n", weight,self->params.patches_dim);


    float *resulttmp = (float *) malloc(  dim0* dim1*sizeof(float)  );
    memset(resulttmp,0,dim0* dim1*sizeof(float) );

    float ntp  =  floor( weight);
    float molla = fmodf ( weight, 1.0);
    molla *=self->fbp_precalculated.prec_gamma ;

    res = self->gpu_context->tv_denoising_patches_OMP(self->gpu_context, dim0, dim1,  img, resulttmp ,  ntp,
                      self->params.patches,self->params.patches_N, self->params.patches_dim  ) ;

    if(last) {
      molla=1;
    }
    {
      int i;
      for (i=0; i< dim0*dim1; i++) {
  result[i] = result[i]*(1-molla)+molla*resulttmp[i];
      }
    }

   free( resulttmp);

  }

  if(self->params.DENOISING_TYPE!=2) {
    self->gpu_context->inuse=0;
    sem_post( &(self->gpustat_sem));
  }

  // printf("chiamo denoising  OK\n" );
  return res;
}



void pro_driver(
     CCspace * self,int num_bins, float *angles_per_proj,
     float * SINOGRAMMA, float*  SLICE, int dimslice, float cpu_offset_x, float cpu_offset_y) {

   int VECTORIALITY =  self->params.VECTORIALITY;
   int iv;
   int blocksino =  num_bins* self->params.nprojs_span;
   int blockslice=      self->params.num_y * self->params.num_x   ;
   for(iv=0; iv<VECTORIALITY; iv++) {
     int usa_gpu=0;
     sem_wait( &(self->gpustat_sem));
     if(self->gpu_context  &&  self->gpu_context->gpu_project){
       if(self->gpu_context->inuse==0) {
	 self->gpu_context->inuse=1;
	 usa_gpu=1 ;
       }
       if(  self->params.ALLOWBOTHGPUCPU) sem_post( &(self->gpustat_sem));
     } else {
       printf(" the context was null\n" );
       sem_post( &(self->gpustat_sem));
     }
     if( usa_gpu ) {
       //sem_wait( &(self->gpustat_sem));
       // printf("chiamo project  \n" );
       int memisonhost=1;
       
       float dive=0.0;
       if(self->params.CONICITY_FAN )  {
	 float  SOURCE_DISTANCE_pixels  =  self->params.SOURCE_DISTANCE *1.0e6/ self->params.IMAGE_PIXEL_SIZE_1  ;
	 dive = 1.0/ SOURCE_DISTANCE_pixels;
       }
       
       self->gpu_context->gpu_project(self->gpu_context->gpuctx ,
				      num_bins,
				      self->params.nprojs_span,
				      angles_per_proj ,
				      self->params.ROTATION_AXIS_POSITION,
				      SINOGRAMMA +iv*blocksino,
				      SLICE + iv*blockslice ,
				      dimslice,
				      self->axis_corrections,
				      cpu_offset_x,
				      cpu_offset_y,
				      self->params.JOSEPHNOCLIP,
				      self->params.DETECTOR_DUTY_RATIO,
				      self->params.DETECTOR_DUTY_OVERSAMPLING,
				      memisonhost,
				      dive,
				      self->params.SOURCE_X
				      );
       
       if(  self->params.ALLOWBOTHGPUCPU) sem_wait( &(self->gpustat_sem));
       self->gpu_context->inuse=0;
       // printf("chiamo project  OK\n" );
       sem_post( &(self->gpustat_sem));
     } else {
       C_HST_PROJECT_1OVER(num_bins,
			   self->params.nprojs_span,
			   angles_per_proj ,
			   self->params.ROTATION_AXIS_POSITION,
			   SINOGRAMMA +iv*blocksino,
			   SLICE + iv*blockslice  ,
			   dimslice,
			   self->axis_corrections,
			   cpu_offset_x,
			   cpu_offset_y
         );
     }
   }
}

void correct_slice(CCspace *self,float *SLICE,float *SLICEcalc )
{
  int i;
  for(i=0; i<self->params.num_x * self->params.num_y ; i++) {
    SLICE[i]=0.9*SLICE[i]+SLICEcalc[i]*0.1;
  }
}

void prepare_correction(CCspace *self, float * data, float * data_orig, float *SINOGRAMMA,  int num_bins) {
  float *data_projected =  SINOGRAMMA ;

    int i;
    printf(" PREPARO LA CORREZIONE DA SMEARING \n");
    for(i=0; i< self->params.nprojs_span*num_bins; i++ ) {
      data[i]= +data_projected[i]*  self->params.BETA_TV*0.5;
    }
}

#define equalsS(A,i,B   )  memcpy( (A)+(i)*dimslice*dimslice, B, dimslice*dimslice*sizeof(float)  )

float scalar( float *A ,float *B,  int dim) {
  double res;
  res=0.0;
  int i;
  for(i=0; i<dim; i++) {
    res=res+A[i]*B[i];
  }
  return (float)  res;
}

void vect_minus_fact_vect(float *A ,float s, float *B,  int dim) {
  int i;
  for(i=0; i<dim; i++) {
    A[i]-=s*B[i];
  }
}

 void   scalmult(float *x, float s , int dim ) {
  int i;
  for(i=0; i<dim; i++) {
    x[i]*=s;
  }
}
 
#define SWAP(x, y) { int temp = x; x = y; y = temp; }

int partition(float *a, int left, int right, int pivotIndex)
{
  int pivot = a[pivotIndex];
  SWAP(a[pivotIndex], a[right]);
  int pIndex = left;
  int i;
  for (i = left; i < right; i++)
    {
      if (a[i] <= pivot)
	{
	  SWAP(a[i], a[pIndex]);
	  pIndex++;
	}
    }
  SWAP(a[pIndex], a[right]);
  return pIndex;
}

float  quickselect(float * A, int left, int right, int k)
{
  while(1) {
    if (left == right) {
      return A[left];
    }
    // int pivotIndex = left + rand() % (right - left + 1);
    int pivotIndex = left +  (right - left + 1)/2;
    pivotIndex = partition(A, left, right, pivotIndex);
    if (k == pivotIndex) {
      return A[k];
    } else if (k < pivotIndex) {
      right = pivotIndex - 1 ; 
    } else {
      left =  pivotIndex + 1 ;
    }
  }
}

float * medianX(  float *A   , int  dim3 , int  dim2 , int  dim1   , int nproc ) {
  float *res = (float *) malloc(sizeof(float) *dim3*dim2*dim1);
  assert(dim1>1);
  int i;
#pragma omp parallel for  num_threads (nproc)
  for (  i=0; i<dim3*dim2; i++) {
    res[i] = quickselect(A+i*dim1 , 0, dim1 -(dim1%2), (dim1 -(dim1%2) -1 )/2);
  }
  return res;


}




      

/* void passeggiaSLICE(CCspace *self, float **WORK, float **WORKbis, int  num_bins,  int dim_fft,  */
/* 	       float **dumf, fcomplex **dumfC , float * WORK_perproje, float **OVERSAMPLE, */
/* 	       int oversampling, int  ncpus   , float cpu_offset_x, float cpu_offset_y, */
/* 	       float *angles_per_proj,  int dimslice, */
/* 		    float *SLICE, float *data, float * SINOGRAMMA, int nsteps) { */
/*   int  istep; */
/*   int N = nsteps+1; */
/*   float *x = (float *) malloc(  dimslice*dimslice*sizeof(float) ) ;  */
/*   float *xnew = (float *) malloc(  dimslice*dimslice*sizeof(float) ) ;  */
/*   int i,j,k ;  */
/*   float *vects=(float *) malloc( N * dimslice*dimslice*sizeof(float) ) ;  */
/*   float *H = (float *) malloc( N*N *sizeof(float) ) ;  */
/*   float norm2,scal; */
/*   memcpy( x, SLICE,  dimslice*dimslice*sizeof(float)   ); */
/*   norm2=scalar(x,x,  dimslice*dimslice); */
/*   scalmult(x,1.0f/sqrt(norm2), dimslice*dimslice  ); */
/*   equalsS(vects,0,x); */
/*   for(istep=0; istep<nsteps; istep++) { */
/*     pro_driver(self, num_bins,angles_per_proj, */
/* 	       SINOGRAMMA, x,  dimslice,  cpu_offset_x,  cpu_offset_y); */
/*     rec_driver(self,  WORK,WORKbis,    xnew     ,  num_bins,  dim_fft,  */
/* 	       dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling, */
/* 	       SINOGRAMMA, ncpus   , cpu_offset_x, cpu_offset_y   ,1 ,0, NULL ); */
/*     for(i=0; i<dimslice*dimslice; i++) { */
/*       xnew[i]=x[i]-xnew[i]; */
/*     } */
/*     for(k=0;k<istep+1;k++) { */
/*       H[k*N+istep]=scalar( vects +k*dimslice*dimslice   ,xnew,  dimslice*dimslice) ; */
/*     } */
/*     printf(" PRIMO GRAM \n"); */
/*     for(k=0;k<istep+1;k++) { */
/*       printf(" %e ", H[k*N+istep]); */
/*       vect_minus_fact_vect( xnew, H[k*N+istep] ,vects +k*dimslice*dimslice   ,dimslice*dimslice) ; */
/*     } */
/*     printf("\n SECONDO GRAM \n"); */
/*     for(k=0;k<istep+1;k++) { */
/*       scal = scalar( vects +k*dimslice*dimslice   ,xnew,  dimslice*dimslice) ;  */
/*       printf(" %e ", scal); */
/*       vect_minus_fact_vect( xnew,scal ,vects +k*dimslice*dimslice   ,dimslice*dimslice) ; */
/*     } */
/*     memcpy( x, xnew ,  dimslice*dimslice*sizeof(float)   ); */
/*     norm2=scalar(x,x,  dimslice*dimslice); */
/*     H[(istep+1)*N+istep]     =  sqrt(norm2)    ; */

/*     printf("RESIDUO %e\n", sqrt(norm2)); */

/*     scalmult(x,1.0f/sqrt(norm2), dimslice*dimslice  ); */
/*     equalsS(vects,istep+1,x); */


/*     for(i=0; i<istep+1; i++) { */
/*       for(j=0; j<istep+1; j++) { */
/* 	printf(" %e ", H[i*N+j]); */
/*       } */
/*       printf("\n"); */
/*     } */
/*   } */

/*   FILE * output=fopen("Hslice.txt","w"); */
/*   for(i=0; i<N; i++) { */
/*     for(j=0; j<N; j++) { */
/*       fprintf(output," %e ", H[i*N+j]); */
/*     } */
/*     fprintf(output,"\n"); */
/*   } */
/*   fclose(output); */
/* } */
/* #define equals(A,i,B   )  memcpy( (A)+(i)*numproj *num_bins, B,  numproj*num_bins*sizeof(float)  )  */

/* void passeggiaSINO(CCspace *self, float **WORK, float **WORKbis, int  num_bins,  int dim_fft,  */
/* 		   float **dumf, fcomplex **dumfC , float * WORK_perproje, float **OVERSAMPLE, */
/* 		   int oversampling, int  ncpus   , float cpu_offset_x, float cpu_offset_y, */
/* 		   float *angles_per_proj,  int dimslice, */
/* 		   float *SLICE, float *data, float * SINOGRAMMA, int solution, int nsteps) { */
/*   int numproj= self->params.nprojs_span;  */
/*   int  istep; */
/*   int N = nsteps+1; */
/*   float *x = (float *) malloc(  numproj*num_bins*sizeof(float) ) ;  */
/*   float *xnew = (float *) malloc(  numproj*num_bins*sizeof(float) ) ;  */
/*   int i,j,k ;  */
/*   float *vects=(float *) malloc( N * numproj*num_bins*sizeof(float) ) ;  */
/*   float *H = (float *) malloc( N*N *sizeof(float) ) ;  */
/*   float norm2,scal; */
/*   float *Solutions=NULL;  */

/*   printf(" nsteps  %d  \n", nsteps ); */
/*   if(solution) { */
/*     pro_driver(self, num_bins,angles_per_proj, */
/* 	       x, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y); */
/*     Solutions=(float *) malloc( N *dimslice *dimslice*sizeof(float) ) ;  */
/*   } else { */
/*     memcpy( x, data,  numproj*num_bins*sizeof(float)   ); */
/*   } */
/*   norm2=scalar(x,x,  numproj*num_bins); */
/*   printf("NORMA INIZIALE %e\n", norm2); */
/*   scalmult(x,1.0f/sqrt(norm2), numproj*num_bins  ); */


/*   equals(vects,0,x); */
/*   if(solution)  { */
/*     equalsS(Solutions,0,SLICE); */
/*     scalmult(Solutions ,1.0f/sqrt(norm2),dimslice*dimslice  ); */
/*   } */

/*   for(istep=0; istep<nsteps; istep++) { */

/*     rec_driver(self,  WORK,WORKbis,    SLICE     ,  num_bins,  dim_fft,  */
/* 	       dumf, dumfC , WORK_perproje,OVERSAMPLE, oversampling, */
/* 	       x, ncpus   , cpu_offset_x, cpu_offset_y    ,1,0, NULL ); */

/*     printf(" rec OK \n"); */

/*     pro_driver(self, num_bins,angles_per_proj, */
/* 	       xnew, SLICE,  dimslice,  cpu_offset_x,  cpu_offset_y); */
/*     printf(" pro OK \n"); */

/*     if(solution)  { */
/*     } else { */
/*       for(i=0; i<numproj*num_bins; i++) { */
/* 	xnew[i]=x[i]-xnew[i]; */
/*       } */
/*     } */

/*     for(k=0;k<istep+1;k++) { */
/*       H[k*N+istep]=scalar( vects +k*numproj*num_bins   ,xnew,  numproj*num_bins) ; */
/*     } */


/*     printf(" PRIMO GRAM \n"); */
/*     for(k=0;k<istep+1;k++) { */
/*       printf(" %e ", H[k*N+istep]); */
/*       vect_minus_fact_vect( xnew, H[k*N+istep] ,vects +k*numproj*num_bins   ,numproj*num_bins) ; */
/*       if(solution) vect_minus_fact_vect( SLICE , H[k*N+istep] ,Solutions +k*dimslice*dimslice  ,dimslice*dimslice ) ; */
/*     } */
/*     printf("\n SECONDO GRAM \n"); */
/*     for(k=0;k<istep+1;k++) { */
/*       scal = scalar( vects +k*numproj*num_bins   ,xnew,  numproj*num_bins) ;  */
/*       printf(" %e ", scal); */
/*       vect_minus_fact_vect( xnew,scal ,vects +k*numproj*num_bins   ,numproj*num_bins) ; */
/*       if(solution) vect_minus_fact_vect( SLICE , scal ,Solutions +k*dimslice*dimslice  ,dimslice*dimslice ) ; */
/*    } */
/*     printf("\n"); */
/*     memcpy( x, xnew ,  numproj*num_bins*sizeof(float)   ); */
/*     norm2=scalar(x,x,  numproj*num_bins); */
/*     H[(istep+1)*N+istep]     =  sqrt(norm2)    ; */
/*     if(solution)  { */
/*       scalmult(SLICE ,1.0f/sqrt(norm2),dimslice*dimslice  ); */
/*       equalsS(Solutions,istep+1,SLICE); */
/*       printf("NORM2 %e\n", norm2); */
/*     } */
/*     scalmult(x,1.0f/sqrt(norm2), numproj*num_bins  ); */
/*     equals(vects,istep+1,x); */


/*     for(i=0; i<istep+1; i++) { */
/*       for(j=0; j<istep+1; j++) { */
/* 	printf(" %e ", H[i*N+j]); */
/*       } */
/*       printf("\n"); */
/*     } */
/*   } */

/*   FILE * output=fopen("Hsino_.txt","w"); */
/*   for(i=0; i<N; i++) { */
/*     for(j=0; j<N; j++) { */
/*       fprintf(output," %e ", H[i*N+j]); */
/*     } */
/*     fprintf(output,"\n"); */
/*   } */
/*   printf("SOLUTION \n"); */
/*   if(solution) { */
/*     memset(SLICE,0, sizeof(float)*dimslice*dimslice); */
/*     for(i=0; i<N; i++) { */
/*       float scal; */
/*       fprintf(output," %d %e\n",i, scalar( Solutions +i*dimslice*dimslice   , Solutions +i*dimslice*dimslice,  dimslice*dimslice)  ); */
/*       scal=scalar(data, vects+i*numproj*num_bins, numproj*num_bins); */
/*       vect_minus_fact_vect( SLICE , -scal ,Solutions +i*dimslice*dimslice  ,dimslice*dimslice ) ; */
/*     } */
/*   } */
/*   fclose(output); */
/*   free(x); */
/*   free(xnew); */
/*   free(vects); */
/*   free(H); */
/*   if (Solutions) free(Solutions); */

/* } */
