
#include<iostream>


#include <stdio.h>
#include <stdlib.h>
#include<math.h>
#include<string>
#include <cuda.h>
#include <fcntl.h>
#include<string.h>
#include<Qt/qimagereader.h>
#include<Qt/qimage.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>

#include"unsharp3D.h"


#include<tiffio.h>
// #include <emmintrin.h> //SSE header.



void writeEdf( char * outname, int islice , float *results , int width, int height );
  

void writetiff( char * outname, int islice , float *results , int width, int height ) ;
  


void tiffRead(char * nomefile, float * data, int n1, int s1, int n2, int s2) {
  QImageReader reader(nomefile)   ;                                   
  QImage img = reader.read();
  if( !img.isNull() )	 //Load succceded ?
    {
      reader.format();
      // Should be "TIFF"
      reader.text("description");

      if(n1+s1> img.width()) {
	printf (" n1 %d  s1 %d  w  %d\n", n1, s1, img.width());
	throw  ( (char * ) "n1+s1> img.width() " ); 
      }
      if(n2+s2> img.height()) {
	throw ((char * ) "n2+s2< img.height() ") ; 
      }

      int w = img.width(); 
      int h = img.height(); 


      FILE *f=fopen(  nomefile  , "r");

      fseek(f ,   8 , SEEK_CUR );
      
      unsigned short  *buffer;
      buffer = new unsigned short[w*h];

      fread( buffer,2, w*h    , f);

      fclose(f);

     
      for (int i1=0 ; i1< s1 ; i1++ ) {
	for (int i2=0 ; i2< s2 ; i2++ ) {
	  data[i1+i2*s1] = *(  (buffer+( ( n2+i2 )*w +  +n1+i1 ) )            ) ; 
	}
      }

      delete buffer;

//       ptr=(char*) img.bits();
//       for (int i1=0 ; i1< s1 ; i1++ ) {
// 	for (int i2=0 ; i2< s2 ; i2++ ) {
// 	  data[i1+i2*s1] = *( (unsigned short *) (ptr+( ( n2+i2 )*w +  +n1+i1 )*4) ) ; 
// 	}
//       }

    } else {
    char messaggio[1000];
    sprintf(messaggio, "Unable to open tiff file %s ", nomefile);
    throw messaggio;
  }  
}




texture<float, 3, cudaReadModeElementType> tex3d_VolumeChunck;

__constant__ float gfacts[256];
__constant__ float gfactsD2[256];


#define CUDA_SAFE_CALL_EXCEPTION(call) {do {	\
       cudaError err = call;    \
       if( cudaSuccess != err) {   \
	   sprintf(messaggio, "Cuda error in file %s in line %i : %s \n",  __FILE__, __LINE__, \
		   cudaGetErrorString( err) ); \
           fprintf(stderr, messaggio , " ");       \
	   goto fail;			    \
       } } while (0); }

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




void extended_fread(   char *ptr,      /* memory to write in */
                       int   size_of_block,
                       int ndims        ,
                       int *dims      ,
                       int  *strides    ,
                       FILE *stream  ) {
  int pos;
  int oldpos;
  int  count;

  int indexes[ndims];
  int i;
  int loop;
  int res;

  oldpos=0;
  pos=0;
  count = 0;
  /*
  printf("received\n");
    printf("block = %d\n",size_of_block);
    printf("ndims = %d\n",ndims);
    printf("dims = %d %d\n",dims[0],dims[1]);
    printf("strides = %d\n",strides[0]);

*/
  for(i=0; i<ndims; i++) {
    indexes[i]=0;
  }
  loop=ndims-1;
  indexes[ndims-1 ]=-1  ;
  pos=-strides[ndims-1];

  while(1) {
    if(indexes[loop]< dims[loop]-1 ) {
      indexes[loop]++;
      pos += strides[loop];
      for( i=loop+1; i<ndims; i++)  {
        pos -= indexes[i]*strides[i];
        indexes[i]=0;
      }
      res=fseek( stream, (pos-oldpos),  SEEK_CUR );
      if(res!=0) {
    /*    throw ErrorExtendedFread_fseek_failed();*/
    printf("Error 1/n");
        break;
      }

      res=fread (  ( (char * ) ptr) +(count)*size_of_block ,
                   size_of_block, 1,  stream );
      if(res!=1) {
    /*    throw ErrorExtendedFread_fseek_failed();*/
    printf("Error 2/n");
        break;
      }
      count++;

      oldpos = pos+ size_of_block;

      loop=ndims-1;
      /*      for(i=0 ; i< ndims; i++) {
              printf(" %d ", indexes[i] );
              }
              printf("\n");
      */
    }else {
      loop--;
    }
    if(loop==-1) {
      break;
    }
  }
}




int getMeta(char * nomeinfo, char * tag ) {
  char messaggio[1000];
  FILE *f=NULL;
  char  *linea=NULL;
  int isset=0;
  int result=0;
  f=fopen(nomeinfo, "r");
  if( !f ) {
    sprintf(messaggio,"was not able to open file %s  ", nomeinfo);
    goto fail;
  }


  while(!feof(f)) {
    size_t len=0; 
    linea=NULL;
    int ires; 
    ires= getline(&linea, &len, f );

    if(ires==-1) break;
    if( strstr(tag, linea) ) {
      const char *ptr;
      ptr =  strstr("=", linea);
      if(!ptr) {
	sprintf(messaggio,"was not able to find = in line  : %s  ",linea );
	goto fail;
      }
      int nres;
      nres=sscanf(ptr+1, "%d", &result );
      if(nres!=1) {
	sprintf(messaggio,"was not able to find an integer after = in line :  %s  : ",linea );
	goto fail;
      }
      isset=1;
      break;
    }
    if(linea) free(linea);
  }
  if(!isset) {
    sprintf(messaggio,"was not able to find tag  %s  ", tag);
    goto fail;
  }
  fclose(f);
  return result;

 fail:
  if(f) fclose(f);
  if(linea) free(linea);
  throw(messaggio);
}


int getMetaEndian(char * nomeinfo ) {
  char messaggio[1000];
  FILE *f=NULL;
  char  *linea=NULL;
  int isset=0;
  int result=0;

  f=fopen(nomeinfo, "r");
  if( !f ) {
    sprintf(messaggio,"was not able to open file %s  ", nomeinfo);
    goto fail;
  }

  while(!feof(f)) {
    size_t len=0; 
    linea=NULL;
    int ires; 
    ires= getline(&linea, &len, f );
    if(ires==-1) break;
    if( strstr("BYTEORDER", linea) ) {
      const char *ptr;
      ptr =  strstr("=", linea);
      if(!ptr) {
	sprintf(messaggio,"was not able to find = in line  : %s  ",linea );
	goto fail;
      }
      if( strstr(   "LOW"     , ptr+1 )) {
	result=1;
      } else{
	result=0;
      }
      isset=1;
    }
    if(linea) free(linea);
  }
  if(!isset) {
    sprintf(messaggio,"was not able to find tag  %s  ","BYTEORDER" );
    goto fail;
  }
  fclose(f);
  return result;

 fail:
  if(f) fclose(f);
  if(linea) free(linea);
  throw(messaggio);
}




__global__ static void	  cudaunsharp_kernel( int Ww1, int Ww2, int Ww3, 
					      float *d_result,size_t pitch,
					      /* int Rw1,int  Rw2,int  Rw3, */
					      int marge,
					      float PUC,
					      int offx, int offy, int offz,
					      int dimgridY) {
  float res=0.0f, dum;
  float factx, facty, factz;
  int ix0 =   blockIdx.x * blockDim.x  +  threadIdx.x + offx ;

  int  bidy = (blockIdx.y %  dimgridY);
  int  bidz = (blockIdx.y /  dimgridY);


  int iy0 =  bidy  * blockDim.y  +  threadIdx.y + offy;
  int iz0 =  bidz  * blockDim.z  +  threadIdx.z + offz;

  for(int imx = -marge; imx<=marge; imx++) {
    factx =gfacts[abs(imx)]; 
    for(int imy = -marge; imy<=marge; imy++) {
      facty =gfacts[abs(imy)]*factx; 
      for(int imz = -marge; imz<=marge; imz++) {
	factz =gfacts[abs(imz)]*facty *( gfactsD2[abs(imz)]+  
					 gfactsD2[abs(imy)]+
					 gfactsD2[abs(imx)]
					 );
	res=res+factz* tex3D(tex3d_VolumeChunck, imx+ix0 ,imy+iy0 , imz+iz0   );
      }
    }
  }

  if( (ix0 - offx)<Ww1  && (iy0 - offy  )<Ww2 && ( iz0- offz  )<Ww3 ) {
    dum =  tex3D(tex3d_VolumeChunck, ix0 ,iy0 , iz0   ) ; 
    // d_result[ ix0 - offx + (iy0 - offy  +(iz0-offz)*Ww2)*(pitch/4)] =     -PUC*res+(1+PUC)*dum  ; 
    d_result[ ix0 - offx + (iy0 - offy  +(iz0-offz)*Ww2)*(pitch/4)] =     PUC*res+ dum  ; 
  }
}



__global__ static void	  cudaunsharp_kernel_noZ( int Ww1, int Ww2, int Ww3, 
					      float *d_result,size_t pitch,
					      /* int Rw1,int  Rw2,int  Rw3, */
					      int marge,
					      float PUC,
					      int offx, int offy, int offz,
					      int dimgridY) {
  float res=0.0f, dum;
  float factx, facty;
  int ix0 =   blockIdx.x * blockDim.x  +  threadIdx.x + offx ;

  int  bidy = (blockIdx.y %  dimgridY);
  int  bidz = (blockIdx.y /  dimgridY);


  int iy0 =  bidy  * blockDim.y  +  threadIdx.y + offy;
  int iz0 =  bidz  * blockDim.z  +  threadIdx.z + offz;

  for(int imx = -marge; imx<=marge; imx++) {
    factx =gfacts[abs(imx)]; 
    for(int imy = -marge; imy<=marge; imy++) {
      facty =gfacts[abs(imy)]*factx *( gfactsD2[abs(imy)]+
				       gfactsD2[abs(imx)]
				       );	
      res=res+facty* tex3D(tex3d_VolumeChunck, imx+ix0 ,imy+iy0 , iz0   );
    }
  } 
  
  if( (ix0 - offx)<Ww1  && (iy0 - offy  )<Ww2 && ( iz0- offz  )<Ww3 ) {
    dum =  tex3D(tex3d_VolumeChunck, ix0 ,iy0 , iz0   ) ; 
    d_result[ ix0 - offx + (iy0 - offy  +(iz0-offz)*Ww2)*(pitch/4)] =     PUC*res+ dum  ; 
  }
}

int mainunsharp(Unsharp_Para pars ) {
  try {
  int n3,n2,n1;	
  int s3,s2,s1;
  char messaggio[10000];
  char nominfo[10000];

  FILE *filinfo ;



  int NUM_X, NUM_Y, NUM_Z;
  // volume dimensions
  {
    NUM_X = pars.V3D_VOLDIMS[0];
    NUM_Y = pars.V3D_VOLDIMS[1] ;
    NUM_Z = pars.V3D_VOLDIMS[2] ;

    n1 = pars.V3D_ROICORNER[0];
    n2 = pars.V3D_ROICORNER[1] ;
    n3 = pars.V3D_ROICORNER[2] ;

    s1 = pars.V3D_ROISIZE[0];
    s2 = pars.V3D_ROISIZE[1] ;
    s3 = pars.V3D_ROISIZE[2] ;
  }

  int LOWBYTEFIRST_vol;
  int LOWBYTEFIRST;

  {
    
    union test {
      signed char car[2];
      unsigned short ush;
    };     union test tlow, thigh;
    tlow.car[0]=1;
    tlow.car[1]=0;  
    thigh.car[1]=1;
    thigh.car[0]=0;
    
    if(thigh.ush>tlow.ush) {
      LOWBYTEFIRST=1;
    } else {
      LOWBYTEFIRST=0;
    }
    LOWBYTEFIRST_vol=LOWBYTEFIRST;
  }


  int convZ;	
 
  convZ=  pars.V3D_CONVZ; 

  int cardtouse = pars.V3D_GPUNUM; 

  int voloutput= (pars.V3D_TIFFOUTPUT ==0) ; 

  int tifoutput= pars.V3D_TIFFOUTPUT ; 

  int tiffread = pars.V3D_TIFFREAD;

  // selecting the card
  int device;
  {


 //    char *gpunum_chr =   getenv ("GPUNUM");
//     if(!gpunum_chr) {
//       sprintf(messaggio, "You must provide an environment variable GPUNUM set to the gpu card ID ");
//       throw(messaggio);
//     }



//     int  nres;
//     nres=sscanf(gpunum_chr, "%d", &device);

    device=cardtouse ; 

//     if(nres!=1) {
//       sprintf(messaggio, " Was not able to read cgpu card id from string : %s",gpunum_chr );
//       throw(messaggio);
//     }

    cudaError_t cudaresult;   	
    cudaresult = cudaSetDevice(device);
    if( cudaresult!=cudaSuccess) {
      sprintf(messaggio, " Was not able to select gpu device # : %d",device);
      throw(messaggio);
    }


  }

  long int totalMem ; 
  // the total amount of global memory available on the device in bytes;
  {
    cudaDeviceProp deviceProp;
    cudaGetDeviceProperties(&deviceProp, device);      
    totalMem = deviceProp.totalGlobalMem ;
  }


  
  FILE *outvol; 
  if(voloutput==1) {
    outvol= fopen(pars.V3D_OUTPUTFILENAMEPATTERN, "w");
  }

  tex3d_VolumeChunck.addressMode[0] = cudaAddressModeClamp;
  tex3d_VolumeChunck.addressMode[1] = cudaAddressModeClamp;
  tex3d_VolumeChunck.addressMode[2] = cudaAddressModeClamp;
  tex3d_VolumeChunck.filterMode = cudaFilterModePoint;
  tex3d_VolumeChunck.normalized = false;

  float PUS=pars.V3D_PUS;
  float PUC=pars.V3D_PUC;


  if(PUS*PUS>1.0 ) {
    PUC = PUC *   PUS*PUS  ;
  }
  PUS = PUS/2; 
 
  int marge= (1+3*PUS);
  int maxdim=2048 ; 



  long int memslice = 1.1*2048*2048*sizeof(float) ;      
  int max_Z_dim = min((int) ((totalMem/4/1.1)/memslice), maxdim) ;
  int lim_wrt_zl=0, lim_wrt_zh ; 
  for(lim_wrt_zl=n3; lim_wrt_zl< n3+s3 ;    ) {
    int lim_read_zl ;
    lim_read_zl = max( 0, lim_wrt_zl-marge) ; 
    int lim_read_zh ;
    lim_read_zh =    min(min( lim_read_zl + max_Z_dim, n3+s3+marge),NUM_Z ) ; 

    if(lim_read_zh<NUM_Z) {
      lim_wrt_zh  =  lim_read_zh-marge  ; 
    }else {
      lim_wrt_zh  =  max(  n3+s3, lim_read_zh-marge) ; 
    }

    int Ww3 = lim_wrt_zh - lim_wrt_zl; 


    int maxtotres = s1*s2 *   Ww3 ;
    float * totresult = new float [  s1*s2 *   Ww3       ] ; 
    if(!totresult) {
      sprintf(messaggio, " was not able to allocate partial totresults array" );
      throw messaggio;
    }

    if(lim_wrt_zh< lim_wrt_zl) {
      sprintf(messaggio, " Not enough memory on the GPU to proceed with these dimensions and margin");
      throw(messaggio);
    }
    if( lim_read_zh  > NUM_Z ) {
      lim_read_zh =NUM_Z; 
    }

    int lim_wrt_yl=0, lim_wrt_yh ;     


    printf( "ZZZZ  scrivero da %d    %d   \n",  lim_wrt_zl ,lim_wrt_zh    );

    for(lim_wrt_yl=n2; lim_wrt_yl< n2+s2 ;    ) {
      int lim_read_yl ;
      lim_read_yl = max( 0, lim_wrt_yl-marge) ; 
      int lim_read_yh ;
      lim_read_yh =    min(min( lim_read_yl + maxdim, n2+s2+marge), NUM_Y)  ; 

 
      if(lim_read_yh<NUM_Y) {
	lim_wrt_yh  =  lim_read_yh-marge  ; 
      }else {
	lim_wrt_yh  =  max(  n2+s2, lim_read_yh-marge) ; 
      }

      if( lim_read_yh  > NUM_Y ) {
	lim_read_yh =NUM_Y; 
      }
           
      int Ww2 = lim_wrt_yh - lim_wrt_yl; 
     
      int lim_wrt_xl=0, lim_wrt_xh ; 


      printf( "YYYY  scrivero da %d    %d    \n",  lim_wrt_yl ,lim_wrt_yh    );

      for(lim_wrt_xl=n1; lim_wrt_xl< n1+s1 ;    ) {
	int lim_read_xl ;
	lim_read_xl = max( 0, lim_wrt_xl-marge) ; 
	int lim_read_xh ;
	lim_read_xh =    min(min( lim_read_xl + maxdim , n1+s1+marge),NUM_X )  ; 
	printf(" leggo da %d a %d  n1 %d s1 %d  marge %d \n", lim_read_xl, lim_read_xh, n1,s1,marge   );

	if(lim_read_xh<NUM_X) {
	  lim_wrt_xh  =  lim_read_xh-marge  ; 
	}else {
	  lim_wrt_xh  =  max(  n1+s1, lim_read_xh-marge) ; 
	}
	
	printf( "XX  scrivero da %d    %d    \n",  lim_wrt_xl ,lim_wrt_xh    );

	int Ww1 = lim_wrt_xh - lim_wrt_xl; 


	

	cudaChannelFormatDesc Volume_channel ; 
	Volume_channel = cudaCreateChannelDesc(32, 0, 0, 0,cudaChannelFormatKindFloat);
	
	cudaExtent vol_extent;

	int Rw1, Rw2, Rw3;
	Rw1 = lim_read_xh - lim_read_xl; 
	Rw2 = lim_read_yh - lim_read_yl; 
	Rw3 = lim_read_zh - lim_read_zl; 
	vol_extent  =make_cudaExtent(Rw1  ,Rw2 , Rw3);
	cudaArray *VolumeChunck_3darray;
	cudaMalloc3DArray(  &VolumeChunck_3darray  ,&Volume_channel, vol_extent   );
	if(cudaError_t err=cudaGetLastError())printf("cudaMalloc3DArray Volume error: %s\n",cudaGetErrorString(err));
	
	cudaMemcpy3DParms copyParams = {0};
	float *h_source =  new float [ Rw1*Rw2*Rw3  ] ; //                   volume_data  + ( n1+ NUM_X(n2+NUM_Y*n3) )  ;
	
	if(!tiffread) {
	  FILE *fvol=fopen(pars.V3D_FILENAMEPATTERN, "r");
	  int dims[]={Rw3, Rw2, Rw1   }, strides[]={ NUM_Y*NUM_X *4  , NUM_X*4, 1*4}; 
	  long int offset=   lim_read_xl +   ( NUM_X*(lim_read_yl+ (long int) NUM_Y* lim_read_zl)  )*4;
	  fseek( fvol, offset ,SEEK_SET);
	  extended_fread(   (char*) h_source , sizeof(float) , 3, dims, strides, fvol);

	  fclose(fvol);
	} else {
	  for(int iz=lim_read_zl; iz<lim_read_zl+Rw3; iz++) {
	    char nomefile[10000];
	    sprintf(nomefile,pars.V3D_FILENAMEPATTERN, iz+1);
	    printf(" lim_wrt_xl,lim_wrt_xh, lim_wrt_yl,lim_wrt_yh,  lim_wrt_zl,lim_wrt_zh, %d %d %d %d %d %d    " ,
		  lim_wrt_xl,lim_wrt_xh, lim_wrt_yl,lim_wrt_yh,  lim_wrt_zl,lim_wrt_zh );

	    printf(" reading tiff file /%s/  offset = %d \n", nomefile, (iz-lim_read_zl)* Rw1*Rw2 );
	    
	    tiffRead(nomefile,h_source+(iz-lim_read_zl)* Rw1*Rw2,
		     lim_read_xl , Rw1 , lim_read_yl   ,  Rw2);

	  }
	}
	
#define SWAP_2(x) ( (((x) & 0xff) << 8) | ((unsigned short)(x) >> 8) )
	if(LOWBYTEFIRST_vol!=LOWBYTEFIRST) {
	  unsigned short *ptr;
	  for(int i=0; i<Rw3* Rw2* Rw1 ; i++) {
	    ptr=(unsigned short* )(&(h_source[i]));
	    h_source[i] = SWAP_2(  *ptr  )     ; 
	  }
	}
	

	copyParams.srcPtr = make_cudaPitchedPtr((void*)h_source,  Rw1*sizeof(float), Rw1 ,Rw2 );
	
	copyParams.dstArray =   VolumeChunck_3darray ;
	
	copyParams.kind = cudaMemcpyHostToDevice;
	
	copyParams.extent = vol_extent ;
	
	if(cudaError_t err=cudaGetLastError())printf("copy3DHostToArray0 error: %s\n",cudaGetErrorString(err));
	cudaMemcpy3D(&copyParams);
	if(cudaError_t err=cudaGetLastError())printf("copy3DHostToArray error: %s\n",cudaGetErrorString(err));

	delete h_source;
		
	cudaBindTextureToArray( tex3d_VolumeChunck , VolumeChunck_3darray  ,Volume_channel);
	if(cudaError_t err=cudaGetLastError())printf("Binds(tex3d_Volumebis) error: %s\n",cudaGetErrorString(err));
       
	
	float * d_result=NULL ; 
	size_t  pitch    ; 

	CUDA_SAFE_CALL_EXCEPTION(cudaMallocPitch((void**)&d_result, &pitch,  sizeof(float) *Ww1   , Ww2*Ww3 ));
	//  chiama kernel
	dim3  dimBlock(  16   ,   4, 4 );

	int dimgridX = (int) (Ww1*1.0/dimBlock.x+ 0.9999999 );
	int dimgridY = (int) (Ww2*1.0/dimBlock.y+ 0.9999999 );
	int dimgridZ = (int) (Ww3*1.0/dimBlock.z+ 0.9999999 );

	dim3  dimGrid ( dimgridX,dimgridY*dimgridZ );




	int off1, off2, off3;

	off1 = lim_wrt_xl-   lim_read_xl;
	off2 = lim_wrt_yl-   lim_read_yl;
	off3 = lim_wrt_zl-   lim_read_zl;

	float gdata[1+marge];
	float gdataD2[1+marge];
        {
	  float sum=0.0;
	  float sumD2=0.0;
	  for(int i=0; i<=marge; i++) {
	    gdata[i]= exp( -(i*i/PUS/PUS)*0.5);
	    gdataD2[i] =( 2*exp( -(i*i/PUS/PUS)*0.5)- 
			  exp( -((i+1)*(i+1)/PUS/PUS)*0.5)- 
			  exp( -((i-1)*(i-1)/PUS/PUS)*0.5)  
			)/(gdata[i]);
	    
 
	    if(i==0) {
	      sum=sum+gdata[i];
	      sumD2+= gdataD2[i]*gdata[i]; 
	    } else {
	      sum=sum+2*gdata[i];	      
	      sumD2+= 2*gdataD2[i]*gdata[i]; 
	    }
	  }
	  printf("sumD2 = %e \n", sumD2);
	  for(int i=0; i<=marge; i++) {
	    gdataD2[i]-= (sumD2/(1+2*marge))/gdata[i] ; 
	    gdata[i]=gdata[i]/sum;

	  }
	}
	if(marge+1>256) {
	  sprintf(messaggio, " required margin is too big" );
	  throw messaggio;
	}




	cudaMemcpyToSymbol(gfacts, gdata, (marge+1)*sizeof(float));
	cudaMemcpyToSymbol(gfactsD2, gdataD2, (marge+1)*sizeof(float));
	

	try{

	  
	  if(convZ==1) {
	    cudaunsharp_kernel<<<dimGrid,dimBlock>>> (Ww1,Ww2,Ww3, d_result, pitch, 
						      marge, PUC,
						      off1, off2, off3, dimgridY);
	  } else if(convZ==0) {
	    cudaunsharp_kernel_noZ<<<dimGrid,dimBlock>>> (Ww1,Ww2,Ww3, d_result, pitch, 
							  marge, PUC,
							  off1, off2, off3, dimgridY);
	    
	  } else {
	    sprintf(messaggio, " convZ input parameter not 1 nor 0" );
	    throw messaggio;
	  }
	  printf( " chiamo kernel OK 	\n");
	} catch (...) {
	  printf(" throwing unknown exception after  cudaunsharp_kernel \n");
	  goto fail; 
	}


	cudaUnbindTexture(tex3d_VolumeChunck);

	cudaFreeArray(VolumeChunck_3darray );

	float * result = new float [   Ww1*Ww2*Ww3     ] ; 
	if(!result) {
	  sprintf(messaggio, " was not able to allocate partial results array" );
	  throw messaggio;
	}
	
	CUDA_SAFE_CALL_EXCEPTION( cudaMemcpy2D(  result, Ww1*sizeof(float) ,d_result ,  pitch,
						 Ww1*sizeof(float)  ,  Ww2*Ww3  ,cudaMemcpyDeviceToHost ); );	

	CUDA_SAFE_CALL_EXCEPTION(cudaFree(d_result));



	for (int i3=0; i3<Ww3; i3++ ) {
	  for (int i2=0; i2<Ww2; i2++ ) {
	    for (int i1=0; i1<Ww1; i1++ ) {
	      if(maxtotres<=  ( (i3 )*s2 +   (lim_wrt_yl-n2 +i2))*s1+(lim_wrt_xl-n1+i1) ) {
		printf("depassamento   n1+s1 %d   lim_wrt_xl +i1 %d  \n", 
		       n1+s1  , lim_wrt_xl +i1      );
		printf(" n2+s2 %d   lim_wrt_yl +i2 %d  \n", 
		       n2+s2  , lim_wrt_yl +i2      );



	      }	      
	    totresult[( (i3 )*s2 +   (lim_wrt_yl-n2 +i2))*s1+(lim_wrt_xl-n1+i1)]  =  
	      result[  (i3*Ww2+i2)*Ww1 +  i1 ]   ;      
	    }
	  }
	}
	printf("delete results \n");
	delete result;	 

	
	
	printf("indice 2 fino a %d massimo %d  \n",  Ww2 + lim_wrt_yl-n2 , s2     );
	printf("indice 1 fino a %d massimo %d  \n",  Ww1 + lim_wrt_xl-n1 , s1     );
	

	lim_wrt_xl  = lim_wrt_xh ; 
      }
      lim_wrt_yl  = lim_wrt_yh ; 
    }

    
    if(voloutput==1) {
      fwrite(  totresult,  sizeof(float),  s1*s2*Ww3, outvol);
    }



    if(tifoutput==2) {
      for(int islice=0; islice<Ww3; islice++ ) {
	printf("writing edf slice %d \n", islice );
	writeEdf( pars.V3D_OUTPUTFILENAMEPATTERN ,  lim_wrt_zl  +islice , totresult + s1*s2*islice, s2,s1 );
      }
    } else  if(tifoutput==1) {
      for(int islice=0; islice<Ww3; islice++ ) {
	printf("writing tiff slice %d \n", islice );
	writetiff(pars.V3D_OUTPUTFILENAMEPATTERN ,  lim_wrt_zl  +islice , totresult + s1*s2*islice, s1,s2 ); 
      }
    }


    delete totresult; 

    lim_wrt_zl  = lim_wrt_zh ; 
  }
  
  if(voloutput) {
    fclose(outvol);
  


    sprintf(nominfo,"%s.info", pars.V3D_OUTPUTFILENAMEPATTERN  );
    filinfo = fopen(nominfo, "w");
  
    if(LOWBYTEFIRST) {
      fprintf(filinfo, "NUM_X = %d \nNUM_Y = %d \nNUM_Z = %d \n BYTEORDER = LOWBYTEFIRST", s1,s2,s3);
      fclose(filinfo);


      sprintf(nominfo,"%s.xml", pars.V3D_OUTPUTFILENAMEPATTERN  );
      filinfo = fopen(nominfo, "w");

      fprintf(filinfo, "  <tomodb2>"
	      " <reconstruction>                                \n"   
	      "     <idAc>N_A_</idAc>				\n"
	      "     <listSubVolume>				  \n"
	      "     <subVolume>				 \n "
	      "     <SUBVOLUME_NAME>%s </SUBVOLUME_NAME>	 \n "
	      "     <SIZEX>%d</SIZEX>			 \n "
	      "     <SIZEY>%d</SIZEY>			  \n"
	      "     <SIZEZ>%d</SIZEZ>				\n  "
	      "     <ORIGINX>1</ORIGINX>			 \n "
	      "     <ORIGINY>1</ORIGINY>			 \n "
	      "     <ORIGINZ>1</ORIGINZ>			\n  "
	      "     <DIM_REC> %ld </DIM_REC>		 \n "
	      "     <BYTE_ORDER>LOWBYTEFIRST</BYTE_ORDER>	 \n "
	      "     </subVolume>				  "
	      "     </listSubVolume>				  \n"
	      "     </reconstruction>				  \n"
	      "     </tomodb2>                                  \n" , pars.V3D_OUTPUTFILENAMEPATTERN , s1,s2,s3, s1*s2*( (long int) s3));
    
      fclose(filinfo);
    } else {
      fprintf(filinfo, "NUM_X = %d \nNUM_Y = %d \nNUM_Z = %d \n BYTEORDER = HIGHBYTEFIRST", s1,s2,s3); 
      fclose(filinfo);

      sprintf(nominfo,"%s.xml", pars.V3D_OUTPUTFILENAMEPATTERN   );
      filinfo = fopen(nominfo, "w");

      fprintf(filinfo, "  <tomodb2>"
	      " <reconstruction>                                \n"   
	      "     <idAc>N_A_</idAc>				  \n"
	      "     <listSubVolume>				  \n"
	      "     <subVolume>				  \n"
	      "     <SUBVOLUME_NAME>%s </SUBVOLUME_NAME>	  \n"
	      "     <SIZEX>%d</SIZEX>			  \n"
	      "     <SIZEY>%d</SIZEY>			  \n"
	      "     <SIZEZ>%d</SIZEZ>			  \n"
	      "     <ORIGINX>1</ORIGINX>			  \n"
	      "     <ORIGINY>1</ORIGINY>			  \n"
	      "     <ORIGINZ>1</ORIGINZ>			  \n"
	      "     <DIM_REC>%ld</DIM_REC>		  \n"
	      "     <BYTE_ORDER>HIGHBYTEFIRST</BYTE_ORDER>	  \n"
	      "     </subVolume>				 \n"
	      "     </listSubVolume>				 \n"
	      "     </reconstruction>				 \n"
	      "     </tomodb2>                                  \n" , pars.V3D_OUTPUTFILENAMEPATTERN , s1,s2,s3 ,s1*s2*((long int)s3) );
      fclose(filinfo);
    }
  }
  

  return 1; 
  
  fail:
  throw(messaggio);
  } catch( char * msg) {
    printf(" error :  %s \n", msg);
    exit(1);
  }
}



void writetiff( char * outname, int islice , float *results , int width, int height ) {
  
  
 
  char nome[10000];
  sprintf(nome, "%s_%05d.tif", outname, islice);
  printf("   WRITING on tiff file %s \n" , nome);
  TIFF *image;
  if((image = TIFFOpen(nome, "w")) == NULL){
    char messaggio[1000];
    sprintf(messaggio, "Unable to open tiff file %s for output ", nome);
    throw messaggio;
  }
#define sd2int(in_d)\
  _mm_cvtss_si32(_mm_load_ss(&(in_d))) 

  unsigned short  * buffer;
  buffer = new unsigned short [ width*height ] ;
  for(int i=0; i<width*height; i++) {
    if( results[i] > 0xffff ) results[i] =  0xffff ; 
    if((results[i]) >0) {
      buffer[i] = (unsigned short)  (results[i]) ; 
    } else {
      buffer[i] =0;
    }
  }
  TIFFSetField(image, TIFFTAG_YRESOLUTION, 150.0);
  TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
  TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 16);
  TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
  TIFFSetField(image, TIFFTAG_ROWSPERSTRIP,height );
  TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
  TIFFSetField(image, TIFFTAG_IMAGELENGTH,height );
  TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
  TIFFSetField(image, TIFFTAG_XRESOLUTION, 150.0);
  TIFFWriteEncodedStrip(image, 0, buffer,width*height*2 );
  TIFFClose(image);
  delete buffer;

}
void writeEdf( char * outname, int islice , float *results , int width, int height ) {
  
  char messaggio[1000];

  char nome[10000];
  sprintf(nome, "%s_%05d.edf", outname, islice);
  
  printf("   WRITING on edf file %s \n" , nome);
  
  
  
  
  FILE *image;

  if((image = fopen(nome, "w")) == NULL){
    sprintf(messaggio, "Unable to open edf file %s for output ", nome);
    throw messaggio;
  }

  sprintf(messaggio , 
"{                                    \n"     
" HeaderID = EH:000001:000000:000000 ;\n"        
" Image = 1 ;                         \n"     
" ByteOrder = LowByteFirst ;          \n"          
" DataType = FloatValue ;             \n"     
" Dim_1 = %d  ;                       \n"      
" Dim_2 = %d  ;                       \n"      
" Size =  %d  ;                       \n"          
" Title = newroi 1 Net ;              \n"      
,height,   width ,  height*width*4  );	                       
  
  fwrite(messaggio,1, strlen(messaggio) , image);
  
  
  int pos = ftell(image);
  
  for(int i=pos; i<1024-2; i++) fwrite(" ",1,1,image);
  fwrite("}\n",1,2,image);
  fwrite( results  ,4, height*width , image);
  fclose(image);
  
  
}
