use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use crate::types::{BduError, BduResult};
use feagi_structures::genomic::cortical_area::CorticalID;
use feagi_structures::genomic::BrainRegion;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrainRegionHierarchy {
regions: HashMap<String, BrainRegion>,
#[serde(default)]
parent_map: HashMap<String, String>,
#[serde(default)]
children_map: HashMap<String, HashSet<String>>,
root_id: Option<String>,
}
impl BrainRegionHierarchy {
pub fn new() -> Self {
Self {
regions: HashMap::new(),
parent_map: HashMap::new(),
children_map: HashMap::new(),
root_id: None,
}
}
pub fn with_root(root: BrainRegion) -> Self {
let mut hierarchy = Self::new();
let root_id = root.region_id;
hierarchy.regions.insert(root_id.to_string(), root);
hierarchy.root_id = Some(root_id.to_string());
hierarchy
}
pub fn add_region(&mut self, region: BrainRegion, parent_id: Option<String>) -> BduResult<()> {
let region_id = region.region_id;
let region_id_str = region_id.to_string();
if parent_id.as_deref() == Some(region_id_str.as_str()) {
return Err(BduError::InvalidArea(
"Region cannot be its own parent".to_string(),
));
}
if self.regions.contains_key(®ion_id_str) {
return Err(BduError::InvalidArea(format!(
"Region {} already exists",
region_id
)));
}
let resolved_parent: Option<String> = match parent_id {
Some(p) => Some(p),
None => {
if self.root_id.is_none() {
None
} else {
self.root_id.clone()
}
}
};
if let Some(ref parent) = resolved_parent {
if !self.regions.contains_key(parent) {
return Err(BduError::InvalidArea(format!(
"Parent region {} does not exist",
parent
)));
}
}
self.regions.insert(region_id_str.clone(), region);
if let Some(parent) = resolved_parent {
self.parent_map
.insert(region_id_str.clone(), parent.clone());
self.children_map
.entry(parent)
.or_default()
.insert(region_id_str.clone());
} else if self.root_id.is_none() {
self.root_id = Some(region_id_str);
}
Ok(())
}
pub fn remove_region(&mut self, region_id: &str) -> BduResult<()> {
if self.root_id.as_deref() == Some(region_id) {
return Err(BduError::InvalidArea(
"Cannot remove root region".to_string(),
));
}
if !self.regions.contains_key(region_id) {
return Err(BduError::InvalidArea(format!(
"Region {} does not exist",
region_id
)));
}
let parent_id = self.parent_map.get(region_id).cloned();
let children = self
.children_map
.get(region_id)
.cloned()
.unwrap_or_default();
if let Some(parent) = &parent_id {
for child in &children {
self.parent_map.insert(child.clone(), parent.clone());
self.children_map
.entry(parent.clone())
.or_default()
.insert(child.clone());
}
}
if let Some(parent) = &parent_id {
if let Some(parent_children) = self.children_map.get_mut(parent) {
parent_children.remove(region_id);
}
}
self.regions.remove(region_id);
self.parent_map.remove(region_id);
self.children_map.remove(region_id);
Ok(())
}
pub fn change_parent(&mut self, region_id: &str, new_parent_id: &str) -> BduResult<()> {
if region_id == new_parent_id {
return Err(BduError::InvalidArea(
"Region cannot be its own parent".to_string(),
));
}
if !self.regions.contains_key(region_id) {
return Err(BduError::InvalidArea(format!(
"Region {} does not exist",
region_id
)));
}
if !self.regions.contains_key(new_parent_id) {
return Err(BduError::InvalidArea(format!(
"Parent region {} does not exist",
new_parent_id
)));
}
if self.is_descendant(new_parent_id, region_id) {
return Err(BduError::InvalidArea(
"Cannot create cycle in hierarchy".to_string(),
));
}
if let Some(old_parent) = self.parent_map.get(region_id) {
if let Some(old_parent_children) = self.children_map.get_mut(old_parent) {
old_parent_children.remove(region_id);
}
}
self.parent_map
.insert(region_id.to_string(), new_parent_id.to_string());
self.children_map
.entry(new_parent_id.to_string())
.or_default()
.insert(region_id.to_string());
Ok(())
}
fn is_descendant(&self, potential_descendant: &str, ancestor: &str) -> bool {
let mut current = potential_descendant;
while let Some(parent) = self.parent_map.get(current) {
if parent == ancestor {
return true;
}
current = parent;
}
false
}
pub fn get_region(&self, region_id: &str) -> Option<&BrainRegion> {
self.regions.get(region_id)
}
pub fn get_region_mut(&mut self, region_id: &str) -> Option<&mut BrainRegion> {
self.regions.get_mut(region_id)
}
pub fn get_parent(&self, region_id: &str) -> Option<&String> {
self.parent_map.get(region_id)
}
pub fn find_region_containing_area(&self, cortical_id: &CorticalID) -> Option<String> {
for (region_id, region) in &self.regions {
if region.cortical_areas.contains(cortical_id) {
return Some(region_id.clone());
}
}
None
}
pub fn get_root_region_id(&self) -> Option<String> {
for region_id in self.regions.keys() {
if !self.parent_map.contains_key(region_id) {
return Some(region_id.clone());
}
}
None
}
pub fn get_children(&self, region_id: &str) -> Vec<&String> {
self.children_map
.get(region_id)
.map(|children| children.iter().collect())
.unwrap_or_default()
}
pub fn get_all_descendants(&self, region_id: &str) -> Vec<&String> {
let mut descendants = Vec::new();
let mut to_visit = vec![region_id];
while let Some(current) = to_visit.pop() {
if let Some(children) = self.children_map.get(current) {
for child in children {
descendants.push(child);
to_visit.push(child);
}
}
}
descendants
}
pub fn get_all_areas_recursive(&self, region_id: &str) -> HashSet<String> {
let mut areas = HashSet::new();
if let Some(region) = self.regions.get(region_id) {
areas.extend(region.cortical_areas.iter().map(|id| id.to_string()));
}
for descendant_id in self.get_all_descendants(region_id) {
if let Some(region) = self.regions.get(descendant_id) {
areas.extend(region.cortical_areas.iter().map(|id| id.to_string()));
}
}
areas
}
pub fn rename_cortical_area_id(&mut self, old_id: &CorticalID, new_id: CorticalID) {
for region in self.regions.values_mut() {
if region.cortical_areas.remove(old_id) {
region.cortical_areas.insert(new_id);
}
}
}
pub fn get_root_id(&self) -> Option<&String> {
self.root_id.as_ref()
}
pub fn get_all_region_ids(&self) -> Vec<&String> {
self.regions.keys().collect()
}
pub fn region_count(&self) -> usize {
self.regions.len()
}
pub fn get_all_regions(&self) -> HashMap<String, BrainRegion> {
self.regions.clone()
}
}
impl Default for BrainRegionHierarchy {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use feagi_structures::genomic::brain_regions::{RegionID, RegionType};
#[test]
fn test_hierarchy_creation() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let hierarchy = BrainRegionHierarchy::with_root(root);
assert_eq!(hierarchy.region_count(), 1);
assert!(hierarchy.get_root_id().is_some());
}
#[test]
fn test_add_regions() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let mut hierarchy = BrainRegionHierarchy::with_root(root);
let root_id = hierarchy.get_root_id().unwrap().clone();
let visual =
BrainRegion::new(RegionID::new(), "Visual".to_string(), RegionType::Undefined).unwrap();
let visual_id = visual.region_id.to_string();
hierarchy.add_region(visual, Some(root_id.clone())).unwrap();
assert_eq!(hierarchy.region_count(), 2);
assert_eq!(hierarchy.get_parent(&visual_id), Some(&root_id));
}
#[test]
fn test_remove_region() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let mut hierarchy = BrainRegionHierarchy::with_root(root);
let root_id = hierarchy.get_root_id().unwrap().clone();
let visual =
BrainRegion::new(RegionID::new(), "Visual".to_string(), RegionType::Undefined).unwrap();
let visual_id = visual.region_id.to_string();
let v1 =
BrainRegion::new(RegionID::new(), "V1".to_string(), RegionType::Undefined).unwrap();
let v1_id = v1.region_id.to_string();
hierarchy.add_region(visual, Some(root_id.clone())).unwrap();
hierarchy.add_region(v1, Some(visual_id.clone())).unwrap();
hierarchy.remove_region(&visual_id).unwrap();
assert_eq!(hierarchy.region_count(), 2);
assert_eq!(hierarchy.get_parent(&v1_id), Some(&root_id));
}
#[test]
fn test_change_parent() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let mut hierarchy = BrainRegionHierarchy::with_root(root);
let root_id = hierarchy.get_root_id().unwrap().clone();
let visual =
BrainRegion::new(RegionID::new(), "Visual".to_string(), RegionType::Undefined).unwrap();
let visual_id = visual.region_id.to_string();
let motor =
BrainRegion::new(RegionID::new(), "Motor".to_string(), RegionType::Undefined).unwrap();
let motor_id = motor.region_id.to_string();
let v1 =
BrainRegion::new(RegionID::new(), "V1".to_string(), RegionType::Undefined).unwrap();
let v1_id = v1.region_id.to_string();
hierarchy.add_region(visual, Some(root_id.clone())).unwrap();
hierarchy.add_region(motor, Some(root_id.clone())).unwrap();
hierarchy.add_region(v1, Some(visual_id.clone())).unwrap();
hierarchy.change_parent(&v1_id, &motor_id).unwrap();
assert_eq!(hierarchy.get_parent(&v1_id), Some(&motor_id));
assert!(!hierarchy.get_children(&visual_id).contains(&&v1_id));
assert!(hierarchy.get_children(&motor_id).contains(&&v1_id));
}
#[test]
fn test_get_descendants() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let mut hierarchy = BrainRegionHierarchy::with_root(root);
let root_id = hierarchy.get_root_id().unwrap().clone();
let visual =
BrainRegion::new(RegionID::new(), "Visual".to_string(), RegionType::Undefined).unwrap();
let visual_id = visual.region_id.to_string();
let v1 =
BrainRegion::new(RegionID::new(), "V1".to_string(), RegionType::Undefined).unwrap();
let v2 =
BrainRegion::new(RegionID::new(), "V2".to_string(), RegionType::Undefined).unwrap();
hierarchy.add_region(visual, Some(root_id.clone())).unwrap();
hierarchy.add_region(v1, Some(visual_id.clone())).unwrap();
hierarchy.add_region(v2, Some(visual_id.clone())).unwrap();
let descendants = hierarchy.get_all_descendants(&root_id);
assert_eq!(descendants.len(), 3);
let visual_descendants = hierarchy.get_all_descendants(&visual_id);
assert_eq!(visual_descendants.len(), 2); }
#[test]
fn test_cycle_prevention() {
let root =
BrainRegion::new(RegionID::new(), "Root".to_string(), RegionType::Undefined).unwrap();
let mut hierarchy = BrainRegionHierarchy::with_root(root);
let root_id = hierarchy.get_root_id().unwrap().clone();
let visual =
BrainRegion::new(RegionID::new(), "Visual".to_string(), RegionType::Undefined).unwrap();
let visual_id = visual.region_id.to_string();
let v1 =
BrainRegion::new(RegionID::new(), "V1".to_string(), RegionType::Undefined).unwrap();
let v1_id = v1.region_id.to_string();
hierarchy.add_region(visual, Some(root_id.clone())).unwrap();
hierarchy.add_region(v1, Some(visual_id.clone())).unwrap();
let result = hierarchy.change_parent(&visual_id, &v1_id);
assert!(result.is_err());
}
}