use crate::eval::{Value, Generation};
use crate::utils::SymbolId;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::hash::{Hash, Hasher};
#[derive(Debug)]
struct VariableCache {
entries: HashMap<SymbolId, CacheEntry>,
max_size: usize,
hits: usize,
misses: usize,
generation: Generation,
}
#[derive(Debug, Clone)]
struct CacheEntry {
value: Value,
generation: Generation,
depth: usize,
access_count: usize,
}
#[derive(Debug)]
pub struct OptimizedEnvironment {
bindings: HashMap<SymbolId, Value>,
parent: Option<Arc<OptimizedEnvironment>>,
cache: RwLock<VariableCache>,
depth: usize,
generation: Generation,
id: u64,
}
#[derive(Debug, Clone)]
pub struct EnvironmentStats {
pub total_lookups: usize,
pub cache_hits: usize,
pub cache_misses: usize,
pub hit_rate: f64,
pub avg_lookup_depth: f64,
pub hot_variables: Vec<(SymbolId, usize)>,
}
impl VariableCache {
fn new(max_size: usize) -> Self {
Self {
entries: HashMap::new(),
max_size,
hits: 0,
misses: 0,
generation: 0,
}
}
fn get(&mut self, symbol: SymbolId, current_generation: Generation) -> Option<Value> {
let should_remove = if let Some(entry) = self.entries.get(&symbol) {
entry.generation > current_generation
} else {
false
};
if should_remove {
self.entries.remove(&symbol);
self.misses += 1;
return None;
}
if let Some(entry) = self.entries.get_mut(&symbol) {
entry.access_count += 1;
self.hits += 1;
Some(entry.value.clone())
} else {
self.misses += 1;
None
}
}
fn insert(&mut self, symbol: SymbolId, value: Value, generation: Generation, depth: usize) {
if self.entries.len() >= self.max_size {
self.evict_lru();
}
let entry = CacheEntry {
value,
generation,
depth,
access_count: 1,
};
self.entries.insert(symbol, entry);
}
fn evict_lru(&mut self) {
if let Some((&symbol_to_remove, _)) = self.entries.iter()
.min_by_key(|(_, entry)| entry.access_count) {
self.entries.remove(&symbol_to_remove);
}
}
fn invalidate(&mut self, generation: Generation) {
self.generation = generation;
self.entries.retain(|_, entry| entry.generation <= generation);
}
fn stats(&self) -> (usize, usize, f64) {
let total = self.hits + self.misses;
let hit_rate = if total > 0 {
(self.hits as f64 / total as f64) * 100.0
} else {
0.0
};
(self.hits, self.misses, hit_rate)
}
}
static ENVIRONMENT_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
impl OptimizedEnvironment {
pub fn new() -> Self {
let id = ENVIRONMENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
Self {
bindings: HashMap::new(),
parent: None,
cache: RwLock::new(VariableCache::new(100)), depth: 0,
generation: 0,
id,
}
}
pub fn new_child(parent: Arc<OptimizedEnvironment>) -> Self {
let id = ENVIRONMENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let depth = parent.depth + 1;
let generation = parent.generation + 1;
Self {
bindings: HashMap::new(),
parent: Some(parent),
cache: RwLock::new(VariableCache::new(50)), depth,
generation,
id,
}
}
pub fn bind(&mut self, symbol: SymbolId, value: Value) {
self.bindings.insert(symbol, value.clone());
if let Ok(mut cache) = self.cache.write() {
cache.insert(symbol, value, self.generation, 0);
}
self.invalidate_parent_caches();
}
pub fn set(&mut self, symbol: SymbolId, value: Value) -> Result<(), String> {
if let std::collections::hash_map::Entry::Occupied(mut entry) = self.bindings.entry(symbol) {
entry.insert(value.clone());
if let Ok(mut cache) = self.cache.write() {
cache.insert(symbol, value, self.generation, 0);
}
return Ok(());
}
if let Some(ref parent) = self.parent {
return self.set_in_parent(symbol, value, parent.clone(), 1);
}
Err(format!("Undefined variable: {symbol:?}"))
}
pub fn lookup(&self, symbol: SymbolId) -> Option<Value> {
if let Ok(mut cache) = self.cache.write() {
if let Some(value) = cache.get(symbol, self.generation) {
return Some(value);
}
}
if let Some(value) = self.bindings.get(&symbol) {
if let Ok(mut cache) = self.cache.write() {
cache.insert(symbol, value.clone(), self.generation, 0);
}
return Some(value.clone());
}
if let Some(ref parent) = self.parent {
if let Some(value) = parent.lookup_with_depth(symbol, 1) {
if let Ok(mut cache) = self.cache.write() {
cache.insert(symbol, value.clone(), self.generation, 1);
}
return Some(value);
}
}
None
}
fn lookup_with_depth(&self, symbol: SymbolId, _current_depth: usize) -> Option<Value> {
if let Some(value) = self.bindings.get(&symbol) {
return Some(value.clone());
}
if let Some(ref parent) = self.parent {
parent.lookup_with_depth(symbol, _current_depth + 1)
} else {
None
}
}
fn set_in_parent(&self, symbol: SymbolId, value: Value, parent: Arc<OptimizedEnvironment>, depth: usize) -> Result<(), String> {
Err("Setting variables in parent environments requires mutable access".to_string())
}
fn invalidate_parent_caches(&self) {
if let Some(ref parent) = self.parent {
if let Ok(mut cache) = parent.cache.write() {
cache.invalidate(self.generation);
}
parent.invalidate_parent_caches();
}
}
pub fn get_stats(&self) -> EnvironmentStats {
let mut total_lookups = 0;
let mut cache_hits = 0;
let mut cache_misses = 0;
let mut total_depth = 0;
let mut hot_variables = Vec::new();
if let Ok(cache) = self.cache.read() {
let (hits, misses, _) = cache.stats();
cache_hits += hits;
cache_misses += misses;
total_lookups += hits + misses;
let mut var_stats: Vec<_> = cache.entries.iter()
.map(|(&symbol, entry)| (symbol, entry.access_count))
.collect();
var_stats.sort_by_key(|(_, count)| std::cmp::Reverse(*count));
hot_variables = var_stats.into_iter().take(10).collect();
let total_weighted_depth: usize = cache.entries.values()
.map(|entry| entry.depth * entry.access_count)
.sum();
let total_accesses: usize = cache.entries.values()
.map(|entry| entry.access_count)
.sum();
if total_accesses > 0 {
total_depth = total_weighted_depth / total_accesses;
}
}
if let Some(ref parent) = self.parent {
let parent_stats = parent.get_stats();
total_lookups += parent_stats.total_lookups;
cache_hits += parent_stats.cache_hits;
cache_misses += parent_stats.cache_misses;
}
let hit_rate = if total_lookups > 0 {
(cache_hits as f64 / total_lookups as f64) * 100.0
} else {
0.0
};
EnvironmentStats {
total_lookups,
cache_hits,
cache_misses,
hit_rate,
avg_lookup_depth: total_depth as f64,
hot_variables,
}
}
pub fn optimize(&mut self, hot_variables: &[(SymbolId, usize)]) {
if let Ok(mut cache) = self.cache.write() {
for &(symbol, access_count) in hot_variables.iter().take(20) {
if let Some(value) = self.bindings.get(&symbol) {
let entry = CacheEntry {
value: value.clone(),
generation: self.generation,
depth: 0,
access_count,
};
cache.entries.insert(symbol, entry);
}
}
}
}
pub fn clear_cache(&mut self) {
if let Ok(mut cache) = self.cache.write() {
cache.entries.clear();
cache.hits = 0;
cache.misses = 0;
}
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn id(&self) -> u64 {
self.id
}
pub fn dump_debug_info(&self) -> String {
let mut info = String::new();
info.push_str(&format!("Environment {} (depth: {})\n", self.id, self.depth));
info.push_str(&format!("Local bindings: {}\n", self.bindings.len()));
if let Ok(cache) = self.cache.read() {
let (hits, misses, hit_rate) = cache.stats();
info.push_str(&format!("Cache: {hits} hits, {misses} misses, {hit_rate:.1}% hit rate\n"));
info.push_str(&format!("Cache entries: {}/{}\n", cache.entries.len(), cache.max_size));
}
if let Some(ref parent) = self.parent {
info.push_str("Parent environment: Yes\n");
} else {
info.push_str("Parent environment: None (top-level)\n");
}
info
}
}
impl Default for OptimizedEnvironment {
fn default() -> Self {
Self::new()
}
}
pub struct OptimizedEnvironmentBuilder {
cache_size: usize,
enable_caching: bool,
pre_populate_cache: bool,
}
impl OptimizedEnvironmentBuilder {
pub fn new() -> Self {
Self {
cache_size: 100,
enable_caching: true,
pre_populate_cache: false,
}
}
pub fn cache_size(mut self, size: usize) -> Self {
self.cache_size = size;
self
}
pub fn enable_caching(mut self, enable: bool) -> Self {
self.enable_caching = enable;
self
}
pub fn pre_populate_cache(mut self, pre_populate: bool) -> Self {
self.pre_populate_cache = pre_populate;
self
}
pub fn build(self) -> OptimizedEnvironment {
let mut env = OptimizedEnvironment::new();
if !self.enable_caching {
env.clear_cache();
}
env
}
}
impl Default for OptimizedEnvironmentBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::intern_symbol;
#[test]
fn test_optimized_environment_basic_operations() {
let mut env = OptimizedEnvironment::new();
let symbol = intern_symbol("test-var");
let value = Value::integer(42);
env.bind(symbol, value.clone());
let result = env.lookup(symbol);
assert!(result.is_some());
assert_eq!(result.unwrap().as_integer(), Some(42));
let result2 = env.lookup(symbol);
assert!(result2.is_some());
}
#[test]
fn test_environment_hierarchy() {
let mut parent = OptimizedEnvironment::new();
let parent_symbol = intern_symbol("parent-var");
parent.bind(parent_symbol, Value::integer(100));
let child = OptimizedEnvironment::new_child(Arc::new(parent));
let child_symbol = intern_symbol("child-var");
let parent_value = child.lookup(parent_symbol);
assert!(parent_value.is_some());
assert_eq!(parent_value.unwrap().as_integer(), Some(100));
}
#[test]
fn test_cache_performance() {
let mut env = OptimizedEnvironment::new();
let symbol = intern_symbol("cached-var");
env.bind(symbol, Value::integer(42));
let _ = env.lookup(symbol);
for _ in 0..10 {
let _ = env.lookup(symbol);
}
let stats = env.get_stats();
assert!(stats.cache_hits > 0);
assert!(stats.hit_rate > 0.0);
}
#[test]
fn test_environment_stats() {
let mut env = OptimizedEnvironment::new();
let symbols: Vec<_> = (0..5).map(|i| intern_symbol(&format!("var_{}", i))).collect();
for (i, &symbol) in symbols.iter().enumerate() {
env.bind(symbol, Value::integer(i as i64));
}
for (i, &symbol) in symbols.iter().enumerate() {
for _ in 0..=i {
let _ = env.lookup(symbol);
}
}
let stats = env.get_stats();
assert!(stats.total_lookups > 0);
assert!(!stats.hot_variables.is_empty());
}
#[test]
fn test_environment_builder() {
let env = OptimizedEnvironmentBuilder::new()
.cache_size(50)
.enable_caching(true)
.build();
assert_eq!(env.depth(), 0);
assert_eq!(env.id(), env.id()); }
}