star-line

Structure for accelerating line importance sampling
git clone git://git.meso-star.com/star-line.git
Log | Files | Refs | README | LICENSE

sln_tree_build.c (53029B)


      1 /* Copyright (C) 2022, 2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2026 Université de Lorraine
      3  * Copyright (C) 2022 Centre National de la Recherche Scientifique
      4  * Copyright (C) 2022 Université Paul Sabatier
      5  *
      6  * This file is part of Star-Line.
      7  *
      8  * This program is free software: you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation, either version 3 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     20 
     21 #include "sln.h"
     22 #include "sln_device_c.h"
     23 #include "sln_polyline.h"
     24 #include "sln_tree_c.h"
     25 
     26 #include <star/shtr.h>
     27 
     28 #include <rsys/cstr.h>
     29 #include <rsys/math.h>
     30 
     31 #include <omp.h>
     32 
     33 /* Structure used to track the progress of a construction phase */
     34 struct progress {
     35   /* Absolute progression */
     36   size_t total; /* Total number of items to process */
     37   size_t processed; /* Number of items already processed */
     38 
     39   /* Relative progression */
     40   int pcent; /* Currently displayed percentage */
     41   int pcent_step; /* Increment between displayed percentages */
     42 
     43   const char* msg; /* Message to add before the displayed percentage */
     44 };
     45 #define PROGRESS_DEFAULT__ {0,0,0,10,NULL}
     46 static const struct progress PROGRESS_DEFAULT = PROGRESS_DEFAULT__;
     47 
     48 /* Structure containing temporary variables used to create polyline on a leaf
     49  * These variables are not declared locally within the function responsible for
     50  * constructing this polyline in order to avoid having to allocate and
     51  * deallocate them with each call to the function. Instead, they are passed as
     52  * function inputs so as to share, as much as possible, the memory space
     53  * allocated during previous calls to the function, thereby reducing the cost of
     54  * allocation and deallocation. */
     55 struct build_leaf_polyline_scratch {
     56   struct darray_vertex vertices; /* Polyline vertices */
     57   struct darray_node nodes; /* Dummy leaf nodes */
     58 
     59   /* Temp vertex buffer used when merging polylines into a single one */
     60   struct darray_vertex vertices_tmp;
     61 };
     62 
     63 static res_T
     64 merge_child_polylines
     65   (struct sln_tree* tree,
     66    const size_t inode, /* Node at which child polylines are merged */
     67    const unsigned nchildren, /* #children to merge */
     68    struct darray_node* nodes, /* List of nodes */
     69    struct darray_vertex* vertices); /* List of polyline vertices */
     70 
     71 static res_T
     72 collapse_child_polylines
     73   (struct sln_tree* tree,
     74    const size_t inode, /* Node at which child polylines are merged */
     75    const unsigned nchildren, /* #children to collapse */
     76    struct darray_node* nodes, /* List of nodes */
     77    struct darray_vertex* scratch, /* Temporary buffer of vertices */
     78    struct darray_vertex* vertices); /* List of polyline vertices */
     79 
     80 /* A multiplier to be applied to the calculated ka values in order to mitigate
     81  * numerical issues related to ka interpolation in the case of a tree with an
     82  * upper bound. This interpolation must ensure that the interpolated ka value is
     83  * well above the actual ka value, which may not be the case due to numerical
     84  * inaccuracies. Hence this constant, which defines a percentage of ka to be
     85  * added to the calculated ka values to ensure that any interpolated value is
     86  * well above the actual ka value. */
     87 #define KA_ADJUSTEMENT 1.01
     88 
     89 /* Maximum queue size used for the breadth-first tree traversal. For a perfectly
     90  * balanced tree, this corresponds to the maximum number of leaves a tree can
     91  * have. Note that this value does not need to be too high, since the
     92  * breadth-first traversal is only used to partition the first few levels of the
     93  * tree until a sufficient number of subtrees have been identified so that they
     94  * can then be constructed in parallel using a depth-first algorithm */
     95 #define QUEUE_SIZE_MAX 256
     96 
     97 /*******************************************************************************
     98  * Helper functions
     99  ******************************************************************************/
    100 static FINLINE uint32_t
    101 ui64_to_ui32(const uint64_t ui64)
    102 {
    103   if(ui64 > UINT32_MAX)
    104     VFATAL("%s: overflow %lu.\n", ARG2(FUNC_NAME, ((unsigned long)ui64)));
    105   return (uint32_t)ui64;
    106 }
    107 
    108 static FINLINE unsigned
    109 size_t_to_unsigned(const size_t sz)
    110 {
    111   if(sz > UINT_MAX)
    112     VFATAL("%s: overflow %lu.\n", ARG2(FUNC_NAME, ((unsigned long)sz)));
    113   return (unsigned)sz;
    114 }
    115 
    116 static void
    117 progress_print(struct sln_device* dev, const struct progress* progress)
    118 {
    119   ASSERT(dev && progress);
    120   if(progress->msg) {
    121     INFO(dev, "%s%3d%%\n", progress->msg, progress->pcent);
    122   } else {
    123     INFO(dev, "%3d%%\n", progress->pcent);
    124   }
    125 }
    126 
    127 static void
    128 progress_update
    129   (struct sln_device* dev,
    130    struct progress* progress,
    131    const size_t count)
    132 {
    133   int pcent = 0;
    134   ASSERT(dev && progress);
    135 
    136   #pragma omp critical
    137   {
    138     progress->processed += count;
    139     ASSERT(progress->processed <= progress->total);
    140 
    141     pcent = (int)
    142       ( (double)progress->processed*100.0
    143       / (double)progress->total
    144       + 0.5 /* round */);
    145 
    146     if(pcent/progress->pcent_step > progress->pcent/progress->pcent_step) {
    147       progress->pcent = pcent;
    148       progress_print(dev, progress);
    149     }
    150   }
    151 }
    152 
    153 static INLINE void
    154 build_leaf_polyline_scratch_init
    155   (struct mem_allocator* allocator,
    156    struct build_leaf_polyline_scratch* scratch)
    157 {
    158   ASSERT(scratch);
    159   darray_vertex_init(allocator, &scratch->vertices);
    160   darray_vertex_init(allocator, &scratch->vertices_tmp);
    161   darray_node_init(allocator, &scratch->nodes);
    162 }
    163 
    164 static INLINE void
    165 build_leaf_polyline_scratch_release
    166   (struct build_leaf_polyline_scratch* scratch)
    167 {
    168   ASSERT(scratch);
    169   darray_vertex_release(&scratch->vertices);
    170   darray_vertex_release(&scratch->vertices_tmp);
    171   darray_node_release(&scratch->nodes);
    172 }
    173 
    174 static INLINE res_T
    175 build_leaf_polyline_from_1line
    176   (struct sln_tree* tree,
    177    struct sln_node* leaf,
    178    struct darray_vertex* vertices)
    179 {
    180   size_t vertices_range[2];
    181   res_T res = RES_OK;
    182 
    183   ASSERT(tree && leaf && vertices);
    184 
    185   /* Assume that there is only one line per leaf */
    186   ASSERT(leaf->range[0] == leaf->range[1]);
    187 
    188   /* Line meshing */
    189   res = line_mesh
    190     (/* in */  tree, leaf->range[0], tree->args.nvertices_hint,
    191      /* out */ vertices, vertices_range);
    192   if(res != RES_OK) goto error;
    193 
    194   /* Decimate the line mesh */
    195   res = polyline_decimate(tree->sln, darray_vertex_data_get(vertices),
    196      vertices_range, (float)tree->args.mesh_decimation_err, tree->args.mesh_type);
    197   if(res != RES_OK) goto error;
    198 
    199   /* Shrink the size of the vertices */
    200   darray_vertex_resize(vertices, vertices_range[1] + 1);
    201 
    202   /* Setup the leaf polyline  */
    203   leaf->ivertex = vertices_range[0];
    204   leaf->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1);
    205 
    206 exit:
    207   return res;
    208 error:
    209   goto exit;
    210 }
    211 
    212 /* Build the polyline of a leaf node containing more than one line.
    213  *
    214  * Each line is drawn as if it were the only one on the leaf. This allows the
    215  * line meshing routine to be used, and consequently the algorithm that relies
    216  * on the properties of the lines (symmetry, monotonicity on one half). Next,
    217  * this set of polylines is merged/collapsed into a single polyline, which is
    218  * the leaf polyline.
    219  *
    220  * To reuse the merging/collapsing designed for internal nodes, the function
    221  * treats a leaf as if it were an internal node. A list of temporary nodes is
    222  * thus created, containing not only the leaf node but also its “children”
    223  * i.e., its lines.
    224  *
    225  * Once created, the leaf’s polyline is finally copied into the tree’s vertex
    226  * buffer */
    227 static INLINE res_T
    228 build_leaf_polyline_from_Nlines
    229   (struct sln_tree* tree,
    230    struct sln_node* leaf,
    231    struct build_leaf_polyline_scratch* scratch,
    232    struct darray_vertex* vertices)
    233 {
    234   const struct sln_vertex* src = NULL;
    235   struct sln_vertex* dst = NULL;
    236   size_t memsz = 0;
    237 
    238   size_t nnodes = 0;
    239   unsigned nlines = 0; /* Number of lines in the leaf */
    240   unsigned i = 0;
    241   res_T res = RES_OK;
    242 
    243   ASSERT(tree && leaf && scratch && vertices);
    244 
    245   /* Compute the number of lines of the leaf */
    246   nlines = size_t_to_unsigned(leaf->range[1] - leaf->range[0] + 1/*inclusive*/);
    247   ASSERT(nlines > 1); /* Assume there is more than one line per leaf */
    248 
    249   nnodes = nlines + 1/*leaf*/;
    250 
    251   /* Helper macro */
    252   #define NODE(Id) (darray_node_data_get(&scratch->nodes) + (Id))
    253 
    254   /* Allocate the temporary list of nodes, one node per leaf to which on
    255    * additionnal node is added for the leaf */
    256   res = darray_node_resize(&scratch->nodes, nnodes);
    257   if(res != RES_OK) goto error;
    258   memset(NODE(0), 0, sizeof(struct sln_node)*nnodes);
    259 
    260   /* Clean up the temporary list of vertices */
    261   darray_vertex_clear(&scratch->vertices);
    262 
    263   /* The leaf node will be the first one. Its "child" representing the node of
    264    * each lines, will be store after it in the node list */
    265   NODE(0)->offset = 1;
    266   NODE(0)->range[0] = leaf->range[0];
    267   NODE(0)->range[1] = leaf->range[1];
    268 
    269   FOR_EACH(i, 0, nlines) {
    270     size_t vertices_range[2] = {0, 0}; /* Range in the vertex buffer */
    271     const size_t ichild = NODE(0)->offset + i; /* Index of the child */
    272     const size_t iline = leaf->range[0] + i;
    273 
    274     /* Mesh the line in the temporary vertex buffer */
    275     res = line_mesh
    276       (/* in */  tree, iline, tree->args.nvertices_hint,
    277        /* out */ &scratch->vertices, vertices_range);
    278     if(res != RES_OK) goto error;
    279 
    280     /* Decimate the line mesh */
    281     res = polyline_decimate(tree->sln,
    282       darray_vertex_data_get(&scratch->vertices), vertices_range,
    283       (float)tree->args.mesh_decimation_err, tree->args.mesh_type);
    284     if(res != RES_OK) goto error;
    285 
    286     NODE(ichild)->ivertex = vertices_range[0];
    287     NODE(ichild)->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1);
    288     NODE(ichild)->range[0] = iline;
    289     NODE(ichild)->range[1] = iline;
    290   }
    291 
    292   /* Merge/collapse the polylines of each lines associated to nodes.
    293    * These nodes are the "children" of the leaf */
    294   if(tree->args.collapse_polylines) {
    295     res = collapse_child_polylines(tree, 0, nlines,
    296       &scratch->nodes, &scratch->vertices_tmp, &scratch->vertices);
    297   } else {
    298     res = merge_child_polylines(tree, 0, nlines,
    299       &scratch->nodes, &scratch->vertices);
    300   }
    301   if(res != RES_OK) goto error;
    302 
    303   /* Setup the leaf */
    304   leaf->ivertex = darray_vertex_size_get(vertices);
    305   leaf->nvertices = NODE(0/*leaf*/)->nvertices;
    306 
    307   /* Copy the leaf vertices from temporary buffer to the vertex buffer of the
    308    * tree */
    309   res = darray_vertex_resize(vertices, leaf->ivertex + leaf->nvertices);
    310   if(res != RES_OK) goto error;
    311   src = darray_vertex_cdata_get(&scratch->vertices) + NODE(0/*leaf*/)->ivertex;
    312   dst = darray_vertex_data_get(vertices) + leaf->ivertex;
    313   memsz = leaf->nvertices * sizeof(*src);
    314   memcpy(dst, src, memsz);
    315 
    316   #undef NODE
    317 
    318 exit:
    319   return res;
    320 error:
    321   goto exit;
    322 }
    323 
    324 static INLINE res_T
    325 build_leaf_polyline
    326   (struct sln_tree* tree,
    327    struct sln_node* leaf,
    328    struct build_leaf_polyline_scratch* scratch,
    329    struct darray_vertex* vertices)
    330 {
    331   size_t nlines = 0; /* Number of lines in the leaf */
    332   ASSERT(tree && leaf);
    333 
    334   nlines = leaf->range[1] - leaf->range[0] + 1;
    335 
    336   if(nlines == 1) {
    337     return build_leaf_polyline_from_1line(tree, leaf, vertices);
    338   } else {
    339     return build_leaf_polyline_from_Nlines(tree, leaf, scratch, vertices);
    340   }
    341 }
    342 
    343 static INLINE double
    344 eval_ka
    345   (const struct sln_node* node,
    346    const struct sln_vertex* vertices,
    347    const double wavenumber)
    348 {
    349   struct sln_mesh mesh = SLN_MESH_NULL;
    350   double ka = 0;
    351   ASSERT(node && vertices);
    352 
    353   /* Whether the mesh to be constructed corresponds to the spectrum or its upper
    354    * limit, use the node mesh to calculate the value of ka at a given wave
    355    * number. Calculating the value from the node lines would take far too long*/
    356   mesh.vertices = vertices + node->ivertex;
    357   mesh.nvertices = node->nvertices;
    358   ka = sln_mesh_eval(&mesh, wavenumber);
    359   return ka;
    360 }
    361 
    362 /* Merge all child polylines in a single step. The polylines are merged before
    363  * being decimated */
    364 res_T
    365 merge_child_polylines
    366   (struct sln_tree* tree,
    367    const size_t inode,
    368    const unsigned nchildren,
    369    struct darray_node* nodes,
    370    struct darray_vertex* vertex_list)
    371 {
    372   /* Helper constant */
    373   static const size_t NO_MORE_VERTEX = SIZE_MAX;
    374 
    375   /* Polyline vertices */
    376   #define NCHILDREN_MAX \
    377     ( SLN_TREE_ARITY_MAX > SLN_LEAF_NLINES_MAX \
    378     ? SLN_TREE_ARITY_MAX : SLN_LEAF_NLINES_MAX)
    379   size_t children_ivtx[NCHILDREN_MAX] = {0};
    380 
    381   size_t vertices_range[2] = {0,0};
    382   struct sln_vertex* vertices = NULL;
    383   size_t ivtx = 0;
    384   size_t nvertices = 0;
    385 
    386   /* Miscellaneous */
    387   unsigned i = 0;
    388   res_T res = RES_OK;
    389 
    390   /* Pre-conditions */
    391   ASSERT(tree && inode < darray_node_size_get(nodes));
    392   ASSERT(nchildren >= 2 && nchildren <= NCHILDREN_MAX);
    393 
    394   #define NODE(Id) (darray_node_data_get(nodes) + (Id))
    395 
    396   /* Compute the number of vertices to be merged,
    397    * i.e., the sum of vertices of the children */
    398   nvertices = 0;
    399   FOR_EACH(i, 0, nchildren) {
    400     const size_t ichild = inode + NODE(inode)->offset + i;
    401     nvertices += NODE(ichild)->nvertices;
    402   }
    403 
    404   /* Define the vertices range of the merged polyline */
    405   vertices_range[0] = darray_vertex_size_get(vertex_list);
    406   vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/;
    407 
    408   /* Allocate the memory space to store the new polyline */
    409   res = darray_vertex_resize(vertex_list, vertices_range[1]+1);
    410   if(res != RES_OK) {
    411     ERROR(tree->sln, "Error in merging polylines -- %s.\n", res_to_cstr(res));
    412     goto error;
    413   }
    414   vertices = darray_vertex_data_get(vertex_list);
    415 
    416   /* Initialize the vertex index list. For each child, the initial value
    417    * corresponds to the index of its first vertex. This index will be
    418    * incremented as vertices are merged into the parent polyline. */
    419   FOR_EACH(i, 0, nchildren) {
    420     const size_t ichild = inode + NODE(inode)->offset + i;
    421     children_ivtx[i] = NODE(ichild)->ivertex;
    422   }
    423 
    424   FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) {
    425     double ka = 0;
    426     double nu = INF;
    427 
    428     /* The number of vertices corresponding to the current wave number for which
    429      * the parent ka is calculated. It is at least equal to one, since this nu
    430      * is defined by the child vertices, but may be greater if multiple children
    431      * share the same vertex, i.e., a ka value calculated for the same nu */
    432     unsigned nvertices_merged = 0;
    433 
    434     /* Find the minimum wave number among the vertices of the child vertices
    435      * that are candidates for merging */
    436     FOR_EACH(i, 0, nchildren) {
    437       const size_t child_ivtx = children_ivtx[i];
    438       if(child_ivtx != NO_MORE_VERTEX) {
    439         nu = MMIN(nu, vertices[child_ivtx].wavenumber);
    440       }
    441     }
    442     ASSERT(nu != INF); /* At least one vertex must have been found */
    443 
    444     /* Compute the value of ka at the wave number determined above */
    445     FOR_EACH(i, 0, nchildren) {
    446       const size_t child_ivtx = children_ivtx[i];
    447       const struct sln_node* child = NODE(inode + NODE(inode)->offset + i);
    448 
    449       if(child_ivtx == NO_MORE_VERTEX
    450       || nu != vertices[child_ivtx].wavenumber) {
    451         /* The wave number does not correspond to a vertex in the current
    452          * child's mesh. Therefore, its contribution to the parent node's ka is
    453          * computed  */
    454         ka += eval_ka(child, vertices, nu);
    455 
    456       } else {
    457         /* The wave number is the one for which the child node stores a ka
    458          * value. Add it to the parent node's ka value and designate the child's
    459          * next vertex as a candidate for merging into the parent. The exception
    460          * is when all vertices of the child have already been merged. In this
    461          * case, report that the child no longer has any candidate vertices */
    462         ka += vertices[child_ivtx].ka;
    463         ++children_ivtx[i];
    464         if(children_ivtx[i] >= child->ivertex + child->nvertices) {
    465           children_ivtx[i] = NO_MORE_VERTEX;
    466         }
    467         ++nvertices_merged; /* Record that a vertex has been merged */
    468       }
    469     }
    470 
    471     /* Setup the parent vertex */
    472     vertices[ivtx].wavenumber = (float)nu;
    473     vertices[ivtx].ka = (float)ka;
    474 
    475     /* If multiple child vertices have been merged, then a single wave number
    476      * corresponds to a vertex with multiple children. The number of parent
    477      * vertices is therefore no longer the sum of the number of its children's
    478      * vertices, since some vertices are duplicated. Hence the following
    479      * adjustment, which removes the duplicate vertices. */
    480     vertices_range[1] -= (nvertices_merged-1);
    481   }
    482 
    483   /* Decimate the resulting polyline */
    484   res = polyline_decimate(tree->sln, darray_vertex_data_get(vertex_list),
    485      vertices_range, (float)tree->args.mesh_decimation_err, tree->args.mesh_type);
    486   if(res != RES_OK) goto error;
    487 
    488   /* Setup the node polyline */
    489   NODE(inode)->ivertex = vertices_range[0];
    490   NODE(inode)->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1);
    491 
    492   /* It is necessary to ensure that the recorded vertices define a polyline
    493    * along which any value (calculated by linear interpolation) is well above
    494    * the sum of the corresponding values of the polylines to be merged. However,
    495    * although this is guaranteed by definition for the vertices of the polyline,
    496    * numerical uncertainty may nevertheless introduce errors that violate this
    497    * criterion. Hence the following adjustment, which slightly increases the ka
    498    * of the mesh so as to guarantee this constraint between the mesh of a node
    499    * and that of its children */
    500   if(tree->args.mesh_type == SLN_MESH_UPPER) {
    501     FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) {
    502       const double ka = vertices[ivtx].ka;
    503       vertices[ivtx].ka = (float)(ka*KA_ADJUSTEMENT);
    504     }
    505   }
    506 
    507   /* Shrink the size of the vertices */
    508   darray_vertex_resize(vertex_list, vertices_range[1] + 1);
    509 
    510   #undef NODE
    511   #undef NCHILDREN_MAX
    512 
    513 exit:
    514   return res;
    515 error:
    516   goto exit;
    517 }
    518 
    519 /* Merge child polylines by combining them in pairs (the resulting polyline is
    520  * then simplified), and repeat this process until only a single polyline
    521  * remains. This polyline becomes the node's polyline */
    522 static res_T
    523 collapse_child_polylines
    524   (struct sln_tree* tree,
    525    const size_t inode,
    526    const unsigned nchildren,
    527    struct darray_node* nodes,
    528    struct darray_vertex* scratch,
    529    struct darray_vertex* vertex_list)
    530 {
    531   /* Polylines to be collapsed, i.e., the ids of their first and last vertices */
    532   #define NCHILDREN_MAX \
    533     ( SLN_TREE_ARITY_MAX > SLN_LEAF_NLINES_MAX \
    534     ? SLN_TREE_ARITY_MAX : SLN_LEAF_NLINES_MAX)
    535   size_t poly_parts[NCHILDREN_MAX][2] = {0};
    536   size_t nparts;
    537 
    538   /* Indices of the first and last vertices of the resulting polyline.
    539    * These indices are absolute to the tree's vertex list */
    540   size_t vertices_range[2] = {0,0};
    541 
    542   /* Redux double buffering */
    543   struct sln_vertex* buf[2] = {NULL, NULL};
    544   int r, w; /* Index of the buffer in read/write */
    545 
    546   /* Miscellaneous */
    547   struct sln_vertex* vertices = NULL; /* Pointer to the tree's vertex buffer */
    548   size_t range_merge[2] = {0,0}; /* vertex range of a merged polyline */
    549   size_t nvertices = 0; /* #vertices of the resulting polyline */
    550   size_t ivtx = 0;
    551   unsigned i = 0;
    552   int ncollapses = 0; /* Number of collapse steps */
    553   res_T res = RES_OK;
    554 
    555   /* Pre-conditions */
    556   ASSERT(tree && inode < darray_node_size_get(nodes));
    557   ASSERT(nchildren >= 2 && nchildren <= NCHILDREN_MAX);
    558 
    559   #define NODE(Id) (darray_node_data_get(nodes) + (Id))
    560 
    561   /* Compute the number of vertices to be merged,
    562    * i.e., the sum of vertices of the children */
    563   nvertices = 0;
    564   FOR_EACH(i, 0, nchildren) {
    565     const size_t ichild = inode + NODE(inode)->offset + i;
    566     nvertices += NODE(ichild)->nvertices;
    567   }
    568 
    569   /* Define the vertices range of the merged polyline */
    570   vertices_range[0] = darray_vertex_size_get(vertex_list);
    571   vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/;
    572 
    573   /* Allocate the memory space required to store the new polyline and the
    574    * temporary buffer. This is the memory space in which the collapse
    575    * procedure's double buffering will take place. Each buffer will be used
    576    * alternately as a read/write buffer when reducing the polylines of the
    577    * child nodes */
    578   if((res = darray_vertex_resize(vertex_list, vertices_range[1]+1)) != RES_OK
    579   || (res = darray_vertex_resize(scratch, nvertices)) != RES_OK) {
    580     ERROR(tree->sln, "Error in merging polylines -- %s.\n", res_to_cstr(res));
    581     goto error;
    582   }
    583 
    584   /* Recover the memory space of the tree's vertices */
    585   vertices = darray_vertex_data_get(vertex_list);
    586 
    587   /* Recover the memory space to be used in the Redux process.
    588    *
    589    * In the vertex buffer, this refers to the newly allocated memory space used
    590    * during the collapse. The beginning of the buffer contains the vertices of
    591    * the registered nodes and must therefore not be modified. At the end of the
    592    * collapse, this space will contain the vertices of the polylines resulting
    593    * from the collapse of the child nodes' polylines.
    594    *
    595    * The scratch buffer is used as-is, since its sole purpose is to temporarily
    596    * store the vertices of the polylines to be merged. */
    597   buf[0] = vertices + vertices_range[0];
    598   buf[1] = darray_vertex_data_get(scratch);
    599 
    600   /* Initially, the number of partitions to be reduced is equal to the number of
    601    * children */
    602   nparts = nchildren;
    603 
    604   /* Calculate the number of reduction steps, i.e., the logarithm of the power
    605    * of 2 that is greater than or equal to the number of polylines to be
    606    * reduced, to which one is added to obtain the number of steps, not the index
    607    * of the last step */
    608   ncollapses = log2i((int)round_up_pow2(nparts)) + 1;
    609 
    610   /* Set the index of the write buffer so that, once the collapse process is
    611    * complete, the last write buffer is the one whose memory space is allocated
    612    * in the tree's vertex buffer. This way, no additional copies will be needed
    613    * to store the result of the reduction */
    614   w = ncollapses % 2 ? 0 : 1;
    615 
    616   /* Initialize the polylines to be reduced, i.e., copy the child vertices into
    617    * a collapse buffer and record their index ranges */
    618   ivtx = 0;
    619   for(i = 0; i < nchildren; ++i) {
    620     const size_t ichild = inode + NODE(inode)->offset + i;
    621     const struct sln_node* child = NODE(ichild);
    622     const size_t memsz = child->nvertices * sizeof(struct sln_vertex);
    623     const struct sln_vertex* src = vertices + child->ivertex;
    624     struct sln_vertex* dst = buf[w] + ivtx;
    625 
    626     memcpy(dst, src, memsz);
    627     poly_parts[i][0] = ivtx;
    628     poly_parts[i][1] = ivtx + child->nvertices - 1/*inclusive bound*/;
    629 
    630     ivtx += child->nvertices;
    631   }
    632 
    633   r = w; /* Index of the buffer from which to read the data */
    634   w =!r; /* Index of the buffer into which to write the data  */
    635 
    636 
    637   /* As long as the number of segments is not equal to one, there are still
    638    * polylines to be merged in pairs */
    639   while(nparts > 1) {
    640     size_t ipart = 0;
    641 
    642     for(ipart=0; ipart < nparts-1; ipart+=2) {
    643       struct sln_mesh mesh0 = SLN_MESH_NULL;
    644       struct sln_mesh mesh1 = SLN_MESH_NULL;
    645 
    646       /* Retrieve the two polylines to be merged */
    647       const size_t* part0 = poly_parts[ipart+0];
    648       const size_t* part1 = poly_parts[ipart+1];
    649 
    650       /* Calculate the partition index of the merged polyline, that is, the index
    651        * that will contain the information for the polyline resulting from the
    652        * merge: the first and last indices of its in the vertex array currently
    653        * being written */
    654       const size_t ipart_merge = ipart/2;
    655 
    656       /* Compute the number of vertices of the merged polyline */
    657       const size_t nvtx =
    658         ((part0[1] - part0[0]) + 1/*inclusive bound*/)
    659       + ((part1[1] - part1[0]) + 1/*inclusive bound*/);
    660 
    661       /* For each child, initialized its vertex index to its first vertex. This
    662        * index will be incremented as vertices are merged into the merged
    663        * polyline  */
    664       size_t ivtx0 = part0[0];
    665       size_t ivtx1 = part1[0];
    666 
    667       /* Set the vertex index range for the merged polyline in the write buffer */
    668       range_merge[0] = part0[0];
    669       range_merge[1] = range_merge[0] + nvtx-1/*inclusive bound*/;
    670 
    671       /* Setup the mesh of the two polylines to be merged */
    672       mesh0.vertices = buf[r] + part0[0]; mesh0.nvertices = part0[1]-part0[0]+1;
    673       mesh1.vertices = buf[r] + part1[0]; mesh1.nvertices = part1[1]-part1[0]+1;
    674 
    675       /* Merge the polylines */
    676       FOR_EACH(ivtx, range_merge[0], range_merge[1]+1/*inclusive bound*/) {
    677         const double nu0 = ivtx0 <= part0[1] ? buf[r][ivtx0].wavenumber : INF;
    678         const double nu1 = ivtx1 <= part1[1] ? buf[r][ivtx1].wavenumber : INF;
    679         double ka = 0;
    680         double nu = 0;
    681 
    682         /* Find the minimum wave number among the vertices of the child vertices
    683          * that are candidates for merging */
    684         if(nu0 < nu1) { /* The vertex comes from the child0 */
    685           nu = buf[r][ivtx0].wavenumber;
    686           ka = buf[r][ivtx0].ka + sln_mesh_eval(&mesh1, nu);
    687           ++ivtx0;
    688 
    689         } else if(nu0 > nu1) { /* The vertex comes from the child1 */
    690           nu = buf[r][ivtx1].wavenumber;
    691           ka = buf[r][ivtx1].ka + sln_mesh_eval(&mesh0, nu);
    692           ++ivtx1;
    693 
    694         } else { /* The vertex is shared by node0 and node1 */
    695           nu = buf[r][ivtx0].wavenumber;
    696           ka = buf[r][ivtx0].ka + buf[r][ivtx1].ka;
    697           --range_merge[1]; /* Remove duplicate */
    698           ++ivtx0;
    699           ++ivtx1;
    700         }
    701         buf[w][ivtx].wavenumber = (float)nu;
    702         buf[w][ivtx].ka = (float)ka;
    703       }
    704 
    705       /* Decimate the resulting polyline */
    706       res = polyline_decimate(tree->sln, buf[w], range_merge,
    707         (float)tree->args.mesh_decimation_err, tree->args.mesh_type);
    708       if(res != RES_OK) goto error;
    709 
    710       /* Setup the partition of the merge polyline */
    711       poly_parts[ipart_merge][0] = range_merge[0];
    712       poly_parts[ipart_merge][1] = range_merge[1];
    713     }
    714 
    715     /* If there is a polyline that has not been merged, copy its vertices to the
    716      * write buffer so that it can be processed in the next reduction step */
    717     if(nparts % 2) {
    718       const size_t* remain_part = poly_parts[nparts-1];
    719       const size_t nvtx = remain_part[1] - remain_part[0] + 1/*inclusive*/;
    720       const size_t memsz = nvtx * sizeof(struct sln_vertex);
    721       const struct sln_vertex* src = buf[r] + remain_part[0];
    722       struct sln_vertex* dst = buf[w] + remain_part[0];
    723 
    724       memcpy(dst, src, memsz);
    725       poly_parts[nparts/2][0] = remain_part[0];
    726       poly_parts[nparts/2][1] = remain_part[1];
    727     }
    728 
    729 #ifndef NDEBUG
    730     FOR_EACH(i, 1, (nparts+1/*ceil*/)/2) {
    731       ASSERT(poly_parts[i][0] > poly_parts[i-1][1]);
    732     }
    733 #endif
    734 
    735     /* Update the number of partitions to be reduced */
    736     nparts = (nparts + 1/*ceil*/)/2;
    737 
    738     /* Swap read/write buffers */
    739     r = !r;
    740     w = !w;
    741   }
    742 
    743   nvertices = (range_merge[1] - range_merge[0]) + 1/*inclusive bound*/;
    744   vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/;
    745 
    746   /* Assumed that double buffering was configured to ensure that the resulting
    747    * polyline is stored in the tree vertex buffer */
    748   ASSERT(buf[r] != darray_vertex_cdata_get(scratch));
    749 
    750   /* Setup the node */
    751   NODE(inode)->ivertex = vertices_range[0];
    752   NODE(inode)->nvertices = ui64_to_ui32(nvertices);
    753 
    754   /* It is necessary to ensure that the recorded vertices define a polyline
    755    * along which any value (calculated by linear interpolation) is well above
    756    * the sum of the corresponding values of the polylines to be merged. However,
    757    * although this is guaranteed by definition for the vertices of the polyline,
    758    * numerical uncertainty may nevertheless introduce errors that violate this
    759    * criterion. Hence the following adjustment, which slightly increases the ka
    760    * of the mesh so as to guarantee this constraint between the mesh of a node
    761    * and that of its children */
    762   if(tree->args.mesh_type == SLN_MESH_UPPER) {
    763     FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) {
    764       const double ka = vertices[ivtx].ka;
    765       vertices[ivtx].ka = (float)(ka*KA_ADJUSTEMENT);
    766     }
    767   }
    768 
    769   /* Shrink the size of the vertices */
    770   darray_vertex_resize(vertex_list, vertices_range[1] + 1);
    771 
    772   #undef NCHILDREN
    773   #undef NODE
    774 
    775 exit:
    776   return res;
    777 error:
    778   goto exit;
    779 }
    780 static res_T
    781 build_polylines
    782   (struct sln_tree* tree,
    783    const size_t root_index,
    784    const size_t nodes_count, /* Total number of nodes in the tree (for debug) */
    785    struct darray_vertex* vertices,
    786    struct progress* progress)
    787 {
    788   /* Stack */
    789   #define STACK_SIZE (SLN_TREE_DEPTH_MAX*SLN_TREE_ARITY_MAX)
    790   size_t stack[STACK_SIZE];
    791   size_t istack = 0;
    792 
    793   /* Progress */
    794   size_t nnodes_processed = 0;
    795 
    796   /* Miscellaneous */
    797   struct build_leaf_polyline_scratch scratch;
    798   size_t inode = 0;
    799   res_T res = RES_OK;
    800 
    801   ASSERT(tree && nodes_count != 0);
    802   ASSERT(root_index < darray_node_size_get(&tree->nodes));
    803   (void)nodes_count; /* Avoid "Unused variable" warning */
    804 
    805   build_leaf_polyline_scratch_init(tree->sln->allocator, &scratch);
    806 
    807   #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id))
    808   #define IS_LEAF(Id) (NODE(Id)->offset == 0)
    809 
    810   /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */
    811   stack[istack++] = SIZE_MAX;
    812 
    813   inode = root_index; /* Root node */
    814   while(inode != SIZE_MAX) {
    815     const size_t istack_saved = istack;
    816 
    817     if(IS_LEAF(inode)) {
    818       res = build_leaf_polyline(tree, NODE(inode), &scratch, vertices);
    819       if(res != RES_OK) goto error;
    820 
    821       inode = stack[--istack]; /* Pop the next node */
    822 
    823     } else {
    824       const size_t ichild0 = inode + NODE(inode)->offset + 0;
    825       const struct sln_node* node = darray_node_cdata_get(&tree->nodes)+inode;
    826       const unsigned nchildren = node_child_count(node, tree->args.arity);
    827       int child_polylines_are_missing = 1;
    828       size_t i = 0;
    829 
    830       FOR_EACH(i, 0, nchildren) {
    831         child_polylines_are_missing = NODE(ichild0 + i)->nvertices == 0;
    832         if(child_polylines_are_missing) break;
    833       }
    834 
    835       /* Child nodes have their polyline created */
    836       if(!child_polylines_are_missing) {
    837 #ifndef NDEBUG
    838         /* Check that all children have their polylines created */
    839         FOR_EACH(i, 1, nchildren) {
    840           const size_t ichild = ichild0 + i;
    841           ASSERT(NODE(ichild)->nvertices != 0);
    842         }
    843 #endif
    844         if(tree->args.collapse_polylines) {
    845           res = collapse_child_polylines(tree, inode, nchildren, &tree->nodes,
    846             &scratch.vertices_tmp, vertices);
    847         } else {
    848           res = merge_child_polylines
    849             (tree, inode, nchildren, &tree->nodes, vertices);
    850         }
    851         if(res != RES_OK) goto error;
    852 
    853         inode = stack[--istack]; /* Pop the next node */
    854 
    855       /* Child nodes have NOT their polyline created */
    856       } else {
    857         ASSERT(istack + (nchildren - 1/*ichild0*/ + 1/*inode*/) <= STACK_SIZE);
    858         stack[istack++] = inode; /* Push the current node */
    859 
    860          /* Push the child nodes, except for those whose polyline has already
    861           * been constructed, as is the case when the child node was the root
    862           * of a separately constructed subtree */
    863         FOR_EACH_REVERSE(i, nchildren, 0) {
    864           const size_t ichild = ichild0 + i-1;
    865           if(NODE(ichild)->nvertices == 0) stack[istack++] = ichild;
    866         }
    867 
    868         /* Ensure that at least one child was pushed */
    869         ASSERT(stack[istack-1] != inode);
    870 
    871         inode = stack[--istack]; /* Recursively build the polyline of the 1st child */
    872       }
    873     }
    874 
    875     /* Handle progression bar */
    876     if(istack < istack_saved) {
    877       size_t nnodes = istack_saved - istack;
    878 
    879       nnodes_processed += nnodes;
    880       progress_update(tree->sln, progress, nnodes);
    881     }
    882   }
    883   ASSERT(nnodes_processed == nodes_count);
    884 
    885   #undef NODE
    886   #undef IS_LEAF
    887   #undef LOG_MSG
    888   #undef STACK_SIZE
    889 
    890 exit:
    891   build_leaf_polyline_scratch_release(&scratch);
    892   return res;
    893 error:
    894   goto exit;
    895 }
    896 
    897 static res_T
    898 partition_lines_depth_first
    899   (struct sln_tree* tree,
    900    const size_t root_index,
    901    struct darray_node* nodes,
    902    struct progress* progress)
    903 {
    904   /* Stack */
    905   #define STACK_SIZE (SLN_TREE_DEPTH_MAX*(SLN_TREE_ARITY_MAX-1))
    906   size_t stack[STACK_SIZE];
    907   size_t istack = 0;
    908 
    909   /* Progress */
    910   size_t nlines_total = 0;
    911   size_t nlines_processed = 0;
    912 
    913   /* Miscellaneous */
    914   size_t inode = 0;
    915   res_T res = RES_OK;
    916 
    917   /* Pre-condition */
    918   ASSERT(tree && nodes && progress);
    919   ASSERT(root_index < darray_node_size_get(nodes));
    920   (void)nlines_total; /* Avoid "Unused variable" warning */
    921 
    922   #define NODE(Id) (darray_node_data_get(nodes) + (Id))
    923   #define CREATE_NODE {                                                        \
    924     res = darray_node_push_back(nodes, &SLN_NODE_NULL);                        \
    925     if(res != RES_OK) goto error;                                              \
    926   } (void)0
    927 
    928   nlines_total = NODE(root_index)->range[1] - NODE(root_index)->range[0] + 1;
    929   nlines_processed = 0;
    930 
    931   /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */
    932   stack[istack++] = SIZE_MAX;
    933 
    934   inode = root_index; /* Root node */
    935   while(inode != SIZE_MAX) {
    936     /* #lines into the node */
    937     size_t nlines_node = NODE(inode)->range[1] - NODE(inode)->range[0] + 1;
    938 
    939     /* Make a leaf */
    940     if(nlines_node <= tree->args.leaf_nlines) {
    941 
    942       NODE(inode)->offset = 0;
    943       inode = stack[--istack]; /* Pop the next node */
    944 
    945       nlines_processed += nlines_node; /* For debug */
    946 
    947       progress_update(tree->sln, progress, nlines_node);
    948 
    949     /* Split the node  */
    950     } else {
    951       size_t node_range[2] = {0,0};
    952       size_t nlines_child_min = 0; /* Min #lines per child */
    953       size_t nlines_remain = 0;
    954       size_t nchildren = 0;
    955       size_t iline = 0;
    956       size_t i = 0;
    957 
    958       /* Calculate the index of the first child */
    959       size_t ichildren = darray_node_size_get(nodes);
    960 
    961       /* Compute how the number of children that node has */
    962       nchildren = node_child_count(NODE(inode), tree->args.arity);
    963 
    964       ASSERT(nchildren <= tree->args.arity);
    965       ASSERT(ichildren > inode);
    966 
    967       node_range[0] = NODE(inode)->range[0];
    968       node_range[1] = NODE(inode)->range[1];
    969 
    970       /* Define the offset from the current node to its children */
    971       NODE(inode)->offset = ui64_to_ui32((uint64_t)(ichildren - inode));
    972 
    973       nlines_child_min = nlines_node / nchildren;
    974       nlines_remain = nlines_node % nchildren;
    975 
    976       iline = node_range[0];
    977       FOR_EACH(i, 0, nchildren) {
    978         /* Compute the number of lines per child. Start by assigning the minimum
    979          * number of lines to each child, then distribute the remaining lines
    980          * among the first children */
    981         size_t nlines_child = nlines_child_min + (i < nlines_remain);
    982 
    983         CREATE_NODE;
    984 
    985         /* Set the range of lines line for the newly created child. Note that
    986          * the boundaries of the range are inclusive, which is why 1 is
    987          * subtracted to the upper bound */
    988         NODE(ichildren+i)->range[0] = iline;
    989         NODE(ichildren+i)->range[1] = iline + nlines_child - 1/*inclusive bound*/;
    990         iline += nlines_child;
    991 
    992         /* Check that the child's lines are a subset of the parent's lines */
    993         ASSERT(NODE(ichildren+i)->range[0] >= node_range[0]);
    994         ASSERT(NODE(ichildren+i)->range[1] <= node_range[1]);
    995       }
    996 
    997       inode = ichildren; /* Make the first child the current node */
    998 
    999       /* Push the other children */
   1000       ASSERT(istack + (nchildren-1/*1st child*/) <= STACK_SIZE);
   1001       FOR_EACH_REVERSE(i, nchildren-1, 0) stack[istack++] = ichildren + i;
   1002     }
   1003   }
   1004   ASSERT(nlines_processed == nlines_total);
   1005 
   1006   #undef NODE
   1007   #undef CREATE_NODE
   1008   #undef STACK_SIZE
   1009 
   1010 exit:
   1011   return res;
   1012 error:
   1013   goto exit;
   1014 }
   1015 
   1016 static res_T
   1017 partition_lines_breadth_first
   1018   (struct sln_tree* tree,
   1019    const size_t root_index,
   1020    const unsigned nleaves_max_hint) /* Advice on the maxium of leafs to create */
   1021 {
   1022   /* Static memory space of the queue */
   1023   struct item {
   1024     struct list_node link;
   1025     size_t inode;
   1026   } items[QUEUE_SIZE_MAX];
   1027 
   1028   /* Linked lists for managing queue data */
   1029   struct list_node free_items;
   1030   struct list_node queue;
   1031   size_t nnodes = 0; /* Number of enqueud nodes */
   1032 
   1033   /* Miscellaneous */
   1034   size_t nlines_total = 0;
   1035   size_t nleaves = 0;
   1036   size_t i = 0;
   1037   res_T res = RES_OK;
   1038 
   1039   /* Pre-conditions */
   1040   ASSERT(tree);
   1041   ASSERT(root_index < darray_node_size_get(&tree->nodes));
   1042 
   1043   /* Macros to help in managing nodes */
   1044   #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id))
   1045   #define CREATE_NODE {                                                        \
   1046     res = darray_node_push_back(&tree->nodes, &SLN_NODE_NULL);                 \
   1047     if(res != RES_OK) goto error;                                              \
   1048   } (void)0
   1049 
   1050   /* Helper macro for the queue data structure */
   1051   #define ENQUEUE(INode) {                                                     \
   1052     struct item* item__ = NULL;                                                \
   1053     ASSERT(!is_list_empty(&free_items));                                       \
   1054     item__ = CONTAINER_OF(list_head(&free_items), struct item, link);          \
   1055     item__->inode = INode;                                                     \
   1056     list_move_tail(&item__->link, &queue);                                     \
   1057     ++nnodes;                                                                  \
   1058   }
   1059   #define DEQUEUE                                                              \
   1060     (list_move_tail(list_head(&queue), &free_items),                           \
   1061     --nnodes,                                                                  \
   1062     CONTAINER_OF(list_tail(&free_items), struct item, link)->inode)
   1063 
   1064   /* Setup the linked lists */
   1065   list_init(&free_items);
   1066   list_init(&queue);
   1067   FOR_EACH(i, 0, sizeof(items)/sizeof(items[0])) {
   1068     items[i].inode = SIZE_MAX;
   1069     list_init(&items[i].link);
   1070     list_add_tail(&free_items, &items[i].link);
   1071   }
   1072 
   1073   SHTR(line_list_get_size(tree->args.lines, &nlines_total));
   1074 
   1075   /* Setup the root node */
   1076   NODE(root_index)->range[0] = 0;
   1077   NODE(root_index)->range[1] = nlines_total - 1;
   1078 
   1079   /* Register the root node in the queue */
   1080   ENQUEUE(root_index);
   1081 
   1082   /* Breadth-first partitioning */
   1083   while(!is_list_empty(&queue)) {
   1084     size_t node_range[2] = {0,0};
   1085     size_t nlines_node = 0; /* #lines in the node */
   1086     size_t nlines_child_min = 0; /* Min #lines per child */
   1087     size_t nlines_remain = 0; /* Lines to be distributed among the children */
   1088     size_t nchildren = 0; /* #children of the node */
   1089     size_t ichildren = 0; /* Index of the node's first child */
   1090     size_t iline = 0;
   1091     size_t inode = 0;
   1092 
   1093 
   1094     /* Check whether the number of registered leaves and currently processed
   1095      * nodes is greater than or equal to the recommended maximum number of
   1096      * leaves. If so, stop breadth-first partitioning and treat all registered
   1097      * nodes as leaves. The policy is therefore to accept more leaves than
   1098      * expected */
   1099     if(nnodes + nleaves >= nleaves_max_hint) {
   1100       nleaves += nnodes;
   1101       break;
   1102     }
   1103 
   1104     inode = DEQUEUE; /* Get the current node */
   1105 
   1106     /* Retrieve the range of lines contained within the node */
   1107     node_range[0] = NODE(inode)->range[0];
   1108     node_range[1] = NODE(inode)->range[1];
   1109     nlines_node = node_range[1] - node_range[0] + 1/*inclusive bound*/;
   1110     ASSERT(nlines_node >= tree->args.leaf_nlines);
   1111 
   1112     if(nlines_node <= tree->args.leaf_nlines) {
   1113       /* Make a leaf */
   1114       ++nleaves;
   1115       continue;
   1116     }
   1117 
   1118     /* Compute the number of children that node has */
   1119     nchildren = node_child_count(NODE(inode), tree->args.arity);
   1120     ASSERT(nchildren <= tree->args.arity);
   1121 
   1122     /* Check whether, after adding the child nodes to the queue, the total
   1123      * number of nodes in the queue, plus the number of leaves already created,
   1124      * does not exceed the max size of the queue. If so, the breadth-first
   1125      * partitioning is complete and the nodes currently in the queue are all
   1126      * leaves */
   1127     if(nnodes + nchildren + nleaves > QUEUE_SIZE_MAX) {
   1128       nleaves += nnodes + 1/*Do not forget the the current node*/;
   1129       break;
   1130     }
   1131 
   1132     /* Calculate the index of the first child */
   1133     ichildren = darray_node_size_get(&tree->nodes);
   1134     ASSERT(ichildren > inode);
   1135 
   1136     /* Define the offset from the current node to its children */
   1137     NODE(inode)->offset = ui64_to_ui32((uint64_t)(ichildren - inode));
   1138 
   1139     /* Calculate the minimum number of lines per child and the number of
   1140      * remaining lines to be distributed equally among them */
   1141     nlines_child_min = nlines_node / nchildren;
   1142     nlines_remain = nlines_node % nchildren;
   1143 
   1144     iline = node_range[0];
   1145     FOR_EACH(i, 0, nchildren) {
   1146       /* Compute the number of lines per child. Start by assigning the minimum
   1147        * number of lines to each child, then distribute the remaining lines
   1148        * among the first children */
   1149       size_t nlines_child = nlines_child_min + (i < nlines_remain);
   1150 
   1151       CREATE_NODE;
   1152 
   1153       /* Set the range of lines line for the newly created child. Note that
   1154        * the boundaries of the range are inclusive, which is why 1 is
   1155        * subtracted to the upper bound */
   1156       NODE(ichildren+i)->range[0] = iline;
   1157       NODE(ichildren+i)->range[1] = iline + nlines_child - 1/*inclusive bound*/;
   1158       iline += nlines_child;
   1159 
   1160       /* Check that the child's lines are a subset of the parent's lines */
   1161       ASSERT(NODE(ichildren+i)->range[0] >= node_range[0]);
   1162       ASSERT(NODE(ichildren+i)->range[1] <= node_range[1]);
   1163 
   1164       ENQUEUE(ichildren+i); /* Register the child node */
   1165     }
   1166   }
   1167 
   1168   #undef NODE
   1169   #undef CREATE_NODE
   1170   #undef ENQUEUE
   1171   #undef DEQUEUE
   1172 
   1173 exit:
   1174   return res;
   1175 error:
   1176   goto exit;
   1177 }
   1178 
   1179 static res_T
   1180 build_subtrees
   1181   (struct sln_tree* tree,
   1182    const size_t subtrees[],
   1183    unsigned nsubtrees,
   1184    struct progress* meshing)
   1185 {
   1186   /* Subtree temporary data */
   1187   struct scratch {
   1188     struct darray_node nodes;
   1189     struct darray_vertex vertices;
   1190     size_t nnodes;
   1191   }* scratches;
   1192 
   1193   /* Miscellaneous */
   1194   struct progress partitioning = PROGRESS_DEFAULT;
   1195   struct mem_allocator* allocator = NULL;
   1196   size_t nlines = 0;
   1197   unsigned nthreads = 0;
   1198   int i = 0;
   1199   ATOMIC res = RES_OK;
   1200 
   1201   /* Pre-conditions */
   1202   ASSERT(tree && subtrees && nsubtrees >= 1);
   1203   ASSERT(nsubtrees < INT_MAX);
   1204 
   1205   #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id))
   1206   #define IS_LEAF(Buf, Id) (darray_node_cdata_get(Buf)[Id].offset == 0)
   1207 
   1208   allocator = tree->sln->allocator;
   1209 
   1210   /* Allocate the per subtree scratch */
   1211   scratches = MEM_CALLOC(allocator, nsubtrees, sizeof(*scratches));
   1212   if(!scratches) { res = RES_MEM_ERR; goto error; }
   1213   FOR_EACH(i, 0, (int)nsubtrees) {
   1214     darray_node_init(allocator, &scratches[i].nodes);
   1215     darray_vertex_init(allocator, &scratches[i].vertices);
   1216   }
   1217 
   1218   /* Setup the progress bar and print that although nothing has been done yet,
   1219    * the calculation is nevertheless in progress */
   1220   SHTR(line_list_get_size(tree->args.lines, &nlines));
   1221   partitioning.total = nlines;
   1222   partitioning.msg = "Partitioning: ";
   1223   progress_print(tree->sln, &partitioning);
   1224 
   1225   /* Set the number of threads to use. The advice on the number of threads must
   1226    * not exceed the number of threads available on the machine (see the
   1227    * sln_tree_create function); the caller can only specify a lower number of
   1228    * threads. urthermore, the number of subtrees is calculated so that each subtree
   1229    * corresponds to at least one thread, ensuring that there are no more
   1230    * subtrees than available threads.
   1231    *
   1232    * The actual number of threads to be used for constructing the subtrees in
   1233    * parallel should therefore always be set to the number of subtrees. However,
   1234    * to provide greater flexibility in the distribution of threads or the number
   1235    * of subtrees, the number of threads is calculated below as the minimum
   1236    * between the number of available threads and the number of subtrees */
   1237   nthreads = MMIN(tree->args.nthreads_hint, nsubtrees);
   1238   omp_set_num_threads((int)nthreads);
   1239 
   1240   #pragma omp parallel for
   1241   for(i=0; i < (int)nsubtrees; ++i) {
   1242     struct sln_node root = SLN_NODE_NULL;
   1243     struct scratch* scratch = scratches + i;
   1244     size_t nnodes_s = 0;
   1245     size_t nnodes_t = 0;
   1246     res_T res2 = RES_OK;
   1247 
   1248     /* Skip the remaining subtrees in case of an error */
   1249     if(ATOMIC_GET(&res) != RES_OK) continue;
   1250 
   1251     /* Copy the subtree root in the temporary node buffer */
   1252     #pragma omp critical
   1253     {
   1254       root = *NODE(subtrees[i]);
   1255     }
   1256     res2 = darray_node_push_back(&scratch->nodes, &root);
   1257     if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; }
   1258 
   1259     /* Partition the line */
   1260     res2 = partition_lines_depth_first
   1261       (tree, 0/*root*/, &scratch->nodes, &partitioning);
   1262     if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; }
   1263 
   1264     #pragma omp critical
   1265     {
   1266       struct sln_node* dst = NULL;
   1267       const struct sln_node* src = NULL;
   1268 
   1269       /* Increase the node buffer to store the subtree nodes */
   1270       nnodes_s = darray_node_size_get(&scratch->nodes);
   1271       nnodes_t = darray_node_size_get(&tree->nodes);
   1272       res2 = darray_node_resize(&tree->nodes, nnodes_t + nnodes_s - 1/*root*/);
   1273       if(res2 == RES_OK) {
   1274 
   1275         if(!IS_LEAF(&scratch->nodes, 0)) {
   1276           /* Set the offset to the first child of the root node of the subtree */
   1277           NODE(subtrees[i])->offset = ui64_to_ui32(nnodes_t - subtrees[i]);
   1278         }
   1279 
   1280         /* Copy the subtree nodes in the main node buffer */
   1281         src = darray_node_cdata_get(&scratch->nodes) + 1/*discard root*/;
   1282         dst = darray_node_data_get(&tree->nodes) + nnodes_t;
   1283         memcpy(dst, src, (nnodes_s-1/*discard root*/)*sizeof(*src));
   1284       }
   1285     }
   1286     if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; }
   1287 
   1288     /* Free the temporary buffer but retain the number of nodes in the subtree.
   1289      * This number is used later when constructing polylines (see below) */
   1290     darray_node_purge(&scratch->nodes);
   1291     scratch->nnodes = nnodes_s;
   1292   }
   1293 
   1294   /* Setup the progress bar and print that although nothing has been done yet,
   1295    * the calculation is nevertheless in progress */
   1296   *meshing = PROGRESS_DEFAULT;
   1297   meshing->total = darray_node_size_get(&tree->nodes);
   1298   meshing->msg = "Meshing: ";
   1299   progress_print(tree->sln, meshing);
   1300 
   1301   #pragma omp parallel for
   1302   for(i=0; i < (int)nsubtrees; ++i) {
   1303     struct scratch* scratch = scratches + i;
   1304     size_t inode = subtrees[i];
   1305     size_t nverts_s = 0;
   1306     size_t nverts_t = 0;
   1307     size_t j = 0;
   1308     res_T res2 = RES_OK;
   1309 
   1310     /* Skip the remaining subtrees in case of an error */
   1311     if(ATOMIC_GET(&res) != RES_OK) continue;
   1312 
   1313     res2 = build_polylines
   1314       (tree, inode, scratch->nnodes, &scratch->vertices, meshing);
   1315     if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; }
   1316 
   1317     #pragma omp critical
   1318     {
   1319       struct sln_vertex* dst = NULL;
   1320       const struct sln_vertex* src = NULL;
   1321 
   1322       /* Increase the vertex buffer to store the subtree polylines */
   1323       nverts_s = darray_vertex_size_get(&scratch->vertices);
   1324       nverts_t = darray_vertex_size_get(&tree->vertices);
   1325       res2 = darray_vertex_resize(&tree->vertices, nverts_t + nverts_s);
   1326       if(res2 == RES_OK) {
   1327         /* Copy the subtree nodes in the main vertex buffer */
   1328         src = darray_vertex_cdata_get(&scratch->vertices);
   1329         dst = darray_vertex_data_get(&tree->vertices) + nverts_t;
   1330         memcpy(dst, src, nverts_s*sizeof(*src));
   1331       }
   1332     }
   1333     if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; }
   1334 
   1335     /* Update the index of the first vertex of the polyline for the nodes in the
   1336      * subtree based on their new locations, i.e., the main vertex buffer */
   1337     NODE(subtrees[i])->ivertex += nverts_t;
   1338     FOR_EACH(j, 0, scratch->nnodes-1/*The root has been just treated*/) {
   1339       inode = subtrees[i] + NODE(subtrees[i])->offset + j;
   1340       NODE(inode)->ivertex += nverts_t;
   1341     }
   1342 
   1343     /* Free the temporary vertex buffer */
   1344     darray_node_purge(&scratch->nodes);
   1345   }
   1346 
   1347   #undef NODE
   1348   #undef IS_LEAF
   1349 
   1350 exit:
   1351   /* Clean up temporary data */
   1352   FOR_EACH(i, 0, (int)nsubtrees) {
   1353     darray_node_release(&scratches[i].nodes);
   1354     darray_vertex_release(&scratches[i].vertices);
   1355   }
   1356   MEM_RM(allocator, scratches);
   1357   return (res_T)res;
   1358 error:
   1359   goto exit;
   1360 }
   1361 
   1362 static res_T
   1363 tree_build_sequential(struct sln_tree* tree)
   1364 {
   1365   /* Progress statuses */
   1366   struct progress partitioning = PROGRESS_DEFAULT;
   1367   struct progress meshing = PROGRESS_DEFAULT;
   1368 
   1369   struct sln_node node = SLN_NODE_NULL;
   1370   size_t nlines = 0;
   1371   size_t nnodes = 0;
   1372   size_t iroot = 0;
   1373   res_T res = RES_OK;
   1374 
   1375   SHTR(line_list_get_size(tree->args.lines, &nlines));
   1376 
   1377   iroot = darray_node_size_get(&tree->nodes);
   1378   ASSERT(iroot == 0); /* assume that the tree contains no data */
   1379 
   1380   /* Setup the root node */
   1381   node.range[0] = 0;
   1382   node.range[1] = nlines - 1/* inclusive bound */;
   1383   res = darray_node_push_back(&tree->nodes, &node);
   1384   if(res != RES_OK) goto error;
   1385 
   1386   /* Partition lines */
   1387   partitioning.total = nlines;
   1388   partitioning.msg = "Partitioning: ";
   1389   progress_print(tree->sln, &partitioning);
   1390   res = partition_lines_depth_first(tree, iroot, &tree->nodes, &partitioning);
   1391   if(res != RES_OK) goto error;
   1392 
   1393   /* Create polylines */
   1394   nnodes = darray_node_size_get(&tree->nodes);
   1395   meshing.total = nnodes;
   1396   meshing.msg = "Meshing: ";
   1397   progress_print(tree->sln, &meshing);
   1398   res = build_polylines(tree, iroot, nnodes, &tree->vertices, &meshing);
   1399   if(res != RES_OK) goto error;
   1400 
   1401 exit:
   1402   return res;
   1403 error:
   1404   goto exit;
   1405 }
   1406 
   1407 static res_T
   1408 tree_build_parallel(struct sln_tree* tree)
   1409 {
   1410   /* Stack data structure */
   1411   #define STACK_SIZE (SLN_TREE_DEPTH_MAX*(SLN_TREE_ARITY_MAX-1))
   1412   size_t stack[STACK_SIZE];
   1413   size_t istack = 0;
   1414 
   1415   /* Subtrees */
   1416   size_t subtrees[QUEUE_SIZE_MAX]; /* List of sub-tree root indices */
   1417   unsigned nsubtrees = 0;
   1418 
   1419   /* Progress message of the meshing step */
   1420   struct progress meshing = PROGRESS_DEFAULT;
   1421 
   1422   /* Miscellaneous */
   1423   struct sln_node root = SLN_NODE_NULL;
   1424   size_t nlines = 0;
   1425   size_t nnodes_upper_tree = 0; /* #nodes from the root to the subtrees */
   1426   size_t iroot = 0;
   1427   size_t inode = 0;
   1428   size_t i = 0;
   1429   res_T res = RES_OK;
   1430 
   1431   ASSERT(tree);
   1432 
   1433   iroot = darray_node_size_get(&tree->nodes);
   1434   ASSERT(iroot == 0); /* assume that the tree contains no data */
   1435 
   1436   SHTR(line_list_get_size(tree->args.lines, &nlines));
   1437 
   1438   /* Setup the root node */
   1439   root.range[0] = 0;
   1440   root.range[1] = nlines;
   1441   res = darray_node_push_back(&tree->nodes, &root);
   1442   if(res != RES_OK) goto error;
   1443 
   1444   /* Partition the lines in bread-first fixing the maximum number of leafs to
   1445    * the available number of threads */
   1446   res = partition_lines_breadth_first(tree, iroot, tree->args.nthreads_hint);
   1447   if(res != RES_OK) goto error;
   1448 
   1449   /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */
   1450   stack[istack++] = SIZE_MAX;
   1451 
   1452   /* Retrieve the leaf indices generated by bread-first partitioning. These
   1453    * correspond to the roots of the subtree to be built */
   1454   inode = iroot; /* Root node */
   1455   while(inode != SIZE_MAX) {
   1456     const struct sln_node* node = darray_node_cdata_get(&tree->nodes) + inode;
   1457     ASSERT(inode < darray_node_size_get(&tree->nodes));
   1458 
   1459     if(sln_node_is_leaf(node)) {
   1460       ASSERT(nsubtrees < QUEUE_SIZE_MAX);
   1461       subtrees[nsubtrees++] = inode;
   1462       inode = stack[--istack]; /* Pop the next node */
   1463 
   1464     } else {
   1465       const size_t ichild0 = inode + node->offset;
   1466       const unsigned nchildren = node_child_count(node, tree->args.arity);
   1467 
   1468       /* Push the children except the 1st one */
   1469       ASSERT(istack + (nchildren-1/*ichild0*/) <= STACK_SIZE);
   1470       FOR_EACH_REVERSE(i, nchildren-1, 0) stack[istack++] = ichild0 + i;
   1471 
   1472       /* Recursively traverse the 1st child */
   1473       inode = ichild0;
   1474     }
   1475   }
   1476 
   1477   /* Retrieves the number of registered nodes. This is the number of nodes from
   1478    * the root to the subtrees, excluding the roots of those subtrees */
   1479   nnodes_upper_tree = darray_node_size_get(&tree->nodes);
   1480   nnodes_upper_tree -= nsubtrees; /* Exclude the root node of the subtrees */
   1481 
   1482   res = build_subtrees(tree, subtrees, nsubtrees, &meshing);
   1483   if(res != RES_OK) goto error;
   1484 
   1485   /* Build the polylines of the upper level if necessary */
   1486   if(nnodes_upper_tree != 0) {
   1487     res = build_polylines
   1488       (tree, 0/*root*/, nnodes_upper_tree, &tree->vertices, &meshing);
   1489     if(res != RES_OK) goto error;
   1490   }
   1491 
   1492   #undef STACK_SIZE
   1493 
   1494 exit:
   1495   return res;
   1496 error:
   1497   goto exit;
   1498 }
   1499 
   1500 /*******************************************************************************
   1501  * Local functions
   1502  ******************************************************************************/
   1503 res_T
   1504 tree_build(struct sln_tree* tree)
   1505 {
   1506   res_T res = RES_OK;
   1507   ASSERT(tree);
   1508 
   1509   res = tree->args.nthreads_hint == 1
   1510     ? tree_build_sequential(tree)
   1511     : tree_build_parallel(tree);
   1512   if(res != RES_OK) goto error;
   1513 
   1514 exit:
   1515   return res;
   1516 error:
   1517   darray_node_purge(&tree->nodes);
   1518   darray_vertex_purge(&tree->vertices);
   1519   goto exit;
   1520 }