use std::fmt;
use rayon::prelude::*;
use crate::batch::RleBatch;
use crate::error::{Error, Result};
use crate::num::Num;
use crate::reduce;
use crate::rle::{Rle, RleLike, RleRef};
pub(crate) fn run_index(ends: &[i64], pos: i64) -> Option<usize> {
let len = ends.last().copied().unwrap_or(0);
if pos < 0 || pos >= len {
return None;
}
Some(ends.partition_point(|&end| end <= pos))
}
pub(crate) fn validate_intervals(starts: &[i64], ends: &[i64]) -> Result<()> {
if starts.len() != ends.len() {
return Err(Error::LengthMismatch {
what: "starts and ends",
left: starts.len(),
right: ends.len(),
});
}
for (index, (&start, &end)) in starts.iter().zip(ends).enumerate() {
if start < 0 || end < start {
return Err(Error::InvalidInterval { index, start, end });
}
}
Ok(())
}
pub(crate) fn slice_into<V: Clone>(
rle: RleRef<'_, V>,
start: i64,
end: i64,
ends: &mut Vec<i64>,
values: &mut Vec<V>,
) {
let (start, end) = (start.max(0), end.min(rle.len()));
if start >= end {
return;
}
let first = rle.ends.partition_point(|&e| e <= start);
let last = rle.ends.partition_point(|&e| e < end);
ends.extend(rle.ends[first..last].iter().map(|&e| e - start));
ends.push(end - start);
values.extend_from_slice(&rle.values[first..=last]);
}
pub(crate) fn slice<V: Clone>(rle: RleRef<'_, V>, start: i64, end: i64) -> Rle<V> {
let mut out = Rle::new();
slice_into(rle, start, end, &mut out.ends, &mut out.values);
out
}
pub(crate) fn getitems<V: Clone>(
rle: RleRef<'_, V>,
starts: &[i64],
ends: &[i64],
) -> Result<RleBatch<usize, V>> {
validate_intervals(starts, ends)?;
Ok(RleBatch::from_slices(
(0..starts.len()).collect(),
|k, out_ends, out_values| slice_into(rle, starts[k], ends[k], out_ends, out_values),
))
}
pub struct SliceView<'a, V> {
ends: &'a [i64],
values: &'a [V],
start: i64,
end: i64,
}
impl<V> Clone for SliceView<'_, V> {
fn clone(&self) -> Self {
*self
}
}
impl<V> Copy for SliceView<'_, V> {}
impl<'a, V> SliceView<'a, V> {
pub(crate) fn new(rle: RleRef<'a, V>, start: i64, end: i64) -> Self {
let (start, end) = (start.max(0), end.min(rle.len()));
if start >= end {
return SliceView {
ends: &[],
values: &[],
start: 0,
end: 0,
};
}
let first = rle.ends.partition_point(|&e| e <= start);
let last = rle.ends.partition_point(|&e| e < end);
SliceView {
ends: &rle.ends[first..=last],
values: &rle.values[first..=last],
start,
end,
}
}
pub fn len(&self) -> i64 {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
pub fn num_runs(&self) -> usize {
self.values.len()
}
pub fn values(&self) -> &'a [V] {
self.values
}
pub fn iter_runs(&self) -> impl Iterator<Item = (i64, i64, &'a V)> + Clone + 'a {
let (start, end) = (self.start, self.end);
let mut prev = start;
self.ends.iter().zip(self.values).map(move |(&e, value)| {
let (run_start, run_end) = (prev - start, e.min(end) - start);
prev = e;
(run_start, run_end, value)
})
}
fn weighted(&self) -> impl Iterator<Item = (i64, V)> + Clone + 'a
where
V: Copy,
{
self.iter_runs().map(|(s, e, &v)| (e - s, v))
}
pub fn to_rle(&self) -> Rle<V>
where
V: Clone,
{
let mut out = Rle::new();
for (_, end, value) in self.iter_runs() {
out.ends.push(end);
out.values.push(value.clone());
}
out
}
pub fn sum(&self) -> V
where
V: Num,
{
reduce::sum(self.weighted())
}
pub fn mean(&self) -> Option<f64>
where
V: Num,
{
reduce::mean(self.weighted(), self.len())
}
pub fn variance(&self, ddof: i64) -> Option<f64>
where
V: Num,
{
reduce::variance(self.weighted(), self.len(), ddof)
}
pub fn std(&self, ddof: i64) -> Option<f64>
where
V: Num,
{
self.variance(ddof).map(f64::sqrt)
}
pub fn min(&self) -> Option<&'a V>
where
V: PartialOrd,
{
reduce::extreme(self.values, |a, b| a < b)
}
pub fn max(&self) -> Option<&'a V>
where
V: PartialOrd,
{
reduce::extreme(self.values, |a, b| a > b)
}
}
impl<V: fmt::Debug> fmt::Debug for SliceView<'_, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.iter_runs().map(|(s, e, v)| (e - s, v)))
.finish()
}
}
const SLICES_PER_TASK: usize = 1024;
pub(crate) fn slice_reduce<'a, V, T, R, F>(
starts: &[i64],
ends: &[i64],
rle_of: R,
f: F,
) -> Result<Vec<T>>
where
V: Sync + 'a,
T: Send,
R: Fn(usize) -> RleRef<'a, V> + Sync,
F: Fn(SliceView<'a, V>) -> T + Sync,
{
validate_intervals(starts, ends)?;
Ok((0..starts.len())
.into_par_iter()
.with_min_len(SLICES_PER_TASK)
.map(|k| f(SliceView::new(rle_of(k), starts[k], ends[k])))
.collect())
}