
// author: N. Matloff

// Rth convolution routine; conforming to R convolve() with type = 'open'

// arguments:
//    x,y:  input vectors
//    nx,ny:  pointers to x,y lengths 
//    z:  output vector, length nx+ny-1
extern "C" void rthconv(double *x, int *nx, double *y, int *ny, double *z);

#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <algorithm>

extern "C" {

#ifdef GPU
#define flouble float
#else
#define flouble double
#endif

typedef thrust::device_vector<int> intvec;
typedef thrust::device_vector<flouble> floublevec;
typedef thrust::device_vector<flouble>::iterator floubleveciter;
typedef thrust::host_vector<int> hintvec;

struct do1i  // name connotes doing one value of i in i,j loop
{
   floubleveciter x;
   floubleveciter y;
   floubleveciter z;
   int nx,ny,nz;
   flouble *xd,*yd,*zd;

   // set up variables for random permutation, needed for load balance:
   intvec rp;
   int *rndperm;

   do1i(floubleveciter _x,floubleveciter _y,floubleveciter _z,
        int _nx, int _ny):
      x(_x),y(_y),z(_z),nx(_nx),ny(_ny) { 
         xd = thrust::raw_pointer_cast(&x[0]);
         yd = thrust::raw_pointer_cast(&y[0]);
         zd = thrust::raw_pointer_cast(&z[0]);
         // set up random permutation; need it on device, and not in
         // this stack frame 
         hintvec hseq(nx);
         thrust::sequence(hseq.begin(),hseq.end(),0);
         std::random_shuffle(hseq.begin(),hseq.end());
         rp.resize(nx);  // rp had had no size before now
         thrust::copy(hseq.begin(),hseq.end(),rp.begin());
         rndperm = thrust::raw_pointer_cast(&rp[0]);
      }
   __device__
   void operator()(const int i)
   {  int j;  // handle one value of i in i,j loop
      int rpi = rndperm[i];
      flouble xdi = xd[rpi];
      for (j = 0; j < ny; j++)
         zd[rpi+j] += xdi * yd[ny-j-1];
   }
};

void rthconv(flouble *x, int *nx, flouble *y, int *ny, flouble *z)
{  int nxx = *nx, nyy = *ny, nzz = nxx + nyy - 1;
   floublevec dx(x,x+nxx);
   floublevec dy(y,y+nyy);
   floublevec dz(nzz);
   thrust::fill(dz.begin(),dz.end(),0);
   thrust::counting_iterator<int> seqb(0);
   thrust::counting_iterator<int> seqe = seqb + nxx;
   thrust::for_each(seqb,seqe,do1i(dx.begin(),dy.begin(),dz.begin(),nxx,nyy));
   thrust::copy(dz.begin(), dz.end(),z);
}

// test
// #include<stdio.h>
// int main(int argc, char **argv)
// {  flouble x[7] = {1,6,0,100,0,8,1};
//    flouble y[8] = {0,1,0.25,0.5,0.25,1,6,12};
//    flouble z[14];
//    int nxx=7,nyy=8,*nx=&nxx,*ny=&nyy;
//    rthconv(x,nx,y,ny,z);
//    int i;
//    should print 12.00   78.00   37.00 1206.25  602.00  199.25   87.50
//    62.00   28.00  104.25    2.50    8.25    1.00    0.00
//    for (i = 0; i < nxx+nyy-1; i++) printf("%f\n",z[i]);
// }

}

