use crate::physical::common::value_cmp;
use crate::physical::order_aggregate::{is_radix_eligible, radix_sort_indices};
use akar_common::types::Value;
pub struct BlockMergeSorter {
block_size: usize,
sort_keys: Vec<(u32, bool)>,
}
impl BlockMergeSorter {
pub fn new(block_size: usize, sort_keys: Vec<(u32, bool)>) -> Self {
Self { block_size, sort_keys }
}
pub fn sort(&self, all_values: &[Vec<(Value, bool)>], num_fields: usize) -> Vec<usize> {
let total_rows = all_values[0].len();
if total_rows == 0 {
return Vec::new();
}
let num_blocks = total_rows.div_ceil(self.block_size);
if num_blocks <= 1 {
let mut indices: Vec<usize> = (0..total_rows).collect();
self.sort_block(&mut indices, all_values, num_fields, 0, total_rows);
return indices;
}
let sort_keys = self.sort_keys.clone();
let block_size = self.block_size;
let blocks: Vec<Vec<usize>> = (0..num_blocks)
.map(|bi| {
let start = bi * block_size;
let end = (start + block_size).min(total_rows);
let mut block_indices: Vec<usize> = (start..end).collect();
Self::sort_block_static(&mut block_indices, all_values, num_fields, &sort_keys);
block_indices
})
.collect();
self.k_way_merge(&blocks, all_values, num_fields, total_rows)
}
fn sort_block(
&self,
indices: &mut [usize],
all_values: &[Vec<(Value, bool)>],
num_fields: usize,
_start: usize,
_end: usize,
) {
Self::sort_block_static(indices, all_values, num_fields, &self.sort_keys);
}
fn sort_block_static(
indices: &mut [usize],
all_values: &[Vec<(Value, bool)>],
num_fields: usize,
sort_keys: &[(u32, bool)],
) {
if sort_keys.is_empty() {
return;
}
let (col, ascending) = sort_keys[0];
let col = col as usize;
if col >= num_fields {
return;
}
if is_radix_eligible(&all_values[col]) {
let keys: Vec<i64> = indices
.iter()
.map(|&i| match &all_values[col][i].0 {
Value::Int64(v) => *v,
_ => i64::MAX, })
.collect();
radix_sort_indices(indices, &keys);
if !ascending {
indices.reverse();
}
if sort_keys.len() > 1 {
indices.sort_by(|a, b| {
for &(k, asc) in sort_keys {
let k = k as usize;
if k >= num_fields {
continue;
}
let cmp = value_cmp(&all_values[k][*a].0, &all_values[k][*b].0);
if cmp != std::cmp::Ordering::Equal {
return if asc { cmp } else { cmp.reverse() };
}
}
std::cmp::Ordering::Equal
});
}
} else {
indices.sort_by(|a, b| {
for &(k, ascending) in sort_keys {
let k = k as usize;
if k >= num_fields {
continue;
}
let cmp = value_cmp(&all_values[k][*a].0, &all_values[k][*b].0);
if cmp != std::cmp::Ordering::Equal {
return if ascending { cmp } else { cmp.reverse() };
}
}
std::cmp::Ordering::Equal
});
}
}
fn k_way_merge(
&self,
blocks: &[Vec<usize>],
all_values: &[Vec<(Value, bool)>],
_num_fields: usize,
total_rows: usize,
) -> Vec<usize> {
use std::collections::BinaryHeap;
struct HeapEntry {
block_idx: usize,
primary: Value,
rest: Vec<Value>,
}
impl Ord for HeapEntry {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let cmp = value_cmp(&self.primary, &other.primary);
if cmp != std::cmp::Ordering::Equal {
return cmp.reverse();
}
for (a, b) in self.rest.iter().zip(other.rest.iter()) {
let cmp = value_cmp(a, b);
if cmp != std::cmp::Ordering::Equal {
return cmp.reverse();
}
}
self.block_idx.cmp(&other.block_idx).reverse()
}
}
impl PartialOrd for HeapEntry {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for HeapEntry {}
impl PartialEq for HeapEntry {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == std::cmp::Ordering::Equal
}
}
let mut result = Vec::with_capacity(total_rows);
let mut positions: Vec<usize> = vec![0usize; blocks.len()];
let mut heap: BinaryHeap<HeapEntry> = BinaryHeap::with_capacity(blocks.len());
let sk = &self.sort_keys;
for bi in 0..blocks.len() {
if !blocks[bi].is_empty() {
let row = blocks[bi][0];
let primary = all_values[sk[0].0 as usize][row].0.clone();
let rest: Vec<Value> = sk[1..]
.iter()
.map(|&(k, _)| all_values[k as usize][row].0.clone())
.collect();
heap.push(HeapEntry {
block_idx: bi,
primary,
rest,
});
}
}
while let Some(entry) = heap.pop() {
let bi = entry.block_idx;
let pos = &mut positions[bi];
result.push(blocks[bi][*pos]);
*pos += 1;
if *pos < blocks[bi].len() {
let row = blocks[bi][*pos];
let primary = all_values[sk[0].0 as usize][row].0.clone();
let rest: Vec<Value> = sk[1..]
.iter()
.map(|&(k, _)| all_values[k as usize][row].0.clone())
.collect();
heap.push(HeapEntry {
block_idx: bi,
primary,
rest,
});
}
}
result
}
}