mathhook_core/calculus/derivatives/educational/
composition_rules.rs

1//! Composition-based differentiation rules: chain rule, product rule, and quotient rule
2
3use crate::calculus::derivatives::{Derivative, FunctionDerivatives};
4use crate::core::{Expression, 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
12/// Explain chain rule (5+ steps)
13pub fn explain_chain_rule(
14    func_name: &str,
15    arg: &Expression,
16    variable: &Symbol,
17) -> StepByStepExplanation {
18    let mut steps = Vec::new();
19    let expr = Expression::function(func_name, vec![arg.clone()]);
20
21    steps.push(Step {
22        title: "Identify Composite Function".to_owned(),
23        description: format!(
24            "Outer function: {}(u)\nInner function: u = {}",
25            func_name,
26            format_expr(arg)
27        ),
28        expression: expr.clone(),
29        rule_applied: "Identification".to_owned(),
30        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "f(g(x))".to_owned())),
31    });
32
33    if let Some(step) = MessageBuilder::new(
34        MessageCategory::Calculus,
35        MessageType::DerivativeChainRule,
36        0,
37    )
38    .with_substitution("outer_function", func_name)
39    .with_substitution("inner_function", format_expr(arg))
40    .build()
41    {
42        steps.push(step);
43    }
44
45    steps.push(Step {
46        title: "State Chain Rule".to_owned(),
47        description: "d/dx[f(g(x))] = f'(g(x)) * g'(x)".to_owned(),
48        expression: expr.clone(),
49        rule_applied: "Chain Rule".to_owned(),
50        latex: Some("f'(g(x)) \\cdot g'(x)".to_owned()),
51    });
52
53    let outer_derivative = FunctionDerivatives::get(func_name, arg, variable.clone());
54
55    steps.push(Step {
56        title: "Differentiate Outer Function".to_owned(),
57        description: format!(
58            "d/du[{}(u)] = {}\nEvaluated at u = {}: {}",
59            func_name,
60            func_name,
61            format_expr(arg),
62            format_expr(&outer_derivative)
63        ),
64        expression: outer_derivative.clone(),
65        rule_applied: "Outer Derivative".to_owned(),
66        latex: Some(
67            outer_derivative
68                .to_latex(None)
69                .unwrap_or_else(|_| "f'(u)".to_owned()),
70        ),
71    });
72
73    let inner_derivative = arg.derivative(variable.clone());
74    steps.push(Step {
75        title: "Differentiate Inner Function".to_owned(),
76        description: format!(
77            "d/d{}[{}] = {}",
78            variable.name(),
79            format_expr(arg),
80            format_expr(&inner_derivative)
81        ),
82        expression: inner_derivative.clone(),
83        rule_applied: "Inner Derivative".to_owned(),
84        latex: Some(
85            inner_derivative
86                .to_latex(None)
87                .unwrap_or_else(|_| "g'(x)".to_owned()),
88        ),
89    });
90
91    let result = Expression::mul(vec![outer_derivative, inner_derivative]).simplify();
92    steps.push(Step {
93        title: "Multiply Results".to_owned(),
94        description: format!("Result: {}", format_expr(&result)),
95        expression: result.clone(),
96        rule_applied: "Chain Rule Application".to_owned(),
97        latex: Some(
98            result
99                .to_latex(None)
100                .unwrap_or_else(|_| "result".to_owned()),
101        ),
102    });
103
104    let step_count = steps.len();
105    StepByStepExplanation {
106        initial_expression: expr,
107        final_expression: result,
108        steps,
109        total_steps: step_count,
110        rules_used: vec!["Chain Rule".to_owned()],
111    }
112}
113
114/// Explain product rule (5+ steps)
115pub fn explain_product_rule(
116    first: &Expression,
117    second: &Expression,
118    variable: &Symbol,
119) -> StepByStepExplanation {
120    let mut steps = Vec::new();
121    let expr = Expression::mul(vec![first.clone(), second.clone()]);
122
123    steps.push(Step {
124        title: "Identify Product".to_owned(),
125        description: format!(
126            "Two functions multiplied:\nf(x) = {}\ng(x) = {}",
127            format_expr(first),
128            format_expr(second)
129        ),
130        expression: expr.clone(),
131        rule_applied: "Identification".to_owned(),
132        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "f*g".to_owned())),
133    });
134
135    if let Some(step) = MessageBuilder::new(
136        MessageCategory::Calculus,
137        MessageType::DerivativeProductRule,
138        0,
139    )
140    .with_substitution("first_function", format_expr(first))
141    .with_substitution("second_function", format_expr(second))
142    .build()
143    {
144        steps.push(step);
145    }
146
147    steps.push(Step {
148        title: "State Product Rule".to_owned(),
149        description: "d/dx[f*g] = f'*g + f*g'".to_owned(),
150        expression: expr.clone(),
151        rule_applied: "Product Rule".to_owned(),
152        latex: Some("f' \\cdot g + f \\cdot g'".to_owned()),
153    });
154
155    let first_derivative = first.derivative(variable.clone());
156    steps.push(Step {
157        title: "Differentiate First Function".to_owned(),
158        description: format!("f'(x) = {}", format_expr(&first_derivative)),
159        expression: first_derivative.clone(),
160        rule_applied: "First Derivative".to_owned(),
161        latex: Some(
162            first_derivative
163                .to_latex(None)
164                .unwrap_or_else(|_| "f'".to_owned()),
165        ),
166    });
167
168    let second_derivative = second.derivative(variable.clone());
169    steps.push(Step {
170        title: "Differentiate Second Function".to_owned(),
171        description: format!("g'(x) = {}", format_expr(&second_derivative)),
172        expression: second_derivative.clone(),
173        rule_applied: "Second Derivative".to_owned(),
174        latex: Some(
175            second_derivative
176                .to_latex(None)
177                .unwrap_or_else(|_| "g'".to_owned()),
178        ),
179    });
180
181    let result = Expression::add(vec![
182        Expression::mul(vec![first_derivative, second.clone()]),
183        Expression::mul(vec![first.clone(), second_derivative]),
184    ])
185    .simplify();
186
187    steps.push(Step {
188        title: "Apply Product Rule Formula".to_owned(),
189        description: format!("f'*g + f*g' = {}", format_expr(&result)),
190        expression: result.clone(),
191        rule_applied: "Product Rule Application".to_owned(),
192        latex: Some(
193            result
194                .to_latex(None)
195                .unwrap_or_else(|_| "result".to_owned()),
196        ),
197    });
198
199    let step_count = steps.len();
200    StepByStepExplanation {
201        initial_expression: expr,
202        final_expression: result,
203        steps,
204        total_steps: step_count,
205        rules_used: vec!["Product Rule".to_owned()],
206    }
207}
208
209/// Explain quotient rule (6+ steps)
210pub fn explain_quotient_rule(
211    numerator: &Expression,
212    denominator: &Expression,
213    variable: &Symbol,
214) -> StepByStepExplanation {
215    let mut steps = Vec::new();
216
217    let expr = Expression::mul(vec![
218        numerator.clone(),
219        Expression::pow(denominator.clone(), Expression::integer(-1)),
220    ]);
221
222    steps.push(Step {
223        title: "Identify Quotient".to_owned(),
224        description: format!(
225            "Numerator: {}\nDenominator: {}",
226            format_expr(numerator),
227            format_expr(denominator)
228        ),
229        expression: expr.clone(),
230        rule_applied: "Identification".to_owned(),
231        latex: Some(format!(
232            "\\frac{{{}}}{{{}}}",
233            numerator.to_latex(None).unwrap_or_else(|_| "f".to_owned()),
234            denominator
235                .to_latex(None)
236                .unwrap_or_else(|_| "g".to_owned())
237        )),
238    });
239
240    if let Some(step) = MessageBuilder::new(
241        MessageCategory::Calculus,
242        MessageType::DerivativeQuotientRule,
243        0,
244    )
245    .with_substitution("numerator", format_expr(numerator))
246    .with_substitution("denominator", format_expr(denominator))
247    .build()
248    {
249        steps.push(step);
250    }
251
252    steps.push(Step {
253        title: "State Quotient Rule".to_owned(),
254        description: "d/dx[f/g] = (f'*g - f*g') / g^2".to_owned(),
255        expression: expr.clone(),
256        rule_applied: "Quotient Rule".to_owned(),
257        latex: Some("\\frac{f' \\cdot g - f \\cdot g'}{g^2}".to_owned()),
258    });
259
260    let numerator_derivative = numerator.derivative(variable.clone());
261    steps.push(Step {
262        title: "Differentiate Numerator".to_owned(),
263        description: format!("f'(x) = {}", format_expr(&numerator_derivative)),
264        expression: numerator_derivative.clone(),
265        rule_applied: "Numerator Derivative".to_owned(),
266        latex: Some(
267            numerator_derivative
268                .to_latex(None)
269                .unwrap_or_else(|_| "f'".to_owned()),
270        ),
271    });
272
273    let denominator_derivative = denominator.derivative(variable.clone());
274    steps.push(Step {
275        title: "Differentiate Denominator".to_owned(),
276        description: format!("g'(x) = {}", format_expr(&denominator_derivative)),
277        expression: denominator_derivative.clone(),
278        rule_applied: "Denominator Derivative".to_owned(),
279        latex: Some(
280            denominator_derivative
281                .to_latex(None)
282                .unwrap_or_else(|_| "g'".to_owned()),
283        ),
284    });
285
286    let result_numerator = Expression::add(vec![
287        Expression::mul(vec![numerator_derivative, denominator.clone()]),
288        Expression::mul(vec![
289            Expression::integer(-1),
290            numerator.clone(),
291            denominator_derivative,
292        ]),
293    ])
294    .simplify();
295
296    steps.push(Step {
297        title: "Apply Quotient Rule Formula".to_owned(),
298        description: format!("(f'*g - f*g') = {}", format_expr(&result_numerator)),
299        expression: result_numerator.clone(),
300        rule_applied: "Numerator Calculation".to_owned(),
301        latex: Some(
302            result_numerator
303                .to_latex(None)
304                .unwrap_or_else(|_| "result_num".to_owned()),
305        ),
306    });
307
308    let result = Expression::mul(vec![
309        result_numerator,
310        Expression::pow(denominator.clone(), Expression::integer(-2)),
311    ])
312    .simplify();
313
314    steps.push(Step {
315        title: "Simplify".to_owned(),
316        description: format!("Result: {}", format_expr(&result)),
317        expression: result.clone(),
318        rule_applied: "Quotient Rule Application".to_owned(),
319        latex: Some(
320            result
321                .to_latex(None)
322                .unwrap_or_else(|_| "result".to_owned()),
323        ),
324    });
325
326    let step_count = steps.len();
327    StepByStepExplanation {
328        initial_expression: expr,
329        final_expression: result,
330        steps,
331        total_steps: step_count,
332        rules_used: vec!["Quotient Rule".to_owned()],
333    }
334}