use super::alpha::AlphaNode;
use super::facts::TypedFacts;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Eq)]
pub struct AlphaPattern {
pub field: String,
pub operator: String,
pub value: String,
}
impl PartialEq for AlphaPattern {
fn eq(&self, other: &Self) -> bool {
self.field == other.field && self.operator == other.operator && self.value == other.value
}
}
impl Hash for AlphaPattern {
fn hash<H: Hasher>(&self, state: &mut H) {
self.field.hash(state);
self.operator.hash(state);
self.value.hash(state);
}
}
impl From<&AlphaNode> for AlphaPattern {
fn from(node: &AlphaNode) -> Self {
Self {
field: node.field.clone(),
operator: node.operator.clone(),
value: node.value.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct SharedAlphaNode {
pub node: AlphaNode,
pub rule_indices: Vec<usize>,
pub ref_count: usize,
}
impl SharedAlphaNode {
pub fn new(node: AlphaNode, rule_idx: usize) -> Self {
Self {
node,
rule_indices: vec![rule_idx],
ref_count: 1,
}
}
pub fn add_reference(&mut self, rule_idx: usize) {
self.rule_indices.push(rule_idx);
self.ref_count += 1;
}
pub fn remove_reference(&mut self, rule_idx: usize) {
self.rule_indices.retain(|&idx| idx != rule_idx);
self.ref_count = self.rule_indices.len();
}
}
pub struct NodeSharingRegistry {
shared_nodes: HashMap<AlphaPattern, SharedAlphaNode>,
total_nodes: usize,
shared_count: usize,
}
impl NodeSharingRegistry {
pub fn new() -> Self {
Self {
shared_nodes: HashMap::new(),
total_nodes: 0,
shared_count: 0,
}
}
pub fn register(&mut self, node: &AlphaNode, rule_idx: usize) -> &SharedAlphaNode {
let pattern = AlphaPattern::from(node);
self.total_nodes += 1;
self.shared_nodes
.entry(pattern.clone())
.and_modify(|shared| {
shared.add_reference(rule_idx);
self.shared_count += 1;
})
.or_insert_with(|| SharedAlphaNode::new(node.clone(), rule_idx));
self.shared_nodes.get(&pattern).unwrap()
}
pub fn get(&self, pattern: &AlphaPattern) -> Option<&SharedAlphaNode> {
self.shared_nodes.get(pattern)
}
pub fn unregister_rule(&mut self, rule_idx: usize) {
let patterns_to_remove: Vec<AlphaPattern> = self
.shared_nodes
.iter_mut()
.filter_map(|(pattern, shared)| {
shared.remove_reference(rule_idx);
if shared.ref_count == 0 {
Some(pattern.clone())
} else {
None
}
})
.collect();
for pattern in patterns_to_remove {
self.shared_nodes.remove(&pattern);
}
}
pub fn stats(&self) -> NodeSharingStats {
NodeSharingStats {
total_nodes: self.total_nodes,
unique_patterns: self.shared_nodes.len(),
shared_instances: self.shared_count,
memory_saved_percent: if self.total_nodes > 0 {
((self.total_nodes - self.shared_nodes.len()) as f64 / self.total_nodes as f64)
* 100.0
} else {
0.0
},
}
}
}
impl Default for NodeSharingRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct NodeSharingStats {
pub total_nodes: usize,
pub unique_patterns: usize,
pub shared_instances: usize,
pub memory_saved_percent: f64,
}
impl std::fmt::Display for NodeSharingStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Node Sharing Stats:\n\
- Total nodes: {}\n\
- Unique patterns: {}\n\
- Shared instances: {}\n\
- Memory saved: {:.1}%",
self.total_nodes,
self.unique_patterns,
self.shared_instances,
self.memory_saved_percent
)
}
}
#[derive(Debug, Clone)]
pub struct CompactAlphaMemory {
facts: HashSet<FactKey>,
ref_counts: HashMap<FactKey, usize>,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FactKey {
pub hash: u64,
}
impl FactKey {
pub fn from_facts(facts: &TypedFacts) -> Self {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
let mut keys: Vec<_> = facts.get_all().keys().collect();
keys.sort();
for key in keys {
key.hash(&mut hasher);
if let Some(value) = facts.get_all().get(key) {
format!("{:?}", value).hash(&mut hasher);
}
}
Self {
hash: hasher.finish(),
}
}
}
impl CompactAlphaMemory {
pub fn new() -> Self {
Self {
facts: HashSet::new(),
ref_counts: HashMap::new(),
}
}
pub fn add(&mut self, fact: &TypedFacts) {
let key = FactKey::from_facts(fact);
if self.facts.insert(key.clone()) {
self.ref_counts.insert(key, 1);
} else {
*self.ref_counts.get_mut(&key).unwrap() += 1;
}
}
pub fn remove(&mut self, fact: &TypedFacts) -> bool {
let key = FactKey::from_facts(fact);
if let Some(count) = self.ref_counts.get_mut(&key) {
*count -= 1;
if *count == 0 {
self.ref_counts.remove(&key);
self.facts.remove(&key);
return true;
}
}
false
}
pub fn contains(&self, fact: &TypedFacts) -> bool {
let key = FactKey::from_facts(fact);
self.facts.contains(&key)
}
pub fn len(&self) -> usize {
self.facts.len()
}
pub fn is_empty(&self) -> bool {
self.facts.is_empty()
}
pub fn total_refs(&self) -> usize {
self.ref_counts.values().sum()
}
pub fn memory_savings(&self) -> f64 {
let total = self.total_refs();
let unique = self.len();
if total > 0 {
((total - unique) as f64 / total as f64) * 100.0
} else {
0.0
}
}
}
impl Default for CompactAlphaMemory {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct BetaMemoryIndex {
index: HashMap<String, Vec<usize>>,
join_key: String,
}
impl BetaMemoryIndex {
pub fn new(join_key: String) -> Self {
Self {
index: HashMap::new(),
join_key,
}
}
pub fn add(&mut self, fact: &TypedFacts, fact_idx: usize) {
if let Some(value) = fact.get(&self.join_key) {
let key = format!("{:?}", value);
self.index.entry(key).or_default().push(fact_idx);
}
}
pub fn lookup(&self, key_value: &str) -> &[usize] {
self.index
.get(key_value)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn remove(&mut self, fact: &TypedFacts, fact_idx: usize) {
if let Some(value) = fact.get(&self.join_key) {
let key = format!("{:?}", value);
if let Some(indices) = self.index.get_mut(&key) {
indices.retain(|&idx| idx != fact_idx);
if indices.is_empty() {
self.index.remove(&key);
}
}
}
}
pub fn size(&self) -> usize {
self.index.len()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub fact: Option<TypedFacts>,
pub parent: Option<Box<Token>>,
pub id: usize,
}
impl Token {
pub fn new(id: usize) -> Self {
Self {
fact: None,
parent: None,
id,
}
}
pub fn with_fact(id: usize, fact: TypedFacts) -> Self {
Self {
fact: Some(fact),
parent: None,
id,
}
}
pub fn reset(&mut self) {
self.fact = None;
self.parent = None;
}
pub fn set_fact(&mut self, fact: TypedFacts) {
self.fact = Some(fact);
}
}
pub struct TokenPool {
available: Vec<Token>,
in_use: HashSet<usize>,
next_id: usize,
total_created: usize,
total_reused: usize,
}
impl TokenPool {
pub fn new(initial_capacity: usize) -> Self {
let available = (0..initial_capacity).map(Token::new).collect();
Self {
available,
in_use: HashSet::new(),
next_id: initial_capacity,
total_created: initial_capacity,
total_reused: 0,
}
}
pub fn acquire(&mut self) -> Token {
if let Some(mut token) = self.available.pop() {
token.reset();
self.in_use.insert(token.id);
self.total_reused += 1;
token
} else {
let token = Token::new(self.next_id);
self.in_use.insert(token.id);
self.next_id += 1;
self.total_created += 1;
token
}
}
pub fn acquire_with_fact(&mut self, fact: TypedFacts) -> Token {
let mut token = self.acquire();
token.set_fact(fact);
token
}
pub fn release(&mut self, mut token: Token) {
if self.in_use.remove(&token.id) {
token.reset();
self.available.push(token);
}
}
pub fn stats(&self) -> TokenPoolStats {
TokenPoolStats {
available: self.available.len(),
in_use: self.in_use.len(),
total_created: self.total_created,
total_reused: self.total_reused,
reuse_rate: if self.total_created > 0 {
(self.total_reused as f64 / (self.total_created + self.total_reused) as f64) * 100.0
} else {
0.0
},
}
}
pub fn clear(&mut self) {
self.available.clear();
self.in_use.clear();
}
}
impl Default for TokenPool {
fn default() -> Self {
Self::new(100) }
}
#[derive(Debug, Clone)]
pub struct TokenPoolStats {
pub available: usize,
pub in_use: usize,
pub total_created: usize,
pub total_reused: usize,
pub reuse_rate: f64,
}
impl std::fmt::Display for TokenPoolStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Token Pool Stats:\n\
- Available: {}\n\
- In use: {}\n\
- Total created: {}\n\
- Total reused: {}\n\
- Reuse rate: {:.1}%",
self.available, self.in_use, self.total_created, self.total_reused, self.reuse_rate
)
}
}
pub struct OptimizationManager {
pub node_sharing: NodeSharingRegistry,
pub token_pool: TokenPool,
enabled: bool,
}
impl OptimizationManager {
pub fn new() -> Self {
Self {
node_sharing: NodeSharingRegistry::new(),
token_pool: TokenPool::new(1000),
enabled: true,
}
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn stats(&self) -> OptimizationStats {
OptimizationStats {
node_sharing: self.node_sharing.stats(),
token_pool: self.token_pool.stats(),
}
}
}
impl Default for OptimizationManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct OptimizationStats {
pub node_sharing: NodeSharingStats,
pub token_pool: TokenPoolStats,
}
impl std::fmt::Display for OptimizationStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"=== RETE Optimization Statistics ===\n\n{}\n\n{}",
self.node_sharing, self.token_pool
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_sharing_basic() {
let mut registry = NodeSharingRegistry::new();
let node1 = AlphaNode {
field: "age".to_string(),
operator: ">".to_string(),
value: "18".to_string(),
};
let node2 = AlphaNode {
field: "age".to_string(),
operator: ">".to_string(),
value: "18".to_string(),
};
registry.register(&node1, 0);
registry.register(&node2, 1);
let stats = registry.stats();
assert_eq!(stats.unique_patterns, 1); assert_eq!(stats.shared_instances, 1); }
#[test]
fn test_compact_alpha_memory() {
let mut memory = CompactAlphaMemory::new();
let mut fact1 = TypedFacts::new();
fact1.set("name", "Alice");
fact1.set("age", 30);
let mut fact2 = TypedFacts::new();
fact2.set("name", "Alice");
fact2.set("age", 30);
memory.add(&fact1);
memory.add(&fact2);
assert_eq!(memory.len(), 1); assert_eq!(memory.total_refs(), 2); assert!(memory.memory_savings() > 0.0);
}
#[test]
fn test_beta_memory_index() {
let mut index = BetaMemoryIndex::new("user_id".to_string());
let mut fact1 = TypedFacts::new();
fact1.set("user_id", "user123");
fact1.set("action", "login");
let mut fact2 = TypedFacts::new();
fact2.set("user_id", "user123");
fact2.set("action", "purchase");
index.add(&fact1, 0);
index.add(&fact2, 1);
let results = index.lookup("String(\"user123\")");
assert_eq!(results.len(), 2); }
#[test]
fn test_token_pool_basic() {
let mut pool = TokenPool::new(10);
let token1 = pool.acquire();
assert_eq!(pool.stats().in_use, 1);
pool.release(token1);
assert_eq!(pool.stats().available, 10);
}
#[test]
fn test_token_pool_reuse() {
let mut pool = TokenPool::new(5);
let mut tokens = Vec::new();
for _ in 0..5 {
tokens.push(pool.acquire());
}
assert_eq!(pool.stats().available, 0);
for token in tokens {
pool.release(token);
}
let token = pool.acquire();
let stats = pool.stats();
assert!(stats.total_reused > 0);
assert!(stats.reuse_rate > 0.0);
pool.release(token);
}
#[test]
fn test_optimization_manager() {
let mut manager = OptimizationManager::new();
assert!(manager.is_enabled());
let node = AlphaNode {
field: "score".to_string(),
operator: ">".to_string(),
value: "100".to_string(),
};
manager.node_sharing.register(&node, 0);
manager.node_sharing.register(&node, 1);
let token = manager.token_pool.acquire();
manager.token_pool.release(token);
let stats = manager.stats();
println!("{}", stats);
}
}