use super::{COMMIT_BLOCK, MAX_RUNS};
use crate::view::{Arr, Elem, RadixKey};
#[derive(Clone, Copy, Debug)]
pub struct ScoutResult {
pub mask: u64,
pub has_mask: bool,
pub committed: bool,
pub descents: usize,
pub ascents: usize,
pub runs: usize,
pub bound: [usize; MAX_RUNS + 1],
pub dir: [i8; MAX_RUNS + 1],
pub tracking: bool,
pub cur_dir: i8,
}
impl Default for ScoutResult {
fn default() -> Self {
ScoutResult { mask: 0, has_mask: false, committed: false, descents: 0, ascents: 0, runs: 1, bound: [0; MAX_RUNS + 1], dir: [0; MAX_RUNS + 1], tracking: true, cur_dir: 0 }
}
}
#[inline(always)]
pub fn commit_now(r: &ScoutResult, scanned: usize) -> bool {
r.ascents > 0 && !r.tracking && r.descents > (scanned >> 3) + 64
}
#[inline(always)]
pub fn track_pair(r: &mut ScoutResult, c: i32, i: usize) {
if r.cur_dir == 0 {
r.cur_dir = if c < 0 { -1 } else { 1 };
return;
}
if (c < 0) != (r.cur_dir < 0) {
if r.runs >= MAX_RUNS {
r.tracking = false;
r.runs = MAX_RUNS + 1;
return;
}
r.dir[r.runs - 1] = r.cur_dir;
r.bound[r.runs] = i;
r.runs += 1;
r.cur_dir = 0;
}
}
#[inline(always)]
pub fn finish_runs(r: &mut ScoutResult, n: usize) {
if !r.tracking {
return;
}
r.dir[r.runs - 1] = if r.cur_dir < 0 { -1 } else { 1 };
r.bound[r.runs] = n;
}
pub fn scout_scalar<V: Arr>(a: V, n: usize, chunk: i32) -> ScoutResult {
let mut r = ScoutResult::default();
let mut prev = a.get(0);
let k0 = V::key(prev, chunk).to_u64();
let (mut desc, mut asc) = (0usize, 0usize);
let mut mask = 0u64;
for i in 1..n {
let cur = a.get(i);
if !<V::T as Elem>::CHUNKED {
mask |= V::key(cur, chunk).to_u64() ^ k0; }
let c = a.compare_from(cur, prev, chunk);
if c != 0 {
desc += (c < 0) as usize;
asc += (c > 0) as usize;
if r.tracking && (r.cur_dir == 0 || (c < 0) != (r.cur_dir < 0)) {
track_pair(&mut r, c, i);
}
} else if r.tracking && r.cur_dir <= 0 {
track_pair(&mut r, 0, i);
}
prev = cur;
if i & (COMMIT_BLOCK - 1) == 0 {
r.descents = desc;
r.ascents = asc;
if commit_now(&r, i) {
r.mask = mask;
r.committed = true;
return r;
}
}
}
r.descents = desc;
r.ascents = asc;
r.mask = mask;
r.has_mask = !<V::T as Elem>::CHUNKED;
finish_runs(&mut r, n);
r
}
pub fn compute_mask<V: Arr>(a: V, n: usize, chunk: i32) -> u64 {
let k0 = V::key(a.get(0), chunk).to_u64();
let mut mask = 0u64;
for i in 1..n {
mask |= V::key(a.get(i), chunk).to_u64() ^ k0;
}
mask
}
#[inline]
pub fn scout_tail<T: Elem>(r: &mut ScoutResult, p: *const T, mut i: usize, n: usize) {
let k0 = T::radix_key(unsafe { *p }, 0).to_u64();
while i < n {
let (cur, prev) = unsafe { (*p.add(i), *p.add(i - 1)) };
r.mask |= T::radix_key(cur, 0).to_u64() ^ k0;
let desc = T::less(cur, prev);
let asc = T::less(prev, cur);
r.descents += desc as usize;
r.ascents += asc as usize;
if r.tracking && (r.cur_dir == 0 || desc != (r.cur_dir < 0)) {
track_pair(
r,
if desc {
-1
} else if asc {
1
} else {
0
},
i,
);
}
i += 1;
}
}
#[inline]
pub fn scout<V: Arr>(a: V, n: usize, chunk: i32) -> ScoutResult {
#[cfg(all(target_arch = "x86_64", not(brainsort_no_simd)))]
{
use crate::view::{Hooks, SimdKind};
if !<V::H as Hooks>::COUNTED && <V::T as Elem>::SIMD != SimdKind::None && chunk == 0 && crate::cpu::have_avx2() {
let mut r = unsafe { crate::simd::x86::scout_avx2::<V::T>(a.data() as *const V::T, n) };
r.has_mask = !r.committed;
return r;
}
}
scout_scalar(a, n, chunk)
}