use std::collections::{HashMap};
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
struct AccessPattern {
access_count: u32,
last_access: Instant,
access_times: Vec<Instant>, average_interval: Duration,
confidence: f64, }
#[derive(Debug, Clone)]
struct Dependency {
source: String,
target: String,
strength: f64, }
pub struct PatternAnalyzer {
patterns: HashMap<String, AccessPattern>,
dependencies: Vec<Dependency>,
access_history: BTreeMap<Instant, String>,
confidence_threshold: f64,
max_history: usize,
}
impl PatternAnalyzer {
pub fn new() -> Self {
Self {
patterns: HashMap::new(),
dependencies: Vec::new(),
access_history: BTreeMap::new(),
confidence_threshold: 0.7,
max_history: 1000,
}
}
pub fn record_access(&mut self, script_name: &str) {
let now: _ = Instant::now();
self.access_history.insert(now, script_name.to_string());
if self.access_history.len() > self.max_history {
if let Some(key) = self.access_history.keys().next().cloned() {
self.access_history.remove(&key);
}
}
let script_name_owned: _ = script_name.to_string();
{
let pattern: _ = self.patterns.entry(script_name_owned.clone()).or_insert_with(|| AccessPattern {
access_count: 0,
last_access: now,
access_times: Vec::with_capacity(100),
average_interval: Duration::from_secs(60),
confidence: 0.0,
});
pattern.access_count += 1;
pattern.last_access = now;
pattern.access_times.push(now);
if pattern.access_times.len() > 100 {
pattern.access_times.remove(0);
}
if pattern.access_times.len() >= 2 {
let mut total_interval = Duration::from_secs(0);
for i in 1..pattern.access_times.len() {
let interval: _ = pattern.access_times[i].duration_since(pattern.access_times[i - 1]);
total_interval += interval;
}
let count: _ = pattern.access_times.len() - 1;
pattern.average_interval = Duration::from_nanos(total_interval.as_nanos() as u64 / count as u64);
}
}
let confidence: _ = self.calculate_confidence(&script_name);
if let Some(pattern) = self.patterns.get_mut(&script_name_owned) {
pattern.confidence = confidence;
}
}
pub fn predict_prefetch(&self, current_script: &str) -> Vec<String> {
let mut predictions = Vec::new();
let deps: _ = self.find_dependencies(current_script);
for dep in deps {
predictions.push(dep);
}
let similar_scripts: _ = self.find_similar_scripts(current_script);
predictions.extend(similar_scripts);
let frequent_scripts: _ = self.find_frequent_scripts();
predictions.extend(frequent_scripts);
predictions.sort_by(|a, b| {
let conf_a: _ = self.get_confidence(a);
let conf_b: _ = self.get_confidence(b);
conf_b.partial_cmp(&conf_a).unwrap_or(std::cmp::Ordering::Equal)
});
predictions.into_iter().take(5).collect()
}
fn calculate_confidence(&self, script_name: &str) -> f64 {
if let Some(pattern) = self.patterns.get(script_name) {
let frequency_score: _ = (pattern.access_count as f64 / 100.0).min(1.0);
let regularity_score: _ = if pattern.access_times.len() >= 3 {
let intervals: Vec<f64> = pattern.access_times.windows(2)
.map(|w| w[1].duration_since(w[0]).as_secs_f64())
.collect();
let mean: f64 = intervals.iter().sum::<f64>() / intervals.len() as f64;
let variance: f64 = intervals.iter()
.map(|&x| (x - mean).powi(2))
.sum::<f64>() / intervals.len() as f64;
let std_dev: _ = variance.sqrt();
let cv: _ = std_dev / mean;
(1.0 / (1.0 + cv)).min(1.0)
} else {
0.5 };
let time_since_last: _ = pattern.last_access.elapsed().as_secs();
let recency_score: _ = (1.0 - (time_since_last as f64 / 3600.0)).max(0.0).min(1.0);
let confidence: _ = frequency_score * 0.4 + regularity_score * 0.4 + recency_score * 0.2;
confidence.min(1.0).max(0.0)
} else {
0.0
}
}
fn find_dependencies(&self, script_name: &str) -> Vec<String> {
self.dependencies
.iter()
.filter(|dep| dep.source == script_name && dep.strength > 0.5)
.map(|dep| dep.target.clone())
.collect()
}
fn find_similar_scripts(&self, script_name: &str) -> Vec<String> {
if let Some(target_pattern) = self.patterns.get(script_name) {
let mut similar = Vec::new();
for (name, pattern) in &self.patterns {
if name != script_name {
let similarity: _ = self.calculate_pattern_similarity(target_pattern, pattern);
if similarity > 0.8 {
similar.push(name.clone());
}
}
}
similar
} else {
Vec::new()
}
}
fn calculate_pattern_similarity(&self, a: &AccessPattern, b: &AccessPattern) -> f64 {
let freq_sim: _ = 1.0 - ((a.access_count as f64 - b.access_count as f64).abs()
/ (a.access_count.max(b.access_count) as f64 + 1.0));
let interval_diff: _ = if a.access_count > 0 && b.access_count > 0 {
let diff: _ = a.average_interval.as_secs_f64() - b.average_interval.as_secs_f64();
(diff / a.average_interval.as_secs_f64().max(b.average_interval.as_secs_f64()).abs())
} else {
1.0
};
let interval_sim: _ = 1.0 - interval_diff.min(1.0);
(freq_sim * 0.6 + interval_sim * 0.4).max(0.0).min(1.0)
}
fn find_frequent_scripts(&self) -> Vec<String> {
let mut frequent: Vec<_> = self.patterns
.iter()
.filter(|(_, pattern)| pattern.access_count > 10 && pattern.confidence > self.confidence_threshold)
.map(|(name, _)| name.clone())
.collect();
frequent.sort_by(|a, b| {
let conf_a: _ = self.get_confidence(a);
let conf_b: _ = self.get_confidence(b);
conf_b.partial_cmp(&conf_a).unwrap_or(std::cmp::Ordering::Equal)
});
frequent
}
fn get_confidence(&self, script_name: &str) -> f64 {
self.patterns.get(script_name).map(|p| p.confidence).unwrap_or(0.0)
}
pub fn add_dependency(&mut self, source: &str, target: &str, strength: f64) {
self.dependencies.push(Dependency {
source: source.to_string(),
target: target.to_string(),
strength: strength.clamp(0.0, 1.0),
});
}
pub fn get_stats(&self) -> PatternStats {
let total_scripts: _ = self.patterns.len();
let high_confidence: _ = self.patterns.values().filter(|p| p.confidence > self.confidence_threshold).count();
let avg_confidence: _ = if total_scripts > 0 {
self.patterns.values().map(|p| p.confidence).sum::<f64>() / total_scripts as f64
} else {
0.0
};
PatternStats {
total_scripts,
high_confidence_scripts: high_confidence,
average_confidence: avg_confidence,
dependency_count: self.dependencies.len(),
access_history_size: self.access_history.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct PatternStats {
pub total_scripts: usize,
pub high_confidence_scripts: usize,
pub average_confidence: f64,
pub dependency_count: usize,
pub access_history_size: usize,
}
impl Default for PatternAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, Instant};
use std::sync::atomic::Ordering;
#[test]
fn test_pattern_recording() {
let mut analyzer = PatternAnalyzer::new();
analyzer.record_access("test.js");
analyzer.record_access("test.js");
analyzer.record_access("test.js");
let stats: _ = analyzer.get_stats();
assert_eq!(stats.total_scripts, 1);
assert!(stats.average_confidence > 0.0);
}
#[test]
fn test_confidence_calculation() {
let mut analyzer = PatternAnalyzer::new();
for _ in 0..20 {
analyzer.record_access("frequent.js");
}
let confidence: _ = analyzer.get_confidence("frequent.js");
assert!(confidence > 0.5);
}
#[test]
fn test_dependency_tracking() {
let mut analyzer = PatternAnalyzer::new();
analyzer.add_dependency("main.js", "util.js", 0.9);
let deps: _ = analyzer.find_dependencies("main.js");
assert_eq!(deps.len(), 1);
assert_eq!(deps[0], "util.js");
}
#[test]
fn test_prefetch_prediction() {
let mut analyzer = PatternAnalyzer::new();
for _ in 0..15 {
analyzer.record_access("main.js");
}
analyzer.add_dependency("main.js", "dep1.js", 0.8);
let predictions: _ = analyzer.predict_prefetch("main.js");
assert!(!predictions.is_empty());
}
}