use super::types::*;
use converge_pack::gate::GateResult as Result;
use converge_pack::gate::{ProblemSpec, ReplayEnvelope, SolverReport};
pub struct NearestNeighborTwoOptSolver;
impl NearestNeighborTwoOptSolver {
pub fn solve(&self, input: &TspInput, spec: &ProblemSpec) -> Result<(TspOutput, SolverReport)> {
let n = input.cities.len();
let mut tour = Vec::with_capacity(n);
let mut visited = vec![false; n];
tour.push(0);
visited[0] = true;
for _ in 1..n {
let last = *tour.last().unwrap();
let mut best_next = None;
let mut best_dist = f64::INFINITY;
for j in 0..n {
if !visited[j] {
let d = input.distance(last, j);
if d < best_dist {
best_dist = d;
best_next = Some(j);
}
}
}
if let Some(next) = best_next {
tour.push(next);
visited[next] = true;
}
}
let mut improved = true;
while improved {
improved = false;
for i in 0..n - 1 {
for j in (i + 2)..n {
let a = tour[i];
let b = tour[i + 1];
let c = tour[j];
let d = tour[(j + 1) % n];
let current = input.distance(a, b) + input.distance(c, d);
let swapped = input.distance(a, c) + input.distance(b, d);
if swapped < current - 1e-10 {
tour[i + 1..=j].reverse();
improved = true;
}
}
}
}
let total_distance = Self::tour_length(input, &tour);
let output = TspOutput {
tour,
total_distance,
};
let replay = ReplayEnvelope::minimal(spec.seed());
let report = SolverReport::optimal("nearest-neighbor-2opt-v1", total_distance, replay);
Ok((output, report))
}
fn tour_length(input: &TspInput, tour: &[usize]) -> f64 {
let n = tour.len();
(0..n)
.map(|i| input.distance(tour[i], tour[(i + 1) % n]))
.sum()
}
}