mod quicksort;
mod tests;
#[doc(hidden)]
pub mod utils;
use crate::{
DBData, DBWeight,
algebra::{AddAssignByRef, HasZero, MonoidValue},
dynamic::{DataTrait, DowncastTrait, DynVec, Erase, LeanVec, WeightTrait},
utils::{Tup2, assume},
};
use std::{
marker::PhantomData,
mem::{replace, size_of},
ptr,
};
use utils::{dedup_payload_starting_at, retain_payload_starting_at};
#[cfg(test)]
pub fn consolidate_pairs<T, R>(vec: &mut LeanVec<(T, R)>)
where
T: Ord,
R: MonoidValue,
{
if vec.is_empty() {
return;
}
vec.as_mut_slice()
.sort_unstable_by(|(key1, _), (key2, _)| key1.cmp(key2));
vec.dedup_by(|(key1, data1), (key2, data2)| {
if key1 == key2 {
data2.add_assign_by_ref(&replace(data1, R::zero()));
true
} else {
false
}
});
vec.retain(|(_, data)| !data.is_zero());
}
pub fn consolidate<T, R>(vec: &mut LeanVec<Tup2<T, R>>)
where
T: Ord,
R: MonoidValue,
{
if vec.is_empty() {
return;
}
vec.as_mut_slice()
.sort_unstable_by(|Tup2(key1, _), Tup2(key2, _)| key1.cmp(key2));
vec.dedup_by(|Tup2(key1, data1), Tup2(key2, data2)| {
if key1 == key2 {
data2.add_assign_by_ref(&replace(data1, R::zero()));
true
} else {
false
}
});
vec.retain(|Tup2(_, data)| !data.is_zero());
}
pub fn consolidate_from<T, R>(vec: &mut LeanVec<Tup2<T, R>>, offset: usize)
where
T: Ord,
R: HasZero + AddAssignByRef,
{
if vec[offset..].is_empty() {
return;
}
vec[offset..].sort_unstable_by(|Tup2(key1, _), Tup2(key2, _)| key1.cmp(key2));
vec.dedup_by_starting_at(offset, |Tup2(key1, data1), Tup2(key2, data2)| {
if key1 == key2 {
data2.add_assign_by_ref(&replace(data1, R::zero()));
true
} else {
false
}
});
vec.retain_starting_at(offset, |Tup2(_, data)| !data.is_zero());
}
pub fn consolidate_slice<T, R>(slice: &mut [Tup2<T, R>]) -> usize
where
T: Ord,
R: AddAssignByRef + HasZero,
{
if slice.is_empty() {
return 0;
}
slice.sort_unstable_by(|Tup2(key1, _), Tup2(key2, _)| key1.cmp(key2));
consolidate_slice_inner(
slice,
|Tup2(key1, _), Tup2(key2, _)| key1 == key2,
|Tup2(_, diff1), Tup2(_, diff2)| diff1.add_assign_by_ref(diff2),
|Tup2(_, diff)| diff.is_zero(),
)
}
#[doc(hidden)]
pub fn consolidate_slice_inner<T, E, M, Z>(
slice: &mut [T],
mut are_equal: E,
mut merge: M,
mut is_zero: Z,
) -> usize
where
E: FnMut(&T, &T) -> bool,
M: FnMut(&mut T, &T),
Z: FnMut(&T) -> bool,
{
let slice_len = slice.len();
let slice_ptr = slice.as_mut_ptr();
let mut offset = 0;
for index in 1..slice_len {
unsafe {
debug_assert!(offset < index);
debug_assert!(index < slice_len);
debug_assert!(offset < slice_len);
let ptr1 = slice_ptr.add(offset);
let ptr2 = slice_ptr.add(index);
if are_equal(&*ptr1, &*ptr2) {
merge(&mut *ptr1, &*ptr2)
} else {
if !is_zero(&*ptr1) {
offset += 1;
}
let ptr1 = slice_ptr.add(offset);
ptr::swap(ptr1, ptr2);
}
}
}
if offset < slice_len && unsafe { !is_zero(&*slice_ptr.add(offset)) } {
offset += 1;
}
offset
}
pub fn consolidate_payload_from<K, R>(keys: &mut Vec<K>, diffs: &mut Vec<R>, offset: usize)
where
K: Ord,
R: HasZero + AddAssignByRef,
{
assert_eq!(keys.len(), diffs.len());
if keys[offset..].is_empty() {
return;
}
quicksort::quicksort(&mut keys[offset..], &mut diffs[offset..]);
dedup_payload_starting_at(keys, &mut *diffs, offset, |key1, diff1, key2, diff2| {
if key1 == key2 {
diff2.add_assign_by_ref(&replace(diff1, R::zero()));
true
} else {
false
}
});
retain_payload_starting_at(keys, diffs, offset, |_key, diff| !diff.is_zero());
}
pub fn consolidate_paired_slices<K, R>(keys: &mut [K], diffs: &mut [R]) -> usize
where
K: Ord,
R: AddAssignByRef + HasZero,
{
assert_eq!(keys.len(), diffs.len());
if keys.is_empty() {
return 0;
}
quicksort::quicksort(keys, diffs);
unsafe { compact_paired_slices(keys, diffs) }
}
unsafe fn compact_paired_slices<T, R>(keys: &mut [T], diffs: &mut [R]) -> usize
where
T: Eq,
R: AddAssignByRef + HasZero,
{
unsafe {
assume(!keys.is_empty());
assume(keys.len() == diffs.len());
}
if size_of::<T>() == 0 {
debug_assert!(!diffs.is_empty());
let (sum, diffs) = diffs.split_at_mut(1);
debug_assert_eq!(sum.len(), 1);
let sum = &mut sum[0];
for diff in diffs {
sum.add_assign_by_ref(diff);
}
return !sum.is_zero() as usize;
} else if size_of::<R>() == 0 {
return !diffs[0].is_zero() as usize;
}
let len = keys.len();
let key_ptr = keys.as_mut_ptr();
let diff_ptr = diffs.as_mut_ptr();
let mut offset = 0;
for index in 1..len {
unsafe {
assume(offset < index);
assume(index < len);
assume(offset < len);
let key1 = key_ptr.add(offset);
let key2 = key_ptr.add(index);
let diff1 = diff_ptr.add(offset);
let diff2 = diff_ptr.add(index);
if *key1 == *key2 {
(*diff1).add_assign_by_ref(&*diff2);
} else {
if !(*diff1).is_zero() {
offset += 1;
}
debug_assert!(offset < len);
ptr::swap(key_ptr.add(offset), key2);
ptr::swap(diff_ptr.add(offset), diff2);
}
}
}
if offset < len && unsafe { !(*diff_ptr.add(offset)).is_zero() } {
offset += 1;
}
offset
}
pub trait ConsolidatePairedSlices<T1: DataTrait + ?Sized, T2: WeightTrait + ?Sized>:
Send + Sync
{
fn consolidate_paired_slices(
&self,
keys: (&mut DynVec<T1>, usize, usize),
weights: (&mut DynVec<T2>, usize, usize),
) -> usize;
}
pub struct ConsolidatePairedSlicesImpl<T1Type, T2Type, T1, T2>
where
T1: DataTrait + ?Sized,
T2: WeightTrait + ?Sized,
T1Type: DBData + Erase<T1>,
T2Type: DBWeight + Erase<T2>,
{
phantom: PhantomData<fn(&T1Type, &T2Type, &T1, &T2)>,
}
impl<T1Type, T2Type, T1, T2> ConsolidatePairedSlices<T1, T2>
for ConsolidatePairedSlicesImpl<T1Type, T2Type, T1, T2>
where
T1: DataTrait + ?Sized,
T2: WeightTrait + ?Sized,
T1Type: DBData + Erase<T1>,
T2Type: DBWeight + Erase<T2>,
{
fn consolidate_paired_slices(
&self,
(keys, from1, to1): (&mut DynVec<T1>, usize, usize),
(weights, from2, to2): (&mut DynVec<T2>, usize, usize),
) -> usize {
let keys: &mut LeanVec<T1Type> = unsafe { keys.downcast_mut::<LeanVec<T1Type>>() };
let weights: &mut LeanVec<T2Type> = unsafe { weights.downcast_mut::<LeanVec<T2Type>>() };
consolidate_paired_slices(&mut keys[from1..to1], &mut weights[from2..to2])
}
}
impl<T1, T2> dyn ConsolidatePairedSlices<T1, T2>
where
T1: DataTrait + ?Sized,
T2: WeightTrait + ?Sized,
{
pub const fn factory<T1Type: DBData + Erase<T1>, T2Type: DBWeight + Erase<T2>>() -> &'static Self
{
&ConsolidatePairedSlicesImpl::<T1Type, T2Type, T1, T2> {
phantom: PhantomData,
}
}
}