1 /**
2  * @file sphere_part.c
3  *
4  * @copyright Copyright  (C)  2014 Moritz Hanke <hanke@dkrz.de>
5  *                                 Thomas Jahns <jahns@dkrz.de>
6  *
7  * @version 1.0
8  * @author Moritz Hanke <hanke@dkrz.de>
9  *         Thomas Jahns <jahns@dkrz.de>
10  */
11 /*
12  * Keywords:
13  * Maintainer: Moritz Hanke <hanke@dkrz.de>
14  *             Rene Redler <rene.redler@mpimet.mpg.de>
15  * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
16  *
17  * This file is part of YAC.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are  permitted provided that the following conditions are
21  * met:
22  *
23  * Redistributions of source code must retain the above copyright notice,
24  * this list of conditions and the following disclaimer.
25  *
26  * Redistributions in binary form must reproduce the above copyright
27  * notice, this list of conditions and the following disclaimer in the
28  * documentation and/or other materials provided with the distribution.
29  *
30  * Neither the name of the DKRZ GmbH nor the names of its contributors
31  * may be used to endorse or promote products derived from this software
32  * without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
35  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
36  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
37  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
38  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
39  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
40  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
41  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
42  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
43  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
44  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45  */
46 
47 #ifdef HAVE_CONFIG_H
48 // Get the definition of the 'restrict' keyword.
49 #include "config.h"
50 #endif
51 
52 #include <assert.h>
53 #include <stdlib.h>
54 #include <math.h>
55 #include <string.h>
56 #include <stdint.h>
57 
58 #include "sphere_part.h"
59 #include "geometry.h"
60 #include "grid.h"
61 #include "interval_tree.h"
62 #include "utils.h"
63 #include "ensure_array_size.h"
64 
65 union I_list {
66   struct {
67     struct interval_node * head_node;
68     size_t num_nodes;
69   } ivt;
70   size_t *list;
71 };
72 
73 enum {
74    I_list_tree_min_size = 2, //!< make I list into tree when list is
75                              //!<  larger than this
76 };
77 enum {
78   U_FLAG = 1,
79   T_FLAG = 2,
80 };
81 
82 enum yac_node_flags {
83    U_IS_LEAF = 1,
84    T_IS_LEAF = 2,
85    I_IS_INTERVAL_TREE = 4,
86 };
87 
88 struct sphere_part_node {
89 
90    int flags;
91 
92    union I_list I;
93    void * U, * T;
94 
95    size_t I_size, U_size, T_size;
96 
97    struct sin_cos_angle I_angle;
98 
99    double gc_norm_vector[3];
100 };
101 
102 struct bnd_sphere_part_search {
103   struct sphere_part_node base_node;
104   size_t * ids;
105 };
106 
107 enum node_type {
108   I_NODE = 0,
109   U_NODE = 1,
110   T_NODE = 2,
111 };
112 
113 struct temp_partition_data {
114   size_t local_id;
115   struct bounding_circle bnd_circle;
116   int node_type;
117 };
118 
119 struct point_id_xyz {
120   size_t idx; // index of the point in the coordinates array passed to
121               // the constructor
122   double coordinates_xyz[3];
123 };
124 
125 struct point_id_xyz_angle {
126   size_t idx; // index of the point in the coordinates array passed to
127               // the constructor
128   double coordinates_xyz[3];
129   double cos_angle;
130 };
131 
132 struct point_sphere_part_node {
133 
134    int flags;
135 
136    void * U, * T;
137 
138    size_t U_size, T_size;
139 
140    double gc_norm_vector[3];
141 };
142 
143 struct point_sphere_part_search {
144 
145    struct point_sphere_part_node base_node;
146    struct point_id_xyz * points;
147    size_t max_tree_depth;
148 };
149 
150 struct point_id_xyz_int32 {
151   size_t idx;
152   int32_t coordinate_xyz[3];
153 };
154 
init_sphere_part_node(struct sphere_part_node * node)155 static void init_sphere_part_node(struct sphere_part_node * node) {
156 
157    node->flags = 0;
158    node->I_size = 0;
159    node->U_size = 0;
160    node->T_size = 0;
161    node->gc_norm_vector[0] = 0;
162    node->gc_norm_vector[1] = 0;
163    node->gc_norm_vector[2] = 1;
164 }
165 
get_sphere_part_node()166 static struct sphere_part_node * get_sphere_part_node() {
167 
168    struct sphere_part_node * node = xmalloc(1 * sizeof(*node));
169 
170    init_sphere_part_node(node);
171 
172    return node;
173 }
174 
compare_temp_partition_data(const void * a,const void * b)175 static int compare_temp_partition_data(const void * a, const void * b) {
176 
177   return ((const struct temp_partition_data *)a)->node_type -
178          ((const struct temp_partition_data *)b)->node_type;
179 }
180 
partition_data(size_t * local_cell_ids,struct temp_partition_data * part_data,size_t num_cell_ids,size_t threshold,struct sphere_part_node * parent_node,double prev_gc_norm_vector[])181 static void partition_data (
182    size_t * local_cell_ids, struct temp_partition_data * part_data,
183    size_t num_cell_ids, size_t threshold, struct sphere_part_node * parent_node,
184    double prev_gc_norm_vector[]) {
185 
186    if (num_cell_ids == 0) {
187      parent_node->flags = U_IS_LEAF + T_IS_LEAF;
188      return;
189    }
190 
191    double balance_point[3] = {0.0,0.0,0.0};
192 
193    // compute balance point
194    for (size_t i = 0; i < num_cell_ids; ++i) {
195 
196      balance_point[0] += part_data[i].bnd_circle.base_vector[0];
197      balance_point[1] += part_data[i].bnd_circle.base_vector[1];
198      balance_point[2] += part_data[i].bnd_circle.base_vector[2];
199    }
200 
201    normalise_vector(balance_point);
202 
203    // compute the great circle that partitions the data in half (more or less)
204 
205    crossproduct_ld(
206       balance_point, prev_gc_norm_vector, parent_node->gc_norm_vector);
207    normalise_vector(parent_node->gc_norm_vector);
208 
209    // partition data into cells that overlap with the great circle and cells
210    // that are on side of the circle
211 
212    size_t I_size = 0;
213    size_t U_size = 0;
214    size_t T_size = 0;
215 
216    struct sin_cos_angle max_inc_angle = SIN_COS_ZERO;
217 
218    for (size_t i = 0; i < num_cell_ids; ++i) {
219 
220       struct bounding_circle curr_bnd_circle = part_data[i].bnd_circle;
221 
222       // get angle between the norm vector of the great circle and the base
223       // point of the bounding circle
224       struct sin_cos_angle angle =
225         get_vector_angle_2(
226           curr_bnd_circle.base_vector, parent_node->gc_norm_vector);
227 
228       // get the angle between between the plane of the great circle and base
229       // point of the bounding circle
230       struct sin_cos_angle diff_angle_gc =
231         sin_cos_angle_new(fabs(angle.cos), angle.sin);
232 
233       // if the point intersects with the great circle
234       if (compare_angles(diff_angle_gc, curr_bnd_circle.inc_angle) <= 0) {
235 
236          // set node type for current cell
237          part_data[i].node_type = I_NODE;
238          I_size++;
239 
240          struct sin_cos_angle inc_angle =
241             sum_angles_no_check(diff_angle_gc, curr_bnd_circle.inc_angle);
242 
243          if (compare_angles(max_inc_angle, inc_angle) < 0)
244             max_inc_angle = inc_angle;
245 
246       // angle > M_PI_2
247       } else if (angle.cos < 0.0) {
248 
249          // set node type for current cell
250          part_data[i].node_type = U_NODE;
251          U_size++;
252 
253       } else {
254 
255          // set node type for current cell
256          part_data[i].node_type = T_NODE;
257          T_size++;
258       }
259    }
260 
261    qsort(
262       part_data, num_cell_ids, sizeof(*part_data), compare_temp_partition_data);
263 
264    parent_node->I_size = I_size;
265    parent_node->U_size = U_size;
266    parent_node->T_size = T_size;
267 
268    // if max_inc_angle > PI/2
269    if (compare_angles(max_inc_angle, SIN_COS_M_PI_2) >= 0) {
270       parent_node->I_angle = SIN_COS_M_PI_2;
271    } else {
272       parent_node->I_angle = max_inc_angle;
273    }
274 
275    if (I_size > 0) {
276 
277       if (I_size > I_list_tree_min_size) {
278 
279          assert(sizeof(struct interval_node) > sizeof(size_t));
280          struct interval_node * head_node = xmalloc(I_size * sizeof(*head_node));
281          parent_node->I.ivt.head_node = head_node;
282          parent_node->I.ivt.num_nodes = I_size;
283 
284          for (size_t i = 0; i < I_size; ++i) {
285 
286             struct sin_cos_angle base_angle, corrected_inc_angle;
287             int big_sum, neg;
288             double GCp[3], bVp[3];
289             struct bounding_circle curr_bnd_circle = part_data[i].bnd_circle;
290 
291             crossproduct_ld(parent_node->gc_norm_vector,
292                             curr_bnd_circle.base_vector, GCp);
293             crossproduct_ld(GCp, parent_node->gc_norm_vector, bVp);
294             normalise_vector(bVp);
295             base_angle = get_vector_angle_2(bVp, prev_gc_norm_vector);
296             big_sum =
297               sum_angles(curr_bnd_circle.inc_angle,
298                          get_vector_angle_2(bVp, curr_bnd_circle.base_vector),
299                          &corrected_inc_angle);
300 
301             // if the angle is bigger then PI
302             if ((big_sum) || (corrected_inc_angle.sin < 0.0))
303               corrected_inc_angle = SIN_COS_M_PI;
304 
305             struct sin_cos_angle left, right;
306             // base_angle - corrected_inc_angle
307             neg = sub_angles(base_angle, corrected_inc_angle, &left);
308             // base_angle + corrected_inc_angle
309             big_sum = sum_angles(base_angle, corrected_inc_angle, &right);
310 
311             head_node[i].range.left = compute_angle(left) - (neg?2.0*M_PI:0.0);
312             head_node[i].range.right = compute_angle(right) +
313                                        (big_sum?2.0*M_PI:0.0);
314             head_node[i].value = part_data[i].local_id;
315          }
316 
317          yac_generate_interval_tree(head_node, I_size);
318          parent_node->flags |= I_IS_INTERVAL_TREE;
319       } else {
320          for (size_t i = 0; i < I_size; ++i)
321             local_cell_ids[i] = part_data[i].local_id;
322          parent_node->I.list = (void*)local_cell_ids;
323       }
324    } else
325       parent_node->I.list = NULL;
326 
327    part_data += I_size;
328    local_cell_ids += I_size;
329 
330    // check whether the lists are small enough (if not -> partition again)
331    if (U_size <= threshold) {
332 
333       for (size_t i = 0; i < U_size; ++i)
334          local_cell_ids[i] = part_data[i].local_id;
335       parent_node->U = (void*)local_cell_ids;
336       parent_node->flags |= U_IS_LEAF;
337 
338    } else {
339 
340       parent_node->U = get_sphere_part_node();
341       partition_data(local_cell_ids, part_data, U_size, threshold,
342                      parent_node->U, parent_node->gc_norm_vector);
343    }
344    local_cell_ids += U_size;
345    part_data += U_size;
346 
347    if (T_size <= threshold) {
348 
349       for (size_t i = 0; i < T_size; ++i)
350          local_cell_ids[i] = part_data[i].local_id;
351       parent_node->T = (void*)local_cell_ids;
352       parent_node->flags |= T_IS_LEAF;
353       local_cell_ids += T_size;
354 
355    } else {
356 
357       parent_node->T = get_sphere_part_node();
358       partition_data(local_cell_ids, part_data, T_size, threshold,
359                      parent_node->T, parent_node->gc_norm_vector);
360    }
361 }
362 
compare_point_idx_xyz(void const * a,void const * b)363 static int compare_point_idx_xyz(void const * a, void const * b) {
364   return (((struct point_id_xyz *)a)->idx > ((struct point_id_xyz *)b)->idx) -
365          (((struct point_id_xyz *)a)->idx < ((struct point_id_xyz *)b)->idx);
366 }
367 
partition_point_data(struct point_id_xyz * points,size_t num_points,size_t threshold,double prev_gc_norm_vector[],size_t curr_tree_depth,size_t * max_tree_depth)368 static struct point_sphere_part_node * partition_point_data (
369   struct point_id_xyz * points, size_t num_points, size_t threshold,
370   double prev_gc_norm_vector[], size_t curr_tree_depth,
371   size_t * max_tree_depth) {
372 
373   if (curr_tree_depth > *max_tree_depth) *max_tree_depth = curr_tree_depth;
374 
375   struct point_sphere_part_node * node = xmalloc(1 * sizeof(*node));
376 
377   double balance_point[3] = {0.0,0.0,0.0};
378 
379   // compute balance point
380 
381   for (size_t i = 0; i < num_points; ++i) {
382 
383     double * curr_coordinates_xyz = &(points[i].coordinates_xyz[0]);
384     balance_point[0] += curr_coordinates_xyz[0];
385     balance_point[1] += curr_coordinates_xyz[1];
386     balance_point[2] += curr_coordinates_xyz[2];
387   }
388 
389   normalise_vector(balance_point);
390 
391   // compute the great circle that partitions the data in half (more or less)
392 
393   double * gc_norm_vector = &(node->gc_norm_vector[0]);
394   crossproduct_ld(balance_point, prev_gc_norm_vector, gc_norm_vector);
395   normalise_vector(gc_norm_vector);
396 
397   // angle between a point and the great circle plane
398   // acos(dot(gc_norm_vector, point_xyz)) = angle(gc_norm_vector, point_xyz)
399   // acos(dot(gc_norm_vector, point_xyz)) - PI/2 = angle(gc_plane, point_xyz)
400   // dot <= 0.0    -> U list
401   // dot >  0.0    -> T list
402 
403   struct point_id_xyz * left = points, * right = points + num_points - 1;
404 
405   // sort such that all points for the U list come first, followed by the
406   // elements of the T list
407   while (1) {
408     // find element that does not belong into U-list
409     while (left <= right) {
410       double * curr_coordinates_xyz = &(left->coordinates_xyz[0]);
411       double dot = curr_coordinates_xyz[0] * gc_norm_vector[0] +
412                    curr_coordinates_xyz[1] * gc_norm_vector[1] +
413                    curr_coordinates_xyz[2] * gc_norm_vector[2];
414 
415       // if (angle < M_PI_2)
416       if (dot > 0.0) break;
417       ++left;
418     };
419 
420     // find element that does not belong into T-list
421     while (left < right) {
422       double * curr_coordinates_xyz = &(right->coordinates_xyz[0]);
423       double dot = curr_coordinates_xyz[0] * gc_norm_vector[0] +
424                    curr_coordinates_xyz[1] * gc_norm_vector[1] +
425                    curr_coordinates_xyz[2] * gc_norm_vector[2];
426 
427       // if (angle >= M_PI_2)
428       if (dot <= 0.0) break;
429       --right;
430     }
431 
432     if (left < right) {
433       struct point_id_xyz tmp_point = *left;
434       *left = *right;
435       *right = tmp_point;
436       ++left;
437       --right;
438     } else {
439       break;
440     }
441   }
442 
443   size_t U_size = left - points;
444   size_t T_size = num_points - U_size;
445 
446   node->U_size = U_size;
447   node->T_size = T_size;
448   node->flags = 0;
449 
450   // check whether the lists are small enough (if not -> partition again)
451   if ((U_size <= threshold) ||(U_size == num_points)) {
452 
453     node->U = points;
454     node->flags |= U_IS_LEAF;
455     qsort(points, U_size, sizeof(*points), compare_point_idx_xyz);
456 
457   } else {
458 
459     node->U = partition_point_data(points, U_size, threshold, gc_norm_vector,
460                                    curr_tree_depth + 1, max_tree_depth);
461   }
462 
463   if ((T_size <= threshold) || (T_size == num_points)) {
464 
465     node->T = points + U_size;
466     node->flags |= T_IS_LEAF;
467     qsort(points + U_size, T_size, sizeof(*points), compare_point_idx_xyz);
468 
469   } else {
470 
471     node->T =
472       partition_point_data(points + U_size, T_size, threshold, gc_norm_vector,
473                            curr_tree_depth + 1, max_tree_depth);
474   }
475 
476   return node;
477 }
478 
yac_bnd_sphere_part_search_new(struct bounding_circle * circles,size_t num_circles)479 struct bnd_sphere_part_search * yac_bnd_sphere_part_search_new(
480   struct bounding_circle * circles, size_t num_circles) {
481 
482   struct bnd_sphere_part_search * search = xmalloc(1 * sizeof(*search));
483 
484   double gc_norm_vector[3] = {0.0,0.0,1.0};
485 
486   init_sphere_part_node(&(search->base_node));
487 
488   size_t * ids = xmalloc(num_circles * sizeof(*ids));
489   search->ids = ids;
490 
491   struct temp_partition_data * part_data =
492     xmalloc(num_circles * sizeof(*part_data));
493    for (size_t i = 0; i < num_circles; ++i) {
494       part_data[i].bnd_circle = circles[i];
495       part_data[i].local_id = i;
496    }
497 
498    partition_data(ids, part_data, num_circles, I_list_tree_min_size,
499                   &(search->base_node), gc_norm_vector);
500 
501    free(part_data);
502 
503    return search;
504 }
505 
compare_points_int32_coord(const void * a,const void * b)506 static inline int compare_points_int32_coord(
507   const void * a,const void * b) {
508 
509   struct point_id_xyz_int32 * point_a = (struct point_id_xyz_int32 *)a;
510   struct point_id_xyz_int32 * point_b = (struct point_id_xyz_int32 *)b;
511 
512   int ret;
513 
514   for (int i = 0; i < 3; ++i)
515     if ((ret = (point_a->coordinate_xyz[i] > point_b->coordinate_xyz[i]) -
516                (point_a->coordinate_xyz[i] < point_b->coordinate_xyz[i])))
517       return ret;
518   return 0;
519 }
520 
521 static struct point_id_xyz *
get_unique_points(size_t * num_points,coordinate_pointer coordinates_xyz,yac_int const * ids,int const * mask)522   get_unique_points(
523     size_t * num_points, coordinate_pointer coordinates_xyz,
524     yac_int const * ids, int const * mask) {
525 
526   struct point_id_xyz_int32 * points_int32 =
527     xmalloc(*num_points * sizeof(*points_int32));
528 
529   double const scale = (double)(2 << 21);
530 
531   size_t num_unmasked_points;
532 
533   if (mask == NULL) {
534     num_unmasked_points = *num_points;
535     for (size_t i = 0; i < num_unmasked_points; ++i) {
536 
537       points_int32[i].idx = i;
538       for (size_t j = 0; j < 3; ++j)
539         points_int32[i].coordinate_xyz[j] =
540           (int32_t)round(coordinates_xyz[i][j] * scale);
541     }
542   } else {
543     num_unmasked_points = 0;
544     for (size_t i = 0; i < *num_points; ++i) {
545 
546       if (!mask[i]) continue;
547       points_int32[num_unmasked_points].idx = i;
548       for (size_t j = 0; j < 3; ++j)
549         points_int32[num_unmasked_points].coordinate_xyz[j] =
550           (int32_t)round(coordinates_xyz[i][j] * scale);
551       num_unmasked_points++;
552     }
553   }
554 
555   // sort points
556   qsort(points_int32, num_unmasked_points,
557         sizeof(*points_int32), compare_points_int32_coord);
558 
559   struct point_id_xyz_int32 dummy;
560   dummy.idx = SIZE_MAX;
561   dummy.coordinate_xyz[0] = INT32_MAX;
562   dummy.coordinate_xyz[1] = INT32_MAX;
563   dummy.coordinate_xyz[2] = INT32_MAX;
564   struct point_id_xyz_int32 * prev = &dummy, * curr = points_int32;
565   yac_int prev_id = XT_INT_MAX;
566   size_t new_num_points = 0;
567   for (size_t i = 0; i < num_unmasked_points; ++i, ++curr) {
568 
569     size_t curr_idx = curr->idx;
570     if (compare_points_int32_coord(prev, curr)) {
571       prev = points_int32 + new_num_points++;
572       if (prev != curr) *prev = *curr;
573       prev_id = ids[curr_idx];
574     } else {
575       yac_int curr_id = ids[curr_idx];
576       if (curr_id > prev_id) {
577         prev_id = curr_id;
578         prev->idx = curr_idx;
579       }
580     }
581   }
582 
583   struct point_id_xyz * points = xmalloc(new_num_points * sizeof(*points));
584   for (size_t i = 0; i < new_num_points; ++i) {
585     size_t curr_idx = points_int32[i].idx;
586     points[i].idx = curr_idx;
587     memcpy(
588       points[i].coordinates_xyz, coordinates_xyz[curr_idx], 3 * sizeof(double));
589   }
590   *num_points = new_num_points;
591   free(points_int32);
592   return points;
593 }
594 
yac_point_sphere_part_search_new(size_t num_points,coordinate_pointer coordinates_xyz,yac_int const * ids)595 struct point_sphere_part_search * yac_point_sphere_part_search_new (
596   size_t num_points, coordinate_pointer coordinates_xyz, yac_int const * ids) {
597 
598   if (num_points == 0) return NULL;
599 
600   struct point_sphere_part_search * search = xmalloc(1 * sizeof(*search));
601   struct point_id_xyz * points =
602     get_unique_points(&num_points, coordinates_xyz, ids, NULL);
603   search->points = points;
604 
605   size_t max_tree_depth = 0;
606 
607   // emperical measurements have given a threshold for the leaf size of 2
608   struct point_sphere_part_node * tmp_node =
609     partition_point_data(
610       points, num_points, I_list_tree_min_size, (double[3]){0.0,0.0,1.0},
611       1, &max_tree_depth);
612 
613   search->base_node = *tmp_node;
614   search->max_tree_depth = max_tree_depth;
615   free(tmp_node);
616 
617   return search;
618 }
619 
yac_point_sphere_part_search_mask_new(size_t num_points,coordinate_pointer coordinates_xyz,yac_int const * ids,int const * mask)620 struct point_sphere_part_search * yac_point_sphere_part_search_mask_new (
621   size_t num_points, coordinate_pointer coordinates_xyz,
622   yac_int const * ids, int const * mask) {
623 
624   if (num_points == 0) return NULL;
625 
626   struct point_id_xyz * points =
627     get_unique_points(&num_points, coordinates_xyz, ids, mask);
628 
629   if (num_points == 0) {
630     free(points);
631     return NULL;
632   }
633 
634   struct point_sphere_part_search * search = xmalloc(1 * sizeof(*search));
635   search->points = xrealloc(points, num_points * sizeof(*points));
636 
637   size_t max_tree_depth = 0;
638 
639   // emperical measurements have given a threshold for the leaf size of 2
640   struct point_sphere_part_node * tmp_node =
641     partition_point_data(
642       search->points, num_points, I_list_tree_min_size,
643       (double[3]){0.0,0.0,1.0}, 1, &max_tree_depth);
644 
645   search->base_node = *tmp_node;
646   search->max_tree_depth = max_tree_depth;
647   free(tmp_node);
648 
649   return search;
650 }
651 
search_bnd_circle_I_node(struct sphere_part_node * node,struct bounding_circle bnd_circle,size_t ** restrict overlap_cells,size_t * overlap_cells_array_size,size_t * restrict num_overlap_cells,struct overlaps * search_interval_tree_buffer,double prev_gc_norm_vector[])652 static void search_bnd_circle_I_node(
653   struct sphere_part_node * node, struct bounding_circle bnd_circle,
654   size_t ** restrict overlap_cells, size_t * overlap_cells_array_size,
655   size_t * restrict num_overlap_cells,
656   struct overlaps * search_interval_tree_buffer, double prev_gc_norm_vector[]) {
657 
658   if (node->flags & I_IS_INTERVAL_TREE) {
659 
660      struct sin_cos_angle base_angle, corrected_inc_angle;
661      int big_sum, neg;
662      double GCp[3], bVp[3];
663      crossproduct_ld(node->gc_norm_vector,
664                      bnd_circle.base_vector, GCp);
665      crossproduct_ld(GCp, node->gc_norm_vector, bVp);
666      normalise_vector(bVp);
667      base_angle = get_vector_angle_2(bVp, prev_gc_norm_vector);
668      big_sum =
669         sum_angles(
670           bnd_circle.inc_angle, get_vector_angle_2(bVp, bnd_circle.base_vector),
671           &corrected_inc_angle);
672 
673      // if the angle is bigger then PI
674      if ((big_sum) || (corrected_inc_angle.sin < 0.0))
675        corrected_inc_angle = SIN_COS_M_PI;
676 
677      struct sin_cos_angle left, right;
678      // base_angle - corrected_inc_angle
679      neg = sub_angles(base_angle, corrected_inc_angle, &left);
680      // base_angle + corrected_inc_angle
681      big_sum = sum_angles(base_angle, corrected_inc_angle, &right);
682 
683      search_interval_tree_buffer->num_overlaps = 0;
684 
685      yac_search_interval_tree(
686         node->I.ivt.head_node, node->I.ivt.num_nodes,
687         (struct interval){
688            .left = compute_angle(left) - (neg?2.0*M_PI:0.0),
689            .right = compute_angle(right) + (big_sum?2.0*M_PI:0.0)},
690         search_interval_tree_buffer);
691 
692      ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
693                        *num_overlap_cells +
694                        search_interval_tree_buffer->num_overlaps);
695 
696      for (size_t i = 0; i < search_interval_tree_buffer->num_overlaps;
697           ++i) {
698         (*overlap_cells)[(*num_overlap_cells)+i] =
699            node->I.ivt.head_node[
700               search_interval_tree_buffer->overlap_iv[i]].value;
701      }
702 
703      *num_overlap_cells += search_interval_tree_buffer->num_overlaps;
704 
705   } else {
706 
707      ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
708                        *num_overlap_cells + node->I_size);
709      memcpy(*overlap_cells + *num_overlap_cells, node->I.list,
710             node->I_size * sizeof(**overlap_cells));
711      *num_overlap_cells += node->I_size;
712   }
713 }
714 
715 //! TODO change to iterative implementation and allocate overlap_cells first
search_big_bnd_circle(struct sphere_part_node * node,struct bounding_circle bnd_circle,size_t ** restrict overlap_cells,size_t * overlap_cells_array_size,size_t * restrict num_overlap_cells,struct overlaps * search_interval_tree_buffer,double prev_gc_norm_vector[])716 static void search_big_bnd_circle(
717   struct sphere_part_node * node, struct bounding_circle bnd_circle,
718   size_t ** restrict overlap_cells, size_t * overlap_cells_array_size,
719   size_t * restrict num_overlap_cells,
720   struct overlaps * search_interval_tree_buffer, double prev_gc_norm_vector[]) {
721 
722   if (node->flags & T_IS_LEAF) {
723 
724     ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
725                       *num_overlap_cells + node->T_size);
726     memcpy(*overlap_cells + *num_overlap_cells, node->T,
727            node->T_size * sizeof(**overlap_cells));
728     *num_overlap_cells += node->T_size;
729 
730   } else {
731     search_big_bnd_circle(
732        node->T, bnd_circle, overlap_cells, overlap_cells_array_size,
733        num_overlap_cells, search_interval_tree_buffer, node->gc_norm_vector);
734   }
735 
736   if (node->flags & U_IS_LEAF) {
737 
738     ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
739                       *num_overlap_cells + node->U_size);
740     memcpy(*overlap_cells + *num_overlap_cells, node->U,
741            node->U_size * sizeof(**overlap_cells));
742     *num_overlap_cells += node->U_size;
743 
744   } else {
745     search_big_bnd_circle(
746        node->U, bnd_circle, overlap_cells, overlap_cells_array_size,
747        num_overlap_cells, search_interval_tree_buffer, node->gc_norm_vector);
748   }
749 
750   search_bnd_circle_I_node(
751      node, bnd_circle, overlap_cells, overlap_cells_array_size,
752      num_overlap_cells, search_interval_tree_buffer, prev_gc_norm_vector);
753 }
754 
search_small_bnd_circle(struct sphere_part_node * node,struct bounding_circle bnd_circle,size_t ** restrict overlap_cells,size_t * overlap_cells_array_size,size_t * restrict num_overlap_cells,struct overlaps * search_interval_tree_buffer,double prev_gc_norm_vector[])755 static void search_small_bnd_circle(
756   struct sphere_part_node * node, struct bounding_circle bnd_circle,
757   size_t ** restrict overlap_cells, size_t * overlap_cells_array_size,
758   size_t * restrict num_overlap_cells,
759   struct overlaps * search_interval_tree_buffer, double prev_gc_norm_vector[]) {
760 
761    double dot = bnd_circle.base_vector[0] * node->gc_norm_vector[0] +
762                 bnd_circle.base_vector[1] * node->gc_norm_vector[1] +
763                 bnd_circle.base_vector[2] * node->gc_norm_vector[2];
764 
765    // angle < M_PI_2 + bnd_circle.inc_angle
766    if (dot > - bnd_circle.inc_angle.sin) {
767 
768       if (node->flags & T_IS_LEAF) {
769 
770          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
771                            *num_overlap_cells + node->T_size);
772          memcpy(*overlap_cells + *num_overlap_cells, node->T,
773                 node->T_size * sizeof(**overlap_cells));
774          *num_overlap_cells += node->T_size;
775 
776       } else {
777          search_small_bnd_circle(
778             node->T, bnd_circle, overlap_cells, overlap_cells_array_size,
779             num_overlap_cells, search_interval_tree_buffer,
780             node->gc_norm_vector);
781       }
782    }
783 
784    // angle > M_PI_2 - bnd_circle.inc_angle
785    if (dot < bnd_circle.inc_angle.sin) {
786 
787       if (node->flags & U_IS_LEAF) {
788 
789          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
790                            *num_overlap_cells + node->U_size);
791          memcpy(*overlap_cells + *num_overlap_cells, node->U,
792                 node->U_size * sizeof(**overlap_cells));
793          *num_overlap_cells += node->U_size;
794 
795       } else {
796          search_small_bnd_circle(
797             node->U, bnd_circle, overlap_cells, overlap_cells_array_size,
798             num_overlap_cells, search_interval_tree_buffer,
799             node->gc_norm_vector);
800       }
801    }
802 
803    struct sin_cos_angle angle_sum =
804       sum_angles_no_check(node->I_angle, bnd_circle.inc_angle);
805 
806    // if (I_angle + inc_angle > PI/2) ||
807    //    (fabs(angle - M_PI_2) <= (I_angle + inc_angle))
808    //
809    // assumtion:
810    //   I_angle >= 0 && I_angle <= PI/2
811    //   inc_angle >= 0 && inc_angle <= PI/2
812    //   angle >= 0 && angle <= PI
813    //
814    //   => I_angle + inc_angle >= 0 && I_angle + inc_angle <= PI
815    //
816    // I_angle + inc_angle >= PI/2
817    //
818    // fabs(angle - M_PI_2) <= (I_angle + inc_angle)
819    // => sin(fabs(angle - M_PI_2)) <= sin(I_angle + inc_angle)
820    //    this is wrong for (I_angle + inc_angle) > PI/2, however that case is
821    //    already covered by the first condition
822    // => fabs(cos(angle)) <= sin(I_angle + inc_angle)
823    if (((angle_sum.sin < 0.0) || (angle_sum.cos <= 0.0)) ||
824        (fabs(dot) <= angle_sum.sin)) {
825       search_bnd_circle_I_node(
826          node, bnd_circle, overlap_cells, overlap_cells_array_size,
827          num_overlap_cells, search_interval_tree_buffer, prev_gc_norm_vector);
828    }
829 }
830 
search_bnd_circle(struct sphere_part_node * node,struct bounding_circle bnd_circle,size_t ** restrict overlap_cells,size_t * overlap_cells_array_size,size_t * restrict num_overlap_cells,struct overlaps * search_interval_tree_buffer,double prev_gc_norm_vector[])831 static void search_bnd_circle(struct sphere_part_node * node,
832                               struct bounding_circle bnd_circle,
833                               size_t ** restrict overlap_cells,
834                               size_t * overlap_cells_array_size,
835                               size_t * restrict num_overlap_cells,
836                               struct overlaps * search_interval_tree_buffer,
837                               double prev_gc_norm_vector[]) {
838 
839   // if the bounding circle has an angle in the range of [0;PI/2[
840   if (bnd_circle.inc_angle.cos > 0.0)
841     search_small_bnd_circle(
842       node, bnd_circle, overlap_cells, overlap_cells_array_size,
843       num_overlap_cells, search_interval_tree_buffer, prev_gc_norm_vector);
844   else
845     search_big_bnd_circle(
846       node, bnd_circle, overlap_cells, overlap_cells_array_size,
847       num_overlap_cells, search_interval_tree_buffer, prev_gc_norm_vector);
848 }
849 
check_leaf_NN(struct point_id_xyz * points,size_t num_points,double * point_coordinates_xyz,struct sin_cos_angle * best_angle,double (** result_coordinates_xyz)[3],size_t * result_coordinates_xyz_array_size,size_t ** local_point_ids,size_t * local_point_ids_array_size,size_t total_num_local_point_ids,size_t * num_local_point_ids)850 static inline void check_leaf_NN(
851   struct point_id_xyz * points, size_t num_points,
852   double * point_coordinates_xyz, struct sin_cos_angle * best_angle,
853   double (**result_coordinates_xyz)[3],
854   size_t * result_coordinates_xyz_array_size, size_t ** local_point_ids,
855   size_t * local_point_ids_array_size, size_t total_num_local_point_ids,
856   size_t * num_local_point_ids) {
857 
858   size_t * local_point_ids_ = *local_point_ids;
859   size_t local_point_ids_array_size_ = *local_point_ids_array_size;
860   double (*result_coordinates_xyz_)[3];
861   size_t result_coordinates_xyz_array_size_;
862   size_t num_local_point_ids_ = *num_local_point_ids;
863 
864   if (result_coordinates_xyz != NULL) {
865     result_coordinates_xyz_ = *result_coordinates_xyz;
866     result_coordinates_xyz_array_size_ = *result_coordinates_xyz_array_size;
867     ENSURE_ARRAY_SIZE(
868       result_coordinates_xyz_, result_coordinates_xyz_array_size_,
869       total_num_local_point_ids + num_local_point_ids_ + num_points);
870     *result_coordinates_xyz = result_coordinates_xyz_;
871     *result_coordinates_xyz_array_size = result_coordinates_xyz_array_size_;
872     result_coordinates_xyz_ += total_num_local_point_ids;
873   }
874   ENSURE_ARRAY_SIZE(
875     local_point_ids_, local_point_ids_array_size_,
876     total_num_local_point_ids + num_local_point_ids_ + num_points);
877   *local_point_ids = local_point_ids_;
878   *local_point_ids_array_size = local_point_ids_array_size_;
879   local_point_ids_ += total_num_local_point_ids;
880 
881 
882   // check leaf for results
883   for (size_t i = 0; i < num_points; ++i) {
884 
885     struct sin_cos_angle curr_angle =
886       get_vector_angle_2(
887         points[i].coordinates_xyz, point_coordinates_xyz);
888     int compare = compare_angles(curr_angle, *best_angle);
889 
890     // if the point is worse than the currently best point
891     if (compare > 0) continue;
892 
893     // if we found a better point
894     if (compare < 0) {
895 
896       *best_angle = curr_angle;
897       num_local_point_ids_ = 1;
898       if (result_coordinates_xyz != NULL) {
899         result_coordinates_xyz_[0][0] = points[i].coordinates_xyz[0];
900         result_coordinates_xyz_[0][1] = points[i].coordinates_xyz[1];
901         result_coordinates_xyz_[0][2] = points[i].coordinates_xyz[2];
902       }
903       local_point_ids_[0] = points[i].idx;
904 
905     } else {
906 
907       if (result_coordinates_xyz != NULL) {
908         result_coordinates_xyz_[num_local_point_ids_][0] =
909           points[i].coordinates_xyz[0];
910         result_coordinates_xyz_[num_local_point_ids_][1] =
911           points[i].coordinates_xyz[1];
912         result_coordinates_xyz_[num_local_point_ids_][2] =
913           points[i].coordinates_xyz[2];
914       }
915       local_point_ids_[num_local_point_ids_] = points[i].idx;
916       num_local_point_ids_++;
917     }
918   }
919 
920   *num_local_point_ids = num_local_point_ids_;
921 }
922 
point_search_NN(struct bounding_circle * bnd_circle,double (** result_coordinates_xyz)[3],size_t * result_coordinates_xyz_array_size,size_t ** local_point_ids,size_t * local_point_ids_array_size,size_t total_num_local_point_ids,size_t * num_local_point_ids,double * dot_stack,struct point_sphere_part_node ** node_stack,int * flags,size_t curr_tree_depth)923 static void point_search_NN(
924   struct bounding_circle * bnd_circle, double (**result_coordinates_xyz)[3],
925   size_t * result_coordinates_xyz_array_size, size_t ** local_point_ids,
926   size_t * local_point_ids_array_size, size_t total_num_local_point_ids,
927   size_t * num_local_point_ids, double * dot_stack,
928   struct point_sphere_part_node ** node_stack,
929   int * flags, size_t curr_tree_depth) {
930 
931   double * point_coordinates_xyz = bnd_circle->base_vector;
932   struct sin_cos_angle best_angle = bnd_circle->inc_angle;
933 
934   double dot = dot_stack[curr_tree_depth];
935   struct point_sphere_part_node * node = node_stack[curr_tree_depth];
936   int skip_U = flags[curr_tree_depth] & U_FLAG;
937   int skip_T = flags[curr_tree_depth] & T_FLAG;
938 
939   do {
940 
941     if (!skip_U) {
942 
943       flags[curr_tree_depth] |= U_FLAG;
944 
945       // angle + inc_angle >= M_PI_2
946       if ((dot < best_angle.sin) | (best_angle.cos <= 0.0)) {
947 
948         if (node->flags & U_IS_LEAF) {
949 
950           check_leaf_NN(
951             (struct point_id_xyz *)(node->U), node->U_size,
952             point_coordinates_xyz, &best_angle, result_coordinates_xyz,
953             result_coordinates_xyz_array_size, local_point_ids,
954             local_point_ids_array_size, total_num_local_point_ids,
955             num_local_point_ids);
956 
957         } else {
958 
959           // traverse down one level
960           ++curr_tree_depth;
961           node = (struct point_sphere_part_node *)(node->U);
962           dot = node->gc_norm_vector[0] * point_coordinates_xyz[0] +
963                 node->gc_norm_vector[1] * point_coordinates_xyz[1] +
964                 node->gc_norm_vector[2] * point_coordinates_xyz[2];
965           dot_stack[curr_tree_depth] = dot;
966           node_stack[curr_tree_depth] = node;
967           flags[curr_tree_depth] = 0;
968           skip_U = 0;
969           skip_T = 0;
970           continue;
971         }
972       }
973     }
974 
975     if (!skip_T) {
976 
977       flags[curr_tree_depth] = U_FLAG + T_FLAG;
978 
979       // angle - inc_angle < M_PI_2
980       if ((dot > - best_angle.sin) || (best_angle.cos <= 0.0)) {
981 
982         if (node->flags & T_IS_LEAF) {
983 
984            check_leaf_NN(
985              (struct point_id_xyz *)(node->T), node->T_size,
986              point_coordinates_xyz, &best_angle, result_coordinates_xyz,
987              result_coordinates_xyz_array_size, local_point_ids,
988              local_point_ids_array_size, total_num_local_point_ids,
989              num_local_point_ids);
990 
991         } else {
992 
993            // traverse down one level
994            ++curr_tree_depth;
995            node = (struct point_sphere_part_node *)(node->T);
996            dot = node->gc_norm_vector[0] * point_coordinates_xyz[0] +
997                  node->gc_norm_vector[1] * point_coordinates_xyz[1] +
998                  node->gc_norm_vector[2] * point_coordinates_xyz[2];
999            dot_stack[curr_tree_depth] = dot;
1000            node_stack[curr_tree_depth] = node;
1001            flags[curr_tree_depth] = 0;
1002            skip_U = 0;
1003            skip_T = 0;
1004            continue;
1005         }
1006       }
1007     }
1008 
1009     if (curr_tree_depth == 0) break;
1010 
1011     // go up one level in the tree
1012 
1013     curr_tree_depth--;
1014     dot = dot_stack[curr_tree_depth];
1015     node = node_stack[curr_tree_depth];
1016     skip_U = flags[curr_tree_depth] & U_FLAG;
1017     skip_T = flags[curr_tree_depth] & T_FLAG;
1018 
1019   } while (1);
1020 
1021   bnd_circle->inc_angle = best_angle;
1022 }
1023 
point_check_bnd_circle(struct point_sphere_part_node * node,struct bounding_circle bnd_circle)1024 static int point_check_bnd_circle(
1025    struct point_sphere_part_node * node, struct bounding_circle bnd_circle) {
1026 
1027    double dot = node->gc_norm_vector[0]*bnd_circle.base_vector[0] +
1028                 node->gc_norm_vector[1]*bnd_circle.base_vector[1] +
1029                 node->gc_norm_vector[2]*bnd_circle.base_vector[2];
1030 
1031    int ret = 0;
1032 
1033    // angle + inc_angle >= M_PI_2
1034    if (dot <= bnd_circle.inc_angle.sin) {
1035 
1036       if (node->flags & U_IS_LEAF) {
1037 
1038          struct point_id_xyz * U = (struct point_id_xyz *)(node->U);
1039          size_t U_size = node->U_size;
1040          for (size_t i = 0; i < U_size; ++i) {
1041             double cos_angle =
1042                U[i].coordinates_xyz[0] * bnd_circle.base_vector[0] +
1043                U[i].coordinates_xyz[1] * bnd_circle.base_vector[1] +
1044                U[i].coordinates_xyz[2] * bnd_circle.base_vector[2];
1045             if (cos_angle > bnd_circle.inc_angle.cos) return 1;
1046          }
1047 
1048       } else {
1049          ret = point_check_bnd_circle(node->U, bnd_circle);
1050       }
1051    }
1052 
1053    // angle - inc_angle < M_PI_2
1054    if ((!ret) && (dot > - bnd_circle.inc_angle.sin)) {
1055 
1056       if (node->flags & T_IS_LEAF) {
1057 
1058          struct point_id_xyz * T = (struct point_id_xyz *)(node->T);
1059          size_t T_size = node->T_size;
1060          for (size_t i = 0; i < T_size; ++i) {
1061             double cos_angle =
1062                T[i].coordinates_xyz[0] * bnd_circle.base_vector[0] +
1063                T[i].coordinates_xyz[1] * bnd_circle.base_vector[1] +
1064                T[i].coordinates_xyz[2] * bnd_circle.base_vector[2];
1065             if (cos_angle > bnd_circle.inc_angle.cos) return 1;
1066          }
1067 
1068       } else {
1069          ret = point_check_bnd_circle(node->T, bnd_circle);
1070       }
1071    }
1072 
1073    return ret;
1074 }
1075 
leaf_contains_matching_points(struct point_id_xyz * points,size_t num_points,double coordinate_xyz[3],size_t ** local_point_ids,size_t * local_point_ids_array_size,double (** result_coordinates_xyz)[3],size_t * result_coordinates_xyz_array_size,size_t total_num_local_point_ids,size_t * num_local_point_ids)1076 static inline int leaf_contains_matching_points(
1077   struct point_id_xyz * points, size_t num_points, double coordinate_xyz[3],
1078   size_t ** local_point_ids, size_t * local_point_ids_array_size,
1079   double (**result_coordinates_xyz)[3],
1080   size_t * result_coordinates_xyz_array_size,
1081   size_t total_num_local_point_ids, size_t * num_local_point_ids) {
1082 
1083   for (size_t i = 0; i < num_points; ++i) {
1084 
1085     // if the points are nearly identical
1086     if ((fabs(points[i].coordinates_xyz[0] - coordinate_xyz[0]) < yac_angle_tol) &&
1087         (fabs(points[i].coordinates_xyz[1] - coordinate_xyz[1]) < yac_angle_tol) &&
1088         (fabs(points[i].coordinates_xyz[2] - coordinate_xyz[2]) < yac_angle_tol)) {
1089 
1090       ENSURE_ARRAY_SIZE(*local_point_ids, *local_point_ids_array_size,
1091                         total_num_local_point_ids + num_points - i);
1092       size_t * local_point_ids_ =
1093         (*local_point_ids) + total_num_local_point_ids;
1094 
1095       double (*result_coordinates_xyz_)[3];
1096 
1097       if (result_coordinates_xyz != NULL) {
1098         ENSURE_ARRAY_SIZE(*result_coordinates_xyz,
1099                           *result_coordinates_xyz_array_size,
1100                           total_num_local_point_ids + num_points - i);
1101         result_coordinates_xyz_ =
1102           (*result_coordinates_xyz) + total_num_local_point_ids;
1103       } else {
1104         result_coordinates_xyz_ = NULL;
1105       }
1106 
1107       local_point_ids_[0] = points[i].idx;
1108       if (result_coordinates_xyz_ != NULL) {
1109         result_coordinates_xyz_[0][0] = points[i].coordinates_xyz[0];
1110         result_coordinates_xyz_[0][1] = points[i].coordinates_xyz[1];
1111         result_coordinates_xyz_[0][2] = points[i].coordinates_xyz[2];
1112       }
1113 
1114       size_t num_local_point_ids_ = 1;
1115 
1116       for (i += 1; i < num_points; ++i) {
1117         // if the points are nearly identical
1118         if ((fabs(points[i].coordinates_xyz[0] - coordinate_xyz[0]) < yac_angle_tol) &&
1119             (fabs(points[i].coordinates_xyz[1] - coordinate_xyz[1]) < yac_angle_tol) &&
1120             (fabs(points[i].coordinates_xyz[2] - coordinate_xyz[2]) < yac_angle_tol)) {
1121 
1122           local_point_ids_[num_local_point_ids_] = points[i].idx;
1123           if (result_coordinates_xyz_ != NULL) {
1124             result_coordinates_xyz_[num_local_point_ids_][0] =
1125               points[i].coordinates_xyz[0];
1126             result_coordinates_xyz_[num_local_point_ids_][1] =
1127               points[i].coordinates_xyz[1];
1128             result_coordinates_xyz_[num_local_point_ids_][2] =
1129               points[i].coordinates_xyz[2];
1130           }
1131           ++num_local_point_ids_;
1132         }
1133       }
1134 
1135       *num_local_point_ids = num_local_point_ids_;
1136 
1137       return 1;
1138     }
1139   }
1140 
1141   return 0;
1142 }
1143 
yac_point_sphere_part_search_NN(struct point_sphere_part_search * search,size_t num_points,double (* coordinates_xyz)[3],double * cos_angles,double (** result_coordinates_xyz)[3],size_t * result_coordinates_xyz_array_size,size_t ** local_point_ids,size_t * local_point_ids_array_size,size_t * num_local_point_ids)1144 void yac_point_sphere_part_search_NN(struct point_sphere_part_search * search,
1145                                      size_t num_points,
1146                                      double (*coordinates_xyz)[3],
1147                                      double * cos_angles,
1148                                      double (**result_coordinates_xyz)[3],
1149                                      size_t * result_coordinates_xyz_array_size,
1150                                      size_t ** local_point_ids,
1151                                      size_t * local_point_ids_array_size,
1152                                      size_t * num_local_point_ids) {
1153 
1154   memset(num_local_point_ids, 0, num_points * sizeof(*num_local_point_ids));
1155 
1156   if (search == NULL) return;
1157 
1158   struct point_sphere_part_node * base_node = &(search->base_node);
1159 
1160   size_t total_num_local_point_ids = 0;
1161 
1162   double * dot_stack = xmalloc(search->max_tree_depth * sizeof(*dot_stack));
1163   struct point_sphere_part_node ** node_stack =
1164     xmalloc(search->max_tree_depth * sizeof(*node_stack));
1165   int * flags = xmalloc(search->max_tree_depth * sizeof(*flags));
1166 
1167   for (size_t i = 0; i < num_points; ++i) {
1168 
1169     struct point_sphere_part_node * curr_node = base_node;
1170 
1171     double * curr_coordinates_xyz = coordinates_xyz[i];
1172 
1173     size_t curr_tree_depth = 0;
1174     struct point_id_xyz * points = NULL;
1175     size_t curr_num_points = 0;
1176 
1177     // get the matching leaf for the current point
1178     do {
1179 
1180       double dot = curr_node->gc_norm_vector[0]*curr_coordinates_xyz[0] +
1181                    curr_node->gc_norm_vector[1]*curr_coordinates_xyz[1] +
1182                    curr_node->gc_norm_vector[2]*curr_coordinates_xyz[2];
1183 
1184       dot_stack[curr_tree_depth] = dot;
1185       node_stack[curr_tree_depth] = curr_node;
1186       flags[curr_tree_depth] = 0;
1187 
1188       // angle > M_PI_2
1189       if (dot < yac_angle_tol) {
1190 
1191         flags[curr_tree_depth] = U_FLAG;
1192 
1193         if (curr_node->flags & U_IS_LEAF) {
1194           if (curr_node->U_size > 0) {
1195             points = (struct point_id_xyz*)(curr_node->U);
1196             curr_num_points = curr_node->U_size;
1197             break;
1198           } else {
1199             flags[curr_tree_depth] = T_FLAG;
1200             if (curr_node->flags & T_IS_LEAF) {
1201               points = (struct point_id_xyz*)(curr_node->T);
1202               curr_num_points = curr_node->T_size;
1203               break;
1204             } else {
1205               curr_node = curr_node->T;
1206             }
1207           }
1208         } else curr_node = curr_node->U;
1209 
1210       // angle < M_PI_2
1211       } else if (dot > -yac_angle_tol) {
1212 
1213         flags[curr_tree_depth] = T_FLAG;
1214 
1215         if (curr_node->flags & T_IS_LEAF) {
1216           if (curr_node->T_size > 0) {
1217             points = (struct point_id_xyz*)(curr_node->T);
1218             curr_num_points = curr_node->T_size;
1219             break;
1220           } else {
1221             flags[curr_tree_depth] = U_FLAG;
1222             if (curr_node->flags & U_IS_LEAF) {
1223               points = (struct point_id_xyz*)(curr_node->U);
1224               curr_num_points = curr_node->U_size;
1225               break;
1226             } else {
1227               curr_node = curr_node->U;
1228             }
1229           }
1230         } else curr_node = curr_node->T;
1231       }
1232 
1233       curr_tree_depth++;
1234     } while (1);
1235 
1236     // if we do not have to do a finer search
1237     if (leaf_contains_matching_points(
1238           points, curr_num_points, curr_coordinates_xyz, local_point_ids,
1239           local_point_ids_array_size, result_coordinates_xyz,
1240           result_coordinates_xyz_array_size, total_num_local_point_ids,
1241           num_local_point_ids + i)) {
1242 
1243       if (cos_angles != NULL) cos_angles[i] = 1.0;
1244 
1245     } else {
1246 
1247       struct bounding_circle bnd_circle;
1248       bnd_circle.base_vector[0] = curr_coordinates_xyz[0];
1249       bnd_circle.base_vector[1] = curr_coordinates_xyz[1];
1250       bnd_circle.base_vector[2] = curr_coordinates_xyz[2];
1251       bnd_circle.inc_angle = SIN_COS_M_PI;
1252 
1253       check_leaf_NN(
1254         points, curr_num_points, curr_coordinates_xyz, &(bnd_circle.inc_angle),
1255         result_coordinates_xyz, result_coordinates_xyz_array_size,
1256         local_point_ids, local_point_ids_array_size, total_num_local_point_ids,
1257         num_local_point_ids + i);
1258 
1259       // get best result points
1260       point_search_NN(
1261         &bnd_circle, result_coordinates_xyz, result_coordinates_xyz_array_size,
1262         local_point_ids, local_point_ids_array_size, total_num_local_point_ids,
1263         num_local_point_ids + i, dot_stack, node_stack, flags, curr_tree_depth);
1264 
1265       if (cos_angles != NULL) cos_angles[i] = bnd_circle.inc_angle.cos;
1266     }
1267 
1268     total_num_local_point_ids += num_local_point_ids[i];
1269   }
1270 
1271   free(flags);
1272   free(node_stack);
1273   free(dot_stack);
1274 }
1275 
compare_point_id_xyz_angle(const void * a,const void * b)1276 static inline int compare_point_id_xyz_angle(const void * a, const void * b) {
1277 
1278   const struct point_id_xyz_angle * p_a = (const struct point_id_xyz_angle *)a;
1279   const struct point_id_xyz_angle * p_b = (const struct point_id_xyz_angle *)b;
1280 
1281   int ret = (p_a->cos_angle < p_b->cos_angle) -
1282             (p_a->cos_angle > p_b->cos_angle);
1283 
1284   if (ret != 0) return ret;
1285 
1286   return (p_a->idx > p_b->idx) - (p_a->idx < p_b->idx);
1287 }
1288 
initial_point_bnd_search_NNN(size_t n,struct point_id_xyz * points,size_t num_points,double * point_coordinates_xyz,struct point_id_xyz_angle ** results,size_t * results_array_size)1289 static size_t initial_point_bnd_search_NNN(
1290   size_t n, struct point_id_xyz * points, size_t num_points,
1291   double * point_coordinates_xyz, struct point_id_xyz_angle ** results,
1292   size_t * results_array_size) {
1293 
1294   assert(num_points > 0);
1295 
1296   ENSURE_ARRAY_SIZE(*results, *results_array_size, num_points);
1297   struct point_id_xyz_angle * results_ = *results;
1298 
1299   for (size_t i = 0; i < num_points; ++i) {
1300 
1301     *(struct point_id_xyz*)(results_ + i) = points[i];
1302     results_[i].cos_angle = clamp_abs_one(
1303       points[i].coordinates_xyz[0] * point_coordinates_xyz[0] +
1304       points[i].coordinates_xyz[1] * point_coordinates_xyz[1] +
1305       points[i].coordinates_xyz[2] * point_coordinates_xyz[2]);
1306   }
1307 
1308   qsort(results_, num_points, sizeof(*results_), compare_point_id_xyz_angle);
1309 
1310   if (num_points <= n) return num_points;
1311 
1312   size_t num_results;
1313   double min_cos_angle = results_[n - 1].cos_angle;
1314 
1315   for (num_results = n;
1316        (num_results < num_points) &&
1317        !(fabs(min_cos_angle - results_[num_results].cos_angle) > 0.0);
1318        ++num_results);
1319 
1320   return num_results;
1321 }
1322 
check_leaf_NNN(size_t n,double * point_coordinates_xyz,struct point_id_xyz * points,size_t num_points,struct point_id_xyz_angle ** results,size_t * results_array_size,size_t * num_results,struct sin_cos_angle curr_angle)1323 static inline struct sin_cos_angle check_leaf_NNN(
1324   size_t n, double * point_coordinates_xyz,
1325   struct point_id_xyz * points, size_t num_points,
1326   struct point_id_xyz_angle ** results, size_t * results_array_size,
1327   size_t * num_results, struct sin_cos_angle curr_angle) {
1328 
1329   size_t num_results_ = *num_results;
1330   ENSURE_ARRAY_SIZE(*results, *results_array_size, num_results_ + num_points);
1331   struct point_id_xyz_angle * results_ = *results;
1332 
1333   int flag = 0;
1334 
1335   double min_cos_angle = results_[num_results_-1].cos_angle;
1336 
1337   // check leaf for results
1338   for (size_t i = 0; i < num_points; ++i) {
1339 
1340     double curr_cos_angle =
1341       points[i].coordinates_xyz[0] * point_coordinates_xyz[0] +
1342       points[i].coordinates_xyz[1] * point_coordinates_xyz[1] +
1343       points[i].coordinates_xyz[2] * point_coordinates_xyz[2];
1344 
1345     // if the point is worse than the currently best point
1346     if (curr_cos_angle < min_cos_angle) continue;
1347 
1348     struct point_id_xyz_angle point;
1349     *(struct point_id_xyz*)(&point) = points[i];
1350     point.cos_angle = curr_cos_angle;
1351 
1352     // insert point
1353     size_t j;
1354     for (j = 0; j < num_results_; ++j) {
1355 
1356       if (compare_point_id_xyz_angle(
1357             &point, results_ + num_results_ - j - 1) < 0) {
1358         results_[num_results_ - j] = results_[num_results_ - j - 1];
1359       } else {
1360         break;
1361       }
1362     }
1363     results_[num_results_ - j] = point;
1364 
1365     ++num_results_;
1366     flag = 1;
1367   }
1368 
1369   if (flag) {
1370 
1371     if (num_results_ > n) {
1372 
1373       size_t new_num_results;
1374       min_cos_angle = results_[n - 1].cos_angle;
1375 
1376       for (new_num_results = n;
1377            (new_num_results < num_results_) &&
1378            !(fabs(min_cos_angle - results_[new_num_results].cos_angle) > 0.0);
1379            ++new_num_results);
1380       num_results_ = new_num_results;
1381     }
1382     *num_results = num_results_;
1383 
1384     return
1385       get_vector_angle_2(
1386         results_[num_results_-1].coordinates_xyz, point_coordinates_xyz);
1387   } else return curr_angle;
1388 }
1389 
point_search_NNN(size_t n,double * point_coordinates_xyz,struct point_id_xyz_angle ** results,size_t * results_array_size,size_t * num_results,double * dot_stack,struct point_sphere_part_node ** node_stack,int * flags,size_t curr_tree_depth)1390 static void point_search_NNN(
1391   size_t n, double * point_coordinates_xyz,
1392   struct point_id_xyz_angle ** results, size_t * results_array_size,
1393   size_t * num_results, double * dot_stack,
1394   struct point_sphere_part_node ** node_stack, int * flags,
1395   size_t curr_tree_depth) {
1396 
1397   struct sin_cos_angle angle =
1398     get_vector_angle_2(
1399       (*results)[(*num_results)-1].coordinates_xyz, point_coordinates_xyz);
1400 
1401   // if we have already found at least n exactly matching points
1402   if ((*num_results >= n) && (angle.sin <= yac_angle_tol)) return;
1403 
1404   double dot = dot_stack[curr_tree_depth];
1405   struct point_sphere_part_node * node = node_stack[curr_tree_depth];
1406   int skip_U = flags[curr_tree_depth] & U_FLAG;
1407   int skip_T = flags[curr_tree_depth] & T_FLAG;
1408 
1409   do {
1410 
1411     if (!skip_U) {
1412 
1413       flags[curr_tree_depth] |= U_FLAG;
1414 
1415       // angle + inc_angle >= M_PI_2
1416       if ((dot < angle.sin) | (angle.cos <= 0.0)) {
1417 
1418         if (node->flags & U_IS_LEAF) {
1419 
1420           angle = check_leaf_NNN(
1421             n, point_coordinates_xyz, (struct point_id_xyz *)(node->U),
1422             node->U_size, results, results_array_size, num_results, angle);
1423 
1424         } else {
1425 
1426           // traverse down one level
1427           ++curr_tree_depth;
1428           node = (struct point_sphere_part_node *)(node->U);
1429           dot = node->gc_norm_vector[0] * point_coordinates_xyz[0] +
1430                 node->gc_norm_vector[1] * point_coordinates_xyz[1] +
1431                 node->gc_norm_vector[2] * point_coordinates_xyz[2];
1432           dot_stack[curr_tree_depth] = dot;
1433           node_stack[curr_tree_depth] = node;
1434           flags[curr_tree_depth] = 0;
1435           skip_U = 0;
1436           skip_T = 0;
1437           continue;
1438         }
1439       }
1440     }
1441 
1442     if (!skip_T) {
1443 
1444       flags[curr_tree_depth] = U_FLAG + T_FLAG;
1445 
1446       // angle - inc_angle < M_PI_2
1447       if ((dot > - angle.sin) || (angle.cos <= 0.0)) {
1448 
1449         if (node->flags & T_IS_LEAF) {
1450 
1451           angle = check_leaf_NNN(
1452             n, point_coordinates_xyz, (struct point_id_xyz *)(node->T),
1453             node->T_size, results, results_array_size, num_results, angle);
1454 
1455         } else {
1456 
1457           // traverse down one level
1458           ++curr_tree_depth;
1459           node = (struct point_sphere_part_node *)(node->T);
1460           dot = node->gc_norm_vector[0] * point_coordinates_xyz[0] +
1461                 node->gc_norm_vector[1] * point_coordinates_xyz[1] +
1462                 node->gc_norm_vector[2] * point_coordinates_xyz[2];
1463           dot_stack[curr_tree_depth] = dot;
1464           node_stack[curr_tree_depth] = node;
1465           flags[curr_tree_depth] = 0;
1466           skip_U = 0;
1467           skip_T = 0;
1468           continue;
1469         }
1470       }
1471     }
1472 
1473     if (curr_tree_depth == 0) break;
1474 
1475     // go up one level in the tree
1476 
1477     curr_tree_depth--;
1478     dot = dot_stack[curr_tree_depth];
1479     node = node_stack[curr_tree_depth];
1480     skip_U = flags[curr_tree_depth] & U_FLAG;
1481     skip_T = flags[curr_tree_depth] & T_FLAG;
1482 
1483   } while (1);
1484 }
1485 
yac_point_sphere_part_search_NNN(struct point_sphere_part_search * search,size_t num_points,double (* coordinates_xyz)[3],size_t n,double ** cos_angles,size_t * cos_angles_array_size,double (** result_coordinates_xyz)[3],size_t * result_coordinates_xyz_array_size,size_t ** local_point_ids,size_t * local_point_ids_array_size,size_t * num_local_point_ids)1486 void yac_point_sphere_part_search_NNN(struct point_sphere_part_search * search,
1487                                       size_t num_points,
1488                                       double (*coordinates_xyz)[3], size_t n,
1489                                       double ** cos_angles,
1490                                       size_t * cos_angles_array_size,
1491                                       double (**result_coordinates_xyz)[3],
1492                                       size_t * result_coordinates_xyz_array_size,
1493                                       size_t ** local_point_ids,
1494                                       size_t * local_point_ids_array_size,
1495                                       size_t * num_local_point_ids) {
1496 
1497   if (cos_angles != NULL)
1498     ENSURE_ARRAY_SIZE(*cos_angles, *cos_angles_array_size, num_points * n);
1499 
1500   if (n == 1) {
1501     yac_point_sphere_part_search_NN(
1502       search, num_points, coordinates_xyz, (cos_angles!=NULL)?*cos_angles:NULL,
1503       result_coordinates_xyz, result_coordinates_xyz_array_size,
1504       local_point_ids, local_point_ids_array_size, num_local_point_ids);
1505 
1506     size_t total_num_local_points = 0;
1507     for (size_t i = 0; i < num_points; ++i)
1508       total_num_local_points += num_local_point_ids[i];
1509 
1510     if ((cos_angles != NULL) && (total_num_local_points > num_points)) {
1511 
1512       ENSURE_ARRAY_SIZE(*cos_angles, *cos_angles_array_size,
1513                         total_num_local_points);
1514 
1515       for (size_t i = num_points - 1, offset = total_num_local_points - 1;
1516            i < num_points; i--) {
1517 
1518         for (size_t j = 0; j < num_local_point_ids[i]; ++j, --offset)
1519           (*cos_angles)[offset] = (*cos_angles)[i];
1520       }
1521     }
1522     return;
1523   }
1524 
1525 
1526   if (search == NULL) {
1527     memset(num_local_point_ids, 0, num_points * sizeof(*num_local_point_ids));
1528     return;
1529   }
1530 
1531   struct point_sphere_part_node * base_node = &(search->base_node);
1532 
1533   size_t total_num_local_point_ids = 0;
1534 
1535   double * dot_stack = xmalloc(search->max_tree_depth * sizeof(*dot_stack));
1536   struct point_sphere_part_node ** node_stack =
1537     xmalloc(search->max_tree_depth * sizeof(*node_stack));
1538   int * flags = xmalloc(search->max_tree_depth * sizeof(*flags));
1539 
1540   struct point_id_xyz_angle * results = NULL;
1541   size_t results_array_size = 0;
1542 
1543   for (size_t i = 0; i < num_points; ++i) {
1544 
1545     struct point_sphere_part_node * curr_node = base_node;
1546 
1547     double * curr_coordinates_xyz = coordinates_xyz[i];
1548 
1549     size_t curr_tree_depth = 0;
1550     struct point_id_xyz * points = search->points;
1551     size_t curr_num_points = 0;
1552 
1553     // get the matching leaf for the current point
1554     do {
1555 
1556       double dot = curr_node->gc_norm_vector[0]*curr_coordinates_xyz[0] +
1557                    curr_node->gc_norm_vector[1]*curr_coordinates_xyz[1] +
1558                    curr_node->gc_norm_vector[2]*curr_coordinates_xyz[2];
1559 
1560       dot_stack[curr_tree_depth] = dot;
1561       node_stack[curr_tree_depth] = curr_node;
1562       flags[curr_tree_depth] = 0;
1563 
1564       // angle >= M_PI_2
1565       if (dot <= 0.0) {
1566 
1567         if (curr_node->U_size < n) {
1568 
1569           flags[curr_tree_depth] = U_FLAG + T_FLAG;
1570           curr_num_points = curr_node->U_size + curr_node->T_size;
1571           break;
1572         } else if (curr_node->flags & U_IS_LEAF) {
1573 
1574           flags[curr_tree_depth] = U_FLAG;
1575           curr_num_points = curr_node->U_size;
1576           break;
1577         } else {
1578 
1579           flags[curr_tree_depth] = U_FLAG;
1580           curr_node = curr_node->U;
1581         }
1582 
1583       } else {
1584 
1585         if (curr_node->T_size < n) {
1586 
1587           flags[curr_tree_depth] = U_FLAG + T_FLAG;
1588           curr_num_points = curr_node->U_size + curr_node->T_size;
1589           break;
1590         } else if (curr_node->flags & T_IS_LEAF) {
1591 
1592           points += curr_node->U_size;
1593           flags[curr_tree_depth] = T_FLAG;
1594           curr_num_points = curr_node->T_size;
1595           break;
1596         } else {
1597 
1598           points += curr_node->U_size;
1599           flags[curr_tree_depth] = T_FLAG;
1600           curr_node = curr_node->T;
1601         }
1602       }
1603 
1604       curr_tree_depth++;
1605     } while (1);
1606 
1607     assert(curr_num_points > 0);
1608 
1609     size_t num_results =
1610       initial_point_bnd_search_NNN(
1611         n, points, curr_num_points, curr_coordinates_xyz,
1612         &results, &results_array_size);
1613 
1614     // do a detailed search
1615     point_search_NNN(
1616       n, curr_coordinates_xyz, &results, &results_array_size, &num_results,
1617       dot_stack, node_stack, flags, curr_tree_depth);
1618 
1619     // extract the results
1620     ENSURE_ARRAY_SIZE(*local_point_ids, *local_point_ids_array_size,
1621                       total_num_local_point_ids + num_results);
1622     size_t * local_point_ids_ =
1623       (*local_point_ids) + total_num_local_point_ids;
1624     double * cos_angles_;
1625     if (cos_angles != NULL) {
1626       ENSURE_ARRAY_SIZE(*cos_angles, *cos_angles_array_size,
1627                         total_num_local_point_ids + num_results);
1628       cos_angles_ = (*cos_angles) + total_num_local_point_ids;
1629     } else {
1630       cos_angles_ = NULL;
1631     }
1632     double (*result_coordinates_xyz_)[3];
1633     if (result_coordinates_xyz != NULL) {
1634       ENSURE_ARRAY_SIZE(*result_coordinates_xyz,
1635                         *result_coordinates_xyz_array_size,
1636                         total_num_local_point_ids + num_results);
1637       result_coordinates_xyz_ =
1638         (*result_coordinates_xyz) + total_num_local_point_ids;
1639     } else {
1640       result_coordinates_xyz_ = NULL;
1641     }
1642 
1643     for (size_t j = 0; j < num_results; ++j) {
1644 
1645       local_point_ids_[j] = results[j].idx;
1646       if (cos_angles_ != NULL) cos_angles_[j] = results[j].cos_angle;
1647       if (result_coordinates_xyz_ != NULL) {
1648         result_coordinates_xyz_[j][0] = results[j].coordinates_xyz[0];
1649         result_coordinates_xyz_[j][1] = results[j].coordinates_xyz[1];
1650         result_coordinates_xyz_[j][2] = results[j].coordinates_xyz[2];
1651       }
1652     }
1653 
1654     num_local_point_ids[i] = num_results;
1655     total_num_local_point_ids += num_results;
1656   }
1657 
1658   free(results);
1659   free(flags);
1660   free(node_stack);
1661   free(dot_stack);
1662 }
1663 
yac_point_sphere_part_search_bnd_circle_contains_points(struct point_sphere_part_search * search,struct bounding_circle circle)1664 int yac_point_sphere_part_search_bnd_circle_contains_points(
1665   struct point_sphere_part_search * search, struct bounding_circle circle) {
1666 
1667   if (search == NULL) return 0;
1668 
1669   return point_check_bnd_circle(&(search->base_node), circle);
1670 }
1671 
search_point(struct sphere_part_node * node,double point[],size_t ** overlap_cells,size_t * overlap_cells_array_size,size_t * num_overlap_cells,struct overlaps * search_interval_tree_buffer,double prev_gc_norm_vector[])1672 static void search_point(struct sphere_part_node * node,
1673                          double point[],
1674                          size_t ** overlap_cells,
1675                          size_t * overlap_cells_array_size,
1676                          size_t * num_overlap_cells,
1677                          struct overlaps * search_interval_tree_buffer,
1678                          double prev_gc_norm_vector[]) {
1679 
1680    double dot = point[0] * node->gc_norm_vector[0] +
1681                 point[1] * node->gc_norm_vector[1] +
1682                 point[2] * node->gc_norm_vector[2];
1683 
1684    // angle < M_PI_2
1685    if (dot > -yac_angle_tol) {
1686 
1687       if (node->flags & T_IS_LEAF) {
1688 
1689          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
1690                            *num_overlap_cells + node->T_size);
1691          memcpy(*overlap_cells + *num_overlap_cells, node->T,
1692                 node->T_size * sizeof(**overlap_cells));
1693          *num_overlap_cells += node->T_size;
1694 
1695       } else {
1696          search_point(node->T, point, overlap_cells,
1697                       overlap_cells_array_size, num_overlap_cells,
1698                       search_interval_tree_buffer, node->gc_norm_vector);
1699       }
1700    }
1701 
1702    // angle > M_PI_2
1703    if (dot < yac_angle_tol) {
1704 
1705       if (node->flags & U_IS_LEAF) {
1706 
1707          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
1708                            *num_overlap_cells + node->U_size);
1709          memcpy(*overlap_cells + *num_overlap_cells, node->U,
1710                 node->U_size * sizeof(**overlap_cells));
1711          *num_overlap_cells += node->U_size;
1712 
1713       } else {
1714          search_point(node->U, point, overlap_cells,
1715                       overlap_cells_array_size, num_overlap_cells,
1716                       search_interval_tree_buffer, node->gc_norm_vector);
1717       }
1718    }
1719 
1720    // fabs(angle - M_PI_2) <= (node->I_angle)
1721    // fabs(cos(angle)) <= sin(node->I_angle)
1722    if (fabs(dot) <= node->I_angle.sin) {
1723 
1724       if (node->flags & I_IS_INTERVAL_TREE) {
1725 
1726          double GCp[3], bVp[3];
1727          crossproduct_ld(node->gc_norm_vector, point, GCp);
1728          crossproduct_ld(GCp, node->gc_norm_vector, bVp);
1729          normalise_vector(bVp);
1730          double base_angle = get_vector_angle(bVp, prev_gc_norm_vector);
1731 
1732          struct interval search_interval =
1733           {.left=base_angle, .right=base_angle};
1734 
1735          search_interval_tree_buffer->num_overlaps = 0;
1736 
1737          yac_search_interval_tree(node->I.ivt.head_node, node->I.ivt.num_nodes,
1738                                   search_interval, search_interval_tree_buffer);
1739 
1740          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
1741                            *num_overlap_cells +
1742                            search_interval_tree_buffer->num_overlaps);
1743 
1744          for (size_t i = 0; i < search_interval_tree_buffer->num_overlaps;
1745               ++i) {
1746             (*overlap_cells)[(*num_overlap_cells)+i] =
1747                node->I.ivt.head_node[
1748                   search_interval_tree_buffer->overlap_iv[i]].value;
1749          }
1750 
1751          *num_overlap_cells += search_interval_tree_buffer->num_overlaps;
1752 
1753       } else {
1754 
1755          ENSURE_ARRAY_SIZE(*overlap_cells, *overlap_cells_array_size,
1756                            *num_overlap_cells + node->I_size);
1757          memcpy(*overlap_cells + *num_overlap_cells, node->I.list,
1758                 node->I_size * sizeof(**overlap_cells));
1759          *num_overlap_cells += node->I_size;
1760       }
1761    }
1762 }
1763 
yac_bnd_sphere_part_search_do_point_search(struct bnd_sphere_part_search * search,coordinate_pointer coordinates_xyz,size_t count,size_t ** cells,size_t * num_cells_per_coordinate)1764 void yac_bnd_sphere_part_search_do_point_search(
1765   struct bnd_sphere_part_search * search, coordinate_pointer coordinates_xyz,
1766   size_t count, size_t ** cells, size_t * num_cells_per_coordinate) {
1767 
1768   struct sphere_part_node * base_node = &(search->base_node);
1769 
1770   size_t * temp_search_results = NULL;
1771   size_t temp_search_results_array_size = 0;
1772   size_t num_temp_search_results = 0;
1773 
1774   struct overlaps search_interval_tree_buffer = {0, 0, NULL};
1775 
1776   memset(
1777     num_cells_per_coordinate, 0, count * sizeof(*num_cells_per_coordinate));
1778   size_t * cells_ = NULL;
1779   size_t cells_array_size = 0;
1780   size_t num_cells = 0;
1781   ENSURE_ARRAY_SIZE(cells_, cells_array_size, count);
1782 
1783   for (size_t i = 0; i < count; ++i) {
1784 
1785     double * curr_coordinates_xyz = &(coordinates_xyz[i][0]);
1786 
1787     num_temp_search_results = 0;
1788 
1789     double gc_norm_vector[3] = {0.0,0.0,1.0};
1790 
1791     search_point(base_node, curr_coordinates_xyz, &temp_search_results,
1792                  &temp_search_results_array_size, &num_temp_search_results,
1793                  &search_interval_tree_buffer, gc_norm_vector);
1794 
1795     ENSURE_ARRAY_SIZE(
1796       cells_, cells_array_size, num_cells + num_temp_search_results);
1797 
1798     memcpy(cells_ + num_cells, temp_search_results,
1799            num_temp_search_results * sizeof(*temp_search_results));
1800     num_cells_per_coordinate[i] = num_temp_search_results;
1801     num_cells += num_temp_search_results;
1802   }
1803 
1804   free(temp_search_results);
1805   free(search_interval_tree_buffer.overlap_iv);
1806 
1807    *cells = xrealloc(cells_, num_cells * sizeof(*cells_));
1808 }
1809 
yac_bnd_sphere_part_search_do_bnd_circle_search(struct bnd_sphere_part_search * search,struct bounding_circle * bnd_circles,size_t count,size_t ** cells,size_t * num_cells_per_bnd_circle)1810 void yac_bnd_sphere_part_search_do_bnd_circle_search(
1811   struct bnd_sphere_part_search * search, struct bounding_circle * bnd_circles,
1812   size_t count, size_t ** cells, size_t * num_cells_per_bnd_circle) {
1813 
1814   struct sphere_part_node * base_node = &(search->base_node);
1815 
1816   size_t * temp_search_results = NULL;
1817   size_t temp_search_results_array_size = 0;
1818   size_t num_temp_search_results = 0;
1819 
1820   struct overlaps search_interval_tree_buffer = {0, 0, NULL};
1821 
1822   memset(
1823     num_cells_per_bnd_circle, 0, count * sizeof(*num_cells_per_bnd_circle));
1824   size_t * cells_ = NULL;
1825   size_t cells_array_size = 0;
1826   size_t num_cells = 0;
1827   ENSURE_ARRAY_SIZE(cells_, cells_array_size, count);
1828 
1829   for (size_t i = 0; i < count; ++i) {
1830 
1831     num_temp_search_results = 0;
1832 
1833     double gc_norm_vector[3] = {0.0,0.0,1.0};
1834 
1835     search_bnd_circle(base_node, bnd_circles[i], &temp_search_results,
1836                       &temp_search_results_array_size, &num_temp_search_results,
1837                       &search_interval_tree_buffer, gc_norm_vector);
1838 
1839     ENSURE_ARRAY_SIZE(
1840       cells_, cells_array_size, num_cells + num_temp_search_results);
1841 
1842     memcpy(cells_ + num_cells, temp_search_results,
1843            num_temp_search_results * sizeof(*temp_search_results));
1844     num_cells_per_bnd_circle[i] = num_temp_search_results;
1845     num_cells += num_temp_search_results;
1846   }
1847 
1848   free(temp_search_results);
1849   free(search_interval_tree_buffer.overlap_iv);
1850 
1851   *cells = xrealloc(cells_, num_cells * sizeof(*cells_));
1852 }
1853 
free_sphere_part_tree(struct sphere_part_node tree)1854 static void free_sphere_part_tree (struct sphere_part_node tree) {
1855 
1856    // free I_list
1857    if (tree.flags & I_IS_INTERVAL_TREE)
1858       free(tree.I.ivt.head_node);
1859 
1860    if ((tree.flags & U_IS_LEAF) == 0) {
1861       free_sphere_part_tree(*(struct sphere_part_node*)(tree.U));
1862       free(tree.U);
1863    }
1864 
1865    if ((tree.flags & T_IS_LEAF) == 0) {
1866       free_sphere_part_tree(*(struct sphere_part_node*)(tree.T));
1867       free(tree.T);
1868    }
1869 }
1870 
free_point_sphere_part_tree(struct point_sphere_part_node * tree)1871 static void free_point_sphere_part_tree (struct point_sphere_part_node * tree) {
1872 
1873    if ((tree->flags & U_IS_LEAF) == 0) {
1874       free_point_sphere_part_tree(tree->U);
1875       free(tree->U);
1876    }
1877 
1878    if ((tree->flags & T_IS_LEAF) == 0) {
1879       free_point_sphere_part_tree(tree->T);
1880       free(tree->T);
1881    }
1882 }
1883 
yac_delete_point_sphere_part_search(struct point_sphere_part_search * search)1884 void yac_delete_point_sphere_part_search(
1885    struct point_sphere_part_search * search) {
1886 
1887    if (search == NULL) return;
1888 
1889    free_point_sphere_part_tree(&(search->base_node));
1890    free(search->points);
1891    free(search);
1892 }
1893 
yac_bnd_sphere_part_search_delete(struct bnd_sphere_part_search * search)1894 void yac_bnd_sphere_part_search_delete(struct bnd_sphere_part_search * search) {
1895 
1896   if (search == NULL) return;
1897 
1898   free_sphere_part_tree(search->base_node);
1899   free(search->ids);
1900   free(search);
1901 }
1902