mathhook_core/educational/
message_registry.rs

1//! Message registry with organized, mapped, indexed educational content
2//! Clean separation of content from code logic
3//! User requirement: "texts mapped and hashed and stuff like that, not bluntly in the code"
4
5pub 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
20/// Educational message generator providing high-level interface
21pub struct EducationalMessageGenerator;
22
23impl EducationalMessageGenerator {
24    /// Generate linear equation explanation
25    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    /// Generate quadratic equation explanation
66    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    /// Generate error explanation
104    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    /// Generate mathematical insight
119    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
129/// Message performance optimizer
130pub struct MessageOptimizer;
131
132impl MessageOptimizer {
133    /// Pre-compute common message combinations for performance
134    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    /// Get cached message or generate new one
159    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}