entrenar/citl/trainer/
stats.rs1#![allow(clippy::field_reassign_with_default)]
4
5use super::{CompilationOutcome, DecisionTrace};
6
7#[derive(Debug, Clone)]
9#[allow(dead_code)] pub(crate) struct Session {
11 pub(crate) id: String,
13 pub(crate) decisions: Vec<DecisionTrace>,
15 pub(crate) outcome: CompilationOutcome,
17 pub(crate) fix_diff: Option<String>,
19}
20
21#[derive(Debug, Clone, Default)]
23pub struct DecisionStats {
24 pub success_count: u32,
26 pub fail_count: u32,
28 pub total_success: u32,
30 pub total_fail: u32,
32}
33
34impl DecisionStats {
35 #[must_use]
41 pub fn tarantula_score(&self) -> f32 {
42 if self.total_fail == 0 || self.fail_count == 0 {
43 return 0.0;
44 }
45
46 let fail_freq = self.fail_count as f32 / self.total_fail as f32;
47 let success_freq = if self.total_success > 0 {
48 self.success_count as f32 / self.total_success as f32
49 } else {
50 0.0
51 };
52
53 if fail_freq + success_freq < f32::EPSILON {
54 0.0
55 } else {
56 fail_freq / (fail_freq + success_freq)
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_decision_stats_tarantula() {
67 let mut stats = DecisionStats::default();
68 stats.success_count = 2;
69 stats.fail_count = 8;
70 stats.total_success = 10;
71 stats.total_fail = 10;
72
73 assert!((stats.tarantula_score() - 0.8).abs() < 0.01);
77 }
78
79 #[test]
80 fn test_decision_stats_tarantula_no_failures() {
81 let stats =
82 DecisionStats { success_count: 5, fail_count: 0, total_success: 5, total_fail: 0 };
83 assert_eq!(stats.tarantula_score(), 0.0);
84 }
85
86 #[test]
87 fn test_decision_stats_tarantula_only_failures() {
88 let stats =
89 DecisionStats { success_count: 0, fail_count: 5, total_success: 0, total_fail: 5 };
90 assert_eq!(stats.tarantula_score(), 1.0);
93 }
94}
95
96#[cfg(test)]
97mod prop_tests {
98 use super::*;
99 use proptest::prelude::*;
100
101 proptest! {
102 #[test]
103 fn prop_tarantula_score_bounded(
104 success in 0u32..100,
105 fail in 0u32..100,
106 total_success in 1u32..100,
107 total_fail in 1u32..100
108 ) {
109 let stats = DecisionStats {
110 success_count: success.min(total_success),
111 fail_count: fail.min(total_fail),
112 total_success,
113 total_fail,
114 };
115
116 let score = stats.tarantula_score();
117 prop_assert!(score >= 0.0);
118 prop_assert!(score <= 1.0);
119 }
120 }
121}