1 /*********************************************************************
2 Interpolate - Fill blank values in a dataset
3 This is part of GNU Astronomy Utilities (Gnuastro) package.
4 
5 Original author:
6      Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2017-2021, Free Software Foundation, Inc.
9 
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14 
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <gnuastro/list.h>
32 #include <gnuastro/fits.h>
33 #include <gnuastro/blank.h>
34 #include <gnuastro/pointer.h>
35 #include <gnuastro/threads.h>
36 #include <gnuastro/dimension.h>
37 #include <gnuastro/statistics.h>
38 #include <gnuastro/interpolate.h>
39 #include <gnuastro/permutation.h>
40 
41 #include <gnuastro-internal/checkset.h>
42 
43 
44 
45 
46 /*********************************************************************/
47 /********************      Nearest neighbor       ********************/
48 /***************         (Dimension agnostic)         ****************/
49 /*********************************************************************/
50 /* These are bit-flags, so we're using hexadecimal notation. */
51 #define INTERPOLATE_FLAGS_NO          0
52 #define INTERPOLATE_FLAGS_NGB_CHECKED 0x1
53 #define INTERPOLATE_FLAGS_BLANK       0x2
54 
55 
56 
57 
58 
59 /* Parameters for interpolation on threads. */
60 struct interpolate_ngb_params
61 {
62   int                         function;
63   gal_data_t                    *input;
64   size_t                           num;
65   gal_data_t                      *out;
66   gal_data_t                   *blanks;
67   size_t                  numneighbors;
68   uint8_t                *thread_flags;
69   int                        onlyblank;
70   gal_list_void_t            *ngb_vals;
71   float (*metric)(size_t *, size_t *, size_t );
72 
73   struct gal_tile_two_layer_params *tl;
74 };
75 
76 
77 
78 
79 
80 /* Run the interpolation on many threads. */
81 static void *
interpolate_neighbors_on_thread(void * in_prm)82 interpolate_neighbors_on_thread(void *in_prm)
83 {
84   /* Low-level variables that others depend on. */
85   struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
86   struct interpolate_ngb_params *prm=
87     (struct interpolate_ngb_params *)(tprm->params);
88 
89   /* Higher-level variables. */
90   struct gal_tile_two_layer_params *tl=prm->tl;
91   int correct_index=(tl && tl->totchannels>1 && !tl->workoverch);
92   gal_data_t *input=prm->input;
93 
94   /* Rest of variables. */
95   void *nv;
96   float dist, pdist;
97   uint8_t *b, *bf, *bb;
98   gal_list_void_t *tvll;
99   size_t ngb_counter, pind;
100   gal_list_dosizet_t *lQ, *sQ;
101   size_t i, index, fullind, chstart=0, ndim=input->ndim;
102   gal_data_t *tin, *tout, *tnear, *value=NULL, *nearest=NULL;
103   size_t size = (correct_index ? tl->tottilesinch : input->size);
104   size_t *dsize = (correct_index ? tl->numtilesinch : input->dsize);
105   size_t *icoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
106                                       "icoord");
107   size_t *ncoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
108                                       "ncoord");
109   uint8_t *flag, *fullflag=&prm->thread_flags[tprm->id*input->size];
110 
111   /* Based on the above. */
112   size_t *dinc=gal_dimension_increment(ndim, dsize);
113 
114 
115   /* Initialize the flags array. We need two flags during this processing:
116      1) to see if there are blanks. 2) to see if a neighbor has been
117      checked. These are both binary (0 or 1). So to avoid wasting space, we
118      will use bits to store them. We start with only setting the blank flag
119      once for the whole thread. Then for each interpolated pixel, we reset
120      the neighbor-check flag. */
121   flag=fullflag;
122   bb=prm->blanks->array;
123   bf=(b=fullflag)+input->size;
124   do *b = *bb++ ? INTERPOLATE_FLAGS_BLANK : 0; while(++b<bf);
125 
126 
127   /* Put the allocated space to keep the neighbor values into a structure
128      for easy processing. */
129   tin=input;
130   for(tvll=prm->ngb_vals; tvll!=NULL; tvll=tvll->next)
131     {
132       nv=gal_pointer_increment(tvll->v, tprm->id*prm->numneighbors,
133                                input->type);
134       gal_list_data_add_alloc(&nearest, nv, tin->type, 1,
135                               &prm->numneighbors, NULL, 0, -1, 1,
136                               NULL, NULL, NULL);
137       tin=tin->next;
138     }
139   gal_list_data_reverse(&nearest);
140 
141 
142   /* Go over all the points given to this thread. */
143   for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
144     {
145       /* For easy reading. */
146       fullind=tprm->indexs[i];
147 
148 
149       /* If the caller only wanted to interpolate over blank values and
150          this value is not blank (we know from the flags), then just set
151          the output value at this element to the input value and go to the
152          next element. */
153       if(prm->onlyblank && !(fullflag[fullind] & INTERPOLATE_FLAGS_BLANK) )
154         {
155           tin=input;
156           for(tout=prm->out; tout!=NULL; tout=tout->next)
157             {
158               memcpy(gal_pointer_increment(tout->array, fullind, tin->type),
159                      gal_pointer_increment(tin->array,  fullind, tin->type),
160                      gal_type_sizeof(tin->type));
161               tin=tin->next;
162             }
163           continue;
164         }
165 
166 
167       /* Correct the index (if necessary). When the values come from a
168          tiled dataset, the caller might want to interpolate the values of
169          each channel separately (not mix values from different
170          channels). In such a case, the tiles of each channel (and their
171          values in 'input' are contiguous. So we need to correct
172          'tprm->indexs[i]' (which is the index over the whole tessellation,
173          including all channels). */
174       if(correct_index)
175         {
176           /* Index of this tile in its channel. */
177           index = fullind % tl->tottilesinch;
178 
179           /* Index of the first tile in this channel. */
180           chstart = (fullind / tl->tottilesinch) * tl->tottilesinch;
181 
182           /* Set the channel's starting pointer for the flags. */
183           flag = gal_pointer_increment(fullflag, chstart, GAL_TYPE_UINT8);
184         }
185       else
186         {
187           chstart=0;
188           index=fullind;
189         }
190 
191 
192       /* Reset all checked bits in the flags array to 0. */
193       ngb_counter=0;
194       bf=(b=flag)+size;
195       do *b &= ~(INTERPOLATE_FLAGS_NGB_CHECKED); while(++b<bf);
196 
197 
198       /* Get the coordinates of this pixel (to be interpolated). */
199       gal_dimension_index_to_coord(index, ndim, dsize, icoord);
200 
201 
202       /* Start parsing the neighbors. We will use a two-way ordered linked
203          list structure. To start from the nearest and go out to the
204          farthest. */
205       lQ=sQ=NULL;
206       gal_list_dosizet_add(&lQ, &sQ, index, 0.0f);
207       while(sQ)
208         {
209           /* Pop-out (p) an index from the queue: */
210           pind=gal_list_dosizet_pop_smallest(&lQ, &sQ, &pdist);
211 
212           /* If this isn't a blank value then add its values to the list of
213              neighbor values. Note that we didn't check whether the values
214              were blank or not when adding this pixel to the queue. */
215           if( !(flag[pind] & INTERPOLATE_FLAGS_BLANK) )
216             {
217               tin=input;
218               for(tnear=nearest; tnear!=NULL; tnear=tnear->next)
219                 {
220                   memcpy(gal_pointer_increment(tnear->array, ngb_counter,
221                                                tin->type),
222                          gal_pointer_increment(tin->array, chstart+pind,
223                                                tin->type),
224                          gal_type_sizeof(tin->type));
225                   tin=tin->next;
226                 }
227 
228               /* If we have filled all the elements clean up the linked
229                  list and break out. */
230               if(++ngb_counter>=prm->numneighbors)
231                 {
232                   if(lQ) gal_list_dosizet_free(lQ);
233                   break;
234                 }
235             }
236 
237           /* Go over all the neighbors of this popped pixel and add them to
238              the list of neighbors to be checked. */
239           GAL_DIMENSION_NEIGHBOR_OP(pind, ndim, dsize, 1, dinc,
240            {
241              /* Only look at neighbors that have not been checked. VERY
242                 IMPORTANT: we must not check for blank values here,
243                 otherwise we won't be able to parse over extended blank
244                 regions. */
245              if( !(flag[nind] & INTERPOLATE_FLAGS_NGB_CHECKED) )
246                {
247                  /* Get the coordinates of this neighbor. */
248                  gal_dimension_index_to_coord(nind, ndim, dsize, ncoord);
249 
250                  /* Distance of this neighbor to the one to be filled. */
251                  dist=prm->metric(icoord, ncoord, ndim);
252 
253                  /* Add this neighbor to the list. */
254                  gal_list_dosizet_add(&lQ, &sQ, nind, dist);
255 
256                  /* Flag this neighbor as checked. */
257                  flag[nind] |= INTERPOLATE_FLAGS_NGB_CHECKED;
258                }
259            } );
260 
261           /* If there are no more meshes to add to the queue, then this
262              shows, there were not enough points for
263              interpolation. Normally, this loop should only be exited
264              through the 'currentnum>=numnearest' check above. */
265           if(sQ==NULL)
266             error(EXIT_FAILURE, 0, "%s: only %zu neighbors found while "
267                   "you had asked to use %zu neighbors for close neighbor "
268                   "interpolation", __func__, ngb_counter,
269                   prm->numneighbors);
270         }
271 
272       /* Calculate the desired statistic, and write it in the output. */
273       tout=prm->out;
274       for(tnear=nearest; tnear!=NULL; tnear=tnear->next)
275         {
276           /* Find the desired statistic and copy it, but first, reset the
277              flags (which remain from the last time). */
278           tnear->flag &= ~(GAL_DATA_FLAG_SORT_CH | GAL_DATA_FLAG_BLANK_CH);
279           switch(prm->function)
280             {
281             case GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN:
282               value=gal_statistics_minimum(tnear); break;
283               break;
284             case GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX:
285               value=gal_statistics_maximum(tnear); break;
286               break;
287             case GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN:
288               value=gal_statistics_median(tnear, 1); break;
289             default:
290               error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
291                     "to fix the problem. The value %d is not a recognized "
292                     "interpolation function identifier", __func__,
293                     PACKAGE_BUGREPORT, prm->function);
294             }
295           memcpy(gal_pointer_increment(tout->array, fullind, tout->type),
296                  value->array, gal_type_sizeof(tout->type));
297 
298           /* Clean up and go to next array. */
299           gal_data_free(value);
300           tout=tout->next;
301         }
302     }
303 
304 
305   /* Clean up. */
306   for(tnear=nearest; tnear!=NULL; tnear=tnear->next) tnear->array=NULL;
307   gal_list_data_free(nearest);
308   free(icoord);
309   free(ncoord);
310   free(dinc);
311 
312 
313   /* Wait for all the other threads to finish and return. */
314   if(tprm->b) pthread_barrier_wait(tprm->b);
315   return NULL;
316 }
317 
318 
319 
320 
321 
322 /* When no interpolation is needed, then we can just copy the input into
323    the output. */
324 static gal_data_t *
interpolate_copy_input(gal_data_t * input,int aslinkedlist)325 interpolate_copy_input(gal_data_t *input, int aslinkedlist)
326 {
327   gal_data_t *tin, *tout;
328 
329   /* Make a copy of the first input. */
330   tout=gal_data_copy(input);
331   tout->next=NULL;
332 
333   /* If we have a linked list, copy each element. */
334   if(aslinkedlist)
335     {
336       /* Note that we have already copied the first input. */
337       for(tin=input->next; tin!=NULL; tin=tin->next)
338         {
339           /* Copy this dataset (will also copy flags). */
340           tout->next=gal_data_copy(tin);
341           tout=tout->next;
342         }
343 
344       /* Output is the reverse of the input, so reverse it. */
345       gal_list_data_reverse(&tout);
346     }
347 
348   /* Return the copied list. */
349   return tout;
350 }
351 
352 
353 
354 
355 
356 /* Interpolate blank values in an array. If the 'tl!=NULL', then it is
357    assumed that the tile values correspond to given tessellation. Such that
358    'input[i]' corresponds to 'tiles[i]' in the tessellation. */
359 gal_data_t *
gal_interpolate_neighbors(gal_data_t * input,struct gal_tile_two_layer_params * tl,uint8_t metric,size_t numneighbors,size_t numthreads,int onlyblank,int aslinkedlist,int function)360 gal_interpolate_neighbors(gal_data_t *input,
361                           struct gal_tile_two_layer_params *tl,
362                           uint8_t metric, size_t numneighbors,
363                           size_t numthreads, int onlyblank,
364                           int aslinkedlist, int function)
365 {
366   gal_data_t *tin, *tout;
367   struct interpolate_ngb_params prm;
368   size_t ngbvnum=numthreads*numneighbors;
369   int permute=(tl && tl->totchannels>1 && tl->workoverch);
370 
371 
372   /* If there are no blank values in the array, AND we should only fill
373      blank values, then simply copy the input and abort. */
374   if( (input->flag | GAL_DATA_FLAG_BLANK_CH)   /* Zero bit is meaningful.*/
375       && !(input->flag | GAL_DATA_FLAG_HASBLANK)/* There are no blanks.  */
376       && onlyblank )                           /* Only interpolate blank.*/
377     return interpolate_copy_input(input, aslinkedlist);
378 
379 
380   /* Initialize the constant parameters. */
381   prm.tl           = tl;
382   prm.ngb_vals     = NULL;
383   prm.input        = input;
384   prm.function     = function;
385   prm.onlyblank    = onlyblank;
386   prm.numneighbors = numneighbors;
387   prm.num          = aslinkedlist ? gal_list_data_number(input) : 1;
388 
389 
390   /* Set the metric. */
391   switch(metric)
392     {
393     case GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL:
394       prm.metric=gal_dimension_dist_radial;
395       break;
396     case GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN:
397       prm.metric=gal_dimension_dist_manhattan;
398       break;
399     default:
400       error(EXIT_FAILURE, 0, "%s: %d is not a valid metric identifier",
401             __func__, metric);
402     }
403 
404 
405   /* Flag the blank values. */
406   prm.blanks=gal_blank_flag(input);
407 
408 
409   /* If the input is from a tile structure and the user has asked to ignore
410      channels, then re-order the values. */
411   if(permute)
412     {
413       /* Prepare the permutation (if necessary/not already defined). */
414       gal_tile_full_permutation(tl);
415 
416       /* Re-order values to ignore channels (if necessary). */
417       gal_permutation_apply(input, tl->permutation);
418       gal_permutation_apply(prm.blanks, tl->permutation);
419 
420       /* If this is a linked list, then permute remaining nodes. */
421       if(aslinkedlist)
422         for(tin=input->next; tin!=NULL; tin=tin->next)
423           gal_permutation_apply(tin, tl->permutation);
424     }
425 
426 
427   /* Allocate space for the (first) output. */
428   prm.out=gal_data_alloc(NULL, input->type, input->ndim, input->dsize,
429                          input->wcs, 0, input->minmapsize,
430                          input->quietmmap, NULL, input->unit, NULL);
431   gal_list_void_add(&prm.ngb_vals,
432                     gal_pointer_allocate(input->type, ngbvnum, 0, __func__,
433                                          "prm.ngb_vals"));
434 
435 
436   /* If we are given a list of datasets, make the necessary
437      allocations. The reason we are doing this after a check of
438      'aslinkedlist' is that the 'input' might have a 'next' element, but
439      the caller might not have called 'aslinkedlist'. */
440   prm.out->next=NULL;
441   if(aslinkedlist)
442     for(tin=input->next; tin!=NULL; tin=tin->next)
443       {
444         /* A small sanity check. */
445         if( gal_dimension_is_different(input, tin) )
446           error(EXIT_FAILURE, 0, "%s: all datasets in the list must have "
447                 "the same dimension and size", __func__);
448 
449         /* Allocate the output array for this node. */
450         gal_list_data_add_alloc(&prm.out, NULL, tin->type, tin->ndim,
451                                 tin->dsize, tin->wcs, 0, tin->minmapsize,
452                                 tin->quietmmap, NULL, tin->unit, NULL);
453 
454         /* Allocate the space for the neighbor values of this input. */
455         gal_list_void_add(&prm.ngb_vals,
456                           gal_pointer_allocate(tin->type, ngbvnum, 0,
457                                                __func__, "prm.ngb_vals"));
458       }
459   gal_list_data_reverse(&prm.out);
460   gal_list_void_reverse(&prm.ngb_vals);
461 
462 
463   /* Allocate space for all the flag values of all the threads here (memory
464      in each thread is limited) and this is cleaner. */
465   prm.thread_flags=gal_pointer_allocate(GAL_TYPE_UINT8,
466                                         numthreads*input->size, 0, __func__,
467                                         "prm.thread_flags");
468 
469 
470   /* Spin off the threads. */
471   gal_threads_spin_off(interpolate_neighbors_on_thread, &prm,
472                        input->size, numthreads, input->minmapsize,
473                        input->quietmmap);
474 
475 
476   /* If the values were permuted for the interpolation, then re-order the
477      values back to their original location (so they correspond to their
478      tile indexs. */
479   if(permute)
480     {
481       gal_permutation_apply_inverse(input, tl->permutation);
482       for(tout=prm.out; tout!=NULL; tout=tout->next)
483         gal_permutation_apply_inverse(tout, tl->permutation);
484     }
485 
486 
487   /* The interpolated array doesn't have blank values. So set the blank
488      flag to 0 and set the use-zero to 1. */
489   for(tout=prm.out; tout!=NULL; tout=tout->next)
490     {
491       tout->flag |= GAL_DATA_FLAG_BLANK_CH;
492       tout->flag &= ~GAL_DATA_FLAG_HASBLANK;
493     }
494 
495 
496   /* Clean up and return. */
497   free(prm.thread_flags);
498   gal_data_free(prm.blanks);
499   gal_list_void_free(prm.ngb_vals, 1);
500   return prm.out;
501 }
502 
503 
504 
505 
506 
507 
508 
509 
510 
511 
512 
513 
514 
515 
516 
517 
518 
519 
520 
521 
522 /*********************************************************************/
523 /********************          1D on grid         ********************/
524 /*********************************************************************/
525 gsl_spline *
gal_interpolate_1d_make_gsl_spline(gal_data_t * X,gal_data_t * Y,int type_1d)526 gal_interpolate_1d_make_gsl_spline(gal_data_t *X, gal_data_t *Y, int type_1d)
527 {
528   size_t i, c;
529   double *x, *y;
530   gal_data_t *Xd, *Yd;
531   gsl_spline *spline=NULL;
532   const gsl_interp_type *itype=NULL;
533   int Yhasblank=gal_blank_present(Y, 0);
534 
535   /* A small sanity check. */
536   if(Y->ndim!=1)
537     error(EXIT_FAILURE, 0, "%s: input dataset is not 1D (it is %zuD)",
538           __func__, Y->ndim);
539   if(X)
540     {
541       if( gal_dimension_is_different(X, Y) )
542         error(EXIT_FAILURE, 0, "%s: when two inputs are given, they must "
543               "have the same dimensions. X has %zu elements, while Y has "
544               "%zu", __func__, X->size, Y->size);
545       if(gal_blank_present(X, 0))
546         error(EXIT_FAILURE, 0, "%s: the X dataset has blank elements",
547               __func__);
548     }
549 
550   /* Set the interpolation type. */
551   switch(type_1d)
552     {
553     case GAL_INTERPOLATE_1D_LINEAR:
554       itype=gsl_interp_linear;           break;
555     case GAL_INTERPOLATE_1D_POLYNOMIAL:
556       itype=gsl_interp_polynomial;       break;
557     case GAL_INTERPOLATE_1D_CSPLINE:
558       itype=gsl_interp_cspline;          break;
559     case GAL_INTERPOLATE_1D_CSPLINE_PERIODIC:
560       itype=gsl_interp_cspline_periodic; break;
561     case GAL_INTERPOLATE_1D_AKIMA:
562       itype=gsl_interp_akima;            break;
563     case GAL_INTERPOLATE_1D_AKIMA_PERIODIC:
564       itype=gsl_interp_akima_periodic;   break;
565     case GAL_INTERPOLATE_1D_STEFFEN:
566 #if HAVE_DECL_GSL_INTERP_STEFFEN
567       itype=gsl_interp_steffen;          break;
568 #else
569       error(EXIT_FAILURE, 0, "%s: Steffen interpolation isn't available "
570             "in the system's GNU Scientific Library (GSL). Please install "
571             "a more recent GSL (version >= 2.0, released in October 2015) "
572             "and rebuild Gnuastro", __func__);
573 #endif
574     default:
575       error(EXIT_FAILURE, 0, "%s: code %d not recognizable for the GSL "
576             "interpolation type", __func__, type_1d);
577     }
578 
579   /* Initializations. Note that if Y doesn't have any blank elements and is
580      already in 'double' type, then we don't need to make a copy. */
581   Yd = ( (Yhasblank || Y->type!=GAL_TYPE_FLOAT64)
582          ? gal_data_copy_to_new_type(Y, GAL_TYPE_FLOAT64)
583          : Y );
584   Xd = ( X
585          /* Has to be 'Yhasblank', we KNOW X doesn't have blank values. */
586          ? ( (Yhasblank || X->type!=GAL_TYPE_FLOAT64)
587              ? gal_data_copy_to_new_type(X, GAL_TYPE_FLOAT64)
588              : X )
589          : gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, Y->dsize, NULL,
590                           0, -1, 1, NULL, NULL, NULL) );
591 
592   /* Fill in the X axis values while also removing NaN/blank elements. */
593   c=0;
594   x=Xd->array;
595   y=Yd->array;
596   for(i=0;i<Yd->size;++i)
597     if( !isnan(y[i]) )
598       {
599         y[ c   ] = y[i];
600 	x[ c++ ] = X ? x[i] : i;
601       }
602 
603   /* Make sure we have enough valid points for interpolation. */
604   if( c>=gsl_interp_type_min_size(itype) )
605     {
606       spline=gsl_spline_alloc(itype, c);
607       gsl_spline_init(spline, x, y, c);
608     }
609   else
610     spline=NULL;
611 
612   /* Clean up and return. */
613   if(Xd!=X) gal_data_free(Xd);
614   if(Yd!=Y) gal_data_free(Yd);
615   return spline;
616 }
617 
618 
619 
620 
621 
622 /* Return 0 if all blanks were filled. */
623 static int
interpolate_1d_blank_write(gal_data_t * in,gsl_spline * spline,gsl_interp_accel * acc)624 interpolate_1d_blank_write(gal_data_t *in, gsl_spline *spline,
625 			   gsl_interp_accel *acc)
626 {
627   double tmp;
628   int hasblank=0;
629   uint8_t  *su8 =in->array, *u8 =in->array, *u8f =u8 +in->size;
630   int8_t   *si8 =in->array, *i8 =in->array, *i8f =i8 +in->size;
631   uint16_t *su16=in->array, *u16=in->array, *u16f=u16+in->size;
632   int16_t  *si16=in->array, *i16=in->array, *i16f=i16+in->size;
633   uint32_t *su32=in->array, *u32=in->array, *u32f=u32+in->size;
634   int32_t  *si32=in->array, *i32=in->array, *i32f=i32+in->size;
635   uint64_t *su64=in->array, *u64=in->array, *u64f=u64+in->size;
636   int64_t  *si64=in->array, *i64=in->array, *i64f=i64+in->size;
637   float    *sf32=in->array, *f32=in->array, *f32f=f32+in->size;
638   double   *sf64=in->array, *f64=in->array, *f64f=f64+in->size;
639 
640   switch(in->type)
641     {
642     case GAL_TYPE_UINT8:
643       do
644         if(*u8==GAL_BLANK_UINT8)
645           {
646             /* If the evaluation is good, this function will return 0. */
647             if( gsl_spline_eval_e(spline, u8-su8, acc, &tmp)==0 )
648               *u8=tmp;
649             else hasblank=1;
650           }
651       while(++u8<u8f);
652       break;
653     case GAL_TYPE_INT8:
654       do
655         if(*i8==GAL_BLANK_INT8)
656           {
657             if( gsl_spline_eval_e(spline, i8-si8, acc, &tmp)==0 )
658               *u16=tmp;
659             else hasblank=1;
660           }
661       while(++i8<i8f);
662       break;
663     case GAL_TYPE_UINT16:
664       do
665         if(*u16==GAL_BLANK_UINT16)
666           {
667             if( gsl_spline_eval_e(spline, u16-su16, acc, &tmp)==0 )
668               *u16=tmp;
669             else hasblank=1;
670           }
671       while(++u16<u16f);
672       break;
673     case GAL_TYPE_INT16:
674       do
675         if(*i16==GAL_BLANK_INT16)
676           {
677             if( gsl_spline_eval_e(spline, i16-si16, acc, &tmp)==0 )
678               *i16=tmp;
679             else hasblank=1;
680           }
681       while(++i16<i16f);
682       break;
683     case GAL_TYPE_UINT32:
684       do
685         if(*u32==GAL_BLANK_UINT32)
686           {
687             if( gsl_spline_eval_e(spline, u32-su32, acc, &tmp)==0 )
688               *u32=tmp;
689             else hasblank=1;
690           }
691       while(++u32<u32f);
692       break;
693     case GAL_TYPE_INT32:
694       do
695         if(*i32==GAL_BLANK_INT32)
696           {
697             if( gsl_spline_eval_e(spline, i32-si32, acc, &tmp)==0 )
698               *i32=tmp;
699             else hasblank=1;
700           }
701       while(++i32<i32f);
702       break;
703     case GAL_TYPE_UINT64:
704       do
705         if(*u64==GAL_BLANK_UINT64)
706           {
707             if( gsl_spline_eval_e(spline, u64-su64, acc, &tmp)==0 )
708               *u64=tmp;
709             else hasblank=1;
710           }
711       while(++u64<u64f);
712       break;
713     case GAL_TYPE_INT64:
714       do
715         if(*i64==GAL_BLANK_INT64)
716           {
717             if( gsl_spline_eval_e(spline, i64-si64, acc, &tmp)==0 )
718               *i64=tmp;
719             else hasblank=1;
720           }
721       while(++i64<i64f);
722       break;
723     case GAL_TYPE_FLOAT32:
724       do
725         if(isnan(*f32))
726           {
727             if( gsl_spline_eval_e(spline, f32-sf32, acc, &tmp)==0 )
728               *f32=tmp;
729             else hasblank=1;
730           }
731       while(++f32<f32f);
732       break;
733     case GAL_TYPE_FLOAT64:
734       do
735         if(isnan(*f64))
736           {
737             if( gsl_spline_eval_e(spline, f64-sf64, acc, f64) )
738               hasblank=1;
739           }
740       while(++f64<f64f);
741       break;
742     default:
743       error(EXIT_FAILURE, 0, "%s: code %d is not a recognized data type",
744 	    __func__, in->type);
745     }
746   return hasblank;
747 }
748 
749 
750 
751 
752 
753 void
gal_interpolate_1d_blank(gal_data_t * in,int type_1d)754 gal_interpolate_1d_blank(gal_data_t *in, int type_1d)
755 {
756   int hasblank;
757   gsl_spline *spline;
758   gsl_interp_accel *acc;
759 
760   /* If there are no blank elements, just return. */
761   if(!gal_blank_present(in, 1)) return;
762 
763   /* Initialize the necessary structures. */
764   spline=gal_interpolate_1d_make_gsl_spline(NULL, in, type_1d);
765 
766   /* If any interpolation structure was actually made. */
767   if(spline)
768     {
769       /* Write the values in the blank elements. */
770       acc=gsl_interp_accel_alloc();
771       hasblank=interpolate_1d_blank_write(in, spline, acc);
772 
773       /* For a check.
774       {
775         size_t i;
776         double *d;
777         gal_data_t *check=gal_data_copy_to_new_type(in, GAL_TYPE_FLOAT64);
778         d=check->array;
779         for(i=0;i<check->size;++i)
780           printf("%-10zu%f\n", i, d[i]);
781         gal_data_free(check);
782       }
783       */
784 
785       /* Set the blank flags, note that 'GAL_DATA_FLAG_BLANK_CH' is already set
786          by the top call to 'gal_blank_present'. */
787       if(hasblank)
788         in->flag |=  GAL_DATA_FLAG_HASBLANK;
789       else
790         in->flag &= ~GAL_DATA_FLAG_HASBLANK;
791 
792       /* Clean up. */
793       gsl_spline_free(spline);
794       gsl_interp_accel_free(acc);
795     }
796 }
797