Design Characteristics
O(log n)complexity for both updates and prefix sum queries.- Storage-efficient using a
mapping(uint256 => int256), instead of an array. - Only supports lengths that are exact powers of two (
2^k), which simplifies internal logic and allows future extensions viaextend().
Invariants and Constraints
- The tree must be initialized with a power-of-two length > 0 via
initialize(...). - Index bounds are enforced — access beyond the current capacity reverts with
IndexOutOfBounds(). - To support dynamic resizing,
extend()can double the current tree length (up to a safe limit). - Use of negative values is supported in
modify(...), allowing decrement operations.
Data Structures
Functions
initialize(Tree storage tree, uint256 length_)
Initializes the tree with the specified length.
- Reverts with
InvalidLength()iflength_ == 0or not a power of two. - Only callable once (re-initialization is not allowed).
length(Tree storage tree) → uint256
Returns the current capacity of the tree.
extend(Tree storage tree)
Doubles the tree’s capacity.
- Preserves prefix sum structure.
- Reverts with
InvalidLength()on overflow.
modify(Tree storage tree, uint256 index, int256 value)
Increments or decrements the value at a given index by value.
- Performs
tree[index] += value. - Reverts if index is out of bounds.
- No-op if
value == 0.
get(Tree storage tree, uint256 index) → int256
Returns the prefix sum for the range [0, index].
- If
index >= length, it is clamped tolength - 1.
get(Tree storage tree, uint256 from, uint256 to) → int256
Returns the sum over the range [from, to] (inclusive).
- Returns
0iffrom > to.
Internals
_modify(...)
Low-level implementation of Fenwick update using bitwise operations:
- Updates
tree[index]and propagates changes upward viaindex |= index + 1.
_get(...)
Assembly-optimized prefix sum computation:
- Aggregates values by descending via
index := and(index, index + 1) - 1.