2019-06-20 15:12:38 +00:00
|
|
|
"""An accessor for navigating flat trees."""
|
|
|
|
|
2020-05-16 16:20:35 +00:00
|
|
|
__all__ = ["FlatTreeAccessor"]
|
2019-06-20 15:12:38 +00:00
|
|
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
|
|
|
|
|
|
|
class FlatTreeAccessor:
|
|
|
|
"""A flat tree accessor."""
|
|
|
|
|
|
|
|
def index(self, depth: int, offset: int) -> int:
|
|
|
|
"""The tree index specified by the depth and offset.
|
|
|
|
|
|
|
|
:param depth: The depth of the tree
|
|
|
|
:param offset: The offset from left hand side of the tree
|
|
|
|
"""
|
|
|
|
return ((1 + (2 * offset)) * (2 ** depth)) - 1
|
|
|
|
|
|
|
|
def offset(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The offset of given index from the left hand side of the tree.
|
|
|
|
|
|
|
|
:param index: The tree index
|
|
|
|
:param depth: The depth of the tree
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return int(index / 2)
|
|
|
|
|
|
|
|
if depth is None:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return int((((index + 1) / (2 ** depth)) - 1) / 2)
|
|
|
|
|
|
|
|
def depth(self, index: int) -> int:
|
|
|
|
"""The depth of the given index in the tree.
|
|
|
|
|
|
|
|
:param index: The tree index
|
|
|
|
"""
|
|
|
|
depth = 0
|
|
|
|
|
|
|
|
index += 1
|
|
|
|
while not (index & 1):
|
|
|
|
depth += 1
|
|
|
|
index = index >> 1
|
|
|
|
|
|
|
|
return depth
|
|
|
|
|
|
|
|
def parent(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The index of the parent relative to the given index.
|
|
|
|
|
|
|
|
:param index: The index relative to the parent
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if depth is None:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
offset = self.offset(index, depth)
|
|
|
|
|
|
|
|
return self.index(depth + 1, int((offset - (offset & 1)) / 2))
|
|
|
|
|
|
|
|
def sibling(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The index of the sibling relative to the given index.
|
|
|
|
|
|
|
|
:param index: The index relative to the sibling
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if depth is None:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
offset = self.offset(index, depth)
|
|
|
|
offset = offset - 1 if (offset & 1) else offset + 1
|
|
|
|
|
|
|
|
return self.index(depth, offset)
|
|
|
|
|
|
|
|
def children(self, index: int, depth: Optional[int] = None) -> List[int]:
|
|
|
|
"""All children relative to the given index.
|
|
|
|
|
|
|
|
:param index: The parent index
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return []
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
offset = self.offset(index, depth) * 2
|
|
|
|
|
|
|
|
return [
|
|
|
|
self.index((depth - 1), offset),
|
|
|
|
self.index((depth - 1), (offset + 1)),
|
|
|
|
]
|
|
|
|
|
|
|
|
def spans(self, index: int, depth: Optional[int] = None) -> List[int]:
|
|
|
|
"""The span of the tree.
|
|
|
|
|
|
|
|
:param index: The index of the root
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return [index, index]
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
offset = self.offset(index, depth)
|
|
|
|
width = 2 ** (depth + 1)
|
|
|
|
|
|
|
|
return [(offset * width), ((offset + 1) * width) - 2]
|
|
|
|
|
|
|
|
def left_span(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The leftmost span of the tree.
|
|
|
|
|
|
|
|
:param index: The index of the tree root
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return index
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return self.offset(index, depth) * (2 ** (depth + 1))
|
|
|
|
|
|
|
|
def right_span(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The rightmost span of the tree.
|
|
|
|
|
|
|
|
:param index: The index of the tree root
|
|
|
|
:param depth: The depth of the index
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return index
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return (self.offset(index, depth) + 1) * (2 ** (depth + 1)) - 2
|
|
|
|
|
|
|
|
def count(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The number of nodes a tree contains.
|
|
|
|
|
|
|
|
:param index: The index of the root of the tree
|
|
|
|
:param depth: The depth of the root of the tree
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return 1
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return (2 ** (depth + 1)) - 1
|
|
|
|
|
|
|
|
def full_roots(self, index: int) -> List[int]:
|
|
|
|
"""All full roots within the tree.
|
|
|
|
|
|
|
|
:param index: The index of the root of the tree
|
|
|
|
"""
|
|
|
|
if index & 1:
|
2020-05-16 16:20:35 +00:00
|
|
|
message = "Roots only available for tree depth 0"
|
2019-06-20 15:12:38 +00:00
|
|
|
raise ValueError(message)
|
|
|
|
|
|
|
|
roots: List[int] = []
|
|
|
|
|
|
|
|
index = int(index / 2)
|
|
|
|
offset, factor = 0, 1
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if not index:
|
|
|
|
return roots
|
|
|
|
|
|
|
|
while (factor * 2) <= index:
|
|
|
|
factor = factor * 2
|
|
|
|
|
|
|
|
roots.append((offset + factor) - 1)
|
|
|
|
|
|
|
|
offset = (offset + 2) * factor
|
|
|
|
index = index - factor
|
|
|
|
factor = 1
|
|
|
|
|
|
|
|
def left_child(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The left child of the given index.
|
|
|
|
|
|
|
|
:param index: The index of the tree
|
|
|
|
:param depth: The depth of the tree
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return -1
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return self.index((depth - 1), (self.offset(index, depth) * 2))
|
|
|
|
|
|
|
|
def right_child(self, index: int, depth: Optional[int] = None) -> int:
|
|
|
|
"""The right child of the given index.
|
|
|
|
|
|
|
|
:param index: The index of the tree
|
|
|
|
:param depth: The depth of the tree
|
|
|
|
"""
|
|
|
|
if not (index & 1):
|
|
|
|
return -1
|
|
|
|
|
|
|
|
if not depth:
|
|
|
|
depth = self.depth(index)
|
|
|
|
|
|
|
|
return self.index((depth - 1), (1 + (self.offset(index, depth) * 2)))
|