Skip to content
Snippets Groups Projects
gmGeostats.c 25.23 KiB
/*
 * SPDX-FileCopyrightText: 2020 Helmholtz-Zentrum Dresden-Rossendorf
 *  <support@boogaart.de>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */
// attention: comment this if not compiling
#include <stdio.h>
#ifdef _OPENMP
#include <omp.h>
#endif

#define USE_FC_LEN_T

#include <Rinternals.h>

#define inR   // attention: this must be uncommented if not compiling

#ifdef inR 
#include <R.h>
#include <Rmath.h>
#include <R_ext/BLAS.h>
#include <R_ext/Lapack.h> 
#endif

#ifndef FCONE
# define FCONE
#endif

#define maxIntervals 1000
short int binBuf[maxIntervals];
double doubleBuf[maxIntervals];
/* int intBuf[maxIntervals];*/

/* massive use of functions to verify: min, max, abs, sqrt, cos, sin */
/* particularly needed: to build the meta-function that calls fbandXXXX 
 * functions, from line 328 */

typedef void (*vgramDensityFunctionPtr)(int d,double *,double *); 
typedef double (*vgramFunctionPtr)(double,const double *); 
typedef void (*bandSimFunctionPtr)(int m,const double *,double *,double,const double *); 

/* invBitExp2
 inverts the sequence of the bits. 
 this is used for the generation of almost equally space directions in 2D
 
 Lantuejoul (2002), page 194
 */
double invBitExp2(int i) {
  int bit = 1;
  int inv = 0;
  while(i) {
    inv <<=1;
    inv |= (i&1);
    i>>=1;
    bit<<=1;
  }
  return ((double)inv)/bit;
}


/* invBitExp
 inverts the sequence of the digits in b-adict representation. 
 this is used for the generation of almost equally space directions in 3D
 
 Lantuejoul (2002), page 194
 */
double invBitExp(int i,int b) {
  int bit = 1;
  int inv = 0;
  while(i) {
    inv *=b;
    inv += (i%b);
    i/=b;
    bit*=b;
  }
  return ((double)inv)/bit;
}

/*
 Gaussian covariance function
 */

double cGauss(double h,const double *extra) {
  return exp(-(h*h));
}

/*
 Spherical covariance function
 */
double cSph(double h,const double *extra) {
  return( h<1 ? 1-1.5*h+0.5*(h*h*h): 0 );
}

/*
 Exponential covariance function
 */
double cExp(double h,const double *extra) {
  return exp(-h);
  
}

/*
 Switcher for covariance functions 
 */
vgramFunctionPtr cgramFunctions[]={
  cGauss,cSph,cExp
};


static R_NativePrimitiveArgType CcalcCgram_t[] = {
  /* dimX  LDX   X       dimY    LDY    Y       dimC   C     Nugget  nCgr  typeCgr  A       Sill    moreC   ijEq*/
  INTSXP,INTSXP,REALSXP,INTSXP,INTSXP,REALSXP,INTSXP,REALSXP,REALSXP,INTSXP,INTSXP,REALSXP,REALSXP,REALSXP,LGLSXP
};

void CcalcCgram(
    const int *dimX, 
    const int *LDX,
    const double *X,
    const int *dimY,
    const int *LDY,
    const double *Y,
    const int *dimC,
    double *C,
    const double *Nugget, /* d x d*/
const int *nCgrams,   /* 1 */
const int *typeCgram, /* length=nCgrams */
const double *A,  /* nCgrams x m x m sqrt inverse Matrices */
const double *Sill,   /* nCgrams x d x d*/
const double *moreCgramData, /* n x ?*/
const int *ijEqual
) {
  int d=dimC[0];
  int nX=dimC[1];
  int nY=dimC[3];
  int m =dimX[1];
  int ldx = *LDX;
  int ldy = *LDY;
  if( dimC[2]!=d )
    error("CcalcVgram: Expected covariance dimensions not compatible");
  if( dimX[0]!=nX )
    error("CcalcVgram: Output does not fit input size for X");
  if( dimY[0]!=nY )
    error("CcalcVgram: Output does not fit input size for Y");
  if( dimY[1]!=m )
    error("CcalcVgram: Column dimensions of X and Y do not fit");
  if( m<1 || m>3)
    error("Can not handel spatial dimensions outside 1-3");
  int outBufSize=d*d*nX*nY;
  int i,j,k,lx,ly,s; /* 20220424: ev removed */
  double delta[3];
  double v[3];
  double h2,h,val;
  for(i=0;i<outBufSize;i++)
    C[i]=0.0;
  if( *ijEqual ) {
    if( nX!=nY )
      error("CcalcVgram: ijEqual and rows of X and Y don't fit");
    for(lx=0;lx<nX;lx++) {
      for(i=0;i<d;i++)
        for(j=0;j<d;j++) 
          C[i+d*(lx+nX*(j+d*lx))]=Nugget[i+d*j];
    }
  }
  for(s=0;s<*nCgrams;s++) { // structure s
    for(lx=0;lx<nX;lx++) // sample index on dataset X
      for(ly=0;ly<nY;ly++) { // sample index on dataset Y
        for(j=0;j<m;j++) // geographic coordinate index
          delta[j]=Y[ly+ldy*j]-X[lx+ldx*j]; // compute spatial lags between the selected locations
        h2=0;
        for(j=0;j<m;j++) { 
          v[j]=0;
          for(k=0;k<m;k++) {
            v[j]+=A[s+*nCgrams * (j+m*k)]*delta[k];
          }
          h2+=v[j]*v[j];
        }
        h=sqrt(h2);
        val=(*(cgramFunctions[typeCgram[s]]))(h,moreCgramData+s);
        for(i=0;i<d;i++)
          for(j=0;j<d;j++)
            C[i+d*(lx+nX*(j+d*ly))] += val*Sill[s+*nCgrams*(i+d*j)];
      }
  }
}



/* BEGIN deprecated function ? */
/*
 vsdfGauss (vector for spatial density function )
 generates a vector of independent random normals in a vector
 */
void vsdfGauss(int d,double *extra,double *omega) {
  int i;
  for(i=0;i<d;i++)
    omega[i]=norm_rand();
}
/* END deprecated function ? */


/*
 fbandGauss
 
 generates a sinus function with random phase and random frequence on
 a band for a gaussian covariance structure.
 
 This is a mixture of turning bands and spectral simulation.
 
 */
void fbandGauss(int n, /* number of locations */
const double *projs, /* projected locations */ 
double *band, /* output */ 
double range, /* projected range */
const double *extra /* extra parameter (unused), for consistency with other covariances */
){
  /* Extract a freq. from the 1D Gaussian density along the unitdirection 
   * where projs were calculated. Evaluate a wave of random phase at the
   * projs points. Return that wave */	
  int i;
  double phase,amp,omega,d1,o1,o2,o3;   
  /* omega = norm_rand()  * M_SQRT2 / range * M_SQRT_3; /* sqrt(3) needed to produce effective range*/
  // chi(3) distributed radial frequency, with random sign
  o1 = norm_rand();
  o2 = norm_rand();
  o3 = norm_rand();
  omega = sqrt(o1*o1 + o2*o2 + o3*o3) * sign(o1);
  omega = omega * M_SQRT2 / range * M_SQRT_3; /* sqrt(3) needed to produce effective range*/
  phase = unif_rand() * M_2PI;
  amp  = M_SQRT2; /* Lantuejoul (2002), page 191 */
  for(i=0;i<n;i++) {
    d1 = phase + projs[i]*omega; // projs, *projs==projs[0], projs[i]==*(projs+i) 
    d1 = sin(d1);
    band[i] = amp*d1;
  }
}


// IS THIS RIGHT??
void fbandSph(int n, /* number of locations */
  const double *projs, /* projected locations */ 
  double *band, /* output */ 
  double range, /* projected range */
  const double *extra /* extra parameter (unused), for consistency with other covariances */
){
  
  int i,j,nIntervals;
  double t,x0,x1,effrange;
  /* Lantuejoul (2002), page 197 */ 					
  /* Find the smallest proj; select a uniform point "x0" left from it at 
   * most "range" away. Domain = (x0, max(projs)) */
  x0 = projs[0];  /* does min exist?? */
  x1 = projs[0];
  effrange = range;
  // effrange = range *2.0; // this was an attempt to get reasonable range fitting
  for(i=1;i<n;i++) {
    if( projs[i]>x1 )
      x1=projs[i];
    else if( projs[i]<x0 )
      x0=projs[i];
  }  
  x0 += -unif_rand() * effrange;
  nIntervals = (int) ceil((x1-x0)/effrange);
  if( nIntervals > maxIntervals )
    error("fbandSph: Exceeded maxIntervals");
  for(i=0;i<nIntervals;i++) 
    binBuf[i] = unif_rand()<0.5?1:-1;
  for(i=0;i<n;i++) {
    t=(projs[i]-x0)/effrange;
    j=(unsigned int)floor(t);
    //band[i]=binBuf[j]*(t-j-0.5)*M_SQRT_3;
    band[i]=binBuf[j]*(t-j-0.5)*2.0*M_SQRT_3;
  }
}

int bsearchDouble(double x,int n,double *s) {
  int j0=0;
  int j1=n-1;
  int j;
  while(j1-j0>1) {
    /* We know s[j0] <= x < s[j1] */
    j = (j0+j1)/2;
    if( x < s[j] )
      j1=j;
    else
      j0=j;
  }
  return(j0);
}


void fbandExp(int n, /* number of locations */
    const double *projs, /* projected locations */ 
    double *band, /* output */ 
    double range, /* projected range */
    const double *extra /* extra parameter (unused), for consistency with other covariances */
){
  int i,j,ns;
  double d1,x0,x1,sign;
  /* Lantuejoul (2002), page 196 */ 
  /* Find the smallest proj; select a random exponential point "x0" left 
   * from it with lambda "2range" away. Domain = (x0, max(projs)) */
  sign = unif_rand()>0.5? 1 : -1; /* start + or - randomly */
  x0 = projs[0];  
  x1 = projs[0];
  for(i=1;i<n;i++) {
    if( projs[i]>x1 )
      x1=projs[i];
    else if( projs[i]<x0 )
      x0=projs[i];
  }  
  x0 -= 2*range*exp_rand();
  
  /* Partition the domain with a Poisson point process of lambda=2range. */
  ns = 0;
  doubleBuf[0] = x0;
  while( doubleBuf[ns]<x1 ){
    if( ns>=maxIntervals )
      error("fbandExp: too small range; merge with nugget?");
    doubleBuf[ns+1] = doubleBuf[ns] + 2*range*exp_rand();
    ns ++;
  }
  /* Assign values*/
  for(i=0;i<n;i++){
    j=bsearchDouble(projs[i],ns,doubleBuf);
    d1 = (doubleBuf[j+1] + doubleBuf[j])/2; /* midpoint */
  d1 = projs[i] - d1;
  band[i] = d1>0 ? sign : -sign; /* -1 if projs[i]<midpoint; +1 otherwise */
  }  
}


bandSimFunctionPtr bandSim[]={
  fbandGauss,fbandSph,fbandExp
};

void getUnitvec(
    int dimX, /* m = 2 or 3 */
  int ip, /* number of the band being simulated */
  double *unitvec /* out: m x 1*/ 
) {
  /* weak discrepancy sequence of pseudorandom directions in 2D or 3D:
   * Lantuejoul (2002), page 194, after Freulon (1992) */			  
  /* 20220424: removed int i; */ 
  double d1,d2,d3;
  if(dimX>3)
    error("no expression for unit vectors in dimension larger than 3");
  if( dimX==3) {
    d1=invBitExp2(ip)*2*M_PI;
    d2=invBitExp(ip,3);
    d3 = sqrt(1-d2*d2); 
    unitvec[2] = d2;
    unitvec[0] = cos(d1)*d3; 
    unitvec[1] = sin(d1)*d3;
  } else if( dimX==2 ) {
    Rprintf("Warning: gmGeostatsC.getUnitvec in 2D is incompatible with spherical variograms");
    d1=invBitExp2(ip);
    unitvec[0] = cos(d1*M_PI); 
    unitvec[1] = sin(d1*M_PI);   
  } else if( dimX== 1) {
    unitvec[0]=1;
  }
}



static R_NativePrimitiveArgType getUnitvecR_t[] = {  /* INTSXP,REALSXP */
  /* dimX,    iP,  unitvec */
  INTSXP,INTSXP,REALSXP
};


void getUnitvecR(int * dimX,
		 int * ip,
		 double *unitvec
		 ) {
  getUnitvec(*dimX,*ip,unitvec);
}


static R_NativePrimitiveArgType CMVTurningBands_t[] = {  /* INTSXP,REALSXP */
 /* dimX,    X,  dimZ    Z     nBands sqrtNug nCgram  typeCgr   A    sqrtSill moreCgr */
 INTSXP,REALSXP,INTSXP,REALSXP,INTSXP,REALSXP,INTSXP,INTSXP,REALSXP,REALSXP,REALSXP   
 };
  

void CMVTurningBands(
    const int *dimX, /* n,m */
  const double *X,
  const int *dimZ, /*d,n,nsim */
  double *Z, /* Output Simulation transposed*/
  const int *nBands,
  const double *sqrtNugget, /* d x d*/
  const int *nCgrams,   /* 1 */
  const int *typeCgram, /* length=nCgrams */
  const double *A,  /* nCgrams x m x m sqrt inverse Matrices */
  const double *sqrtSill,   /* nCgrams x d x d*/
  const double *moreCgramData /* n x ?*/ 
) {
  const int maxCgramType=2;
  const int nsim=dimZ[2];
  int i,j,k,s,ss,ev;
  double d1,d2; /* 20220424: removed ,d3; */
  const double sqrtNBands=sqrt((double) *nBands);
  double phase; /* 20220424: removed ,amp; */
  const int n=dimX[0];
  const int m=dimX[1];
  const int d=dimZ[0];
  double projs[n];
  double band[n];
  double v[3];
  double omega[3];
  int sim;
  Rprintf("Starting calculations\n");
  if( m<1 || m>3 )
    error("CMVTurningBands: illegal X column dimension");
  if( dimZ[1]!=n )
    error("CMVTurningBands: Z and X do not fit in dimension");
#ifdef inR 
  GetRNGstate();
#endif
  /*setting Z to 0*/
  for(sim=0;sim<nsim;sim++) { 
    for(i=0;i<n;i++)
      for(j=0;j<d;j++)
        Z[d*i+j]=0.0;
    for(s=0;s<*nBands;s++){/* band */
      getUnitvec(3, s+1, &(omega[0])); /* obtain a direction; always in 3D, in order for the spherical variogram to be correct */
      //getUnitvec(m, s+1, omega); /* obtain a direction */
      for(ss=0;ss< *nCgrams;ss++) { /* variogram structure */
        if( typeCgram[ss]<0 || typeCgram[ss] > maxCgramType )
           error("CMVTurningBands: Unknown variogram type");
        /* project all data onto the direction */
        for(i=0;i<n;i++){ /* location */
         for(j=0;j<m;j++) {
          v[j]=0;
          for(k=0;k<m;k++)
           v[j]+=A[ss+ *nCgrams *(j+m*k)]*X[i+n*k];
         }
         projs[i]=0;
         for(j=0;j<m;j++){ /* spatial dimension */
          projs[i]+=omega[j]*v[j];
         }
        }
        /* for each eigenvalue, ... */
        for(ev=0;ev<d;ev++) { /* eigenvector */
         /* ... obtain a curve at all proj points following the covariance model */  
         (*bandSim[typeCgram[ss]])(n,projs,band,1.0,moreCgramData+ss);  
         /* this function takes the projs and returns on band the  the curve */
         /* ... multiply the eigenvector by the curve, and accumulate */
#ifdef _OPENMP
#pragma omp parallel for		             \
    if(!omp_in_parallel()&&0)		        \
      num_threads(omp_get_num_procs())	\
      default(shared) private(i,j,d2) 
         for(i=0;i<n;i++){ /* location */
          for(j=0;j<d;j++){ /* variable */
           d2 = sqrtSill[ss + *nCgrams *(j+d*ev)]*band[i]; 
           Z[d*i+j]+=d2;  
          }	  
         }
#else
         for(i=0;i<n;i++){ /* location */
          for(j=0;j<d;j++){ /* variable */
           d2 = sqrtSill[ss + *nCgrams *(j+d*ev)]*band[i]; 
           Z[d*i+j]+=d2;  
          }	  
         }
#endif
        }
      }
    } 
    /* Rescale*/
    for(i=0;i<n;i++)
      for(j=0;j<d;j++)
        Z[d*i+j] /= sqrtNBands; // Lantuejoul 2002 p.193
    /* Nugget */
    for(i=0;i<n;i++)
      for(j=0;j<d;j++) {
        d1 = norm_rand();
        for(k=0;k<d;k++)
          Z[d*i+k] += d1*sqrtNugget[k+d*j]; /*check*/
      }
      Z+=d*n;
  }
#ifdef inR 
  PutRNGstate();
#endif
}



static R_NativePrimitiveArgType CCondSim_t[] = {  /* INTSXP,REALSXP */
 /* dimZin  Zin   Cinv   dimX,    X,     dimZ    Z     nBands sqrtNugget nugget nCgrams typeCgr   A  sqrtSill sill   moreCgr   cbuf   dbuf */
   INTSXP,REALSXP,REALSXP,INTSXP,REALSXP,INTSXP,REALSXP,INTSXP,REALSXP,REALSXP,INTSXP,INTSXP,REALSXP,REALSXP,REALSXP,REALSXP,REALSXP,REALSXP
};
 

void CCondSim(
    const int *dimZin, /* IN: d, nin */
    const double *Zin, /*IN: nin x d Randomfield data to condition to */
    const double *Cinv,/*IN: (nin * d) x (nin x d) inverse of Covariance*/
    const int *dimX, /* IN: n, m */
    const double *X, /* IN: All Lokations, first nin conditioning */ 
    const int *dimZ, /* IN: d, n,nsim */
    double *Z, /* OUT: t() Output Simulation */
    const int *nBands, /* IN: Desired number of Bands*/
    const double *sqrtNugget, /* IN: d x d */
    const double *nugget, /* IN: dxd */
    const int *nCgrams,   /* IN: number of variograms */
    const int *typeCgram, /* IN: type of each variogram,length=nCgrams */
    /* 0=Gauss, 1=Spherical, 2=Exponential */
    const double *A,  /* IN: Anisotropy matrices, nCgrams x m x m inverse Matrices */
    const double *sqrtSill,   /* IN: nCgrams x d x d*/
    const double *sill,       /* IN: nCgrams x d x d*/
    const double *moreCgramData, /* nGrams x 1 Extraparamter*/ 
    double *cbuf,  /* BUF: Buffer of length d*d*nin */
    double *dbuf  /* BUF: Buffer of length d*nin*nsim */
) {
  /* 20220424 removed: , const int maxCgramType=2; */
  const int n=dimX[0];
  const int m=dimX[1];
  const int d=dimZin[0];
  const int nin = dimZin[1];
  const int nsim= dimZ[2];
  const int nd=n*d;
  const char No='N';
  const char Transposed='T';
  const int dmnin=d*nin;
  const int oneI=1;
  const int zeroI = 0;
  const double zero=0.0;
  const double one=1.0;
  const double minus1=-1.0;
  int i,j,k; /* 20220424 removed: ,s,ss,ev,l; */
  int sim,shift;
  /* 20220424 removed: double d1,d2,d3,cv; */
  const int dimXin[2] = {nin,m}; 
  const int dimXout[2] = {1,m};
  const int dimCbuf[4] = {d,nin,d,1};
  // Unconditional Simulation
  Rprintf("starting unconditional simulation (%d)\n",nsim);
  CMVTurningBands(dimX,
                  X,
                  dimZ,
                  Z,
                  nBands,
                  sqrtNugget,
                  nCgrams,
                  typeCgram,
                  A,
                  sqrtSill,
                  moreCgramData
  );	  
  Rprintf("unconditional simulation done (%d)\n",nsim);
  Rprintf("starting conditioning by dual kriging\n");
  /* Kriging from simulated using Cinv */
  // Create differenes of obs and sim
#ifdef _OPENMP
#pragma omp parallel			               \
  if(!omp_in_parallel())			           \
    num_threads(omp_get_num_procs())		\
    default(shared) private(i,j,sim,shift,k)
    {
#pragma omp parallel for	      
      for(int sim=0;sim<nsim;sim++) {
        int shift=nd*sim;
        for(int i=0;i<nin;i++)
          for(int j=0;j<d;j++){
            int k=d*i+j;
            Z[k+shift]-=Zin[k];
          }
      }
    }
#else
  for(int sim=0;sim<nsim;sim++) {
    int shift=nd*sim;
    for(int i=0;i<nin;i++)
      for(int j=0;j<d;j++){
        int k=d*i+j;
        Z[k+shift]-=Zin[k];
      }
  }
#endif
  /* Z[hinten] -= \hat{Z[hinten]}(Z[vorn]) = cov(hinten,vorn)%*% Cinv %*% Z[vorn] */
  /* dbuf = Cinv %*% Z[vorn] 
   Cinv in R ^ d*nin x d*nin
   Z[vorn] in R^d*nin
   
   */
  // Dual Kriging preparation
#ifdef _OPENMP
#pragma omp parallel for			           \
  if(!omp_in_parallel()&&0)			        \
    num_threads(omp_get_num_procs())		\
    default(shared) private(sim)
    for(sim=0;sim<nsim;sim++) {
      F77_NAME(dgemv)(&No,
               &dmnin,
               &dmnin,
               &one,
               Cinv,
               &dmnin,
               Z+nd*sim,
               &oneI,
               &zero,
               dbuf+dmnin*sim,
               &oneI FCONE);
    }
#else
    for(sim=0;sim<nsim;sim++) {
      F77_NAME(dgemv)(&No,
               &dmnin,
               &dmnin,
               &one,
               Cinv,
               &dmnin,
               Z+nd*sim,
               &oneI,
               &zero,
               dbuf+dmnin*sim,
               &oneI FCONE);
    }
#endif
    // /* points in*/
    //for(i=0;i<nin;i++)
    //for(j=0;j<m;j++)
    //Xin[m*i+j] = X[m*i+j];
    // /* points out*/
    //for(i=nin;i<n;i++)
    //for(j=0;j<m;j++)
    //Xout[m*(i-nin)+j] = X[m*i+j];
    /* fill cbuf*/
    // ******** Iterate over points
    for(i=nin;i<n;i++) {
      CcalcCgram(
        dimXin,
        &n,
        X,
        dimXout,
        &n,
        X+i,
        dimCbuf,
        cbuf,
        nugget, /* d x d*/
    nCgrams,   /* 1 */
    typeCgram, /* length=nCgrams */
    A,  /* nCgrams x m x m sqrt inverse Matrices */
    sill,   /* nCgrams x d x d*/
    moreCgramData,
    &zeroI
      );
      
      //for(l=0;l<nin;l++) { /* points in*/
      //for(j=0;j<d;j++)  /* koor point in */
      //for(k=0;k<d;k++) /* koor point out */
      //cbuf[j+d*(k+d*l)]=0.0; /* geeignet d*d*nin, nugget no */
      //for(s=0;s<*nCgrams;s++) { /* structure */
      //d1=0;
      //for(j=0;j<m;j++) /* spatial dimension */
      //for(k=0;k<m;k++) /* spatial dimension */
      //d1+=A[s + *nCgrams *(j+m*k)]*omega[j]*omega[k]; /* check */
      //cv=(*cgramFunctions[typeCgram[s]])(sqrt(d1),moreCgramData+s);
      //for(j=0;j<d;j++)  /* koor point in */
      //for(k=0;k<d;k++) /* koor point out */
      //cbuf[j+d*(k+d*l)]+=cv*sill[s+*nCgrams*(j+d*k)];
      //}
      //}
      
      /* Z[,i] +=  t(cbuf(d*nin,d)) %*% dbuf(d*nin) */
#ifdef _OPENMP
#pragma omp parallel for			               \
      if(!omp_in_parallel()&&0)			        \
        num_threads(omp_get_num_procs())		\
        default(shared) private(sim)
        for(sim=0;sim<nsim;sim++) {
          F77_NAME(dgemv)(&Transposed,
                   &dmnin,
                   &d,
                   &minus1,
                   cbuf,
                   &dmnin,
                   dbuf+dmnin*sim,
                   &oneI,
                   &one,
                   Z+i*d+nd*sim,
                   &oneI FCONE);
        }
#else
        for(sim=0;sim<nsim;sim++) {
          F77_NAME(dgemv)(&Transposed,
                   &dmnin,
                   &d,
                   &minus1,
                   cbuf,
                   &dmnin,
                   dbuf+dmnin*sim,
                   &oneI,
                   &one,
                   Z+i*d+nd*sim,
                   &oneI FCONE);
        }
#endif
    }
}



//anaV(v1,m,mx,i*h,dimY,y,sigma0,sigma1);
extern void anaV(double *v,   // velocity of a datum
                 const int m,         // number of variables
                 const double *x,     // location of the datum
                 const double t,      // time moment
                 const int *dimY,     // dimension of the data nodes
                 const double *y,     // data nodes
                 const double *wY,    // weights of the data nodes
                 const double sigma0, // parameter sigma0
                 const double sigma1  // parameter sigma1
) {
  size_t nY=dimY[1];  // number of data nodes
  double sigmat=sigma0+t*(sigma1-sigma0);  // deviation at this moment
  double sigmaD=(sigma1-sigma0); 
  for(int i=0;i<m;i++){ // initialize velocity
    v[i]=0;
  }
  double ws=0; // sum of weights
  for(int j=0;j<nY;j++) { // loop on number of data nodes
    // double d=0; 
    double dz[m];    
    double s2=0;
    for(int i=0;i<m;i++) {
      const double ddz=(x[i]-(1-t)*y[m*j+i])/sigmat;
      dz[i]=ddz;
      s2+=ddz*ddz;
    }
    double w=exp(-s2/2.0)*wY[j]; // weighting
    ws+=w;
    for(int i=0;i<m;i++)
      v[i]+=w*(sigmaD*dz[i]-y[m*j+i]);
  }
  for(int i=0;i<m;i++){
    v[i]/=ws;
  } 
}



static R_NativePrimitiveArgType anaForwardC_t[] = {  /* INTSXP,REALSXP */
    /* dimX,    x,     dimY    y     wY    stepsp   sigma0p    sigma1p */
     INTSXP,REALSXP,INTSXP,REALSXP,REALSXP,INTSXP, REALSXP,  REALSXP
};


extern void anaForwardC(const int *dimX,
                        double *x,
                        const int *dimY,
                        const double *y,
                        const double *wY,    // weights of the data nodes
                        const int *stepsp,
                        const double *sigma0p,
                        const double *sigma1p
) {
  
  const double sigma0=*sigma0p;
  const double sigma1=*sigma1p;
  const size_t steps=*stepsp;
  const size_t m=dimX[0];
  const size_t nx=dimX[1];
  // const size_t ny=dimY[1];
  const double h=((double)1.0)/steps;
  if( dimY[0]!=m )
    error("anaForwardC: x and y have different number of variables / rows");
#ifdef _OPENMP
  #pragma omp parallel for 
  for(size_t i=0;i<nx;i++) {
    double v1[m];
    double v2[m];
    double xx[m];
    double *mx=x+m*i;
    for(size_t s=0;s<steps;s++) {
      // v1
      anaV(v1,m,mx,s*h,dimY,y,wY,sigma0,sigma1);
      // xx=x+h*v1
      for(int j=0;j<m;j++)
        xx[j]=mx[j]+h*v1[j];
      // v2
      anaV(v2,m,xx,(s+1)*h,dimY,y,wY,sigma0,sigma1);
      // x+=0.5*h*(v1+v2)
      for(int j=0;j<m;j++)
        mx[j]+=0.5*h*(v1[j]+v2[j]);
    }
  }
#else
  for(size_t i=0;i<nx;i++) {
    double v1[m];
    double v2[m];
    double xx[m];
    double *mx=x+m*i;
    for(size_t s=0;s<steps;s++) {
      // v1
      anaV(v1,m,mx,s*h,dimY,y,wY,sigma0,sigma1);
      // xx=x+h*v1
      for(int j=0;j<m;j++)
        xx[j]=mx[j]+h*v1[j];
      // v2
      anaV(v2,m,xx,(s+1)*h,dimY,y,wY,sigma0,sigma1);
      // x+=0.5*h*(v1+v2)
      for(int j=0;j<m;j++)
        mx[j]+=0.5*h*(v1[j]+v2[j]);
    }
  }
#endif
}


static R_NativePrimitiveArgType anaBackwardC_t[] = {  /* INTSXP,REALSXP */
    /* dimX,    x,     dimY    y     wY    stepsp   sigma0p    sigma1p */
    INTSXP,REALSXP,INTSXP,REALSXP,REALSXP,INTSXP, REALSXP,  REALSXP
};

extern void anaBackwardC(const int *dimX,
                         double *x,
                         const int *dimY,
                         const double *y,
                         const double *wY,    // weights of the data nodes
                         const int *stepsp,
                         const double *sigma0p,
                         const double *sigma1p
) {
  
  const double sigma0=*sigma0p;
  const double sigma1=*sigma1p;
  const size_t steps=*stepsp;
  const size_t m=dimX[0];
  const size_t nx=dimX[1];
  // const size_t ny=dimY[1];
  const double h=((double)1.0)/steps;
  if( dimY[0]!=m )
    error("anaBackwardC: x and y have different number of variables / rows");
#ifdef _OPENMP
#pragma omp parallel for 
  for(size_t i=0;i<nx;i++) {
    double v1[m];
    double v2[m];
    double xx[m];
    double *mx=x+m*i;
    for(size_t s=0;s<steps;s++) {
      // v1
      anaV(v1,m,mx,1-s*h,dimY,y,wY,sigma0,sigma1);
      // xx=x-h*v1
      for(int j=0;j<m;j++)
        xx[j]=mx[j]-h*v1[j];
      // v2
      anaV(v2,m,xx,1-(s+1)*h,dimY,y,wY,sigma0,sigma1);
      // x+=0.5*h*(v1+v2)
      for(int j=0;j<m;j++)
        mx[j]-=0.5*h*(v1[j]+v2[j]);
    }
  }
#else
  for(size_t i=0;i<nx;i++) {
    double v1[m];
    double v2[m];
    double xx[m];
    double *mx=x+m*i;
    for(size_t s=0;s<steps;s++) {
      // v1
      anaV(v1,m,mx,1-s*h,dimY,y,wY,sigma0,sigma1);
      // xx=x-h*v1
      for(int j=0;j<m;j++)
        xx[j]=mx[j]-h*v1[j];
      // v2
      anaV(v2,m,xx,1-(s+1)*h,dimY,y,wY,sigma0,sigma1);
      // x+=0.5*h*(v1+v2)
      for(int j=0;j<m;j++)
        mx[j]-=0.5*h*(v1[j]+v2[j]);
    }
  }
#endif
}





/* ======================================
 * EXPORT FUNCTIONS TO R
 * ======================================
 */




static R_CMethodDef cMethods[] = {
  {"CcalcCgram", (DL_FUNC) &CcalcCgram, 15, CcalcCgram_t},
  {"CMVTurningBands", (DL_FUNC) &CMVTurningBands, 11, CMVTurningBands_t},
  {"CCondSim", (DL_FUNC) & CCondSim, 18,  CCondSim_t},
  {"anaForwardC", (DL_FUNC) &anaForwardC, 8, anaForwardC_t},
  {"anaBackwardC", (DL_FUNC) &anaBackwardC, 8, anaBackwardC_t},
  {"getUnitvecR", (DL_FUNC) &getUnitvecR, 3, getUnitvecR_t},
  {NULL, NULL, 0}
};


//{"CMVTurningBands2", (DL_FUNC) &CMVTurningBands2, 11, CMVTurningBands2_t},



void R_init_gmGeostats(DllInfo *info)
{
  R_registerRoutines(info, cMethods, NULL, NULL, NULL);
  R_useDynamicSymbols(info, FALSE);
  R_forceSymbols(info, TRUE);
}