use crate::query_result::{PatternMatch, QueryResult};
use shape_ast::error::Result;
use std::collections::HashMap;
use super::correlations::calculate_pattern_correlations;
use super::descriptive::calculate_std_dev;
use super::types::{PatternMetrics, PatternPerformance, PatternStatistics};
pub(super) fn calculate_pattern_statistics(result: &QueryResult) -> Result<PatternStatistics> {
let mut by_pattern = HashMap::new();
let matches = result.matches.as_deref().unwrap_or(&[]);
let mut pattern_groups: HashMap<String, Vec<&PatternMatch>> = HashMap::new();
for pattern_match in matches {
pattern_groups
.entry(pattern_match.pattern_name.clone())
.or_default()
.push(pattern_match);
}
for (pattern_name, group_matches) in pattern_groups {
let metrics = calculate_pattern_metrics(&group_matches)?;
by_pattern.insert(pattern_name, metrics);
}
let correlations = calculate_pattern_correlations(matches)?;
let top_performers = rank_pattern_performance(&by_pattern);
Ok(PatternStatistics {
by_pattern,
correlations,
top_performers,
})
}
pub(super) fn calculate_pattern_metrics(matches: &[&PatternMatch]) -> Result<PatternMetrics> {
let occurrences = matches.len();
let mut values = Vec::new();
let mut durations = Vec::new();
let mut successes = 0;
for pattern_match in matches {
values.push(pattern_match.confidence);
if pattern_match.confidence > 0.5 {
successes += 1;
}
durations.push(0.0);
}
let success_rate = if occurrences > 0 {
successes as f64 / occurrences as f64
} else {
0.0
};
let avg_value = if !values.is_empty() {
values.iter().sum::<f64>() / values.len() as f64
} else {
0.0
};
let avg_duration = if !durations.is_empty() {
durations.iter().sum::<f64>() / durations.len() as f64
} else {
0.0
};
let reliability_score = calculate_reliability_score(success_rate, &values);
Ok(PatternMetrics {
occurrences,
success_rate,
avg_value,
avg_duration,
reliability_score,
})
}
fn calculate_reliability_score(success_rate: f64, values: &[f64]) -> f64 {
if values.is_empty() {
return 0.0;
}
let avg_value = values.iter().sum::<f64>() / values.len() as f64;
let std_dev = calculate_std_dev(values, avg_value);
let consistency = if avg_value != 0.0 {
1.0 - (std_dev / avg_value.abs()).min(1.0)
} else {
0.0
};
success_rate * 0.6 + consistency * 0.4
}
fn rank_pattern_performance(
pattern_metrics: &HashMap<String, PatternMetrics>,
) -> Vec<PatternPerformance> {
let mut performances: Vec<PatternPerformance> = pattern_metrics
.iter()
.map(|(name, metrics)| {
let score = metrics.success_rate * 0.3
+ metrics.avg_value * 0.3
+ metrics.reliability_score * 0.4;
PatternPerformance {
pattern_name: name.clone(),
score,
metrics: metrics.clone(),
}
})
.collect();
performances.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap());
performances
}