#[derive(Debug, Clone)]
pub struct CurrentStats {
pub node_count: usize,
pub edge_count: usize,
}
#[derive(Debug)]
pub(crate) struct FilterStats {
pub(crate) search_count: std::sync::atomic::AtomicU64,
pub(crate) total_candidates: std::sync::atomic::AtomicU64,
pub(crate) total_results: std::sync::atomic::AtomicU64,
}
impl FilterStats {
pub(crate) fn new() -> Self {
FilterStats {
search_count: std::sync::atomic::AtomicU64::new(0),
total_candidates: std::sync::atomic::AtomicU64::new(0),
total_results: std::sync::atomic::AtomicU64::new(0),
}
}
pub(crate) fn record_search(&self, candidates_fetched: usize, results_returned: usize) {
use std::sync::atomic::Ordering;
self.search_count.fetch_add(1, Ordering::Relaxed);
self.total_candidates
.fetch_add(candidates_fetched as u64, Ordering::Relaxed);
self.total_results
.fetch_add(results_returned as u64, Ordering::Relaxed);
}
pub(crate) fn get_adaptive_multiplier(&self) -> f64 {
use std::sync::atomic::Ordering;
const MIN_SEARCHES_FOR_ADAPTATION: u64 = 3;
const DEFAULT_MULTIPLIER: f64 = 10.0;
const MIN_MULTIPLIER: f64 = 5.0;
const MAX_MULTIPLIER: f64 = 50.0;
let search_count = self.search_count.load(Ordering::Relaxed);
if search_count < MIN_SEARCHES_FOR_ADAPTATION {
return DEFAULT_MULTIPLIER;
}
let total_candidates = self.total_candidates.load(Ordering::Relaxed);
let total_results = self.total_results.load(Ordering::Relaxed);
if total_candidates == 0 {
return DEFAULT_MULTIPLIER;
}
let pass_rate = (total_results as f64 / total_candidates as f64).min(1.0);
let multiplier = MIN_MULTIPLIER / pass_rate.sqrt();
multiplier.clamp(MIN_MULTIPLIER, MAX_MULTIPLIER)
}
}