-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fenwick): implement Fenwick trees
- Loading branch information
Showing
7 changed files
with
173 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
add_subdirectory(slice) | ||
add_subdirectory(stack) | ||
add_subdirectory(queue) | ||
add_subdirectory(binary-heap) | ||
|
||
add_subdirectory(binary-heap) | ||
add_subdirectory(avl-tree) | ||
add_subdirectory(fenwick-tree) | ||
|
||
add_subdirectory(singly-linked-list) | ||
add_subdirectory(doubly-linked-list) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
add_library(${PROJECT_NAME}_FenwickTree STATIC fenwick.c) | ||
target_link_libraries(${PROJECT_NAME}_FenwickTree ${PROJECT_NAME}_Slice) | ||
target_include_directories(${PROJECT_NAME}_FenwickTree PUBLIC .) | ||
|
||
if(BUILD_TESTING) | ||
add_executable(${PROJECT_NAME}_FenwickTree_Test test.c) | ||
target_link_libraries(${PROJECT_NAME}_FenwickTree_Test ${PROJECT_NAME}_FenwickTree unity) | ||
add_test(NAME ${PROJECT_NAME}_FenwickTree_Suite COMMAND $<TARGET_FILE:${PROJECT_NAME}_FenwickTree_Test>) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#include <assert.h> | ||
#include <stdbool.h> | ||
#include <stdint.h> | ||
|
||
#include "fenwick.h" | ||
#include "slice.h" | ||
|
||
// Obtain a mask for the least significant bit of `n` in constant time. | ||
size_t least_significant_bit(const size_t n) { | ||
return n & -n; | ||
} | ||
|
||
void fenwick_init(struct FenwickTree * const self, const struct Slice slice) { | ||
// Accumulate the range indexes by only adding the current element to their immediate parent | ||
for (size_t i = 1; i < slice.length; ++i) { | ||
const size_t direct = i + least_significant_bit(i); | ||
uint8_t * const parent = slice_try_get(slice, direct); | ||
if (parent == NULL) continue; | ||
*parent += slice_get(slice, i); | ||
} | ||
self->slice = slice; | ||
} | ||
|
||
struct FenwickTree fenwick_create(const struct Slice slice) { | ||
struct FenwickTree self; | ||
fenwick_init(&self, slice); | ||
return self; | ||
} | ||
|
||
void fenwick_add(const struct FenwickTree * const self, size_t n, const size_t value) { | ||
assert(n > 0); | ||
while (n < self->slice.length) { | ||
uint8_t * const sum = slice_try_get(self->slice, n); | ||
assert(sum != NULL); | ||
*sum += value; | ||
n += least_significant_bit(n); | ||
} | ||
} | ||
|
||
uint8_t fenwick_prefix_sum(const struct FenwickTree * const self, size_t n) { | ||
uint8_t sum = 0; | ||
while (n > 0) { | ||
sum += slice_get(self->slice, n); | ||
n -= least_significant_bit(n); | ||
} | ||
return sum; | ||
} | ||
|
||
uint8_t fenwick_range_sum(const struct FenwickTree * const self, const size_t start, const size_t end) { | ||
assert(start <= end); | ||
return fenwick_prefix_sum(self, end) - fenwick_prefix_sum(self, start); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#ifndef FENWICK_H | ||
#define FENWICK_H | ||
|
||
#include "slice.h" | ||
|
||
struct FenwickTree { | ||
// Implementation is one-indexed, so the first item is always ignored. | ||
struct Slice slice; | ||
}; | ||
|
||
// Initializes a Fenwick tree index in-place. The internal `slice` is modified. | ||
void fenwick_init(struct FenwickTree * self, struct Slice slice); | ||
|
||
// Initializes a Fenwick tree index in-place. The internal `slice` is modified. | ||
struct FenwickTree fenwick_create(struct Slice slice); | ||
|
||
// Adds `value` to the item currently at index `n`. | ||
void fenwick_add(const struct FenwickTree * self, size_t n, size_t value); | ||
|
||
// Compute the prefix sum up to index `start` (inclusive). | ||
uint8_t fenwick_prefix_sum(const struct FenwickTree * self, size_t n); | ||
|
||
// Compute the range sum `(start, end]`. | ||
uint8_t fenwick_range_sum(const struct FenwickTree * self, size_t start, size_t end); | ||
|
||
#endif // !FENWICK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#include "fenwick.h" | ||
#include "slice.h" | ||
#include "unity.h" | ||
#include "unity_internals.h" | ||
|
||
void setUp(void) {} | ||
|
||
void tearDown(void) {} | ||
|
||
void prefix_sums_power_of_two_length(void) { | ||
uint8_t bytes[9] = {0, 1, 5, 8, 2, 0, 8, 9, 3}; // 0th is unused | ||
const struct FenwickTree fenwick = fenwick_create(slice_create_valid(bytes, 9)); | ||
TEST_ASSERT_EQUAL_UINT(0, fenwick_prefix_sum(&fenwick, 0)); | ||
TEST_ASSERT_EQUAL_UINT(1, fenwick_prefix_sum(&fenwick, 1)); | ||
TEST_ASSERT_EQUAL_UINT(6, fenwick_prefix_sum(&fenwick, 2)); | ||
TEST_ASSERT_EQUAL_UINT(14, fenwick_prefix_sum(&fenwick, 3)); | ||
TEST_ASSERT_EQUAL_UINT(16, fenwick_prefix_sum(&fenwick, 4)); | ||
TEST_ASSERT_EQUAL_UINT(16, fenwick_prefix_sum(&fenwick, 5)); | ||
TEST_ASSERT_EQUAL_UINT(24, fenwick_prefix_sum(&fenwick, 6)); | ||
TEST_ASSERT_EQUAL_UINT(33, fenwick_prefix_sum(&fenwick, 7)); | ||
TEST_ASSERT_EQUAL_UINT(36, fenwick_prefix_sum(&fenwick, 8)); | ||
} | ||
|
||
void prefix_sums_non_power_of_two_length(void) { | ||
uint8_t bytes[7] = {0, 6, 3, 9, 4, 10, 8}; // 0th is unused | ||
const struct FenwickTree fenwick = fenwick_create(slice_create_valid(bytes, 7)); | ||
TEST_ASSERT_EQUAL_UINT(0, fenwick_prefix_sum(&fenwick, 0)); | ||
TEST_ASSERT_EQUAL_UINT(6, fenwick_prefix_sum(&fenwick, 1)); | ||
TEST_ASSERT_EQUAL_UINT(9, fenwick_prefix_sum(&fenwick, 2)); | ||
TEST_ASSERT_EQUAL_UINT(18, fenwick_prefix_sum(&fenwick, 3)); | ||
TEST_ASSERT_EQUAL_UINT(22, fenwick_prefix_sum(&fenwick, 4)); | ||
TEST_ASSERT_EQUAL_UINT(32, fenwick_prefix_sum(&fenwick, 5)); | ||
TEST_ASSERT_EQUAL_UINT(40, fenwick_prefix_sum(&fenwick, 6)); | ||
} | ||
|
||
void range_sums_power_of_two_length(void) { | ||
uint8_t bytes[9] = {0, 1, 5, 8, 2, 0, 8, 9, 3}; // 0th is unused | ||
const struct FenwickTree fenwick = fenwick_create(slice_create_valid(bytes, 9)); | ||
TEST_ASSERT_EQUAL_UINT(35, fenwick_range_sum(&fenwick, 1, 8)); | ||
TEST_ASSERT_EQUAL_UINT(15, fenwick_range_sum(&fenwick, 1, 4)); | ||
TEST_ASSERT_EQUAL_UINT(17, fenwick_range_sum(&fenwick, 5, 7)); | ||
TEST_ASSERT_EQUAL_UINT(20, fenwick_range_sum(&fenwick, 4, 8)); | ||
} | ||
|
||
void range_sums_non_power_of_two_length(void) { | ||
uint8_t bytes[5] = {0, 1, 5, 8, 2}; // 0th is unused | ||
const struct FenwickTree fenwick = fenwick_create(slice_create_valid(bytes, 5)); | ||
TEST_ASSERT_EQUAL_UINT(5, fenwick_range_sum(&fenwick, 1, 2)); | ||
TEST_ASSERT_EQUAL_UINT(13, fenwick_range_sum(&fenwick, 1, 3)); | ||
TEST_ASSERT_EQUAL_UINT(15, fenwick_range_sum(&fenwick, 1, 4)); | ||
TEST_ASSERT_EQUAL_UINT(8, fenwick_range_sum(&fenwick, 2, 3)); | ||
TEST_ASSERT_EQUAL_UINT(10, fenwick_range_sum(&fenwick, 2, 4)); | ||
TEST_ASSERT_EQUAL_UINT(2, fenwick_range_sum(&fenwick, 3, 4)); | ||
} | ||
|
||
void update_tree_tests(void) { | ||
uint8_t bytes[9] = {0, 1, 5, 8, 2, 0, 8, 9, 3}; // 0th is unused | ||
const struct FenwickTree fenwick = fenwick_create(slice_create_valid(bytes, 9)); | ||
TEST_ASSERT_EQUAL_UINT(15, fenwick_range_sum(&fenwick, 1, 4)); | ||
TEST_ASSERT_EQUAL_UINT(22, fenwick_range_sum(&fenwick, 3, 8)); | ||
|
||
fenwick_add(&fenwick, 2, 8); | ||
TEST_ASSERT_EQUAL_UINT(23, fenwick_range_sum(&fenwick, 1, 4)); | ||
TEST_ASSERT_EQUAL_UINT(22, fenwick_range_sum(&fenwick, 3, 8)); | ||
|
||
fenwick_add(&fenwick, 5, 1); | ||
TEST_ASSERT_EQUAL_UINT(23, fenwick_range_sum(&fenwick, 1, 4)); | ||
TEST_ASSERT_EQUAL_UINT(23, fenwick_range_sum(&fenwick, 3, 8)); | ||
} | ||
|
||
int main(void) { | ||
UNITY_BEGIN(); | ||
RUN_TEST(prefix_sums_power_of_two_length); | ||
RUN_TEST(prefix_sums_non_power_of_two_length); | ||
RUN_TEST(range_sums_power_of_two_length); | ||
RUN_TEST(range_sums_non_power_of_two_length); | ||
RUN_TEST(update_tree_tests); | ||
return UNITY_END(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters