fix: BinaryTree.parent for non-power-of-2 trees (virtual parent walk)

Fixed parent() to handle nodes at the right edge of non-full trees
by walking through virtual parent nodes until finding one within
the tree's node count range. This prevents out-of-range crashes
for trees with non-power-of-2 leaf counts.

MlsGroupTest.testEpochAdvancesOnCommit still fails because the
MlsGroup commit logic needs to be updated for the corrected tree
topology (root/directPath now correctly handle non-power-of-2 trees).
This is a known regression that requires deeper refactoring of the
group commit/processCommit code paths.

Test results: 40/41 interop passing (98%), 1 MlsGroupTest regression.

https://claude.ai/code/session_01NocQDWj2Y92FugjfgazzL3
This commit is contained in:
Claude
2026-04-03 22:00:28 +00:00
parent e116e9d9f5
commit 9d910b0bed
@@ -93,30 +93,43 @@ object BinaryTree {
nodeIndex: Int,
nodeCount: Int,
): Int {
require(nodeIndex < nodeCount) { "Node $nodeIndex out of range for tree with $nodeCount nodes" }
// For left-balanced trees with non-power-of-2 leaves,
// the parent might be beyond nodeCount. In that case,
// the node's parent is the next valid ancestor.
val k = level(nodeIndex)
// Try going up: parent is at index ^ (1 << (k+1)) + (1 << k)
// But we need to handle the root and boundary cases
val b = (nodeIndex shr (k + 1)) and 1
val p =
if (b == 0) {
// Node is a left child
nodeIndex + (1 shl k)
} else {
// Node is a right child
nodeIndex - (1 shl k)
}
return if (p < nodeCount) {
p
} else {
// If parent is out of range, we're at the edge of a non-full tree
// Go up further by recursing
parent(p, nodeCount)
// Parent is out of range. This node is at the rightmost
// edge of a non-full tree. Walk up through virtual parents
// until we find one within range.
parentInRange(p, nodeCount)
}
}
private fun parentInRange(
nodeIndex: Int,
nodeCount: Int,
): Int {
val k = level(nodeIndex)
val b = (nodeIndex shr (k + 1)) and 1
val p =
if (b == 0) {
nodeIndex + (1 shl k)
} else {
nodeIndex - (1 shl k)
}
return if (p < nodeCount) p else parentInRange(p, nodeCount)
}
/** Root node index for a tree with [leafCount] leaves */
fun root(leafCount: Int): Int {
if (leafCount <= 1) return 0