use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use chrono::Timelike;
pub struct CountMinSketch {
counters: Vec<Vec<u64>>,
width: usize,
depth: usize,
total_count: u64,
}
impl CountMinSketch {
pub fn new(epsilon: f64, delta: f64) -> Self {
let width = (std::f64::consts::E / epsilon).ceil() as usize;
let depth = (1.0 / delta).ln().ceil() as usize;
Self::with_dimensions(width.max(64), depth.max(4))
}
pub fn with_dimensions(width: usize, depth: usize) -> Self {
Self {
counters: vec![vec![0u64; width]; depth],
width,
depth,
total_count: 0,
}
}
fn hash(&self, item: u64, row: usize) -> usize {
let mut hasher = DefaultHasher::new();
item.hash(&mut hasher);
(row as u64).hash(&mut hasher);
(hasher.finish() as usize) % self.width
}
pub fn add(&mut self, item: u64) {
self.add_count(item, 1);
}
pub fn add_count(&mut self, item: u64, count: u64) {
for row in 0..self.depth {
let col = self.hash(item, row);
self.counters[row][col] = self.counters[row][col].saturating_add(count);
}
self.total_count = self.total_count.saturating_add(count);
}
pub fn estimate(&self, item: u64) -> u64 {
let mut min_count = u64::MAX;
for row in 0..self.depth {
let col = self.hash(item, row);
min_count = min_count.min(self.counters[row][col]);
}
min_count
}
pub fn frequency(&self, item: u64) -> f64 {
if self.total_count == 0 {
return 0.0;
}
self.estimate(item) as f64 / self.total_count as f64
}
pub fn is_frequent(&self, item: u64, threshold: f64) -> bool {
self.frequency(item) >= threshold
}
pub fn total_count(&self) -> u64 {
self.total_count
}
pub fn memory_usage(&self) -> usize {
self.width * self.depth * std::mem::size_of::<u64>()
}
pub fn clear(&mut self) {
for row in &mut self.counters {
for counter in row {
*counter = 0;
}
}
self.total_count = 0;
}
pub fn merge(&mut self, other: &CountMinSketch) {
if self.width != other.width || self.depth != other.depth {
return;
}
for row in 0..self.depth {
for col in 0..self.width {
self.counters[row][col] =
self.counters[row][col].saturating_add(other.counters[row][col]);
}
}
self.total_count = self.total_count.saturating_add(other.total_count);
}
pub fn stats(&self) -> SketchStats {
let non_zero: usize = self.counters
.iter()
.flat_map(|row| row.iter())
.filter(|&&c| c > 0)
.count();
SketchStats {
width: self.width,
depth: self.depth,
total_count: self.total_count,
memory_bytes: self.memory_usage(),
fill_ratio: non_zero as f64 / (self.width * self.depth) as f64,
}
}
}
impl Default for CountMinSketch {
fn default() -> Self {
Self::new(0.01, 0.001) }
}
#[derive(Debug, Clone)]
pub struct SketchStats {
pub width: usize,
pub depth: usize,
pub total_count: u64,
pub memory_bytes: usize,
pub fill_ratio: f64,
}
pub struct PressureTracker {
load_sketch: CountMinSketch,
process_sketch: CountMinSketch,
time_sketch: CountMinSketch,
pressure_threshold: u32,
}
impl PressureTracker {
pub fn new() -> Self {
Self {
load_sketch: CountMinSketch::new(0.01, 0.001),
process_sketch: CountMinSketch::new(0.01, 0.001),
time_sketch: CountMinSketch::with_dimensions(24, 4), pressure_threshold: 80,
}
}
pub fn record_pressure(&mut self, load_percent: u32, top_processes: &[u32]) {
self.load_sketch.add(load_percent as u64);
for &pid in top_processes {
self.process_sketch.add(pid as u64);
}
let hour = chrono::Local::now().hour();
self.time_sketch.add(hour as u64);
}
pub fn is_common_load(&self, load_percent: u32) -> bool {
self.load_sketch.is_frequent(load_percent as u64, 0.05)
}
pub fn process_pressure_frequency(&self, pid: u32) -> f64 {
self.process_sketch.frequency(pid as u64)
}
pub fn get_peak_hours(&self) -> Vec<(u32, u64)> {
let mut hours: Vec<(u32, u64)> = (0..24)
.map(|h| (h, self.time_sketch.estimate(h as u64)))
.collect();
hours.sort_by(|a, b| b.1.cmp(&a.1));
hours.into_iter().take(5).collect()
}
pub fn stats(&self) -> PressureTrackerStats {
PressureTrackerStats {
load_stats: self.load_sketch.stats(),
process_stats: self.process_sketch.stats(),
time_stats: self.time_sketch.stats(),
}
}
}
impl Default for PressureTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct PressureTrackerStats {
pub load_stats: SketchStats,
pub process_stats: SketchStats,
pub time_stats: SketchStats,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_count_min_sketch() {
let mut sketch = CountMinSketch::new(0.01, 0.001);
for _ in 0..1000 {
sketch.add(42);
}
for _ in 0..500 {
sketch.add(100);
}
sketch.add(999);
assert!(sketch.estimate(42) >= 1000);
assert!(sketch.estimate(100) >= 500);
assert!(sketch.estimate(999) >= 1);
assert!(sketch.is_frequent(42, 0.5));
assert!(!sketch.is_frequent(999, 0.1));
}
#[test]
fn test_pressure_tracker() {
let mut tracker = PressureTracker::new();
tracker.record_pressure(85, &[1234, 5678]);
tracker.record_pressure(90, &[1234]);
tracker.record_pressure(75, &[9999]);
assert!(tracker.process_pressure_frequency(1234) > tracker.process_pressure_frequency(9999));
}
}