use crate::equations::{Equations, State, Work};
use crate::lane::{Lane, DV};
use crate::taylor::{plan_path, taylor_path, Plan};
use multidouble::dd::DD;
use multidouble::field::Fl;
#[derive(Clone, Debug)]
pub struct ExtraRow {
pub terms: Vec<(usize, DD)>,
pub rhs: DD,
}
pub struct Shooting<E: Equations<B>, const B: usize> {
pub eq: E,
pub nodes: Vec<f64>,
pub segs: Vec<Vec<f64>>,
pub kt: usize,
pub match_node: usize,
pub extra: Vec<ExtraRow>,
pub row_scale: Vec<f64>,
#[cfg(feature = "mutate")]
pub(crate) control: crate::control::Control,
}
impl<E: Equations<B>, const B: usize> Shooting<E, B> {
pub const M: usize = 8 * B;
pub fn new(eq: E, nodes: Vec<f64>, kt: usize) -> Self {
assert!(!nodes.is_empty(), "Shooting::new: no nodes");
assert!(
nodes.windows(2).all(|w| w[0] < w[1]),
"Shooting::new: nodes not increasing"
);
let segs = nodes.windows(2).map(|w| vec![w[0], w[1]]).collect();
let match_node = nodes.len() - 1;
Shooting {
eq,
nodes,
segs,
kt,
match_node,
extra: Vec::new(),
row_scale: Vec::new(),
#[cfg(feature = "mutate")]
control: crate::control::Control::None,
}
}
#[cfg(feature = "mutate")]
pub fn set_control(&mut self, c: crate::control::Control) {
self.control = c;
}
pub fn k(&self) -> usize {
self.nodes.len()
}
pub fn len(&self) -> usize {
self.off_global() + E::NG
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn rows(&self) -> usize {
(self.k() + 1) * E::NF * Self::M + self.extra.len()
}
pub fn is_square(&self) -> bool {
self.rows() == self.len()
}
pub fn off_node(&self, i: usize) -> usize {
E::NL * Self::M + E::NF * Self::M * i
}
pub fn off_right(&self) -> usize {
self.off_node(self.k())
}
pub fn off_global(&self) -> usize {
self.off_right() + E::NR * Self::M
}
pub fn row_block(&self, b: usize) -> usize {
b * E::NF * Self::M
}
pub fn row_extra(&self) -> usize {
self.row_block(self.k() + 1)
}
pub fn seg_inward(&self, b: usize) -> bool {
b > self.match_node && b < self.k()
}
pub fn seg_source(&self, b: usize) -> usize {
if self.seg_inward(b) {
b
} else {
b - 1
}
}
pub fn block_inputs(&self, b: usize) -> Vec<usize> {
let m = Self::M;
let k = self.k();
if b == 0 {
(0..E::NL).map(|f| f * m).collect()
} else if b < k {
let o = self.off_node(self.seg_source(b));
(0..E::NF).map(|f| o + f * m).collect()
} else {
(0..E::NR).map(|f| self.off_right() + f * m).collect()
}
}
pub fn block_target(&self, b: usize) -> usize {
let k = self.k();
if b == 0 {
self.off_node(0)
} else if b < k {
self.off_node(if self.seg_inward(b) { b - 1 } else { b })
} else {
self.off_node(k - 1)
}
}
pub fn block_state<T: Lane>(&self, b: usize, inp: &[Fl<T, B>], g: &[T], prep: &E::Prep) -> State<T, B> {
let k = self.k();
if b == 0 {
self.eq.left_state(self.nodes[0], inp, g, prep)
} else if b < k {
let mut w = Work::<T, B>::new(E::NF, E::NAUX, self.kt);
taylor_path(&self.eq, inp, &self.segs[b - 1], g, &mut w)
} else {
self.eq.right_state(self.nodes[k - 1], inp, g, prep)
}
}
pub fn plan_segments(&mut self, zh: &[f64], zl: &[f64], plan: &Plan) {
assert!(
zh.len() == self.len() && zl.len() == self.len(),
"plan_segments: z of length {}",
self.len()
);
let m = Self::M;
let og = self.off_global();
let g: Vec<DV> = (0..E::NG).map(|i| DV::splat(DD::new(zh[og + i], zl[og + i]))).collect();
let segs = (1..self.k())
.map(|b| {
let src = self.seg_source(b);
let dst = if self.seg_inward(b) { b - 1 } else { b };
let o = self.off_node(src);
let s0: Vec<Fl<DV, B>> = (0..E::NF)
.map(|f| Fl::load(&zh[o + f * m..o + (f + 1) * m], &zl[o + f * m..o + (f + 1) * m]))
.collect();
plan_path(&self.eq, &s0, self.nodes[src], self.nodes[dst], &g, plan)
})
.collect();
self.segs = segs;
self.kt = plan.kt;
}
}