@@ -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