Refactor some of the patience code into a utils module
This commit is contained in:
parent
28bfd4698a
commit
77c7654589
3 changed files with 76 additions and 75 deletions
|
|
@ -356,17 +356,9 @@ where
|
||||||
if is_empty_range(&old_range) && is_empty_range(&new_range) {
|
if is_empty_range(&old_range) && is_empty_range(&new_range) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if is_empty_range(&new_range) {
|
} else if is_empty_range(&new_range) {
|
||||||
d.delete(
|
d.delete(old_range.start, old_range.len(), new_range.start)?;
|
||||||
old_range.start,
|
|
||||||
old_range.end - old_range.start,
|
|
||||||
new_range.start,
|
|
||||||
)?;
|
|
||||||
} else if is_empty_range(&old_range) {
|
} else if is_empty_range(&old_range) {
|
||||||
d.insert(
|
d.insert(old_range.start, new_range.start, new_range.len())?;
|
||||||
old_range.start,
|
|
||||||
new_range.start,
|
|
||||||
new_range.end - new_range.start,
|
|
||||||
)?;
|
|
||||||
} else if let Some((x_start, y_start)) = find_middle_snake(
|
} else if let Some((x_start, y_start)) = find_middle_snake(
|
||||||
old,
|
old,
|
||||||
old_range.clone(),
|
old_range.clone(),
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@
|
||||||
//!
|
//!
|
||||||
//! This is based on the patience implementation of [pijul](https://pijul.org/)
|
//! This is based on the patience implementation of [pijul](https://pijul.org/)
|
||||||
//! by Pierre-Étienne Meunier.
|
//! by Pierre-Étienne Meunier.
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::{Index, Range};
|
use std::ops::{Index, Range};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace};
|
use crate::algorithms::{myers, DiffHook, NoFinishHook, Replace};
|
||||||
|
|
||||||
|
use super::utils::{unique, Indexable};
|
||||||
|
|
||||||
/// Patience diff algorithm.
|
/// Patience diff algorithm.
|
||||||
///
|
///
|
||||||
/// Diff `old`, between indices `old_range` and `new` between indices `new_range`.
|
/// Diff `old`, between indices `old_range` and `new` between indices `new_range`.
|
||||||
|
|
@ -57,8 +57,8 @@ where
|
||||||
New::Output: PartialEq<Old::Output> + Hash + Eq,
|
New::Output: PartialEq<Old::Output> + Hash + Eq,
|
||||||
D: DiffHook,
|
D: DiffHook,
|
||||||
{
|
{
|
||||||
let old_indexes = unique(old, old_range.start, old_range.end);
|
let old_indexes = unique(old, old_range.clone());
|
||||||
let new_indexes = unique(new, new_range.start, new_range.end);
|
let new_indexes = unique(new, new_range.clone());
|
||||||
|
|
||||||
let mut d = Replace::new(Patience {
|
let mut d = Replace::new(Patience {
|
||||||
d,
|
d,
|
||||||
|
|
@ -96,59 +96,6 @@ where
|
||||||
diff(d, old, 0..old.len(), new, 0..new.len())
|
diff(d, old, 0..old.len(), new, 0..new.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Indexable<'a, T: ?Sized> {
|
|
||||||
value: &'a T,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Index<usize> + 'a> std::fmt::Debug for Indexable<'a, T>
|
|
||||||
where
|
|
||||||
T::Output: std::fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(fmt, "{:?}", &self.value[self.index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, A, B> PartialEq<Indexable<'a, A>> for Indexable<'b, B>
|
|
||||||
where
|
|
||||||
A: Index<usize> + 'b + ?Sized,
|
|
||||||
B: Index<usize> + 'b + ?Sized,
|
|
||||||
B::Output: PartialEq<A::Output>,
|
|
||||||
{
|
|
||||||
fn eq(&self, b: &Indexable<'a, A>) -> bool {
|
|
||||||
self.value[self.index] == b.value[b.index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unique<T>(seq: &T, lower: usize, upper: usize) -> Vec<Indexable<T>>
|
|
||||||
where
|
|
||||||
T: Index<usize> + ?Sized,
|
|
||||||
T::Output: Hash + Eq,
|
|
||||||
{
|
|
||||||
let mut by_item = HashMap::new();
|
|
||||||
for index in lower..upper {
|
|
||||||
match by_item.entry(&seq[index]) {
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(Some(index));
|
|
||||||
}
|
|
||||||
Entry::Occupied(mut entry) => {
|
|
||||||
let entry = entry.get_mut();
|
|
||||||
if entry.is_some() {
|
|
||||||
*entry = None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut rv = by_item
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(_, x)| x)
|
|
||||||
.map(|index| Indexable { value: seq, index })
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
rv.sort_by(|a, b| a.index.cmp(&b.index));
|
|
||||||
rv
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Patience<'old, 'new, 'd, Old: ?Sized, New: ?Sized, D> {
|
struct Patience<'old, 'new, 'd, Old: ?Sized, New: ?Sized, D> {
|
||||||
d: &'d mut D,
|
d: &'d mut D,
|
||||||
old: &'old Old,
|
old: &'old Old,
|
||||||
|
|
@ -174,8 +121,8 @@ where
|
||||||
for (old, new) in (old..old + len).zip(new..new + len) {
|
for (old, new) in (old..old + len).zip(new..new + len) {
|
||||||
let a0 = self.old_current;
|
let a0 = self.old_current;
|
||||||
let b0 = self.new_current;
|
let b0 = self.new_current;
|
||||||
while self.old_current < self.old_indexes[old].index
|
while self.old_current < self.old_indexes[old].index()
|
||||||
&& self.new_current < self.new_indexes[new].index
|
&& self.new_current < self.new_indexes[new].index()
|
||||||
&& self.new[self.new_current] == self.old[self.old_current]
|
&& self.new[self.new_current] == self.old[self.old_current]
|
||||||
{
|
{
|
||||||
self.old_current += 1;
|
self.old_current += 1;
|
||||||
|
|
@ -188,13 +135,13 @@ where
|
||||||
myers::diff_deadline(
|
myers::diff_deadline(
|
||||||
&mut no_finish_d,
|
&mut no_finish_d,
|
||||||
self.old,
|
self.old,
|
||||||
self.old_current..self.old_indexes[old].index,
|
self.old_current..self.old_indexes[old].index(),
|
||||||
self.new,
|
self.new,
|
||||||
self.new_current..self.new_indexes[new].index,
|
self.new_current..self.new_indexes[new].index(),
|
||||||
self.deadline,
|
self.deadline,
|
||||||
)?;
|
)?;
|
||||||
self.old_current = self.old_indexes[old].index;
|
self.old_current = self.old_indexes[old].index();
|
||||||
self.new_current = self.new_indexes[new].index;
|
self.new_current = self.new_indexes[new].index();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,69 @@
|
||||||
use std::ops::Range;
|
use std::collections::hash_map::Entry;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::ops::{Index, Range};
|
||||||
|
|
||||||
/// Utility function to check if a range is empty that works on older rust versions
|
/// Utility function to check if a range is empty that works on older rust versions
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn is_empty_range<T: PartialOrd>(range: &Range<T>) -> bool {
|
pub fn is_empty_range<T: PartialOrd>(range: &Range<T>) -> bool {
|
||||||
!(range.start < range.end)
|
!(range.start < range.end)
|
||||||
}
|
}
|
||||||
|
pub struct Indexable<'a, Idx: ?Sized> {
|
||||||
|
lookup: &'a Idx,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Idx: ?Sized> Indexable<'a, Idx> {
|
||||||
|
/// Returns the index.
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Idx: Index<usize> + 'a> std::fmt::Debug for Indexable<'a, Idx>
|
||||||
|
where
|
||||||
|
Idx::Output: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(fmt, "{:?}", &self.lookup[self.index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, A, B> PartialEq<Indexable<'a, A>> for Indexable<'b, B>
|
||||||
|
where
|
||||||
|
A: Index<usize> + 'b + ?Sized,
|
||||||
|
B: Index<usize> + 'b + ?Sized,
|
||||||
|
B::Output: PartialEq<A::Output>,
|
||||||
|
{
|
||||||
|
fn eq(&self, b: &Indexable<'a, A>) -> bool {
|
||||||
|
self.lookup[self.index] == b.lookup[b.index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unique<Idx>(seq: &Idx, range: Range<usize>) -> Vec<Indexable<Idx>>
|
||||||
|
where
|
||||||
|
Idx: Index<usize> + ?Sized,
|
||||||
|
Idx::Output: Hash + Eq,
|
||||||
|
{
|
||||||
|
let mut by_item = HashMap::new();
|
||||||
|
for index in range {
|
||||||
|
match by_item.entry(&seq[index]) {
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(Some(index));
|
||||||
|
}
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
let entry = entry.get_mut();
|
||||||
|
if entry.is_some() {
|
||||||
|
*entry = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut rv = by_item
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(_, x)| x)
|
||||||
|
.map(|index| Indexable { lookup: seq, index })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
rv.sort_by(|a, b| a.index.cmp(&b.index));
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue