#[cfg(feature = "serde")]
use super::serde_traits::IndPtrBaseShadow;
use crate::errors::StructureError;
use crate::indexing::SpIndex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::ops::Range;
use std::ops::{Deref, DerefMut};
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(try_from = "IndPtrBaseShadow<Iptr, Storage>")
)]
pub struct IndPtrBase<Iptr, Storage>
where
Iptr: SpIndex,
Storage: Deref<Target = [Iptr]>,
{
storage: Storage,
}
pub type IndPtr<Iptr> = IndPtrBase<Iptr, Vec<Iptr>>;
pub type IndPtrView<'a, Iptr> = IndPtrBase<Iptr, &'a [Iptr]>;
impl<Iptr, Storage> IndPtrBase<Iptr, Storage>
where
Iptr: SpIndex,
Storage: Deref<Target = [Iptr]>,
{
pub(crate) fn check_structure(
storage: &Storage,
) -> Result<(), StructureError> {
for i in storage.iter() {
if i.try_index().is_none() {
return Err(StructureError::OutOfRange(
"Indptr value out of range of usize",
));
}
}
if !storage
.windows(2)
.all(|x| x[0].index_unchecked() <= x[1].index_unchecked())
{
return Err(StructureError::Unsorted("Unsorted indptr"));
}
if storage
.last()
.cloned()
.map(Iptr::index_unchecked)
.map(|i| i > usize::max_value() / 2)
.unwrap_or(false)
{
return Err(StructureError::OutOfRange(
"An indptr value is larger than allowed",
));
}
if storage.len() == 0 {
return Err(StructureError::SizeMismatch(
"An indptr should have its len >= 1",
));
}
Ok(())
}
pub fn new_checked(
storage: Storage,
) -> Result<Self, (Storage, StructureError)> {
match IndPtrBase::check_structure(&storage) {
Ok(_) => Ok(IndPtrBase::new_trusted(storage)),
Err(e) => Err((storage, e)),
}
}
pub(crate) fn new_trusted(storage: Storage) -> Self {
IndPtrBase { storage }
}
pub fn view(&self) -> IndPtrView<Iptr> {
IndPtrView {
storage: &self.storage[..],
}
}
pub fn len(&self) -> usize {
self.storage.len()
}
pub fn is_empty(&self) -> bool {
debug_assert!(self.storage.len() != 0);
self.storage.len() <= 1
}
pub fn outer_dims(&self) -> usize {
if self.storage.len() >= 1 {
self.storage.len() - 1
} else {
0
}
}
pub fn is_proper(&self) -> bool {
self.storage
.get(0)
.map(|i| *i == Iptr::zero())
.unwrap_or(false)
}
pub fn as_slice(&self) -> Option<&[Iptr]> {
if self.is_proper() {
Some(&self.storage[..])
} else {
None
}
}
pub fn raw_storage(&self) -> &[Iptr] {
&self.storage[..]
}
pub(crate) fn raw_storage_mut(&mut self) -> &mut [Iptr]
where
Storage: DerefMut<Target = [Iptr]>,
{
&mut self.storage[..]
}
pub fn into_raw_storage(self) -> Storage {
self.storage
}
pub fn to_owned(&self) -> IndPtr<Iptr> {
IndPtr {
storage: self.storage.to_vec(),
}
}
pub fn to_proper(&self) -> std::borrow::Cow<[Iptr]> {
if self.is_proper() {
std::borrow::Cow::Borrowed(&self.storage[..])
} else {
let offset = self.offset();
let proper = self.storage.iter().map(|i| *i - offset).collect();
std::borrow::Cow::Owned(proper)
}
}
fn offset(&self) -> Iptr {
let zero = Iptr::zero();
self.storage.get(0).cloned().unwrap_or(zero)
}
pub fn iter_outer_nnz_inds(
&self,
) -> impl std::iter::DoubleEndedIterator<Item = usize>
+ std::iter::ExactSizeIterator<Item = usize>
+ '_ {
let mut cur_outer = 0;
(0..self.nnz()).map(move |i| {
loop {
let nnz_end = self.outer_inds_sz(cur_outer).end;
if i == nnz_end {
cur_outer += 1;
} else {
break;
}
}
cur_outer
})
}
pub fn iter_outer(
&self,
) -> impl std::iter::DoubleEndedIterator<Item = Range<Iptr>>
+ std::iter::ExactSizeIterator<Item = Range<Iptr>>
+ '_ {
let offset = self.offset();
self.storage.windows(2).map(move |x| {
if offset == Iptr::zero() {
x[0]..x[1]
} else {
(x[0] - offset)..(x[1] - offset)
}
})
}
pub fn iter_outer_sz(
&self,
) -> impl std::iter::DoubleEndedIterator<Item = Range<usize>>
+ std::iter::ExactSizeIterator<Item = Range<usize>>
+ '_ {
self.iter_outer().map(|range| {
range.start.index_unchecked()..range.end.index_unchecked()
})
}
pub fn index(&self, i: usize) -> Iptr {
let offset = self.offset();
self.storage[i] - offset
}
pub fn outer_inds(&self, i: usize) -> Range<Iptr> {
assert!(i + 1 < self.storage.len());
let offset = self.offset();
(self.storage[i] - offset)..(self.storage[i + 1] - offset)
}
pub fn outer_inds_sz(&self, i: usize) -> Range<usize> {
let range = self.outer_inds(i);
range.start.index_unchecked()..range.end.index_unchecked()
}
pub fn nnz_in_outer(&self, i: usize) -> Iptr {
assert!(i + 1 < self.storage.len());
self.storage[i + 1] - self.storage[i]
}
pub fn nnz_in_outer_sz(&self, i: usize) -> usize {
self.nnz_in_outer(i).index_unchecked()
}
pub fn outer_inds_slice(&self, start: usize, end: usize) -> Range<usize> {
let off = self.offset();
let range = (self.storage[start] - off)..(self.storage[end] - off);
range.start.index_unchecked()..range.end.index_unchecked()
}
pub fn nnz(&self) -> usize {
let offset = self.offset();
self.storage
.last()
.map(|i| *i - offset)
.map(Iptr::index_unchecked)
.unwrap_or(0)
}
pub fn nnz_i(&self) -> Iptr {
let offset = self.offset();
let zero = Iptr::zero();
self.storage.last().map(|i| *i - offset).unwrap_or(zero)
}
pub(crate) fn middle_slice(
&self,
range: impl crate::range::Range,
) -> IndPtrView<Iptr> {
self.view().middle_slice_rbr(range)
}
}
impl<Iptr: SpIndex> IndPtr<Iptr> {
pub(crate) fn reserve(&mut self, cap: usize) {
self.storage.reserve(cap);
}
pub(crate) fn reserve_exact(&mut self, cap: usize) {
self.storage.reserve_exact(cap);
}
pub(crate) fn push(&mut self, elem: Iptr) {
self.storage.push(elem);
}
pub(crate) fn resize(&mut self, new_len: usize, value: Iptr) {
self.storage.resize(new_len, value);
}
pub(crate) fn record_new_element(&mut self, outer_ind: usize) {
for val in self.storage[outer_ind + 1..].iter_mut() {
*val += Iptr::one();
}
}
}
impl<'a, Iptr: SpIndex> IndPtrView<'a, Iptr> {
pub(crate) fn middle_slice_rbr(
&self,
range: impl crate::range::Range,
) -> IndPtrView<'a, Iptr> {
let start = range.start();
let end = range.end().unwrap_or_else(|| self.outer_dims());
IndPtrView {
storage: &self.storage[start..=end],
}
}
pub(crate) fn reborrow(&self) -> IndPtrView<'a, Iptr> {
IndPtrView {
storage: &self.storage[..],
}
}
}
impl<Iptr: SpIndex, IptrStorage, IptrStorage2> std::cmp::PartialEq<IptrStorage2>
for IndPtrBase<Iptr, IptrStorage>
where
IptrStorage: Deref<Target = [Iptr]>,
IptrStorage2: Deref<Target = [Iptr]>,
{
fn eq(&self, other: &IptrStorage2) -> bool {
self.raw_storage() == other.deref()
}
}
#[cfg(test)]
mod tests {
use super::{IndPtr, IndPtrView};
#[test]
fn constructors() {
let raw_valid = vec![0, 1, 2, 3];
assert!(IndPtr::new_checked(raw_valid).is_ok());
let raw_valid = vec![0, 1, 2, 3];
assert!(IndPtrView::new_checked(&raw_valid).is_ok());
let raw_valid = vec![0];
assert!(IndPtrView::new_checked(&raw_valid).is_ok());
let raw_valid = vec![1];
assert!(IndPtrView::new_checked(&raw_valid).is_ok());
let raw_invalid = &[0, 2, 1];
assert_eq!(
IndPtrView::new_checked(raw_invalid)
.map_err(|(_, e)| e.kind())
.unwrap_err(),
crate::errors::StructureErrorKind::Unsorted
);
let raw_invalid: &[usize] = &[];
assert!(IndPtrView::new_checked(raw_invalid).is_err());
}
#[test]
fn empty() {
assert!(IndPtrView::new_checked(&[0]).unwrap().is_empty());
assert!(!IndPtrView::new_checked(&[0, 1]).unwrap().is_empty());
#[cfg(debug_assertions)]
{
#[should_panic]
assert!(IndPtrView::new_trusted(&[0]).is_empty());
}
#[cfg(not(debug_assertions))]
{
assert!(IndPtrView::new_trusted(&[0]).is_empty());
}
}
#[test]
fn outer_dims() {
assert_eq!(IndPtrView::new_checked(&[0]).unwrap().outer_dims(), 0);
assert_eq!(IndPtrView::new_checked(&[0, 1]).unwrap().outer_dims(), 1);
assert_eq!(
IndPtrView::new_checked(&[2, 3, 5, 7]).unwrap().outer_dims(),
3
);
}
#[test]
fn is_proper() {
assert!(IndPtrView::new_checked(&[0, 1]).unwrap().is_proper());
assert!(!IndPtrView::new_checked(&[1, 2]).unwrap().is_proper());
}
#[test]
fn offset() {
assert_eq!(IndPtrView::new_checked(&[0, 1]).unwrap().offset(), 0);
assert_eq!(IndPtrView::new_checked(&[1, 2]).unwrap().offset(), 1);
}
#[test]
fn nnz() {
assert_eq!(IndPtrView::new_checked(&[0, 1]).unwrap().nnz(), 1);
assert_eq!(IndPtrView::new_checked(&[1, 2]).unwrap().nnz(), 1);
}
#[test]
fn outer_inds() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
assert_eq!(iptr.outer_inds(0), 0..1);
assert_eq!(iptr.outer_inds(1), 1..3);
assert_eq!(iptr.outer_inds(2), 3..8);
let res = std::panic::catch_unwind(|| iptr.outer_inds(3));
assert!(res.is_err());
}
#[test]
fn nnz_in_outer() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
assert_eq!(iptr.nnz_in_outer(0), 1);
assert_eq!(iptr.nnz_in_outer(1), 2);
assert_eq!(iptr.nnz_in_outer(2), 5);
}
#[test]
fn outer_inds_slice() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
assert_eq!(iptr.outer_inds_slice(0, 1), 0..1);
assert_eq!(iptr.outer_inds_slice(0, 2), 0..3);
assert_eq!(iptr.outer_inds_slice(1, 3), 1..8);
let res = std::panic::catch_unwind(|| iptr.outer_inds_slice(3, 4));
assert!(res.is_err());
}
#[test]
fn iter_outer() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
let mut iter = iptr.iter_outer();
assert_eq!(iter.next().unwrap(), 0..1);
assert_eq!(iter.next().unwrap(), 1..3);
assert_eq!(iter.next().unwrap(), 3..8);
assert!(iter.next().is_none());
}
#[test]
fn iter_outer_nnz_inds() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
let mut iter = iptr.iter_outer_nnz_inds();
assert_eq!(iter.next().unwrap(), 0);
assert_eq!(iter.next().unwrap(), 1);
assert_eq!(iter.next().unwrap(), 1);
assert_eq!(iter.next().unwrap(), 2);
assert_eq!(iter.next().unwrap(), 2);
assert_eq!(iter.next().unwrap(), 2);
assert_eq!(iter.next().unwrap(), 2);
assert_eq!(iter.next().unwrap(), 2);
assert!(iter.next().is_none());
}
#[test]
fn compare_with_slices() {
let iptr = IndPtrView::new_checked(&[0, 1, 3, 8]).unwrap();
assert!(iptr == &[0, 1, 3, 8][..]);
assert!(iptr == vec![0, 1, 3, 8]);
let iptr = IndPtrView::new_checked(&[1, 1, 3, 8]).unwrap();
assert!(iptr == &[1, 1, 3, 8][..]);
}
}