
#/*##########################################################################
# 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.
#############################################################################*/
// Author :  Alessandro Mirone ESRF 2009

// #include<iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
// #include<string.h>

#include <cuda.h>
#include <fcntl.h>

#include<unistd.h>
// #include<sys/sem.h>
// #include<sys/ipc.h>
#include<sys/types.h>
#include<errno.h>

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


texture<float, 2, cudaReadModeElementType> texData;

using namespace std;


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

#  define CUDA_SAFE_CALL( call)     CUDA_SAFE_CALL_NO_SYNC(call);

#define CUDA_SAFE_CALL_EXCEPTION  CUDA_SAFE_CALL

// #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\n", \
//                    __FILE__, __LINE__, cudaGetErrorString( err) );     \
//            fprintf(stderr, messaggio );     \
// 	   goto fail;			    \
//        } } while (0); }

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



#define DODEBUG 0

#define DEBUG( call  ) if(DODEBUG) call ;




union PackedShorts
{
  struct __align__(8) 
  {
    short Y;
    short X;
  };
  int hInt;
  float fvalue;
};


	
// // this routine is not used. it is work in progress for a future version
// // which will treat arbitrary size filters
// __global__ static void    cudamedianfilterBIG_kernel(int ny,int nx,int  hwy, int hwx,
// 						     float * d_result,  size_t pitch ,
// 						     float * bufferA, float * bufferB,
// 						     int shared_size_goal, float threshold) {
//   __shared__   int  coords[ 2048 ] ; 
//   const int shared_size =2048;
  
//   PackedShorts sh0, sh1;
  
//   const  short bidy = blockIdx.y;
//   const  short bidx = blockIdx.x;
  
//   const  short tidy = threadIdx.y;
//   const  short tidx = threadIdx.x;
  
//   int  cornerY, cornerX;
//   // float *bufferTmp; 

//   int DimY, DimX, XY;
//   DimX=blockDim.x;
//   DimY=blockDim.y;
//   XY=DimX*DimY;



//   cornerY = DimY*bidy ;
//   cornerX = DimX*bidx ;

//   cornerY = cornerY - hwy ; 
//   cornerX = cornerX - hwx ; 
  
//   short wX;
//   // wY = 16+ 2*hwy;
//   wX = DimX+ 2*hwx;
  
//   unsigned  short start ; 
  
//   int volte=shared_size_goal/shared_size;


  
//   int  posinshared ; 
//   start = tidy*DimX+tidx ;                                                                       
//   for(int volta=0; volta<volte; volta++) {
//     for( posinshared=start; posinshared< shared_size; posinshared+=XY) {
//       sh0.Y = cornerY + posinshared/wX ;
//       sh0.X = cornerX + posinshared%wX ;
//       coords[posinshared]=sh0.hInt ; 
//     }
    
//     __syncthreads();
//     int tmp;
//     // Parallel bitonic sort
//     if(1)
//       for ( unsigned short  k = 2; k <=shared_size; k *= 2)
// 	{
// 	  // Bitonic merge:
// 	  for ( unsigned short  j = k / 2; j>0; j /= 2)
// 	    {
// 	      start = tidy*DimX+tidx ;                                                                       
	      
// 	      for( ; start <shared_size; start +=XY) {
// 		unsigned short  ixj = start ^ j;
// 		sh0.hInt = coords[start];
// 		sh1.hInt = coords[ixj  ];
		
// 		if (ixj > start)
// 		  {
// 		    if ((start & k) == 0)
// 		      {
// 			if (     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) >    tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //         shared[tid] > shared[ixj]      )
// 			  {
// 			    tmp= coords[start];
// 			    coords[start]= coords[ixj];
// 			    coords[ixj]=tmp;
// 			  }
// 		      }
// 		    else
// 		      {
// 			if(     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) < tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //  (shared[tid] < shared[ixj])
// 			  {
// 			    tmp= coords[start];
// 			    coords[start]= coords[ixj];
// 			    coords[ixj]=tmp;
// 			  }
// 		      }
// 		  }
// 		syncthreads();
// 	      }
// 	    }
// 	}
    
//     cornerY = DimY*bidy +tidy;
//     cornerX = DimX*bidx +tidx;
//     short count=0;
//     short N =   ( (1+2*hwy)*(1+2*hwx))/2;
    
//     int ly,lx;
//     int hy,hx;
    
//     ly =  cornerY-hwy ;
//     lx =  cornerX-hwx ;
//     hy = cornerY+hwy;
//     hx = cornerX+hwx ;
    
//     for(int  k=0; k< shared_size; k++) {
      
//       sh0 .hInt = coords[k];
      
//       if( ((ly <=sh0.Y)   && (sh0.Y<=hy)  &&
// 	   (lx<=sh0.X)   && (sh0.X<= hx))  ) {
	
// 	if( count==N) {  sh1.hInt= sh0 .hInt;  }; 
// 	count=count+1;
	
//       } else {
	
//       }
      
//     }
//     start = tidy*DimX+tidx ;                                                                       
    
//     for( ; start <shared_size; start +=XY) {
//       sh0.hInt = coords[start];
//       *(bufferA +  volta*shared_size+start)= sh0.fvalue ; 
//     }
//   }

//  return; 
// }




template<int shared_size>
__global__ static void    cudamedianfilter_kernel(int ny,int nx,int  hwy, int hwx,
						  float * d_result,  size_t pitch, float threshold ) {

  __shared__   int  coords[ shared_size ] ; 



  PackedShorts sh0, sh1;

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

  float original, substitution;
 
  int DimY, DimX, XY;
  DimX=blockDim.x;
  DimY=blockDim.y;
  XY=DimX*DimY;

 
  int  cornerY, cornerX;

  cornerY = DimY*bidy ;
  cornerX = DimX*bidx ;

  cornerY = cornerY - hwy ; 
  cornerX = cornerX - hwx ; 

   short wX;
  // wY = 16+ 2*hwy;
  wX = DimX+ 2*hwx;
  
  unsigned  short start ; 

  


  int  posinshared ; 
  start = tidy*DimX+tidx ;                                                                       
  for( posinshared=start; posinshared< shared_size; posinshared+=XY) {
    sh0.Y = cornerY + posinshared/wX ;
    sh0.X = cornerX + posinshared%wX ;
    coords[posinshared]=sh0.hInt ; 
  }

  __syncthreads();
  int tmp;
  // Parallel bitonic sort
  if(1)
   for ( unsigned short  k = 2; k <=shared_size; k *= 2)
    {
      // Bitonic merge:
      for ( unsigned short  j = k / 2; j>0; j /= 2)
        {
	  start = tidy*DimX+tidx ;                                                                       

	  for( ; start <shared_size; start +=XY) {
	    unsigned short  ixj = start ^ j;
	    sh0.hInt = coords[start];
	    sh1.hInt = coords[ixj  ];
	    
	    if (ixj > start)
	      {
		if ((start & k) == 0)
		  {
		    if (     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) >    tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //         shared[tid] > shared[ixj]      )
		      {
			tmp= coords[start];
			coords[start]= coords[ixj];
			coords[ixj]=tmp;
		      }
		  }
		else
		  {
		    if(     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) < tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //  (shared[tid] < shared[ixj])
		      {
			tmp= coords[start];
			coords[start]= coords[ixj];
			coords[ixj]=tmp;
		      }
		  }
	      }
	  __syncthreads();
	  }
	}
    }
 
  cornerY = DimY*bidy +tidy;
  cornerX = DimX*bidx +tidx;

  if(cornerY<ny && cornerX<nx) {    
    original = ((float*)((char*) d_result +  pitch*cornerY))[cornerX] ; 
  }

  short count=0;
  short N =   ( (1+2*hwy)*(1+2*hwx))/2;
  
  int ly,lx;
  int hy,hx;

  ly =  cornerY-hwy ;
  lx =  cornerX-hwx ;
  hy = cornerY+hwy;
  hx = cornerX+hwx ;

  if(0) {
    ly =  cornerY +1;
    lx =  cornerX ;
    hy = ly;
    hx = lx;
    N=0; 
  }
  for(int  k=0; k< shared_size; k++) {

     sh0 .hInt = coords[k];

    if( ((ly <=sh0.Y)   && (sh0.Y<=hy)  &&
	(lx<=sh0.X)   && (sh0.X<= hx))  ) {

      if( count==N) {  sh1.hInt= sh0 .hInt;  }; 
      count=count+1;

    } else {

    }

    //   __syncthreads();
    
  }
  // Write result.
  if(cornerY<ny && cornerX<nx) {
    substitution =  tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f)  ;
    if (  original  - substitution>= threshold ) {
    }else {
      substitution=original;
    }
    ((float*)((char*) d_result +  pitch*cornerY))[cornerX] =  substitution;
  }
 return; 
}  



template<int shared_sizeMAX>
__global__ static void    cudamedianfilterMAX_kernel(int ny,int nx,int  hwy, int hwx,
						     float * d_result,  size_t pitch ,
						     int shared_size, float threshold) {

  __shared__   int  coords[ shared_sizeMAX ] ; // [ shared_size ];

  PackedShorts sh0, sh1;

  const  short bidy = blockIdx.y;
  const  short bidx = blockIdx.x;
  
  const  short tidy = threadIdx.y;
  const  short tidx = threadIdx.x;
  
  int  cornerY, cornerX;
  int DimY, DimX, XY;
  DimX=blockDim.x;
  DimY=blockDim.y;
  XY=DimX*DimY;


  cornerY = DimY*bidy ;
  cornerX = DimX*bidx ;

  cornerY = cornerY - hwy ; 
  cornerX = cornerX - hwx ; 

   short wX;
  // wY = 16+ 2*hwy;
  wX = DimX+ 2*hwx;
  
  unsigned  short start ; 
  float original, substitution;

  
  int  posinshared ; 
  start = tidy*DimX+tidx ;                                                                       
  for( posinshared=start; posinshared< shared_sizeMAX; posinshared+=XY) {
    sh0.Y = cornerY + posinshared/wX ;
    sh0.X = cornerX + posinshared%wX ;
    coords[posinshared]=sh0.hInt ; 
  }

  __syncthreads();
  int tmp;
  // Parallel bitonic sort
  if(1)
   for ( unsigned short  k = 2; k <=shared_size; k *= 2)
    {
      // Bitonic merge:
      for ( unsigned short  j = k / 2; j>0; j /= 2)
        {
	  start = tidy*DimX+tidx ;                                                                       
	  
	  for( ; start <shared_size; start +=XY) {
	    unsigned short  ixj = start ^ j;
	    if(start< shared_sizeMAX  &&  ixj < shared_sizeMAX ) {
	      sh0.hInt = coords[start];
	      sh1.hInt = coords[ixj  ];
	      if (ixj > start)
		{
		  if( (shared_sizeMAX & k) == 0 ) {
		    if ((start & k) == 0)
		      {
			if (     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) >    tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //         shared[tid] > shared[ixj]      )
			  {
			    tmp= coords[start];
			    coords[start]= coords[ixj];
			    coords[ixj]=tmp;
			  }
		      }
		    else
		      {
			if(     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) < tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //  (shared[tid] < shared[ixj])
			  {
			    tmp= coords[start];
			    coords[start]= coords[ixj];
			    coords[ixj]=tmp;
			  }
		      }
		  } else {
		    if ((start & k) == 0)
		      {
			if (     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) <    tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //         shared[tid] > shared[ixj]      )
			  {
			    tmp= coords[start];
			    coords[start]= coords[ixj];
			    coords[ixj]=tmp;
			  }
		      }
		    else
		      {
			if(     tex2D(texData,sh0.X+0.5f,sh0.Y+0.5f) > tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f) )  //  (shared[tid] < shared[ixj])
			  {
			    tmp= coords[start];
			    coords[start]= coords[ixj];
			    coords[ixj]=tmp;
			  }
		      }
		  }
		  
		}
	    }
	    __syncthreads();
	  }
	}
    }
  
  cornerY = DimY*bidy +tidy;
  cornerX = DimX*bidx +tidx;

  if(cornerY<ny && cornerX<nx) {    
    original = ((float*)((char*) d_result +  pitch*cornerY))[cornerX] ; 
  }

  short count=0;
  short N =   ( (1+2*hwy)*(1+2*hwx))/2;
  
  int ly,lx;
  int hy,hx;

  ly =  cornerY-hwy ;
  lx =  cornerX-hwx ;
  hy = cornerY+hwy;
  hx = cornerX+hwx ;

  if(0) {
    ly =  cornerY +1;
    lx =  cornerX ;
    hy = ly;
    hx = lx;
    N=0; 
  }
  for(int  k=0; k< shared_sizeMAX; k++) {

     sh0 .hInt = coords[k];

    if( ((ly <=sh0.Y)   && (sh0.Y<=hy)  &&
	(lx<=sh0.X)   && (sh0.X<= hx))  ) {

      if( count==N) {  sh1.hInt= sh0 .hInt;  }; 
      count=count+1;

    } else {

    }

    //   __syncthreads();
    
  }
  // Write result.
  if(cornerY<ny && cornerX<nx) {
    substitution =  tex2D(texData,sh1.X+0.5f,sh1.Y+0.5f)  ;
    if (  original  - substitution>= threshold ) {
    } else {
      substitution=original;
    }
    ((float*)((char*) d_result +  pitch*cornerY))[cornerX] =  substitution;
  }
 return; 
}  


 
void  gpu_medCtxCreate (Gpu_med_Context * self) {
  self->gpuctx = (void*)  malloc(sizeof(CUcontext)) ; 
  cudaSetDevice(self->MYGPU);
  cuCtxCreate( (CUcontext *) self->gpuctx	,
	       CU_CTX_SCHED_YIELD*0,
	       self->MYGPU	 
	       );
}



void  gpu_medCtxDestroy (Gpu_med_Context * self) {
    cuCtxDestroy( *((CUcontext *) (self->gpuctx))	  );
}

void gpu_med( Gpu_med_Context * self,  int ny, int nx, float *data, float *result, int hwy, int hwx, float threshold) {
  cudaArray * a_data=NULL;
  float * d_result=NULL ; 
  float * d_bufferA=NULL ; 
  float * d_bufferB=NULL ; 

  char messaggio[10000];   
  int shared_size_goal, goal;
  // key_t key1=0; 

  int tileX=16;
  int tileY=16;

  cuCtxSetCurrent ( *((CUcontext *) self->gpuctx  ))  ; 

  

#define   shared_size_max 4000 
  goal = (tileX+2*hwx)*(tileY+2*hwy) ;
  if( goal >shared_size_max ) {
    tileY=4;
  }


  dim3 dimGrid (  nx/tileX+1  ,  ny/tileY+1 );
  dim3      dimBlock         (  tileX    ,   tileY );
  cudaChannelFormatDesc floatTex = cudaCreateChannelDesc<float>();
  
  DEBUG(printf("  in  cuda_median_filter   ny,nx,hwy,hwx  =%d %d %d %d \n", ny,nx,hwy,hwx      );  )
    
  int dev;
  cudaDeviceProp  prop;
  CUDA_SAFE_CALL_EXCEPTION( cudaGetDevice(&dev));

  DEBUG(printf(" il device utilizzato est %d \n", dev ););

    
  // key1 = 1839234+dev;

//   struct sembuf wait,signal;
//   wait.sem_num = 0;
//   wait.sem_op = -1;
//   wait.sem_flg = SEM_UNDO;  
//   signal.sem_num = 0;
//   signal.sem_op = 1;
//   signal.sem_flg = SEM_UNDO;
//   static int semidCompute =0;
//   static int semidSend    =0;
//   static int semidReceive =0;
//   unsigned short semval;
//   if(semidCompute==0 )  {
//     int firstone=0;
//     semidCompute  = semget(key1,1, (IPC_CREAT | IPC_EXCL | 0666 )  );
//     printf("Allocating the semaphore for Gpu COMPUTE: %s\n",strerror(errno));
//     if(semidCompute ==-1 ) {
//       firstone=0;
//       semidCompute  = semget(key1,1, (IPC_CREAT )  );
//       printf("Re-Allocating the semaphore for Gpu COMPUTE: %s\n",strerror(errno));
//     } else {
//       firstone=1 ; 
//     }
//     semidSend  = semget(key1,1,IPC_CREAT| 0666);
//     printf("Allocating the semaphore for Gpu SEND: %s\n",strerror(errno));
//     semidReceive  = semget(key1,1,IPC_CREAT| 0666);
//     printf("Allocating the semaphore for Gpu RECEIVE: %s\n",strerror(errno));
//     if(firstone) {
//       semval = 1;
//       semctl(semidCompute,0,SETVAL,semval);
//       semctl(semidReceive,0,SETVAL,semval);
//       semctl(semidSend   ,0,SETVAL,semval);
//     }
//   }

  CUDA_SAFE_CALL_EXCEPTION(cudaGetDeviceProperties( &prop,dev));
  DEBUG(printf(" la shared mem per block est %ld ( bytes)\n", prop.sharedMemPerBlock););
  
  DEBUG(printf(" la totalConstMem;  est %ld ( bytes)\n", prop.totalConstMem););
    



  goal = (tileX+2*hwx)*(tileY+2*hwy) ;
  shared_size_goal=1;
  
  while(shared_size_goal<goal) {
    shared_size_goal*=2;
  }  



  if( goal< shared_size_max) {
  
    if(  prop.sharedMemPerBlock < shared_size_goal * 4) {
      sprintf(messaggio, "Cannot launch kernel at file %s line %d , required shared size too big  %d %ld \n", __FILE__, __LINE__,shared_size_goal, prop.sharedMemPerBlock/4 );
      goto fail ; 
    }
    
    
    CUDA_SAFE_CALL_EXCEPTION( cudaMallocArray(&a_data, &floatTex ,  nx    ,  ny  ) );

    // semop(semidSend,&wait,1);
    CUDA_SAFE_CALL_EXCEPTION( cudaMemcpyToArray(a_data, 0, 0, data , ny*nx*sizeof(float) , cudaMemcpyHostToDevice) );
    // semop(semidSend,&signal,1);
    CUDA_SAFE_CALL_EXCEPTION( cudaBindTextureToArray(texData, a_data) );


    
    texData.filterMode  =   cudaFilterModePoint;
    
    
    size_t  pitch    ; 
    
    CUDA_SAFE_CALL_EXCEPTION( cudaMallocPitch((void**)&d_result, &pitch,  sizeof(float) * nx , ny     ));
    
    if(   nx+hwx>=  65535/2-1 || ny+hwy >= 65535/2-1   ) {
      sprintf(messaggio, "file %s line %d , dimensions too large \n", __FILE__, __LINE__);
      goto fail; 
    }
    
    CUDA_SAFE_CALL_EXCEPTION( cudaMemcpy2D(  d_result ,  pitch,result, nx*sizeof(float) ,
					     nx*sizeof(float)  , ny ,cudaMemcpyHostToDevice ); );
    
    if(1) {
    try {
      DEBUG(printf( " chiamo kernel %d \n", shared_size_goal););
      

      // semop(semidCompute,&wait,1);

      if(shared_size_goal<=1024) {
	cudamedianfilter_kernel<1024><<<dimGrid,dimBlock>>> (ny,nx, hwy,hwx, d_result, pitch, threshold  );  
      } else if(shared_size_goal==2048) {

	cudamedianfilter_kernel<2048><<<dimGrid,dimBlock>>> (ny,nx, hwy,hwx, d_result, pitch, threshold  );  

      } else if(goal< shared_size_max) {
	cudamedianfilterMAX_kernel<shared_size_max><<<dimGrid,dimBlock>>> (ny,nx, hwy,hwx, d_result, pitch ,shared_size_goal , threshold);  
      }
      cudaThreadSynchronize();	
      // semop(semidCompute,&signal,1);

            
      DEBUG(printf( " chiamo kernel OK 	\n"););
    } catch (...) {
      DEBUG(printf(" throwing unknown exception after cudamedian_kernel  \n");)
	goto fail; 
    }
	}

    CUDA_SAFE_CALL_EXCEPTION(cudaGetLastError(););
	
    DEBUG( printf(" adesso copio \n"););

    // semop(semidReceive,&wait,1);

    CUDA_SAFE_CALL_EXCEPTION( cudaMemcpy2D(  result, nx*sizeof(float) ,d_result ,  pitch,
					     nx*sizeof(float)  , ny ,cudaMemcpyDeviceToHost ); );
    
    // semop(semidReceive,&signal,1);


    CUDA_SAFE_CALL_EXCEPTION(cudaFree(d_result));

    cudaUnbindTexture( texData  );

    CUDA_SAFE_CALL_EXCEPTION(cudaFreeArray(a_data));
	DEBUG(printf(" adesso ritorno \n");)


	
    
    return ;
  } else {

    {
      sprintf(messaggio, "Cannot launch kernel at file %s line %d , required shared size too big  %d %ld \n", __FILE__, __LINE__,shared_size_goal, prop.sharedMemPerBlock/4 );
      goto fail ;
    }
  }
 
 fail :
  if(a_data) cudaFreeArray(a_data);
  if(d_result) cudaFree(d_result);

  if(d_bufferA)  cudaFree(d_bufferA);
  if(d_bufferB)   cudaFree(d_bufferB);

  throw messaggio ;				
}


// main() {

//   float *data, *result;
//   data = new float[2000*4000];
//   result= new float [2000*4000];
//   for(int i=0; i<2000*4000; i++ ) {
//     data[i]=i;
//   }
//   data[125480]=-1000;
//   for(int i=0; i<3; i++) {
//   try {
//     cuda_median_filter( 2000,4000, data,result,  14, 14    );
//   } catch(char * msg ) {
//     printf(" errore %s \n", msg);
//     exit(1);
//   }
//   printf(" il risultato est %f %f %f\n",data[125480]  ,result[125480] ,result[0] );
//   }
// }


