use crate::range::{DiffRange, Range};
use std::ops::{Index, IndexMut};
#[derive(Debug, Clone)]
struct V {
offset: isize,
v: Vec<usize>, }
impl V {
fn new(max_d: usize) -> Self {
Self {
offset: max_d as isize,
v: vec![0; 2 * max_d],
}
}
fn len(&self) -> usize {
self.v.len()
}
}
impl Index<isize> for V {
type Output = usize;
fn index(&self, index: isize) -> &Self::Output {
&self.v[(index + self.offset) as usize]
}
}
impl IndexMut<isize> for V {
fn index_mut(&mut self, index: isize) -> &mut Self::Output {
&mut self.v[(index + self.offset) as usize]
}
}
#[derive(Debug)]
struct Snake {
x_start: usize,
y_start: usize,
x_end: usize,
y_end: usize,
}
impl ::std::fmt::Display for Snake {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
"({}, {}) -> ({}, {})",
self.x_start, self.y_start, self.x_end, self.y_end
)
}
}
fn max_d(len1: usize, len2: usize) -> usize {
(len1 + len2).div_ceil(2) + 1
}
fn find_middle_snake<T: PartialEq>(
old: Range<'_, [T]>,
new: Range<'_, [T]>,
vf: &mut V,
vb: &mut V,
) -> (isize, Snake) {
let n = old.len();
let m = new.len();
let delta = n as isize - m as isize;
let odd = delta & 1 == 1;
vf[1] = 0;
vb[1] = 0;
let d_max = max_d(n, m);
assert!(vf.len() >= d_max);
assert!(vb.len() >= d_max);
for d in 0..d_max as isize {
for k in (-d..=d).rev().step_by(2) {
let mut x = if k == -d || (k != d && vf[k - 1] < vf[k + 1]) {
vf[k + 1]
} else {
vf[k - 1] + 1
};
let mut y = (x as isize - k) as usize;
let (x0, y0) = (x, y);
if let (Some(s1), Some(s2)) = (old.get(x..), new.get(y..)) {
let advance = s1.common_prefix_len(s2);
x += advance;
y += advance;
}
vf[k] = x;
if odd && (k - delta).abs() <= (d - 1) {
if vf[k] + vb[-(k - delta)] >= n {
let snake = Snake {
x_start: x0,
y_start: y0,
x_end: x,
y_end: y,
};
return (2 * d - 1, snake);
}
}
}
for k in (-d..=d).rev().step_by(2) {
let mut x = if k == -d || (k != d && vb[k - 1] < vb[k + 1]) {
vb[k + 1]
} else {
vb[k - 1] + 1
};
let mut y = (x as isize - k) as usize;
let (x0, y0) = (x, y);
if x < n && y < m {
let advance = old.slice(..n - x).common_suffix_len(new.slice(..m - y));
x += advance;
y += advance;
}
vb[k] = x;
if !odd && (k - delta).abs() <= d {
if vb[k] + vf[-(k - delta)] >= n {
let snake = Snake {
x_start: n - x,
y_start: m - y,
x_end: n - x0,
y_end: m - y0,
};
return (2 * d, snake);
}
}
}
}
unreachable!("unable to find a middle snake");
}
fn conquer<'a, 'b, T: PartialEq>(
mut old: Range<'a, [T]>,
mut new: Range<'b, [T]>,
vf: &mut V,
vb: &mut V,
solution: &mut Vec<DiffRange<'a, 'b, [T]>>,
) {
let common_prefix_len = old.common_prefix_len(new);
if common_prefix_len > 0 {
let common_prefix = DiffRange::Equal(
old.slice(..common_prefix_len),
new.slice(..common_prefix_len),
);
solution.push(common_prefix);
}
old = old.slice(common_prefix_len..old.len());
new = new.slice(common_prefix_len..new.len());
let common_suffix_len = old.common_suffix_len(new);
let common_suffix = DiffRange::Equal(
old.slice(old.len() - common_suffix_len..),
new.slice(new.len() - common_suffix_len..),
);
old = old.slice(..old.len() - common_suffix_len);
new = new.slice(..new.len() - common_suffix_len);
if old.is_empty() && new.is_empty() {
} else if old.is_empty() {
solution.push(DiffRange::Insert(new));
} else if new.is_empty() {
solution.push(DiffRange::Delete(old));
} else {
let (_shortest_edit_script_len, snake) = find_middle_snake(old, new, vf, vb);
let (old_a, old_b) = old.split_at(snake.x_start);
let (new_a, new_b) = new.split_at(snake.y_start);
conquer(old_a, new_a, vf, vb, solution);
conquer(old_b, new_b, vf, vb, solution);
}
if common_suffix_len > 0 {
solution.push(common_suffix);
}
}
pub fn diff<'a, 'b, T: PartialEq>(old: &'a [T], new: &'b [T]) -> Vec<DiffRange<'a, 'b, [T]>> {
let old_recs = Range::new(old, ..);
let new_recs = Range::new(new, ..);
let mut solution = Vec::new();
let max_d = max_d(old.len(), new.len());
let mut vf = V::new(max_d);
let mut vb = V::new(max_d);
conquer(old_recs, new_recs, &mut vf, &mut vb, &mut solution);
solution
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_middle_snake() {
let a = Range::new(&b"ABCABBA"[..], ..);
let b = Range::new(&b"CBABAC"[..], ..);
let max_d = max_d(a.len(), b.len());
let mut vf = V::new(max_d);
let mut vb = V::new(max_d);
find_middle_snake(a, b, &mut vf, &mut vb);
}
}