pub const LINE_CAP: usize = 400;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Row {
Same(String),
Left(String),
Right(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Diff {
pub rows: Vec<Row>,
pub truncated: bool,
}
pub fn lines(left: &str, right: &str) -> Diff {
let heads: (Vec<&str>, Vec<&str>) = (
left.lines().take(LINE_CAP).collect(),
right.lines().take(LINE_CAP).collect(),
);
let truncated = left.lines().nth(LINE_CAP).is_some() || right.lines().nth(LINE_CAP).is_some();
let table = Table::of(&heads.0, &heads.1);
Diff {
rows: walk(&heads.0, &heads.1, &table),
truncated,
}
}
struct Table {
cells: Vec<usize>,
width: usize,
}
impl Table {
fn of(left: &[&str], right: &[&str]) -> Table {
let width = right.len() + 1;
let mut table = Table {
cells: vec![0; (left.len() + 1) * width],
width,
};
for (i, line) in left.iter().enumerate().rev() {
for (j, other) in right.iter().enumerate().rev() {
let cell = if line == other {
table.at(i + 1, j + 1) + 1
} else {
table.at(i + 1, j).max(table.at(i, j + 1))
};
if let Some(seat) = table.cells.get_mut(i * width + j) {
*seat = cell;
}
}
}
table
}
fn at(&self, i: usize, j: usize) -> usize {
self.cells.get(i * self.width + j).copied().unwrap_or(0)
}
}
fn walk(left: &[&str], right: &[&str], table: &Table) -> Vec<Row> {
let (mut i, mut j) = (0, 0);
let mut rows = Vec::new();
while let (Some(line), Some(other)) = (left.get(i), right.get(j)) {
if line == other {
rows.push(Row::Same((*line).to_owned()));
i += 1;
j += 1;
} else if table.at(i + 1, j) >= table.at(i, j + 1) {
rows.push(Row::Left((*line).to_owned()));
i += 1;
} else {
rows.push(Row::Right((*other).to_owned()));
j += 1;
}
}
rows.extend(left.iter().skip(i).map(|s| Row::Left((*s).to_owned())));
rows.extend(right.iter().skip(j).map(|s| Row::Right((*s).to_owned())));
rows
}