1use once_cell::sync::Lazy;
4use std::collections::HashMap;
5
6#[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#[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#[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#[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
134pub 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
209pub 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
257pub 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
278pub 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
299pub 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
317pub struct MessageBuilder {
319 key: MessageKey,
320 substitutions: HashMap<String, String>,
321}
322
323impl MessageBuilder {
324 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 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 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
356pub struct MessageHashSystem;
358
359impl MessageHashSystem {
360 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 pub fn get_message_by_hash(_hash: u64) -> Option<&'static MessageTemplate> {
378 MESSAGE_REGISTRY.values().next()
379 }
380
381 pub fn validate_registry() -> bool {
383 MESSAGE_REGISTRY
384 .values()
385 .all(|template| !template.title.is_empty() && !template.content.is_empty())
386 }
387}