use crate::calculus::ode::educational::{ODEExplanation, ODEStepFactory};
use crate::{expr, symbol};
pub struct ODEExamples;
impl ODEExamples {
pub fn separable_simple() -> ODEExplanation {
let x = symbol!(x);
let y = symbol!(y);
let rhs = expr!(x);
let mut steps = Vec::new();
steps.push(ODEStepFactory::detection(
"separable",
&rhs,
"The right-hand side depends only on x, so we can write dy/dx = g(x)·h(y) where g(x) = x and h(y) = 1",
));
steps.push(ODEStepFactory::separation(&rhs, &expr!(x), "x", "1"));
steps.push(ODEStepFactory::integration(
&expr!(1),
&expr!(y),
&y,
"left",
));
let integral_result = expr!((x ^ 2) / 2);
steps.push(ODEStepFactory::integration(
&expr!(x),
&integral_result,
&x,
"right",
));
let solution = expr!(y); steps.push(ODEStepFactory::solution_construction(
&expr!(y),
&solution,
"algebraic rearrangement",
));
ODEExplanation::new(
solution,
steps,
"Separable".to_owned(),
"Variable separation: Separate variables and integrate both sides".to_owned(),
)
}
pub fn exponential_growth() -> ODEExplanation {
let x = symbol!(x);
let y = symbol!(y);
let rhs = expr!(y);
let mut steps: Vec<crate::calculus::ode::ODESolutionStep> = vec![
ODEStepFactory::detection(
"separable (exponential)",
&rhs,
"Can be written as dy/dx = 1·y, where g(x) = 1 and h(y) = y",
),
ODEStepFactory::separation(&rhs, &expr!(1 / y), "1", "y"),
ODEStepFactory::integration(
&expr!(1 / y),
&expr!(y), &y,
"left",
),
ODEStepFactory::integration(&expr!(1), &expr!(x), &x, "right"),
];
let solution = expr!(y); steps.push(ODEStepFactory::solution_construction(
&expr!(y),
&solution,
"exponentiation and constant manipulation",
));
ODEExplanation::new(
solution,
steps,
"Separable (Exponential Growth)".to_owned(),
"Separation and integration yields exponential solution".to_owned(),
)
}
pub fn linear_first_order() -> ODEExplanation {
let rhs = expr!(x - y);
let steps = vec![ODEStepFactory::detection(
"linear first-order",
&rhs,
"Can be written as dy/dx + P(x)y = Q(x) where P(x) = 1 and Q(x) = x",
)];
let solution = expr!(y); ODEExplanation::new(
solution,
steps,
"Linear First-Order".to_owned(),
"Integrating factor method: μ(x) = e^(∫P(x)dx)".to_owned(),
)
}
pub fn product_separable() -> ODEExplanation {
let x = symbol!(x);
let y = symbol!(y);
let rhs = expr!(x * y);
let mut steps = vec![
ODEStepFactory::detection(
"separable (product form)",
&rhs,
"Equation dy/dx = xy can be written as dy/y = x dx (product of functions of different variables)",
),
ODEStepFactory::separation(&rhs, &expr!(1 / y), "x", "y"),
ODEStepFactory::integration(
&expr!(1 / y),
&expr!(y), &y,
"left",
),
ODEStepFactory::integration(
&expr!(x),
&expr!((x ^ 2) / 2),
&x,
"right",
),
];
let solution = expr!(y); steps.push(ODEStepFactory::solution_construction(
&expr!(y),
&solution,
"exponentiation and constant simplification",
));
ODEExplanation::new(
solution,
steps,
"Separable (Product Form)".to_owned(),
"Separate and integrate: ln|y| = x²/2 + C → y = C·e^(x²/2)".to_owned(),
)
}
pub fn initial_value_problem() -> ODEExplanation {
let x = symbol!(x);
let y = symbol!(y);
let rhs = expr!(x);
let mut steps = vec![
ODEStepFactory::detection(
"separable with initial condition",
&rhs,
"Standard separable form dy/dx = g(x) with initial condition y(0) = 1",
),
ODEStepFactory::separation(&rhs, &expr!(x), "x", "1"),
ODEStepFactory::integration(&expr!(1), &expr!(y), &y, "left"),
ODEStepFactory::integration(&expr!(x), &expr!((x ^ 2) / 2), &x, "right"),
];
let mut ic_step = ODEStepFactory::solution_construction(
&expr!(y),
&expr!(y), "applying initial condition y(0) = 1",
);
ic_step.description =
"Substitute x = 0, y = 1 into general solution to find C = 1".to_owned();
steps.push(ic_step);
let solution = expr!(y); ODEExplanation::new(
solution,
steps,
"Initial Value Problem".to_owned(),
"Solve general solution, then apply initial condition to find constant".to_owned(),
)
}
pub fn all_examples() -> Vec<ODEExplanation> {
vec![
Self::separable_simple(),
Self::exponential_growth(),
Self::linear_first_order(),
Self::product_separable(),
Self::initial_value_problem(),
]
}
pub fn get_example(name: &str) -> Option<ODEExplanation> {
match name {
"separable_simple" => Some(Self::separable_simple()),
"exponential_growth" => Some(Self::exponential_growth()),
"linear_first_order" => Some(Self::linear_first_order()),
"product_separable" => Some(Self::product_separable()),
"initial_value_problem" => Some(Self::initial_value_problem()),
_ => None,
}
}
pub fn example_names() -> Vec<&'static str> {
vec![
"separable_simple",
"exponential_growth",
"linear_first_order",
"product_separable",
"initial_value_problem",
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_separable_simple_example() {
let example = ODEExamples::separable_simple();
assert_eq!(example.ode_type, "Separable");
assert!(!example.steps.is_empty());
assert!(example.steps.len() >= 5);
}
#[test]
fn test_exponential_growth_example() {
let example = ODEExamples::exponential_growth();
assert!(example.ode_type.contains("Exponential"));
assert!(!example.steps.is_empty());
}
#[test]
fn test_linear_first_order_example() {
let example = ODEExamples::linear_first_order();
assert!(example.ode_type.contains("Linear"));
assert!(!example.steps.is_empty());
}
#[test]
fn test_product_separable_example() {
let example = ODEExamples::product_separable();
assert!(example.ode_type.contains("Product"));
assert!(!example.steps.is_empty());
}
#[test]
fn test_initial_value_problem_example() {
let example = ODEExamples::initial_value_problem();
assert!(example.ode_type.contains("Initial Value"));
assert!(!example.steps.is_empty());
}
#[test]
fn test_all_examples() {
let examples = ODEExamples::all_examples();
assert_eq!(examples.len(), 5);
for example in examples {
assert!(!example.steps.is_empty());
assert!(!example.ode_type.is_empty());
}
}
#[test]
fn test_get_example_by_name() {
let example = ODEExamples::get_example("separable_simple");
assert!(example.is_some());
let example = ODEExamples::get_example("nonexistent");
assert!(example.is_none());
}
#[test]
fn test_example_names() {
let names = ODEExamples::example_names();
assert_eq!(names.len(), 5);
assert!(names.contains(&"separable_simple"));
assert!(names.contains(&"exponential_growth"));
}
#[test]
fn test_example_human_readable() {
let example = ODEExamples::separable_simple();
let human = example.to_human_readable();
assert!(human.contains("ODE Type"));
assert!(human.contains("Method"));
assert!(human.contains("Step"));
}
#[test]
fn test_example_latex() {
let example = ODEExamples::exponential_growth();
let latex = example.to_latex();
assert!(latex.contains("\\begin{align*}"));
assert!(latex.contains("\\end{align*}"));
}
}