1use std::collections::HashMap;
8use std::fmt::{self, Debug, Formatter};
9use std::ops::Range;
10use std::sync::Arc;
11use dashmap::DashMap;
12use super::concurrency::StripedLock;
13use super::hash_table::GeometricSignature;
14use super::tensor_network::{HyperbolicTensorNetwork, CompressedNode, NodeMetadata};
15
16#[derive(Debug)]
18pub enum IntegrationError {
19 AlreadyExists(String),
21 NotFound(String),
23 OperationFailed(String),
25 ValidationFailed(String),
27 DeserializationError(String),
29 LockError(String),
31 ConfigurationError(String),
33}
34
35impl IntegrationError {
36 pub fn configuration_error(msg: String) -> Self {
38 Self::ConfigurationError(msg)
39 }
40}
41
42impl std::fmt::Display for IntegrationError {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Self::AlreadyExists(s) => write!(f, "already exists: {s}"),
46 Self::NotFound(s) => write!(f, "not found: {s}"),
47 Self::OperationFailed(s) => write!(f, "operation failed: {s}"),
48 Self::ValidationFailed(s) => write!(f, "validation failed: {s}"),
49 Self::DeserializationError(s) => write!(f, "deserialization error: {s}"),
50 Self::LockError(s) => write!(f, "lock error: {s}"),
51 Self::ConfigurationError(s) => write!(f, "configuration error: {s}"),
52 }
53 }
54}
55
56impl std::error::Error for IntegrationError {}
57
58pub type IntegrationResult<T> = Result<T, IntegrationError>;
60
61pub trait PathOperations {
63 fn split_path(&self, path: &str) -> Vec<String>;
65
66 fn join_path(&self, components: &[String]) -> String;
68
69 fn parent_path(&self, path: &str) -> Option<String>;
71
72 fn last_component(&self, path: &str) -> Option<String>;
74}
75
76pub struct DefaultPathOps;
78
79impl PathOperations for DefaultPathOps {
80 fn split_path(&self, path: &str) -> Vec<String> {
81 path.split('/')
82 .filter(|s| !s.is_empty())
83 .map(String::from)
84 .collect()
85 }
86
87 fn join_path(&self, components: &[String]) -> String {
88 let mut path = String::new();
89 for component in components {
90 path.push('/');
91 path.push_str(component);
92 }
93 if path.is_empty() {
94 path.push('/');
95 }
96 path
97 }
98
99 fn parent_path(&self, path: &str) -> Option<String> {
100 if path == "/" {
101 return None;
102 }
103
104 let components = self.split_path(path);
105 if components.is_empty() {
106 return Some("/".to_string());
107 }
108
109 let parent_components = &components[0..components.len() - 1];
110 Some(self.join_path(parent_components))
111 }
112
113 fn last_component(&self, path: &str) -> Option<String> {
114 let components = self.split_path(path);
115 components.last().cloned()
116 }
117}
118
119#[derive(Clone, Debug)]
121pub struct HTTConfig {
122 dimension: usize,
124 max_memory_nodes: usize,
126 cache_size: usize,
128 tau: g_math::fixed_point::FixedPoint,
130}
131
132impl HTTConfig {
133 pub fn new(dimension: usize, max_memory_nodes: usize, cache_size: usize) -> Self {
135 Self {
136 dimension,
137 max_memory_nodes,
138 cache_size,
139 tau: crate::constants::default_tau(),
140 }
141 }
142
143 pub fn default_config() -> Self {
145 Self {
146 dimension: 4,
147 max_memory_nodes: 1000,
148 cache_size: 100,
149 tau: crate::constants::default_tau(),
150 }
151 }
152
153 pub fn with_tau(mut self, tau: g_math::fixed_point::FixedPoint) -> Self {
155 self.tau = tau;
156 self
157 }
158
159 pub fn dimension(&self) -> usize {
161 self.dimension
162 }
163
164 pub fn max_memory_nodes(&self) -> usize {
166 self.max_memory_nodes
167 }
168
169 pub fn cache_size(&self) -> usize {
171 self.cache_size
172 }
173
174 pub fn tau(&self) -> g_math::fixed_point::FixedPoint {
176 self.tau
177 }
178}
179
180impl Default for HTTConfig {
181 fn default() -> Self {
182 Self::default_config()
183 }
184}
185
186pub struct HyperbolicTreeTensor {
194 tensor_network: HyperbolicTensorNetwork,
196 path_ops: Box<dyn PathOperations + Send + Sync>,
198 path_map: DashMap<String, GeometricSignature>,
200 id_to_path: DashMap<String, String>,
202 parent_locks: StripedLock<64>,
205 config: HTTConfig,
207}
208
209impl HyperbolicTreeTensor {
210 pub fn new(config: HTTConfig) -> Self {
212 let tensor_network = HyperbolicTensorNetwork::new(config.dimension(), config.tau());
213
214 Self {
215 tensor_network,
216 path_ops: Box::new(DefaultPathOps),
217 path_map: DashMap::new(),
218 id_to_path: DashMap::new(),
219 parent_locks: StripedLock::new(),
220 config,
221 }
222 }
223
224 fn resolve_parent(
233 &self,
234 path: &str,
235 parent_path: &Option<String>,
236 ) -> IntegrationResult<Option<GeometricSignature>> {
237 match parent_path {
238 Some(p) if p.as_str() != path => match self.path_map.get(p.as_str()) {
239 Some(r) => Ok(Some(r.value().clone())),
240 None => Err(IntegrationError::NotFound(format!(
241 "cannot insert '{}': parent '{}' does not exist",
242 path, p
243 ))),
244 },
245 _ => Ok(None), }
247 }
248
249 pub fn insert_data_only(&self, path: &str, value: Vec<u8>, content_type: Option<String>) -> IntegrationResult<()> {
255 if self.path_map.contains_key(path) {
257 return Err(IntegrationError::AlreadyExists(
258 format!("Node at path {} already exists", path),
259 ));
260 }
261
262 let parent_path = self.path_ops.parent_path(path);
266 if let Some(p) = &parent_path {
267 if p.as_str() != path && !self.path_map.contains_key(p.as_str()) {
268 return Err(IntegrationError::NotFound(format!(
269 "cannot insert '{}': parent '{}' does not exist",
270 path, p
271 )));
272 }
273 }
274
275 let _stripe_guard = parent_path.as_deref().map(|p| self.parent_locks.lock(p));
280
281 if self.path_map.contains_key(path) {
283 return Err(IntegrationError::AlreadyExists(
284 format!("Node at path {} already exists", path),
285 ));
286 }
287
288 let metadata = NodeMetadata::new(path.to_string(), content_type);
289
290 let unique_id = self.tensor_network.add_node_data_only(metadata, value, 0);
291
292 let stub_sig = GeometricSignature::stub(&unique_id);
294 self.id_to_path.insert(unique_id, path.to_string());
295 self.path_map.insert(path.to_string(), stub_sig);
296
297 Ok(())
298 }
299
300 pub fn insert(&self, path: &str, value: Vec<u8>, content_type: Option<String>) -> IntegrationResult<()> {
305 if self.path_map.contains_key(path) {
307 return Err(IntegrationError::AlreadyExists(
308 format!("Node at path {} already exists", path),
309 ));
310 }
311
312 let parent_path = self.path_ops.parent_path(path);
313 let level = self.path_depth(path);
314 let metadata = NodeMetadata::new(path.to_string(), content_type);
315
316 let parent_signature = self.resolve_parent(path, &parent_path)?;
320
321 let parent_uid = parent_signature.as_ref().map(|s| s.unique_id());
326 let _stripe_guard = parent_uid.as_deref().map(|uid| self.parent_locks.lock(uid));
327
328 if self.path_map.contains_key(path) {
330 return Err(IntegrationError::AlreadyExists(
331 format!("Node at path {} already exists", path),
332 ));
333 }
334
335 let signature = self
336 .tensor_network
337 .add_node(metadata, value, parent_signature.as_ref(), level)
338 .ok_or_else(|| {
339 IntegrationError::OperationFailed(
340 "Failed to add node to tensor network".to_string(),
341 )
342 })?;
343
344 let unique_id = signature.unique_id();
345 self.id_to_path.insert(unique_id, path.to_string());
346 self.path_map.insert(path.to_string(), signature);
347
348 Ok(())
350 }
351
352 pub fn insert_positioned(&self, path: &str, value: Vec<u8>, content_type: Option<String>, child_index: u32) -> IntegrationResult<()> {
357 if self.path_map.contains_key(path) {
358 return Err(IntegrationError::AlreadyExists(
359 format!("Node at path {} already exists", path),
360 ));
361 }
362
363 let parent_path = self.path_ops.parent_path(path);
364 let level = self.path_depth(path);
365 let metadata = NodeMetadata::new(path.to_string(), content_type);
366
367 let parent_signature = self.resolve_parent(path, &parent_path)?;
368
369 let parent_uid = parent_signature.as_ref().map(|s| s.unique_id());
370 let _stripe_guard = parent_uid.as_deref().map(|uid| self.parent_locks.lock(uid));
371
372 if self.path_map.contains_key(path) {
373 return Err(IntegrationError::AlreadyExists(
374 format!("Node at path {} already exists", path),
375 ));
376 }
377
378 let signature = self
379 .tensor_network
380 .add_node_positioned(metadata, value, parent_signature.as_ref(), level, child_index)
381 .ok_or_else(|| {
382 IntegrationError::OperationFailed(
383 "Failed to add node to tensor network".to_string(),
384 )
385 })?;
386
387 let unique_id = signature.unique_id();
388 self.id_to_path.insert(unique_id, path.to_string());
389 self.path_map.insert(path.to_string(), signature);
390
391 Ok(())
392 }
393
394 pub fn embed_existing(&self, path: &str) -> IntegrationResult<bool> {
423 let sig = match self.path_map.get(path) {
425 Some(r) => r.value().clone(),
426 None => {
427 return Err(IntegrationError::NotFound(format!(
428 "Node at path {} not found",
429 path
430 )))
431 }
432 };
433 if !sig.is_stub() {
434 return Ok(false);
435 }
436
437 let parent_path = self.path_ops.parent_path(path);
441 if let Some(p) = &parent_path {
442 if p.as_str() != path {
443 self.embed_existing(p)?;
444 }
445 }
446
447 let parent_signature = self.resolve_parent(path, &parent_path)?;
448 if let Some(ps) = &parent_signature {
449 if ps.is_stub() {
450 return Err(IntegrationError::OperationFailed(format!(
454 "cannot embed '{}': parent lost its embedding concurrently",
455 path
456 )));
457 }
458 }
459 let parent_uid = parent_signature.as_ref().map(|s| s.unique_id());
460 let _stripe_guard = parent_uid.as_deref().map(|uid| self.parent_locks.lock(uid));
461
462 let old_sig = match self.path_map.get(path) {
465 Some(r) => r.value().clone(),
466 None => {
467 return Err(IntegrationError::NotFound(format!(
468 "Node at path {} not found",
469 path
470 )))
471 }
472 };
473 if !old_sig.is_stub() {
474 return Ok(false);
475 }
476 let old_uid = old_sig.unique_id();
477
478 let node = self
480 .tensor_network
481 .get_node_by_signature(&old_sig)
482 .ok_or_else(|| {
483 IntegrationError::OperationFailed(format!(
484 "data-only node '{}' missing from the node map",
485 path
486 ))
487 })?;
488 let metadata = node.metadata().clone();
489 let value = node.value().to_vec();
490 let coords = node.semantic_coords().to_vec();
491
492 let level = self.path_depth(path);
493 let signature = self
494 .tensor_network
495 .add_node(metadata, value, parent_signature.as_ref(), level)
496 .ok_or_else(|| {
497 IntegrationError::OperationFailed(format!(
498 "failed to embed node '{}'",
499 path
500 ))
501 })?;
502 let new_uid = signature.unique_id();
503
504 if !coords.is_empty() {
507 self.tensor_network.set_node_semantic(&new_uid, coords);
508 }
509
510 self.id_to_path.insert(new_uid, path.to_string());
512 self.path_map.insert(path.to_string(), signature);
513 self.id_to_path.remove(&old_uid);
514 self.tensor_network.remove_detached_node(&old_uid);
515
516 Ok(true)
517 }
518
519 pub fn get(&self, path: &str) -> IntegrationResult<CompressedNode> {
521 let signature = self
522 .path_map
523 .get(path)
524 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
525
526 self.tensor_network
527 .get_node_by_signature(signature.value())
528 .ok_or_else(|| {
529 IntegrationError::NotFound(format!(
530 "Node with signature {} not found",
531 signature.value().hash()
532 ))
533 })
534 }
535
536 pub fn update_value(&self, path: &str, value: Vec<u8>) -> IntegrationResult<()> {
538 let guard = self
549 .path_map
550 .get(path)
551 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
552 let uid = guard.value().unique_id();
553 let ok = self.tensor_network.update_node_value(&uid, value);
554 drop(guard);
555
556 if ok {
557 Ok(())
558 } else {
559 Err(IntegrationError::NotFound(format!("Node with uid {} not found in network", uid)))
560 }
561 }
562
563 pub fn set_node_metadata(&self, path: &str, key: &str, value: &str) -> IntegrationResult<()> {
565 let guard = self
576 .path_map
577 .get(path)
578 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
579 let uid = guard.value().unique_id();
580 let ok = self.tensor_network.set_node_metadata_entry(&uid, key, value);
581 drop(guard);
582
583 if ok {
584 Ok(())
585 } else {
586 Err(IntegrationError::NotFound(format!("Node with uid {} not found in network", uid)))
587 }
588 }
589
590 pub fn set_semantic(&self, path: &str, coords: Vec<u8>) -> IntegrationResult<()> {
592 let guard = self
603 .path_map
604 .get(path)
605 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
606 let uid = guard.value().unique_id();
607 let ok = self.tensor_network.set_node_semantic(&uid, coords);
608 drop(guard);
609
610 if ok {
611 Ok(())
612 } else {
613 Err(IntegrationError::NotFound(format!("Node with uid {} not found in network", uid)))
614 }
615 }
616
617 pub fn get_semantic(&self, path: &str) -> IntegrationResult<Vec<u8>> {
619 let guard = self
630 .path_map
631 .get(path)
632 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
633 let uid = guard.value().unique_id();
634 let found = self.tensor_network.get_node_semantic(&uid);
635 drop(guard);
636
637 found
638 .ok_or_else(|| IntegrationError::NotFound(format!("Node with uid {} not found in network", uid)))
639 }
640
641 pub fn delete(&self, path: &str) -> IntegrationResult<()> {
651 let node_uid = match self.path_map.get(path) {
653 Some(r) => r.value().unique_id(),
654 None => {
655 return Err(IntegrationError::NotFound(format!(
656 "Node at path {} not found",
657 path
658 )))
659 }
660 };
661 let parent_uid = self
662 .path_ops
663 .parent_path(path)
664 .and_then(|pp| self.path_map.get(&pp).map(|s| s.unique_id()));
665
666 let _guards = match &parent_uid {
669 Some(puid) => {
670 let (g1, g2) = self.parent_locks.lock_two(&node_uid, puid);
671 (Some(g1), g2)
672 }
673 None => (Some(self.parent_locks.lock(&node_uid)), None),
674 };
675
676 if !self.path_map.contains_key(path) {
679 return Err(IntegrationError::NotFound(format!(
680 "Node at path {} not found",
681 path
682 )));
683 }
684 let children = self.list_children(path)?;
685 if !children.is_empty() {
686 return Err(IntegrationError::ValidationFailed(format!(
687 "Cannot delete node at {} because it has {} children",
688 path,
689 children.len()
690 )));
691 }
692
693 if let Some((_, sig)) = self.path_map.remove(path) {
697 let unique_id = sig.unique_id();
698 self.id_to_path.remove(&unique_id);
699 self.tensor_network
700 .unregister_node_with_parent(&unique_id, parent_uid.as_deref());
701 }
702
703 Ok(())
704 }
705
706 pub fn list_children(&self, path: &str) -> IntegrationResult<Vec<CompressedNode>> {
708 let signature = self
709 .path_map
710 .get(path)
711 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
712
713 let children = self.tensor_network.children_of(signature.value());
714
715 let result = children
717 .into_iter()
718 .filter(|child| self.path_map.contains_key(&child.metadata().key))
719 .collect();
720
721 Ok(result)
722 }
723
724 pub fn list_subtree(&self, path: &str) -> IntegrationResult<Vec<String>> {
726 let prefix = if path.ends_with('/') {
727 path.to_string()
728 } else {
729 format!("{}/", path)
730 };
731
732 let mut result = Vec::new();
733 for entry in self.path_map.iter() {
734 let node_path = entry.key();
735 if node_path.as_str() != path && (path == "/" || node_path.starts_with(&prefix)) {
736 result.push(node_path.clone());
737 }
738 }
739
740 Ok(result)
741 }
742
743 fn path_depth(&self, path: &str) -> u32 {
745 if path == "/" {
746 return 0;
747 }
748 self.path_ops.split_path(path).len() as u32
749 }
750
751 pub fn exists(&self, path: &str) -> bool {
753 self.path_map.contains_key(path)
754 }
755
756 pub fn node_count(&self) -> usize {
758 self.path_map.len()
759 }
760
761 pub fn stats(&self) -> HashMap<String, String> {
763 let mut stats = HashMap::new();
764 stats.insert("node_count".to_string(), self.node_count().to_string());
765 stats.insert(
766 "dimension".to_string(),
767 self.config.dimension().to_string(),
768 );
769 stats
770 }
771
772 pub fn validate(&self) -> bool {
777 if self.path_map.is_empty() {
779 return true;
780 }
781
782 if !self.tensor_network.validate_network() {
784 return false;
785 }
786
787 for entry in self.path_map.iter() {
789 let path = entry.key();
790 if path == "/" {
791 continue;
792 }
793 if let Some(parent_path) = self.path_ops.parent_path(path) {
794 if !self.path_map.contains_key(&parent_path) {
795 return false;
796 }
797 }
798 }
799
800 for entry in self.path_map.iter() {
802 let path = entry.key();
803 let sig = entry.value();
804 let uid = sig.unique_id();
805 match self.id_to_path.get(&uid) {
806 Some(reverse_path) if reverse_path.value() == path => {},
807 _ => return false,
808 }
809 }
810 for entry in self.id_to_path.iter() {
811 let uid = entry.key();
812 let path = entry.value();
813 match self.path_map.get(path.as_str()) {
814 Some(sig) if sig.value().unique_id() == *uid => {},
815 _ => return false,
816 }
817 }
818
819 if self.path_map.len() != self.id_to_path.len() {
821 return false;
822 }
823
824 true
825 }
826
827 pub fn tensor_network(&self) -> &HyperbolicTensorNetwork {
829 &self.tensor_network
830 }
831
832 pub fn path_for_id(&self, unique_id: &str) -> Option<String> {
834 self.id_to_path.get(unique_id).map(|r| r.value().clone())
835 }
836
837 pub fn position(&self, path: &str) -> IntegrationResult<super::hyperbolic_geometry::HyperbolicPoint> {
842 let sig = self
843 .path_map
844 .get(path)
845 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
846 let unique_id = sig.value().unique_id();
847 drop(sig);
848 self.tensor_network.get_point(&unique_id).ok_or_else(|| {
849 IntegrationError::OperationFailed(format!(
850 "node {} has no geometric embedding (data-only)",
851 path
852 ))
853 })
854 }
855
856 pub fn path_ops(&self) -> &dyn PathOperations {
858 &*self.path_ops
859 }
860
861 pub fn nearest_neighbor_point(&self, query: &super::hyperbolic_geometry::HyperbolicPoint) -> IntegrationResult<(String, g_math::fixed_point::FixedPoint)> {
866 let (uid, dist) = self.tensor_network
867 .nearest_neighbor_point(query)
868 .ok_or_else(|| IntegrationError::OperationFailed(
869 "No nodes in tree for nearest neighbor query".to_string()
870 ))?;
871
872 let path = self.id_to_path.get(&uid)
873 .ok_or_else(|| IntegrationError::NotFound(
874 format!("No path for unique_id {}", uid)
875 ))?;
876
877 Ok((path.value().clone(), dist))
878 }
879
880 pub fn nearest_neighbor_point_k(&self, query: &super::hyperbolic_geometry::HyperbolicPoint, k: usize) -> IntegrationResult<Vec<(String, g_math::fixed_point::FixedPoint)>> {
889 if k == 0 {
890 return Ok(Vec::new());
891 }
892 let results = self.tensor_network.nearest_neighbor_point_k(query, k);
893 if results.is_empty() {
894 return Err(IntegrationError::OperationFailed(
895 "No nodes in tree for nearest neighbor query".to_string()
896 ));
897 }
898
899 let found = results.len();
900 let mut paths = Vec::with_capacity(found);
901 for (uid, dist) in results {
902 if let Some(p) = self.id_to_path.get(&uid) {
903 paths.push((p.value().clone(), dist));
904 }
905 }
906 Self::note_unmapped(found, paths.len(), "nearest_neighbor_point_k");
907 Ok(paths)
908 }
909
910 fn note_unmapped(found: usize, kept: usize, op: &str) {
922 if kept < found {
923 log::debug!(
924 "{}: {} of {} index hits had no path and were dropped, so the result \
925 is short by that many. Transient during a concurrent delete; \
926 persistent means id_to_path has drifted from the spatial index.",
927 op,
928 found - kept,
929 found,
930 );
931 }
932 }
933
934 pub fn find_nearest(&self, path: &str, k: usize) -> IntegrationResult<Vec<(String, g_math::fixed_point::FixedPoint)>> {
937 let sig = self
938 .path_map
939 .get(path)
940 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
941
942 let unique_id = sig.value().unique_id();
943 drop(sig);
945
946 let point = self
947 .tensor_network
948 .get_point(&unique_id)
949 .ok_or_else(|| IntegrationError::OperationFailed("Point not found in spatial index".to_string()))?;
950
951 let results = self
952 .tensor_network
953 .nearest_neighbor_point_k(&point, k + 1); let mut considered = 0usize;
958 let mut paths = Vec::new();
959 for (uid, dist) in results {
960 if uid == unique_id {
961 continue; }
963 considered += 1;
964 if let Some(p) = self.id_to_path.get(&uid) {
965 paths.push((p.value().clone(), dist));
966 }
967 }
968 Self::note_unmapped(considered, paths.len(), "find_nearest");
969 paths.truncate(k);
970 Ok(paths)
971 }
972
973 pub fn nearest_semantic(
986 &self,
987 query_coords: &[u8],
988 k: usize,
989 dim_range: &Range<usize>,
990 ) -> IntegrationResult<Vec<(String, g_math::fixed_point::FixedPoint)>> {
991 Ok(self.tensor_network.nearest_semantic(query_coords, k, dim_range))
994 }
995
996 pub fn neighbors_semantic(
1001 &self,
1002 path: &str,
1003 k: usize,
1004 dim_range: &Range<usize>,
1005 ) -> IntegrationResult<Vec<(String, g_math::fixed_point::FixedPoint)>> {
1006 let coords = self.get_semantic(path)?;
1007
1008 let results = self.tensor_network.nearest_semantic(&coords, k + 1, dim_range);
1013
1014 let self_key = self
1015 .path_map
1016 .get(path)
1017 .and_then(|r| self.id_to_path.get(&r.value().unique_id()).map(|p| p.value().clone()));
1018
1019 let mut paths = Vec::with_capacity(k);
1020 for (key, dist) in results {
1021 if Some(&key) == self_key.as_ref() {
1022 continue; }
1024 paths.push((key, dist));
1025 if paths.len() >= k {
1026 break;
1027 }
1028 }
1029 Ok(paths)
1030 }
1031
1032 pub fn find_in_radius(&self, path: &str, radius: g_math::fixed_point::FixedPoint) -> IntegrationResult<Vec<(String, g_math::fixed_point::FixedPoint)>> {
1035 let sig = self
1036 .path_map
1037 .get(path)
1038 .ok_or_else(|| IntegrationError::NotFound(format!("Node at path {} not found", path)))?;
1039
1040 let unique_id = sig.value().unique_id();
1041 drop(sig);
1042
1043 let point = self
1044 .tensor_network
1045 .get_point(&unique_id)
1046 .ok_or_else(|| IntegrationError::OperationFailed("Point not found in spatial index".to_string()))?;
1047
1048 let results = self
1049 .tensor_network
1050 .nodes_in_radius(&point, radius);
1051
1052 let mut paths = Vec::new();
1053 for (uid, dist) in results {
1054 if uid == unique_id {
1055 continue; }
1057 if let Some(p) = self.id_to_path.get(&uid) {
1058 paths.push((p.value().clone(), dist));
1059 }
1060 }
1061 Ok(paths)
1062 }
1063}
1064
1065impl Debug for HyperbolicTreeTensor {
1066 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1067 write!(f, "HyperbolicTreeTensor(nodes={})", self.node_count())
1068 }
1069}
1070
1071pub type SharedHTT = Arc<HyperbolicTreeTensor>;
1076
1077#[cfg(test)]
1078mod tests {
1079 use super::*;
1080
1081 #[test]
1082 fn test_path_operations() {
1083 let path_ops = DefaultPathOps;
1084
1085 let components = path_ops.split_path("/a/b/c");
1086 assert_eq!(
1087 components,
1088 vec!["a".to_string(), "b".to_string(), "c".to_string()]
1089 );
1090
1091 let path = path_ops.join_path(&["a".to_string(), "b".to_string(), "c".to_string()]);
1092 assert_eq!(path, "/a/b/c");
1093
1094 assert_eq!(path_ops.parent_path("/a/b/c"), Some("/a/b".to_string()));
1095 assert_eq!(path_ops.parent_path("/a"), Some("/".to_string()));
1096 assert_eq!(path_ops.parent_path("/"), None);
1097
1098 assert_eq!(path_ops.last_component("/a/b/c"), Some("c".to_string()));
1099 assert_eq!(path_ops.last_component("/a"), Some("a".to_string()));
1100 assert_eq!(path_ops.last_component("/"), None);
1101 }
1102
1103 #[test]
1104 fn test_tree_tensor_creation() {
1105 let config = HTTConfig::default();
1106 let tree = HyperbolicTreeTensor::new(config);
1107 assert_eq!(tree.node_count(), 0);
1108 }
1109
1110 #[test]
1111 fn test_tree_tensor_operations() {
1112 let config = HTTConfig::default();
1113 let tree = HyperbolicTreeTensor::new(config);
1114
1115 tree.insert("/", vec![], None).unwrap();
1117 assert_eq!(tree.node_count(), 1);
1118 assert!(tree.exists("/"));
1119
1120 let root = tree.get("/").unwrap();
1122 assert_eq!(root.metadata().key, "/");
1123
1124 tree.insert("/child1", b"child1 data".to_vec(), None).unwrap();
1126 tree.insert("/child2", b"child2 data".to_vec(), None).unwrap();
1127 assert_eq!(tree.node_count(), 3);
1128
1129 tree.insert("/child1/grandchild", b"grandchild data".to_vec(), None).unwrap();
1131 assert_eq!(tree.node_count(), 4);
1132
1133 let children = tree.list_children("/").unwrap();
1135 assert_eq!(children.len(), 2);
1136
1137 let child_keys: Vec<&str> = children.iter().map(|c| c.metadata().key.as_str()).collect();
1138 assert!(child_keys.contains(&"/child1"));
1139 assert!(child_keys.contains(&"/child2"));
1140
1141 tree.update_value("/child1", b"updated data".to_vec()).unwrap();
1143 let updated = tree.get("/child1").unwrap();
1144 assert_eq!(updated.value(), b"updated data");
1145
1146 let subtree = tree.list_subtree("/").unwrap();
1148 assert_eq!(subtree.len(), 3); let result = tree.delete("/child1");
1152 assert!(result.is_err());
1153
1154 tree.delete("/child1/grandchild").unwrap();
1156 assert_eq!(tree.node_count(), 3);
1157
1158 tree.delete("/child1").unwrap();
1160 assert_eq!(tree.node_count(), 2);
1161 }
1162
1163 #[test]
1164 fn test_tree_validation() {
1165 let config = HTTConfig::default();
1166 let tree = HyperbolicTreeTensor::new(config);
1167
1168 assert!(tree.validate());
1170
1171 tree.insert("/", vec![], None).unwrap();
1173 tree.insert("/child", b"child data".to_vec(), None).unwrap();
1174
1175 assert!(tree.validate());
1177 }
1178
1179 #[test]
1180 fn test_id_to_path_mapping() {
1181 let config = HTTConfig::default();
1182 let tree = HyperbolicTreeTensor::new(config);
1183
1184 tree.insert("/", vec![], None).unwrap();
1185 tree.insert("/test", b"test".to_vec(), None).unwrap();
1186
1187 let uid = tree.path_map.get("/test").unwrap().value().unique_id();
1189 let resolved_path = tree.path_for_id(&uid);
1190 assert_eq!(resolved_path, Some("/test".to_string()));
1191 }
1192
1193 #[test]
1194 fn test_find_nearest() {
1195 let config = HTTConfig::default();
1196 let tree = HyperbolicTreeTensor::new(config);
1197
1198 tree.insert("/", vec![], None).unwrap();
1199 tree.insert("/a", b"a".to_vec(), None).unwrap();
1200 tree.insert("/b", b"b".to_vec(), None).unwrap();
1201 tree.insert("/c", b"c".to_vec(), None).unwrap();
1202 tree.insert("/a/child", b"ac".to_vec(), None).unwrap();
1203
1204 let nearest = tree.find_nearest("/a", 3).unwrap();
1206 assert!(!nearest.is_empty());
1207 assert!(nearest.len() <= 3);
1208
1209 let paths: Vec<&str> = nearest.iter().map(|(p, _)| p.as_str()).collect();
1211 assert!(!paths.contains(&"/a"));
1212
1213 for i in 1..nearest.len() {
1215 assert!(nearest[i].1 >= nearest[i - 1].1);
1216 }
1217 }
1218
1219 #[test]
1220 fn test_find_in_radius() {
1221 use g_math::fixed_point::FixedPoint;
1222
1223 let config = HTTConfig::default();
1224 let tree = HyperbolicTreeTensor::new(config);
1225
1226 tree.insert("/", vec![], None).unwrap();
1227 tree.insert("/a", b"a".to_vec(), None).unwrap();
1228 tree.insert("/b", b"b".to_vec(), None).unwrap();
1229
1230 let large_radius = FixedPoint::from_int(10);
1232 let results = tree.find_in_radius("/", large_radius).unwrap();
1233 assert!(results.len() >= 2, "Expected at least 2 nodes within large radius, got {}", results.len());
1234
1235 let tiny_radius = FixedPoint::from_int(1) / FixedPoint::from_int(10000);
1237 let results = tree.find_in_radius("/", tiny_radius).unwrap();
1238 assert!(results.len() <= 2);
1241 }
1242
1243 #[test]
1244 fn test_delete_unregisters_spatial() {
1245 let config = HTTConfig::default();
1246 let tree = HyperbolicTreeTensor::new(config);
1247
1248 tree.insert("/", vec![], None).unwrap();
1249 tree.insert("/leaf", b"leaf".to_vec(), None).unwrap();
1250
1251 let unique_id = tree.path_map.get("/leaf").unwrap().value().unique_id();
1253
1254 assert!(tree.tensor_network().get_point(&unique_id).is_some());
1256
1257 tree.delete("/leaf").unwrap();
1259
1260 assert!(tree.tensor_network().get_point(&unique_id).is_none());
1262 assert!(tree.path_for_id(&unique_id).is_none());
1263 }
1264}