1use 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#[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#[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#[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#[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
56pub 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 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 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 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 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 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 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 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 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 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
177pub type SmartStep = Step;
179
180pub 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
227pub struct StepFactory;
229
230pub type SmartStepFactory = StepFactory;
232
233pub type EnhancedStepFactory = StepFactory;
235
236impl StepFactory {
237 pub fn linear_introduction(equation: &Expression, variable: &Symbol) -> EnhancedStep {
239 Self::linear_introduction_with_format(equation, variable, &FormatContext::default())
240 }
241
242 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 pub fn linear_strategy(variable: &Symbol) -> EnhancedStep {
269 Self::linear_strategy_with_format(variable, &FormatContext::default())
270 }
271
272 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 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 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 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 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 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 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 pub fn linear_no_solution(equation: &Expression) -> SmartStep {
432 Self::linear_no_solution_with_format(equation, &FormatContext::default())
433 }
434
435 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 pub fn linear_infinite_solutions(equation: &Expression, variable: &Symbol) -> SmartStep {
460 Self::linear_infinite_solutions_with_format(equation, variable, &FormatContext::default())
461 }
462
463 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#[derive(Debug, Clone, PartialEq)]
491pub enum DifficultyLevel {
492 Beginner,
493 Intermediate,
494 Advanced,
495}
496
497#[derive(Debug, Clone, PartialEq)]
499pub struct EducationalResult {
500 pub result: crate::core::Expression,
501 pub difficulty: DifficultyLevel,
502}