use std::collections::HashMap;
use super::{
common::EvictionPolicy,
lru::LRU,
};
pub struct LFU<K>
where
K: Eq + std::hash::Hash + Clone + std::fmt::Debug, {
map: HashMap<K, usize>,
least_freq: usize,
freq_nodes: HashMap<usize, LRU<K>>,
}
impl<K: Eq + std::hash::Hash + Clone + std::fmt::Debug> LFU<K> {
pub fn new() -> Self {
Self {
map: HashMap::new(),
least_freq: 0,
freq_nodes: HashMap::new(),
}
}
fn record_access(&mut self, key: &K) {
if let Some(freq) = self.map.get_mut(key) {
if *freq != 0 {
if let Some(lru) = self.freq_nodes.get_mut(freq) {
lru.remove(key.clone()); if lru.len() == 0 && *freq == self.least_freq {
self.least_freq += 1;
}
} else {
panic!("Should never happen!!!!!!!!!!11");
}
} else {
self.least_freq += 1;
}
*freq += 1;
self.freq_nodes
.entry(*freq)
.or_insert_with(LRU::new) .on_set(key.clone()); }
}
fn remove_key(&mut self, key: K) {
if let Some(freq) = self.map.remove(&key) {
if let Some(lru) = self.freq_nodes.get_mut(&freq) {
lru.remove(key.clone()); }
if self.map.len() == 0 {
self.least_freq = 0;
return;
};
let mut lruopt = self.freq_nodes.get(&self.least_freq);
while lruopt.is_none() || lruopt.is_some_and(|x| x.len() == 0) {
self.least_freq += 1;
lruopt = self.freq_nodes.get(&self.least_freq);
}
}
}
fn remove_lfu_key(&mut self) -> Option<K> {
if self.least_freq == 0 {
return None; }
let evicted = if let Some(lru) = self.freq_nodes.get_mut(&self.least_freq) {
if let Some(evicted) = lru.evict() {
self.map.remove(&evicted); Some(evicted)
} else {
None
}
} else {
None
};
if self.map.len() == 0 {
self.least_freq = 0;
return evicted;
};
let mut lruopt = self.freq_nodes.get(&self.least_freq);
while lruopt.is_none() || lruopt.is_some_and(|x| x.len() == 0) {
self.least_freq += 1;
lruopt = self.freq_nodes.get(&self.least_freq);
}
evicted
}
}
impl<K: Eq + std::hash::Hash + Clone + std::fmt::Debug> EvictionPolicy<K> for LFU<K> {
fn on_get(&mut self, key: &K) {
self.record_access(key);
}
fn on_set(&mut self, key: K) {
if !self.map.contains_key(&key) {
self.map.insert(key.clone(), 0); self.least_freq = 0; }
self.record_access(&key); }
fn evict(&mut self) -> Option<K> {
return self.remove_lfu_key();
}
fn remove(&mut self, key: K) {
self.remove_key(key);
}
}