use crate::{Sink, myers, trimmed};
pub(crate) fn diff(a: &[u32], b: &[u32], ao: usize, bo: usize, sink: &mut Sink) {
trimmed(a, b, ao, bo, sink, middle);
}
fn middle(a: &[u32], b: &[u32], ao: usize, bo: usize, sink: &mut Sink) {
let snake = middle_snake(a, b);
if snake.d <= 1 {
myers::diff(a, b, ao, bo, sink);
return;
}
let (xs, ys, xe, ye) = (snake.x_start, snake.y_start, snake.x_end, snake.y_end);
diff(&a[..xs], &b[..ys], ao, bo, sink);
for i in 0..xe - xs {
sink.equal(ao + xs + i, bo + ys + i);
}
diff(&a[xe..], &b[ye..], ao + xe, bo + ye, sink);
}
struct Snake {
d: usize,
x_start: usize,
y_start: usize,
x_end: usize,
y_end: usize,
}
fn middle_snake(a: &[u32], b: &[u32]) -> Snake {
let (n, m) = (a.len() as isize, b.len() as isize);
let delta = n - m;
let odd = delta % 2 != 0;
let max = (n + m + 1) / 2;
let offset = max + 1;
let size = 2 * offset as usize + 1;
let mut vf = vec![0isize; size];
let mut vb = vec![0isize; size];
for d in 0..=max {
for k in (-d..=d).step_by(2) {
let i = (offset + k) as usize;
let mut x = if k == -d || (k != d && vf[i - 1] < vf[i + 1]) {
vf[i + 1]
} else {
vf[i - 1] + 1
};
let mut y = x - k;
let (x0, y0) = (x, y);
while x < n && y < m && a[x as usize] == b[y as usize] {
x += 1;
y += 1;
}
vf[i] = x;
let kr = delta - k;
if odd && kr.abs() < d && vf[i] + vb[(offset + kr) as usize] >= n {
return Snake {
d: (2 * d - 1) as usize,
x_start: x0 as usize,
y_start: y0 as usize,
x_end: x as usize,
y_end: y as usize,
};
}
}
for k in (-d..=d).step_by(2) {
let i = (offset + k) as usize;
let mut x = if k == -d || (k != d && vb[i - 1] < vb[i + 1]) {
vb[i + 1]
} else {
vb[i - 1] + 1
};
let mut y = x - k;
let (x0, y0) = (x, y);
while x < n && y < m && a[(n - 1 - x) as usize] == b[(m - 1 - y) as usize] {
x += 1;
y += 1;
}
vb[i] = x;
let kf = delta - k;
if !odd && kf.abs() <= d && vb[i] + vf[(offset + kf) as usize] >= n {
return Snake {
d: (2 * d) as usize,
x_start: (n - x) as usize,
y_start: (m - y) as usize,
x_end: (n - x0) as usize,
y_end: (m - y0) as usize,
};
}
}
}
unreachable!("the forward and reverse searches always meet within ⌈(N+M)/2⌉ rounds")
}