diff --git a/interval-tree/src/lib.rs b/interval-tree/src/lib.rs index 70b3332..fa509ec 100644 --- a/interval-tree/src/lib.rs +++ b/interval-tree/src/lib.rs @@ -101,10 +101,7 @@ impl IntervalTree { "Inputs must have the same length" ); if n == 0 { - return Self { - root: None, - size: 0, - }; + return Self::new(); } let elements: Vec<(f64, f64, usize)> = (0..n).map(|i| (mins[i], maxs[i], ids[i])).collect(); Self { @@ -160,6 +157,15 @@ impl IntervalTree { pub fn root(&self) -> Option<&IntervalTreeNode> { self.root.as_ref() } + + /// Returns an empty interval tree. + #[must_use] + pub fn new() -> Self { + Self { + root: None, + size: 0, + } + } } #[cfg(test)] diff --git a/interval-tree/src/tests.rs b/interval-tree/src/tests.rs index a712a32..f07b973 100644 --- a/interval-tree/src/tests.rs +++ b/interval-tree/src/tests.rs @@ -67,6 +67,13 @@ fn test_interval_tree_bulk_load() { assert_eq!(right_node.right, None); } +#[test] +fn test_interval_tree_new() { + let tree = IntervalTree::new(); + assert_eq!(tree.size(), 0); + assert_eq!(tree.root(), None); +} + #[test] fn test_interval_tree_empty_bulk_load() { let mins = vec![]; diff --git a/rtree-capi/include/rtree-capi.h b/rtree-capi/include/rtree-capi.h index fea4860..a16d96e 100644 --- a/rtree-capi/include/rtree-capi.h +++ b/rtree-capi/include/rtree-capi.h @@ -9,6 +9,7 @@ enum RTreeError { Success = 0, NullPointer = 1, InvalidDimension = 2, + EmptyNodeEnvelope = 3, }; typedef uint16_t RTreeError; @@ -47,3 +48,5 @@ RTreeError rtree_node_envelope(const struct RTreeNodeH *node, double *min_out, d RTreeError rtree_node_free(struct RTreeNodeH *node); RTreeError rtree_root_node(const struct RTreeH *tree, struct RTreeNodeH **node); + +RTreeError rtree_size(const struct RTreeH *tree, size_t *size_out); diff --git a/rtree-capi/src/error.rs b/rtree-capi/src/error.rs index bd5687f..ba98dc7 100644 --- a/rtree-capi/src/error.rs +++ b/rtree-capi/src/error.rs @@ -3,4 +3,5 @@ pub enum RTreeError { Success = 0, NullPointer = 1, InvalidDimension = 2, + EmptyNodeEnvelope = 3, } diff --git a/rtree-capi/src/node.rs b/rtree-capi/src/node.rs index d366c98..11ffce7 100644 --- a/rtree-capi/src/node.rs +++ b/rtree-capi/src/node.rs @@ -10,6 +10,7 @@ enum NodeRef { Parent3D(*const ParentNode), Node2D(*const RTreeNode), Node3D(*const RTreeNode), + EmptyNode, // root node of empty tree } pub enum RTreeNodeH {} @@ -20,6 +21,16 @@ pub extern "C" fn rtree_root_node(tree: *const RTreeH, node: *mut *mut RTreeNode return RTreeError::NullPointer; } let rtree = unsafe { &*(tree as *const RTreeDim) }; + let size = match rtree { + RTreeDim::D1(tree) => tree.size(), + RTreeDim::D2(tree) => tree.size(), + RTreeDim::D3(tree) => tree.size(), + }; + if size == 0 { + let node_ref = NodeRef::EmptyNode; + unsafe { *node = Box::into_raw(Box::new(node_ref)) as *mut RTreeNodeH }; + return RTreeError::Success; + } let node_ref = match rtree { RTreeDim::D1(tree) => NodeRef::ITreeNode(tree.root().unwrap()), RTreeDim::D2(tree) => NodeRef::Parent2D(tree.root() as *const _), @@ -41,6 +52,7 @@ pub extern "C" fn rtree_node_children( let node_ref = unsafe { &*(node as *const NodeRef) }; let child_node_refs: Vec = match node_ref { + NodeRef::EmptyNode => Vec::new(), NodeRef::ITreeNode(ptr) => { let node = unsafe { &**ptr }; let mut children = Vec::new(); @@ -115,6 +127,7 @@ pub extern "C" fn rtree_node_envelope( let node_ref = unsafe { &*(node as *const NodeRef) }; match node_ref { + NodeRef::EmptyNode => return RTreeError::EmptyNodeEnvelope, NodeRef::ITreeNode(ptr) => { let node = unsafe { &**ptr }; let min = node.min; diff --git a/rtree-capi/src/rtree.rs b/rtree-capi/src/rtree.rs index da0d21b..d09265d 100644 --- a/rtree-capi/src/rtree.rs +++ b/rtree-capi/src/rtree.rs @@ -101,7 +101,22 @@ pub extern "C" fn rtree_bulk_load( n: usize, dim: u32, ) -> RTreeError { - if tree.is_null() || mins.is_null() || maxs.is_null() || ids.is_null() { + if tree.is_null() { + return RTreeError::NullPointer; + } + + if n == 0 { + let rtree = match dim { + 1 => RTreeDim::D1(IntervalTree::new()), + 2 => RTreeDim::D2(RTree::new()), + 3 => RTreeDim::D3(RTree::new()), + _ => return RTreeError::InvalidDimension, + }; + unsafe { *tree = Box::into_raw(Box::new(rtree)) as *mut RTreeH }; + return RTreeError::Success; + } + + if mins.is_null() || maxs.is_null() || ids.is_null() { return RTreeError::NullPointer; } @@ -153,6 +168,21 @@ pub extern "C" fn rtree_locate_all_at_point( RTreeError::Success } +#[no_mangle] +pub extern "C" fn rtree_size(tree: *const RTreeH, size_out: *mut usize) -> RTreeError { + if tree.is_null() || size_out.is_null() { + return RTreeError::NullPointer; + } + let rtree = unsafe { &*(tree as *const RTreeDim) }; + let size = match rtree { + RTreeDim::D1(tree) => tree.size(), + RTreeDim::D2(tree) => tree.size(), + RTreeDim::D3(tree) => tree.size(), + }; + unsafe { *size_out = size }; + RTreeError::Success +} + #[no_mangle] pub extern "C" fn rtree_free_ids(ids: *mut usize, n: usize) -> RTreeError { if ids.is_null() { diff --git a/rtree-capi/tests/test.c b/rtree-capi/tests/test.c index 5e28876..bd54d87 100644 --- a/rtree-capi/tests/test.c +++ b/rtree-capi/tests/test.c @@ -34,6 +34,21 @@ bool test_null(void) { return true; } +bool test_invalid_dimension(void) { + RTreeH *tree = NULL; + RTreeError err = rtree_create(&tree, 0); + if (err != InvalidDimension) { + fprintf(stderr, "Expected InvalidDimension error for rtree_create with dimension 0\n"); + return false; + } + err = rtree_create(&tree, 4); + if (err != InvalidDimension) { + fprintf(stderr, "Expected InvalidDimension error for rtree_create with dimension 4\n"); + return false; + } + return true; +} + bool test_get_dimension(void) { RTreeH *tree3d = NULL; @@ -79,6 +94,15 @@ bool test_bulk_load(void) { return false; } + // test rtree_size + size_t size = 0; + rtree_size(tree, &size); + if (size != N) { + fprintf(stderr, "Expected tree size %zu, got %zu\n", N, size); + rtree_free(tree); + return false; + } + double point1[2] = {1.5, 1.5}; double point2[2] = {0.0, 0.0}; double point3[2] = {-1.0, 0.0}; @@ -208,29 +232,6 @@ bool test_nodes(void) { } -bool test_root_node_id(void) { - const size_t N = 2; - const uint32_t dim = 2; - double mins[4] = {0.0, 0.0, 1.0, 1.0}; - double maxs[4] = {2.0, 2.0, 3.0, 3.0}; - size_t ids[2] = {1, 2}; - RTreeH *tree = NULL; - rtree_bulk_load(&tree, mins, maxs, ids, N, dim); - if (tree == NULL) { - return false; - } - - RTreeNodeH *root = NULL; - rtree_root_node(tree, &root); - if (root == NULL) { - rtree_free(tree); - return false; - } - rtree_node_free(root); - rtree_free(tree); - return true; -} - bool test_rtree_1d(void) { const size_t N = 4; const uint32_t dim = 1; @@ -242,6 +243,15 @@ bool test_rtree_1d(void) { if (tree == NULL) { return false; } + + // test rtree_size + size_t size = 0; + rtree_size(tree, &size); + if (size != N) { + fprintf(stderr, "Expected tree size %zu, got %zu\n", N, size); + rtree_free(tree); + return false; + } double point1[1] = {0.5}; double point2[1] = {1.5}; @@ -311,6 +321,8 @@ bool test_rtree_node_1d(void) { if (tree == NULL) { return false; } + + // Get root node RTreeNodeH *root = NULL; rtree_root_node(tree, &root); if (root == NULL) { @@ -318,6 +330,7 @@ bool test_rtree_node_1d(void) { return false; } + // Get envelope of root node double root_min[1]; double root_max[1]; rtree_node_envelope(root, root_min, root_max); @@ -329,6 +342,7 @@ bool test_rtree_node_1d(void) { return false; } + // Get children of root node struct RTreeNodeH **children = NULL; size_t nchildren = 0; rtree_node_children(root, &children, &nchildren); @@ -340,6 +354,7 @@ bool test_rtree_node_1d(void) { return false; } + // Get envelopes of child nodes double child1_min[1]; double child1_max[1]; rtree_node_envelope(children[0], child1_min, child1_max); @@ -364,6 +379,7 @@ bool test_rtree_node_1d(void) { return false; } + // Get children of child1 node RTreeNodeH **child1children = NULL; size_t nchild1children = 0; rtree_node_children(children[0], &child1children, &nchild1children); @@ -383,6 +399,169 @@ bool test_rtree_node_1d(void) { return true; } +bool test_rtree_empty(void) { + const size_t N = 0; + const uint32_t dim = 2; + double *mins = NULL; + double *maxs = NULL; + size_t *ids = NULL; + RTreeH *tree = NULL; + rtree_bulk_load(&tree, mins, maxs, ids, N, dim); + if (tree == NULL) { + fprintf(stderr, "Expected to create empty tree, got null pointer\n"); + return false; + } + + // test rtree_size + size_t size = 0; + rtree_size(tree, &size); + if (size != N) { + fprintf(stderr, "Expected tree size %zu, got %zu\n", N, size); + rtree_free(tree); + return false; + } + + // Query empty tree + double point[2] = {0.0, 0.0}; + size_t *ids_out = NULL; + size_t nids_out = 0; + rtree_locate_all_at_point(tree, point, &ids_out, &nids_out); + if (nids_out != 0) { + fprintf(stderr, "Expected to find no ids at point in empty tree"); + rtree_free_ids(ids_out, nids_out); + rtree_free(tree); + return false; + } else { + rtree_free_ids(ids_out, nids_out); + } + + // Check root node of empty tree + RTreeNodeH *root = NULL; + rtree_root_node(tree, &root); + if (root == NULL) { + fprintf(stderr, "Expected root node of empty tree to be non-null\n"); + rtree_free(tree); + return false; + } + + // Check envelope of root node of empty tree + double root_min[2] = {1.0, 1.0}; // initialize to something + double root_max[2] = {1.0, 1.0}; + RTreeError err = rtree_node_envelope(root, root_min, root_max); + if (err != EmptyNodeEnvelope) { + fprintf(stderr, "Expected EmptyNodeEnvelope error for envelope of root node of empty tree\n"); + rtree_node_free(root); + rtree_free(tree); + return false; + } + for (size_t i = 0; i < dim; i++) { + if (root_min[i] != 1.0 || root_max[i] != 1.0) { + fprintf(stderr, "Expected root_min and root_max to be unchanged for empty node, got root_min[%zu] = %f, root_max[%zu] = %f\n", + i, root_min[i], i, root_max[i]); + rtree_node_free(root); + rtree_free(tree); + return false; + } + } + + // Get children of root node of empty tree + RTreeNodeH **children = NULL; + size_t nchildren = 0; + rtree_node_children(root, &children, &nchildren); + if (nchildren != 0) { + fprintf(stderr, "Expected root of empty tree to have 0 children, got %zu\n", nchildren); + rtree_node_children_free(children, nchildren); + rtree_node_free(root); + rtree_free(tree); + return false; + } + + rtree_node_children_free(children, nchildren); + rtree_node_free(root); + rtree_free(tree); + return true; +} + +bool test_rtree_empty_1d(void) { + const size_t N = 0; + const uint32_t dim = 1; + double *mins = NULL; + double *maxs = NULL; + size_t *ids = NULL; + RTreeH *tree = NULL; + rtree_bulk_load(&tree, mins, maxs, ids, N, dim); + if (tree == NULL) { + fprintf(stderr, "Expected to create empty tree, got null pointer\n"); + return false; + } + // test rtree_size + size_t size = 0; + rtree_size(tree, &size); + if (size != N) { + fprintf(stderr, "Expected tree size %zu, got %zu\n", N, size); + rtree_free(tree); + return false; + } + + // Query empty tree + double point[1] = {0.0}; + size_t *ids_out = NULL; + size_t nids_out = 0; + rtree_locate_all_at_point(tree, point, &ids_out, &nids_out); + if (nids_out != 0) { + fprintf(stderr, "Expected to find no ids at point in empty tree"); + rtree_free_ids(ids_out, nids_out); + rtree_free(tree); + return false; + } else { + rtree_free_ids(ids_out, nids_out); + } + + // Check root node of empty tree + RTreeNodeH *root = NULL; + rtree_root_node(tree, &root); + if (root == NULL) { + fprintf(stderr, "Expected root node of empty tree to be non-null\n"); + rtree_free(tree); + return false; + } + + // Check envelope of root node of empty tree + double root_min[1] = {1.0}; // initialize to something + double root_max[1] = {1.0}; + RTreeError err = rtree_node_envelope(root, root_min, root_max); + if (err != EmptyNodeEnvelope) { + fprintf(stderr, "Expected EmptyNodeEnvelope error for envelope of root node of empty tree\n"); + rtree_node_free(root); + rtree_free(tree); + return false; + } + if (root_min[0] != 1.0 || root_max[0] != 1.0) { + fprintf(stderr, "Expected root_min and root_max to be unchanged for empty node, got root_min[0] = %f, root_max[0] = %f\n", + root_min[0], root_max[0]); + rtree_node_free(root); + rtree_free(tree); + return false; + } + + // Get children of root node of empty tree + RTreeNodeH **children = NULL; + size_t nchildren = 0; + rtree_node_children(root, &children, &nchildren); + if (nchildren != 0) { + fprintf(stderr, "Expected root of empty tree to have 0 children, got %zu\n", nchildren); + rtree_node_children_free(children, nchildren); + rtree_node_free(root); + rtree_free(tree); + return false; + } + + rtree_node_children_free(children, nchildren); + rtree_node_free(root); + rtree_free(tree); + return true; +} + void run_test( bool (test)(void), const char *test_name, @@ -404,9 +583,11 @@ int main(void) { run_test(test_get_dimension, "test_get_dimension", &passed); run_test(test_bulk_load, "test_bulk_load", &passed); run_test(test_nodes, "test_nodes", &passed); - run_test(test_root_node_id, "test_root_node_id", &passed); run_test(test_rtree_1d, "test_rtree_1d", &passed); run_test(test_rtree_node_1d, "test_rtree_node_1d", &passed); + run_test(test_rtree_empty, "test_rtree_empty", &passed); + run_test(test_rtree_empty_1d, "test_rtree_empty_1d", &passed); + run_test(test_invalid_dimension, "test_invalid_dimension", &passed); if (passed) { fprintf(stdout, "All tests passed\n");