use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Sub};
use std::sync::OnceLock;
use rayon::prelude::*;
use crate::error::{Error, Result};
use crate::identical::Identical;
use crate::ops;
use crate::rle::{Rle, RleLike, RleRef, validate_ends};
use crate::slice::{self, SliceView};
use crate::transform;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum How {
#[default]
Outer,
Inner,
Left,
Right,
}
pub struct RleBatch<K, V> {
keys: Vec<K>,
offsets: Vec<usize>,
ends: Vec<i64>,
values: Vec<V>,
index: OnceLock<HashMap<K, usize>>,
}
impl<K, V> RleBatch<K, V> {
pub fn new() -> Self {
RleBatch {
keys: Vec::new(),
offsets: vec![0],
ends: Vec::new(),
values: Vec::new(),
index: OnceLock::new(),
}
}
pub(crate) fn from_slices(
keys: Vec<K>,
mut fill: impl FnMut(usize, &mut Vec<i64>, &mut Vec<V>),
) -> Self {
let mut out = RleBatch::new();
out.offsets.reserve(keys.len());
for k in 0..keys.len() {
fill(k, &mut out.ends, &mut out.values);
out.offsets.push(out.ends.len());
}
out.keys = keys;
out
}
pub(crate) fn from_rles_unchecked(keys: Vec<K>, rles: Vec<Rle<V>>) -> Self {
debug_assert_eq!(keys.len(), rles.len());
let total = rles.iter().map(|r| r.ends.len()).sum();
let mut out = RleBatch::new();
out.ends.reserve(total);
out.values.reserve(total);
for rle in rles {
out.ends.extend(rle.ends);
out.values.extend(rle.values);
out.offsets.push(out.ends.len());
}
out.keys = keys;
out
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub fn keys(&self) -> &[K] {
&self.keys
}
pub fn offsets(&self) -> &[usize] {
&self.offsets
}
pub fn ends(&self) -> &[i64] {
&self.ends
}
pub fn values(&self) -> &[V] {
&self.values
}
pub fn get_index(&self, position: usize) -> Option<RleRef<'_, V>> {
(position < self.len()).then(|| self.rle_at(position))
}
fn rle_at(&self, position: usize) -> RleRef<'_, V> {
let (start, end) = (self.offsets[position], self.offsets[position + 1]);
RleRef::new_unchecked(&self.ends[start..end], &self.values[start..end])
}
pub fn iter(&self) -> impl Iterator<Item = (&K, RleRef<'_, V>)> {
self.keys
.iter()
.enumerate()
.map(|(i, k)| (k, self.rle_at(i)))
}
pub fn into_parts(self) -> (Vec<K>, Vec<usize>, Vec<i64>, Vec<V>) {
(self.keys, self.offsets, self.ends, self.values)
}
pub fn reduce<T, F>(&self, f: F) -> Vec<T>
where
K: Send + Sync,
V: Sync,
T: Send,
F: Fn(RleRef<'_, V>) -> T + Sync,
{
(0..self.len())
.into_par_iter()
.map(|i| f(self.rle_at(i)))
.collect()
}
pub fn map_rles<W, F>(&self, f: F) -> RleBatch<K, W>
where
K: Clone + Send + Sync,
V: Sync,
W: Send,
F: Fn(RleRef<'_, V>) -> Rle<W> + Sync,
{
RleBatch::from_rles_unchecked(self.keys.clone(), self.reduce(f))
}
pub fn shift(&self, distance: i64, fill: V) -> RleBatch<K, V>
where
K: Clone + Send + Sync,
V: Clone + Identical + Send + Sync,
{
self.map_rles(|r| r.shift(distance, fill.clone()))
}
pub fn reversed(&self) -> RleBatch<K, V>
where
K: Clone + Send + Sync,
V: Clone + Send + Sync,
{
self.map_rles(|r| r.reversed())
}
}
impl<K, V> RleBatch<K, V> {
pub fn collapse<K2, F, G>(&self, key: F, fill: Option<&V>, f: G) -> Result<RleBatch<K2, V>>
where
K: Send + Sync,
K2: Hash + Eq + Clone + Send + Sync,
V: Clone + Identical + Send + Sync,
F: Fn(&K) -> K2,
G: Fn(&V, &V) -> V + Sync,
{
let mut positions: HashMap<K2, usize> = HashMap::new();
let mut unique = Vec::new();
let mut groups: Vec<Vec<usize>> = Vec::new();
for (i, k) in self.keys.iter().enumerate() {
let new_key = key(k);
let g = *positions.entry(new_key.clone()).or_insert_with(|| {
unique.push(new_key);
groups.push(Vec::new());
groups.len() - 1
});
groups[g].push(i);
}
let rles = groups
.par_iter()
.enumerate()
.map(|(position, members)| {
let mut acc = self.rle_at(members[0]).merged();
for &i in &members[1..] {
acc = ops::zip_with(acc.view(), self.rle_at(i), fill, fill, &f).map_err(
|source| Error::InKey {
position,
source: Box::new(source),
},
)?;
}
Ok(acc)
})
.collect::<Result<Vec<_>>>()?;
Ok(RleBatch::from_rles_unchecked(unique, rles))
}
}
impl<K: Hash + Eq + Clone, V> RleBatch<K, V> {
pub fn from_rles<I: IntoIterator<Item = (K, Rle<V>)>>(rles: I) -> Result<Self> {
let mut out = RleBatch::new();
for (key, rle) in rles {
out.push(key, rle)?;
}
Ok(out)
}
pub fn from_parts(
keys: Vec<K>,
offsets: Vec<usize>,
ends: Vec<i64>,
values: Vec<V>,
) -> Result<Self> {
if offsets.len() != keys.len() + 1 {
return Err(Error::LengthMismatch {
what: "offsets and keys + 1",
left: offsets.len(),
right: keys.len() + 1,
});
}
if ends.len() != values.len() {
return Err(Error::LengthMismatch {
what: "ends and values",
left: ends.len(),
right: values.len(),
});
}
if offsets[0] != 0
|| offsets[keys.len()] != ends.len()
|| offsets.windows(2).any(|w| w[0] > w[1])
{
return Err(Error::InvalidOffsets);
}
for (position, w) in offsets.windows(2).enumerate() {
validate_ends(&ends[w[0]..w[1]]).map_err(|source| Error::InKey {
position,
source: Box::new(source),
})?;
}
let mut index = HashMap::with_capacity(keys.len());
for (position, key) in keys.iter().enumerate() {
if index.insert(key.clone(), position).is_some() {
return Err(Error::DuplicateKey { position });
}
}
Ok(RleBatch {
keys,
offsets,
ends,
values,
index: OnceLock::from(index),
})
}
fn index(&self) -> &HashMap<K, usize> {
self.index.get_or_init(|| {
self.keys
.iter()
.cloned()
.enumerate()
.map(|(i, k)| (k, i))
.collect()
})
}
pub fn push(&mut self, key: K, rle: Rle<V>) -> Result<()> {
let position = self.keys.len();
self.index();
let index = self.index.get_mut().expect("initialised above");
if index.contains_key(&key) {
return Err(Error::DuplicateKey { position });
}
index.insert(key.clone(), position);
self.keys.push(key);
self.ends.extend(rle.ends);
self.values.extend(rle.values);
self.offsets.push(self.ends.len());
Ok(())
}
pub fn position<Q>(&self, key: &Q) -> Option<usize>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.index().get(key).copied()
}
pub fn get<Q>(&self, key: &Q) -> Option<RleRef<'_, V>>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.position(key).map(|i| self.rle_at(i))
}
pub fn getitems<Q>(
&self,
keys: &[Q],
starts: &[i64],
ends: &[i64],
) -> Result<RleBatch<usize, V>>
where
K: Borrow<Q>,
Q: Hash + Eq,
V: Clone,
{
slice::validate_intervals(starts, ends)?;
if keys.len() != starts.len() {
return Err(Error::LengthMismatch {
what: "keys and starts",
left: keys.len(),
right: starts.len(),
});
}
Ok(RleBatch::from_slices(
(0..keys.len()).collect(),
|k, out_ends, out_values| {
let rle = self.get(&keys[k]).unwrap_or_else(RleRef::empty);
slice::slice_into(rle, starts[k], ends[k], out_ends, out_values)
},
))
}
pub fn slice_reduce<'a, Q, T, F>(
&'a self,
keys: &[Q],
starts: &[i64],
ends: &[i64],
f: F,
) -> Result<Vec<T>>
where
K: Borrow<Q> + Send + Sync,
Q: Hash + Eq + Sync,
V: Sync,
T: Send,
F: Fn(SliceView<'a, V>) -> T + Sync,
{
if keys.len() != starts.len() {
return Err(Error::LengthMismatch {
what: "keys and starts",
left: keys.len(),
right: starts.len(),
});
}
let positions: Vec<Option<usize>> = keys.iter().map(|k| self.position(k)).collect();
slice::slice_reduce(
starts,
ends,
|k| positions[k].map_or_else(RleRef::empty, |p| self.rle_at(p)),
f,
)
}
pub fn concat(batches: &[&RleBatch<K, V>]) -> RleBatch<K, V>
where
K: Send + Sync,
V: Clone + Identical + Send + Sync,
{
let mut keys: Vec<K> = Vec::new();
let mut seen: HashMap<&K, usize> = HashMap::new();
for batch in batches {
for key in &batch.keys {
seen.entry(key).or_insert_with(|| {
keys.push(key.clone());
keys.len() - 1
});
}
}
let rles = keys
.par_iter()
.map(|key| transform::concat(batches.iter().filter_map(|b| b.get(key))))
.collect();
RleBatch::from_rles_unchecked(keys, rles)
}
fn join<'a, B>(
&'a self,
other: &'a RleBatch<K, B>,
how: How,
) -> Vec<(&'a K, Option<usize>, Option<usize>)> {
let mut out = Vec::with_capacity(self.len().max(other.len()));
for (i, key) in self.keys.iter().enumerate() {
let j = other.position(key);
if j.is_some() || matches!(how, How::Outer | How::Left) {
out.push((key, Some(i), j));
}
}
if matches!(how, How::Outer | How::Right) {
for (j, key) in other.keys.iter().enumerate() {
if self.position(key).is_none() {
out.push((key, None, Some(j)));
}
}
}
out
}
pub fn zip_with<B, C, F>(
&self,
other: &RleBatch<K, B>,
how: How,
fill_a: Option<&V>,
fill_b: Option<&B>,
f: F,
) -> Result<RleBatch<K, C>>
where
K: Send + Sync,
V: Sync,
B: Sync,
C: Identical + Send,
F: Fn(&V, &B) -> C + Sync,
{
let pairs = self.join(other, how);
let rles = pairs
.par_iter()
.enumerate()
.map(|(position, &(_, i, j))| {
let a = i.map_or_else(RleRef::empty, |i| self.rle_at(i));
let b = j.map_or_else(RleRef::empty, |j| other.rle_at(j));
ops::zip_with(a, b, fill_a, fill_b, &f).map_err(|source| Error::InKey {
position,
source: Box::new(source),
})
})
.collect::<Result<Vec<_>>>()?;
let keys = pairs.into_iter().map(|(key, _, _)| key.clone()).collect();
Ok(RleBatch::from_rles_unchecked(keys, rles))
}
}
impl<K, V> Default for RleBatch<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Clone, V: Clone> Clone for RleBatch<K, V> {
fn clone(&self) -> Self {
RleBatch {
keys: self.keys.clone(),
offsets: self.offsets.clone(),
ends: self.ends.clone(),
values: self.values.clone(),
index: OnceLock::new(),
}
}
}
impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for RleBatch<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl<K: PartialEq, V: Identical> PartialEq for RleBatch<K, V> {
fn eq(&self, other: &Self) -> bool {
self.keys == other.keys
&& self
.iter()
.zip(other.iter())
.all(|((_, a), (_, b))| a.equals(&b))
}
}
macro_rules! batch_binop {
($Trait:ident, $method:ident) => {
impl<K, V> $Trait<&RleBatch<K, V>> for &RleBatch<K, V>
where
K: Hash + Eq + Clone + Send + Sync,
V: $Trait<Output = V> + Clone + Default + Identical + Send + Sync,
{
type Output = RleBatch<K, V>;
fn $method(self, rhs: &RleBatch<K, V>) -> RleBatch<K, V> {
let zero = V::default();
self.zip_with(rhs, How::Outer, Some(&zero), Some(&zero), |a, b| {
$Trait::$method(a.clone(), b.clone())
})
.expect("both sides have a fill value")
}
}
};
}
batch_binop!(Add, add);
batch_binop!(Sub, sub);
batch_binop!(Mul, mul);
batch_binop!(Div, div);
batch_binop!(BitAnd, bitand);
batch_binop!(BitOr, bitor);
batch_binop!(BitXor, bitxor);