mathhook_core/calculus/derivatives/educational/
basic_rules.rs1use crate::calculus::derivatives::Derivative;
4use crate::core::{Expression, Number, Symbol};
5use crate::educational::message_registry::{MessageBuilder, MessageCategory, MessageType};
6use crate::educational::step_by_step::{Step, StepByStepExplanation};
7use crate::formatter::latex::LaTeXFormatter;
8use crate::simplify::Simplify;
9
10use super::format_expr;
11
12pub fn explain_constant_derivative(expr: &Expression, variable: &Symbol) -> StepByStepExplanation {
14 let mut steps = Vec::new();
15
16 steps.push(Step {
17 title: "Identify Constant".to_owned(),
18 description: format!(
19 "{} is a constant (does not depend on {})",
20 format_expr(expr),
21 variable.name()
22 ),
23 expression: expr.clone(),
24 rule_applied: "Identification".to_owned(),
25 latex: Some(expr.to_latex(None).unwrap_or_else(|_| "c".to_owned())),
26 });
27
28 if let Some(step) = MessageBuilder::new(
29 MessageCategory::Calculus,
30 MessageType::DerivativeConstant,
31 0,
32 )
33 .with_substitution("constant", format_expr(expr))
34 .build()
35 {
36 steps.push(step);
37 }
38
39 let result = Expression::integer(0);
40 steps.push(Step {
41 title: "Result".to_owned(),
42 description: "0".to_owned(),
43 expression: result.clone(),
44 rule_applied: "Constant Rule".to_owned(),
45 latex: Some("0".to_owned()),
46 });
47
48 let step_count = steps.len();
49 StepByStepExplanation {
50 initial_expression: expr.clone(),
51 final_expression: result,
52 steps,
53 total_steps: step_count,
54 rules_used: vec!["Constant Rule".to_owned()],
55 }
56}
57
58pub fn explain_variable_derivative(sym: &Symbol, variable: &Symbol) -> StepByStepExplanation {
60 let mut steps = Vec::new();
61 let expr = Expression::symbol(sym.clone());
62
63 if sym == variable {
64 steps.push(Step {
65 title: "Differentiate Variable".to_owned(),
66 description: format!("Differentiating {} with respect to itself", variable.name()),
67 expression: expr.clone(),
68 rule_applied: "Identification".to_owned(),
69 latex: Some(variable.name().to_owned()),
70 });
71
72 if let Some(step) = MessageBuilder::new(
73 MessageCategory::Calculus,
74 MessageType::DerivativeVariable,
75 0,
76 )
77 .with_substitution("variable", variable.name())
78 .build()
79 {
80 steps.push(step);
81 }
82
83 let result = Expression::integer(1);
84 steps.push(Step {
85 title: "Result".to_owned(),
86 description: "1".to_owned(),
87 expression: result.clone(),
88 rule_applied: "Variable Rule".to_owned(),
89 latex: Some("1".to_owned()),
90 });
91
92 let step_count = steps.len();
93 StepByStepExplanation {
94 initial_expression: expr,
95 final_expression: result,
96 steps,
97 total_steps: step_count,
98 rules_used: vec!["Variable Rule".to_owned()],
99 }
100 } else {
101 steps.push(Step {
102 title: "Different Variable".to_owned(),
103 description: format!(
104 "{} does not depend on {} (treating as constant)",
105 sym.name(),
106 variable.name()
107 ),
108 expression: expr.clone(),
109 rule_applied: "Identification".to_owned(),
110 latex: Some(sym.name().to_owned()),
111 });
112
113 let result = Expression::integer(0);
114 steps.push(Step {
115 title: "Result".to_owned(),
116 description: "0".to_owned(),
117 expression: result.clone(),
118 rule_applied: "Constant Rule".to_owned(),
119 latex: Some("0".to_owned()),
120 });
121
122 let step_count = steps.len();
123 StepByStepExplanation {
124 initial_expression: expr,
125 final_expression: result,
126 steps,
127 total_steps: step_count,
128 rules_used: vec!["Constant Rule".to_owned()],
129 }
130 }
131}
132
133pub fn explain_sum_derivative(terms: &[Expression], variable: &Symbol) -> StepByStepExplanation {
135 let mut steps = Vec::new();
136 let expr = Expression::add(terms.to_vec());
137
138 steps.push(Step {
139 title: "Identify Sum".to_owned(),
140 description: format!("Function is a sum of {} terms", terms.len()),
141 expression: expr.clone(),
142 rule_applied: "Identification".to_owned(),
143 latex: Some(expr.to_latex(None).unwrap_or_else(|_| "sum".to_owned())),
144 });
145
146 steps.push(Step {
147 title: "Apply Sum Rule".to_owned(),
148 description:
149 "d/dx[f + g + h + ...] = f' + g' + h' + ...\nDifferentiate each term separately"
150 .to_owned(),
151 expression: expr.clone(),
152 rule_applied: "Sum Rule".to_owned(),
153 latex: Some(expr.to_latex(None).unwrap_or_else(|_| "sum".to_owned())),
154 });
155
156 let mut derivative_terms = Vec::new();
157 for (i, term) in terms.iter().enumerate() {
158 let term_derivative = term.derivative(variable.clone());
159 derivative_terms.push(term_derivative.clone());
160
161 steps.push(Step {
162 title: format!("Differentiate Term {}", i + 1),
163 description: format!(
164 "d/d{}({}) = {}",
165 variable.name(),
166 format_expr(term),
167 format_expr(&term_derivative)
168 ),
169 expression: term_derivative.clone(),
170 rule_applied: format!("Term {} Derivative", i + 1),
171 latex: Some(
172 term_derivative
173 .to_latex(None)
174 .unwrap_or_else(|_| "term".to_owned()),
175 ),
176 });
177 }
178
179 let result = Expression::add(derivative_terms).simplify();
180 steps.push(Step {
181 title: "Combine Results".to_owned(),
182 description: format!("Add all derivatives: {}", format_expr(&result)),
183 expression: result.clone(),
184 rule_applied: "Combination".to_owned(),
185 latex: Some(
186 result
187 .to_latex(None)
188 .unwrap_or_else(|_| "result".to_owned()),
189 ),
190 });
191
192 let step_count = steps.len();
193 StepByStepExplanation {
194 initial_expression: expr,
195 final_expression: result,
196 steps,
197 total_steps: step_count,
198 rules_used: vec!["Sum Rule".to_owned()],
199 }
200}
201
202pub fn explain_power_rule(
204 base: &Expression,
205 exp: &Expression,
206 _variable: &Symbol,
207) -> StepByStepExplanation {
208 let mut steps = Vec::new();
209 let expr = Expression::pow(base.clone(), exp.clone());
210
211 steps.push(Step {
212 title: "Identify Power Function".to_owned(),
213 description: format!(
214 "Function is a power: {}^{}",
215 format_expr(base),
216 format_expr(exp)
217 ),
218 expression: expr.clone(),
219 rule_applied: "Identification".to_owned(),
220 latex: Some(expr.to_latex(None).unwrap_or_else(|_| "x^n".to_owned())),
221 });
222
223 let exp_str = if let Expression::Number(Number::Integer(n)) = exp {
224 format!("n = {}", n)
225 } else {
226 format!("n = {}", format_expr(exp))
227 };
228
229 if let Some(step) = MessageBuilder::new(
230 MessageCategory::Calculus,
231 MessageType::DerivativePowerRule,
232 0,
233 )
234 .with_substitution("expression", format_expr(&expr))
235 .with_substitution("exponent", &exp_str)
236 .build()
237 {
238 steps.push(step);
239 }
240
241 steps.push(Step {
242 title: "Apply Power Rule".to_owned(),
243 description: "d/dx(x^n) = n*x^(n-1)".to_owned(),
244 expression: expr.clone(),
245 rule_applied: "Power Rule".to_owned(),
246 latex: Some("n \\cdot x^{n-1}".to_owned()),
247 });
248
249 let n_minus_1 = Expression::add(vec![exp.clone(), Expression::integer(-1)]).simplify();
250 let result = Expression::mul(vec![
251 exp.clone(),
252 Expression::pow(base.clone(), n_minus_1.clone()),
253 ])
254 .simplify();
255
256 steps.push(Step {
257 title: "Simplify".to_owned(),
258 description: format!(
259 "{}*{}^({})",
260 format_expr(exp),
261 format_expr(base),
262 format_expr(&n_minus_1)
263 ),
264 expression: result.clone(),
265 rule_applied: "Simplification".to_owned(),
266 latex: Some(
267 result
268 .to_latex(None)
269 .unwrap_or_else(|_| "result".to_owned()),
270 ),
271 });
272
273 let step_count = steps.len();
274 StepByStepExplanation {
275 initial_expression: expr,
276 final_expression: result,
277 steps,
278 total_steps: step_count,
279 rules_used: vec!["Power Rule".to_owned()],
280 }
281}