use std::collections::HashMap;
use crate::hash::RollingHash;
use crate::splay::SplayTree;
use crate::types::{Command, DiffOptions};
pub fn diff_greedy(r: &[u8], v: &[u8], opts: &DiffOptions) -> Vec<Command> {
let p = opts.p;
let verbose = opts.verbose;
let use_splay = opts.use_splay;
let mut commands = Vec::new();
if v.is_empty() {
return commands;
}
let mut h_r: HashMap<u64, Vec<usize>> = HashMap::new();
let mut splay_r: SplayTree<Vec<usize>> = SplayTree::new();
if r.len() >= p {
let mut rh = RollingHash::new(r, 0, p);
if use_splay {
splay_r.insert_or_get(rh.value(), Vec::new()).push(0);
} else {
h_r.entry(rh.value()).or_default().push(0);
}
for a in 1..=(r.len() - p) {
rh.roll(r[a - 1], r[a + p - 1]);
if use_splay {
splay_r.insert_or_get(rh.value(), Vec::new()).push(a);
} else {
h_r.entry(rh.value()).or_default().push(a);
}
}
}
if verbose {
eprintln!(
"greedy: {}, |R|={}, |V|={}, seed_len={}",
if use_splay { "splay tree" } else { "hash table" },
r.len(), v.len(), p
);
}
let mut v_c: usize = 0;
let mut v_s: usize = 0;
let mut rh_v: Option<RollingHash> = if v.len() >= p { Some(RollingHash::new(v, 0, p)) } else { None };
let mut rh_v_pos: usize = 0;
loop {
if v_c + p > v.len() {
break;
}
let fp_v = if let Some(ref mut rh) = rh_v {
if v_c == rh_v_pos {
rh.value()
} else if v_c == rh_v_pos + 1 {
rh.roll(v[v_c - 1], v[v_c + p - 1]);
rh_v_pos = v_c;
rh.value()
} else {
*rh = RollingHash::new(v, v_c, p);
rh_v_pos = v_c;
rh.value()
}
} else {
break;
};
let mut best_rm: Option<usize> = None;
let mut best_len: usize = 0;
let offsets: Option<&[usize]> = if use_splay {
splay_r.find(fp_v).map(|v| v.as_slice())
} else {
h_r.get(&fp_v).map(|v| v.as_slice())
};
if let Some(offsets) = offsets {
for &r_cand in offsets {
if r[r_cand..r_cand + p] != v[v_c..v_c + p] {
continue;
}
let mut ml = p;
while v_c + ml < v.len() && r_cand + ml < r.len() && v[v_c + ml] == r[r_cand + ml]
{
ml += 1;
}
if ml > best_len {
best_len = ml;
best_rm = Some(r_cand);
}
}
}
if best_len < p {
v_c += 1;
continue;
}
if v_s < v_c {
commands.push(Command::Add {
data: v[v_s..v_c].to_vec(),
});
}
commands.push(Command::Copy {
offset: best_rm.unwrap(),
length: best_len,
});
v_s = v_c + best_len;
v_c += best_len;
}
if v_s < v.len() {
commands.push(Command::Add {
data: v[v_s..].to_vec(),
});
}
if verbose {
super::print_command_stats(&commands);
}
commands
}
pub fn diff_greedy_default(r: &[u8], v: &[u8]) -> Vec<Command> {
diff_greedy(r, v, &DiffOptions::default())
}