Coverage Report

Created: 2025-07-12 06:04

/src/h3/src/h3lib/lib/h3Index.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2016-2021, 2024 Uber Technologies, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *         http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/** @file h3Index.c
17
 * @brief   H3Index utility functions
18
 *          (see h3api.h for the main library entry functions)
19
 */
20
#include "h3Index.h"
21
22
#include <inttypes.h>
23
#include <math.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "alloc.h"
28
#include "baseCells.h"
29
#include "faceijk.h"
30
#include "h3Assert.h"
31
#include "iterators.h"
32
#include "mathExtensions.h"
33
34
/** @var H3ErrorDescriptions
35
 *  @brief An array of strings describing each of the H3ErrorCodes enum values
36
 */
37
static char *H3ErrorDescriptions[] = {
38
    /* E_SUCCESS */ "Success",
39
    /* E_FAILED */
40
    "The operation failed but a more specific error is not available",
41
    /* E_DOMAIN */ "Argument was outside of acceptable range",
42
    /* E_LATLNG_DOMAIN */
43
    "Latitude or longitude arguments were outside of acceptable range",
44
    /* E_RES_DOMAIN */ "Resolution argument was outside of acceptable range",
45
    /* E_CELL_INVALID */ "Cell argument was not valid",
46
    /* E_DIR_EDGE_INVALID */ "Directed edge argument was not valid",
47
    /* E_UNDIR_EDGE_INVALID */ "Undirected edge argument was not valid",
48
    /* E_VERTEX_INVALID */ "Vertex argument was not valid",
49
    /* E_PENTAGON */ "Pentagon distortion was encountered",
50
    /* E_DUPLICATE_INPUT */ "Duplicate input",
51
    /* E_NOT_NEIGHBORS */ "Cell arguments were not neighbors",
52
    /* E_RES_MISMATCH */ "Cell arguments had incompatible resolutions",
53
    /* E_MEMORY_ALLOC */ "Memory allocation failed",
54
    /* E_MEMORY_BOUNDS */ "Bounds of provided memory were insufficient",
55
    /* E_OPTION_INVALID */ "Mode or flags argument was not valid"};
56
57
/**
58
 * Returns the string describing the H3Error. This string is internally
59
 * allocated and should not be `free`d.
60
 * @param err The H3 error.
61
 * @return The string describing the H3Error
62
 */
63
0
const char *H3_EXPORT(describeH3Error)(H3Error err) {
64
    // err is always non-negative because it is an unsigned integer
65
    // TODO: Better way to bounds check here?
66
0
    if (err <= 15) {
67
0
        return H3ErrorDescriptions[err];
68
0
    } else {
69
0
        return "Invalid error code";
70
0
    }
71
0
}
72
73
/**
74
 * Returns the H3 resolution of an H3 index.
75
 * @param h The H3 index.
76
 * @return The resolution of the H3 index argument.
77
 */
78
0
int H3_EXPORT(getResolution)(H3Index h) { return H3_GET_RESOLUTION(h); }
79
80
/**
81
 * Returns the H3 base cell "number" of an H3 cell (hexagon or pentagon).
82
 *
83
 * Note: Technically works on H3 edges, but will return base cell of the
84
 * origin cell.
85
 *
86
 * @param h The H3 cell.
87
 * @return The base cell "number" of the H3 cell argument.
88
 */
89
0
int H3_EXPORT(getBaseCellNumber)(H3Index h) { return H3_GET_BASE_CELL(h); }
90
91
/**
92
 * Converts a string representation of an H3 index into an H3 index.
93
 * @param str The string representation of an H3 index.
94
 * @param out Output: The H3 index corresponding to the string argument
95
 */
96
0
H3Error H3_EXPORT(stringToH3)(const char *str, H3Index *out) {
97
0
    H3Index h = H3_NULL;
98
    // If failed, h will be unmodified and we should return H3_NULL anyways.
99
0
    int read = sscanf(str, "%" PRIx64, &h);
100
0
    if (read != 1) {
101
0
        return E_FAILED;
102
0
    }
103
0
    *out = h;
104
0
    return E_SUCCESS;
105
0
}
106
107
/**
108
 * Converts an H3 index into a string representation.
109
 * @param h The H3 index to convert.
110
 * @param str The string representation of the H3 index.
111
 * @param sz Size of the buffer `str`
112
 */
113
0
H3Error H3_EXPORT(h3ToString)(H3Index h, char *str, size_t sz) {
114
    // An unsigned 64 bit integer will be expressed in at most
115
    // 16 digits plus 1 for the null terminator.
116
0
    if (sz < 17) {
117
        // Buffer is potentially not large enough.
118
0
        return E_MEMORY_BOUNDS;
119
0
    }
120
0
    sprintf(str, "%" PRIx64, h);
121
0
    return E_SUCCESS;
122
0
}
123
124
/*
125
The top 8 bits of any cell should be a specific constant:
126
127
- The 1 high bit should be `0`
128
- The 4 mode bits should be `0001` (H3_CELL_MODE)
129
- The 3 reserved bits should be `000`
130
131
In total, the top 8 bits should be `0_0001_000`
132
*/
133
0
static inline bool _hasGoodTopBits(H3Index h) {
134
0
    h >>= (64 - 8);
135
0
    return h == 0b00001000;
136
0
}
137
138
/* Check that no digit from 1 to `res` is 7 (INVALID_DIGIT).
139
140
MHI = 0b100100100100100100100100100100100100100100100;
141
MLO = MHI >> 2;
142
143
|  d  | d & MHI |  ~d | ~d - MLO | d & MHI & (~d - MLO) |  result |
144
|-----|---------|-----|----------|----------------------|---------|
145
| 000 |     000 |     |          |                  000 | OK      |
146
| 001 |     000 |     |          |                  000 | OK      |
147
| 010 |     000 |     |          |                  000 | OK      |
148
| 011 |     000 |     |          |                  000 | OK      |
149
| 100 |     100 | 011 | 010      |                  000 | OK      |
150
| 101 |     100 | 010 | 001      |                  000 | OK      |
151
| 110 |     100 | 001 | 000      |                  000 | OK      |
152
| 111 |     100 | 000 | 111*     |                  100 | invalid |
153
154
  *: carry happened
155
156
157
Note: only care about identifying the *lowest* 7.
158
159
Examples with multiple digits:
160
161
|    d    | d & MHI |    ~d   | ~d - MLO | d & MHI & (~d - MLO) |  result |
162
|---------|---------|---------|----------|----------------------|---------|
163
| 111.111 | 100.100 | 000.000 | 110.111* |              100.100 | invalid |
164
| 110.111 | 100.100 | 001.000 | 111.111* |              100.100 | invalid |
165
| 110.110 | 100.100 | 001.001 | 000.000  |              000.000 | OK      |
166
167
  *: carry happened
168
169
In the second example with 110.111, we "misidentify" the 110 as a 7, due
170
to a carry from the lower bits. But this is OK because we correctly
171
identify the lowest (only, in this example) 7 just before it.
172
173
We only have to worry about carries affecting higher bits in the case of
174
a 7; all other digits (0--6) don't cause a carry when computing ~d - MLO.
175
So even though a 7 can affect the results of higher bits, this is OK
176
because we will always correctly identify the lowest 7.
177
178
For further notes, see the discussion here:
179
https://github.com/uber/h3/pull/496#discussion_r795851046
180
*/
181
0
static inline bool _hasAny7UptoRes(H3Index h, int res) {
182
0
    const uint64_t MHI = 0b100100100100100100100100100100100100100100100;
183
0
    const uint64_t MLO = MHI >> 2;
184
185
0
    int shift = 3 * (15 - res);
186
0
    h >>= shift;
187
0
    h <<= shift;
188
0
    h = (h & MHI & (~h - MLO));
189
190
0
    return h != 0;
191
0
}
192
193
/* Check that all unused digits after `res` are set to 7 (INVALID_DIGIT).
194
195
Bit shift to avoid looping through digits.
196
*/
197
0
static inline bool _hasAll7AfterRes(H3Index h, int res) {
198
    // NOTE: res check is needed because we can't shift by 64
199
0
    if (res < 15) {
200
0
        int shift = 19 + 3 * res;
201
202
0
        h = ~h;
203
0
        h <<= shift;
204
0
        h >>= shift;
205
206
0
        return h == 0;
207
0
    }
208
0
    return true;
209
0
}
210
211
/*
212
Get index of first nonzero bit of an H3Index.
213
214
When available, use compiler intrinsics, which should be fast.
215
If not available, fall back to a loop.
216
*/
217
0
static inline int _firstOneIndex(H3Index h) {
218
0
#if defined(__GNUC__) || defined(__clang__)
219
0
    return 63 - __builtin_clzll(h);
220
#elif defined(_MSC_VER) && defined(_M_X64)  // doesn't work on win32
221
    unsigned long index;
222
    _BitScanReverse64(&index, h);
223
    return (int)index;
224
#else
225
    // Portable fallback
226
    int pos = 63 - 19;
227
    H3Index m = 1;
228
    while ((h & (m << pos)) == 0) pos--;
229
    return pos;
230
#endif
231
0
}
232
233
/*
234
One final validation just for cells whose base cell (res 0)
235
is a pentagon.
236
237
Pentagon cells start with a sequence of 0's (CENTER_DIGIT's).
238
The first nonzero digit can't be a 1 (i.e., "deleted subsequence",
239
PENTAGON_SKIPPED_DIGIT, or K_AXES_DIGIT).
240
241
We can check that (in the lower 45 = 15*3 bits) the position of the
242
first 1 bit isn't divisible by 3.
243
*/
244
0
static inline bool _hasDeletedSubsequence(H3Index h, int base_cell) {
245
    // TODO: https://github.com/uber/h3/issues/984
246
0
    static const bool isBaseCellPentagonArr[128] = {
247
0
        [4] = 1,  [14] = 1, [24] = 1, [38] = 1, [49] = 1,  [58] = 1,
248
0
        [63] = 1, [72] = 1, [83] = 1, [97] = 1, [107] = 1, [117] = 1};
249
250
0
    if (isBaseCellPentagonArr[base_cell]) {
251
0
        h <<= 19;
252
0
        h >>= 19;
253
254
0
        if (h == 0) return false;  // all zeros: res 15 pentagon
255
0
        return _firstOneIndex(h) % 3 == 0;
256
0
    }
257
0
    return false;
258
0
}
259
260
/**
261
 * Returns whether or not an H3 index is a valid cell (hexagon or pentagon).
262
 * @param h The H3 index to validate.
263
 * @return 1 if the H3 index if valid, and 0 if it is not.
264
 */
265
0
int H3_EXPORT(isValidCell)(H3Index h) {
266
    /*
267
    Look for bit patterns that would disqualify an H3Index from
268
    being valid. If identified, exit early.
269
270
    For reference the H3 index bit layout:
271
272
    |   Region   | # bits |
273
    |------------|--------|
274
    | High       |      1 |
275
    | Mode       |      4 |
276
    | Reserved   |      3 |
277
    | Resolution |      4 |
278
    | Base Cell  |      7 |
279
    | Digit 1    |      3 |
280
    | Digit 2    |      3 |
281
    | ...        |    ... |
282
    | Digit 15   |      3 |
283
284
    Speed benefits come from using bit manipulation instead of loops,
285
    whenever possible.
286
    */
287
0
    if (!_hasGoodTopBits(h)) return false;
288
289
    // No need to check resolution; any 4 bits give a valid resolution.
290
0
    const int res = H3_GET_RESOLUTION(h);
291
292
    // Get base cell number and check that it is valid.
293
0
    const int bc = H3_GET_BASE_CELL(h);
294
0
    if (bc >= NUM_BASE_CELLS) return false;
295
296
0
    if (_hasAny7UptoRes(h, res)) return false;
297
0
    if (!_hasAll7AfterRes(h, res)) return false;
298
0
    if (_hasDeletedSubsequence(h, bc)) return false;
299
300
    // If no disqualifications were identified, the index is a valid H3 cell.
301
0
    return true;
302
0
}
303
304
/**
305
 * Initializes an H3 index.
306
 * @param hp The H3 index to initialize.
307
 * @param res The H3 resolution to initialize the index to.
308
 * @param baseCell The H3 base cell to initialize the index to.
309
 * @param initDigit The H3 digit (0-7) to initialize all of the index digits to.
310
 */
311
0
void setH3Index(H3Index *hp, int res, int baseCell, Direction initDigit) {
312
0
    H3Index h = H3_INIT;
313
0
    H3_SET_MODE(h, H3_CELL_MODE);
314
0
    H3_SET_RESOLUTION(h, res);
315
0
    H3_SET_BASE_CELL(h, baseCell);
316
0
    for (int r = 1; r <= res; r++) H3_SET_INDEX_DIGIT(h, r, initDigit);
317
0
    *hp = h;
318
0
}
319
320
/**
321
 * cellToParent produces the parent index for a given H3 index
322
 *
323
 * @param h H3Index to find parent of
324
 * @param parentRes The resolution to switch to (parent, grandparent, etc)
325
 * @param out Output: H3Index of the parent
326
 */
327
0
H3Error H3_EXPORT(cellToParent)(H3Index h, int parentRes, H3Index *out) {
328
0
    int childRes = H3_GET_RESOLUTION(h);
329
0
    if (parentRes < 0 || parentRes > MAX_H3_RES) {
330
0
        return E_RES_DOMAIN;
331
0
    } else if (parentRes > childRes) {
332
0
        return E_RES_MISMATCH;
333
0
    } else if (parentRes == childRes) {
334
0
        *out = h;
335
0
        return E_SUCCESS;
336
0
    }
337
0
    H3Index parentH = H3_SET_RESOLUTION(h, parentRes);
338
0
    for (int i = parentRes + 1; i <= childRes; i++) {
339
0
        H3_SET_INDEX_DIGIT(parentH, i, H3_DIGIT_MASK);
340
0
    }
341
0
    *out = parentH;
342
0
    return E_SUCCESS;
343
0
}
344
345
/**
346
 * Determines whether one resolution is a valid child resolution for a cell.
347
 * Each resolution is considered a valid child resolution of itself.
348
 *
349
 * @param h         h3Index  parent cell
350
 * @param childRes  int      resolution of the child
351
 *
352
 * @return The validity of the child resolution
353
 */
354
0
static bool _hasChildAtRes(H3Index h, int childRes) {
355
0
    int parentRes = H3_GET_RESOLUTION(h);
356
0
    if (childRes < parentRes || childRes > MAX_H3_RES) {
357
0
        return false;
358
0
    }
359
0
    return true;
360
0
}
361
362
/**
363
 * cellToChildrenSize returns the exact number of children for a cell at a
364
 * given child resolution.
365
 *
366
 * @param h         H3Index to find the number of children of
367
 * @param childRes  The child resolution you're interested in
368
 * @param out      Output: exact number of children (handles hexagons and
369
 * pentagons correctly)
370
 */
371
0
H3Error H3_EXPORT(cellToChildrenSize)(H3Index h, int childRes, int64_t *out) {
372
0
    if (!_hasChildAtRes(h, childRes)) return E_RES_DOMAIN;
373
374
0
    int n = childRes - H3_GET_RESOLUTION(h);
375
376
0
    if (H3_EXPORT(isPentagon)(h)) {
377
0
        *out = 1 + 5 * (_ipow(7, n) - 1) / 6;
378
0
    } else {
379
0
        *out = _ipow(7, n);
380
0
    }
381
0
    return E_SUCCESS;
382
0
}
383
384
/**
385
 * makeDirectChild takes an index and immediately returns the immediate child
386
 * index based on the specified cell number. Bit operations only, could generate
387
 * invalid indexes if not careful (deleted cell under a pentagon).
388
 *
389
 * @param h H3Index to find the direct child of
390
 * @param cellNumber int id of the direct child (0-6)
391
 *
392
 * @return The new H3Index for the child
393
 */
394
0
H3Index makeDirectChild(H3Index h, int cellNumber) {
395
0
    int childRes = H3_GET_RESOLUTION(h) + 1;
396
0
    H3Index childH = H3_SET_RESOLUTION(h, childRes);
397
0
    H3_SET_INDEX_DIGIT(childH, childRes, cellNumber);
398
0
    return childH;
399
0
}
400
401
/**
402
 * cellToChildren takes the given hexagon id and generates all of the children
403
 * at the specified resolution storing them into the provided memory pointer.
404
 * It's assumed that cellToChildrenSize was used to determine the allocation.
405
 *
406
 * @param h H3Index to find the children of
407
 * @param childRes int the child level to produce
408
 * @param children H3Index* the memory to store the resulting addresses in
409
 */
410
0
H3Error H3_EXPORT(cellToChildren)(H3Index h, int childRes, H3Index *children) {
411
0
    int64_t i = 0;
412
0
    for (IterCellsChildren iter = iterInitParent(h, childRes); iter.h;
413
0
         iterStepChild(&iter)) {
414
0
        children[i] = iter.h;
415
0
        i++;
416
0
    }
417
0
    return E_SUCCESS;
418
0
}
419
420
/**
421
 * Zero out index digits from start to end, inclusive.
422
 * No-op if start > end.
423
 */
424
0
H3Index _zeroIndexDigits(H3Index h, int start, int end) {
425
0
    if (start > end) return h;
426
427
0
    H3Index m = 0;
428
429
0
    m = ~m;
430
0
    m <<= H3_PER_DIGIT_OFFSET * (end - start + 1);
431
0
    m = ~m;
432
0
    m <<= H3_PER_DIGIT_OFFSET * (MAX_H3_RES - end);
433
0
    m = ~m;
434
435
0
    return h & m;
436
0
}
437
438
/**
439
 * cellToCenterChild produces the center child index for a given H3 index at
440
 * the specified resolution
441
 *
442
 * @param h H3Index to find center child of
443
 * @param childRes The resolution to switch to
444
 * @param child H3Index of the center child
445
 * @return 0 (E_SUCCESS) on success
446
 */
447
0
H3Error H3_EXPORT(cellToCenterChild)(H3Index h, int childRes, H3Index *child) {
448
0
    if (!_hasChildAtRes(h, childRes)) return E_RES_DOMAIN;
449
450
0
    h = _zeroIndexDigits(h, H3_GET_RESOLUTION(h) + 1, childRes);
451
0
    H3_SET_RESOLUTION(h, childRes);
452
0
    *child = h;
453
0
    return E_SUCCESS;
454
0
}
455
456
/**
457
 * compactCells takes a set of hexagons all at the same resolution and
458
 * compresses them by pruning full child branches to the parent level. This is
459
 * also done for all parents recursively to get the minimum number of hex
460
 * addresses that perfectly cover the defined space.
461
 * @param h3Set Set of hexagons
462
 * @param compactedSet The output array of compressed hexagons (preallocated)
463
 * @param numHexes The size of the input and output arrays (possible that no
464
 * contiguous regions exist in the set at all and no compression possible)
465
 * @return an error code on bad input data
466
 */
467
H3Error H3_EXPORT(compactCells)(const H3Index *h3Set, H3Index *compactedSet,
468
0
                                const int64_t numHexes) {
469
0
    if (numHexes == 0) {
470
0
        return E_SUCCESS;
471
0
    }
472
0
    int res = H3_GET_RESOLUTION(h3Set[0]);
473
0
    if (res == 0) {
474
        // No compaction possible, just copy the set to output
475
0
        for (int64_t i = 0; i < numHexes; i++) {
476
0
            compactedSet[i] = h3Set[i];
477
0
        }
478
0
        return E_SUCCESS;
479
0
    }
480
0
    H3Index *remainingHexes = H3_MEMORY(malloc)(numHexes * sizeof(H3Index));
481
0
    if (!remainingHexes) {
482
0
        return E_MEMORY_ALLOC;
483
0
    }
484
0
    memcpy(remainingHexes, h3Set, numHexes * sizeof(H3Index));
485
0
    H3Index *hashSetArray = H3_MEMORY(calloc)(numHexes, sizeof(H3Index));
486
0
    if (!hashSetArray) {
487
0
        H3_MEMORY(free)(remainingHexes);
488
0
        return E_MEMORY_ALLOC;
489
0
    }
490
0
    H3Index *compactedSetOffset = compactedSet;
491
0
    int64_t numRemainingHexes = numHexes;
492
0
    while (numRemainingHexes) {
493
0
        res = H3_GET_RESOLUTION(remainingHexes[0]);
494
0
        int parentRes = res - 1;
495
496
        // If parentRes is less than zero, we've compacted all the way up to the
497
        // base cells. Time to process the remaining cells.
498
0
        if (parentRes >= 0) {
499
            // Put the parents of the hexagons into the temp array
500
            // via a hashing mechanism, and use the reserved bits
501
            // to track how many times a parent is duplicated
502
0
            for (int64_t i = 0; i < numRemainingHexes; i++) {
503
0
                H3Index currIndex = remainingHexes[i];
504
                // TODO: This case is coverable (reachable by fuzzer)
505
0
                if (currIndex != 0) {
506
                    // If the reserved bits were set by the caller, the
507
                    // algorithm below may encounter undefined behavior
508
                    // because it expects to have set the reserved bits
509
                    // itself.
510
0
                    if (H3_GET_RESERVED_BITS(currIndex) != 0) {
511
0
                        H3_MEMORY(free)(remainingHexes);
512
0
                        H3_MEMORY(free)(hashSetArray);
513
0
                        return E_CELL_INVALID;
514
0
                    }
515
516
0
                    H3Index parent;
517
0
                    H3Error parentError =
518
0
                        H3_EXPORT(cellToParent)(currIndex, parentRes, &parent);
519
                    // Should never be reachable as a result of the compact
520
                    // algorithm. Can happen if cellToParent errors e.g.
521
                    // because of incompatible resolutions.
522
0
                    if (parentError) {
523
0
                        H3_MEMORY(free)(remainingHexes);
524
0
                        H3_MEMORY(free)(hashSetArray);
525
0
                        return parentError;
526
0
                    }
527
                    // Modulus hash the parent into the temp array
528
0
                    int64_t loc = (int64_t)(parent % numRemainingHexes);
529
0
                    int64_t loopCount = 0;
530
0
                    while (hashSetArray[loc] != 0) {
531
0
                        if (NEVER(loopCount > numRemainingHexes)) {
532
                            // This case should not be possible because at
533
                            // most one index is placed into hashSetArray
534
                            // per numRemainingHexes.
535
0
                            H3_MEMORY(free)(remainingHexes);
536
0
                            H3_MEMORY(free)(hashSetArray);
537
0
                            return E_FAILED;
538
0
                        }
539
0
                        H3Index tempIndex =
540
0
                            hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE;
541
0
                        if (tempIndex == parent) {
542
0
                            int count =
543
0
                                H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1;
544
0
                            int limitCount = 7;
545
0
                            if (H3_EXPORT(isPentagon)(
546
0
                                    tempIndex & H3_RESERVED_MASK_NEGATIVE)) {
547
0
                                limitCount--;
548
0
                            }
549
                            // One is added to count for this check to match
550
                            // one being added to count later in this
551
                            // function when checking for all children being
552
                            // present.
553
0
                            if (count + 1 > limitCount) {
554
                                // Only possible on duplicate input
555
0
                                H3_MEMORY(free)(remainingHexes);
556
0
                                H3_MEMORY(free)(hashSetArray);
557
0
                                return E_DUPLICATE_INPUT;
558
0
                            }
559
0
                            H3_SET_RESERVED_BITS(parent, count);
560
0
                            hashSetArray[loc] = H3_NULL;
561
0
                        } else {
562
0
                            loc = (loc + 1) % numRemainingHexes;
563
0
                        }
564
0
                        loopCount++;
565
0
                    }
566
0
                    hashSetArray[loc] = parent;
567
0
                }
568
0
            }
569
0
        }
570
571
        // Determine which parent hexagons have a complete set
572
        // of children and put them in the compactableHexes array
573
0
        int64_t compactableCount = 0;
574
0
        int64_t maxCompactableCount =
575
0
            numRemainingHexes / 6;  // Somehow all pentagons; conservative
576
0
        if (maxCompactableCount == 0) {
577
0
            memcpy(compactedSetOffset, remainingHexes,
578
0
                   numRemainingHexes * sizeof(remainingHexes[0]));
579
0
            break;
580
0
        }
581
0
        H3Index *compactableHexes =
582
0
            H3_MEMORY(calloc)(maxCompactableCount, sizeof(H3Index));
583
0
        if (!compactableHexes) {
584
0
            H3_MEMORY(free)(remainingHexes);
585
0
            H3_MEMORY(free)(hashSetArray);
586
0
            return E_MEMORY_ALLOC;
587
0
        }
588
0
        for (int64_t i = 0; i < numRemainingHexes; i++) {
589
0
            if (hashSetArray[i] == 0) continue;
590
0
            int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1;
591
            // Include the deleted direction for pentagons as implicitly "there"
592
0
            if (H3_EXPORT(isPentagon)(hashSetArray[i] &
593
0
                                      H3_RESERVED_MASK_NEGATIVE)) {
594
                // We need this later on, no need to recalculate
595
0
                H3_SET_RESERVED_BITS(hashSetArray[i], count);
596
                // Increment count after setting the reserved bits,
597
                // since count is already incremented above, so it
598
                // will be the expected value for a complete hexagon.
599
0
                count++;
600
0
            }
601
0
            if (count == 7) {
602
                // Bingo! Full set!
603
0
                compactableHexes[compactableCount] =
604
0
                    hashSetArray[i] & H3_RESERVED_MASK_NEGATIVE;
605
0
                compactableCount++;
606
0
            }
607
0
        }
608
        // Uncompactable hexes are immediately copied into the
609
        // output compactedSetOffset
610
0
        int64_t uncompactableCount = 0;
611
0
        for (int64_t i = 0; i < numRemainingHexes; i++) {
612
0
            H3Index currIndex = remainingHexes[i];
613
            // TODO: This case is coverable (reachable by fuzzer)
614
0
            if (currIndex != H3_NULL) {
615
0
                bool isUncompactable = true;
616
                // Resolution 0 cells always uncompactable, and trying to take
617
                // the res -1 parent of a cell is invalid.
618
0
                if (parentRes >= 0) {
619
0
                    H3Index parent;
620
0
                    H3Error parentError =
621
0
                        H3_EXPORT(cellToParent)(currIndex, parentRes, &parent);
622
0
                    if (NEVER(parentError)) {
623
0
                        H3_MEMORY(free)(compactableHexes);
624
0
                        H3_MEMORY(free)(remainingHexes);
625
0
                        H3_MEMORY(free)(hashSetArray);
626
0
                        return parentError;
627
0
                    }
628
                    // Modulus hash the parent into the temp array
629
                    // to determine if this index was included in
630
                    // the compactableHexes array
631
0
                    int64_t loc = (int64_t)(parent % numRemainingHexes);
632
0
                    int64_t loopCount = 0;
633
0
                    do {
634
0
                        if (NEVER(loopCount > numRemainingHexes)) {
635
                            // This case should not be possible because at most
636
                            // one index is placed into hashSetArray per input
637
                            // hexagon.
638
0
                            H3_MEMORY(free)(compactableHexes);
639
0
                            H3_MEMORY(free)(remainingHexes);
640
0
                            H3_MEMORY(free)(hashSetArray);
641
0
                            return E_FAILED;
642
0
                        }
643
0
                        H3Index tempIndex =
644
0
                            hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE;
645
0
                        if (tempIndex == parent) {
646
0
                            int count =
647
0
                                H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1;
648
0
                            if (count == 7) {
649
0
                                isUncompactable = false;
650
0
                            }
651
0
                            break;
652
0
                        } else {
653
0
                            loc = (loc + 1) % numRemainingHexes;
654
0
                        }
655
0
                        loopCount++;
656
0
                    } while (hashSetArray[loc] != parent);
657
0
                }
658
0
                if (isUncompactable) {
659
0
                    compactedSetOffset[uncompactableCount] = remainingHexes[i];
660
0
                    uncompactableCount++;
661
0
                }
662
0
            }
663
0
        }
664
        // Set up for the next loop
665
0
        memset(hashSetArray, 0, numHexes * sizeof(H3Index));
666
0
        compactedSetOffset += uncompactableCount;
667
0
        memcpy(remainingHexes, compactableHexes,
668
0
               compactableCount * sizeof(H3Index));
669
0
        numRemainingHexes = compactableCount;
670
0
        H3_MEMORY(free)(compactableHexes);
671
0
    }
672
0
    H3_MEMORY(free)(remainingHexes);
673
0
    H3_MEMORY(free)(hashSetArray);
674
0
    return E_SUCCESS;
675
0
}
676
677
/**
678
 * uncompactCells takes a compressed set of cells and expands back to the
679
 * original set of cells.
680
 *
681
 * Skips elements that are H3_NULL (i.e., 0).
682
 *
683
 * @param   compactedSet  Set of compacted cells
684
 * @param   numCompacted  The number of cells in the input compacted set
685
 * @param   outSet      Output array for decompressed cells (preallocated)
686
 * @param   numOut      The size of the output array to bound check against
687
 * @param   res         The H3 resolution to decompress to
688
 * @return              An error code if output array is too small or any cell
689
 *                      is smaller than the output resolution.
690
 */
691
H3Error H3_EXPORT(uncompactCells)(const H3Index *compactedSet,
692
                                  const int64_t numCompacted, H3Index *outSet,
693
0
                                  const int64_t numOut, const int res) {
694
0
    int64_t i = 0;
695
696
0
    for (int64_t j = 0; j < numCompacted; j++) {
697
0
        if (!_hasChildAtRes(compactedSet[j], res)) return E_RES_MISMATCH;
698
699
0
        for (IterCellsChildren iter = iterInitParent(compactedSet[j], res);
700
0
             iter.h; i++, iterStepChild(&iter)) {
701
0
            if (i >= numOut) return E_MEMORY_BOUNDS;  // went too far; abort!
702
0
            outSet[i] = iter.h;
703
0
        }
704
0
    }
705
0
    return E_SUCCESS;
706
0
}
707
708
/**
709
 * uncompactCellsSize takes a compacted set of hexagons and provides
710
 * the exact size of the uncompacted set of hexagons.
711
 *
712
 * @param   compactedSet  Set of hexagons
713
 * @param   numCompacted  The number of hexes in the input set
714
 * @param   res           The hexagon resolution to decompress to
715
 * @param   out           The number of hexagons to allocate memory for
716
 * @returns E_SUCCESS on success, or another value on error
717
 */
718
H3Error H3_EXPORT(uncompactCellsSize)(const H3Index *compactedSet,
719
                                      const int64_t numCompacted, const int res,
720
0
                                      int64_t *out) {
721
0
    int64_t numOut = 0;
722
0
    for (int64_t i = 0; i < numCompacted; i++) {
723
0
        if (compactedSet[i] == H3_NULL) continue;
724
725
0
        int64_t childrenSize;
726
0
        H3Error childrenError =
727
0
            H3_EXPORT(cellToChildrenSize)(compactedSet[i], res, &childrenSize);
728
0
        if (childrenError) {
729
            // The parent res does not contain `res`.
730
0
            return E_RES_MISMATCH;
731
0
        }
732
0
        numOut += childrenSize;
733
0
    }
734
0
    *out = numOut;
735
0
    return E_SUCCESS;
736
0
}
737
738
/**
739
 * isResClassIII takes a hexagon ID and determines if it is in a
740
 * Class III resolution (rotated versus the icosahedron and subject
741
 * to shape distortion adding extra points on icosahedron edges, making
742
 * them not true hexagons).
743
 * @param h The H3Index to check.
744
 * @return Returns 1 if the hexagon is class III, otherwise 0.
745
 */
746
0
int H3_EXPORT(isResClassIII)(H3Index h) { return H3_GET_RESOLUTION(h) % 2; }
747
748
/**
749
 * isPentagon takes an H3Index and determines if it is actually a pentagon.
750
 * @param h The H3Index to check.
751
 * @return Returns 1 if it is a pentagon, otherwise 0.
752
 */
753
0
int H3_EXPORT(isPentagon)(H3Index h) {
754
0
    return _isBaseCellPentagon(H3_GET_BASE_CELL(h)) &&
755
0
           !_h3LeadingNonZeroDigit(h);
756
0
}
757
758
/**
759
 * Returns the highest resolution non-zero digit in an H3Index.
760
 * @param h The H3Index.
761
 * @return The highest resolution non-zero digit in the H3Index.
762
 */
763
0
Direction _h3LeadingNonZeroDigit(H3Index h) {
764
0
    for (int r = 1; r <= H3_GET_RESOLUTION(h); r++)
765
0
        if (H3_GET_INDEX_DIGIT(h, r)) return H3_GET_INDEX_DIGIT(h, r);
766
767
    // if we're here it's all 0's
768
0
    return CENTER_DIGIT;
769
0
}
770
771
/**
772
 * Rotate an H3Index 60 degrees counter-clockwise about a pentagonal center.
773
 * @param h The H3Index.
774
 */
775
0
H3Index _h3RotatePent60ccw(H3Index h) {
776
    // rotate in place; skips any leading 1 digits (k-axis)
777
778
0
    int foundFirstNonZeroDigit = 0;
779
0
    for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) {
780
        // rotate this digit
781
0
        H3_SET_INDEX_DIGIT(h, r, _rotate60ccw(H3_GET_INDEX_DIGIT(h, r)));
782
783
        // look for the first non-zero digit so we
784
        // can adjust for deleted k-axes sequence
785
        // if necessary
786
0
        if (!foundFirstNonZeroDigit && H3_GET_INDEX_DIGIT(h, r) != 0) {
787
0
            foundFirstNonZeroDigit = 1;
788
789
            // adjust for deleted k-axes sequence
790
0
            if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT)
791
0
                h = _h3Rotate60ccw(h);
792
0
        }
793
0
    }
794
0
    return h;
795
0
}
796
797
/**
798
 * Rotate an H3Index 60 degrees clockwise about a pentagonal center.
799
 * @param h The H3Index.
800
 */
801
0
H3Index _h3RotatePent60cw(H3Index h) {
802
    // rotate in place; skips any leading 1 digits (k-axis)
803
804
0
    int foundFirstNonZeroDigit = 0;
805
0
    for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) {
806
        // rotate this digit
807
0
        H3_SET_INDEX_DIGIT(h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r)));
808
809
        // look for the first non-zero digit so we
810
        // can adjust for deleted k-axes sequence
811
        // if necessary
812
0
        if (!foundFirstNonZeroDigit && H3_GET_INDEX_DIGIT(h, r) != 0) {
813
0
            foundFirstNonZeroDigit = 1;
814
815
            // adjust for deleted k-axes sequence
816
0
            if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) h = _h3Rotate60cw(h);
817
0
        }
818
0
    }
819
0
    return h;
820
0
}
821
822
/**
823
 * Rotate an H3Index 60 degrees counter-clockwise.
824
 * @param h The H3Index.
825
 */
826
0
H3Index _h3Rotate60ccw(H3Index h) {
827
0
    for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) {
828
0
        Direction oldDigit = H3_GET_INDEX_DIGIT(h, r);
829
0
        H3_SET_INDEX_DIGIT(h, r, _rotate60ccw(oldDigit));
830
0
    }
831
832
0
    return h;
833
0
}
834
835
/**
836
 * Rotate an H3Index 60 degrees clockwise.
837
 * @param h The H3Index.
838
 */
839
0
H3Index _h3Rotate60cw(H3Index h) {
840
0
    for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) {
841
0
        H3_SET_INDEX_DIGIT(h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r)));
842
0
    }
843
844
0
    return h;
845
0
}
846
847
/**
848
 * Convert an FaceIJK address to the corresponding H3Index.
849
 * @param fijk The FaceIJK address.
850
 * @param res The cell resolution.
851
 * @return The encoded H3Index (or H3_NULL on failure).
852
 */
853
0
H3Index _faceIjkToH3(const FaceIJK *fijk, int res) {
854
    // initialize the index
855
0
    H3Index h = H3_INIT;
856
0
    H3_SET_MODE(h, H3_CELL_MODE);
857
0
    H3_SET_RESOLUTION(h, res);
858
859
    // check for res 0/base cell
860
0
    if (res == 0) {
861
0
        if (fijk->coord.i > MAX_FACE_COORD || fijk->coord.j > MAX_FACE_COORD ||
862
0
            fijk->coord.k > MAX_FACE_COORD) {
863
            // out of range input
864
0
            return H3_NULL;
865
0
        }
866
867
0
        H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk));
868
0
        return h;
869
0
    }
870
871
    // we need to find the correct base cell FaceIJK for this H3 index;
872
    // start with the passed in face and resolution res ijk coordinates
873
    // in that face's coordinate system
874
0
    FaceIJK fijkBC = *fijk;
875
876
    // build the H3Index from finest res up
877
    // adjust r for the fact that the res 0 base cell offsets the indexing
878
    // digits
879
0
    CoordIJK *ijk = &fijkBC.coord;
880
0
    for (int r = res - 1; r >= 0; r--) {
881
0
        CoordIJK lastIJK = *ijk;
882
0
        CoordIJK lastCenter;
883
0
        if (isResolutionClassIII(r + 1)) {
884
            // rotate ccw
885
0
            _upAp7(ijk);
886
0
            lastCenter = *ijk;
887
0
            _downAp7(&lastCenter);
888
0
        } else {
889
            // rotate cw
890
0
            _upAp7r(ijk);
891
0
            lastCenter = *ijk;
892
0
            _downAp7r(&lastCenter);
893
0
        }
894
895
0
        CoordIJK diff;
896
0
        _ijkSub(&lastIJK, &lastCenter, &diff);
897
0
        _ijkNormalize(&diff);
898
899
0
        H3_SET_INDEX_DIGIT(h, r + 1, _unitIjkToDigit(&diff));
900
0
    }
901
902
    // fijkBC should now hold the IJK of the base cell in the
903
    // coordinate system of the current face
904
905
0
    if (fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD ||
906
0
        fijkBC.coord.k > MAX_FACE_COORD) {
907
        // out of range input
908
0
        return H3_NULL;
909
0
    }
910
911
    // lookup the correct base cell
912
0
    int baseCell = _faceIjkToBaseCell(&fijkBC);
913
0
    H3_SET_BASE_CELL(h, baseCell);
914
915
    // rotate if necessary to get canonical base cell orientation
916
    // for this base cell
917
0
    int numRots = _faceIjkToBaseCellCCWrot60(&fijkBC);
918
0
    if (_isBaseCellPentagon(baseCell)) {
919
        // force rotation out of missing k-axes sub-sequence
920
0
        if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) {
921
            // check for a cw/ccw offset face; default is ccw
922
0
            if (_baseCellIsCwOffset(baseCell, fijkBC.face)) {
923
0
                h = _h3Rotate60cw(h);
924
0
            } else {
925
0
                h = _h3Rotate60ccw(h);
926
0
            }
927
0
        }
928
929
0
        for (int i = 0; i < numRots; i++) h = _h3RotatePent60ccw(h);
930
0
    } else {
931
0
        for (int i = 0; i < numRots; i++) {
932
0
            h = _h3Rotate60ccw(h);
933
0
        }
934
0
    }
935
936
0
    return h;
937
0
}
938
939
/**
940
 * Encodes a coordinate on the sphere to the H3 index of the containing cell at
941
 * the specified resolution.
942
 *
943
 * Returns 0 on invalid input.
944
 *
945
 * @param g The spherical coordinates to encode.
946
 * @param res The desired H3 resolution for the encoding.
947
 * @param out The encoded H3Index.
948
 * @returns E_SUCCESS (0) on success, another value otherwise
949
 */
950
0
H3Error H3_EXPORT(latLngToCell)(const LatLng *g, int res, H3Index *out) {
951
0
    if (res < 0 || res > MAX_H3_RES) {
952
0
        return E_RES_DOMAIN;
953
0
    }
954
0
    if (!isfinite(g->lat) || !isfinite(g->lng)) {
955
0
        return E_LATLNG_DOMAIN;
956
0
    }
957
958
0
    FaceIJK fijk;
959
0
    _geoToFaceIjk(g, res, &fijk);
960
0
    *out = _faceIjkToH3(&fijk, res);
961
0
    if (ALWAYS(*out)) {
962
0
        return E_SUCCESS;
963
0
    } else {
964
0
        return E_FAILED;
965
0
    }
966
0
}
967
968
/**
969
 * Convert an H3Index to the FaceIJK address on a specified icosahedral face.
970
 * @param h The H3Index.
971
 * @param fijk The FaceIJK address, initialized with the desired face
972
 *        and normalized base cell coordinates.
973
 * @return Returns 1 if the possibility of overage exists, otherwise 0.
974
 */
975
0
int _h3ToFaceIjkWithInitializedFijk(H3Index h, FaceIJK *fijk) {
976
0
    CoordIJK *ijk = &fijk->coord;
977
0
    int res = H3_GET_RESOLUTION(h);
978
979
    // center base cell hierarchy is entirely on this face
980
0
    int possibleOverage = 1;
981
0
    if (!_isBaseCellPentagon(H3_GET_BASE_CELL(h)) &&
982
0
        (res == 0 ||
983
0
         (fijk->coord.i == 0 && fijk->coord.j == 0 && fijk->coord.k == 0)))
984
0
        possibleOverage = 0;
985
986
0
    for (int r = 1; r <= res; r++) {
987
0
        if (isResolutionClassIII(r)) {
988
            // Class III == rotate ccw
989
0
            _downAp7(ijk);
990
0
        } else {
991
            // Class II == rotate cw
992
0
            _downAp7r(ijk);
993
0
        }
994
995
0
        _neighbor(ijk, H3_GET_INDEX_DIGIT(h, r));
996
0
    }
997
998
0
    return possibleOverage;
999
0
}
1000
1001
/**
1002
 * Convert an H3Index to a FaceIJK address.
1003
 * @param h The H3Index.
1004
 * @param fijk The corresponding FaceIJK address.
1005
 */
1006
0
H3Error _h3ToFaceIjk(H3Index h, FaceIJK *fijk) {
1007
0
    int baseCell = H3_GET_BASE_CELL(h);
1008
0
    if (NEVER(baseCell < 0) || baseCell >= NUM_BASE_CELLS) {
1009
        // Base cells less than zero can not be represented in an index
1010
        // To prevent reading uninitialized memory, we zero the output.
1011
0
        fijk->face = 0;
1012
0
        fijk->coord.i = fijk->coord.j = fijk->coord.k = 0;
1013
0
        return E_CELL_INVALID;
1014
0
    }
1015
    // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs
1016
    // to be adjusted (and some of sub-sequence 4 below)
1017
0
    if (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 5)
1018
0
        h = _h3Rotate60cw(h);
1019
1020
    // start with the "home" face and ijk+ coordinates for the base cell of c
1021
0
    *fijk = baseCellData[baseCell].homeFijk;
1022
0
    if (!_h3ToFaceIjkWithInitializedFijk(h, fijk))
1023
0
        return E_SUCCESS;  // no overage is possible; h lies on this face
1024
1025
    // if we're here we have the potential for an "overage"; i.e., it is
1026
    // possible that c lies on an adjacent face
1027
1028
0
    CoordIJK origIJK = fijk->coord;
1029
1030
    // if we're in Class III, drop into the next finer Class II grid
1031
0
    int res = H3_GET_RESOLUTION(h);
1032
0
    if (isResolutionClassIII(res)) {
1033
        // Class III
1034
0
        _downAp7r(&fijk->coord);
1035
0
        res++;
1036
0
    }
1037
1038
    // adjust for overage if needed
1039
    // a pentagon base cell with a leading 4 digit requires special handling
1040
0
    int pentLeading4 =
1041
0
        (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 4);
1042
0
    if (_adjustOverageClassII(fijk, res, pentLeading4, 0) != NO_OVERAGE) {
1043
        // if the base cell is a pentagon we have the potential for secondary
1044
        // overages
1045
0
        if (_isBaseCellPentagon(baseCell)) {
1046
0
            while (_adjustOverageClassII(fijk, res, 0, 0) != NO_OVERAGE)
1047
0
                continue;
1048
0
        }
1049
1050
0
        if (res != H3_GET_RESOLUTION(h)) _upAp7r(&fijk->coord);
1051
0
    } else if (res != H3_GET_RESOLUTION(h)) {
1052
0
        fijk->coord = origIJK;
1053
0
    }
1054
0
    return E_SUCCESS;
1055
0
}
1056
1057
/**
1058
 * Determines the spherical coordinates of the center point of an H3 index.
1059
 *
1060
 * @param h3 The H3 index.
1061
 * @param g The spherical coordinates of the H3 cell center.
1062
 */
1063
0
H3Error H3_EXPORT(cellToLatLng)(H3Index h3, LatLng *g) {
1064
0
    FaceIJK fijk;
1065
0
    H3Error e = _h3ToFaceIjk(h3, &fijk);
1066
0
    if (e) {
1067
0
        return e;
1068
0
    }
1069
0
    _faceIjkToGeo(&fijk, H3_GET_RESOLUTION(h3), g);
1070
0
    return E_SUCCESS;
1071
0
}
1072
1073
/**
1074
 * Determines the cell boundary in spherical coordinates for an H3 index.
1075
 *
1076
 * @param h3 The H3 index.
1077
 * @param cb The boundary of the H3 cell in spherical coordinates.
1078
 */
1079
0
H3Error H3_EXPORT(cellToBoundary)(H3Index h3, CellBoundary *cb) {
1080
0
    FaceIJK fijk;
1081
0
    H3Error e = _h3ToFaceIjk(h3, &fijk);
1082
0
    if (e) {
1083
0
        return e;
1084
0
    }
1085
0
    if (H3_EXPORT(isPentagon)(h3)) {
1086
0
        _faceIjkPentToCellBoundary(&fijk, H3_GET_RESOLUTION(h3), 0,
1087
0
                                   NUM_PENT_VERTS, cb);
1088
0
    } else {
1089
0
        _faceIjkToCellBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, NUM_HEX_VERTS,
1090
0
                               cb);
1091
0
    }
1092
0
    return E_SUCCESS;
1093
0
}
1094
1095
/**
1096
 * Returns the max number of possible icosahedron faces an H3 index
1097
 * may intersect.
1098
 *
1099
 * @return int count of faces
1100
 */
1101
0
H3Error H3_EXPORT(maxFaceCount)(H3Index h3, int *out) {
1102
    // a pentagon always intersects 5 faces, a hexagon never intersects more
1103
    // than 2 (but may only intersect 1)
1104
0
    *out = H3_EXPORT(isPentagon)(h3) ? 5 : 2;
1105
0
    return E_SUCCESS;
1106
0
}
1107
1108
/**
1109
 * Find all icosahedron faces intersected by a given H3 index, represented
1110
 * as integers from 0-19. The array is sparse; since 0 is a valid value,
1111
 * invalid array values are represented as -1. It is the responsibility of
1112
 * the caller to filter out invalid values.
1113
 *
1114
 * @param h3 The H3 index
1115
 * @param out Output array. Must be of size maxFaceCount(h3).
1116
 */
1117
0
H3Error H3_EXPORT(getIcosahedronFaces)(H3Index h3, int *out) {
1118
0
    int res = H3_GET_RESOLUTION(h3);
1119
0
    int isPent = H3_EXPORT(isPentagon)(h3);
1120
1121
    // We can't use the vertex-based approach here for class II pentagons,
1122
    // because all their vertices are on the icosahedron edges. Their
1123
    // direct child pentagons cross the same faces, so use those instead.
1124
0
    if (isPent && !isResolutionClassIII(res)) {
1125
        // Note that this would not work for res 15, but this is only run on
1126
        // Class II pentagons, it should never be invoked for a res 15 index.
1127
0
        H3Index childPentagon = makeDirectChild(h3, 0);
1128
0
        return H3_EXPORT(getIcosahedronFaces)(childPentagon, out);
1129
0
    }
1130
1131
    // convert to FaceIJK
1132
0
    FaceIJK fijk;
1133
0
    H3Error err = _h3ToFaceIjk(h3, &fijk);
1134
0
    if (err) {
1135
0
        return err;
1136
0
    }
1137
1138
    // Get all vertices as FaceIJK addresses. For simplicity, always
1139
    // initialize the array with 6 verts, ignoring the last one for pentagons
1140
0
    FaceIJK fijkVerts[NUM_HEX_VERTS];
1141
0
    int vertexCount;
1142
1143
0
    if (isPent) {
1144
0
        vertexCount = NUM_PENT_VERTS;
1145
0
        _faceIjkPentToVerts(&fijk, &res, fijkVerts);
1146
0
    } else {
1147
0
        vertexCount = NUM_HEX_VERTS;
1148
0
        _faceIjkToVerts(&fijk, &res, fijkVerts);
1149
0
    }
1150
1151
    // We may not use all of the slots in the output array,
1152
    // so fill with invalid values to indicate unused slots
1153
0
    int faceCount;
1154
0
    H3Error maxFaceCountError = H3_EXPORT(maxFaceCount)(h3, &faceCount);
1155
0
    if (NEVER(maxFaceCountError != E_SUCCESS)) {
1156
0
        return maxFaceCountError;
1157
0
    }
1158
0
    for (int i = 0; i < faceCount; i++) {
1159
0
        out[i] = INVALID_FACE;
1160
0
    }
1161
1162
    // add each vertex face, using the output array as a hash set
1163
0
    for (int i = 0; i < vertexCount; i++) {
1164
0
        FaceIJK *vert = &fijkVerts[i];
1165
1166
        // Adjust overage, determining whether this vertex is
1167
        // on another face
1168
0
        if (isPent) {
1169
0
            _adjustPentVertOverage(vert, res);
1170
0
        } else {
1171
0
            _adjustOverageClassII(vert, res, 0, 1);
1172
0
        }
1173
1174
        // Save the face to the output array
1175
0
        int face = vert->face;
1176
0
        int pos = 0;
1177
        // Find the first empty output position, or the first position
1178
        // matching the current face
1179
0
        while (out[pos] != INVALID_FACE && out[pos] != face) {
1180
0
            pos++;
1181
0
            if (pos >= faceCount) {
1182
                // Mismatch between the heuristic used in maxFaceCount and
1183
                // calculation here - indicates an invalid index.
1184
0
                return E_FAILED;
1185
0
            }
1186
0
        }
1187
0
        out[pos] = face;
1188
0
    }
1189
0
    return E_SUCCESS;
1190
0
}
1191
1192
/**
1193
 * pentagonCount returns the number of pentagons (same at any resolution)
1194
 *
1195
 * @return int count of pentagon indexes
1196
 */
1197
0
int H3_EXPORT(pentagonCount)(void) { return NUM_PENTAGONS; }
1198
1199
/**
1200
 * Generates all pentagons at the specified resolution
1201
 *
1202
 * @param res The resolution to produce pentagons at.
1203
 * @param out Output array. Must be of size pentagonCount().
1204
 */
1205
0
H3Error H3_EXPORT(getPentagons)(int res, H3Index *out) {
1206
0
    if (res < 0 || res > MAX_H3_RES) {
1207
0
        return E_RES_DOMAIN;
1208
0
    }
1209
0
    int i = 0;
1210
0
    for (int bc = 0; bc < NUM_BASE_CELLS; bc++) {
1211
0
        if (_isBaseCellPentagon(bc)) {
1212
0
            H3Index pentagon;
1213
0
            setH3Index(&pentagon, res, bc, 0);
1214
0
            out[i++] = pentagon;
1215
0
        }
1216
0
    }
1217
0
    return E_SUCCESS;
1218
0
}
1219
1220
/**
1221
 * Returns whether or not a resolution is a Class III grid. Note that odd
1222
 * resolutions are Class III and even resolutions are Class II.
1223
 * @param res The H3 resolution.
1224
 * @return 1 if the resolution is a Class III grid, and 0 if the resolution is
1225
 *         a Class II grid.
1226
 */
1227
0
int isResolutionClassIII(int res) { return res % 2; }
1228
1229
/**
1230
 * Validate a child position in the context of a given parent, returning
1231
 * an error if validation fails.
1232
 */
1233
static H3Error validateChildPos(int64_t childPos, H3Index parent,
1234
0
                                int childRes) {
1235
0
    int64_t maxChildCount;
1236
0
    H3Error sizeError =
1237
0
        H3_EXPORT(cellToChildrenSize)(parent, childRes, &maxChildCount);
1238
0
    if (NEVER(sizeError)) {
1239
0
        return sizeError;
1240
0
    }
1241
0
    if (childPos < 0 || childPos >= maxChildCount) {
1242
0
        return E_DOMAIN;
1243
0
    }
1244
0
    return E_SUCCESS;
1245
0
}
1246
1247
/**
1248
 * Returns the position of the cell within an ordered list of all children of
1249
 * the cell's parent at the specified resolution
1250
 * @param child Child cell index
1251
 * @param parentRes Resolution of the parent cell to find the position within
1252
 * @param out Output: The position of the child cell within its parents cell
1253
 * list of children
1254
 */
1255
0
H3Error H3_EXPORT(cellToChildPos)(H3Index child, int parentRes, int64_t *out) {
1256
0
    int childRes = H3_GET_RESOLUTION(child);
1257
    // Get the parent at res. This will catch any resolution errors
1258
0
    H3Index originalParent;
1259
0
    H3Error parentError =
1260
0
        H3_EXPORT(cellToParent(child, parentRes, &originalParent));
1261
0
    if (parentError) {
1262
0
        return parentError;
1263
0
    }
1264
1265
    // Define the initial parent. Note that these variables are reassigned
1266
    // within the loop.
1267
0
    H3Index parent = originalParent;
1268
0
    int parentIsPentagon = H3_EXPORT(isPentagon)(parent);
1269
1270
    // Walk up the resolution digits, incrementing the index
1271
0
    *out = 0;
1272
0
    if (parentIsPentagon) {
1273
        // Pentagon logic. Pentagon parents skip the 1 digit, so the offsets are
1274
        // different from hexagons
1275
0
        for (int res = childRes; res > parentRes; res--) {
1276
0
            H3Error parentError =
1277
0
                H3_EXPORT(cellToParent(child, res - 1, &parent));
1278
0
            if (NEVER(parentError)) {
1279
0
                return parentError;
1280
0
            }
1281
1282
0
            parentIsPentagon = H3_EXPORT(isPentagon)(parent);
1283
0
            int rawDigit = H3_GET_INDEX_DIGIT(child, res);
1284
            // Validate the digit before proceeding
1285
0
            if (rawDigit == INVALID_DIGIT ||
1286
0
                (parentIsPentagon && rawDigit == K_AXES_DIGIT)) {
1287
0
                return E_CELL_INVALID;
1288
0
            }
1289
0
            int digit =
1290
0
                parentIsPentagon && rawDigit > 0 ? rawDigit - 1 : rawDigit;
1291
0
            if (digit != CENTER_DIGIT) {
1292
0
                int64_t hexChildCount = _ipow(7, childRes - res);
1293
                // The offset for the 0-digit slot depends on whether the
1294
                // current index is the child of a pentagon. If so, the offset
1295
                // is based on the count of pentagon children, otherwise,
1296
                // hexagon children.
1297
0
                *out += (parentIsPentagon
1298
0
                             ?  // pentagon children. See the explanation
1299
                                // for getNumCells in h3api.h.in
1300
0
                             1 + (5 * (hexChildCount - 1)) / 6
1301
0
                             :  // one hexagon's children
1302
0
                             hexChildCount) +
1303
                        // the other hexagon children
1304
0
                        (digit - 1) * hexChildCount;
1305
0
            }
1306
0
        }
1307
0
    } else {
1308
        // Hexagon logic. Offsets are simple powers of 7
1309
0
        for (int res = childRes; res > parentRes; res--) {
1310
0
            int digit = H3_GET_INDEX_DIGIT(child, res);
1311
0
            if (digit == INVALID_DIGIT) {
1312
0
                return E_CELL_INVALID;
1313
0
            }
1314
0
            *out += digit * _ipow(7, childRes - res);
1315
0
        }
1316
0
    }
1317
1318
0
    if (NEVER(validateChildPos(*out, originalParent, childRes))) {
1319
        // This is the result of an internal error, so return E_FAILED
1320
        // instead of the validation error
1321
0
        return E_FAILED;
1322
0
    }
1323
1324
0
    return E_SUCCESS;
1325
0
}
1326
1327
/**
1328
 * Returns the child cell at a given position within an ordered list of all
1329
 * children at the specified resolution
1330
 * @param childPos Position within the ordered list
1331
 * @param parent Parent cell of the cell index to find
1332
 * @param childRes Resolution of the child cell index
1333
 * @param child Output: child cell index
1334
 */
1335
H3Error H3_EXPORT(childPosToCell)(int64_t childPos, H3Index parent,
1336
0
                                  int childRes, H3Index *child) {
1337
    // Validate resolution
1338
0
    if (childRes < 0 || childRes > MAX_H3_RES) {
1339
0
        return E_RES_DOMAIN;
1340
0
    }
1341
    // Validate parent resolution
1342
0
    int parentRes = H3_GET_RESOLUTION(parent);
1343
0
    if (childRes < parentRes) {
1344
0
        return E_RES_MISMATCH;
1345
0
    }
1346
    // Validate child pos
1347
0
    H3Error childPosErr = validateChildPos(childPos, parent, childRes);
1348
0
    if (childPosErr) {
1349
0
        return childPosErr;
1350
0
    }
1351
1352
0
    int resOffset = childRes - parentRes;
1353
1354
0
    *child = parent;
1355
0
    int64_t idx = childPos;
1356
1357
0
    H3_SET_RESOLUTION(*child, childRes);
1358
1359
0
    if (H3_EXPORT(isPentagon)(parent)) {
1360
        // Pentagon tile logic. Pentagon tiles skip the 1 digit, so the offsets
1361
        // are different
1362
0
        bool inPent = true;
1363
0
        for (int res = 1; res <= resOffset; res++) {
1364
0
            int64_t resWidth = _ipow(7, resOffset - res);
1365
0
            if (inPent) {
1366
                // While we are inside a parent pentagon, we need to check if
1367
                // this cell is a pentagon, and if not, we need to offset its
1368
                // digit to account for the skipped direction
1369
0
                int64_t pentWidth = 1 + (5 * (resWidth - 1)) / 6;
1370
0
                if (idx < pentWidth) {
1371
0
                    H3_SET_INDEX_DIGIT(*child, parentRes + res, 0);
1372
0
                } else {
1373
0
                    idx -= pentWidth;
1374
0
                    inPent = false;
1375
0
                    H3_SET_INDEX_DIGIT(*child, parentRes + res,
1376
0
                                       (idx / resWidth) + 2);
1377
0
                    idx %= resWidth;
1378
0
                }
1379
0
            } else {
1380
                // We're no longer inside a pentagon, continue as for hex
1381
0
                H3_SET_INDEX_DIGIT(*child, parentRes + res, idx / resWidth);
1382
0
                idx %= resWidth;
1383
0
            }
1384
0
        }
1385
0
    } else {
1386
        // Hexagon tile logic. Offsets are simple powers of 7
1387
0
        for (int res = 1; res <= resOffset; res++) {
1388
0
            int64_t resWidth = _ipow(7, resOffset - res);
1389
0
            H3_SET_INDEX_DIGIT(*child, parentRes + res, idx / resWidth);
1390
0
            idx %= resWidth;
1391
0
        }
1392
0
    }
1393
1394
0
    return E_SUCCESS;
1395
0
}