use core::cmp::Ordering::{self, Equal, Greater, Less};
use core::hint::select_unpredictable;
use core::mem;
pub fn selection<T: Ord>(arr: &mut [T]) {
let (mut lo, mut hi) = (0, arr.len());
let size = mem::size_of::<T>();
let mut first = size > 8 && size <= 32;
let mut branchy = false;
while hi - lo > 1 {
let mut pass = Pass::new(&mut arr[lo..hi]);
if first {
let (pairs, flips) = pass.scan::<true, true>();
branchy = flips * FLIPS_MAX < pairs;
first = false;
} else if branchy {
pass.scan::<true, false>();
} else {
pass.scan::<false, false>();
}
match pass.finish() {
Some((front, back)) => {
lo += front;
hi -= back;
}
None => return, }
}
}
const FLIPS_MAX: usize = 16;
struct Pass<'a, T> {
v: &'a mut [T],
end: usize,
same: bool,
min_len: usize,
mi: usize,
max_len: usize,
mb: usize,
mx: usize,
held: [usize; 2],
}
impl<'a, T: Ord> Pass<'a, T> {
fn new(v: &'a mut [T]) -> Self {
Pass {
v,
end: 0,
same: true,
min_len: 0,
mi: 0,
max_len: 0,
mb: 0,
mx: 0,
held: [0; 2],
}
}
#[inline(always)]
fn scan<const BRANCHY: bool, const COUNT: bool>(&mut self) -> (usize, usize) {
let len = self.v.len();
if len % 2 == 1 {
self.end = 1;
} else {
self.end = 2;
match self.v[0].cmp(&self.v[1]) {
Less => {
self.same = false;
self.mx = 1;
}
Greater => {
self.same = false;
self.mi = 1;
}
Equal => self.push_min(1),
}
}
let start = self.end;
let mut greater = 0;
while self.end < len {
let x = self.end;
self.end += 2;
let y = x + 1;
let order = unsafe { self.v.get_unchecked(x).cmp(self.v.get_unchecked(y)) };
if order == Equal {
self.pair_equal(x, y);
continue;
}
let less = order == Less;
let (s, l) = if BRANCHY {
if less {
(x, y)
} else {
if COUNT {
greater += 1;
}
(y, x)
}
} else {
if COUNT {
greater += usize::from(!less);
}
select_unpredictable(less, (x, y), (y, x))
};
let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
let (below, above) = unsafe {
(
self.v.get_unchecked(s).cmp(self.v.get_unchecked(min)),
self.v.get_unchecked(l).cmp(self.v.get_unchecked(max)),
)
};
if below == Greater && above == Less {
continue;
}
self.place(s, l, below, above);
}
let pairs = (len - start) / 2;
(pairs, greater.min(pairs - greater))
}
fn pair_equal(&mut self, x: usize, y: usize) {
let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
self.held[0] = y;
let joined_min = match self.v[x].cmp(&self.v[min]) {
Less => {
self.new_min(x);
true
}
Equal => {
self.push_min(x);
true
}
Greater if self.same => {
self.new_max(x);
false
}
Greater => match self.v[x].cmp(&self.v[max]) {
Less => return, Equal => {
self.push_max(x);
false
}
Greater => {
self.new_max(x);
false
}
},
};
let y = self.held[0];
if joined_min {
self.push_min(y);
} else {
self.push_max(y);
}
}
fn place(&mut self, s: usize, l: usize, below: Ordering, above: Ordering) {
self.held[0] = l;
match below {
Less => self.new_min(s),
Equal => self.push_min(s),
Greater => {}
}
let l = self.held[0];
match above {
Less => {}
Equal => self.push_max(l),
Greater => self.new_max(l),
}
}
fn new_min(&mut self, src: usize) {
if self.same {
self.same = false;
self.max_len = self.min_len;
self.mb = 0;
self.mx = self.mi;
}
self.min_len = 0;
self.mi = src;
}
fn new_max(&mut self, src: usize) {
self.same = false;
self.max_len = 0;
self.mx = src;
}
fn push_min(&mut self, src: usize) {
self.held[1] = src;
if self.min_len == 0 {
self.make_room_for_min();
self.swap(0, self.mi);
self.min_len = 1;
}
self.make_room_for_min();
self.swap(self.min_len, self.held[1]);
self.min_len += 1;
self.mi = 0;
}
fn make_room_for_min(&mut self) {
if self.max_len > 0 && self.mb == self.min_len {
self.swap(self.mb, self.mb + self.max_len);
self.mb += 1;
self.mx = self.mb;
}
}
fn push_max(&mut self, src: usize) {
if self.max_len == 0 {
self.mb = self.mx;
self.max_len = 1;
}
let next = self.mb + self.max_len;
let slot = if next < self.end {
next
} else {
self.mb -= 1;
self.mb
};
self.swap(slot, src);
self.max_len += 1;
self.mx = self.mb;
}
fn finish(&mut self) -> Option<(usize, usize)> {
if self.same {
return None;
}
let len = self.v.len();
let back = if self.max_len == 0 {
self.swap(len - 1, self.mx);
1
} else {
let (start, count) = (self.mb, self.max_len);
let target = len - count;
let free = target.max(start + count);
for k in 0..(target - start).min(count) {
self.swap(start + k, free + k);
}
count
};
let front = if self.min_len == 0 {
self.swap(0, self.mi);
1
} else {
self.min_len
};
Some((front, back))
}
fn swap(&mut self, a: usize, b: usize) {
if a == b {
return;
}
self.v.swap(a, b);
let [h0, h1] = &mut self.held;
for index in [&mut self.mi, &mut self.mx, h0, h1] {
if *index == a {
*index = b;
} else if *index == b {
*index = a;
}
}
}
}