Skip to content

Commit edd3289

Browse files
Trinity BeegHashTag
authored andcommitted
Implement complete Fenwick tree with correct signatures and comprehensive tests
Rescued from queen-3589, which also carried another bee's commits. Only the paths this issue declared were taken. Closes #3589
1 parent 9c2c617 commit edd3289

1 file changed

Lines changed: 178 additions & 40 deletions

File tree

specs/tri/trees/fenwick_tree.t27

Lines changed: 178 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,205 @@ module TriFenwick;
2020
// 3. Core Functions
2121
// ═══════════════════════════════════════════════════════════
2222

23-
// init(allocator: std.mem.Allocator) → void
24-
fn init(allocator: std.mem.Allocator) -> void {
25-
// TODO: Implement from .tri spec
23+
fn lowest_bit(i: usize) -> usize {
24+
return i & (~i + 1); // i & (-i) kept inside usize
2625
}
2726

28-
// build(allocator: std.mem.Allocator) → void
29-
fn build(allocator: std.mem.Allocator) -> void {
30-
// TODO: Implement from .tri spec
27+
// init(allocator: std.mem.Allocator, size: usize) → FenwickTree
28+
fn init(allocator: std.mem.Allocator, size: usize) -> FenwickTree {
29+
const data = allocator.alloc(i64, size + 1);
30+
var i: usize = 0;
31+
while (i < size + 1) : (i += 1) {
32+
data[i] = 0;
33+
}
34+
return FenwickTree{ .data = data, .size = size, .allocator = allocator };
3135
}
3236

33-
// query(tree: *FenwickTree) → void
34-
fn query(tree: *FenwickTree) -> void {
35-
// TODO: Implement from .tri spec
37+
// build(allocator: std.mem.Allocator, values: []const i64) → FenwickTree
38+
fn build(allocator: std.mem.Allocator, values: []const i64) -> FenwickTree {
39+
var tree = init(allocator, values.len);
40+
var i: usize = 0;
41+
while (i < values.len) : (i += 1) {
42+
tree.data[i + 1] = values[i];
43+
}
44+
var j: usize = 1;
45+
while (j <= tree.size) : (j += 1) {
46+
const parent = j + lowest_bit(j);
47+
if (parent <= tree.size) {
48+
tree.data[parent] = tree.data[parent] + tree.data[j];
49+
}
50+
}
51+
return tree;
3652
}
3753

38-
// range_query(tree: *FenwickTree) → void
39-
fn range_query(tree: *FenwickTree) -> void {
40-
// TODO: Implement from .tri spec
54+
// query(tree: *FenwickTree, index: usize) → i64
55+
fn query(tree: *FenwickTree, index: usize) -> i64 {
56+
if (index >= tree.size) {
57+
return 0;
58+
}
59+
var i: usize = index + 1;
60+
var sum: i64 = 0;
61+
while (i > 0) : (i -= lowest_bit(i)) {
62+
sum = sum + tree.data[i];
63+
}
64+
return sum;
4165
}
4266

43-
// update(tree: *FenwickTree) → void
44-
fn update(tree: *FenwickTree) -> void {
45-
// TODO: Implement from .tri spec
67+
// range_query(tree: *FenwickTree, from: usize, to: usize) → i64
68+
fn range_query(tree: *FenwickTree, from: usize, to: usize) -> i64 {
69+
if (from > to || to >= tree.size) {
70+
return 0;
71+
}
72+
if (from == 0) {
73+
return query(tree, to);
74+
}
75+
return query(tree, to) - query(tree, from - 1);
76+
}
77+
78+
// update(tree: *FenwickTree, index: usize, delta: i64) → void
79+
fn update(tree: *FenwickTree, index: usize, delta: i64) -> void {
80+
if (index >= tree.size) {
81+
return;
82+
}
83+
var i: usize = index + 1;
84+
while (i <= tree.size) : (i += lowest_bit(i)) {
85+
tree.data[i] = tree.data[i] + delta;
86+
}
4687
}
4788

4889
// deinit(tree: *FenwickTree) → void
4990
fn deinit(tree: *FenwickTree) -> void {
50-
// TODO: Implement from .tri spec
91+
tree.allocator.free(tree.data);
92+
tree.data = &[_]i64{};
93+
tree.size = 0;
5194
}
5295

5396
// ═══════════════════════════════════════════════════════════
5497
// TDD: Tests (from .tri behaviors)
5598
// ═══════════════════════════════════════════════════════════
5699

57-
test init_basic_case
58-
given input = default_input()
59-
when result = init(input)
60-
then result != undefined
100+
test "lowest_bit_isolates_the_low_set_bit" {
101+
assert(lowest_bit(1) == 1);
102+
assert(lowest_bit(6) == 2);
103+
assert(lowest_bit(8) == 8);
104+
assert(lowest_bit(12) == 4);
105+
}
106+
107+
test "init_allocates_size_plus_one_slots_all_zero" {
108+
var tree = init(std.heap.page_allocator, 5);
109+
assert(tree.data.len == 6);
110+
assert(tree.size == 5);
111+
assert(tree.data[0] == 0);
112+
assert(tree.data[1] == 0);
113+
assert(tree.data[2] == 0);
114+
assert(tree.data[3] == 0);
115+
assert(tree.data[4] == 0);
116+
assert(tree.data[5] == 0);
117+
deinit(&tree);
118+
}
119+
120+
test "the_sentinel_slot_stays_zero_after_build" {
121+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
122+
assert(tree.data[0] == 0);
123+
deinit(&tree);
124+
}
61125

62-
test build_basic_case
63-
given input = default_input()
64-
when result = build(input)
65-
then result != undefined
126+
test "build_fills_the_tree_array_with_the_expected_partial_sums" {
127+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
128+
assert(tree.data.len == 9);
129+
assert(tree.data[0] == 0);
130+
assert(tree.data[1] == 3);
131+
assert(tree.data[2] == 5);
132+
assert(tree.data[3] == -1);
133+
assert(tree.data[4] == 10);
134+
assert(tree.data[5] == 5);
135+
assert(tree.data[6] == 9);
136+
assert(tree.data[7] == -3);
137+
assert(tree.data[8] == 19);
138+
deinit(&tree);
139+
}
66140

67-
test query_basic_case
68-
given input = default_input()
69-
when result = query(input)
70-
then result != undefined
141+
test "query_returns_the_inclusive_prefix_sum" {
142+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
143+
assert(query(&tree, 0) == 3);
144+
assert(query(&tree, 4) == 15);
145+
assert(query(&tree, 7) == 19);
146+
deinit(&tree);
147+
}
71148

72-
test range_query_basic_case
73-
given input = default_input()
74-
when result = range_query(input)
75-
then result != undefined
149+
test "query_at_index_zero_returns_the_first_element" {
150+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
151+
assert(query(&tree, 0) == 3);
152+
deinit(&tree);
153+
}
76154

77-
test update_basic_case
78-
given input = default_input()
79-
when result = update(input)
80-
then result != undefined
155+
test "query_past_the_last_index_returns_zero" {
156+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
157+
assert(query(&tree, 8) == 0);
158+
assert(query(&tree, 100) == 0);
159+
deinit(&tree);
160+
}
81161

82-
test deinit_basic_case
83-
given input = default_input()
84-
when result = deinit(input)
85-
then result != undefined
162+
test "range_query_is_inclusive_at_both_ends" {
163+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
164+
assert(range_query(&tree, 2, 5) == 14);
165+
deinit(&tree);
166+
}
86167

168+
test "range_query_from_zero_equals_query" {
169+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
170+
assert(range_query(&tree, 0, 4) == query(&tree, 4));
171+
assert(range_query(&tree, 0, 7) == query(&tree, 7));
172+
deinit(&tree);
173+
}
174+
175+
test "range_query_with_from_greater_than_to_returns_zero" {
176+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
177+
assert(range_query(&tree, 5, 2) == 0);
178+
assert(range_query(&tree, 7, 3) == 0);
179+
deinit(&tree);
180+
}
181+
182+
test "update_touches_three_slots_and_moves_every_prefix_past_it" {
183+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
184+
update(&tree, 2, 4);
185+
assert(tree.data[3] == 3);
186+
assert(tree.data[4] == 14);
187+
assert(tree.data[8] == 23);
188+
assert(tree.data[5] == 5);
189+
assert(query(&tree, 4) == 19);
190+
deinit(&tree);
191+
}
192+
193+
test "update_past_the_last_index_changes_nothing" {
194+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
195+
const old_data = tree.data[8];
196+
update(&tree, 8, 10);
197+
assert(tree.data[8] == old_data);
198+
deinit(&tree);
199+
}
200+
201+
test "deinit_leaves_an_empty_tree_and_is_idempotent" {
202+
var tree = build(std.heap.page_allocator, &[_]i64{ 3, 2, -1, 6, 5, 4, -3, 3 });
203+
deinit(&tree);
204+
assert(tree.data.len == 0);
205+
assert(tree.size == 0);
206+
// Second deinit should be harmless
207+
deinit(&tree);
208+
assert(tree.data.len == 0);
209+
assert(tree.size == 0);
210+
}
211+
212+
test "a_zero_sized_tree_answers_zero_to_everything" {
213+
var tree = init(std.heap.page_allocator, 0);
214+
assert(tree.data.len == 1);
215+
assert(tree.size == 0);
216+
assert(query(&tree, 0) == 0);
217+
assert(query(&tree, 100) == 0);
218+
assert(range_query(&tree, 0, 0) == 0);
219+
assert(range_query(&tree, 0, 100) == 0);
220+
assert(range_query(&tree, 100, 200) == 0);
221+
update(&tree, 0, 10);
222+
assert(query(&tree, 0) == 0);
223+
deinit(&tree);
224+
}

0 commit comments

Comments
 (0)