#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Csr<T> {
offsets: Vec<usize>,
values: Vec<T>,
}
impl<T> Csr<T> {
#[must_use]
pub fn empty() -> Self {
Self {
offsets: vec![0],
values: Vec::new(),
}
}
#[must_use]
pub fn rows(&self) -> usize {
self.offsets.len() - 1
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.rows() == 0
}
#[must_use]
pub fn len(&self) -> usize {
self.values.len()
}
#[must_use]
pub fn row(&self, index: usize) -> &[T] {
let (Some(start), Some(end)) = (
self.offsets.get(index),
index.checked_add(1).and_then(|next| self.offsets.get(next)),
) else {
return &[];
};
match self.values.get(*start..*end) {
Some(items) => items,
None => &[],
}
}
pub fn row_mut(&mut self, index: usize) -> &mut [T] {
let (Some(start), Some(end)) = (
self.offsets.get(index),
index.checked_add(1).and_then(|next| self.offsets.get(next)),
) else {
return &mut [];
};
let (start, end) = (*start, *end);
match self.values.get_mut(start..end) {
Some(items) => items,
None => &mut [],
}
}
pub fn iter(&self) -> impl Iterator<Item = &[T]> {
(0..self.rows()).map(|index| self.row(index))
}
}
#[derive(Debug)]
pub struct CsrBuilder<T> {
cursors: Vec<usize>,
offsets: Vec<usize>,
values: Vec<T>,
}
impl<T: Clone + Default> CsrBuilder<T> {
#[must_use]
pub fn with_degrees(degrees: &[usize]) -> Self {
let mut offsets = Vec::with_capacity(degrees.len() + 1);
let mut total = 0usize;
offsets.push(0);
for degree in degrees {
total = total.saturating_add(*degree);
offsets.push(total);
}
Self {
cursors: offsets[..degrees.len()].to_vec(),
offsets,
values: vec![T::default(); total],
}
}
pub fn push(&mut self, row: usize, item: T) {
let (Some(cursor), Some(end)) = (self.cursors.get_mut(row), self.offsets.get(row + 1))
else {
return;
};
if *cursor >= *end {
return;
}
if let Some(slot) = self.values.get_mut(*cursor) {
*slot = item;
*cursor += 1;
}
}
#[must_use]
pub fn finish(mut self) -> Csr<T> {
let mut offsets = Vec::with_capacity(self.offsets.len());
let mut write = 0usize;
offsets.push(0);
for row in 0..self.cursors.len() {
let (Some(start), Some(cursor)) = (self.offsets.get(row), self.cursors.get(row)) else {
break;
};
for read in *start..*cursor {
self.values.swap(write, read);
write += 1;
}
offsets.push(write);
}
self.values.truncate(write);
Csr {
offsets,
values: self.values,
}
}
}
impl<T: Clone + Default + Ord> CsrBuilder<T> {
#[must_use]
pub fn finish_canonical(mut self) -> Csr<T> {
let mut offsets = Vec::with_capacity(self.offsets.len());
let mut write = 0usize;
offsets.push(0);
for row in 0..self.cursors.len() {
let (Some(start), Some(cursor)) = (self.offsets.get(row), self.cursors.get(row)) else {
break;
};
let (start, cursor) = (*start, *cursor);
if let Some(items) = self.values.get_mut(start..cursor) {
items.sort_unstable();
}
let row_start = write;
for read in start..cursor {
if write > row_start && self.values.get(write - 1) == self.values.get(read) {
continue;
}
self.values.swap(write, read);
write += 1;
}
offsets.push(write);
}
self.values.truncate(write);
Csr {
offsets,
values: self.values,
}
}
}
pub(super) fn stored_range(
starts: &[u32],
counts: &[u32],
index: usize,
) -> Option<std::ops::Range<u32>> {
let start = *starts.get(index)?;
let count = *counts.get(index)?;
Some(start..start.checked_add(count)?)
}
#[cfg(test)]
#[path = "csr_tests.rs"]
mod tests;