mathhook_core/educational/
message_registry.rs1pub mod algebra;
6pub mod calculus;
7pub mod core;
8pub mod noncommutative;
9pub mod ode;
10pub mod pde;
11pub mod solvers;
12
13pub use core::{
14 MessageBuilder, MessageCategory, MessageHashSystem, MessageKey, MessageTemplate, MessageType,
15 MESSAGE_REGISTRY,
16};
17
18use std::collections::HashMap;
19
20pub struct EducationalMessageGenerator;
22
23impl EducationalMessageGenerator {
24 pub fn linear_equation_steps(
26 equation: &str,
27 variable: &str,
28 solution: &str,
29 ) -> Vec<crate::educational::step_by_step::Step> {
30 vec![
31 MessageBuilder::new(
32 MessageCategory::LinearEquation,
33 MessageType::Introduction,
34 0,
35 )
36 .with_substitution("equation", equation)
37 .with_substitution("variable", variable)
38 .build()
39 .unwrap(),
40 MessageBuilder::new(MessageCategory::LinearEquation, MessageType::Strategy, 0)
41 .with_substitution("variable", variable)
42 .build()
43 .unwrap(),
44 MessageBuilder::new(MessageCategory::LinearEquation, MessageType::Result, 0)
45 .with_substitution("variable", variable)
46 .with_substitution("solution", solution)
47 .build()
48 .unwrap(),
49 MessageBuilder::new(
50 MessageCategory::LinearEquation,
51 MessageType::Verification,
52 0,
53 )
54 .with_substitution("variable", variable)
55 .with_substitution("solution", solution)
56 .with_substitution(
57 "verification",
58 format!("{}({}) + constant", variable, solution),
59 )
60 .build()
61 .unwrap(),
62 ]
63 }
64
65 pub fn quadratic_equation_steps(
67 equation: &str,
68 variable: &str,
69 a: &str,
70 b: &str,
71 c: &str,
72 solutions: &str,
73 ) -> Vec<crate::educational::step_by_step::Step> {
74 vec![
75 MessageBuilder::new(
76 MessageCategory::QuadraticEquation,
77 MessageType::Introduction,
78 0,
79 )
80 .with_substitution("equation", equation)
81 .with_substitution("variable", variable)
82 .build()
83 .unwrap(),
84 MessageBuilder::new(MessageCategory::QuadraticEquation, MessageType::Strategy, 0)
85 .build()
86 .unwrap(),
87 MessageBuilder::new(MessageCategory::QuadraticEquation, MessageType::Step, 0)
88 .with_substitution("a_coeff", a)
89 .with_substitution("b_coeff", b)
90 .with_substitution("c_coeff", c)
91 .with_substitution("variable", variable)
92 .build()
93 .unwrap(),
94 MessageBuilder::new(MessageCategory::QuadraticEquation, MessageType::Result, 0)
95 .with_substitution("variable", variable)
96 .with_substitution("solution_formula", "(-b +/- sqrt(Delta))/(2a)")
97 .with_substitution("solutions", solutions)
98 .build()
99 .unwrap(),
100 ]
101 }
102
103 pub fn error_explanation(
105 category: MessageCategory,
106 error_type: u8,
107 context: &HashMap<String, String>,
108 ) -> Option<crate::educational::step_by_step::Step> {
109 let mut builder = MessageBuilder::new(category, MessageType::Error, error_type);
110
111 for (key, value) in context {
112 builder = builder.with_substitution(key, value);
113 }
114
115 builder.build()
116 }
117
118 pub fn mathematical_insight(
120 variant: u8,
121 variable: &str,
122 ) -> Option<crate::educational::step_by_step::Step> {
123 MessageBuilder::new(MessageCategory::GeneralMath, MessageType::Insight, variant)
124 .with_substitution("variable", variable)
125 .build()
126 }
127}
128
129pub struct MessageOptimizer;
131
132impl MessageOptimizer {
133 pub fn precompute_common_messages(
135 ) -> HashMap<String, Vec<crate::educational::step_by_step::Step>> {
136 let mut cache = HashMap::new();
137
138 cache.insert(
139 "linear_simple".to_owned(),
140 EducationalMessageGenerator::linear_equation_steps("x + 2", "x", "3"),
141 );
142
143 cache.insert(
144 "linear_coefficient".to_owned(),
145 EducationalMessageGenerator::linear_equation_steps("2x + 4", "x", "2"),
146 );
147
148 cache.insert(
149 "quadratic_simple".to_owned(),
150 EducationalMessageGenerator::quadratic_equation_steps(
151 "x^2 - 4", "x", "1", "0", "-4", "x = +/-2",
152 ),
153 );
154
155 cache
156 }
157
158 pub fn get_optimized_message(
160 scenario: &str,
161 ) -> Option<Vec<crate::educational::step_by_step::Step>> {
162 use once_cell::sync::Lazy;
163 static CACHE: Lazy<HashMap<String, Vec<crate::educational::step_by_step::Step>>> =
164 Lazy::new(MessageOptimizer::precompute_common_messages);
165
166 CACHE.get(scenario).cloned()
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_message_registry_integrity() {
176 assert!(MessageHashSystem::validate_registry());
177 assert!(!MESSAGE_REGISTRY.is_empty());
178 }
179
180 #[test]
181 fn test_message_count() {
182 let count = MESSAGE_REGISTRY.len();
183 assert!(
184 count >= 95,
185 "Expected at least 95 messages (including ODE), found {}",
186 count
187 );
188 }
189
190 #[test]
191 fn test_message_builder() {
192 let step = MessageBuilder::new(
193 MessageCategory::LinearEquation,
194 MessageType::Introduction,
195 0,
196 )
197 .with_substitution("equation", "2x + 3")
198 .with_substitution("variable", "x")
199 .build();
200
201 assert!(step.is_some());
202 let step = step.unwrap();
203 assert!(step.description.contains("2x + 3"));
204 assert!(step.description.contains("linear equation"));
205 }
206
207 #[test]
208 fn test_hash_system() {
209 let hash1 = MessageHashSystem::hash_message_key(
210 MessageCategory::LinearEquation,
211 MessageType::Introduction,
212 0,
213 );
214
215 let hash2 = MessageHashSystem::hash_message_key(
216 MessageCategory::LinearEquation,
217 MessageType::Introduction,
218 1,
219 );
220
221 assert_ne!(hash1, hash2);
222 }
223
224 #[test]
225 fn test_calculus_messages_exist() {
226 let derivative_msg = MessageBuilder::new(
227 MessageCategory::Calculus,
228 MessageType::DerivativePowerRule,
229 0,
230 )
231 .with_substitution("expression", "x^2")
232 .with_substitution("exponent", "2")
233 .build();
234
235 assert!(derivative_msg.is_some());
236 }
237
238 #[test]
239 fn test_algebra_messages_exist() {
240 let algebra_msg = MessageBuilder::new(
241 MessageCategory::Algebra,
242 MessageType::SimplifyCombineLike,
243 0,
244 )
245 .with_substitution("expression", "2x + 3x")
246 .with_substitution("like_terms", "2x and 3x")
247 .build();
248
249 assert!(algebra_msg.is_some());
250 }
251
252 #[test]
253 fn test_system_messages_exist() {
254 let system_msg = MessageBuilder::new(
255 MessageCategory::SystemEquation,
256 MessageType::SystemSubstitution,
257 0,
258 )
259 .build();
260
261 assert!(system_msg.is_some());
262 }
263
264 #[test]
265 fn test_ode_messages_exist() {
266 let ode_msg = MessageBuilder::new(
267 MessageCategory::OrdinaryDifferentialEquation,
268 MessageType::ODESeparable,
269 0,
270 )
271 .with_substitution("rhs", "x*y")
272 .with_substitution("independent", "x")
273 .with_substitution("dependent", "y")
274 .build();
275
276 assert!(ode_msg.is_some());
277 }
278
279 #[test]
280 fn test_pde_messages_exist() {
281 use crate::educational::message_registry::pde::{
282 pde_message_key, PdeMessageVariant, PDE_MESSAGES,
283 };
284
285 let heat_key = pde_message_key(MessageType::Introduction, PdeMessageVariant::HEAT_EQUATION);
286 let message = PDE_MESSAGES.get(&heat_key);
287
288 assert!(message.is_some());
289 assert!(message.unwrap().contains("Heat Equation"));
290 }
291}