use super::{MAX_POP, RING};
use crate::view::{AllocError, Arr, Hooks, IdxView};
use core::ptr::NonNull;
pub struct DispBuf<V: Arr> {
elem: NonNull<V::T>,
pos: NonNull<u32>,
rank: NonNull<u32>,
cap: usize,
}
impl<V: Arr> DispBuf<V> {
pub fn new(cap: usize) -> Result<Self, AllocError> {
let elem = V::alloc_array::<V::T>(cap)?;
let pos = match V::alloc_array::<u32>(cap) {
Ok(p) => p,
Err(e) => {
unsafe { V::free_array(elem, cap) };
return Err(e);
}
};
let rank = match V::alloc_array::<u32>(cap) {
Ok(p) => p,
Err(e) => {
unsafe {
V::free_array(elem, cap);
V::free_array(pos, cap);
}
return Err(e);
}
};
Ok(DispBuf { elem, pos, rank, cap })
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.cap
}
pub fn grow(&mut self) -> bool {
let ncap = self.cap * 2;
let Ok(ne) = V::alloc_array::<V::T>(ncap) else { return false };
let Ok(np) = V::alloc_array::<u32>(ncap) else {
unsafe { V::free_array(ne, ncap) };
return false;
};
let Ok(nr) = V::alloc_array::<u32>(ncap) else {
unsafe {
V::free_array(ne, ncap);
V::free_array(np, ncap);
}
return false;
};
unsafe {
core::ptr::copy_nonoverlapping(self.elem.as_ptr(), ne.as_ptr(), self.cap);
core::ptr::copy_nonoverlapping(self.pos.as_ptr(), np.as_ptr(), self.cap);
core::ptr::copy_nonoverlapping(self.rank.as_ptr(), nr.as_ptr(), self.cap);
}
self.release();
self.elem = ne;
self.pos = np;
self.rank = nr;
self.cap = ncap;
true
}
#[inline(always)]
pub fn elem(&self, i: usize) -> V::T {
debug_assert!(i < self.cap);
if V::H::COUNTED {
V::H::on_read(self.elem.as_ptr().wrapping_add(i) as *const u8, core::mem::size_of::<V::T>());
}
unsafe { *self.elem.as_ptr().add(i) }
}
#[inline(always)]
pub fn set(&mut self, i: usize, e: V::T, p: u32, r: u32) {
debug_assert!(i < self.cap);
if V::H::COUNTED {
V::H::on_write(self.elem.as_ptr().wrapping_add(i) as *const u8, core::mem::size_of::<V::T>());
V::H::on_table_write(self.pos.as_ptr().wrapping_add(i) as *const u8, 4);
V::H::on_table_write(self.rank.as_ptr().wrapping_add(i) as *const u8, 4);
}
unsafe {
*self.elem.as_ptr().add(i) = e;
*self.pos.as_ptr().add(i) = p;
*self.rank.as_ptr().add(i) = r;
}
}
#[inline(always)]
pub fn pos(&self, i: usize) -> u32 {
if V::H::COUNTED {
V::H::on_table_read(self.pos.as_ptr().wrapping_add(i) as *const u8, 4);
}
unsafe { *self.pos.as_ptr().add(i) }
}
#[inline(always)]
pub fn rank(&self, i: usize) -> u32 {
if V::H::COUNTED {
V::H::on_table_read(self.rank.as_ptr().wrapping_add(i) as *const u8, 4);
}
unsafe { *self.rank.as_ptr().add(i) }
}
#[inline(always)]
pub fn ranks(&mut self) -> *mut u32 {
self.rank.as_ptr()
}
#[inline(always)]
pub fn positions(&self) -> *const u32 {
self.pos.as_ptr()
}
fn release(&mut self) {
unsafe {
V::free_array(self.elem, self.cap);
V::free_array(self.pos, self.cap);
V::free_array(self.rank, self.cap);
}
self.cap = 0;
self.elem = NonNull::dangling();
self.pos = NonNull::dangling();
self.rank = NonNull::dangling();
}
}
impl<V: Arr> Drop for DispBuf<V> {
fn drop(&mut self) {
if self.cap != 0 || self.elem != NonNull::dangling() {
self.release();
}
}
}
pub fn restore_displaced<V: Arr>(a: V, disp: &mut DispBuf<V>, nd: usize, nk: usize, scanned: usize) {
let idx = disp.ranks(); let idx = unsafe { core::slice::from_raw_parts_mut(idx, nd) };
for (t, x) in idx.iter_mut().enumerate() {
*x = t as u32;
}
let pos = disp.positions();
idx.sort_unstable_by_key(|&x| unsafe { *pos.add(x as usize) });
let (mut t, mut k) = (nd, nk);
let mut p = scanned;
while p > 0 {
p -= 1;
if t > 0 && unsafe { *pos.add(idx[t - 1] as usize) } as usize == p {
t -= 1;
a.set(p, disp.elem(idx[t] as usize));
} else {
k -= 1;
a.set(p, a.get(k)); }
}
}
struct IdxBuf<V: Arr> {
p: NonNull<u32>,
n: usize,
_v: core::marker::PhantomData<V>,
}
impl<V: Arr> Drop for IdxBuf<V> {
fn drop(&mut self) {
unsafe { V::free_array(self.p, self.n) };
}
}
struct Route3<V: Arr> {
a: V,
disp: DispBuf<V>,
idx_buf: Option<IdxBuf<V>>,
nk: usize,
nd: usize,
scanned: usize,
phase: u8,
idx: Option<IdxView<V::H>>,
ik: usize,
id: usize,
}
impl<V: Arr> Drop for Route3<V> {
fn drop(&mut self) {
match self.phase {
0 => restore_displaced(self.a, &mut self.disp, self.nd, self.nk, self.scanned),
1 => {
if let Some(idx) = self.idx {
for u in 0..self.id {
self.a.set(self.ik + u, self.disp.elem(idx.get(u) as usize));
}
}
}
_ => {}
}
}
}
pub fn sort_displaced<V: Arr>(a: V, n: usize) -> Result<bool, AllocError> {
let counted = <V::H as Hooks>::COUNTED;
let limit = n / 8;
let mut g = Route3::<V> { a, disp: DispBuf::<V>::new(256.min(limit + 1))?, idx_buf: None, nk: 0, nd: 0, scanned: 0, phase: 0, idx: None, ik: 0, id: 0 };
let mut nk_max = 0usize; let (mut k1, mut k2) = (V::T::default(), V::T::default()); let mut ring = [0u32; RING];
let p = a.data(); let rd = |i: usize| -> V::T { if counted { a.get(i) } else { unsafe { *p.add(i) } } };
let wr = |i: usize, v: V::T| {
if counted { a.set(i, v) } else { unsafe { *p.add(i) = v } }
};
let give_up = |g: &mut Route3<V>| -> Result<bool, AllocError> {
g.phase = 2;
restore_displaced(g.a, &mut g.disp, g.nd, g.nk, g.scanned);
Ok(false)
};
for i in 0..n {
g.scanned = i;
let it = rd(i);
if g.nk == 0 || !a.less(it, k1) {
ring[g.nk & (RING - 1)] = i as u32;
wr(g.nk, it);
g.nk += 1;
k2 = k1;
k1 = it;
continue;
}
let mut pops = 0usize;
if g.nk == 1 || !a.less(it, k2) {
pops = 1;
} else {
for j in 2..=MAX_POP {
if g.nk <= j {
pops = j;
break;
}
if !a.less(it, rd(g.nk - 1 - j)) {
pops = j;
break;
}
}
}
let budget = limit.min((i >> 3) + 64);
if g.nd + pops.max(1) > budget + 1 {
return give_up(&mut g);
}
while g.nd + pops.max(1) > g.disp.capacity() {
if !g.disp.grow() {
return give_up(&mut g);
}
}
if pops == 0 {
let nd = g.nd;
g.disp.set(nd, it, i as u32, g.nk as u32);
g.nd += 1;
continue;
}
if g.nk > nk_max {
nk_max = g.nk;
}
if nk_max > RING && g.nk - pops < nk_max - RING {
return give_up(&mut g);
}
for j in 1..=pops {
let (nd, nk) = (g.nd, g.nk);
g.disp.set(nd, rd(nk - j), ring[(nk - j) & (RING - 1)], (nk - j) as u32);
g.nd += 1;
}
g.nk -= pops;
ring[g.nk & (RING - 1)] = i as u32;
wr(g.nk, it);
g.nk += 1;
k1 = it;
k2 = if g.nk >= 2 { rd(g.nk - 2) } else { V::T::default() };
}
g.scanned = n;
let (nk, nd) = (g.nk, g.nd);
if nd == 0 {
g.phase = 2;
return Ok(true); }
{
let r = g.disp.ranks();
unsafe {
let mut mn = *r.add(nd - 1);
for t in (0..nd).rev() {
if *r.add(t) < mn {
mn = *r.add(t);
}
*r.add(t) = mn;
}
}
}
let idx_ptr = match V::alloc_array::<u32>(2 * nd) {
Ok(p) => {
g.idx_buf = Some(IdxBuf { p, n: 2 * nd, _v: core::marker::PhantomData });
p
}
Err(_) => return give_up(&mut g),
};
let idx = IdxView::<V::H>::new(idx_ptr.as_ptr(), 2 * nd);
for t in 0..nd {
idx.set(t, t as u32);
}
{
let (mut src, mut dst) = (0usize, nd); let mut width = 1;
while width < nd {
let mut lo = 0;
while lo < nd {
let mid = (lo + width).min(nd);
let hi = (lo + 2 * width).min(nd);
let (mut i, mut j, mut o) = (lo, mid, lo);
while i < mid && j < hi {
let (pi, pj) = (idx.get(src + i), idx.get(src + j));
let ei = g.disp.elem(pi as usize);
let ej = g.disp.elem(pj as usize);
let c = a.compare(ej, ei);
let mut take_j = c < 0;
if c == 0 {
let (qi, qj) = (g.disp.pos(pi as usize), g.disp.pos(pj as usize));
take_j = qj < qi;
}
if take_j {
idx.set(dst + o, pj);
j += 1;
} else {
idx.set(dst + o, pi);
i += 1;
}
o += 1;
}
while i < mid {
idx.set(dst + o, idx.get(src + i));
o += 1;
i += 1;
}
while j < hi {
idx.set(dst + o, idx.get(src + j));
o += 1;
j += 1;
}
lo += 2 * width;
}
core::mem::swap(&mut src, &mut dst);
width *= 2;
}
if src != 0 {
for t in 0..nd {
idx.set(t, idx.get(nd + t));
}
}
}
g.phase = 1;
g.idx = Some(idx);
g.ik = nk;
g.id = nd;
let mut o = n;
let mut t = idx.get(g.id - 1);
let mut ed = g.disp.elem(t as usize);
if g.ik > 0 {
let mut ek = a.get(g.ik - 1);
loop {
let take_kept = if a.less(ed, ek) {
true
} else if a.less(ek, ed) {
false
} else {
g.ik - 1 >= g.disp.rank(t as usize) as usize
};
if take_kept {
o -= 1;
a.set(o, ek);
g.ik -= 1;
if g.ik == 0 {
break;
}
ek = a.get(g.ik - 1);
} else {
o -= 1;
a.set(o, ed);
g.id -= 1;
if g.id == 0 {
break;
}
t = idx.get(g.id - 1);
ed = g.disp.elem(t as usize);
}
}
}
g.phase = 2; while g.id > 0 {
o -= 1;
a.set(o, ed);
g.id -= 1;
if g.id == 0 {
break;
}
t = idx.get(g.id - 1);
ed = g.disp.elem(t as usize);
}
Ok(true) }