use crate::{
control::ControlFlag,
dde::DDE,
dde::numerical_method::DelayNumericalMethod,
error::Error,
interpolate::Interpolation,
solout::*,
solution::Solution,
status::Status,
traits::{Real, State},
};
pub fn solve_dde<const L: usize, T, Y, S, F, H, O>(
solver: &mut S,
dde: &F,
t0: T,
tf: T,
y0: &Y,
phi: H,
solout: &mut O,
) -> Result<Solution<T, Y>, Error<T, Y>>
where
T: Real,
Y: State<T>,
F: DDE<L, T, Y>,
H: Fn(T) -> Y + Clone,
S: DelayNumericalMethod<L, T, Y, H> + Interpolation<T, Y>,
O: Solout<T, Y>,
{
let mut solution = Solution::new();
solution.timer.start();
let integration_direction = match (tf - t0).signum() {
x if x == T::one() => T::one(),
x if x == T::from_f64(-1.0).unwrap() => T::from_f64(-1.0).unwrap(),
_ => {
return Err(Error::BadInput {
msg: "Final time tf must be different from initial time t0.".to_string(),
});
}
};
match solver.init(dde, t0, tf, y0, &phi) {
Ok(evals) => {
solution.evals += evals;
}
Err(e) => return Err(e),
}
let mut y_curr = *solver.y();
let mut y_prev = *solver.y_prev();
match solout.solout(
solver.t(),
solver.t_prev(),
&y_curr,
&y_prev,
solver,
&mut solution,
) {
ControlFlag::Continue => {}
ControlFlag::ModifyState(tm, ym) => {
match solver.init(dde, tm, tf, &ym, &phi) {
Ok(evals) => {
solution.evals += evals;
}
Err(e) => return Err(e),
}
}
ControlFlag::Terminate => {
solution.status = Status::Interrupted;
solution.timer.complete();
return Ok(solution);
}
}
solver.set_status(Status::Solving);
solution.status = Status::Solving;
loop {
if (solver.t() + solver.h() - tf) * integration_direction > T::zero() {
let h_new = tf - solver.t();
if h_new.abs() < T::default_epsilon() * T::from_f64(10.0).unwrap() {
solver.set_status(Status::Complete);
solution.status = Status::Complete;
solution.timer.complete();
return Ok(solution);
}
solver.set_h(h_new);
}
match solver.step(dde, &phi) {
Ok(evals) => {
solution.evals += evals;
if let Status::RejectedStep = solver.status() {
solution.steps.rejected += 1;
continue;
} else {
solution.steps.accepted += 1;
}
}
Err(e) => {
return Err(e);
}
}
y_curr = *solver.y();
y_prev = *solver.y_prev();
match solout.solout(
solver.t(),
solver.t_prev(),
&y_curr,
&y_prev,
solver,
&mut solution,
) {
ControlFlag::Continue => {}
ControlFlag::ModifyState(tm, ym) => {
match solver.init(dde, tm, tf, &ym, &phi) {
Ok(evals) => {
solution.evals += evals;
}
Err(e) => return Err(e),
}
}
ControlFlag::Terminate => {
solution.status = Status::Interrupted;
solution.timer.complete();
return Ok(solution);
}
}
if (tf - solver.t()).abs() <= T::default_epsilon() * T::from_f64(10.0).unwrap() {
break;
}
}
solver.set_status(Status::Complete);
solution.status = Status::Complete;
solution.timer.complete();
Ok(solution)
}