mathhook_core/educational/enhanced_steps/
generation.rs

1//! Step generation methods including factories and builders
2
3use crate::core::{Expression, Symbol};
4use crate::educational::step_by_step::Step;
5use serde::{Deserialize, Serialize};
6use std::collections::hash_map::DefaultHasher;
7use std::collections::HashMap;
8use std::hash::{Hash, Hasher};
9
10use super::formatting::FormatContext;
11
12/// Enhanced step with human and API data
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct EnhancedStep {
15    pub step_id: String,
16    pub title: String,
17    pub human_message: String,
18    pub api_data: StepApiData,
19    pub message_key: MessageKey,
20    pub math_context: MathContext,
21    pub presentation: super::formatting::PresentationHints,
22}
23
24/// Step API data for external applications
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct StepApiData {
27    pub category: String,
28    pub step_type: String,
29    pub operation: String,
30    pub inputs: HashMap<String, String>,
31    pub outputs: HashMap<String, String>,
32    pub properties: HashMap<String, serde_json::Value>,
33}
34
35/// Message key for external message systems
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct MessageKey {
38    pub category: String,
39    pub message_type: String,
40    pub variant: u32,
41    pub hash: u64,
42    pub template_params: Vec<String>,
43}
44
45/// Mathematical context information
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct MathContext {
48    pub equation: String,
49    pub variable: String,
50    pub current_state: String,
51    pub coefficients: HashMap<String, String>,
52    pub progress: f64,
53    pub equation_type: String,
54}
55
56/// Enhanced step builder
57pub struct EnhancedStepBuilder {
58    step_id: String,
59    title: String,
60    human_message: String,
61    api_data: StepApiData,
62    message_key: MessageKey,
63    math_context: MathContext,
64    presentation: super::formatting::PresentationHints,
65}
66
67impl EnhancedStepBuilder {
68    /// Create new enhanced step builder
69    pub fn new(step_id: &str) -> Self {
70        Self {
71            step_id: step_id.to_owned(),
72            title: String::new(),
73            human_message: String::new(),
74            api_data: StepApiData {
75                category: String::new(),
76                step_type: String::new(),
77                operation: String::new(),
78                inputs: HashMap::new(),
79                outputs: HashMap::new(),
80                properties: HashMap::new(),
81            },
82            message_key: MessageKey {
83                category: String::new(),
84                message_type: String::new(),
85                variant: 0,
86                hash: 0,
87                template_params: Vec::new(),
88            },
89            math_context: MathContext {
90                equation: String::new(),
91                variable: String::new(),
92                current_state: String::new(),
93                coefficients: HashMap::new(),
94                progress: 0.0,
95                equation_type: String::new(),
96            },
97            presentation: super::formatting::PresentationHints::default(),
98        }
99    }
100
101    /// Set human-readable content
102    pub fn with_human_message(mut self, title: &str, message: &str) -> Self {
103        self.title = title.to_owned();
104        self.human_message = message.to_owned();
105        self
106    }
107
108    /// Set API data
109    pub fn with_api_data(mut self, category: &str, step_type: &str, operation: &str) -> Self {
110        self.api_data.category = category.to_owned();
111        self.api_data.step_type = step_type.to_owned();
112        self.api_data.operation = operation.to_owned();
113        self
114    }
115
116    /// Add input data
117    pub fn with_input(mut self, key: &str, value: &str) -> Self {
118        self.api_data
119            .inputs
120            .insert(key.to_owned(), value.to_owned());
121        self
122    }
123
124    /// Add output data
125    pub fn with_output(mut self, key: &str, value: &str) -> Self {
126        self.api_data
127            .outputs
128            .insert(key.to_owned(), value.to_owned());
129        self
130    }
131
132    /// Set mathematical context
133    pub fn with_math_context(mut self, equation: &str, variable: &str, progress: f64) -> Self {
134        self.math_context.equation = equation.to_owned();
135        self.math_context.variable = variable.to_owned();
136        self.math_context.progress = progress;
137        self
138    }
139
140    /// Set message key for external systems
141    pub fn with_message_key(mut self, category: &str, message_type: &str, variant: u32) -> Self {
142        self.message_key.category = category.to_owned();
143        self.message_key.message_type = message_type.to_owned();
144        self.message_key.variant = variant;
145
146        let mut hasher = DefaultHasher::new();
147        category.hash(&mut hasher);
148        message_type.hash(&mut hasher);
149        variant.hash(&mut hasher);
150        self.message_key.hash = hasher.finish();
151
152        self
153    }
154
155    /// Set presentation hints
156    pub fn with_presentation(mut self, color: &str, importance: u8, animation: &str) -> Self {
157        self.presentation.color_theme = color.to_owned();
158        self.presentation.importance = importance;
159        self.presentation.animation = animation.to_owned();
160        self
161    }
162
163    /// Build the enhanced step
164    pub fn build(self) -> EnhancedStep {
165        EnhancedStep {
166            step_id: self.step_id,
167            title: self.title,
168            human_message: self.human_message,
169            api_data: self.api_data,
170            message_key: self.message_key,
171            math_context: self.math_context,
172            presentation: self.presentation,
173        }
174    }
175}
176
177/// Smart step type (legacy compatibility)
178pub type SmartStep = Step;
179
180/// Smart step builder for legacy system
181pub struct SmartStepBuilder {
182    title: String,
183}
184
185impl SmartStepBuilder {
186    pub fn new(title: &str) -> Self {
187        Self {
188            title: title.to_owned(),
189        }
190    }
191
192    pub fn with_human_message(self, title: &str, message: &str) -> Self {
193        Self {
194            title: format!("{}: {}", title, message),
195        }
196    }
197
198    pub fn with_api_data(self, _category: &str, _step_type: &str, _operation: &str) -> Self {
199        self
200    }
201
202    pub fn with_input(self, _key: &str, _value: &str) -> Self {
203        self
204    }
205
206    pub fn with_output(self, _key: &str, _value: &str) -> Self {
207        self
208    }
209
210    pub fn with_math_context(self, _equation: &str, _variable: &str, _progress: f64) -> Self {
211        self
212    }
213
214    pub fn with_message_key(self, _category: &str, _message_type: &str, _variant: u32) -> Self {
215        self
216    }
217
218    pub fn with_presentation(self, _color: &str, _importance: u8, _animation: &str) -> Self {
219        self
220    }
221
222    pub fn build(self) -> SmartStep {
223        Step::new(self.title, "Enhanced step content".to_owned())
224    }
225}
226
227/// Step factory for creating enhanced steps
228pub struct StepFactory;
229
230/// Smart step factory (alias)
231pub type SmartStepFactory = StepFactory;
232
233/// Enhanced step factory (alias)
234pub type EnhancedStepFactory = StepFactory;
235
236impl StepFactory {
237    /// Generate linear equation introduction step
238    pub fn linear_introduction(equation: &Expression, variable: &Symbol) -> EnhancedStep {
239        Self::linear_introduction_with_format(equation, variable, &FormatContext::default())
240    }
241
242    /// Generate linear equation introduction step with custom format
243    pub fn linear_introduction_with_format(
244        equation: &Expression,
245        variable: &Symbol,
246        context: &FormatContext,
247    ) -> EnhancedStep {
248        let equation_formatted = context.format_expression_safe(equation);
249
250        EnhancedStepBuilder::new("linear_intro_001")
251            .with_human_message(
252                "Given Equation",
253                &format!("We need to solve: {} = 0\nThis is a linear equation because {} appears only to the first power.",
254                        equation_formatted, variable.name())
255            )
256            .with_api_data("linear_equation", "introduction", "equation_analysis")
257            .with_input("original_equation", &equation_formatted)
258            .with_input("variable", variable.name())
259            .with_output("equation_type", "linear")
260            .with_output("complexity", "simple")
261            .with_math_context(&equation_formatted, variable.name(), 0.1)
262            .with_message_key("linear", "introduction", 0)
263            .with_presentation("blue", 4, "slide-in")
264            .build()
265    }
266
267    /// Generate linear equation strategy step
268    pub fn linear_strategy(variable: &Symbol) -> EnhancedStep {
269        Self::linear_strategy_with_format(variable, &FormatContext::default())
270    }
271
272    /// Generate linear equation strategy step with custom format
273    pub fn linear_strategy_with_format(
274        variable: &Symbol,
275        _context: &FormatContext,
276    ) -> EnhancedStep {
277        EnhancedStepBuilder::new("linear_strategy_001")
278            .with_human_message(
279                "Solution Strategy",
280                &format!("To solve for {}, we'll isolate it using inverse operations.\nWhatever operation is applied to {}, we'll undo it step by step.",
281                        variable.name(), variable.name())
282            )
283            .with_api_data("linear_equation", "strategy", "isolation_method")
284            .with_input("variable", variable.name())
285            .with_output("method", "inverse_operations")
286            .with_output("approach", "systematic_isolation")
287            .with_math_context("", variable.name(), 0.2)
288            .with_message_key("linear", "strategy", 0)
289            .with_presentation("green", 3, "fade-in")
290            .build()
291    }
292
293    /// Generate coefficient identification step
294    pub fn linear_coefficient_identification(
295        a: &Expression,
296        b: &Expression,
297        variable: &Symbol,
298    ) -> SmartStep {
299        Self::linear_coefficient_identification_with_format(
300            a,
301            b,
302            variable,
303            &FormatContext::default(),
304        )
305    }
306
307    /// Generate coefficient identification step with custom format
308    pub fn linear_coefficient_identification_with_format(
309        a: &Expression,
310        b: &Expression,
311        variable: &Symbol,
312        context: &FormatContext,
313    ) -> SmartStep {
314        let a_formatted = context.format_expression_safe(a);
315        let b_formatted = context.format_expression_safe(b);
316
317        SmartStepBuilder::new("linear_coeffs_001")
318            .with_human_message(
319                "Identify Components",
320                &format!("In our equation, we can identify:\n• Coefficient of {}: {}\n• Constant term: {}\nForm: {}·{} + {} = 0",
321                        variable.name(), a_formatted, b_formatted, a_formatted, variable.name(), b_formatted)
322            )
323            .with_api_data("linear_equation", "analysis", "coefficient_extraction")
324            .with_input("variable", variable.name())
325            .with_input("coefficient", &a_formatted)
326            .with_input("constant", &b_formatted)
327            .with_output("standard_form", &format!("{}x + {}", a_formatted, b_formatted))
328            .with_math_context("", variable.name(), 0.4)
329            .with_message_key("linear", "analysis", 0)
330            .with_presentation("orange", 4, "highlight")
331            .build()
332    }
333
334    /// Generate solution calculation step
335    pub fn linear_solution_calculation(
336        variable: &Symbol,
337        solution: &Expression,
338        a: &Expression,
339        b: &Expression,
340    ) -> SmartStep {
341        Self::linear_solution_calculation_with_format(
342            variable,
343            solution,
344            a,
345            b,
346            &FormatContext::default(),
347        )
348    }
349
350    /// Generate solution calculation step with custom format
351    pub fn linear_solution_calculation_with_format(
352        variable: &Symbol,
353        solution: &Expression,
354        a: &Expression,
355        b: &Expression,
356        context: &FormatContext,
357    ) -> SmartStep {
358        let a_formatted = context.format_expression_safe(a);
359        let b_formatted = context.format_expression_safe(b);
360        let solution_formatted = context.format_expression_safe(solution);
361
362        SmartStepBuilder::new("linear_calc_001")
363            .with_human_message(
364                "Calculate Solution",
365                &format!("Using the linear equation formula:\n{} = -({}) ÷ ({})\n{} = {}\nThis gives us our solution.",
366                        variable.name(), b_formatted, a_formatted, variable.name(), solution_formatted)
367            )
368            .with_api_data("linear_equation", "calculation", "division_operation")
369            .with_input("numerator", &format!("-({})", b_formatted))
370            .with_input("denominator", &a_formatted)
371            .with_input("variable", variable.name())
372            .with_output("solution", &solution_formatted)
373            .with_output("calculation_method", "division")
374            .with_math_context("", variable.name(), 0.8)
375            .with_message_key("linear", "calculation", 0)
376            .with_presentation("purple", 5, "calculate")
377            .build()
378    }
379
380    /// Generate verification step
381    pub fn linear_verification(
382        equation: &Expression,
383        variable: &Symbol,
384        solution: &Expression,
385    ) -> SmartStep {
386        Self::linear_verification_with_format(
387            equation,
388            variable,
389            solution,
390            &FormatContext::default(),
391        )
392    }
393
394    /// Generate verification step with custom format
395    pub fn linear_verification_with_format(
396        equation: &Expression,
397        variable: &Symbol,
398        solution: &Expression,
399        context: &FormatContext,
400    ) -> SmartStep {
401        let equation_formatted = context.format_expression_safe(equation);
402        let solution_formatted = context.format_expression_safe(solution);
403
404        let verification_text = format!(
405            "Substitute {} = {} into original equation",
406            variable.name(),
407            solution_formatted
408        );
409
410        SmartStepBuilder::new("linear_verify_001")
411            .with_human_message(
412                "Verify Solution",
413                &format!(
414                    "Let's check our answer:\n{}\nResult: The equation is satisfied.",
415                    verification_text
416                ),
417            )
418            .with_api_data("linear_equation", "verification", "substitution_check")
419            .with_input("original_equation", &equation_formatted)
420            .with_input("variable", variable.name())
421            .with_input("solution", &solution_formatted)
422            .with_output("verification_result", "success")
423            .with_output("substitution", &verification_text)
424            .with_math_context(&equation_formatted, variable.name(), 1.0)
425            .with_message_key("linear", "verification", 0)
426            .with_presentation("green", 4, "success")
427            .build()
428    }
429
430    /// Generate no solution step
431    pub fn linear_no_solution(equation: &Expression) -> SmartStep {
432        Self::linear_no_solution_with_format(equation, &FormatContext::default())
433    }
434
435    /// Generate no solution step with custom format
436    pub fn linear_no_solution_with_format(
437        equation: &Expression,
438        context: &FormatContext,
439    ) -> SmartStep {
440        let equation_formatted = context.format_expression_safe(equation);
441
442        SmartStepBuilder::new("linear_no_solution_001")
443            .with_human_message(
444                "No Solution",
445                &format!("This equation: {} = 0\nSimplifies to a contradiction (like 5 = 0).\nTherefore, no solution exists.",
446                        equation_formatted)
447            )
448            .with_api_data("linear_equation", "error", "no_solution")
449            .with_input("equation", &equation_formatted)
450            .with_output("result_type", "no_solution")
451            .with_output("reason", "contradiction")
452            .with_math_context(&equation_formatted, "", 1.0)
453            .with_message_key("linear", "error", 0)
454            .with_presentation("red", 5, "alert")
455            .build()
456    }
457
458    /// Generate infinite solutions step
459    pub fn linear_infinite_solutions(equation: &Expression, variable: &Symbol) -> SmartStep {
460        Self::linear_infinite_solutions_with_format(equation, variable, &FormatContext::default())
461    }
462
463    /// Generate infinite solutions step with custom format
464    pub fn linear_infinite_solutions_with_format(
465        equation: &Expression,
466        variable: &Symbol,
467        context: &FormatContext,
468    ) -> SmartStep {
469        let equation_formatted = context.format_expression_safe(equation);
470
471        SmartStepBuilder::new("linear_infinite_001")
472            .with_human_message(
473                "Infinite Solutions",
474                &format!("This equation: {} = 0\nSimplifies to 0 = 0, which is always true.\nTherefore, any value of {} is a solution.",
475                        equation_formatted, variable.name())
476            )
477            .with_api_data("linear_equation", "result", "infinite_solutions")
478            .with_input("equation", &equation_formatted)
479            .with_input("variable", variable.name())
480            .with_output("result_type", "infinite_solutions")
481            .with_output("reason", "identity_equation")
482            .with_math_context(&equation_formatted, variable.name(), 1.0)
483            .with_message_key("linear", "infinite", 0)
484            .with_presentation("blue", 4, "expand")
485            .build()
486    }
487}
488
489/// Difficulty level for educational content
490#[derive(Debug, Clone, PartialEq)]
491pub enum DifficultyLevel {
492    Beginner,
493    Intermediate,
494    Advanced,
495}
496
497/// Educational result wrapper
498#[derive(Debug, Clone, PartialEq)]
499pub struct EducationalResult {
500    pub result: crate::core::Expression,
501    pub difficulty: DifficultyLevel,
502}