use std::collections::BTreeMap;
pub struct HyperEdgeReverseIndex {
forward: BTreeMap<u64, Vec<u64>>,
reverse: BTreeMap<u64, Vec<u64>>,
}
impl HyperEdgeReverseIndex {
pub fn new() -> Self {
Self {
forward: BTreeMap::new(),
reverse: BTreeMap::new(),
}
}
pub fn add(&mut self, hyperedge_id: u64, participant_id: u64) {
let fwd = self.forward.entry(hyperedge_id).or_default();
if !fwd.contains(&participant_id) {
fwd.push(participant_id);
}
let rev = self.reverse.entry(participant_id).or_default();
if !rev.contains(&hyperedge_id) {
rev.push(hyperedge_id);
}
}
pub fn remove(&mut self, hyperedge_id: u64, participant_id: u64) -> bool {
let removed = if let Some(fwd) = self.forward.get_mut(&hyperedge_id) {
let before = fwd.len();
fwd.retain(|&id| id != participant_id);
fwd.len() < before
} else {
false
};
if removed {
if let Some(rev) = self.reverse.get_mut(&participant_id) {
rev.retain(|&id| id != hyperedge_id);
if rev.is_empty() {
self.reverse.remove(&participant_id);
}
}
if let Some(fwd) = self.forward.get(&hyperedge_id) {
if fwd.is_empty() {
self.forward.remove(&hyperedge_id);
}
}
}
removed
}
pub fn remove_all(&mut self, hyperedge_id: u64) -> Vec<u64> {
let participant_ids = self.forward.remove(&hyperedge_id).unwrap_or_default();
for &pid in &participant_ids {
if let Some(rev) = self.reverse.get_mut(&pid) {
rev.retain(|&hid| hid != hyperedge_id);
if rev.is_empty() {
self.reverse.remove(&pid);
}
}
}
participant_ids
}
pub fn hyperedges_for(&self, participant_id: u64) -> Vec<u64> {
self.reverse
.get(&participant_id)
.cloned()
.unwrap_or_default()
}
pub fn participants_for(&self, hyperedge_id: u64) -> Vec<u64> {
self.forward.get(&hyperedge_id).cloned().unwrap_or_default()
}
}
impl Default for HyperEdgeReverseIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reverse_index_new_is_empty() {
let idx = HyperEdgeReverseIndex::new();
assert!(idx.hyperedges_for(1).is_empty());
assert!(idx.participants_for(1).is_empty());
}
#[test]
fn test_add_participant() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
let participants = idx.participants_for(1);
assert_eq!(participants, vec![10]);
}
#[test]
fn test_add_multiple_participants() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(1, 20);
idx.add(1, 30);
let participants = idx.participants_for(1);
assert_eq!(participants.len(), 3);
assert!(participants.contains(&10));
assert!(participants.contains(&20));
assert!(participants.contains(&30));
}
#[test]
fn test_add_duplicate_is_idempotent() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(1, 10); let participants = idx.participants_for(1);
assert_eq!(participants.len(), 1);
}
#[test]
fn test_hyperedges_for_entity() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(2, 10);
idx.add(3, 10);
let hyperedges = idx.hyperedges_for(10);
assert_eq!(hyperedges.len(), 3);
assert!(hyperedges.contains(&1));
assert!(hyperedges.contains(&2));
assert!(hyperedges.contains(&3));
}
#[test]
fn test_participants_for_hyperedge() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(1, 20);
let participants = idx.participants_for(1);
assert_eq!(participants.len(), 2);
}
#[test]
fn test_remove_participant() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(1, 20);
let removed = idx.remove(1, 10);
assert!(removed);
let participants = idx.participants_for(1);
assert_eq!(participants, vec![20]);
assert!(idx.hyperedges_for(10).is_empty());
}
#[test]
fn test_remove_nonexistent_participant() {
let mut idx = HyperEdgeReverseIndex::new();
let removed = idx.remove(1, 10);
assert!(!removed);
}
#[test]
fn test_remove_all_participants() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.add(1, 20);
let removed = idx.remove_all(1);
assert_eq!(removed.len(), 2);
assert!(idx.participants_for(1).is_empty());
assert!(idx.hyperedges_for(10).is_empty());
assert!(idx.hyperedges_for(20).is_empty());
}
#[test]
fn test_empty_list_cleanup() {
let mut idx = HyperEdgeReverseIndex::new();
idx.add(1, 10);
idx.remove(1, 10);
assert!(idx.participants_for(1).is_empty());
assert!(idx.hyperedges_for(10).is_empty());
}
#[test]
fn test_remove_all_from_empty() {
let mut idx = HyperEdgeReverseIndex::new();
let removed = idx.remove_all(999);
assert!(removed.is_empty());
}
#[test]
fn test_reverse_index_default() {
let idx = HyperEdgeReverseIndex::default();
assert!(idx.hyperedges_for(1).is_empty());
}
}