use crate::proc::LevyProcess;
use crate::rng::BaseRng;
use ordered_float::OrderedFloat;
pub fn runge_kutta_iteration(
scenario_data: &mut [f64], processes: &mut [Box<LevyProcess>],
times: &[OrderedFloat<f64>],
time_idx: usize,
rng: &mut dyn BaseRng,
) {
let num_processes = processes.len();
let current_time = times[time_idx];
let dt = (times[time_idx + 1] - times[time_idx]).into_inner();
let sqrt_dt = dt.sqrt();
let offset = time_idx * num_processes;
let next_offset = (time_idx + 1) * num_processes;
let current_values = scenario_data[offset..offset + num_processes].to_vec();
let sk = if rng.sample(time_idx, 0) > 0.5 {
1.0
} else {
-1.0
};
let mut k1 = vec![0.0; num_processes];
let mut intermediate_values = current_values.clone();
for p_idx in 0..num_processes {
let mut step_k1 = 0.0;
let num_incs = processes[p_idx].incrementors.len();
for inc_idx in 0..num_incs {
let c = (processes[p_idx].coefficients[inc_idx])(¤t_values, current_time);
let d = processes[p_idx].incrementors[inc_idx].sample(time_idx, rng);
step_k1 += if inc_idx == 0 {
c * d
} else {
c * (d - sk * sqrt_dt)
};
}
k1[p_idx] = step_k1;
intermediate_values[p_idx] += step_k1;
}
let mut k2 = vec![0.0; num_processes];
for p_idx in 0..num_processes {
let mut step_k2 = 0.0;
let num_incs = processes[p_idx].incrementors.len();
for inc_idx in 0..num_incs {
let c = (processes[p_idx].coefficients[inc_idx])(&intermediate_values, current_time);
let d = processes[p_idx].incrementors[inc_idx].sample(time_idx, rng);
step_k2 += if inc_idx == 0 {
c * d
} else {
c * (d + sk * sqrt_dt)
};
}
k2[p_idx] = step_k2;
}
for p_idx in 0..num_processes {
let val = current_values[p_idx] + 0.5 * (k1[p_idx] + k2[p_idx]);
scenario_data[next_offset + p_idx] = val;
}
}