1use std::collections::HashMap;
11use std::hash::Hash;
12
13pub const DEFAULT_MAX_COST: usize = 1024;
16
17const MAX_COST_CEILING: usize = 2048;
19
20const MAX_DEPTH: u32 = 6;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum Op {
26 Equal { old: usize, new: usize, len: usize },
28 Delete { old: usize, len: usize },
30 Insert { new: usize, len: usize },
32}
33
34#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct Edit {
37 pub ops: Vec<Op>,
39 pub coarse: bool,
41}
42
43impl Edit {
44 pub fn cost(&self) -> usize {
46 self.ops
47 .iter()
48 .map(|op| match op {
49 Op::Equal { .. } => 0,
50 Op::Delete { len, .. } | Op::Insert { len, .. } => *len,
51 })
52 .fold(0usize, usize::saturating_add)
53 }
54}
55
56#[derive(Clone, Copy, PartialEq, Eq)]
57enum Step {
58 Eq,
59 Del,
60 Ins,
61}
62
63pub fn diff_ops<T: Eq + Hash>(a: &[T], b: &[T], max_cost: usize) -> Edit {
74 let mut ids: HashMap<&T, u32> = HashMap::new();
75 let mut ia = Vec::with_capacity(a.len());
76 for x in a {
77 let next = u32::try_from(ids.len()).unwrap_or(u32::MAX);
78 ia.push(*ids.entry(x).or_insert(next));
79 }
80 let mut ib = Vec::with_capacity(b.len());
81 for x in b {
82 let next = u32::try_from(ids.len()).unwrap_or(u32::MAX);
83 ib.push(*ids.entry(x).or_insert(next));
84 }
85 diff_ids(&ia, &ib, max_cost)
86}
87
88pub(crate) fn diff_ids(a: &[u32], b: &[u32], max_cost: usize) -> Edit {
90 let cap = max_cost.clamp(1, MAX_COST_CEILING);
91 let mut steps = Vec::with_capacity(a.len().saturating_add(b.len()));
92 let mut coarse = false;
93 let mut scratch = Vec::new();
94 rec(a, b, cap, 0, &mut steps, &mut coarse, &mut scratch);
95 Edit { ops: compress(&steps), coarse }
96}
97
98fn compress(steps: &[Step]) -> Vec<Op> {
99 let mut ops: Vec<Op> = Vec::new();
100 let (mut x, mut y) = (0usize, 0usize);
101 for s in steps {
102 match (s, ops.last_mut()) {
103 (Step::Eq, Some(Op::Equal { len, .. })) | (Step::Del, Some(Op::Delete { len, .. })) | (Step::Ins, Some(Op::Insert { len, .. })) => {
104 *len = len.saturating_add(1);
105 }
106 (Step::Eq, _) => ops.push(Op::Equal { old: x, new: y, len: 1 }),
107 (Step::Del, _) => ops.push(Op::Delete { old: x, len: 1 }),
108 (Step::Ins, _) => ops.push(Op::Insert { new: y, len: 1 }),
109 }
110 match s {
111 Step::Eq => {
112 x = x.saturating_add(1);
113 y = y.saturating_add(1);
114 }
115 Step::Del => x = x.saturating_add(1),
116 Step::Ins => y = y.saturating_add(1),
117 }
118 }
119 ops
120}
121
122fn push_n(out: &mut Vec<Step>, step: Step, n: usize) {
123 out.extend(std::iter::repeat_n(step, n));
124}
125
126fn rec(a: &[u32], b: &[u32], cap: usize, depth: u32, out: &mut Vec<Step>, coarse: &mut bool, scratch: &mut Vec<i64>) {
127 let prefix = a.iter().zip(b).take_while(|(x, y)| x == y).count();
128 let (a, b) = (a.get(prefix..).unwrap_or(&[]), b.get(prefix..).unwrap_or(&[]));
129 let suffix = a.iter().rev().zip(b.iter().rev()).take_while(|(x, y)| x == y).count();
130 let (a, b) = (a.get(..a.len() - suffix).unwrap_or(&[]), b.get(..b.len() - suffix).unwrap_or(&[]));
131 push_n(out, Step::Eq, prefix);
132 if a.is_empty() || b.is_empty() {
133 push_n(out, Step::Del, a.len());
134 push_n(out, Step::Ins, b.len());
135 } else if !myers(a, b, cap, out, scratch) {
136 let anchors = if depth < MAX_DEPTH { unique_anchors(a, b) } else { Vec::new() };
137 if anchors.is_empty() {
138 *coarse = true;
139 push_n(out, Step::Del, a.len());
140 push_n(out, Step::Ins, b.len());
141 } else {
142 let (mut i, mut j) = (0usize, 0usize);
143 for (ai, bj) in anchors {
144 let (Some(pa), Some(pb)) = (a.get(i..ai), b.get(j..bj)) else { continue };
145 rec(pa, pb, cap, depth + 1, out, coarse, scratch);
146 out.push(Step::Eq);
147 i = ai + 1;
148 j = bj + 1;
149 }
150 rec(a.get(i..).unwrap_or(&[]), b.get(j..).unwrap_or(&[]), cap, depth + 1, out, coarse, scratch);
151 }
152 }
153 push_n(out, Step::Eq, suffix);
154}
155
156const NONE: i64 = i64::MIN;
158
159fn myers(a: &[u32], b: &[u32], cap: usize, out: &mut Vec<Step>, trace: &mut Vec<i64>) -> bool {
165 let (n, m) = (a.len() as i64, b.len() as i64);
166 let limit = (cap as i64).min(n + m);
167 trace.clear();
168 let snake = |mut x: i64, mut y: i64| {
169 while x < n && y < m && a.get(x as usize) == b.get(y as usize) {
170 x += 1;
171 y += 1;
172 }
173 x
174 };
175 for d in 0..=limit {
176 for j in 0..=d {
177 let k = 2 * j - d;
178 let x = if d == 0 { Some(0) } else { choose(trace, d, k, n, m).map(|(x, _)| x) };
179 let x = match x {
180 Some(x) => snake(x, x - k),
181 None => NONE,
182 };
183 trace.push(x);
184 if x == n && x - k == m {
185 backtrack(trace, d, n, m, out);
186 return true;
187 }
188 }
189 }
190 false
191}
192
193fn choose(trace: &[i64], d: i64, k: i64, n: i64, m: i64) -> Option<(i64, bool)> {
196 if k < -m || k > n {
197 return None;
198 }
199 let prev = d - 1;
200 let start = (prev * (prev + 1) / 2) as usize;
201 let at = |kk: i64| -> Option<i64> {
202 if kk < -prev || kk > prev {
203 return None;
204 }
205 let x = *trace.get(start + ((kk + prev) / 2) as usize)?;
206 (x != NONE).then_some(x)
207 };
208 let down = at(k + 1).filter(|x| x - k <= m);
209 let right = at(k - 1).map(|x| x + 1).filter(|x| *x <= n);
210 match (down, right) {
211 (Some(xd), Some(xr)) if xd >= xr => Some((xd, true)),
212 (_, Some(xr)) => Some((xr, false)),
213 (Some(xd), None) => Some((xd, true)),
214 (None, None) => None,
215 }
216}
217
218fn backtrack(trace: &[i64], last: i64, n: i64, m: i64, out: &mut Vec<Step>) {
219 let mut rev: Vec<Step> = Vec::new();
220 let (mut x, mut y) = (n, m);
221 let mut d = last;
222 while d > 0 {
223 let k = x - y;
224 let Some((mx, down)) = choose(trace, d, k, n, m) else { break };
225 for _ in mx..x {
227 rev.push(Step::Eq);
228 }
229 if down {
230 rev.push(Step::Ins);
231 x = mx;
232 y = mx - (k + 1);
233 } else {
234 rev.push(Step::Del);
235 x = mx - 1;
236 y = x - (k - 1);
237 }
238 d -= 1;
239 }
240 for _ in 0..x.min(y) {
241 rev.push(Step::Eq);
242 }
243 out.extend(rev.into_iter().rev());
244}
245
246fn unique_anchors(a: &[u32], b: &[u32]) -> Vec<(usize, usize)> {
249 let mut seen: HashMap<u32, (u32, u32, usize)> = HashMap::new();
251 for x in a {
252 let e = seen.entry(*x).or_insert((0, 0, 0));
253 e.0 = e.0.saturating_add(1);
254 }
255 for (j, x) in b.iter().enumerate() {
256 if let Some(e) = seen.get_mut(x) {
257 e.1 = e.1.saturating_add(1);
258 e.2 = j;
259 }
260 }
261 let pairs: Vec<(usize, usize)> =
262 a.iter().enumerate().filter_map(|(i, x)| seen.get(x).filter(|e| e.0 == 1 && e.1 == 1).map(|e| (i, e.2))).collect();
263 longest_increasing(&pairs)
264}
265
266fn longest_increasing(pairs: &[(usize, usize)]) -> Vec<(usize, usize)> {
268 let mut tails: Vec<usize> = Vec::new();
270 let mut prev: Vec<Option<usize>> = vec![None; pairs.len()];
271 for (idx, (_, v)) in pairs.iter().enumerate() {
272 let pos = tails.partition_point(|&t| pairs.get(t).is_some_and(|p| p.1 < *v));
273 if let Some(p) = prev.get_mut(idx) {
274 *p = pos.checked_sub(1).and_then(|q| tails.get(q).copied());
275 }
276 if pos < tails.len() {
277 if let Some(t) = tails.get_mut(pos) {
278 *t = idx;
279 }
280 } else {
281 tails.push(idx);
282 }
283 }
284 let mut run = Vec::with_capacity(tails.len());
285 let mut cur = tails.last().copied();
286 while let Some(c) = cur {
287 if let Some(p) = pairs.get(c) {
288 run.push(*p);
289 }
290 cur = prev.get(c).copied().flatten();
291 if run.len() > pairs.len() {
292 break;
293 }
294 }
295 run.reverse();
296 run
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 fn apply(a: &[u32], b: &[u32], edit: &Edit) -> Vec<u32> {
304 let mut out = Vec::new();
305 for op in &edit.ops {
306 match *op {
307 Op::Equal { old, len, .. } => out.extend_from_slice(&a[old..old + len]),
308 Op::Delete { .. } => {}
309 Op::Insert { new, len } => out.extend_from_slice(&b[new..new + len]),
310 }
311 }
312 out
313 }
314
315 #[test]
316 fn simple_scripts() {
317 let a = [1, 2, 3, 4, 5];
318 let b = [1, 3, 4, 6, 5];
319 let e = diff_ids(&a, &b, 1024);
320 assert_eq!(e.cost(), 2);
321 assert_eq!(apply(&a, &b, &e), b);
322 assert!(!e.coarse);
323 }
324
325 #[test]
326 fn cap_falls_back_to_anchors_then_coarse() {
327 let a: Vec<u32> = (0..200).collect();
328 let b: Vec<u32> = (0..200).map(|i| if i % 2 == 0 { i } else { 1000 + i }).collect();
329 let e = diff_ids(&a, &b, 4);
330 assert!(e.coarse || e.cost() == 200);
331 assert_eq!(apply(&a, &b, &e), b);
332 assert_eq!(e.cost(), 200);
334 let c: Vec<u32> = (0..50).map(|i| 5000 + i).collect();
335 let e = diff_ids(&a[..50], &c, 4);
336 assert!(e.coarse);
337 assert_eq!(e.cost(), 100);
338 }
339
340 #[test]
341 fn lis_is_increasing() {
342 let pairs = [(0, 5), (1, 1), (2, 2), (3, 9), (4, 3), (5, 4)];
343 assert_eq!(longest_increasing(&pairs), vec![(1, 1), (2, 2), (4, 3), (5, 4)]);
344 }
345}