Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add cellToChildPos and childPosToCell #719

Merged
merged 11 commits into from
Nov 28, 2022

Conversation

nrabinowitz
Copy link
Collaborator

@nrabinowitz nrabinowitz commented Oct 24, 2022

Adds two new functions:

  • cellToChildPos gives the position of the child in an ordered list of all the children of the cell's parent at a given resolution. The "ordered list" here is the same order as returned by cellToChildren, which I believe is numerically sorted by index. Essentially, this function allows you to determine the numeric index of the cell among its siblings, given a parent resolution that defines the sibling list.
  • childPosToCell offers the reverse functionality, returning a child cell given a parent, a child resolution, and a child index.

This pair of functions opens up several new use cases:

  • Easy iteration through all children of some parent, with no need to allocate memory for cellToChildren. I think we can do this internally in the library using iterators, but this offers an ergonomic API for external consumers (simply iterate for (int i = 0; i < cellToChildrenSize(parent, res); i++) and take childPosToCell(i, parent, res)).
  • Compact representations for sets of common-ancestor cells, including arrays of small bit-width integers (e.g. uint16 if the parent/child res difference is <= 5) and bit arrays (e.g. 16807 bits / 2101 bytes to indicate yes/no whether each child 5 steps down is included in the set). This is likely more effective than compact if there's no particular likelihood that the cells are contiguous.
  • Association between data arrays and H3 indexes without the need to store the H3 indexes themselves (e.g. a list of 16807 values corresponding by position to each child 5 steps down from some parent)

Notes

  • I'm open to suggestions on the name. childPosition might be clearer (but longer), childIndex makes sense but risked confusion with H3 indexes.
  • I included exhaustive tests for 0-3 resolution steps for all cells up to res 2. Let me know if you think any further tests are needed here.

@coveralls
Copy link

coveralls commented Oct 24, 2022

Coverage Status

Coverage increased (+0.04%) to 98.64% when pulling 1172251 on nrabinowitz:child-pos-functions into 139e5bf on uber:master.

// different from hexagons
for (int res = childRes; res > parentRes; res--) {
// It shouldn't be possible to get an error here, so we don't check
H3_EXPORT(cellToParent(child, res - 1, &parent));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a good spot for NEVER once #720 lands

Copy link
Collaborator

@isaacbrodsky isaacbrodsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand how these functions are used so they look helpful.

Please add website API documentation for these functions.

@@ -0,0 +1,95 @@
/*
* Copyright 2017-2021 Uber Technologies, Inc.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Copyright 2017-2021 Uber Technologies, Inc.
* Copyright 2022 Uber Technologies, Inc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Copyright 2017-2021 Uber Technologies, Inc.
* Copyright 2017-2022 Uber Technologies, Inc.

}
}

return E_SUCCESS;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the passed in childPos is abnormally high or low? E.g. if idx > 0 here or if childPos < 0?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good question. You'd get an invalid digit for output; I'm not sure there would be other issues. Probably worth validating childPos and returning an error for bad cases.

H3_EXPORT(cellToParent(child, res - 1, &parent));
parentIsPentagon = H3_EXPORT(isPentagon)(parent);
int rawDigit = H3_GET_INDEX_DIGIT(child, res);
int digit =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens for invalid indexes into the deleted K subsequence of a pentagon, or indexes with INVALID_DIGIT (7)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good questions:

  • Invalid indexes into the K subsequence will be treated as 0 per the logic on the next line
  • INVALID_DIGIT will result in an offset that's too high, and the resulting number might be greater than the number of max children

This might be okay, on the principle of GIGO, but I could imagine attacks on systems using H3 that would use poor output here to trigger random memory access. Possible failsafes:

  • We could validate the digit and return an error
  • We could clamp output to the max possible child index for the given parent

Validating the digits is probably worthwhile. If we do that we don't need to clamp output, but we could (after #720) add an assertion at the end of the function like

if (NEVER(out > cellToChildrenSize(parent, childRes))) {
  return E_FAILED;
}

* children at the specified resolution */
H3Error H3_EXPORT(childPosToCell)(int64_t childPos, H3Index parent,
int childRes, H3Index *child) {
if (childRes < 0 || childRes > MAX_H3_RES) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would also be a good place for testcase from #720 to ensure boundary conditions work as expected.

<TabItem value="c">

```c
H3Index cellToChildPos(H3Index child, int parentRes, int64_t *out);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the other bindings say "cell" - maybe they should also have the parameter named "child"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all prospective, of course, as the binding owners may have other ideas, but I can update all to child for now

int64_t maxChildCount;
H3Error sizeError =
H3_EXPORT(cellToChildrenSize)(parent, childRes, &maxChildCount);
if (NEVER(sizeError)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we should require fuzzers be added too to try to exercise these. I am fine with merging this without the fuzzer and then adding it in a follow up PR.

@nrabinowitz nrabinowitz merged commit e84cfa0 into uber:master Nov 28, 2022
@nrabinowitz nrabinowitz deleted the child-pos-functions branch November 28, 2022 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants