fenwick_tree/
tree.rs

1use std::ops::{AddAssign, Bound, RangeBounds, SubAssign};
2
3use crate::errors::{AddError, SumError};
4
5/// An implementation of the binary indexed tree (Fenwick tree) data structure.
6///
7/// The tree is backed by a simple vec of the fixed size where each item is
8/// responsible for storing cumulative sum of some range, allowing to perform
9/// queries and updates in _O_(log _n_) time.
10pub struct FenwickTree<I>
11where
12    I: Default + Copy + AddAssign + SubAssign,
13{
14    tree: Vec<I>,
15}
16
17impl<I> FenwickTree<I>
18where
19    I: Default + Copy + AddAssign + SubAssign,
20{
21    /// Constructs a new Fenwick tree with the specified `len` with each element set as
22    /// `I::default()`.
23    ///
24    /// The vector is initialized with `vec![I::default(); len]`.
25    ///
26    /// # Panics
27    ///
28    /// Vector initialization may panic if `len` is too big.
29    pub fn with_len(len: usize) -> Self {
30        Self {
31            tree: vec![I::default(); len],
32        }
33    }
34
35    /// A length of the backing vector of the tree.
36    pub fn len(&self) -> usize {
37        self.tree.len()
38    }
39
40    /// A partial sum of the specified `bounds`.
41    ///
42    /// Complexity: _O_(log _n_).
43    ///
44    /// Note that `sum` for empty range (a range `(i..j)` where `i >= j`) is `0`.
45    ///
46    /// Also, `bounds` are converted into a pair `(start, end)` that represents `[start,
47    /// end)` range. This means that boundary case `tree.sum(i..=usize::MAX)` fallbacks to `tree.sum(0..usize::MAX)`.
48    /// However, in practice, it's not possible to construct such a big tree ([`Vec`] panics with
49    /// `capacity overflow`).
50    pub fn sum(&self, bounds: impl RangeBounds<usize>) -> Result<I, SumError> {
51        let len = self.len();
52
53        let mut sum = I::default();
54        let mut start = start(bounds.start_bound());
55        let mut end = end(bounds.end_bound(), len);
56
57        if start >= len || end > len {
58            return Err(SumError::OutOfRange {
59                bounds: as_pair(bounds),
60                len,
61            });
62        }
63
64        while end > start {
65            sum += self.tree[end - 1];
66            end = prev(end);
67        }
68
69        while start > end {
70            sum -= self.tree[start - 1];
71            start = prev(start);
72        }
73
74        return Ok(sum);
75
76        // As inclusive.
77        fn start(bound: Bound<&usize>) -> usize {
78            match bound {
79                Bound::Excluded(&usize::MAX) => usize::MAX,
80                Bound::Excluded(x) => *x + 1,
81                Bound::Included(x) => *x,
82                Bound::Unbounded => 0,
83            }
84        }
85
86        // As exclusive.
87        fn end(bound: Bound<&usize>, len: usize) -> usize {
88            match bound {
89                Bound::Included(&usize::MAX) => usize::MAX,
90                Bound::Included(x) => *x + 1,
91                Bound::Excluded(x) => *x,
92                Bound::Unbounded => len,
93            }
94        }
95
96        fn as_pair(bounds: impl RangeBounds<usize>) -> (Bound<usize>, Bound<usize>) {
97            let start = cloned(bounds.start_bound());
98            let end = cloned(bounds.end_bound());
99
100            return (start, end);
101
102            // From nightly Rust (https://doc.rust-lang.org/std/ops/enum.Bound.html#method.cloned).
103            fn cloned(x: Bound<&usize>) -> Bound<usize> {
104                match x {
105                    Bound::Unbounded => Bound::Unbounded,
106                    Bound::Included(x) => Bound::Included(*x),
107                    Bound::Excluded(x) => Bound::Excluded(*x),
108                }
109            }
110        }
111    }
112
113    /// Updates the value at `i` by `delta`.
114    ///
115    /// Complexity: _O_(log _n_).
116    pub fn add(&mut self, mut i: usize, delta: I) -> Result<(), AddError> {
117        let size = self.len();
118
119        if i >= size {
120            return Err(AddError::IndexOutOfRange { index: i, size });
121        }
122
123        while i < size {
124            self.tree[i] += delta;
125            i = next(i);
126        }
127
128        Ok(())
129    }
130}
131
132/// Flips first trailing `1` in the binary representation of the `i`. Same as `i - (i & (-i))` (see
133/// crate docs).
134///
135/// This allows fast calculating of prefix sums:
136///     - call `i = prev(i)` until `i` is greater than 0
137///     - access sums by `i - 1`
138///
139/// This function assumes that indexing is one-based, hence we access sums by `i - 1`.
140/// However, it's worth to note that zero-based solution (`i & (i + 1)`) produces less cleaner code
141/// because to iterate we need to call `i = prev(i) - 1`, which involves additional checks when `i`
142/// is of `usize` (decrement may result in panic).
143const fn prev(i: usize) -> usize {
144    i & (i - 1)
145}
146
147/// Flips first trailing `0` in the binary representation of the `i`.
148///
149/// In the same way as with `prev`, `i = next(i)` allows traversing the array but in the opposite
150/// direction.
151/// However, unlike `prev`, this function assumes that indexing is zero-based, hence we access sums by `i`.
152const fn next(i: usize) -> usize {
153    i | (i + 1)
154}