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