use std::sync::Arc;
use crate::enums::error::MinarrowError;
use crate::enums::shape_dim::ShapeDim;
use crate::structs::chunked::super_table::SuperTable;
use crate::traits::concatenate::Concatenate;
use crate::traits::consolidate::Consolidate;
use crate::traits::shape::Shape;
use crate::{Field, Table, TableV};
#[derive(Debug, Clone)]
pub struct SuperTableV {
pub slices: Vec<TableV>,
pub len: usize,
}
impl SuperTableV {
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn n_slices(&self) -> usize {
self.slices.len()
}
#[inline]
pub fn cols(&self) -> Vec<Arc<Field>> {
self.slices[0].fields.iter().map(|x| x.clone()).collect()
}
#[inline]
pub fn chunks(&self) -> impl Iterator<Item = &TableV> {
self.slices.iter()
}
#[inline]
pub fn slice(&self, mut offset: usize, mut len: usize) -> Self {
assert!(offset + len <= self.len, "slice out of bounds");
let mut slices = Vec::new();
let requested_len = len;
for slice in &self.slices {
if offset >= slice.len {
offset -= slice.len;
continue;
}
let take = (slice.len - offset).min(len);
slices.push(slice.from_self(offset, take));
len -= take;
if len == 0 {
break;
}
offset = 0;
}
SuperTableV {
slices: slices,
len: requested_len,
}
}
#[inline]
pub fn row(&self, mut row_idx: usize) -> TableV {
for slice in &self.slices {
if row_idx < slice.len {
return slice.from_self(row_idx, 1);
}
row_idx -= slice.len;
}
panic!("row index out of bounds");
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = TableV> + '_ {
self.slices.iter().cloned()
}
#[inline]
fn locate(&self, row: usize) -> (usize, usize) {
assert!(row < self.len, "row out of bounds");
let mut acc = 0;
for (ci, p) in self.slices.iter().enumerate() {
if row < acc + p.len {
return (ci, row - acc);
}
acc += p.len;
}
unreachable!()
}
#[inline]
pub fn row_slice(&self, row: usize) -> TableV {
let (ci, ri) = self.locate(row);
self.slices[ci].from_self(ri, 1)
}
#[inline]
pub fn n_rows(&self) -> usize {
self.len
}
#[inline]
pub fn n_cols(&self) -> usize {
let n_batches = self.slices.len();
if n_batches > 0 {
self.slices[0].fields.len()
} else {
0
}
}
}
impl Shape for SuperTableV {
fn shape(&self) -> ShapeDim {
ShapeDim::Rank2 {
rows: self.n_rows(),
cols: self.n_cols(),
}
}
}
impl Consolidate for SuperTableV {
type Output = Table;
fn consolidate(self) -> Table {
if self.slices.is_empty() {
return Table::default();
}
SuperTable::from_views(&self.slices, "part".to_string()).consolidate()
}
}
impl Concatenate for SuperTableV {
fn concat(self, other: Self) -> Result<Self, MinarrowError> {
let self_table = self.consolidate();
let other_table = other.consolidate();
let concatenated = self_table.concat(other_table)?;
let len = concatenated.n_rows;
Ok(SuperTableV {
slices: vec![TableV::from(concatenated)],
len,
})
}
}
#[cfg(feature = "chunked")]
impl From<SuperTable> for SuperTableV {
fn from(super_table: SuperTable) -> Self {
if super_table.is_empty() {
return SuperTableV {
slices: Vec::new(),
len: 0,
};
}
super_table.view(0, super_table.n_rows())
}
}
#[cfg(feature = "views")]
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi::arrow_dtype::ArrowType;
use crate::{Array, Field, FieldArray, IntegerArray, NumericArray, Table};
fn fa_i32(name: &str, vals: &[i32]) -> FieldArray {
let arr = Array::from_int32(IntegerArray::<i32>::from_slice(vals));
let field = Field::new(name, ArrowType::Int32, false, None);
FieldArray::new(field, arr)
}
fn table(name: &str, vals: &[i32]) -> Table {
Table::build(vec![fa_i32(name, vals)], vals.len(), name.to_string())
}
fn col_vals(t: &Table) -> Vec<i32> {
if let Array::NumericArray(NumericArray::Int32(a)) = &t.cols[0].array {
a.data.as_slice().to_vec()
} else {
unreachable!()
}
}
#[test]
fn slice_basic_properties() {
let b1 = table("t", &[1, 2]);
let b2 = table("t", &[3, 4, 5]);
let big_slice = SuperTableV {
slices: vec![TableV::from_table(b1, 0, 2), TableV::from_table(b2, 0, 3)],
len: 5,
};
assert!(!big_slice.is_empty());
assert_eq!(big_slice.n_slices(), 2);
assert_eq!(big_slice.len, 5);
let tbl = big_slice.consolidate();
assert_eq!(tbl.n_rows, 5);
assert_eq!(col_vals(&tbl), vec![1, 2, 3, 4, 5]);
}
#[test]
fn subslice_and_row_access() {
let big = table("x", &[10, 11, 12, 13, 14]);
let full = SuperTableV {
slices: vec![TableV::from_table(big, 0, 5)],
len: 5,
};
let sub = full.slice(1, 3);
assert_eq!(sub.len, 3);
let row_ts = sub.row(1);
assert_eq!(row_ts.n_rows(), 1);
let row_tbl = row_ts.to_table();
assert_eq!(col_vals(&row_tbl)[0], 12);
let single = sub.row_slice(2);
assert_eq!(single.len, 1);
let single_tbl = single.to_table();
assert_eq!(col_vals(&single_tbl)[0], 13);
let sub_tbl = sub.consolidate();
assert_eq!(col_vals(&sub_tbl), vec![11, 12, 13]);
}
#[test]
fn iterate_slices_and_iters() {
let b1 = table("c", &[0, 1]);
let b2 = table("c", &[2]);
let slice = SuperTableV {
slices: vec![TableV::from_table(b1, 0, 2), TableV::from_table(b2, 0, 1)],
len: 3,
};
let pcs: Vec<_> = slice.chunks().collect();
assert_eq!(pcs.len(), 2);
assert_eq!(pcs[0].n_rows(), 2);
assert_eq!(pcs[1].n_rows(), 1);
let collected: Vec<_> = slice.iter().collect();
assert_eq!(collected.len(), 2);
assert_eq!(collected[0].offset, 0);
}
#[test]
#[should_panic(expected = "row index out of bounds")]
fn row_oob_panics() {
let t = table("q", &[1, 2]);
let slice = SuperTableV {
slices: vec![TableV::from_table(t, 0, 2)],
len: 2,
};
let _ = slice.row(5);
}
#[test]
#[should_panic(expected = "slice out of bounds")]
fn subslice_oob_panics() {
let t = table("p", &[1, 2, 3]);
let slice = SuperTableV {
slices: vec![TableV::from_table(t, 0, 3)],
len: 3,
};
let _ = slice.slice(2, 5);
}
}