use core::cmp::Ordering::{Equal, Greater, Less};
pub fn selection<T: Ord>(arr: &mut [T]) {
let (mut lo, mut hi) = (0, arr.len());
while hi - lo > 1 {
let mut pass = Pass::new(&mut arr[lo..hi]);
pass.scan();
match pass.finish() {
Some((front, back)) => {
lo += front;
hi -= back;
}
None => return, }
}
}
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],
}
}
fn scan(&mut self) {
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),
}
}
while self.end < len {
let x = self.end;
self.end += 2;
self.pair(x, x + 1);
}
}
fn pair(&mut self, x: usize, y: usize) {
let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
match self.v[x].cmp(&self.v[y]) {
Equal => {
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);
}
}
order => {
let (s, l) = if order == Less { (x, y) } else { (y, x) };
let below = self.v[s].cmp(&self.v[min]);
let above = self.v[l].cmp(&self.v[max]);
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;
}
}
}
}