use mem_dbg::*;
use value_traits::slices::SliceByValue;
use crate::dict::elias_fano::EfSeq;
use crate::dict::{EliasFano, EliasFanoBuilder};
use crate::traits::SelectUnchecked;
use crate::traits::iter::{IntoIteratorFrom, UncheckedIterator};
#[derive(Debug, Clone, MemSize, MemDbg)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PrefixSumIntList<D = EfSeq<usize>> {
n: usize,
prefix_sums: D,
}
impl PrefixSumIntList {
#[must_use]
pub fn new<I: ?Sized>(values: &I) -> Self
where
for<'a> &'a I: IntoIterator<Item = &'a usize>,
{
let mut n = 0;
let mut total: usize = 0;
for &v in values {
n += 1;
total = total
.checked_add(v)
.expect("PrefixSumIntList: prefix sum overflow");
}
let mut efb = EliasFanoBuilder::new(n + 1, total);
let mut prefix: usize = 0;
unsafe { efb.push_unchecked(prefix) };
for &v in values {
prefix += v;
unsafe { efb.push_unchecked(prefix) };
}
let prefix_sums = efb.build_with_seq();
PrefixSumIntList { n, prefix_sums }
}
}
impl<H: AsRef<[usize]> + SelectUnchecked, L: SliceByValue<Value = usize>>
From<EliasFano<usize, H, L>> for PrefixSumIntList<EliasFano<usize, H, L>>
{
fn from(elias_fano: EliasFano<usize, H, L>) -> Self {
PrefixSumIntList {
n: elias_fano.len() - 1,
prefix_sums: elias_fano,
}
}
}
impl<D: SliceByValue<Value = usize>> PrefixSumIntList<D> {
pub fn try_from_prefix_sums(prefix_sums: D) -> Result<Self, &'static str> {
let len = prefix_sums.len();
if len == 0 {
return Err("PrefixSumIntList: prefix-sum sequence must be non-empty");
}
let mut prev = prefix_sums.index_value(0);
if prev != 0 {
return Err("PrefixSumIntList: first element must be zero");
}
for i in 1..len {
let cur = prefix_sums.index_value(i);
if cur < prev {
return Err("PrefixSumIntList: values must be monotonically nondecreasing");
}
prev = cur;
}
Ok(PrefixSumIntList {
n: len - 1,
prefix_sums,
})
}
}
impl<D: SliceByValue<Value = usize>> PrefixSumIntList<D> {
pub unsafe fn map_prefix_sums<F, D2>(self, func: F) -> PrefixSumIntList<D2>
where
F: FnOnce(D) -> D2,
D2: SliceByValue<Value = usize>,
{
PrefixSumIntList {
n: self.n,
prefix_sums: func(self.prefix_sums),
}
}
pub fn into_inner(self) -> D {
self.prefix_sums
}
}
impl<D: SliceByValue<Value = usize>> PrefixSumIntList<D>
where
for<'a> &'a D: IntoIteratorFrom,
for<'a> <&'a D as IntoIteratorFrom>::IntoIterFrom: UncheckedIterator<Item = usize>,
{
#[inline(always)]
pub fn prefix_sum(&self, i: usize) -> usize {
self.prefix_sums.index_value(i)
}
#[inline(always)]
pub unsafe fn prefix_sum_unchecked(&self, i: usize) -> usize {
unsafe { self.prefix_sums.get_value_unchecked(i) }
}
}
impl<D: SliceByValue<Value = usize>> SliceByValue for PrefixSumIntList<D>
where
for<'a> &'a D: IntoIteratorFrom,
for<'a> <&'a D as IntoIteratorFrom>::IntoIterFrom: UncheckedIterator<Item = usize>,
{
type Value = usize;
#[inline(always)]
fn len(&self) -> usize {
self.n
}
#[inline]
unsafe fn get_value_unchecked(&self, index: usize) -> usize {
let mut iter = (&self.prefix_sums).into_iter_from(index);
let start = unsafe { iter.next_unchecked() };
let end = unsafe { iter.next_unchecked() };
end - start
}
}
use crate::traits::TryIntoUnaligned;
impl<D: TryIntoUnaligned + SliceByValue<Value = usize>> TryIntoUnaligned for PrefixSumIntList<D>
where
D::Unaligned: SliceByValue<Value = usize>,
{
type Unaligned = PrefixSumIntList<D::Unaligned>;
fn try_into_unaligned(
self,
) -> Result<Self::Unaligned, crate::traits::UnalignedConversionError> {
Ok(PrefixSumIntList {
n: self.n,
prefix_sums: self.prefix_sums.try_into_unaligned()?,
})
}
}