use std::cmp::Ordering;
use std::cmp::Ordering::*;
use std::mem;
use std::ptr;
pub fn split_merge<T: Default, F1, F2>(mut terms: &mut [T], cmp: &F1, merger: &F2) -> Vec<usize>
where
F1: Fn(&T, &T) -> Ordering,
F2: Fn(&mut T, &mut T) -> bool,
{
let n = terms.len();
let mut row: Vec<usize> = (0..n).collect();
let mut sbuf = vec![0; n / 2];
unsafe {
let n_out = split_merge_rec(&mut terms, &mut row, &mut sbuf, n, cmp, merger);
row.truncate(n_out);
}
row
}
unsafe fn split_merge_rec<T: Default, F1, F2>(
mut terms: &mut [T],
row: &mut [usize],
mut sbuf: &mut [usize],
n: usize,
cmp: &F1,
merger: &F2,
) -> usize
where
F1: Fn(&T, &T) -> Ordering,
F2: Fn(&mut T, &mut T) -> bool,
{
#[inline]
unsafe fn add_one<T: Default, F>(
terms: &mut [T],
row: &mut [usize],
one: usize,
two: usize,
merger: &F,
) -> bool
where
F: Fn(&mut T, &mut T) -> bool,
{
let mut term = mem::replace(
terms.get_unchecked_mut(*row.get_unchecked(two)),
T::default(),
);
let v = terms.get_unchecked_mut(*row.get_unchecked(one));
merger(v, &mut term)
}
if n < 2 {
return n;
} else if n == 2 {
match cmp(
terms.get_unchecked(*row.get_unchecked(0)),
terms.get_unchecked(*row.get_unchecked(1)),
) {
Greater => {
ptr::swap_nonoverlapping(row.get_unchecked_mut(0), row.get_unchecked_mut(1), 1);
}
Less => (),
Equal => {
if add_one(terms, row, 0, 1, merger) {
return 0;
}
return 1;
}
}
return 2;
}
let split = n / 2;
let mut len1 = split_merge_rec(
&mut terms,
row.get_unchecked_mut(0..split),
&mut sbuf,
split,
cmp,
merger,
);
let len2 = split_merge_rec(
&mut terms,
row.get_unchecked_mut(split..n),
&mut sbuf,
n - split,
cmp,
merger,
);
if len1 > 0 && len2 > 0 {
match cmp(
terms.get_unchecked(*row.get_unchecked(len1 - 1)),
terms.get_unchecked(*row.get_unchecked(split)),
) {
Greater => (), Less => {
if len1 < split {
ptr::copy(row.get_unchecked(split), row.get_unchecked_mut(len1), len2);
}
return len1 + len2;
}
Equal => {
if add_one(terms, row, len1 - 1, split, merger) {
len1 -= 1;
}
ptr::copy(
row.get_unchecked(split + 1),
row.get_unchecked_mut(len1),
len2 - 1,
);
return len1 + len2 - 1;
}
}
let mut i1: usize = 0;
let mut i2: usize = 0;
let mut ifill: usize = 0;
let mut size1 = len1;
while size1 > 8 {
let ins = size1 / 2;
match cmp(
terms.get_unchecked(*row.get_unchecked(i1 + ins - 1)),
terms.get_unchecked(*row.get_unchecked(split)),
) {
Greater => {
size1 = ins;
}
Less => {
i1 += ins;
size1 -= ins;
ifill = i1;
}
Equal => {
if add_one(terms, row, i1 + ins - 1, split, merger) {
i1 += ins;
ifill = i1 - 1;
} else {
i1 += ins;
ifill = i1;
}
i2 += 1;
break;
}
}
}
ptr::copy_nonoverlapping(row.get_unchecked(i1), sbuf.get_unchecked_mut(i1), len1 - i1);
if i2 < len2 {
loop {
match cmp(
terms.get_unchecked(*sbuf.get_unchecked(i1)),
terms.get_unchecked(*row.get_unchecked(i2 + split)),
) {
Greater => {
*row.get_unchecked_mut(ifill) = *row.get_unchecked(i2 + split);
i2 += 1;
ifill += 1;
if i2 >= len2 {
break;
}
}
Less => {
*row.get_unchecked_mut(ifill) = *sbuf.get_unchecked(i1);
i1 += 1;
ifill += 1;
if i1 >= len1 {
break;
}
}
Equal => {
*row.get_unchecked_mut(ifill) = *sbuf.get_unchecked(i1);
if !add_one(terms, row, ifill, i2 + split, merger) {
ifill += 1;
}
i1 += 1;
i2 += 1;
if i1 >= len1 || i2 >= len2 {
break;
}
}
}
}
}
if i1 < len1 {
ptr::copy_nonoverlapping(
sbuf.get_unchecked(i1),
row.get_unchecked_mut(ifill),
len1 - i1,
);
ifill += len1 - i1;
} else if i2 < len2 {
ptr::copy(
row.get_unchecked(split + i2),
row.get_unchecked_mut(ifill),
len2 - i2,
);
ifill += len2 - i2;
}
ifill
} else if len1 > 0 {
len1
} else if len2 > 0 {
ptr::copy(row.get_unchecked(split), row.get_unchecked_mut(0), len2);
len2
} else {
0
}
}