use core::ops::Range;
use uor_matmul_core::{Alphabet, Bound, Element};
use crate::tier::Codec;
#[derive(Clone, Copy, Debug)]
pub struct CodedMatrix<'a, E: Element, Bd: Bound, C: Codec<E, Bd>> {
codec: C,
rows: usize,
cols: usize,
codes: &'a [C::Code],
_marker: core::marker::PhantomData<fn() -> (E, Bd)>,
}
impl<'a, E: Element, Bd: Bound, C: Codec<E, Bd>> CodedMatrix<'a, E, Bd, C> {
pub fn new(codec: C, rows: usize, cols: usize, codes: &'a [C::Code]) -> Option<Self> {
if C::MAX_BLOCK == 0 {
return None;
}
if C::IS_FIXED_WIDTH {
if !cols.is_multiple_of(C::MAX_BLOCK) {
return None;
}
let per_row = cols / C::MAX_BLOCK;
if rows.checked_mul(per_row)? != codes.len() {
return None;
}
return Some(Self {
codec,
rows,
cols,
codes,
_marker: core::marker::PhantomData,
});
}
let mut at = 0usize;
for _ in 0..rows {
let mut width = 0usize;
while width < cols {
let code = *codes.get(at)?;
let n = codec.decode_len(code);
if n == 0 {
return None;
}
width = width.checked_add(n)?;
at = at.checked_add(1)?;
}
if width != cols {
return None;
}
}
if at != codes.len() {
return None;
}
Some(Self {
codec,
rows,
cols,
codes,
_marker: core::marker::PhantomData,
})
}
pub const fn rows(&self) -> usize {
self.rows
}
pub const fn cols(&self) -> usize {
self.cols
}
pub const fn codec(&self) -> &C {
&self.codec
}
pub const fn codes_per_row(&self) -> usize {
self.cols / C::MAX_BLOCK
}
pub fn row_code_range(&self, r: usize) -> Range<usize> {
if C::IS_FIXED_WIDTH {
let per_row = self.codes_per_row();
return r * per_row..(r + 1) * per_row;
}
let mut start = 0usize;
for _ in 0..r {
let mut width = 0usize;
while width < self.cols {
width += self.codec.decode_len(self.codes[start]);
start += 1;
}
}
let mut end = start;
let mut width = 0usize;
while width < self.cols {
width += self.codec.decode_len(self.codes[end]);
end += 1;
}
start..end
}
pub const fn codec_ref(&self) -> &C {
&self.codec
}
pub const fn codes(&self) -> &'a [C::Code] {
self.codes
}
pub fn decode_row_into(&self, r: usize, out: &mut [Alphabet<E, Bd>]) -> usize {
let range = self.row_code_range(r);
self.codec.decode_seq(&self.codes[range], out)
}
pub fn decode_range_into(&self, r: usize, cols: Range<usize>, out: &mut [Alphabet<E, Bd>]) {
for (slot, col) in out.iter_mut().zip(cols) {
*slot = self.at(r, col);
}
}
pub fn column_walk<'m>(&'m self, c: usize) -> ColumnWalk<'m, 'a, E, Bd, C> {
ColumnWalk {
m: self,
col: c,
row: 0,
cursor: 0,
}
}
pub fn at(&self, r: usize, c: usize) -> Alphabet<E, Bd> {
if C::IS_FIXED_WIDTH {
let block = C::MAX_BLOCK;
let code = self.codes[r * self.codes_per_row() + c / block];
return self.codec.decode_element(code, c % block);
}
let range = self.row_code_range(r);
let mut width = 0usize;
for &code in &self.codes[range] {
let n = self.codec.decode_len(code);
if c < width + n {
return self.codec.decode_element(code, c - width);
}
width += n;
}
Alphabet::ZERO
}
}
#[derive(Clone, Copy)]
pub struct ColumnWalk<'m, 'a, E: Element, Bd: Bound, C: Codec<E, Bd>> {
m: &'m CodedMatrix<'a, E, Bd, C>,
col: usize,
row: usize,
cursor: usize,
}
impl<E: Element, Bd: Bound, C: Codec<E, Bd>> Iterator for ColumnWalk<'_, '_, E, Bd, C> {
type Item = Alphabet<E, Bd>;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.m.rows() || self.col >= self.m.cols() {
return None;
}
let out = if C::IS_FIXED_WIDTH {
let block = C::MAX_BLOCK;
let code = self.m.codes()[self.row * self.m.codes_per_row() + self.col / block];
self.m.codec_ref().decode_element(code, self.col % block)
} else {
let mut found = Alphabet::ZERO;
let mut width = 0usize;
while width < self.m.cols() {
let code = self.m.codes()[self.cursor];
let n = self.m.codec_ref().decode_len(code);
if self.col >= width && self.col < width + n {
found = self.m.codec_ref().decode_element(code, self.col - width);
}
width += n;
self.cursor += 1;
}
found
};
self.row += 1;
Some(out)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let left = self.m.rows().saturating_sub(self.row); (left, Some(left))
}
}
impl<E: Element, Bd: Bound, C: Codec<E, Bd>> ExactSizeIterator for ColumnWalk<'_, '_, E, Bd, C> {}