1 /***************************************************************************
2          interpolate.cpp  -  all things related to interpol command
3                              -------------------
4     begin                : Mar 30 2021
5     copyright            : (C) 2004 by Joel Gales
6                          : (C) 2018 G. Duvert
7     email                : see https://github.com/gnudatalanguage/gdl
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 #include "includefirst.hpp"
20 
21 #include "datatypes.hpp"
22 #include "envt.hpp"
23 #include "dinterpreter.hpp"
24 
25 #ifdef _MSC_VER
26 #define isfinite _finite
27 #endif
28 
29 //interpolate
30 #include <gsl/gsl_errno.h>
31 #include <gsl/gsl_interp.h>
32 #include <gsl/gsl_spline.h>
33 #include <gsl/gsl_nan.h>
34 
35 using std::isfinite;
36 
37 /* convolutions available */
linConv(double d,double x0,double x1)38 inline double linConv(double d, double x0, double x1) {
39   return (1. - d)*x0 + d*x1;
40 }
41 
42 typedef struct {
43     const char* name;
44     unsigned int min_size;
45     void* (*alloc)(ssize_t size);
46     int (*init)(void*, const double xa[], const double ta[], ssize_t xsize);
47     int (*eval)(const void*, const double xa[], const double ta[], ssize_t xsize, double x, gsl_interp_accel*, ssize_t *lastval, double* lastCoefs, double *t);
48     void (*free)(void*);
49   } gdl_interpol_type;
50 
51   typedef struct {
52     const gdl_interpol_type* type;
53     double xmin;
54     double xmax;
55     ssize_t xsize;
56     void* state;
57     ssize_t * lastval;
58     double * lastCoefs; //if last index is same, use last coefs
59   } gdl_interpol;
60 
gdl_interpol_type_min_size(const gdl_interpol_type * T)61 ssize_t gdl_interpol_type_min_size(const gdl_interpol_type* T) {
62     return T->min_size;
63 }
64 
gdl_interpol_min_size(const gdl_interpol * interp)65 ssize_t gdl_interpol_min_size(const gdl_interpol* interp) {
66   return interp->type->min_size;
67 }
68 
gdl_interpol_name(const gdl_interpol * interp)69 const char* gdl_interpol_name(const gdl_interpol* interp) {
70   return interp->type->name;
71 }
72 
73 GSL_VAR const gdl_interpol_type* gdl_interpol_linear;
74 GSL_VAR const gdl_interpol_type* gdl_interpol_quadratic;
75 GSL_VAR const gdl_interpol_type* gdl_interpol_spline;
76 
77 double gdl_interpol_eval(const gdl_interpol* interp, const double xarr[], const double tarr[], const double x, gsl_interp_accel* xa);
78 
gdl_interpol_alloc(const gdl_interpol_type * T,ssize_t xsize)79 gdl_interpol* gdl_interpol_alloc(const gdl_interpol_type* T, ssize_t xsize) {
80     gdl_interpol* interp;
81     interp = (gdl_interpol*) malloc(sizeof (gdl_interpol));
82     if (interp == NULL) {
83       GSL_ERROR_NULL("failed to allocate space for gdl_interpol struct", GSL_ENOMEM);
84     }
85     interp->type = T;
86     interp->xsize = xsize;
87     if (interp->type->alloc == NULL) {
88       interp->state = NULL;
89       return interp;
90     }
91     interp->state = interp->type->alloc(xsize);
92     if (interp->state == NULL) {
93       free(interp);
94       GSL_ERROR_NULL("failed to allocate space for gdl_interpol state", GSL_ENOMEM);
95     }
96     return interp;
97   }
98 
gdl_interpol_init(gdl_interpol * interp,const double xarr[],const double tarr[],ssize_t xsize)99   int gdl_interpol_init(gdl_interpol* interp, const double xarr[], const double tarr[], ssize_t xsize) {
100     ssize_t i;
101     if (xsize != interp->xsize) {
102       GSL_ERROR("data must match size of interpolation object", GSL_EINVAL);
103     }
104     for (i = 1; i < xsize; i++) {
105       if (xarr[i - 1] >= xarr[i]) {
106 //        GSL_ERROR("x values must be strictly increasing", GSL_EINVAL);
107         Message ("X values are not strictly increasing, INTERPOL may give incorrect results");
108         break;
109       }
110     }
111     interp->xmin = xarr[0];
112     interp->xmax = xarr[xsize - 1];
113     int status = interp->type->init(interp->state, xarr, tarr, xsize);
114     //allocate lastCoefs and set lastval to -1:
115     interp->lastval=(ssize_t*)malloc(1*sizeof(ssize_t));
116     interp->lastval[0]=-1;
117     interp->lastCoefs=(double*)malloc(2*interp->type->min_size*sizeof(double)); //overkill in most cases.
118     return status;
119   }
gdl_interpol_free(gdl_interpol * interp)120   void gdl_interpol_free(gdl_interpol* interp) {
121     if (!interp) {
122       return;
123     }
124     if (interp->type->free) {
125       interp->type->free(interp->state);
126     }
127     free(interp->lastval);
128     free(interp->lastCoefs);
129     free(interp);
130   }
131 
gdl_interpol_eval(const gdl_interpol * interp,const double xarr[],const double tarr[],const double x,gsl_interp_accel * xa)132   double gdl_interpol_eval(const gdl_interpol* interp, const double xarr[], const double tarr[], const double x, gsl_interp_accel* xa) {
133     double xx, t;
134     xx = x;
135     int status;
136     status = interp->type->eval(interp->state, xarr, tarr, interp->xsize, xx, xa, interp->lastval, interp->lastCoefs, &t);
137     if ((status) != GSL_SUCCESS) GSL_ERROR_VAL("interpolation error", (status), GSL_NAN);
138 
139     return t;
140   }
linear_init(void * state,const double xa[],const double ta[],ssize_t xsize)141   static int linear_init(void* state, const double xa[], const double ta[], ssize_t xsize) {
142     return GSL_SUCCESS;
143   }
quadratic_init(void * state,const double xa[],const double ta[],ssize_t xsize)144   static int quadratic_init(void* state, const double xa[], const double ta[], ssize_t xsize) {
145     return GSL_SUCCESS;
146   }
147 
linear_eval(const void * state,const double xarr[],const double tarr[],ssize_t xsize,double x,gsl_interp_accel * xa,ssize_t * lastval,double * C,double * t)148   static int linear_eval(const void* state, const double xarr[], const double tarr[], ssize_t xsize, double x, gsl_interp_accel* xa, ssize_t* lastval, double* C, double *t) {
149     ssize_t xi = gsl_interp_accel_find(xa, xarr, xsize, x); //xa must be ALWAYS defined for GDL
150     if (xi != *lastval) {
151       *lastval = xi;
152       ssize_t xp = (xi + 1 < xsize) ? xi + 1 : xi;
153       C[0] = tarr[xi];
154       C[1] = tarr[xp];
155       C[2] = xarr[xi];
156       C[3] = xarr[xp]-xarr[xi];
157     }
158     double u = (C[3] > 0.0) ? (x - C[2]) / C[3] : 0.0;
159     *t = linConv(u, C[0], C[1]);
160     return GSL_SUCCESS;
161   }
162 
quadratic_eval(const void * state,const double xarr[],const double tarr[],ssize_t xsize,double x,gsl_interp_accel * xa,ssize_t * lastval,double * C,double * t)163   static int quadratic_eval(const void* state, const double xarr[], const double tarr[], ssize_t xsize, double x, gsl_interp_accel* xa, ssize_t* lastval, double* C, double *t) {
164     ssize_t xi = gsl_interp_accel_find(xa, xarr, xsize, x);//xa must be ALWAYS defined for GDL
165     if (xi != *lastval) {
166       *lastval = xi;
167       ssize_t xp, xm;
168       if ( xi+1 >= xsize ) { xp = xsize-1; xi = xsize-2; xm=xsize-3;}
169       else if ( xi-1 < 0 ) { xm = 0; xi = 1; xp=2; }
170       else { xm=xi-1; xp=xi+1;}
171       C[3] = tarr[xm];
172       C[4] = tarr[xi];
173       C[5] = tarr[xp];
174       C[0] = xarr[xm];
175       C[1] = xarr[xi];
176       C[2] = xarr[xp];
177     }
178     *t = (C[3] * (x-C[1]) * (x-C[2]) / ((C[0]-C[1]) * (C[0]-C[2]))) + (C[4] * (x-C[0]) * (x-C[2]) / ((C[1]-C[0]) * (C[1]-C[2])))+ (C[5] * (x-C[0]) * (x-C[1]) / ((C[2]-C[0]) * (C[2]-C[1])));
179     return GSL_SUCCESS;
180   }
181 
182   static const gdl_interpol_type linear_type = {
183     "linear",
184     2,
185     NULL,
186     &linear_init,
187     &linear_eval
188   };
189 
190   static const gdl_interpol_type quadratic_type = {
191     "quadratic",
192     3,
193     NULL,
194     &quadratic_init,
195     &quadratic_eval
196   };
197 
198  const gdl_interpol_type* gdl_interpol_linear = &linear_type;
199  const gdl_interpol_type* gdl_interpol_quadratic = &quadratic_type;
200 
201 #if defined(USE_EIGEN)
202  GSL_VAR const gdl_interpol_type* gdl_interpol_lsquadratic;
lsquadratic_init(void * state,const double xa[],const double ta[],ssize_t xsize)203  static int lsquadratic_init(void* state, const double xa[], const double ta[], ssize_t xsize) {
204     return GSL_SUCCESS;
205   }
206 
207 #include <Eigen/LU>
208 #include <Eigen/Eigenvalues>
209 #include <Eigen/Core>
210  //no need to use another complicated general GSL solution -- just the same as IDL's procedure.
lsquadratic_eval(const void * state,const double xarr[],const double tarr[],ssize_t xsize,double pos,gsl_interp_accel * xa,ssize_t * lastval,double * C,double * t)211   static int lsquadratic_eval(const void* state, const double xarr[], const double tarr[], ssize_t xsize, double pos, gsl_interp_accel* xa, ssize_t* lastval, double* C, double *t) {
212     ssize_t xi = gsl_interp_accel_find(xa, xarr, xsize, pos);//xa must be ALWAYS defined for GDL
213     if (xi != *lastval) {
214       *lastval = xi;
215       ssize_t x[4];
216       //make in range
217       if ( xi+2 >= xsize ) {  x[0]=xsize-4;  x[1]=xsize-3;  x[2] = xsize-2; x[3] = xsize-1;}
218       else if ( xi-1 < 0 ) { x[0] = 0; x[1] = 1; x[2]=2; x[3]=3;}
219       else { x[0]=xi-1; x[1]= xi; x[2]=xi+1; x[3]=xi+2;}
220      // least_square fit of a 2nd degree polynomial on these 4 points:
221       double matrix[]={xarr[x[0]]*xarr[x[0]],xarr[x[0]],1.0,xarr[x[1]]*xarr[x[1]],xarr[x[1]],
222       1.0,xarr[x[2]]*xarr[x[2]],xarr[x[2]],1.0,xarr[x[3]]*xarr[x[3]],xarr[x[3]],1.0};
223       Eigen::MatrixXd A(4,3) ; //<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> > A(matrix, 3,4);
224       for (int i=0; i< 3; ++i) for (int j=0; j<4; ++j) A(j,i)=matrix[j*3+i];
225       Eigen::Vector4d b(tarr[x[0]],tarr[x[1]],tarr[x[2]],tarr[x[3]]);
226       Eigen::MatrixXd r=(A.transpose() * A).ldlt().solve(A.transpose() * b);
227       C[0] = r.coeff(0);
228       C[1] = r.coeff(1);
229       C[2] = r.coeff(2);
230     }
231    *t= C[2] + C[1]* pos + C[0] * pos*pos;
232    return GSL_SUCCESS;
233   }
234   static const gdl_interpol_type lsquadratic_type = {
235     "lsquadratic",
236     4,
237     NULL,
238     &lsquadratic_init,
239     &lsquadratic_eval
240   };
241  const gdl_interpol_type* gdl_interpol_lsquadratic = &lsquadratic_type;
242 #endif
243 
244 #if defined(USE_EIGEN)
245  GSL_VAR const gdl_interpol_type* gdl_interpol_cspline;
cspline_init(void * state,const double xa[],const double ta[],ssize_t xsize)246  static int cspline_init(void* state, const double xa[], const double ta[], ssize_t xsize) {
247     return GSL_SUCCESS;
248   }
249 
250 #include <Eigen/LU>
251 #include <Eigen/Eigenvalues>
252 #include <Eigen/Core>
cspline_eval(const void * state,const double xarr[],const double tarr[],ssize_t xsize,double pos,gsl_interp_accel * xa,ssize_t * lastval,double * C,double * t)253 static int cspline_eval(const void* state, const double xarr[], const double tarr[], ssize_t xsize, double pos, gsl_interp_accel* xa, ssize_t* lastval, double* C, double *t) {
254   ssize_t xii = gsl_interp_accel_find(xa, xarr, xsize, pos); //xa must be ALWAYS defined for GDL
255   //xii in range [0: xsize-2]--> the real index, produces 4 derivatives C[0..3], but we save the 2 to be used.
256   //use xi to select the good 4 points on either end, and the good C[] to be used afterwards.
257   ssize_t xi=xii;
258   if ( xi == xsize-2 ) xi=xsize-3; else if ( xi == 0 ) xi=1;
259   double dx=xarr[xii+1]-xarr[xii];
260   //acceleration: do not recompute.
261   if (xii != *lastval) {
262     *lastval = xii;
263     double dx0 = 1 / (xarr[xi] - xarr[xi-1]);
264     double dx1 = 1 / (xarr[xi+1] - xarr[xi]);
265     double dx2 = 1 / (xarr[xi + 2] - xarr[xi + 1]);
266     double b0 = 3 * dx0 * dx0 * (tarr[xi] - tarr[xi-1]);
267     double b1 = 3 * dx1 * dx1 * (tarr[xi+1] - tarr[xi]);
268     double b2 = 3 * dx2 * dx2 * (tarr[xi + 2] - tarr[xi + 1]);
269     // cspline solution on these 4 points:
270     double matrix[] = {2 * dx0, dx0, 0, 0, dx0, 2 * (dx0 + dx1), dx1, 0, 0, dx1, 2 * (dx1+dx2),dx2,0,0,dx2,2*dx2};
271     Eigen::MatrixXd A(4, 4);
272     for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) A(j, i) = matrix[j * 4 + i];
273     Eigen::Vector4d b(b0, b0+b1, b1+b2, b2);
274     Eigen::MatrixXd r = (A.transpose() * A).ldlt().solve(A.transpose() * b);
275     if (xii == 0) {C[0] = r.coeff(0); C[1] = r.coeff(1); }
276     else if (xii == xsize-2) {C[0] = r.coeff(2); C[1] = r.coeff(3); }
277     else {C[0] = r.coeff(1); C[1] = r.coeff(2); }
278     double dy=tarr[xii+1]-tarr[xii];
279     C[0] = C[0]*dx-dy;
280     C[1] = C[1]*-dx+dy;
281   }
282    double dpos=(pos-xarr[xii])/dx;
283    *t= (1-dpos)*tarr[xii]+dpos*tarr[xii+1]+dpos*(1-dpos)*((1-dpos)*C[0]+dpos*C[1]);
284 //   double dpos=pos-xarr[xii]; double dpos3=dpos*dpos*dpos; double dpos2=dpos*dpos;
285 //  *t=(2*dpos3-3*dpos2+1)*tarr[xii]+(dpos3-2*dpos2+dpos)*C[0]+(-2*dpos3+3*dpos2)*tarr[xii+1]+(dpos3-dpos2)*C[1];
286 //Catmull-Rom Spline (not the good one!) : double u=pos-xarr[xi]; *t=0.5*(((-tarr[xi-1]+3*tarr[xi]-3*tarr[xi+1]+tarr[xi+2])*u+(2*tarr[xi-1]-5*tarr[xi]+4*tarr[xi+1]-tarr[xi+2]))*u+(-tarr[xi-1]+tarr[xi+1]))*u+tarr[xi];
287   return GSL_SUCCESS;
288 }
289 static const gdl_interpol_type cspline_type = {
290   "cspline",
291   4,
292   NULL,
293   &cspline_init,
294   &cspline_eval
295 };
296 const gdl_interpol_type* gdl_interpol_cspline = &cspline_type;
297 #endif
298 
spline_init(void * state,const double xa[],const double ta[],ssize_t xsize)299   static int spline_init(void* state, const double xa[], const double ta[], ssize_t xsize) {
300     return GSL_SUCCESS;
301   }
spline_eval(const void * state,const double xarr[],const double tarr[],ssize_t xsize,double pos,gsl_interp_accel * xa,ssize_t * lastval,double * C,double * t)302   static int spline_eval(const void* state, const double xarr[], const double tarr[], ssize_t xsize, double pos, gsl_interp_accel* xa, ssize_t* lastval, double* C, double *t) {
303     static DIntGDL One=DIntGDL(1);
304     ssize_t xi = gsl_interp_accel_find(xa, xarr, xsize, pos);//xa must be ALWAYS defined for GDL
305       DDoubleGDL* P=new DDoubleGDL(pos);
306       DDoubleGDL* X;
307       DDoubleGDL* Y;
308 
309       *lastval = xi;
310       ssize_t x[4];
311       //make in range
312       if ( xi+2 >= xsize ) {  x[0]=xsize-4;  x[1]=xsize-3;  x[2] = xsize-2; x[3] = xsize-1;}
313       else if ( xi-1 < 0 ) { x[0] = 0; x[1] = 1; x[2]=2; x[3]=3;}
314       else { x[0]=xi-1; x[1]= xi; x[2]=xi+1; x[3]=xi+2;}
315       C[0]=xarr[x[0]];C[1]=xarr[x[1]];C[2]=xarr[x[2]];C[3]=xarr[x[3]];
316       C[4]=tarr[x[0]];C[5]=tarr[x[1]];C[6]=tarr[x[2]];C[7]=tarr[x[3]];
317       X=new DDoubleGDL(dimension(4),BaseGDL::NOZERO); for(int i=0; i<4; ++i) (*X)[i]=C[i];
318       Y=new DDoubleGDL(dimension(4),BaseGDL::NOZERO); for(int i=0; i<4; ++i) (*Y)[i]=C[i+4];
319 
320       static int splinitIx = LibFunIx( "SPL_INIT" );
321       EnvT* newEnv = new EnvT(NULL, libFunList[ splinitIx]);
322       newEnv->SetNextPar( X ); // pass as local
323       newEnv->SetNextPar( Y ); // pass as local
324       newEnv->SetKeyword("DOUBLE",&One);
325       DDoubleGDL* Q = static_cast<DDoubleGDL*>(static_cast<DLibFun*>(newEnv->GetPro())->Fun()(static_cast<EnvT*>(newEnv)));
326 
327       static int splinterpIx = LibFunIx( "SPL_INTERP" );
328       EnvT* newEnv1 = new EnvT(NULL, libFunList[ splinterpIx]);
329       newEnv1->SetNextPar( X ); // pass as local
330       newEnv1->SetNextPar( Y ); // pass as local
331       newEnv1->SetNextPar( Q ); // pass as local
332       newEnv1->SetNextPar( P ); // pass as local
333       newEnv1->SetKeyword("DOUBLE",&One);
334       DDoubleGDL* res =static_cast<DDoubleGDL*>( static_cast<DLibFun*>(newEnv1->GetPro())->Fun()(static_cast<EnvT*>(newEnv1)));
335       *t=(*res)[0];
336     return GSL_SUCCESS;
337   }
338   static const gdl_interpol_type spline_type = {
339     "spline",
340     4,
341     NULL,
342     &spline_init,
343     &spline_eval
344   };
345  const gdl_interpol_type* gdl_interpol_spline = &spline_type;
346 
347 namespace lib {
348 
interpol_fun(EnvT * e)349   BaseGDL* interpol_fun(EnvT* e){
350     SizeT nParam = e->NParam();
351     if (nParam < 2 || nParam > 3) e->Throw("Incorrect number of arguments.");
352 
353     const gdl_interpol_type* interpol=gdl_interpol_linear;
354     // options
355     static int LSQUADRATIC = e->KeywordIx("LSQUADRATIC");
356 #if defined(USE_EIGEN)
357     if (e->KeywordSet(LSQUADRATIC)) interpol=gdl_interpol_lsquadratic;
358 #else
359     if (e->KeywordSet(LSQUADRATIC)) interpol=gdl_interpol_quadratic;
360 #endif
361     static int QUADRATIC = e->KeywordIx("QUADRATIC");
362     if (e->KeywordSet(QUADRATIC)) interpol=gdl_interpol_quadratic;
363     static int SPLINE = e->KeywordIx("SPLINE");
364 #if defined(USE_EIGEN)
365     if (e->KeywordSet(SPLINE)) interpol=gdl_interpol_cspline;
366 #else
367     if (e->KeywordSet(SPLINE)) interpol=gdl_interpol_spline;
368 #endif
369     static int NANIx = e->KeywordIx("NAN");
370     bool doNan=e->KeywordSet(NANIx);
371     unsigned int nmin=gdl_interpol_type_min_size(interpol);
372 
373     //dimensions
374     BaseGDL* p0 = e->GetParDefined(0);
375     DType type=p0->Type();
376     SizeT nV=p0->N_Elements();
377     SizeT orig_nV=nV;
378     if (nV <nmin) e->Throw("V has too few elements for this kind of interpolation.");
379 
380     DDoubleGDL* V; Guard<BaseGDL> guardV; //used when noNan
381     if (doNan) {
382       V=e->GetParAs<DDoubleGDL>(0)->Dup();guardV.Reset(V);
383     } else {
384       V=e->GetParAs<DDoubleGDL>(0);
385     }
386     //X and its guard
387     DDoubleGDL* X; Guard<BaseGDL> guardX;
388     //Xout and its guard
389     DDoubleGDL* Xout; Guard<BaseGDL> guardXout;
390     // the type of output.
391     DType t=type; //V's type by default.
392     //size of the out value
393     SizeT nout=0;
394     //type of output
395     bool isDouble = false;
396 
397     if (nParam==2) {       // we make X, only V has to be chacked for Nan
398       BaseGDL* p1 = e->GetParDefined(1);
399       if (p1->N_Elements() >1) e->Throw("N must be one positive integer");
400       DLongGDL* n1gdl=e->GetParAs<DLongGDL>(1);
401       nout=(*n1gdl)[0];
402       if (nout < 1) e->Throw("N must be one positive integer");
403       // check V for Nans, error if size is not good enough
404       X=new DDoubleGDL(nV,BaseGDL::INDGEN); //easy
405       guardX.Reset(X);//X will be destroyed on exit
406       if (doNan) {
407         SizeT k =0;
408         for (SizeT i = 0; i < nV; i++) {
409           if (isfinite((*V)[i])) {
410             (*V)[k] = (*V)[i];
411             (*X)[k++] = (*X)[i];
412           }
413         }
414         nV=k;
415         if (nV <nmin) e->Throw("too few non-NAN elements left for this kind of interpolation.");
416       }
417       Xout=new DDoubleGDL(nout,BaseGDL::INDGEN);
418       guardXout.Reset(Xout);//Xout will be destroyed on exit
419       (*Xout)[0]=0; //to not divide by 0  in following loop if nout=1
420 
421       for (SizeT i = 1; i < nout; ++i) {
422         (*Xout)[i] = double(i)*(orig_nV - 1) / (nout - 1);
423       }
424        //is any arg DOUBLE ?
425       isDouble = (p0->Type() == GDL_DOUBLE);
426     } else {
427       BaseGDL* p1 = e->GetParDefined(1);
428       if (p1->N_Elements() != nV) e->Throw("V and X arrays must have same # of elements");
429 
430       // check V and X for Nans, error if size is not good enough
431       if (doNan) {
432         X = e->GetParAs<DDoubleGDL>(1)->Dup(); guardX.Reset(X);//X will be destroyed on exit
433         SizeT k =0;
434         for (SizeT i = 0; i < nV; i++) {
435           if (isfinite((*V)[i]) && isfinite((*X)[i])) {
436             (*V)[k] = (*V)[i];
437             (*X)[k++] = (*X)[i];
438           }
439         }
440         nV=k;
441         if (nV <nmin) e->Throw("too few non-NAN elements left for this kind of interpolation.");
442       } else {
443         X = e->GetParAs<DDoubleGDL>(1);
444       }
445       BaseGDL* p2 = e->GetParDefined(2);
446       nout = p2->N_Elements();
447       DType t = (DTypeOrder[ p0->Type()] > DTypeOrder[ p1->Type()]) ? p0->Type() : p1->Type();
448       t = (DTypeOrder[t] > DTypeOrder[ p2->Type()]) ? t : p2->Type();
449       Xout=e->GetParAs<DDoubleGDL>(2);
450        //is any arg DOUBLE ?
451       isDouble = (p0->Type() == GDL_DOUBLE || p1->Type() == GDL_DOUBLE || p2->Type() == GDL_DOUBLE);
452     }
453     //alloc interpolant object & guard NOW that everything is fine with Arrays!
454     gdl_interpol * myinterp = gdl_interpol_alloc(interpol, nV);
455     GDLGuard<gdl_interpol> ginterpol(myinterp, gdl_interpol_free);
456     //alloc accelerator & guard
457     gsl_interp_accel * acc = gsl_interp_accel_alloc();
458     GDLGuard<gsl_interp_accel> g1(acc, gsl_interp_accel_free);
459     int status=gdl_interpol_init (myinterp, (const double*)X->DataAddr(), (const double*)V->DataAddr() , nV);
460     //allocate result
461     DDoubleGDL* res=new DDoubleGDL(nout,BaseGDL::NOZERO);
462 
463     //compute
464     for (SizeT i=0; i< nout; ++i)  {
465       (*res)[i]=gdl_interpol_eval(myinterp, (const double*)X->DataAddr() , (const double*) V->DataAddr(), (*Xout)[i] , acc);
466     }
467     // if we generated only 1 value, return a scalar instead of an array (IDL behaviour)
468     if(nout == 1) res = new DDoubleGDL((*res)[0]);
469 
470     if (isDouble) return res;
471     else return res->Convert2(GDL_FLOAT, BaseGDL::CONVERT);
472   }
473 }