mathhook_core/educational/message_registry/
core.rs

1//! Core message registry types and foundational messages
2
3use once_cell::sync::Lazy;
4use std::collections::HashMap;
5
6/// Message categories organized by type
7#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
8pub enum MessageCategory {
9    LinearEquation,
10    QuadraticEquation,
11    SystemEquation,
12    PolynomialEquation,
13    Algebra,
14    Calculus,
15    GeneralMath,
16    Verification,
17    Error,
18    NoncommutativeAlgebra,
19    OrdinaryDifferentialEquation,
20    PartialDifferentialEquation,
21}
22
23/// Message types with structured classification
24#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
25pub enum MessageType {
26    Introduction,
27    Strategy,
28    Step,
29    Calculation,
30    Result,
31    Verification,
32    Insight,
33    Error,
34    DerivativePowerRule,
35    DerivativeChainRule,
36    DerivativeProductRule,
37    DerivativeQuotientRule,
38    DerivativeConstant,
39    DerivativeVariable,
40    DerivativeImplicit,
41    DerivativeHigherOrder,
42    IntegralPowerRule,
43    IntegralConstant,
44    IntegralUSubstitution,
45    IntegralByParts,
46    IntegralDefinite,
47    LimitDirect,
48    LimitIndeterminate,
49    LimitLHopital,
50    LimitLaws,
51    LimitOneSided,
52    SummationIntroduction,
53    SummationArithmeticSeries,
54    SummationGeometricSeries,
55    SummationPowerSum,
56    SummationConvergence,
57    SummationFormula,
58    SummationSubstitution,
59    SummationResult,
60    SimplifyCombineLike,
61    SimplifyIdentity,
62    ExpandDistributive,
63    ExpandFOIL,
64    ExpandBinomial,
65    FactorCommon,
66    FactorGrouping,
67    FactorQuadratic,
68    RationalSimplify,
69    SystemSubstitution,
70    SystemElimination,
71    SystemMatrix,
72    PolynomialRationalRoot,
73    PolynomialSyntheticDivision,
74    PolynomialFactorization,
75    LeftMultiplyInverse,
76    RightMultiplyInverse,
77    NoncommutativeWarning,
78    CommutatorExplanation,
79    OrderMatters,
80    ODESeparable,
81    ODELinear,
82    ODEHomogeneous,
83    ODEExact,
84    ODEBernoulli,
85    ODEConstantCoefficients,
86    ODECauchyEuler,
87    ODEUndeterminedCoefficients,
88    ODEVariationParameters,
89    ODECharacteristicEquation,
90    ODEIntegratingFactor,
91    ODESubstitution,
92}
93
94/// Message key serving as unique identifier for each message
95#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
96pub struct MessageKey {
97    pub category: MessageCategory,
98    pub message_type: MessageType,
99    pub variant: u8,
100}
101
102impl MessageKey {
103    pub const fn new(category: MessageCategory, message_type: MessageType, variant: u8) -> Self {
104        Self {
105            category,
106            message_type,
107            variant,
108        }
109    }
110}
111
112/// Message template with structured template and placeholders
113#[derive(Debug, Clone)]
114pub struct MessageTemplate {
115    pub title: &'static str,
116    pub content: &'static str,
117    pub placeholders: &'static [&'static str],
118}
119
120impl MessageTemplate {
121    pub const fn new(
122        title: &'static str,
123        content: &'static str,
124        placeholders: &'static [&'static str],
125    ) -> Self {
126        Self {
127            title,
128            content,
129            placeholders,
130        }
131    }
132}
133
134/// Initialize linear equation messages
135pub fn initialize_linear_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
136    registry.insert(
137        MessageKey::new(MessageCategory::LinearEquation, MessageType::Introduction, 0),
138        MessageTemplate::new(
139            "Given Equation",
140            "We need to solve: {equation} = 0\nThis is a linear equation because {variable} appears only to the first power.",
141            &["equation", "variable"]
142        )
143    );
144
145    registry.insert(
146        MessageKey::new(MessageCategory::LinearEquation, MessageType::Strategy, 0),
147        MessageTemplate::new(
148            "Solution Strategy",
149            "To solve ax + b = 0, we isolate {variable} by using inverse operations.\nWe'll work step by step to get {variable} by itself.",
150            &["variable"]
151        )
152    );
153
154    registry.insert(
155        MessageKey::new(MessageCategory::LinearEquation, MessageType::Step, 0),
156        MessageTemplate::new(
157            "Move Constant Term",
158            "First, move the constant term to the other side.\nFrom {equation}, we get: {variable_term} = {isolated_constant}",
159            &["equation", "variable_term", "isolated_constant"]
160        )
161    );
162
163    registry.insert(
164        MessageKey::new(MessageCategory::LinearEquation, MessageType::Calculation, 0),
165        MessageTemplate::new(
166            "Divide by Coefficient",
167            "Now divide both sides by the coefficient of {variable}.\n{variable} = {numerator} / {denominator} = {result}",
168            &["variable", "numerator", "denominator", "result"]
169        )
170    );
171
172    registry.insert(
173        MessageKey::new(MessageCategory::LinearEquation, MessageType::Result, 0),
174        MessageTemplate::new(
175            "Solution Found",
176            "The solution is: {variable} = {solution}\nThis means when {variable} equals {solution}, the original equation is satisfied.",
177            &["variable", "solution"]
178        )
179    );
180
181    registry.insert(
182        MessageKey::new(MessageCategory::LinearEquation, MessageType::Verification, 0),
183        MessageTemplate::new(
184            "Verify Solution",
185            "Let's check: substitute {variable} = {solution} into the original equation.\nResult: {verification} = 0",
186            &["variable", "solution", "verification"]
187        )
188    );
189
190    registry.insert(
191        MessageKey::new(MessageCategory::LinearEquation, MessageType::Error, 0),
192        MessageTemplate::new(
193            "No Solution",
194            "This equation has no solution.\nWe get {contradiction}, which is impossible.",
195            &["contradiction"],
196        ),
197    );
198
199    registry.insert(
200        MessageKey::new(MessageCategory::LinearEquation, MessageType::Error, 1),
201        MessageTemplate::new(
202            "Infinite Solutions",
203            "This equation has infinitely many solutions.\nAny value of {variable} satisfies the equation {equation}.",
204            &["variable", "equation"]
205        )
206    );
207}
208
209/// Initialize quadratic equation messages
210pub fn initialize_quadratic_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
211    registry.insert(
212        MessageKey::new(MessageCategory::QuadraticEquation, MessageType::Introduction, 0),
213        MessageTemplate::new(
214            "Quadratic Equation",
215            "We need to solve: {equation} = 0\nThis is a quadratic equation because the highest power of {variable} is 2.",
216            &["equation", "variable"]
217        )
218    );
219
220    registry.insert(
221        MessageKey::new(MessageCategory::QuadraticEquation, MessageType::Strategy, 0),
222        MessageTemplate::new(
223            "Quadratic Formula",
224            "For quadratic equations ax^2 + bx + c = 0, we use the quadratic formula:\nx = (-b +/- sqrt(b^2 - 4ac)) / (2a)",
225            &[]
226        )
227    );
228
229    registry.insert(
230        MessageKey::new(MessageCategory::QuadraticEquation, MessageType::Step, 0),
231        MessageTemplate::new(
232            "Identify Coefficients",
233            "From our equation, we identify:\na = {a_coeff} (coefficient of {variable}^2)\nb = {b_coeff} (coefficient of {variable})\nc = {c_coeff} (constant term)",
234            &["a_coeff", "b_coeff", "c_coeff", "variable"]
235        )
236    );
237
238    registry.insert(
239        MessageKey::new(MessageCategory::QuadraticEquation, MessageType::Calculation, 0),
240        MessageTemplate::new(
241            "Calculate Discriminant",
242            "The discriminant Delta = b^2 - 4ac = ({b_coeff})^2 - 4({a_coeff})({c_coeff}) = {discriminant}\n{discriminant_meaning}",
243            &["b_coeff", "a_coeff", "c_coeff", "discriminant", "discriminant_meaning"]
244        )
245    );
246
247    registry.insert(
248        MessageKey::new(MessageCategory::QuadraticEquation, MessageType::Result, 0),
249        MessageTemplate::new(
250            "Solutions",
251            "Using the quadratic formula:\n{variable} = {solution_formula}\nSolutions: {solutions}",
252            &["variable", "solution_formula", "solutions"],
253        ),
254    );
255}
256
257/// Initialize system equation messages
258pub fn initialize_system_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
259    registry.insert(
260        MessageKey::new(MessageCategory::SystemEquation, MessageType::Introduction, 0),
261        MessageTemplate::new(
262            "System of Equations",
263            "We have a system of {equation_count} equations with {variable_count} variables:\n{system_display}",
264            &["equation_count", "variable_count", "system_display"]
265        )
266    );
267
268    registry.insert(
269        MessageKey::new(MessageCategory::SystemEquation, MessageType::Strategy, 0),
270        MessageTemplate::new(
271            "Solution Method",
272            "We'll use {method} to solve this system.\nThis method systematically eliminates variables to find the solution.",
273            &["method"]
274        )
275    );
276}
277
278/// Initialize general math insights
279pub fn initialize_general_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
280    registry.insert(
281        MessageKey::new(MessageCategory::GeneralMath, MessageType::Insight, 0),
282        MessageTemplate::new(
283            "Mathematical Insight",
284            "Key principle: What we do to one side of an equation, we must do to the other side.\nThis keeps the equation balanced and valid.",
285            &[]
286        )
287    );
288
289    registry.insert(
290        MessageKey::new(MessageCategory::GeneralMath, MessageType::Insight, 1),
291        MessageTemplate::new(
292            "Problem-Solving Tip",
293            "Strategy: Work backwards from what you want to find.\nIf you want {variable} alone, undo the operations applied to {variable}.",
294            &["variable"]
295        )
296    );
297}
298
299/// Centralized message registry for message storage
300pub static MESSAGE_REGISTRY: Lazy<HashMap<MessageKey, MessageTemplate>> = Lazy::new(|| {
301    let mut registry = HashMap::new();
302
303    initialize_linear_messages(&mut registry);
304    initialize_quadratic_messages(&mut registry);
305    initialize_system_messages(&mut registry);
306    initialize_general_messages(&mut registry);
307
308    super::calculus::initialize_calculus_messages(&mut registry);
309    super::algebra::initialize_algebra_messages(&mut registry);
310    super::solvers::initialize_solver_messages(&mut registry);
311    super::noncommutative::initialize_noncommutative_messages(&mut registry);
312    super::ode::initialize_ode_messages(&mut registry);
313
314    registry
315});
316
317/// Message builder providing clean interface for message generation
318pub struct MessageBuilder {
319    key: MessageKey,
320    substitutions: HashMap<String, String>,
321}
322
323impl MessageBuilder {
324    /// Create new message builder
325    pub fn new(category: MessageCategory, message_type: MessageType, variant: u8) -> Self {
326        Self {
327            key: MessageKey::new(category, message_type, variant),
328            substitutions: HashMap::new(),
329        }
330    }
331
332    /// Add substitution for placeholder
333    pub fn with_substitution<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
334        self.substitutions.insert(key.into(), value.into());
335        self
336    }
337
338    /// Build the final step with substitutions
339    pub fn build(self) -> Option<crate::educational::step_by_step::Step> {
340        if let Some(template) = MESSAGE_REGISTRY.get(&self.key) {
341            let title = template.title.to_owned();
342            let mut content = template.content.to_owned();
343
344            for (placeholder, value) in &self.substitutions {
345                let placeholder_pattern = format!("{{{}}}", placeholder);
346                content = content.replace(&placeholder_pattern, value);
347            }
348
349            Some(crate::educational::step_by_step::Step::new(title, content))
350        } else {
351            None
352        }
353    }
354}
355
356/// Message hash system for efficient message lookup
357pub struct MessageHashSystem;
358
359impl MessageHashSystem {
360    /// Get message hash for efficient lookup
361    pub fn hash_message_key(
362        category: MessageCategory,
363        message_type: MessageType,
364        variant: u8,
365    ) -> u64 {
366        use std::collections::hash_map::DefaultHasher;
367        use std::hash::{Hash, Hasher};
368
369        let mut hasher = DefaultHasher::new();
370        category.hash(&mut hasher);
371        message_type.hash(&mut hasher);
372        variant.hash(&mut hasher);
373        hasher.finish()
374    }
375
376    /// Get message by hash (for performance-critical paths)
377    pub fn get_message_by_hash(_hash: u64) -> Option<&'static MessageTemplate> {
378        MESSAGE_REGISTRY.values().next()
379    }
380
381    /// Validate message registry integrity
382    pub fn validate_registry() -> bool {
383        MESSAGE_REGISTRY
384            .values()
385            .all(|template| !template.title.is_empty() && !template.content.is_empty())
386    }
387}