mathhook_core/educational/message_registry/
ode.rs

1//! ODE educational messages for differential equation solving
2
3use super::core::{MessageCategory, MessageKey, MessageTemplate, MessageType};
4use std::collections::HashMap;
5
6/// Initialize all ODE-related messages
7pub fn initialize_ode_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
8    initialize_separable_messages(registry);
9    initialize_linear_ode_messages(registry);
10    initialize_homogeneous_messages(registry);
11    initialize_exact_messages(registry);
12    initialize_bernoulli_messages(registry);
13    initialize_second_order_messages(registry);
14}
15
16fn initialize_separable_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
17    registry.insert(
18        MessageKey::new(
19            MessageCategory::OrdinaryDifferentialEquation,
20            MessageType::ODESeparable,
21            0,
22        ),
23        MessageTemplate::new(
24            "Separable ODE Identified",
25            "This is a separable ODE: dy/dx = {rhs}\nWe can write it as: dy/dx = g({independent})*h({dependent})",
26            &["rhs", "independent", "dependent"],
27        ),
28    );
29
30    registry.insert(
31        MessageKey::new(
32            MessageCategory::OrdinaryDifferentialEquation,
33            MessageType::ODESeparable,
34            1,
35        ),
36        MessageTemplate::new(
37            "Separate Variables",
38            "Separate variables by dividing both sides:\n(1/h({dependent})) d{dependent} = g({independent}) d{independent}",
39            &["dependent", "independent"],
40        ),
41    );
42
43    registry.insert(
44        MessageKey::new(
45            MessageCategory::OrdinaryDifferentialEquation,
46            MessageType::ODESeparable,
47            2,
48        ),
49        MessageTemplate::new(
50            "Integrate Both Sides",
51            "Integrate both sides:\n∫ {y_integral} d{dependent} = ∫ {x_integral} d{independent}",
52            &["y_integral", "dependent", "x_integral", "independent"],
53        ),
54    );
55
56    registry.insert(
57        MessageKey::new(
58            MessageCategory::OrdinaryDifferentialEquation,
59            MessageType::ODESeparable,
60            3,
61        ),
62        MessageTemplate::new(
63            "General Solution",
64            "After integration and adding constant C:\n{solution}",
65            &["solution"],
66        ),
67    );
68}
69
70fn initialize_linear_ode_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
71    registry.insert(
72        MessageKey::new(
73            MessageCategory::OrdinaryDifferentialEquation,
74            MessageType::ODELinear,
75            0,
76        ),
77        MessageTemplate::new(
78            "Linear First-Order ODE",
79            "This is a linear first-order ODE: dy/dx + P({independent}){dependent} = Q({independent})\nWhere P({independent}) = {p_func} and Q({independent}) = {q_func}",
80            &["independent", "dependent", "p_func", "q_func"],
81        ),
82    );
83
84    registry.insert(
85        MessageKey::new(
86            MessageCategory::OrdinaryDifferentialEquation,
87            MessageType::ODEIntegratingFactor,
88            0,
89        ),
90        MessageTemplate::new(
91            "Compute Integrating Factor",
92            "Compute integrating factor μ({independent}) = exp(∫ P({independent}) d{independent})\nμ({independent}) = exp(∫ {p_func} d{independent}) = {integrating_factor}",
93            &["independent", "p_func", "integrating_factor"],
94        ),
95    );
96
97    registry.insert(
98        MessageKey::new(
99            MessageCategory::OrdinaryDifferentialEquation,
100            MessageType::ODELinear,
101            1,
102        ),
103        MessageTemplate::new(
104            "Multiply by Integrating Factor",
105            "Multiply both sides by μ({independent}) = {integrating_factor}:\nd/d{independent}[μ({independent}) {dependent}] = μ({independent}) Q({independent})",
106            &["independent", "integrating_factor", "dependent"],
107        ),
108    );
109
110    registry.insert(
111        MessageKey::new(
112            MessageCategory::OrdinaryDifferentialEquation,
113            MessageType::ODELinear,
114            2,
115        ),
116        MessageTemplate::new(
117            "Integrate to Solve",
118            "Integrate both sides:\nμ({independent}) {dependent} = ∫ μ({independent}) Q({independent}) d{independent}\n{dependent} = (1/μ({independent})) [∫ {integral_expr} d{independent} + C]",
119            &["independent", "dependent", "integral_expr"],
120        ),
121    );
122
123    registry.insert(
124        MessageKey::new(
125            MessageCategory::OrdinaryDifferentialEquation,
126            MessageType::ODELinear,
127            3,
128        ),
129        MessageTemplate::new(
130            "General Solution",
131            "General solution:\n{dependent}({independent}) = {solution}",
132            &["dependent", "independent", "solution"],
133        ),
134    );
135}
136
137fn initialize_homogeneous_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
138    registry.insert(
139        MessageKey::new(
140            MessageCategory::OrdinaryDifferentialEquation,
141            MessageType::ODEHomogeneous,
142            0,
143        ),
144        MessageTemplate::new(
145            "Homogeneous ODE Identified",
146            "This is a homogeneous ODE: dy/dx = f({dependent}/{independent})\nThe right-hand side depends only on the ratio {dependent}/{independent}",
147            &["dependent", "independent"],
148        ),
149    );
150
151    registry.insert(
152        MessageKey::new(
153            MessageCategory::OrdinaryDifferentialEquation,
154            MessageType::ODESubstitution,
155            0,
156        ),
157        MessageTemplate::new(
158            "Substitution v = y/x",
159            "Let v = {dependent}/{independent}, so {dependent} = v{independent}\nThen dy/d{independent} = v + {independent}(dv/d{independent})",
160            &["dependent", "independent"],
161        ),
162    );
163
164    registry.insert(
165        MessageKey::new(
166            MessageCategory::OrdinaryDifferentialEquation,
167            MessageType::ODEHomogeneous,
168            1,
169        ),
170        MessageTemplate::new(
171            "Transform to Separable",
172            "Substituting into the ODE:\nv + {independent}(dv/d{independent}) = f(v)\n{independent}(dv/d{independent}) = f(v) - v\nThis is now separable!",
173            &["independent"],
174        ),
175    );
176
177    registry.insert(
178        MessageKey::new(
179            MessageCategory::OrdinaryDifferentialEquation,
180            MessageType::ODEHomogeneous,
181            2,
182        ),
183        MessageTemplate::new(
184            "Back-Substitute",
185            "After solving for v, substitute back v = {dependent}/{independent}:\n{solution}",
186            &["dependent", "independent", "solution"],
187        ),
188    );
189}
190
191fn initialize_exact_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
192    registry.insert(
193        MessageKey::new(
194            MessageCategory::OrdinaryDifferentialEquation,
195            MessageType::ODEExact,
196            0,
197        ),
198        MessageTemplate::new(
199            "Exact ODE Identified",
200            "This is an exact ODE: M({independent}, {dependent})d{independent} + N({independent}, {dependent})d{dependent} = 0\nWhere ∂M/∂{dependent} = ∂N/∂{independent}",
201            &["independent", "dependent"],
202        ),
203    );
204
205    registry.insert(
206        MessageKey::new(
207            MessageCategory::OrdinaryDifferentialEquation,
208            MessageType::ODEExact,
209            1,
210        ),
211        MessageTemplate::new(
212            "Find Potential Function",
213            "Find function F({independent}, {dependent}) such that:\n∂F/∂{independent} = M({independent}, {dependent})\n∂F/∂{dependent} = N({independent}, {dependent})",
214            &["independent", "dependent"],
215        ),
216    );
217
218    registry.insert(
219        MessageKey::new(
220            MessageCategory::OrdinaryDifferentialEquation,
221            MessageType::ODEExact,
222            2,
223        ),
224        MessageTemplate::new(
225            "Implicit Solution",
226            "The solution is given implicitly by:\nF({independent}, {dependent}) = C\nWhere {solution_implicit}",
227            &["independent", "dependent", "solution_implicit"],
228        ),
229    );
230}
231
232fn initialize_bernoulli_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
233    registry.insert(
234        MessageKey::new(
235            MessageCategory::OrdinaryDifferentialEquation,
236            MessageType::ODEBernoulli,
237            0,
238        ),
239        MessageTemplate::new(
240            "Bernoulli Equation",
241            "This is a Bernoulli equation: dy/dx + P({independent}){dependent} = Q({independent}){dependent}^n\nWith n = {n_value}, P = {p_func}, Q = {q_func}",
242            &["independent", "dependent", "n_value", "p_func", "q_func"],
243        ),
244    );
245
246    registry.insert(
247        MessageKey::new(
248            MessageCategory::OrdinaryDifferentialEquation,
249            MessageType::ODESubstitution,
250            1,
251        ),
252        MessageTemplate::new(
253            "Substitution to Linearize",
254            "Let v = {dependent}^(1-n) = {dependent}^{exponent}\nThen dv/d{independent} = (1-n){dependent}^(-n) d{dependent}/d{independent}",
255            &["dependent", "exponent", "independent"],
256        ),
257    );
258
259    registry.insert(
260        MessageKey::new(
261            MessageCategory::OrdinaryDifferentialEquation,
262            MessageType::ODEBernoulli,
263            1,
264        ),
265        MessageTemplate::new(
266            "Linear ODE in v",
267            "After substitution, we get a linear ODE:\ndv/d{independent} + (1-n)P({independent})v = (1-n)Q({independent})\nSolve this linear ODE for v, then substitute back",
268            &["independent"],
269        ),
270    );
271}
272
273fn initialize_second_order_messages(registry: &mut HashMap<MessageKey, MessageTemplate>) {
274    registry.insert(
275        MessageKey::new(
276            MessageCategory::OrdinaryDifferentialEquation,
277            MessageType::ODEConstantCoefficients,
278            0,
279        ),
280        MessageTemplate::new(
281            "Second-Order Constant Coefficients",
282            "This is a second-order linear ODE with constant coefficients:\na{y_double_prime} + b{y_prime} + c{dependent} = {rhs}\nCoefficients: a = {a_val}, b = {b_val}, c = {c_val}",
283            &["y_double_prime", "y_prime", "dependent", "rhs", "a_val", "b_val", "c_val"],
284        ),
285    );
286
287    registry.insert(
288        MessageKey::new(
289            MessageCategory::OrdinaryDifferentialEquation,
290            MessageType::ODECharacteristicEquation,
291            0,
292        ),
293        MessageTemplate::new(
294            "Characteristic Equation",
295            "Form the characteristic equation by assuming {dependent} = exp(r{independent}):\nar^2 + br + c = 0\n{a_val}r^2 + {b_val}r + {c_val} = 0",
296            &["dependent", "independent", "a_val", "b_val", "c_val"],
297        ),
298    );
299
300    registry.insert(
301        MessageKey::new(
302            MessageCategory::OrdinaryDifferentialEquation,
303            MessageType::ODECharacteristicEquation,
304            1,
305        ),
306        MessageTemplate::new(
307            "Solve for Roots",
308            "Using quadratic formula:\nr = (-b ± sqrt(b^2 - 4ac)) / (2a)\nr = {roots}\nDiscriminant: {discriminant}",
309            &["roots", "discriminant"],
310        ),
311    );
312
313    registry.insert(
314        MessageKey::new(
315            MessageCategory::OrdinaryDifferentialEquation,
316            MessageType::ODEConstantCoefficients,
317            1,
318        ),
319        MessageTemplate::new(
320            "Homogeneous Solution (Real Distinct Roots)",
321            "Since roots r1 = {r1} and r2 = {r2} are real and distinct:\n{dependent}_h({independent}) = C1*exp({r1}{independent}) + C2*exp({r2}{independent})",
322            &["r1", "r2", "dependent", "independent"],
323        ),
324    );
325
326    registry.insert(
327        MessageKey::new(
328            MessageCategory::OrdinaryDifferentialEquation,
329            MessageType::ODEConstantCoefficients,
330            2,
331        ),
332        MessageTemplate::new(
333            "Homogeneous Solution (Repeated Root)",
334            "Since roots are equal r1 = r2 = {r}:\n{dependent}_h({independent}) = (C1 + C2{independent})*exp({r}{independent})",
335            &["r", "dependent", "independent"],
336        ),
337    );
338
339    registry.insert(
340        MessageKey::new(
341            MessageCategory::OrdinaryDifferentialEquation,
342            MessageType::ODEConstantCoefficients,
343            3,
344        ),
345        MessageTemplate::new(
346            "Homogeneous Solution (Complex Roots)",
347            "Since roots are complex r = {alpha} ± {beta}i:\n{dependent}_h({independent}) = exp({alpha}{independent})[C1*cos({beta}{independent}) + C2*sin({beta}{independent})]",
348            &["alpha", "beta", "dependent", "independent"],
349        ),
350    );
351
352    registry.insert(
353        MessageKey::new(
354            MessageCategory::OrdinaryDifferentialEquation,
355            MessageType::ODEUndeterminedCoefficients,
356            0,
357        ),
358        MessageTemplate::new(
359            "Method of Undetermined Coefficients",
360            "For non-homogeneous ODE with {rhs_type} forcing:\nGuess particular solution form: {particular_guess}",
361            &["rhs_type", "particular_guess"],
362        ),
363    );
364
365    registry.insert(
366        MessageKey::new(
367            MessageCategory::OrdinaryDifferentialEquation,
368            MessageType::ODEVariationParameters,
369            0,
370        ),
371        MessageTemplate::new(
372            "Variation of Parameters",
373            "Use variation of parameters for general forcing function.\nAssume {dependent}_p = u1({independent}){y1} + u2({independent}){y2}\nWhere {y1}, {y2} are homogeneous solutions",
374            &["dependent", "independent", "y1", "y2"],
375        ),
376    );
377
378    registry.insert(
379        MessageKey::new(
380            MessageCategory::OrdinaryDifferentialEquation,
381            MessageType::ODECauchyEuler,
382            0,
383        ),
384        MessageTemplate::new(
385            "Cauchy-Euler Equation",
386            "This is a Cauchy-Euler (equidimensional) equation:\na{independent}^2{y_double_prime} + b{independent}{y_prime} + c{dependent} = 0\nSubstitute {dependent} = {independent}^r",
387            &["independent", "y_double_prime", "y_prime", "dependent"],
388        ),
389    );
390
391    registry.insert(
392        MessageKey::new(
393            MessageCategory::OrdinaryDifferentialEquation,
394            MessageType::ODECauchyEuler,
395            1,
396        ),
397        MessageTemplate::new(
398            "Indicial Equation",
399            "After substitution, the indicial equation is:\nar(r-1) + br + c = 0\n{indicial_eq}\nSolve for r",
400            &["indicial_eq"],
401        ),
402    );
403}