use crate::instance::Instance;
use crate::solution::RouteInfo;
use crate::two_k_opt::is_route_feasible_with_reuse;
pub fn apply_four_opt(inst: &Instance, routes: &mut [Vec<usize>], infos: &mut [RouteInfo]) -> bool {
let nr = routes.len();
let mut any_improved = false;
let max_route = routes.iter().map(|r| r.len()).max().unwrap_or(0);
if max_route < 6 {
return false;
}
let stride = max_route;
let mut pos = vec![0usize; inst.n + 1];
let mut sub_reversible = vec![false; stride * stride];
let mut sub_before_later = vec![-1i32; stride * stride];
let mut cost_c = vec![0.0f64; stride * stride];
let mut cost_d = vec![0.0f64; stride * stride];
let mut best_reach_c = vec![f64::MAX; stride];
let mut best_reach_d = vec![f64::MAX; stride];
let mut pred_reach_c = vec![0usize; stride];
let mut pred_reach_d = vec![0usize; stride];
let mut best_cross_c = vec![f64::MAX; stride];
let mut best_cross_d = vec![f64::MAX; stride];
let mut pred_cross_c_i = vec![0usize; stride];
let mut pred_cross_c_j = vec![0usize; stride];
let mut pred_cross_d_i = vec![0usize; stride];
let mut pred_cross_d_j = vec![0usize; stride];
let mut new_route: Vec<usize> = Vec::with_capacity(max_route);
let mut prec_state: Vec<u8> = vec![0; inst.n + 1];
let mut touched: Vec<usize> = Vec::new();
for ri in 0..nr {
loop {
let route = &routes[ri];
let n = route.len();
if n < 6 {
break; }
let ne = n - 1;
for (idx, &v) in route.iter().enumerate() {
if v != 0 && v <= inst.n {
pos[v] = idx;
}
}
let s = n; precompute_subroute_info(
inst,
route,
&pos,
s,
&mut sub_reversible,
&mut sub_before_later,
);
compute_2ac_costs(inst, route, ne, s, &mut cost_c, &mut cost_d);
let result = dp_find_best(
inst,
route,
ne,
s,
&cost_c,
&cost_d,
&sub_reversible,
&sub_before_later,
&mut best_reach_c,
&mut best_reach_d,
&mut pred_reach_c,
&mut pred_reach_d,
&mut best_cross_c,
&mut best_cross_d,
&mut pred_cross_c_i,
&mut pred_cross_c_j,
&mut pred_cross_d_i,
&mut pred_cross_d_j,
&mut new_route,
&mut prec_state,
&mut touched,
);
for &v in routes[ri].iter() {
if v != 0 && v <= inst.n {
pos[v] = 0;
}
}
if let Some(new_r) = result {
routes[ri] = new_r;
infos[ri].compute(inst, &routes[ri]);
any_improved = true;
} else {
break;
}
}
}
any_improved
}
fn precompute_subroute_info(
inst: &Instance,
route: &[usize],
pos: &[usize],
stride: usize,
sub_reversible: &mut [bool],
sub_before_later: &mut [i32],
) {
let sz = route.len();
for i in 0..sz {
let mut reversible = true;
let mut before_later: i32 = -1;
for j in i..sz {
let v = route[j];
if v != 0 && v <= inst.n {
let pair = if inst.is_pickup(v) {
inst.pair_delivery[v]
} else {
inst.pickup_of(v)
};
if pair != 0 {
let pair_pos = pos[pair];
if pair_pos >= i && pair_pos <= j {
reversible = false;
} else if pair_pos < i {
if !inst.is_pickup(v) {
if pair_pos as i32 > before_later {
before_later = pair_pos as i32;
}
}
}
}
}
sub_reversible[i * stride + j] = reversible;
sub_before_later[i * stride + j] = before_later;
}
}
}
fn compute_2ac_costs(
inst: &Instance,
route: &[usize],
ne: usize, s: usize, cost_c: &mut [f64],
cost_d: &mut [f64],
) {
for i in 0..ne.saturating_sub(2) {
let prev_i = route[i];
let next_i = route[i + 1];
let dist_pi_ni = inst.dist(prev_i, next_i);
for j in (i + 2)..ne {
let prev_j = route[j];
let next_j = route[j + 1];
let dist_pj_nj = inst.dist(prev_j, next_j);
let delta_r = dist_pi_ni + dist_pj_nj;
cost_c[i * s + j] = inst.dist(prev_i, prev_j) + inst.dist(next_i, next_j) - delta_r;
cost_d[i * s + j] = inst.dist(prev_i, next_j) + inst.dist(next_i, prev_j) - delta_r;
}
}
}
#[derive(Clone, Copy, PartialEq)]
#[allow(dead_code)]
enum MoveType {
CC, DD, DC, CD, }
#[allow(clippy::too_many_arguments)]
fn dp_find_best(
inst: &Instance,
route: &[usize],
ne: usize,
s: usize,
cost_c: &[f64],
cost_d: &[f64],
sub_rev: &[bool],
sub_bl: &[i32],
best_reach_c: &mut [f64],
best_reach_d: &mut [f64],
pred_reach_c: &mut [usize],
pred_reach_d: &mut [usize],
best_cross_c: &mut [f64],
best_cross_d: &mut [f64],
pred_cross_c_i: &mut [usize],
pred_cross_c_j: &mut [usize],
pred_cross_d_i: &mut [usize],
pred_cross_d_j: &mut [usize],
new_route: &mut Vec<usize>,
prec_state: &mut [u8],
touched: &mut Vec<usize>,
) -> Option<Vec<usize>> {
let mut best_t = -1e-10_f64;
let mut best_move: Option<(usize, usize, usize, usize, MoveType)> = None;
let validate_cc = |i1: usize, j1: usize| -> bool { sub_rev[(i1 + 1) * s + j1] };
let validate_dd = |i1: usize, j1: usize, i2: usize, j2: usize| -> bool {
sub_bl[(i2 + 1) * s + j2] <= i1 as i32 && sub_bl[(j1 + 1) * s + j2] <= i1 as i32
};
let validate_cd = |i1: usize, j1: usize, i2: usize, j2: usize| -> bool {
sub_bl[(i2 + 1) * s + j2] <= i1 as i32
&& sub_rev[(i2 + 1) * s + j1]
&& sub_rev[(j1 + 1) * s + j2]
};
let validate_dc = |i1: usize, j1: usize, i2: usize, j2: usize| -> bool {
sub_bl[(j1 + 1) * s + j2] <= i1 as i32
&& sub_rev[(i1 + 1) * s + i2]
&& sub_rev[(i2 + 1) * s + j1]
};
let mut try_candidate =
|delta: f64,
i1: usize,
j1: usize,
i2: usize,
j2: usize,
mt: MoveType,
best_t: &mut f64,
best_move: &mut Option<(usize, usize, usize, usize, MoveType)>| {
if delta < *best_t {
build_4opt_route(route, i1, j1, i2, j2, mt, new_route);
if is_route_feasible_with_reuse(inst, new_route, prec_state, touched) {
*best_t = delta;
*best_move = Some((i1, j1, i2, j2, mt));
}
}
};
for j in 2..ne {
best_reach_c[j] = cost_c[j]; best_reach_d[j] = cost_d[j]; pred_reach_c[j] = 0;
pred_reach_d[j] = 0;
if cost_c[j] < best_t && validate_cc(0, j) {
}
}
for i in 1..ne.saturating_sub(1) {
best_cross_c[i + 1] = best_reach_c[i + 1];
pred_cross_c_i[i + 1] = pred_reach_c[i + 1];
pred_cross_c_j[i + 1] = i + 1;
best_cross_d[i + 1] = best_reach_d[i + 1];
pred_cross_d_i[i + 1] = pred_reach_d[i + 1];
pred_cross_d_j[i + 1] = i + 1;
for j in (i + 2)..ne {
{
let delta = cost_d[i * s + j] + best_cross_d[j - 1];
let pi = pred_cross_d_i[j - 1];
let pj = pred_cross_d_j[j - 1];
if delta < best_t && validate_dd(pi, pj, i, j) {
try_candidate(
delta,
pi,
pj,
i,
j,
MoveType::DD,
&mut best_t,
&mut best_move,
);
}
}
{
let delta = cost_d[i * s + j] + best_cross_c[j - 1];
let pi = pred_cross_c_i[j - 1];
let pj = pred_cross_c_j[j - 1];
if delta < best_t && validate_cd(pi, pj, i, j) {
try_candidate(
delta,
pi,
pj,
i,
j,
MoveType::CD,
&mut best_t,
&mut best_move,
);
}
}
{
let delta = cost_c[i * s + j] + best_cross_d[j - 1];
let pi = pred_cross_d_i[j - 1];
let pj = pred_cross_d_j[j - 1];
if delta < best_t && validate_dc(pi, pj, i, j) {
try_candidate(
delta,
pi,
pj,
i,
j,
MoveType::DC,
&mut best_t,
&mut best_move,
);
}
}
if j < ne - 1 {
if best_reach_c[j] < best_cross_c[j - 1] {
best_cross_c[j] = best_reach_c[j];
pred_cross_c_i[j] = pred_reach_c[j];
pred_cross_c_j[j] = j;
} else {
best_cross_c[j] = best_cross_c[j - 1];
pred_cross_c_i[j] = pred_cross_c_i[j - 1];
pred_cross_c_j[j] = pred_cross_c_j[j - 1];
}
if best_reach_d[j] < best_cross_d[j - 1] {
best_cross_d[j] = best_reach_d[j];
pred_cross_d_i[j] = pred_reach_d[j];
pred_cross_d_j[j] = j;
} else {
best_cross_d[j] = best_cross_d[j - 1];
pred_cross_d_i[j] = pred_cross_d_i[j - 1];
pred_cross_d_j[j] = pred_cross_d_j[j - 1];
}
if cost_c[i * s + j] < best_reach_c[j] {
best_reach_c[j] = cost_c[i * s + j];
pred_reach_c[j] = i;
}
if cost_d[i * s + j] < best_reach_d[j] {
best_reach_d[j] = cost_d[i * s + j];
pred_reach_d[j] = i;
}
}
}
}
if let Some((i1, j1, i2, j2, mt)) = best_move {
build_4opt_route(route, i1, j1, i2, j2, mt, new_route);
debug_assert!(is_route_feasible_with_reuse(
inst, new_route, prec_state, touched
));
Some(new_route.clone())
} else {
None
}
}
fn build_4opt_route(
route: &[usize],
i1: usize,
j1: usize,
i2: usize,
j2: usize,
mt: MoveType,
buf: &mut Vec<usize>,
) {
buf.clear();
match mt {
MoveType::CC => {
buf.extend_from_slice(&route[..=i1]);
for k in (i1 + 1..=j1).rev() {
buf.push(route[k]);
}
buf.extend_from_slice(&route[j1 + 1..]);
}
MoveType::DD => {
buf.extend_from_slice(&route[..=i1]);
buf.extend_from_slice(&route[j1 + 1..=j2]);
buf.extend_from_slice(&route[i2 + 1..=j1]);
buf.extend_from_slice(&route[i1 + 1..=i2]);
buf.extend_from_slice(&route[j2 + 1..]);
}
MoveType::DC => {
buf.extend_from_slice(&route[..=i1]);
buf.extend_from_slice(&route[j1 + 1..=j2]);
for k in (i1 + 1..=i2).rev() {
buf.push(route[k]);
}
for k in (i2 + 1..=j1).rev() {
buf.push(route[k]);
}
buf.extend_from_slice(&route[j2 + 1..]);
}
MoveType::CD => {
buf.extend_from_slice(&route[..=i1]);
for k in (i2 + 1..=j1).rev() {
buf.push(route[k]);
}
for k in (j1 + 1..=j2).rev() {
buf.push(route[k]);
}
buf.extend_from_slice(&route[i1 + 1..=i2]);
buf.extend_from_slice(&route[j2 + 1..]);
}
}
}