The Tiny HTM Library
src/htm.c
Go to the documentation of this file.
00001 
00014 #include "tinyhtm/htm.h"
00015 
00016 #include <stdlib.h>
00017 
00018 #include "tinyhtm/tree.h"
00019 #include "tinyhtm/varint.h"
00020 
00021 
00022 #ifdef __cplusplus
00023 extern "C" {
00024 #endif
00025 
00026 
00027 /*
00028     HTM triangles are subdivided into 4 sub-triangles as follows :
00029 
00030             v2
00031              *
00032             / \
00033            /   \
00034       sv1 *-----* sv0
00035          / \   / \
00036         /   \ /   \
00037     v0 *-----*-----* v1
00038             sv2
00039 
00040      -  vertices are unit magnitude 3-vectors
00041      -  edges are great circles on the unit sphere
00042      -  vertices are stored in counter-clockwise order
00043        (when viewed from outside the unit sphere in a
00044        right handed coordinate system)
00045      -  sv0 = (v1 + v2) / ||v1 + v2||, and likewise for sv1, sv2
00046 
00047     Note that if the HTM triangle given by (v0,v1,v2) has index I, then:
00048      -  sub triangle T0 = (v0,sv2,sv1) has index I*4
00049      -  sub triangle T1 = (v1,sv0,sv2) has index I*4 + 1
00050      -  sub triangle T2 = (v2,sv1,sv0) has index I*4 + 2
00051      -  sub triangle T3 = (sv0,sv1,sv2) has index I*4 + 3
00052 
00053     All HTM triangles are obtained via subdivision of 8 initial
00054     triangles, defined from the following set of 6 vertices :
00055      -  V0 = ( 0,  0,  1) north pole
00056      -  V1 = ( 1,  0,  0)
00057      -  V2 = ( 0,  1,  0)
00058      -  V3 = (-1,  0,  0)
00059      -  V4 = ( 0, -1,  0)
00060      -  V5 = ( 0,  0, -1) south pole
00061 
00062     The root triangles (corresponding to subdivision level 0) are :
00063      -  S0 = (V1, V5, V2), HTM index = 8
00064      -  S1 = (V2, V5, V3), HTM index = 9
00065      -  S2 = (V3, V5, V4), HTM index = 10
00066      -  S3 = (V4, V5, V1), HTM index = 11
00067      -  N0 = (V1, V0, V4), HTM index = 12
00068      -  N1 = (V4, V0, V3), HTM index = 13
00069      -  N2 = (V3, V0, V2), HTM index = 14
00070      -  N3 = (V2, V0, V1), HTM index = 15
00071 
00072     'S' denotes a triangle in the southern hemisphere,
00073     'N' denotes a triangle in the northern hemisphere.
00074  */
00075 
00076 
00077 /* ---- Types ---- */
00078 
00081 enum _htm_cov {
00082     HTM_DISJOINT = 0,  
00083     HTM_INTERSECT = 1, 
00084     HTM_CONTAINS = 2,  
00085     HTM_INSIDE = 3     
00086 };
00087 
00090 struct _htm_node {
00091     struct htm_v3 mid_vert[3];      
00092     struct htm_v3 mid_edge[3];      
00093     const struct htm_v3 *vert[3];   
00094     const struct htm_v3 *edge[3];   
00095     struct htm_v3p *end;            
00096     const unsigned char *s;         
00097     uint64_t index;                 
00098     int64_t id;                     
00099     int child;                      
00100 };
00101 
00104 struct _htm_path {
00105     enum htm_root root; 
00106     struct _htm_node node[HTM_MAX_LEVEL + 1];
00107 };
00108 
00109 
00110 /* ---- Data ---- */
00111 
00112 /*  HTM root triangle vertices/edge plane normals.
00113  */
00114 static const struct htm_v3 _htm_root_v3[6] = {
00115   { 0.0,  0.0,  1.0 },
00116   { 1.0,  0.0,  0.0 },
00117   { 0.0,  1.0,  0.0 },
00118   {-1.0,  0.0,  0.0 },
00119   { 0.0, -1.0,  0.0 },
00120   { 0.0,  0.0, -1.0 }
00121 };
00122 
00123 #define HTM_Z  &_htm_root_v3[0]
00124 #define HTM_X  &_htm_root_v3[1]
00125 #define HTM_Y  &_htm_root_v3[2]
00126 #define HTM_NX &_htm_root_v3[3]
00127 #define HTM_NY &_htm_root_v3[4]
00128 #define HTM_NZ &_htm_root_v3[5]
00129 
00130 /*  Vertex pointers for the 3 vertices of each HTM root triangle.
00131  */
00132 static const struct htm_v3 * const _htm_root_vert[24] = {
00133   HTM_X,  HTM_NZ, HTM_Y,  /* S0 */
00134   HTM_Y,  HTM_NZ, HTM_NX, /* S1 */
00135   HTM_NX, HTM_NZ, HTM_NY, /* S2 */
00136   HTM_NY, HTM_NZ, HTM_X,  /* S3 */
00137   HTM_X,  HTM_Z,  HTM_NY, /* N0 */
00138   HTM_NY, HTM_Z,  HTM_NX, /* N1 */
00139   HTM_NX, HTM_Z,  HTM_Y,  /* N2 */
00140   HTM_Y,  HTM_Z,  HTM_X   /* N3 */
00141 };
00142 
00143 /*  Edge normal pointers for the 3 edge normals of each HTM root triangle.
00144  */
00145 static const struct htm_v3 * const _htm_root_edge[24] = {
00146   HTM_Y,  HTM_X,  HTM_NZ, /* S0 */
00147   HTM_NX, HTM_Y,  HTM_NZ, /* S1 */
00148   HTM_NY, HTM_NX, HTM_NZ, /* S2 */
00149   HTM_X,  HTM_NY, HTM_NZ, /* S3 */
00150   HTM_NY, HTM_X,  HTM_Z,  /* N0 */
00151   HTM_NX, HTM_NY, HTM_Z,  /* N1 */
00152   HTM_Y,  HTM_NX, HTM_Z,  /* N2 */
00153   HTM_X,  HTM_Y,  HTM_Z   /* N3 */
00154 };
00155 
00156 
00157 /* ---- Implementation details ---- */
00158 
00159 /*  Sets path to the i-th HTM root triangle.
00160  */
00161 HTM_INLINE void _htm_path_root(struct _htm_path *path, enum htm_root r)
00162 {
00163     path->node[0].vert[0] = _htm_root_vert[r*3];
00164     path->node[0].vert[1] = _htm_root_vert[r*3 + 1];
00165     path->node[0].vert[2] = _htm_root_vert[r*3 + 2];
00166     path->node[0].edge[0] = _htm_root_edge[r*3];
00167     path->node[0].edge[1] = _htm_root_edge[r*3 + 1];
00168     path->node[0].edge[2] = _htm_root_edge[r*3 + 2];
00169     path->node[0].id = r + 8;
00170     path->node[0].child = 0;
00171     path->root = r;
00172 }
00173 
00174 /*  Computes the normalized average of two input vertices.
00175  */
00176 HTM_INLINE void _htm_vertex(struct htm_v3 *out,
00177                             const struct htm_v3 *v1,
00178                             const struct htm_v3 *v2)
00179 {
00180     htm_v3_add(out, v1, v2);
00181     htm_v3_normalize(out, out);
00182 }
00183 
00184 /*  Computes quantities needed by _htm_node_make0(node).
00185  */
00186 HTM_INLINE void _htm_node_prep0(struct _htm_node *node)
00187 {
00188     _htm_vertex(&node->mid_vert[1], node->vert[2], node->vert[0]);
00189     _htm_vertex(&node->mid_vert[2], node->vert[0], node->vert[1]);
00190     htm_v3_rcross(&node->mid_edge[1], &node->mid_vert[2], &node->mid_vert[1]);
00191 }
00192 
00193 /*  Sets node[1] to child 0 of node[0]. Assumes _htm_node_prep0(node)
00194     has been called.
00195  */
00196 HTM_INLINE void _htm_node_make0(struct _htm_node *node)
00197 {
00198     node[1].vert[0] = node[0].vert[0];
00199     node[1].vert[1] = &node[0].mid_vert[2];
00200     node[1].vert[2] = &node[0].mid_vert[1];
00201     node[1].edge[0] = node[0].edge[0];
00202     node[1].edge[1] = &node[0].mid_edge[1];
00203     node[1].edge[2] = node[0].edge[2];
00204     node[0].child = 1;
00205     node[1].id = node[0].id << 2;
00206     node[1].child = 0;
00207 }
00208 
00209 /*  Computes quantities needed by _htm_node_make1(node). Assumes
00210     _htm_node_prep0(node) has been called.
00211  */
00212 HTM_INLINE void _htm_node_prep1(struct _htm_node *node)
00213 {
00214     _htm_vertex(&node->mid_vert[0], node->vert[1], node->vert[2]);
00215     htm_v3_rcross(&node->mid_edge[2], &node->mid_vert[0], &node->mid_vert[2]);
00216 }
00217 
00218 /*  Sets node[1] to child 1 of node[0]. Assumes _htm_node_prep1(node)
00219     has been called.
00220  */
00221 HTM_INLINE void _htm_node_make1(struct _htm_node *node)
00222 {
00223     node[1].vert[0] = node[0].vert[1];
00224     node[1].vert[1] = &node[0].mid_vert[0];
00225     node[1].vert[2] = &node[0].mid_vert[2];
00226     node[1].edge[0] = node[0].edge[1];
00227     node[1].edge[1] = &node[0].mid_edge[2];
00228     node[1].edge[2] = node[0].edge[0];
00229     node[0].child = 2;
00230     node[1].id = (node[0].id << 2) + 1;
00231     node[1].child = 0;
00232 }
00233 
00234 /*  Computes quantities needed by _htm_node_make2(node). Assumes
00235     _htm_node_prep1 has been called.
00236  */
00237 HTM_INLINE void _htm_node_prep2(struct _htm_node *node)
00238 {
00239     htm_v3_rcross(&node->mid_edge[0], &node->mid_vert[1], &node->mid_vert[0]);
00240 }
00241 
00242 /*  Sets node[1] to child 2 of node[0]. Assumes _htm_node_prep2(node)
00243     has been called.
00244  */
00245 HTM_INLINE void _htm_node_make2(struct _htm_node *node)
00246 {
00247     node[1].vert[0] = node[0].vert[2];
00248     node[1].vert[1] = &node[0].mid_vert[1];
00249     node[1].vert[2] = &node[0].mid_vert[0];
00250     node[1].edge[0] = node[0].edge[2];
00251     node[1].edge[1] = &node[0].mid_edge[0];
00252     node[1].edge[2] = node[0].edge[1];
00253     node[0].child = 3;
00254     node[1].id = (node[0].id << 2) + 2;
00255     node[1].child = 0;
00256 }
00257 
00258 /*  Sets node[1] to child 3 of node[0]. Assumes _htm_node_prep2(node)
00259     has been called.
00260  */
00261 HTM_INLINE void _htm_node_make3(struct _htm_node *node)
00262 {
00263     htm_v3_neg(&node[0].mid_edge[0], &node[0].mid_edge[0]);
00264     htm_v3_neg(&node[0].mid_edge[1], &node[0].mid_edge[1]);
00265     htm_v3_neg(&node[0].mid_edge[2], &node[0].mid_edge[2]);
00266     node[1].vert[0] = &node[0].mid_vert[0];
00267     node[1].vert[1] = &node[0].mid_vert[1];
00268     node[1].vert[2] = &node[0].mid_vert[2];
00269     node[1].edge[0] = &node[0].mid_edge[0];
00270     node[1].edge[1] = &node[0].mid_edge[1];
00271     node[1].edge[2] = &node[0].mid_edge[2];
00272     node[0].child = 4;
00273     node[1].id = (node[0].id << 2) + 3;
00274     node[1].child = 0;
00275 }
00276 
00277 /*  Reorders the vectors in [begin,end) such that the resulting array can be
00278     partitioned into [begin,m) and [m,end), where all vectors in [begin,m) are
00279     inside the partitioning plane, and all vectors in [m, end) are outside.
00280 
00281     A pointer to the partitioning element m is returned.
00282 
00283     Assumes that plane != 0, begin != 0, end != 0, and begin <= end.
00284  */
00285 static struct htm_v3p * _htm_partition(const struct htm_v3 *plane,
00286                                        struct htm_v3p *beg,
00287                                        struct htm_v3p *end)
00288 {
00289     struct htm_v3p tmp;
00290     for (; beg < end; ++beg) {
00291         if (htm_v3_dot(plane, &beg->v) < 0.0) {
00292             /* beg is outside plane, find end which is inside,
00293                swap contents of beg and end. */
00294             for (--end; end > beg; --end) {
00295                 if (htm_v3_dot(plane, &end->v) >= 0.0) {
00296                     break;
00297                 }
00298             }
00299             if (end <= beg) {
00300                 break;
00301             }
00302             tmp = *beg;
00303             *beg = *end;
00304             *end = tmp;
00305         }
00306     }
00307     return beg;
00308 }
00309 
00310 /*  Perform a depth-first traversal of an HTM tree. At each step of the
00311     traversal, the input position list is partitioned into the list of
00312     points inside the current HTM node and those outside - the full
00313     depth-first traverals of the HTM tree therefore yields a list of
00314     positions sorted on their HTM indexes. This can be faster than computing
00315     HTM indexes one at a time when inputs are clustered spatially, since the
00316     boundary representation of a given HTM triangle is computed at most once.
00317  */
00318 static void _htm_path_sort(struct _htm_path *path,
00319                            struct htm_v3p *begin,
00320                            struct htm_v3p *end,
00321                            int64_t *ids,
00322                            int level)
00323 {
00324     struct _htm_node * const root = path->node;
00325     struct _htm_node * const leaf = root + level;
00326     struct _htm_node *curnode = path->node;
00327     struct htm_v3p *beg = begin;
00328 
00329     curnode->end = end;
00330 
00331     while (1) {
00332         if (curnode != leaf) {
00333             /* Not a leaf node, so continue descending the tree.
00334                Mid-points and edge normals are computed on-demand. */
00335             int child = curnode->child;
00336             if (child == 0) {
00337                 _htm_node_prep0(curnode);
00338                 end = _htm_partition(&curnode->mid_edge[1], beg, end);
00339                 if (beg < end) {
00340                     _htm_node_make0(curnode);
00341                     ++curnode;
00342                     curnode->end = end;
00343                     continue;
00344                 }
00345                 end = curnode->end;
00346             }
00347             if (child <= 1) {
00348                 _htm_node_prep1(curnode);
00349                 end = _htm_partition(&curnode->mid_edge[2], beg, end);
00350                 if (beg < end) {
00351                     _htm_node_make1(curnode);
00352                     ++curnode;
00353                     curnode->end = end;
00354                     continue;
00355                 }
00356                 end = curnode->end;
00357             }
00358             if (child <= 2) {
00359                 _htm_node_prep2(curnode);
00360                 end = _htm_partition(&curnode->mid_edge[0], beg, end);
00361                 if (beg < end) {
00362                     _htm_node_make2(curnode);
00363                     ++curnode;
00364                     curnode->end = end;
00365                     continue;
00366                 }
00367                 end = curnode->end;
00368             }
00369             if (beg < end) {
00370                 _htm_node_make3(curnode);
00371                 ++curnode;
00372                 curnode->end = end;
00373                 continue;
00374             }
00375         } else {
00376             /* reached a leaf triangle - all points between beg and end are
00377                inside and have the same HTM index. */
00378             int64_t id = curnode->id;
00379             size_t i = beg - begin;
00380             size_t j = end - begin;
00381             for (; i < j; ++i) {
00382                 ids[i] = id;
00383             }
00384             beg = end;
00385         }
00386         /* walk back up the path until we find a node which
00387            still contains unsorted points. */
00388         do {
00389             if (curnode == root) {
00390                 return;
00391             }
00392             --curnode;
00393             end = curnode->end;
00394         } while (beg == end);
00395     }
00396 }
00397 
00398 #define HTM_IDS_INIT_CAP 16
00399 
00400 static struct htm_ids * _htm_ids_init()
00401 {
00402     struct htm_ids *ids = (struct htm_ids *) malloc(
00403         sizeof(struct htm_ids) + HTM_IDS_INIT_CAP * sizeof(struct htm_range));
00404     if (ids != NULL) {
00405         ids->n = 0;
00406         ids->cap = HTM_IDS_INIT_CAP;
00407     }
00408     return ids;
00409 }
00410 
00411 static struct htm_ids * _htm_ids_grow(struct htm_ids *ids)
00412 {
00413     size_t cap = ids->cap;
00414     size_t nbytes = sizeof(struct htm_ids) + 2 * cap * sizeof(struct htm_range);
00415     struct htm_ids *out = (struct htm_ids *) realloc(ids, nbytes);
00416     if (out != NULL) {
00417         out->cap = 2 * cap;
00418     } else {
00419         free(ids);
00420     }
00421     return out;
00422 }
00423 
00424 HTM_INLINE struct htm_ids * _htm_ids_add(struct htm_ids *ids,
00425                                          int64_t min,
00426                                          int64_t max)
00427 {
00428     size_t n = ids->n;
00429     if (n == 0) {
00430         ids->n = 1;
00431         ids->range[0].min = min;
00432         ids->range[0].max = max;
00433     } else if (min == ids->range[n - 1].max + 1) {
00434         ids->range[n - 1].max = max;
00435     } else {
00436         if (n == ids->cap) {
00437             ids = _htm_ids_grow(ids);
00438             if (ids == NULL) {
00439                 return NULL;
00440             }
00441         }
00442         ids->n = n + 1;
00443         ids->range[n].min = min;
00444         ids->range[n].max = max;
00445     }
00446     return ids;
00447 }
00448 
00449 
00450 /*  Returns the coverage code describing the spatial relationship between the
00451     given HTM triangle and spherical circle.
00452  */
00453 static enum _htm_cov _htm_s2circle_htmcov(const struct _htm_node *n,
00454                                           const struct htm_v3 *c,
00455                                           double dist2)
00456 {
00457     int nin = htm_v3_dist2(c, n->vert[0]) <= dist2;
00458     nin += htm_v3_dist2(c, n->vert[1]) <= dist2;
00459     nin += htm_v3_dist2(c, n->vert[2]) <= dist2;
00460     if (nin == 3) {
00461         /* every vertex inside circle */
00462         return HTM_INSIDE;
00463     } else if (nin != 0) {
00464         return HTM_INTERSECT;
00465     }
00466     /* no triangle vertices inside circle */
00467     if (htm_v3_edgedist2(c, n->vert[0], n->vert[1], n->edge[0]) <= dist2 ||
00468         htm_v3_edgedist2(c, n->vert[1], n->vert[2], n->edge[1]) <= dist2 ||
00469         htm_v3_edgedist2(c, n->vert[2], n->vert[0], n->edge[2]) <= dist2) {
00470         /* min distance to at least one edge is <= circle radius */
00471         return HTM_INTERSECT;
00472     }
00473     /* min distance to every edge is > circle radius - circle
00474        is either inside triangle or disjoint from it */
00475     if (htm_v3_dot(c, n->edge[0]) >= 0.0 &&
00476         htm_v3_dot(c, n->edge[1]) >= 0.0 &&
00477         htm_v3_dot(c, n->edge[2]) >= 0.0) {
00478         return HTM_CONTAINS;
00479     }
00480     return HTM_DISJOINT;
00481 }
00482 
00483 
00484 /*  Returns 1 if the edge between v1 and v2 intersects the given spherical
00485     ellipse.
00486 
00487     Let M be the 3x3 symmetric matrix corresponding to the ellipse; the
00488     ellipse boundary is given by:
00489 
00490     v' M v = 0
00491 
00492     (where ' denotes transpose, and v = [x y z]'). The lines of intersection,
00493     between the plane defined by v1, v2 and M are given by:
00494 
00495     a*(v1 + v2) + b*(v2 - v1)
00496 
00497     where a,b are scalars to be determined. Note that we can use any basis
00498     for the plane; (v1 + v2, v2 - v1) is chosen because of numerical stability
00499     when v1 and v2 are nearly identical, and because it yields a simple test
00500     for whether a particular solution lies on the edge in question.
00501 
00502     Note that if a,b is a solution, so is k*a,k*b for any scalar k. So
00503     wlog, set a = 1 and plug into the ellipse boundary equation:
00504 
00505     0 = (v1 + v2)' M (v1 + v2) + b*(v2 - v1)' M (v1 + v2) +
00506         b*(v1 + v2)' M (v2 - v1) + b^2*(v2 - v1)' M (v2 - v1)
00507 
00508     Since M is symmetric:
00509 
00510     0 = b^2 * (v2 - v1)' M (v2 - v1) +
00511          2b * (v2 - v1)' M (v1 + v2) +
00512               (v1 + v2)' M (v1 + v2)
00513 
00514     0 = c22 * b^2 + 2*c21 * b + c11
00515 
00516     Solving this quadratic equation yields 0, 1, or 2 lines of intersection.
00517     Note that for an intersection to lie on the edge between v1 and v2,
00518     b must be in the range [-1, 1].
00519  */
00520 static int _htm_s2ellipse_isect(const struct htm_v3 *v1,
00521                                 const struct htm_v3 *v2,
00522                                 const struct htm_s2ellipse *ellipse)
00523 {
00524     struct htm_v3 e1, e2, v;
00525     double c11, c21, c22, delta;
00526 
00527     htm_v3_add(&e1, v1, v2);
00528     htm_v3_sub(&e2, v2, v1);
00529     /* compute coeffs of quadratic eqn. */
00530     c11 = e1.x * e1.x * ellipse->xx +
00531           e1.y * e1.y * ellipse->yy +
00532           e1.z * e1.z * ellipse->zz +
00533           e1.x * e1.y * ellipse->xy * 2.0 +
00534           e1.x * e1.z * ellipse->xz * 2.0 +
00535           e1.y * e1.z * ellipse->yz * 2.0;
00536     c22 = e2.x * e2.x * ellipse->xx +
00537           e2.y * e2.y * ellipse->yy +
00538           e2.z * e2.z * ellipse->zz +
00539           e2.x * e2.y * ellipse->xy * 2.0 +
00540           e2.x * e2.z * ellipse->xz * 2.0 +
00541           e2.y * e2.z * ellipse->yz * 2.0;
00542     c21 = e2.x * e1.x * ellipse->xx +
00543           e2.y * e1.y * ellipse->yy +
00544           e2.z * e1.z * ellipse->zz +
00545           (e2.x * e1.y + e2.y * e1.x) * ellipse->xy +
00546           (e2.x * e1.z + e2.z * e1.x) * ellipse->xz +
00547           (e2.y * e1.z + e2.z * e1.y) * ellipse->yz;
00548     if (c11 == 0.0) {
00549         /* v1 + v2 is a solution, and lies on the edge */
00550         if (ellipse->a >= 90.0 || htm_v3_dot(&e1, &ellipse->cen) >= 0.0) {
00551             return 1;
00552         }
00553         /* other solution is given by a linear equation */
00554         if (c22 == 0.0 || fabs(c22) < fabs(2.0*c21)) {
00555             return 0;
00556         }
00557         /* check whether solution lies in correct hemisphere */
00558         htm_v3_mul(&v, &e2, -2.0*c21/c22);
00559         htm_v3_add(&v, &v, &e1);
00560         return htm_v3_dot(&v, &ellipse->cen) >= 0.0;
00561     }
00562     if (c22 == 0.0) {
00563         /* v2 - v1 is a solution, the other is given by b = -c11/(2*c21). */
00564         if (c21 == 0.0) {
00565             return 0;
00566         }
00567         if (fabs(c11) <= fabs(2.0*c21)) {
00568             if (ellipse->a >= 90.0) {
00569                 return 1;
00570             }
00571             /* check whether solution lies in correct hemisphere */
00572             htm_v3_mul(&v, &e2, -0.5*c11/c21);
00573             htm_v3_add(&v, &v, &e1);
00574             return htm_v3_dot(&v, &ellipse->cen) >= 0.0;
00575         }
00576         return 0;
00577     }
00578     delta = c21*c21 - c11*c22;
00579     if (delta < 0.0) {
00580         /* no solutions */
00581         return 0;
00582     }
00583     /* 1 or 2 solutions */
00584     delta = sqrt(delta);
00585     if (fabs(c22) >= fabs(delta - c21)) {
00586         if (ellipse->a >= 90.0) {
00587             return 1;
00588         }
00589         /* check whether solution lies in correct hemisphere */
00590         htm_v3_mul(&v, &e2, (delta - c21)/c22);
00591         htm_v3_add(&v, &v, &e1);
00592         return htm_v3_dot(&v, &ellipse->cen) >= 0.0;
00593     }
00594     if (fabs(c22) >= fabs(delta + c21)) {
00595         if (ellipse->a >= 90.0) {
00596             return 1;
00597         }
00598         /* check whether solution lies in correct hemisphere */
00599         htm_v3_mul(&v, &e2, -(delta + c21)/c22);
00600         htm_v3_add(&v, &v, &e1);
00601         return htm_v3_dot(&v, &ellipse->cen) >= 0.0;
00602     }
00603     return 0;
00604 }
00605 
00606 
00607 /*  Returns the coverage code describing the spatial relationship between the
00608     given HTM triangle and spherical ellipse.
00609  */
00610 static enum _htm_cov _htm_s2ellipse_htmcov(const struct _htm_node *n,
00611                                            const struct htm_s2ellipse *e)
00612 {
00613     int nin = htm_s2ellipse_cv3(e, n->vert[0]);
00614     nin += htm_s2ellipse_cv3(e, n->vert[1]);
00615     nin += htm_s2ellipse_cv3(e, n->vert[2]);
00616     if (nin == 3) {
00617         return HTM_INSIDE;
00618     } else if (nin != 0) {
00619         return HTM_INTERSECT;
00620     }
00621     /* no triangle vertices inside ellipse - check for edge/ellipse
00622        intersections */
00623     if (_htm_s2ellipse_isect(n->vert[0], n->vert[1], e) ||
00624         _htm_s2ellipse_isect(n->vert[1], n->vert[2], e) ||
00625         _htm_s2ellipse_isect(n->vert[2], n->vert[0], e)) {
00626         return HTM_INTERSECT;
00627     }
00628     /* no triangle/ellipse intersections */
00629     if (htm_v3_dot(&e->cen, n->edge[0]) >= 0.0 &&
00630         htm_v3_dot(&e->cen, n->edge[1]) >= 0.0 &&
00631         htm_v3_dot(&e->cen, n->edge[2]) >= 0.0) {
00632         /* ellipse center inside triangle */
00633         return HTM_CONTAINS;
00634     }
00635     return HTM_DISJOINT;
00636 }
00637 
00638 
00639 static const double HTM_INF = 1.0 / 0.0;
00640 static const double HTM_NEG_INF = -1.0 / 0.0;
00641 
00642 /*  Tests whether poly intersects the edge (v1, v2) with plane normal n.
00643 
00644     The idea is that a solution v = (x,y,z) must satisfy:
00645 
00646         v . n = 0, v != 0
00647         v . (n ^ v1) >= 0
00648         v . (v2 ^ n) >= 0
00649         v . e_i >= 0
00650 
00651     where e_i are the edge plane normals for the polygon, and (n ^ v1),
00652     (v2 ^ n) are plane normals that bound the lune defined by n, v1, and v2.
00653     Write this as:
00654 
00655         v . n = 0
00656         v . c_i >= 0
00657 
00658     Now assume nz > 0 (for a non zero solution, at least one of nx, ny, nz
00659     must be non-zero, and treatment of negative values is analagous). Use
00660     the equality to obtain
00661 
00662         z = - (x * nx + y * ny) / nz
00663 
00664     and substitute into the inequalities to obtain:
00665 
00666         x * (c_ix * nz - c_iz * nx) + y * (c_iy * nz - c_iz * ny) >= 0
00667 
00668     which we write
00669 
00670         x * a_i + y * b_i >= 0
00671 
00672     If a solution v exists, then Kv is also a solution (for positive scalar K),
00673     so we can fix y = 1 and look for solutions to
00674 
00675         x * a_i + b_i >= 0
00676 
00677     If there are none, fix y = -1 and look for solutions to:
00678 
00679         x * a_i - b_i >= 0
00680 
00681     If again there are none, then y = 0, and the problem reduces to checking
00682     whether
00683 
00684         x * a_i >= 0
00685 
00686     has any solutions. This is the case when the non-zero a_i have the same
00687     sign.
00688  */
00689 static int _htm_isect_test(const struct htm_v3 *v1,
00690                            const struct htm_v3 *v2,
00691                            const struct htm_v3 *n,
00692                            const struct htm_s2cpoly *poly,
00693                            double *ab)
00694 {
00695     struct htm_v3 c0;
00696     struct htm_v3 c1;
00697     double min_1, max_1, min_m1, max_m1;
00698     size_t nv, i, neg, pos;
00699 
00700     htm_v3_cross(&c0, n, v1);
00701     htm_v3_cross(&c1, v2, n);
00702     nv = poly->n;
00703     if (n->z != 0.0) {
00704         double s = (n->z > 0.0) ? 1.0 : -1.0;
00705         ab[0] = s * (c0.x * n->z - c0.z * n->x);
00706         ab[1] = s * (c0.y * n->z - c0.z * n->y);
00707         ab[2] = s * (c1.x * n->z - c1.z * n->x);
00708         ab[3] = s * (c1.y * n->z - c1.z * n->y);
00709         for (i = 0; i < nv; ++i) {
00710             ab[2*i + 4] = s * (poly->ve[nv + i].x * n->z - poly->ve[nv + i].z * n->x);
00711             ab[2*i + 5] = s * (poly->ve[nv + i].y * n->z - poly->ve[nv + i].z * n->y);
00712         }
00713     } else if (n->y != 0.0) {
00714         double s = (n->y > 0.0) ? 1.0 : -1.0;
00715         ab[0] = s * (c0.x * n->y - c0.y * n->x);
00716         ab[1] = s * (c0.z * n->y);
00717         ab[2] = s * (c1.x * n->y - c1.y * n->x);
00718         ab[3] = s * (c1.z * n->y);
00719         for (i = 0; i < nv; ++i) {
00720             ab[2*i + 4] = s * (poly->ve[nv + i].x * n->y - poly->ve[nv + i].y * n->x);
00721             ab[2*i + 5] = s * (poly->ve[nv + i].z * n->y);
00722         }
00723     } else if (n->x != 0.0) {
00724         double s = (n->x > 0.0) ? 1.0 : -1.0;
00725         ab[0] = s * (c0.y * n->x);
00726         ab[1] = s * (c0.z * n->x);
00727         ab[2] = s * (c1.y * n->x);
00728         ab[3] = s * (c1.z * n->x);
00729         for (i = 0; i < nv; ++i) {
00730             ab[2*i + 4] = s * (poly->ve[nv + i].y * n->x);
00731             ab[2*i + 5] = s * (poly->ve[nv + i].z * n->x);
00732         }
00733     } else {
00734         return 0;
00735     }
00736     /* search for solutions to a*x +/- b >= 0, with constraint coeffs stored in
00737        ab */
00738     min_1 = min_m1 = HTM_NEG_INF;
00739     max_1 = max_m1 = HTM_INF;
00740     for (i = 0, neg = 0, pos = 0; i < nv + 2; ++i) {
00741         double a = ab[2*i];
00742         double b = ab[2*i + 1];
00743         if (a == 0.0) {
00744             if (b < 0.0) {
00745                 min_1 = HTM_INF;
00746                 max_1 = HTM_NEG_INF;
00747             } else if (b > 0.0) {
00748                 min_m1 = HTM_INF;
00749                 max_m1 = HTM_NEG_INF;
00750             }
00751         } else if (a < 0.0) {
00752             ++neg;
00753             double d = -b / a;
00754             if (d < max_1) {
00755                 max_1 = d;
00756             }
00757             if (-d < max_m1) {
00758                 max_m1 = -d;
00759             }
00760         } else {
00761             ++pos;
00762             double d = -b / a;
00763             if (d > min_1) {
00764                 min_1 = d;
00765             }
00766             if (-d > min_m1) {
00767                 min_m1 = -d;
00768             }
00769         }
00770     }
00771     if (min_1 <= max_1 || min_m1 <= max_m1) {
00772         return 1;
00773     }
00774     return (neg == 0 || pos == 0);
00775 }
00776 
00777 /*  Returns the coverage code describing the spatial relationship between the
00778     given HTM triangle and spherical convex polygon.
00779  */
00780 static enum _htm_cov _htm_s2cpoly_htmcov(const struct _htm_node *n,
00781                                          const struct htm_s2cpoly *poly,
00782                                          double *ab)
00783 {
00784     int nin = htm_s2cpoly_cv3(poly, n->vert[0]);
00785     nin += htm_s2cpoly_cv3(poly, n->vert[1]);
00786     nin += htm_s2cpoly_cv3(poly, n->vert[2]);
00787 
00788     if (nin == 3) {
00789         /* all triangle vertices are inside poly,
00790            so triangle is inside by convexity. */
00791         return HTM_INSIDE;
00792     } else if (nin != 0) {
00793         return HTM_INTERSECT;
00794     }
00795     /* all triangle vertices are outside poly - check for edge intersections */
00796     if (_htm_isect_test(n->vert[0], n->vert[1], n->edge[0], poly, ab) != 0 ||
00797         _htm_isect_test(n->vert[1], n->vert[2], n->edge[1], poly, ab) != 0 ||
00798         _htm_isect_test(n->vert[2], n->vert[0], n->edge[2], poly, ab) != 0) {
00799         return HTM_INTERSECT;
00800     }
00801     /* All triangle vertices are outside poly and there are no edge/edge
00802        intersections. Polygon is either inside triangle or disjoint from
00803        it */
00804     if (htm_v3_dot(&poly->vsum, n->edge[0]) >= 0.0 &&
00805         htm_v3_dot(&poly->vsum, n->edge[1]) >= 0.0 &&
00806         htm_v3_dot(&poly->vsum, n->edge[2]) >= 0.0) {
00807         return HTM_CONTAINS;
00808     }
00809     return HTM_DISJOINT;
00810 }
00811 
00812 /*  Returns the HTM root triangle for a point.
00813  */
00814 HTM_INLINE enum htm_root _htm_v3_htmroot(const struct htm_v3 *v)
00815 {
00816     if (v->z < 0.0) {
00817         /* S0, S1, S2, S3 */
00818         if (v->y > 0.0) {
00819             return (v->x > 0.0) ? HTM_S0 : HTM_S1;
00820         } else if (v->y == 0.0) {
00821             return (v->x >= 0.0) ? HTM_S0 : HTM_S2;
00822         } else {
00823             return (v->x < 0.0) ? HTM_S2 : HTM_S3;
00824         }
00825     } else {
00826         /* N0, N1, N2, N3 */
00827         if (v->y > 0.0) {
00828             return (v->x > 0.0) ? HTM_N3 : HTM_N2;
00829         } else if (v->y == 0.0) {
00830             return (v->x >= 0.0) ? HTM_N3 : HTM_N1;
00831         } else {
00832             return (v->x < 0.0) ? HTM_N1 : HTM_N0;
00833         }
00834     }
00835 }
00836 
00837 /*  Partitions an array of points according to their root triangle numbers.
00838  */
00839 static size_t _htm_rootpart(struct htm_v3p *points,
00840                             unsigned char *ids,
00841                             size_t n,
00842                             enum htm_root root)
00843 {
00844     struct htm_v3p tmp;
00845     size_t beg, end;
00846     unsigned char c;
00847     for (beg = 0, end = n; beg < end; ++beg) {
00848         if (ids[beg] >= root) {
00849             for (; end > beg; --end) {
00850                 if (ids[end - 1] < root) {
00851                     break;
00852                 }
00853             }
00854             if (end == beg) {
00855                 break;
00856             }
00857             tmp = points[beg];
00858             points[beg] = points[end - 1];
00859             points[end - 1] = tmp;
00860             c = ids[beg];
00861             ids[beg] = ids[end - 1];
00862             ids[end - 1] = c;
00863         }
00864     }
00865     return beg;
00866 }
00867 
00868 /*  Sorts the given array of positions by root triangle number.
00869  */
00870 static void _htm_rootsort(size_t roots[HTM_NROOTS + 1],
00871                           struct htm_v3p *points,
00872                           unsigned char *ids,
00873                           size_t n)
00874 {
00875     size_t i, n0, n2, s2;
00876 
00877     /* compute root ids for all points */
00878     for (i = 0; i < n; ++i) {
00879         ids[i] = (unsigned char) _htm_v3_htmroot(&points[i].v);
00880     }
00881     n0 = _htm_rootpart(points, ids, n, HTM_N0);
00882     s2 = _htm_rootpart(points, ids, n0, HTM_S2);
00883     roots[HTM_S0] = 0;
00884     roots[HTM_S1] = _htm_rootpart(points, ids, s2, HTM_S1);
00885     roots[HTM_S2] = s2;
00886     roots[HTM_S3] = _htm_rootpart(points + s2, ids + s2, n0 - s2, HTM_S3) + s2;
00887     n2 = _htm_rootpart(points + n0, ids + n0, n - n0, HTM_N2) + n0;
00888     roots[HTM_N0] = n0;
00889     roots[HTM_N1] = _htm_rootpart(points + n0, ids + n0, n2 - n0, HTM_N1) + n0;
00890     roots[HTM_N2] = n2;
00891     roots[HTM_N3] = _htm_rootpart(points + n2, ids + n2, n - n2, HTM_N3) + n2;
00892     roots[HTM_NROOTS] = n;
00893 }
00894 
00895 /*  Reduces the effective subdivision level of an HTM id range list by n levels
00896     and merges adjacent ranges. This typically reduces the number of ranges in
00897     the list, but also makes it a poorer approximation of the underlying
00898     geometry. Note that with sufficiently large n, any range list can be shrunk
00899     down to at most 4 ranges.
00900 
00901     In detail: a range [I1, I2] is mapped to [I1 & ~mask, I2 | mask], where
00902     mask = (1 << 2*n) - 1.
00903  */
00904 static void _htm_simplify_ids(struct htm_ids *ids, int n)
00905 {
00906     size_t i, j, nr;
00907     int64_t mask;
00908     if (n <= 0 || ids == 0 || ids->n == 0) {
00909         return;
00910     }
00911     mask = (((int64_t) 1) << 2*n) - 1;
00912     for (i = 0, j = 0, nr = ids->n; i < nr; ++i, ++j) {
00913         int64_t min = ids->range[i].min & ~mask;
00914         int64_t max = ids->range[i].max | mask;
00915         for (; i < nr - 1; ++i) {
00916             int64_t next = ids->range[i + 1].min & ~mask;
00917             if (next > max + 1) {
00918                 break;
00919             }
00920             max = ids->range[i + 1].max | mask;
00921         }
00922         ids->range[j].min = min;
00923         ids->range[j].max = max;
00924     }
00925     ids->n = j;
00926 }
00927 
00928 
00929 /*  Constructs the next non-empty child of \p node.
00930  */
00931 static const unsigned char * _htm_subdivide(struct _htm_node *node,
00932                                             const unsigned char *s)
00933 {
00934     uint64_t off;
00935     switch (node->child) {
00936         case 0:
00937             off = htm_varint_decode(s);
00938             s += 1 + htm_varint_nfollow(*s);
00939             _htm_node_prep0(node);
00940             _htm_node_make0(node);
00941             if (off != 0) {
00942                 break;
00943             }
00944             /* fall-through */
00945         case 1:
00946             off = htm_varint_decode(s);
00947             s += 1 + htm_varint_nfollow(*s);
00948             _htm_node_prep1(node);
00949             _htm_node_make1(node);
00950             if (off != 0) {
00951                 break;
00952             }
00953             /* fall-through */
00954         case 2:
00955             off = htm_varint_decode(s);
00956             s += 1 + htm_varint_nfollow(*s);
00957             _htm_node_prep2(node);
00958             _htm_node_make2(node);
00959             if (off != 0) {
00960                 break;
00961             }
00962             /* fall-through */
00963         case 3:
00964             off = htm_varint_decode(s);
00965             s += 1 + htm_varint_nfollow(*s);
00966             if (off != 0) {
00967                 _htm_node_make3(node);
00968                 break;
00969             }
00970         default:
00971             return NULL;
00972     }
00973     node->s = s;
00974     return s + (off - 1);
00975 }
00976 
00977 
00978 /* ---- API ---- */
00979 
00980 int64_t htm_v3_id(const struct htm_v3 *point, int level)
00981 {
00982     struct htm_v3 v0, v1, v2;
00983     struct htm_v3 sv0, sv1, sv2;
00984     struct htm_v3 e;
00985     int64_t id;
00986     int curlevel;
00987     enum htm_root r;
00988 
00989     if (point == NULL) {
00990         return 0;
00991     }
00992     if (level < 0 || level > HTM_MAX_LEVEL) {
00993         return 0;
00994     }
00995     r = _htm_v3_htmroot(point);
00996     v0 = *_htm_root_vert[r*3];
00997     v1 = *_htm_root_vert[r*3 + 1];
00998     v2 = *_htm_root_vert[r*3 + 2];
00999     id = r + 8;
01000     for (curlevel = 0; curlevel < level; ++curlevel) {
01001         _htm_vertex(&sv1, &v2, &v0);
01002         _htm_vertex(&sv2, &v0, &v1);
01003         htm_v3_rcross(&e, &sv2, &sv1);
01004         if (htm_v3_dot(&e, point) >= 0) {
01005             v1 = sv2;
01006             v2 = sv1;
01007             id = id << 2;
01008             continue;
01009         }
01010         _htm_vertex(&sv0, &v1, &v2);
01011         htm_v3_rcross(&e, &sv0, &sv2);
01012         if (htm_v3_dot(&e, point) >= 0) {
01013             v0 = v1;
01014             v1 = sv0;
01015             v2 = sv2;
01016             id = (id << 2) + 1;
01017             continue;
01018         }
01019         htm_v3_rcross(&e, &sv1, &sv0);
01020         if (htm_v3_dot(&e, point) >= 0) {
01021             v0 = v2;
01022             v1 = sv1;
01023             v2 = sv0;
01024             id = (id << 2) + 2;
01025         } else {
01026             v0 = sv0;
01027             v1 = sv1;
01028             v2 = sv2;
01029             id = (id << 2) + 3;
01030         }
01031     }
01032     return id;
01033 }
01034 
01035 
01036 enum htm_errcode htm_v3p_idsort(struct htm_v3p *points,
01037                                 int64_t *ids,
01038                                 size_t n,
01039                                 int level)
01040 {
01041     struct _htm_path path;
01042     size_t roots[HTM_NROOTS + 1];
01043     enum htm_root r;
01044 
01045     if (n == 0) {
01046         return HTM_ELEN;
01047     } else if (points == 0 || ids == 0) {
01048         return HTM_ENULLPTR;
01049     } else if (level < 0 || level > HTM_MAX_LEVEL) {
01050         return HTM_ELEVEL;
01051     }
01052     _htm_rootsort(roots, points, (unsigned char *) ids, n);
01053     for (r = HTM_S0; r <= HTM_N3; ++r) {
01054         if (roots[r] < roots[r + 1]) {
01055             _htm_path_root(&path, r);
01056             _htm_path_sort(&path, points + roots[r],
01057                                  points + roots[r + 1], ids + roots[r], level);
01058         }
01059     }
01060     return HTM_OK;
01061 }
01062 
01063 
01064 int htm_level(int64_t id)
01065 {
01066     uint64_t x = (uint64_t) id;
01067     int l;
01068     if (id < 8) {
01069         return -1;
01070     }
01071     x |= (x >> 1);
01072     x |= (x >> 2);
01073     x |= (x >> 4);
01074     x |= (x >> 8);
01075     x |= (x >> 16);
01076     x |= (x >> 32);
01077     l = htm_popcount(x) - 4;
01078     /* check that l is even, in range, and that the 4 MSBs of id
01079        give a valid root ID (8-15) */
01080     if ((l & 1) != 0 || ((id >> l) & 0x8) == 0 || l > HTM_MAX_LEVEL*2) {
01081         return -1;
01082     }
01083     return l / 2;
01084 }
01085 
01086 
01087 enum htm_errcode htm_tri_init(struct htm_tri *tri, int64_t id)
01088 {
01089     struct htm_v3 v0, v1, v2;
01090     struct htm_v3 sv0, sv1, sv2;
01091     int shift, level;
01092     enum htm_root r;
01093 
01094     if (tri == NULL) {
01095         return HTM_ENULLPTR;
01096     }
01097     level = htm_level(id);
01098     if (level < 0) {
01099         return HTM_EID;
01100     }
01101     tri->id = id;
01102     tri->level = level;
01103     shift = 2*level;
01104     r = (id >> shift) & 0x7;
01105     v0 = *_htm_root_vert[r*3];
01106     v1 = *_htm_root_vert[r*3 + 1];
01107     v2 = *_htm_root_vert[r*3 + 2];
01108     for (shift -= 2; shift >= 0; shift -= 2) {
01109         int child = (id >> shift) & 0x3;
01110         _htm_vertex(&sv1, &v2, &v0);
01111         _htm_vertex(&sv2, &v0, &v1);
01112         _htm_vertex(&sv0, &v1, &v2);
01113         switch (child) {
01114             case 0:
01115                 v1 = sv2;
01116                 v2 = sv1;
01117                 break;
01118             case 1:
01119                 v0 = v1;
01120                 v1 = sv0;
01121                 v2 = sv2;
01122                 break;
01123             case 2:
01124 
01125                 v0 = v2;
01126                 v1 = sv1;
01127                 v2 = sv0;
01128                 break;
01129             case 3:
01130                 v0 = sv0;
01131                 v1 = sv1;
01132                 v2 = sv2;
01133                 break;
01134         }
01135     }
01136     tri->verts[0] = v0;
01137     tri->verts[1] = v1;
01138     tri->verts[2] = v2;
01139     htm_v3_add(&sv0, &v0, &v1);
01140     htm_v3_add(&sv0, &sv0, &v2);
01141     htm_v3_normalize(&tri->center, &sv0);
01142     tri->radius = htm_v3_angsep(&sv0, &v0);
01143     return HTM_OK;
01144 }
01145 
01146 
01147 struct htm_ids * htm_s2circle_ids(struct htm_ids *ids,
01148                                   const struct htm_v3 *center,
01149                                   double radius,
01150                                   int level,
01151                                   size_t maxranges,
01152                                   enum htm_errcode *err)
01153 {
01154     struct _htm_path path;
01155     double dist2;
01156     enum htm_root root;
01157     int efflevel;
01158 
01159     if (center == NULL) {
01160         if (err != NULL) {
01161             *err = HTM_ENULLPTR;
01162         }
01163         free(ids);
01164         return NULL;
01165     } else if (level < 0 || level > HTM_MAX_LEVEL) {
01166         if (err != NULL) {
01167             *err = HTM_ELEVEL;
01168         }
01169         free(ids);
01170         return NULL;
01171     }
01172     if (ids == NULL) {
01173         ids = _htm_ids_init();
01174         if (ids == NULL) {
01175             if (err != NULL) {
01176                 *err = HTM_ENOMEM;
01177             }
01178             return NULL;
01179         }
01180     } else {
01181         ids->n = 0;
01182     }
01183     /* Deal with degenerate cases */
01184     if (radius < 0.0) {
01185         /* empty ID list */
01186         if (err != NULL) {
01187             *err = HTM_OK;
01188         }
01189         return ids;
01190     } else if (radius >= 180.0) {
01191         /* the entire sky */
01192         int64_t min_id = (8 + HTM_S0) << level * 2;
01193         int64_t max_id = ((8 + HTM_NROOTS) << level * 2) - 1;
01194         if (err != NULL) {
01195             *err = HTM_OK;
01196         }
01197         ids = _htm_ids_add(ids, min_id, max_id);
01198         if (ids == NULL && err != NULL) {
01199             *err = HTM_ENOMEM;
01200         }
01201         return ids;
01202     }
01203 
01204     efflevel = level;
01205     /* compute square of secant distance corresponding to radius */
01206     dist2 = sin(radius * 0.5 * HTM_RAD_PER_DEG);
01207     dist2 = 4.0 * dist2 * dist2;
01208 
01209     for (root = HTM_S0; root <= HTM_N3; ++root) {
01210         struct _htm_node *curnode = path.node;
01211         int curlevel = 0;
01212         _htm_path_root(&path, root);
01213 
01214         while (1) {
01215             switch (_htm_s2circle_htmcov(curnode, center, dist2)) {
01216                 case HTM_CONTAINS:
01217                     if (curlevel == 0) {
01218                         /* no need to consider other roots */
01219                         root = HTM_N3;
01220                     } else {
01221                         /* no need to consider other children of parent */
01222                         curnode[-1].child = 4;
01223                     }
01224                     /* fall-through */
01225                 case HTM_INTERSECT:
01226                     if (curlevel < efflevel) {
01227                         /* continue subdividing */
01228                         _htm_node_prep0(curnode);
01229                         _htm_node_make0(curnode);
01230                         ++curnode;
01231                         ++curlevel;
01232                         continue;
01233                     }
01234                     /* fall-through */
01235                 case HTM_INSIDE:
01236                     /* reached a leaf or fully covered HTM triangle,
01237                        append HTM ID range to results */
01238                     {
01239                         int64_t id = curnode->id << (level - curlevel) * 2;
01240                         int64_t n = ((int64_t) 1) << (level - curlevel) * 2;
01241                         ids = _htm_ids_add(ids, id, id + n - 1);
01242                     }
01243                     if (ids == NULL) {
01244                         if (err != NULL) {
01245                             *err = HTM_ENOMEM;
01246                         }
01247                         return NULL;
01248                     }
01249                     while (ids->n > maxranges && efflevel != 0) {
01250                         /* too many ranges:
01251                            reduce effective subdivision level */
01252                         --efflevel;
01253                         if (curlevel > efflevel) {
01254                            curnode = curnode - (curlevel - efflevel);
01255                            curlevel = efflevel;
01256                         }
01257                         _htm_simplify_ids(ids, level - efflevel);
01258                     }
01259                     break;
01260                 default:
01261                     /* HTM triangle does not intersect circle */
01262                     break;
01263             }
01264             /* ascend towards the root */
01265             --curlevel;
01266             --curnode;
01267             while (curlevel >= 0 && curnode->child == 4) {
01268                 --curnode;
01269                 --curlevel;
01270             }
01271             if (curlevel < 0) {
01272                 /* finished with this root */
01273                 break;
01274             }
01275             if (curnode->child == 1) {
01276                 _htm_node_prep1(curnode);
01277                 _htm_node_make1(curnode);
01278             } else if (curnode->child == 2) {
01279                 _htm_node_prep2(curnode);
01280                 _htm_node_make2(curnode);
01281             } else {
01282                 _htm_node_make3(curnode);
01283             }
01284             ++curnode;
01285             ++curlevel;
01286         }
01287     }
01288     if (err != NULL) {
01289         *err = HTM_OK;
01290     }
01291     return ids;
01292 }
01293 
01294 
01295 struct htm_ids * htm_s2ellipse_ids(struct htm_ids *ids,
01296                                    const struct htm_s2ellipse *ellipse,
01297                                    int level,
01298                                    size_t maxranges,
01299                                    enum htm_errcode *err)
01300 {
01301     struct _htm_path path;
01302     enum htm_root root;
01303     int efflevel;
01304 
01305     if (ellipse == NULL) {
01306         if (err != NULL) {
01307             *err = HTM_ENULLPTR;
01308         }
01309         free(ids);
01310         return NULL;
01311     } else if (level < 0 || level > HTM_MAX_LEVEL) {
01312         if (err != NULL) {
01313             *err = HTM_ELEVEL;
01314         }
01315         free(ids);
01316         return NULL;
01317     }
01318     if (ids == NULL) {
01319         ids = _htm_ids_init();
01320         if (ids == NULL) {
01321             if (err != NULL) {
01322                 *err = HTM_ENOMEM;
01323             }
01324             return NULL;
01325         }
01326     } else {
01327         ids->n = 0;
01328     }
01329 
01330     efflevel = level;
01331     for (root = HTM_S0; root <= HTM_N3; ++root) {
01332         struct _htm_node *curnode = path.node;
01333         int curlevel = 0;
01334         _htm_path_root(&path, root);
01335 
01336         while (1) {
01337             switch (_htm_s2ellipse_htmcov(curnode, ellipse)) {
01338                 case HTM_CONTAINS:
01339                     if (curlevel == 0) {
01340                         /* no need to consider other roots */
01341                         root = HTM_N3;
01342                     } else {
01343                         /* no need to consider other children of parent */
01344                         curnode[-1].child = 4;
01345                     }
01346                     /* fall-through */
01347                 case HTM_INTERSECT:
01348                     if (curlevel < efflevel) {
01349                         /* continue subdividing */
01350                         _htm_node_prep0(curnode);
01351                         _htm_node_make0(curnode);
01352                         ++curnode;
01353                         ++curlevel;
01354                         continue;
01355                     }
01356                     /* fall-through */
01357                 case HTM_INSIDE:
01358                     /* reached a leaf or fully covered HTM triangle,
01359                        append HTM ID range to results */
01360                     {
01361                         int64_t id = curnode->id << (level - curlevel) * 2;
01362                         int64_t n = ((int64_t) 1) << (level - curlevel) * 2;
01363                         ids = _htm_ids_add(ids, id, id + n - 1);
01364                     }
01365                     if (ids == NULL) {
01366                         if (err != NULL) {
01367                             *err = HTM_ENOMEM;
01368                         }
01369                         return NULL;
01370                     }
01371                     while (ids->n > maxranges && efflevel != 0) {
01372                         /* too many ranges:
01373                            reduce effective subdivision level */
01374                         --efflevel;
01375                         if (curlevel > efflevel) {
01376                            curnode = curnode - (curlevel - efflevel);
01377                            curlevel = efflevel;
01378                         }
01379                         _htm_simplify_ids(ids, level - efflevel);
01380                     }
01381                     break;
01382                 default:
01383                     /* HTM triangle does not intersect circle */
01384                     break;
01385             }
01386             /* ascend towards the root */
01387             --curlevel;
01388             --curnode;
01389             while (curlevel >= 0 && curnode->child == 4) {
01390                 --curnode;
01391                 --curlevel;
01392             }
01393             if (curlevel < 0) {
01394                 /* finished with this root */
01395                 break;
01396             }
01397             if (curnode->child == 1) {
01398                 _htm_node_prep1(curnode);
01399                 _htm_node_make1(curnode);
01400             } else if (curnode->child == 2) {
01401                 _htm_node_prep2(curnode);
01402                 _htm_node_make2(curnode);
01403             } else {
01404                 _htm_node_make3(curnode);
01405             }
01406             ++curnode;
01407             ++curlevel;
01408         }
01409     }
01410     if (err != NULL) {
01411         *err = HTM_OK;
01412     }
01413     return ids;
01414 }
01415 
01416 
01417 struct htm_ids * htm_s2cpoly_ids(struct htm_ids *ids,
01418                                  const struct htm_s2cpoly *poly,
01419                                  int level,
01420                                  size_t maxranges,
01421                                  enum htm_errcode *err)
01422 {
01423     double stackab[2*256 + 4];
01424     struct _htm_path path;
01425     enum htm_root root;
01426     double *ab;
01427     size_t nb;
01428     int efflevel;
01429 
01430     if (poly == NULL) {
01431         if (err != NULL) {
01432             *err = HTM_ENULLPTR;
01433         }
01434         free(ids);
01435         return NULL;
01436     } else if (level < 0 || level > HTM_MAX_LEVEL) {
01437         if (err != NULL) {
01438             *err = HTM_ELEVEL;
01439         }
01440         free(ids);
01441         return NULL;
01442     }
01443     if (ids == NULL) {
01444         ids = _htm_ids_init();
01445         if (ids == NULL) {
01446             if (err != NULL) {
01447                 *err = HTM_ENOMEM;
01448             }
01449             return NULL;
01450         }
01451     } else {
01452         ids->n = 0;
01453     }
01454     nb = (2 * poly->n + 4) * sizeof(double);
01455     if (nb > sizeof(stackab)) {
01456         ab = (double *) malloc(nb);
01457         if (ab == NULL) {
01458             if (err != NULL) {
01459                 *err = HTM_ENOMEM;
01460             }
01461             free(ids);
01462             return NULL;
01463         }
01464     } else {
01465         ab = stackab;
01466     }
01467 
01468     efflevel = level;
01469 
01470     for (root = HTM_S0; root <= HTM_N3; ++root) {
01471         struct _htm_node *curnode = path.node;
01472         int curlevel = 0;
01473         _htm_path_root(&path, root);
01474 
01475         while (1) {
01476             switch (_htm_s2cpoly_htmcov(curnode, poly, ab)) {
01477                 case HTM_CONTAINS:
01478                     if (curlevel == 0) {
01479                         /* no need to consider other roots */
01480                         root = HTM_N3;
01481                     } else {
01482                         /* no need to consider other children of parent */
01483                         curnode[-1].child = 4;
01484                     }
01485                     /* fall-through */
01486                 case HTM_INTERSECT:
01487                     if (curlevel < efflevel) {
01488                         /* continue subdividing */
01489                         _htm_node_prep0(curnode);
01490                         _htm_node_make0(curnode);
01491                         ++curnode;
01492                         ++curlevel;
01493                         continue;
01494                     }
01495                     /* fall-through */
01496                 case HTM_INSIDE:
01497                     /* reached a leaf or fully covered HTM triangle,
01498                        append HTM ID range to results */
01499                     {
01500                         int64_t id = curnode->id << (level - curlevel) * 2;
01501                         int64_t n = ((int64_t) 1) << (level - curlevel) * 2;
01502                         ids = _htm_ids_add(ids, id, id + n - 1);
01503                     }
01504                     if (ids == NULL) {
01505                         if (ab != stackab) {
01506                             free(ab);
01507                         }
01508                         if (err != NULL) {
01509                             *err = HTM_ENOMEM;
01510                         }
01511                         return ids;
01512                     }
01513                     while (ids->n > maxranges && efflevel != 0) {
01514                         /* too many ranges:
01515                            reduce effetive subdivision level */
01516                         --efflevel;
01517                         if (curlevel > efflevel) {
01518                            curnode = curnode - (curlevel - efflevel);
01519                            curlevel = efflevel;
01520                         }
01521                         _htm_simplify_ids(ids, level - efflevel);
01522                     }
01523                     break;
01524                 default:
01525                     /* HTM triangle does not intersect polygon */
01526                     break;
01527             }
01528             /* ascend towards the root */
01529             --curlevel;
01530             --curnode;
01531             while (curlevel >= 0 && curnode->child == 4) {
01532                 --curnode;
01533                 --curlevel;
01534             }
01535             if (curlevel < 0) {
01536                 /* finished with this root */
01537                 break;
01538             }
01539             if (curnode->child == 1) {
01540                 _htm_node_prep1(curnode);
01541                 _htm_node_make1(curnode);
01542             } else if (curnode->child == 2) {
01543                 _htm_node_prep2(curnode);
01544                 _htm_node_make2(curnode);
01545             } else {
01546                 _htm_node_make3(curnode);
01547             }
01548             ++curnode;
01549             ++curlevel;
01550         }
01551     }
01552     if (ab != stackab) {
01553         free(ab);
01554     }
01555     if (err != NULL) {
01556         *err = HTM_OK;
01557     }
01558     return ids;
01559 }
01560 
01561 
01562 int64_t htm_idtodec(int64_t id)
01563 {
01564     int64_t dec = 0;
01565     int64_t factor = 1;
01566     int level = htm_level(id);
01567     if (level < 0 || level > HTM_DEC_MAX_LEVEL) {
01568         return 0;
01569     }
01570     for (++level; level > 0; --level, id >>= 2, factor *= 10) {
01571         dec += factor * (id & 3);
01572     }
01573     if ((id & 1) == 1) {
01574         dec += 2*factor;
01575     } else {
01576         dec += factor;
01577     }
01578     return dec;
01579 }
01580 
01581 
01582 int64_t htm_tree_s2circle_count(const struct htm_tree *tree,
01583                                 const struct htm_v3 *center,
01584                                 double radius,
01585                                 enum htm_errcode *err)
01586 {
01587     struct _htm_path path;
01588     enum htm_root root;
01589     double d2;
01590     int64_t count;
01591 
01592     if (tree == NULL || center == NULL) {
01593         if (err != NULL) {
01594             *err = HTM_ENULLPTR;
01595         }
01596         return -1;
01597     }
01598     if (tree->indexfd == -1) {
01599         return htm_tree_s2circle_scan(tree, center, radius, err);
01600     } else if (radius < 0.0) {
01601         /* circle is empty */
01602         return 0;
01603     } else if (radius >= 180.0) {
01604         /* entire sky */
01605         return (int64_t) tree->count;
01606     }
01607 
01608     count = 0;
01609     /* compute square of secant distance corresponding to radius */
01610     d2 = sin(radius * 0.5 * HTM_RAD_PER_DEG);
01611     d2 = 4.0 * d2 * d2;
01612 
01613     for (root = HTM_S0; root <= HTM_N3; ++root) {
01614         struct _htm_node *curnode = path.node;
01615         const unsigned char *s = tree->root[root];
01616         uint64_t index = 0;
01617         int level = 0;
01618 
01619         if (s == NULL) {
01620             /* root contains no points */
01621             continue;
01622         }
01623         _htm_path_root(&path, root);
01624 
01625         while (1) {
01626             uint64_t curcount = htm_varint_decode(s);
01627             s += 1 + htm_varint_nfollow(*s);
01628             index += htm_varint_decode(s);
01629             s += 1 + htm_varint_nfollow(*s);
01630             curnode->index = index;
01631 
01632             switch (_htm_s2circle_htmcov(curnode, center, d2)) {
01633                 case HTM_CONTAINS:
01634                     if (level == 0) {
01635                         /* no need to consider other roots */
01636                         root = HTM_N3;
01637                     } else {
01638                         /* no need to consider other children of parent */
01639                         curnode[-1].child = 4;
01640                     }
01641                     /* fall-through */
01642                 case HTM_INTERSECT:
01643                     if (level < 20 && curcount >= tree->leafthresh) {
01644                         s = _htm_subdivide(curnode, s);
01645                         if (s == NULL) {
01646                             /* tree is invalid */
01647                             if (err != NULL) {
01648                                 *err = HTM_EINV;
01649                             }
01650                             return -1;
01651                         }
01652                         ++level;
01653                         ++curnode;
01654                         continue;
01655                     }
01656                     /* scan points in leaf */
01657                     {
01658                         uint64_t i;
01659                         for (i = index; i < index + curcount; ++i) {
01660                             if (htm_v3_dist2(center, &tree->entries[i].v) <= d2) {
01661                                 ++count;
01662                             }
01663                         }
01664                     }
01665                     break;
01666                 case HTM_INSIDE:
01667                     /* fully covered HTM triangle */
01668                     count += (int64_t) curcount;
01669                     break;
01670                 default:
01671                     /* HTM triangle does not intersect circle */
01672                     break;
01673             }
01674             /* ascend towards the root */
01675 ascend:
01676             --level;
01677             --curnode;
01678             while (level >= 0 && curnode->child == 4) {
01679                 --curnode;
01680                 --level;
01681             }
01682             if (level < 0) {
01683                 /* finished with this root */
01684                 break;
01685             }
01686             index = curnode->index;
01687             s = _htm_subdivide(curnode, curnode->s);
01688             if (s == NULL) {
01689                 /* no non-empty children remain */
01690                 goto ascend;
01691             }
01692             ++level;
01693             ++curnode;
01694         }
01695     }
01696     if (err != NULL) {
01697         *err = HTM_OK;
01698     }
01699     return count;
01700 }
01701 
01702 
01703 int64_t htm_tree_s2ellipse_count(const struct htm_tree *tree,
01704                                  const struct htm_s2ellipse *ellipse,
01705                                  enum htm_errcode *err)
01706 {
01707     struct _htm_path path;
01708     enum htm_root root;
01709     int64_t count;
01710 
01711     if (tree == NULL || ellipse == NULL) {
01712         if (err != NULL) {
01713             *err = HTM_ENULLPTR;
01714         }
01715         return -1;
01716     }
01717     if (tree->indexfd == -1) {
01718         return htm_tree_s2ellipse_scan(tree, ellipse, err);
01719     }
01720 
01721     count = 0;
01722 
01723     for (root = HTM_S0; root <= HTM_N3; ++root) {
01724         struct _htm_node *curnode = path.node;
01725         const unsigned char *s = tree->root[root];
01726         uint64_t index = 0;
01727         int level = 0;
01728 
01729         if (s == NULL) {
01730             /* root contains no points */
01731             continue;
01732         }
01733         _htm_path_root(&path, root);
01734 
01735         while (1) {
01736             uint64_t curcount = htm_varint_decode(s);
01737             s += 1 + htm_varint_nfollow(*s);
01738             index += htm_varint_decode(s);
01739             s += 1 + htm_varint_nfollow(*s);
01740             curnode->index = index;
01741 
01742             switch (_htm_s2ellipse_htmcov(curnode, ellipse)) {
01743                 case HTM_CONTAINS:
01744                     if (level == 0) {
01745                         /* no need to consider other roots */
01746                         root = HTM_N3;
01747                     } else {
01748                         /* no need to consider other children of parent */
01749                         curnode[-1].child = 4;
01750                     }
01751                     /* fall-through */
01752                 case HTM_INTERSECT:
01753                     if (level < 20 && curcount >= tree->leafthresh) {
01754                         s = _htm_subdivide(curnode, s);
01755                         if (s == NULL) {
01756                             /* tree is invalid */
01757                             if (err != NULL) {
01758                                 *err = HTM_EINV;
01759                             }
01760                             return -1;
01761                         }
01762                         ++level;
01763                         ++curnode;
01764                         continue;
01765                     }
01766                     /* scan points in leaf */
01767                     {
01768                         uint64_t i;
01769                         for (i = index; i < index + curcount; ++i) {
01770                             if (htm_s2ellipse_cv3(ellipse, &tree->entries[i].v)) {
01771                                 ++count;
01772                             }
01773                         }
01774                     }
01775                     break;
01776                 case HTM_INSIDE:
01777                     /* fully covered HTM triangle */
01778                     count += (int64_t) curcount;
01779                     break;
01780                 default:
01781                     /* HTM triangle does not intersect circle */
01782                     break;
01783             }
01784             /* ascend towards the root */
01785 ascend:
01786             --level;
01787             --curnode;
01788             while (level >= 0 && curnode->child == 4) {
01789                 --curnode;
01790                 --level;
01791             }
01792             if (level < 0) {
01793                 /* finished with this root */
01794                 break;
01795             }
01796             index = curnode->index;
01797             s = _htm_subdivide(curnode, curnode->s);
01798             if (s == NULL) {
01799                 /* no non-empty children remain */
01800                 goto ascend;
01801             }
01802             ++level;
01803             ++curnode;
01804         }
01805     }
01806     if (err != NULL) {
01807         *err = HTM_OK;
01808     }
01809     return count;
01810 }
01811 
01812 
01813 int64_t htm_tree_s2cpoly_count(const struct htm_tree *tree,
01814                                const struct htm_s2cpoly *poly,
01815                                enum htm_errcode *err)
01816 {
01817     double stackab[2*256 + 4];
01818     struct _htm_path path;
01819     enum htm_root root;
01820     double *ab;
01821     size_t nb;
01822     int64_t count;
01823 
01824     if (tree == NULL || poly == NULL) {
01825         if (err != NULL) {
01826             *err = HTM_ENULLPTR;
01827         }
01828         return -1;
01829     }
01830     if (tree->indexfd == -1) {
01831         return htm_tree_s2cpoly_scan(tree, poly, err);
01832     }
01833     nb = (2 * poly->n + 4) * sizeof(double);
01834     if (nb > sizeof(stackab)) {
01835         ab = (double *) malloc(nb);
01836         if (ab == NULL) {
01837             if (err != NULL) {
01838                 *err = HTM_ENOMEM;
01839             }
01840             return -1;
01841         }
01842     } else {
01843         ab = stackab;
01844     }
01845     count = 0;
01846 
01847     for (root = HTM_S0; root <= HTM_N3; ++root) {
01848         struct _htm_node *curnode = path.node;
01849         const unsigned char *s = tree->root[root];
01850         uint64_t index = 0;
01851         int level = 0;
01852 
01853         if (s == NULL) {
01854             /* root contains no points */
01855             continue;
01856         }
01857         _htm_path_root(&path, root);
01858 
01859         while (1) {
01860             uint64_t curcount = htm_varint_decode(s);
01861             s += 1 + htm_varint_nfollow(*s);
01862             index += htm_varint_decode(s);
01863             s += 1 + htm_varint_nfollow(*s);
01864             curnode->index = index;
01865 
01866             switch (_htm_s2cpoly_htmcov(curnode, poly, ab)) {
01867                 case HTM_CONTAINS:
01868                     if (level == 0) {
01869                         /* no need to consider other roots */
01870                         root = HTM_N3;
01871                     } else {
01872                         /* no need to consider other children of parent */
01873                         curnode[-1].child = 4;
01874                     }
01875                     /* fall-through */
01876                 case HTM_INTERSECT:
01877                     if (level < 20 && curcount >= tree->leafthresh) {
01878                         s = _htm_subdivide(curnode, s);
01879                         if (s == NULL) {
01880                             /* tree is invalid */
01881                             if (ab != stackab) {
01882                                 free(ab);
01883                             }
01884                             if (err != NULL) {
01885                                 *err = HTM_EINV;
01886                             }
01887                             return -1;
01888                         }
01889                         ++level;
01890                         ++curnode;
01891                         continue;
01892                     }
01893                     /* scan points in leaf */
01894                     {
01895                         uint64_t i;
01896                         for (i = index; i < index + curcount; ++i) {
01897                             if (htm_s2cpoly_cv3(poly, &tree->entries[i].v)) {
01898                                 ++count;
01899                             }
01900                         }
01901                     }
01902                     break;
01903                 case HTM_INSIDE:
01904                     /* fully covered HTM triangle */
01905                     count += (int64_t) curcount;
01906                     break;
01907                 default:
01908                     /* HTM triangle does not intersect circle */
01909                     break;
01910             }
01911             /* ascend towards the root */
01912 ascend:
01913             --level;
01914             --curnode;
01915             while (level >= 0 && curnode->child == 4) {
01916                 --curnode;
01917                 --level;
01918             }
01919             if (level < 0) {
01920                 /* finished with this root */
01921                 break;
01922             }
01923             index = curnode->index;
01924             s = _htm_subdivide(curnode, curnode->s);
01925             if (s == NULL) {
01926                 /* no non-empty children remain */
01927                 goto ascend;
01928             }
01929             ++level;
01930             ++curnode;
01931         }
01932     }
01933     if (ab != stackab) {
01934         free(ab);
01935     }
01936     if (err != NULL) {
01937         *err = HTM_OK;
01938     }
01939     return count;
01940 }
01941 
01942 
01943 struct htm_range htm_tree_s2circle_range(const struct htm_tree *tree,
01944                                          const struct htm_v3 *center,
01945                                          double radius,
01946                                          enum htm_errcode *err)
01947 {
01948     struct _htm_path path;
01949     enum htm_root root;
01950     double d2;
01951     struct htm_range range;
01952 
01953     range.min = 0;
01954     range.max = 0;
01955     if (tree == NULL || center == NULL) {
01956         if (err != NULL) {
01957             *err = HTM_ENULLPTR;
01958         }
01959         range.max = -1;
01960         return range;
01961     }
01962     if (tree->indexfd == -1) {
01963         range.max = htm_tree_s2circle_scan(tree, center, radius, err);
01964         if (range.max >= 0) {
01965             range.min = range.max;
01966         }
01967         return range;
01968     } else if (radius < 0.0) {
01969         /* circle is empty */
01970         return range;
01971     } else if (radius >= 180.0) {
01972         /* entire sky */
01973         range.min = (int64_t) tree->count;
01974         range.max = (int64_t) tree->count;
01975         return range;
01976     }
01977     /* compute square of secant distance corresponding to radius */
01978     d2 = sin(radius * 0.5 * HTM_RAD_PER_DEG);
01979     d2 = 4.0 * d2 * d2;
01980 
01981     for (root = HTM_S0; root <= HTM_N3; ++root) {
01982         struct _htm_node *curnode = path.node;
01983         const unsigned char *s = tree->root[root];
01984         int level = 0;
01985 
01986         if (s == NULL) {
01987             /* root contains no points */
01988             continue;
01989         }
01990         _htm_path_root(&path, root);
01991 
01992         while (1) {
01993             uint64_t curcount = htm_varint_decode(s);
01994             s += 1 + htm_varint_nfollow(*s);
01995             s += 1 + htm_varint_nfollow(*s);
01996             switch (_htm_s2circle_htmcov(curnode, center, d2)) {
01997                 case HTM_CONTAINS:
01998                     if (level == 0) {
01999                         /* no need to consider other roots */
02000                         root = HTM_N3;
02001                     } else {
02002                         /* no need to consider other children of parent */
02003                         curnode[-1].child = 4;
02004                     }
02005                     /* fall-through */
02006                 case HTM_INTERSECT:
02007                     if (level < 20 && curcount >= tree->leafthresh) {
02008                         s = _htm_subdivide(curnode, s);
02009                         if (s == NULL) {
02010                             /* tree is invalid */
02011                             if (err != NULL) {
02012                                 *err = HTM_EINV;
02013                             }
02014                             range.max = -1;
02015                             return range;
02016                         }
02017                         ++level;
02018                         ++curnode;
02019                         continue;
02020                     }
02021                     range.max += (int64_t) curcount;
02022                     break;
02023                 case HTM_INSIDE:
02024                     /* fully covered HTM triangle */
02025                     range.min += (int64_t) curcount;
02026                     range.max += (int64_t) curcount;
02027                     break;
02028                 default:
02029                     /* HTM triangle does not intersect circle */
02030                     break;
02031             }
02032             /* ascend towards the root */
02033 ascend:
02034             --level;
02035             --curnode;
02036             while (level >= 0 && curnode->child == 4) {
02037                 --curnode;
02038                 --level;
02039             }
02040             if (level < 0) {
02041                 /* finished with this root */
02042                 break;
02043             }
02044             s = _htm_subdivide(curnode, curnode->s);
02045             if (s == NULL) {
02046                 /* no non-empty children remain */
02047                 goto ascend;
02048             }
02049             ++level;
02050             ++curnode;
02051         }
02052     }
02053     if (err != NULL) {
02054         *err = HTM_OK;
02055     }
02056     return range;
02057 }
02058 
02059 
02060 struct htm_range htm_tree_s2ellipse_range(const struct htm_tree *tree,
02061                                           const struct htm_s2ellipse *ellipse,
02062                                           enum htm_errcode *err)
02063 {
02064     struct _htm_path path;
02065     enum htm_root root;
02066     struct htm_range range;
02067 
02068     range.min = 0;
02069     range.max = 0;
02070     if (tree == NULL || ellipse== NULL) {
02071         if (err != NULL) {
02072             *err = HTM_ENULLPTR;
02073         }
02074         range.max = -1;
02075         return range;
02076     }
02077     if (tree->indexfd == -1) {
02078         range.max = htm_tree_s2ellipse_scan(tree, ellipse, err);
02079         if (range.max >= 0) {
02080             range.min = range.max;
02081         }
02082         return range;
02083     }
02084 
02085     for (root = HTM_S0; root <= HTM_N3; ++root) {
02086         struct _htm_node *curnode = path.node;
02087         const unsigned char *s = tree->root[root];
02088         int level = 0;
02089 
02090         if (s == NULL) {
02091             /* root contains no points */
02092             continue;
02093         }
02094         _htm_path_root(&path, root);
02095 
02096         while (1) {
02097             uint64_t curcount = htm_varint_decode(s);
02098             s += 1 + htm_varint_nfollow(*s);
02099             s += 1 + htm_varint_nfollow(*s);
02100             switch (_htm_s2ellipse_htmcov(curnode, ellipse)) {
02101                 case HTM_CONTAINS:
02102                     if (level == 0) {
02103                         /* no need to consider other roots */
02104                         root = HTM_N3;
02105                     } else {
02106                         /* no need to consider other children of parent */
02107                         curnode[-1].child = 4;
02108                     }
02109                     /* fall-through */
02110                 case HTM_INTERSECT:
02111                     if (level < 20 && curcount >= tree->leafthresh) {
02112                         s = _htm_subdivide(curnode, s);
02113                         if (s == NULL) {
02114                             /* tree is invalid */
02115                             if (err != NULL) {
02116                                 *err = HTM_EINV;
02117                             }
02118                             range.max = -1;
02119                             return range;
02120                         }
02121                         ++level;
02122                         ++curnode;
02123                         continue;
02124                     }
02125                     range.max += (int64_t) curcount;
02126                     break;
02127                 case HTM_INSIDE:
02128                     /* fully covered HTM triangle */
02129                     range.min += (int64_t) curcount;
02130                     range.max += (int64_t) curcount;
02131                     break;
02132                 default:
02133                     /* HTM triangle does not intersect circle */
02134                     break;
02135             }
02136             /* ascend towards the root */
02137 ascend:
02138             --level;
02139             --curnode;
02140             while (level >= 0 && curnode->child == 4) {
02141                 --curnode;
02142                 --level;
02143             }
02144             if (level < 0) {
02145                 /* finished with this root */
02146                 break;
02147             }
02148             s = _htm_subdivide(curnode, curnode->s);
02149             if (s == NULL) {
02150                 /* no non-empty children remain */
02151                 goto ascend;
02152             }
02153             ++level;
02154             ++curnode;
02155         }
02156     }
02157     if (err != NULL) {
02158         *err = HTM_OK;
02159     }
02160     return range;
02161 }
02162 
02163 
02164 struct htm_range htm_tree_s2cpoly_range(const struct htm_tree *tree,
02165                                         const struct htm_s2cpoly *poly,
02166                                         enum htm_errcode *err)
02167 {
02168     double stackab[2*256 + 4];
02169     struct _htm_path path;
02170     enum htm_root root;
02171     double *ab;
02172     size_t nb;
02173     struct htm_range range;
02174 
02175     range.min = 0;
02176     range.max = 0;
02177     if (tree == NULL || poly == NULL) {
02178         if (err != NULL) {
02179             *err = HTM_ENULLPTR;
02180         }
02181         range.max = -1;
02182         return range;
02183     }
02184     if (tree->indexfd == -1) {
02185         range.max = htm_tree_s2cpoly_scan(tree, poly, err);
02186         if (range.max >= 0) {
02187             range.min = range.max;
02188         }
02189         return range;
02190     }
02191     nb = (2 * poly->n + 4) * sizeof(double);
02192     if (nb > sizeof(stackab)) {
02193         ab = (double *) malloc(nb);
02194         if (ab == NULL) {
02195             if (err != NULL) {
02196                 *err = HTM_ENOMEM;
02197             }
02198             range.max = -1;
02199             return range;
02200         }
02201     } else {
02202         ab = stackab;
02203     }
02204 
02205     for (root = HTM_S0; root <= HTM_N3; ++root) {
02206         struct _htm_node *curnode = path.node;
02207         const unsigned char *s = tree->root[root];
02208         int level = 0;
02209 
02210         if (s == NULL) {
02211             /* root contains no points */
02212             continue;
02213         }
02214         _htm_path_root(&path, root);
02215 
02216         while (1) {
02217             uint64_t curcount = htm_varint_decode(s);
02218             s += 1 + htm_varint_nfollow(*s);
02219             s += 1 + htm_varint_nfollow(*s);
02220             switch (_htm_s2cpoly_htmcov(curnode, poly, ab)) {
02221                 case HTM_CONTAINS:
02222                     if (level == 0) {
02223                         /* no need to consider other roots */
02224                         root = HTM_N3;
02225                     } else {
02226                         /* no need to consider other children of parent */
02227                         curnode[-1].child = 4;
02228                     }
02229                     /* fall-through */
02230                 case HTM_INTERSECT:
02231                     if (level < 20 && curcount >= tree->leafthresh) {
02232                         s = _htm_subdivide(curnode, s);
02233                         if (s == NULL) {
02234                             /* tree is invalid */
02235                             if (ab != stackab) {
02236                                 free(ab);
02237                             }
02238                             if (err != NULL) {
02239                                 *err = HTM_EINV;
02240                             }
02241                             range.max = -1;
02242                             return range;
02243                         }
02244                         ++level;
02245                         ++curnode;
02246                         continue;
02247                     }
02248                     range.max += (int64_t) curcount;
02249                     break;
02250                 case HTM_INSIDE:
02251                     /* fully covered HTM triangle */
02252                     range.min += (int64_t) curcount;
02253                     range.max += (int64_t) curcount;
02254                     break;
02255                 default:
02256                     /* HTM triangle does not intersect circle */
02257                     break;
02258             }
02259             /* ascend towards the root */
02260 ascend:
02261             --level;
02262             --curnode;
02263             while (level >= 0 && curnode->child == 4) {
02264                 --curnode;
02265                 --level;
02266             }
02267             if (level < 0) {
02268                 /* finished with this root */
02269                 break;
02270             }
02271             s = _htm_subdivide(curnode, curnode->s);
02272             if (s == NULL) {
02273                 /* no non-empty children remain */
02274                 goto ascend;
02275             }
02276             ++level;
02277             ++curnode;
02278         }
02279     }
02280     if (ab != stackab) {
02281         free(ab);
02282     }
02283     if (err != NULL) {
02284         *err = HTM_OK;
02285     }
02286     return range;
02287 }
02288 
02289 
02290 #ifdef __cplusplus
02291 }
02292 #endif