1use crate::search::Match;
4use crate::solver::{canonical_expression_key, solve_for_x_rhs_expression};
5use crate::thresholds::EXACT_MATCH_TOLERANCE;
6
7#[derive(Clone, Debug)]
12pub struct MatchSummary {
13 pub lhs: String,
15 pub rhs: String,
17 pub lhs_postfix: String,
19 pub rhs_postfix: String,
21 pub solve_for_x: Option<String>,
23 pub solve_for_x_postfix: Option<String>,
25 pub canonical_key: String,
27 pub x_value: f64,
29 pub error: f64,
31 pub complexity: u32,
33 pub operator_count: usize,
35 pub tree_depth: usize,
37 pub is_exact: bool,
39}
40
41impl From<Match> for MatchSummary {
42 fn from(m: Match) -> Self {
43 Self::from_match(&m)
44 }
45}
46
47impl MatchSummary {
48 pub fn from_match(m: &Match) -> Self {
50 let lhs_infix = m.lhs.expr.to_infix_or_postfix();
51 let rhs_infix = m.rhs.expr.to_infix_or_postfix();
52
53 let solved = solve_for_x_rhs_expression(&m.lhs.expr, &m.rhs.expr);
54 let solve_for_x = solved
55 .as_ref()
56 .map(|e| format!("x = {}", e.to_infix_or_postfix()));
57 let solve_for_x_postfix = solved.as_ref().map(|e| e.to_postfix());
58
59 let canonical_key = canonical_expression_key(&m.lhs.expr)
60 .zip(canonical_expression_key(&m.rhs.expr))
61 .map(|(l, r)| format!("{l}={r}"))
62 .unwrap_or_else(|| format!("{}={}", m.lhs.expr.to_postfix(), m.rhs.expr.to_postfix()));
63
64 Self {
65 lhs: lhs_infix,
66 rhs: rhs_infix,
67 lhs_postfix: m.lhs.expr.to_postfix(),
68 rhs_postfix: m.rhs.expr.to_postfix(),
69 solve_for_x,
70 solve_for_x_postfix,
71 canonical_key,
72 x_value: m.x_value,
73 error: m.error,
74 complexity: m.complexity,
75 operator_count: m.lhs.expr.operator_count() + m.rhs.expr.operator_count(),
76 tree_depth: m.lhs.expr.tree_depth().max(m.rhs.expr.tree_depth()),
77 is_exact: m.error.abs() < EXACT_MATCH_TOLERANCE,
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::expr::{EvaluatedExpr, Expression};
86 use crate::symbol::NumType;
87
88 fn make_match(lhs: &str, rhs: &str, error: f64) -> Match {
89 let lhs_expr = Expression::parse(lhs).unwrap();
90 let rhs_expr = Expression::parse(rhs).unwrap();
91 let complexity = lhs_expr.complexity() + rhs_expr.complexity();
92 Match {
93 lhs: EvaluatedExpr::new(lhs_expr, 0.0, 1.0, NumType::Integer),
94 rhs: EvaluatedExpr::new(rhs_expr, 0.0, 0.0, NumType::Integer),
95 x_value: 2.5,
96 error,
97 complexity,
98 }
99 }
100
101 #[test]
102 fn match_summary_populates_core_fields() {
103 let summary = MatchSummary::from_match(&make_match("x1+", "3", 0.0));
104 assert_eq!(summary.lhs_postfix, "x1+");
105 assert_eq!(summary.rhs_postfix, "3");
106 assert!(summary.is_exact);
107 assert!(summary.operator_count > 0);
108 assert!(summary.tree_depth > 0);
109 assert!(!summary.canonical_key.is_empty());
110 }
111}