diff --git a/src/algorithms/lcs.rs b/src/algorithms/lcs.rs index b6f5d60..8dd7fe9 100644 --- a/src/algorithms/lcs.rs +++ b/src/algorithms/lcs.rs @@ -2,6 +2,7 @@ //! //! * time: `O((NM)D log (M)D)` //! * space `O(MN)` +use std::collections::BTreeMap; use std::ops::{Index, Range}; use crate::algorithms::DiffHook; @@ -66,7 +67,9 @@ where d.equal(old_orig_idx, new_orig_idx, 1)?; old_idx += 1; new_idx += 1; - } else if table[new_idx][old_idx + 1] >= table[new_idx + 1][old_idx] { + } else if table.get(&(new_idx, old_idx + 1)).map_or(0, |&x| x) + >= table.get(&(new_idx + 1, old_idx)).map_or(0, |&x| x) + { d.delete(old_orig_idx, 1, new_orig_idx)?; old_idx += 1; } else { @@ -117,7 +120,7 @@ fn make_table( old_range: Range, new: &New, new_range: Range, -) -> Vec> +) -> BTreeMap<(usize, usize), u32> where Old: Index + ?Sized, New: Index + ?Sized, @@ -125,17 +128,20 @@ where { let old_len = old_range.len(); let new_len = new_range.len(); - let mut table = vec![vec![0; old_len + 1]; new_len + 1]; + let mut table = BTreeMap::new(); - for i in 0..new_len { - let i = new_len - i - 1; - table[i][old_len] = 0; - for j in 0..old_len { - let j = old_len - j - 1; - table[i][j] = if new[i] == old[j] { - table[i + 1][j + 1] + 1 + for i in (0..new_len).rev() { + for j in (0..old_len).rev() { + let val = if new[i] == old[j] { + table.get(&(i + 1, j + 1)).map_or(0, |&x| x) + 1 } else { - table[i + 1][j].max(table[i][j + 1]) + table + .get(&(i + 1, j)) + .map_or(0, |&x| x) + .max(table.get(&(i, j + 1)).map_or(0, |&x| x)) + }; + if val > 0 { + table.insert((i, j), val); } } } @@ -146,7 +152,13 @@ where #[test] fn test_table() { let table = make_table(&vec![2, 3], 0..2, &vec![0, 1, 2], 0..3); - let expected = vec![vec![1, 0, 0], vec![1, 0, 0], vec![1, 0, 0], vec![0, 0, 0]]; + let expected = { + let mut m = BTreeMap::new(); + m.insert((1, 0), 1); + m.insert((0, 0), 1); + m.insert((2, 0), 1); + m + }; assert_eq!(table, expected); } diff --git a/src/text/inline.rs b/src/text/inline.rs index cfedd21..7453990 100644 --- a/src/text/inline.rs +++ b/src/text/inline.rs @@ -8,6 +8,8 @@ use crate::{capture_diff, get_diff_ratio}; use std::ops::Index; +use super::utils::upper_seq_ratio; + struct MultiLookup<'bufs, 's, T: DiffableStr + ?Sized> { strings: &'bufs [&'s T], seqs: Vec<(&'s T, usize, usize)>, @@ -184,6 +186,8 @@ impl<'s, T: DiffableStr + ?Sized> fmt::Display for InlineChange<'s, T> { } } +const MIN_RATIO: f32 = 0.5; + pub(crate) fn iter_inline_changes<'x, 'diff, 'old, 'new, 'bufs, T>( diff: &'diff TextDiff<'old, 'new, 'bufs, T>, op: &DiffOp, @@ -204,6 +208,11 @@ where let mut new_index = new_range.start; let old_slices = &diff.old_slices()[old_range]; let new_slices = &diff.new_slices()[new_range]; + + if upper_seq_ratio(old_slices, new_slices) < MIN_RATIO { + return Box::new(diff.iter_changes(op).map(|x| x.into())) as Box>; + } + let old_lookup = MultiLookup::new(old_slices); let new_lookup = MultiLookup::new(new_slices); @@ -215,7 +224,7 @@ where 0..new_lookup.len(), ); - if get_diff_ratio(&ops, old_lookup.len(), new_lookup.len()) < 0.5 { + if get_diff_ratio(&ops, old_lookup.len(), new_lookup.len()) < MIN_RATIO { return Box::new(diff.iter_changes(op).map(|x| x.into())) as Box>; }