use super::{ShrinkResult, find_integer_r};
pub(super) fn shrink_ordering<T, K, F>(n: usize, mut keys: K, mut accept: F) -> ShrinkResult<()>
where
T: Ord,
K: FnMut(usize) -> T,
F: FnMut(&[usize]) -> ShrinkResult<bool>,
{
if n <= 1 {
return Ok(());
}
let mut current: Vec<usize> = (0..n).collect();
let sorted_candidate = {
let mut p = current.clone();
p.sort_by_key(|&i| keys(i));
p
};
if sorted_candidate != current && accept(&sorted_candidate)? {
return Ok(());
}
let mut i = 0;
while i + 1 < current.len() {
let snapshot = current.clone();
let prefix: Vec<usize> = snapshot[..i].to_vec();
let len = snapshot.len();
let mut best: Vec<usize> = Vec::new();
let k = find_integer_r(|k| {
if i + k > len {
return Ok(false);
}
let mut region: Vec<usize> = snapshot[i..i + k].to_vec();
region.sort_by_key(|&j| keys(j));
let mut attempt = prefix.clone();
attempt.extend_from_slice(®ion);
attempt.extend_from_slice(&snapshot[i + k..]);
if attempt == snapshot {
return Ok(true);
}
if accept(&attempt)? {
best = attempt;
Ok(true)
} else {
Ok(false)
}
})?;
if !best.is_empty() {
current = best;
}
i += k.max(1);
}
let len = current.len();
if len < 3 {
return Ok(());
}
for i in 1..len - 1 {
if keys(current[i - 1]) <= keys(current[i]) && keys(current[i]) <= keys(current[i + 1]) {
continue;
}
let left = i;
let mut right = i + 1;
let snapshot_r = current.clone();
let i_fixed = i;
let k_r = find_integer_r(|k| {
if right + k > snapshot_r.len() {
return Ok(false);
}
try_sort_around(
&snapshot_r,
left,
right + k,
i_fixed,
&mut keys,
&mut accept,
)
})?;
right += k_r;
let snapshot_l = current.clone();
find_integer_r(|k| {
if k > left {
return Ok(false);
}
try_sort_around(
&snapshot_l,
left - k,
right,
i_fixed,
&mut keys,
&mut accept,
)
})?;
}
Ok(())
}
fn try_sort_around<T, K, F>(
snapshot: &[usize],
a: usize,
b: usize,
centre: usize,
keys: &mut K,
accept: &mut F,
) -> ShrinkResult<bool>
where
T: Ord,
K: FnMut(usize) -> T,
F: FnMut(&[usize]) -> ShrinkResult<bool>,
{
debug_assert!(a <= centre && centre < b && b <= snapshot.len());
let split = centre - a;
let mut sides: Vec<usize> = snapshot[a..centre].to_vec();
sides.extend_from_slice(&snapshot[centre + 1..b]);
sides.sort_by_key(|&j| keys(j));
let mut attempt: Vec<usize> = snapshot[..a].to_vec();
attempt.extend_from_slice(&sides[..split]);
attempt.push(snapshot[centre]);
attempt.extend_from_slice(&sides[split..]);
attempt.extend_from_slice(&snapshot[b..]);
if attempt == snapshot {
return Ok(true);
}
accept(&attempt)
}
#[cfg(test)]
#[path = "../../../tests/embedded/native/shrinker_ordering_tests.rs"]
mod tests;