mod conversions;
#[cfg(feature = "cube")]
use crate::Cube;
#[cfg(feature = "matrix")]
use crate::Matrix;
#[cfg(all(feature = "matrix", feature = "views"))]
use crate::MatrixV;
#[cfg(feature = "ndarray")]
use crate::NdArray;
#[cfg(all(feature = "ndarray", feature = "views"))]
use crate::NdArrayV;
#[cfg(feature = "scalar_type")]
use crate::Scalar;
#[cfg(all(feature = "ndarray", feature = "chunked"))]
use crate::SuperNdArray;
#[cfg(all(feature = "ndarray", feature = "chunked", feature = "views"))]
use crate::SuperNdArrayV;
#[cfg(feature = "xarray")]
use crate::XArray;
use crate::{Array, FieldArray, Table, traits::custom_value::CustomValue};
use std::sync::Arc;
#[cfg(feature = "chunked")]
use crate::{SuperArray, SuperTable};
#[cfg(feature = "views")]
use crate::{ArrayV, TableV};
#[cfg(all(feature = "chunked", feature = "views"))]
use crate::{SuperArrayV, SuperTableV};
mod impls;
#[derive(Debug, Clone)]
pub enum Value {
#[cfg(feature = "scalar_type")]
Scalar(Scalar),
Array(Arc<Array>),
#[cfg(feature = "views")]
ArrayView(Arc<ArrayV>),
FieldArray(Arc<FieldArray>),
Table(Arc<Table>),
#[cfg(feature = "views")]
TableView(Arc<TableV>),
#[cfg(feature = "chunked")]
SuperArray(Arc<SuperArray>),
#[cfg(all(feature = "chunked", feature = "views"))]
SuperArrayView(Arc<SuperArrayV>),
#[cfg(feature = "chunked")]
SuperTable(Arc<SuperTable>),
#[cfg(all(feature = "chunked", feature = "views"))]
SuperTableView(Arc<SuperTableV>),
#[cfg(feature = "matrix")]
Matrix(Arc<Matrix>),
#[cfg(all(feature = "matrix", feature = "views"))]
MatrixView(Arc<MatrixV>),
#[cfg(feature = "ndarray")]
NdArray(Arc<NdArray<f64>>),
#[cfg(all(feature = "ndarray", feature = "views"))]
NdArrayView(Arc<NdArrayV<f64>>),
#[cfg(all(feature = "ndarray", feature = "chunked"))]
SuperNdArray(Arc<SuperNdArray<f64>>),
#[cfg(all(feature = "ndarray", feature = "chunked", feature = "views"))]
SuperNdArrayView(Arc<SuperNdArrayV<f64>>),
#[cfg(feature = "xarray")]
XArray(Arc<XArray<f64>>),
#[cfg(feature = "cube")]
Cube(Arc<Cube>),
VecValue(Arc<Vec<Value>>),
BoxValue(Box<Value>),
ArcValue(Arc<Value>),
Tuple2(Arc<(Value, Value)>),
Tuple3(Arc<(Value, Value, Value)>),
Tuple4(Arc<(Value, Value, Value, Value)>),
Tuple5(Arc<(Value, Value, Value, Value, Value)>),
Tuple6(Arc<(Value, Value, Value, Value, Value, Value)>),
Custom(Arc<dyn CustomValue>),
}
impl Value {
#[inline]
pub fn len(&self) -> usize {
match self {
#[cfg(feature = "scalar_type")]
Value::Scalar(_) => 1,
Value::Table(t) => t.n_rows,
#[cfg(feature = "views")]
Value::TableView(tv) => tv.len,
Value::Array(a) => a.len(),
#[cfg(feature = "views")]
Value::ArrayView(av) => av.len(),
Value::FieldArray(fa) => fa.array.len(),
#[cfg(feature = "chunked")]
Value::SuperArray(sa) => sa.len(),
#[cfg(all(feature = "chunked", feature = "views"))]
Value::SuperArrayView(sav) => sav.len(),
#[cfg(feature = "chunked")]
Value::SuperTable(st) => st.len(),
#[cfg(all(feature = "chunked", feature = "views"))]
Value::SuperTableView(stv) => stv.len,
#[cfg(feature = "matrix")]
Value::Matrix(m) => m.n_rows,
#[cfg(all(feature = "matrix", feature = "views"))]
Value::MatrixView(mv) => mv.len,
#[cfg(feature = "ndarray")]
Value::NdArray(nd) => nd.shape()[0],
#[cfg(all(feature = "ndarray", feature = "views"))]
Value::NdArrayView(v) => v.shape()[0],
#[cfg(all(feature = "ndarray", feature = "chunked"))]
Value::SuperNdArray(snd) => snd.n_obs(),
#[cfg(all(feature = "ndarray", feature = "chunked", feature = "views"))]
Value::SuperNdArrayView(v) => v.n_obs(),
#[cfg(feature = "xarray")]
Value::XArray(xa) => xa.shape()[0],
#[cfg(feature = "cube")]
Value::Cube(c) => c.len(),
Value::VecValue(vv) => vv.iter().map(|x| x.len()).sum(),
Value::BoxValue(bv) => bv.len(),
Value::ArcValue(av) => av.len(),
Value::Tuple2(t2) => t2.0.len() + t2.1.len(),
Value::Tuple3(t3) => t3.0.len() + t3.1.len() + t3.2.len(),
Value::Tuple4(t4) => t4.0.len() + t4.1.len() + t4.2.len() + t4.3.len(),
Value::Tuple5(t5) => t5.0.len() + t5.1.len() + t5.2.len() + t5.3.len() + t5.4.len(),
Value::Tuple6(t6) => {
t6.0.len() + t6.1.len() + t6.2.len() + t6.3.len() + t6.4.len() + t6.5.len()
}
Value::Custom(_cv) => panic!("Length is not implemented for custom value type."),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[cfg(feature = "views")]
pub fn slice(&self, offset: usize, length: usize) -> Value {
match self {
#[cfg(feature = "scalar_type")]
Value::Scalar(s) => Value::Scalar(s.clone()),
Value::Table(t) => Value::TableView(Arc::new(t.slice(offset, length))),
Value::TableView(tv) => Value::TableView(Arc::new(tv.from_self(offset, length))),
Value::Array(a) => {
Value::ArrayView(Arc::new(ArrayV::new((**a).clone(), offset, length)))
}
Value::ArrayView(av) => Value::ArrayView(Arc::new(av.slice(offset, length))),
Value::FieldArray(fa) => {
Value::ArrayView(Arc::new(ArrayV::new(fa.array.clone(), offset, length)))
}
#[cfg(feature = "chunked")]
Value::SuperArray(sa) => Value::SuperArrayView(Arc::new(sa.slice(offset, length))),
#[cfg(all(feature = "chunked", feature = "views"))]
Value::SuperArrayView(sav) => {
Value::SuperArrayView(Arc::new(sav.slice(offset, length)))
}
#[cfg(feature = "chunked")]
Value::SuperTable(st) => Value::SuperTableView(Arc::new(st.view(offset, length))),
#[cfg(all(feature = "chunked", feature = "views"))]
Value::SuperTableView(stv) => {
Value::SuperTableView(Arc::new(stv.slice(offset, length)))
}
#[cfg(feature = "matrix")]
Value::Matrix(m) => Value::MatrixView(Arc::new(MatrixV::from_arc_matrix(
m.clone(),
offset,
length,
))),
#[cfg(all(feature = "matrix", feature = "views"))]
Value::MatrixView(mv) => {
Value::MatrixView(Arc::new(mv.from_self(offset, length)))
}
#[cfg(feature = "ndarray")]
Value::NdArray(nd) => {
assert!(
offset + length <= nd.shape()[0],
"Value::slice: window {}..{} out of bounds for axis 0 (size {})",
offset, offset + length, nd.shape()[0]
);
let mut window_shape = vec![length];
window_shape.extend_from_slice(&nd.shape()[1..]);
Value::NdArrayView(Arc::new(NdArrayV::new(
nd.as_ref().clone(),
offset * nd.strides()[0],
&window_shape,
nd.strides(),
)))
}
#[cfg(all(feature = "ndarray", feature = "views"))]
Value::NdArrayView(v) => {
assert!(
offset + length <= v.shape()[0],
"Value::slice: window {}..{} out of bounds for axis 0 (size {})",
offset, offset + length, v.shape()[0]
);
let mut window_shape = vec![length];
window_shape.extend_from_slice(&v.shape()[1..]);
Value::NdArrayView(Arc::new(NdArrayV::new(
v.source.clone(),
v.offset + offset * v.strides()[0],
&window_shape,
v.strides(),
)))
}
#[cfg(all(feature = "ndarray", feature = "chunked"))]
Value::SuperNdArray(snd) => {
Value::SuperNdArrayView(Arc::new(snd.slice(offset, length)))
}
#[cfg(all(feature = "ndarray", feature = "chunked", feature = "views"))]
Value::SuperNdArrayView(v) => {
Value::SuperNdArrayView(Arc::new(v.slice(offset, length)))
}
#[cfg(all(feature = "xarray", feature = "select"))]
Value::XArray(xa) => {
let range = offset..offset + length;
let dim0 = xa.dim_names()[0].to_string();
Value::XArray(Arc::new(xa.select(&[(dim0.as_str(), &range)])))
}
#[cfg(all(feature = "xarray", not(feature = "select")))]
Value::XArray(_) => unimplemented!("XArray slicing requires the select feature"),
#[cfg(feature = "cube")]
Value::Cube(_) => unimplemented!("Cube slicing"),
Value::VecValue(v) => {
let end = (offset + length).min(v.len());
Value::VecValue(Arc::new(v[offset..end].to_vec()))
}
Value::BoxValue(bv) => bv.slice(offset, length),
Value::ArcValue(av) => av.slice(offset, length),
Value::Tuple2(t) => Value::Tuple2(Arc::new((
t.0.slice(offset, length),
t.1.slice(offset, length),
))),
Value::Tuple3(t) => Value::Tuple3(Arc::new((
t.0.slice(offset, length),
t.1.slice(offset, length),
t.2.slice(offset, length),
))),
Value::Tuple4(t) => Value::Tuple4(Arc::new((
t.0.slice(offset, length),
t.1.slice(offset, length),
t.2.slice(offset, length),
t.3.slice(offset, length),
))),
Value::Tuple5(t) => Value::Tuple5(Arc::new((
t.0.slice(offset, length),
t.1.slice(offset, length),
t.2.slice(offset, length),
t.3.slice(offset, length),
t.4.slice(offset, length),
))),
Value::Tuple6(t) => Value::Tuple6(Arc::new((
t.0.slice(offset, length),
t.1.slice(offset, length),
t.2.slice(offset, length),
t.3.slice(offset, length),
t.4.slice(offset, length),
t.5.slice(offset, length),
))),
Value::Custom(_) => panic!("Slicing is not implemented for custom value types."),
}
}
pub fn arity(&self) -> usize {
match self {
Value::Tuple2(_) => 2,
Value::Tuple3(_) => 3,
Value::Tuple4(_) => 4,
Value::Tuple5(_) => 5,
Value::Tuple6(_) => 6,
_ => 1,
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::{
Array, ArrayV, ArrowType, Field, FieldArray, IntegerArray, MaskedArray, NumericArray, Table,
};
fn seq_array(n: usize) -> Array {
let mut arr = IntegerArray::<i64>::default();
for i in 0..n {
arr.push(i as i64);
}
Array::NumericArray(NumericArray::Int64(Arc::new(arr)))
}
#[cfg(feature = "views")]
#[test]
fn len_of_an_array_view_counts_the_window() {
let view = ArrayV::new(seq_array(100), 30, 10);
let value = Value::ArrayView(Arc::new(view));
assert_eq!(value.len(), 10, "the window, not the backing array");
}
#[cfg(feature = "views")]
#[test]
fn len_agrees_across_the_view_variants() {
let array = Value::ArrayView(Arc::new(ArrayV::new(seq_array(100), 30, 10)));
let mut table = Table::new("t".to_string(), None);
table.add_col(FieldArray::new(
Field::new("v", ArrowType::Int64, false, None),
seq_array(100),
));
let table = Value::Table(Arc::new(table)).slice(30, 10);
assert_eq!(array.len(), table.len());
}
#[cfg(feature = "views")]
#[test]
fn len_bounds_a_slice_of_the_same_value() {
let value = Value::ArrayView(Arc::new(ArrayV::new(seq_array(100), 30, 10)));
let whole = value.slice(0, value.len());
assert_eq!(whole.len(), 10);
}
#[cfg(all(feature = "matrix", feature = "views"))]
mod matrix_view_variant {
use std::sync::Arc;
use super::Value;
use crate::enums::shape_dim::ShapeDim;
use crate::traits::shape::Shape;
use crate::{Matrix, MatrixV, mat};
fn sample() -> Matrix {
mat![
[0.0, 1.0, 2.0, 3.0, 4.0],
[10.0, 11.0, 12.0, 13.0, 14.0]
]
}
#[test]
fn len_counts_rows_on_both_matrix_arms() {
let owned = Value::Matrix(Arc::new(sample()));
assert_eq!(owned.len(), 5);
let windowed = owned.slice(1, 3);
assert_eq!(windowed.len(), 3);
}
#[test]
fn slicing_a_matrix_shares_the_backing_allocation() {
let backing = Arc::new(sample());
let value = Value::Matrix(backing.clone());
let Value::MatrixView(view) = value.slice(1, 3) else {
panic!("Expected Value::MatrixView");
};
assert!(
Arc::ptr_eq(&backing, &view.matrix),
"slicing a Matrix must not copy its buffer"
);
assert_eq!(view.offset, 1);
assert_eq!(view.col(0), &[1.0, 2.0, 3.0]);
}
#[test]
fn slicing_a_matrix_view_rewindows_against_the_same_backing() {
let backing = Arc::new(sample());
let value = Value::Matrix(backing.clone()).slice(1, 4);
let Value::MatrixView(view) = value.slice(1, 2) else {
panic!("Expected Value::MatrixView");
};
assert!(Arc::ptr_eq(&backing, &view.matrix));
assert_eq!(view.offset, 2);
assert_eq!(view.col(1), &[12.0, 13.0]);
}
#[test]
fn shape_and_equality_read_the_window() {
let value = Value::Matrix(Arc::new(sample())).slice(1, 3);
assert_eq!(value.shape(), ShapeDim::Rank2 { rows: 3, cols: 2 });
let same = Value::Matrix(Arc::new(sample())).slice(1, 3);
assert_eq!(value, same);
assert_ne!(value, Value::Matrix(Arc::new(sample())).slice(0, 3));
}
#[test]
fn round_trips_through_from_and_try_from() {
let view = MatrixV::from_matrix(sample(), 1, 3);
let value = Value::from(view.clone());
assert_eq!(MatrixV::try_from(value).unwrap(), view);
let whole = MatrixV::try_from(Value::Matrix(Arc::new(sample()))).unwrap();
assert_eq!(whole.n_rows(), 5);
assert!(whole.spans_backing());
}
#[test]
fn try_from_reports_a_type_mismatch() {
let tuple = Value::Tuple2(Arc::new((
Value::Matrix(Arc::new(sample())),
Value::Matrix(Arc::new(sample())),
)));
assert!(MatrixV::try_from(tuple).is_err());
let view = Value::Matrix(Arc::new(sample())).slice(1, 3);
assert!(view.try_matv().is_ok());
assert!(Value::Matrix(Arc::new(sample())).try_matv().is_err());
}
#[test]
fn converting_to_a_matrix_materialises_the_window() {
let value = Value::Matrix(Arc::new(sample())).slice(1, 3);
let materialised = Matrix::try_from(value).unwrap();
assert_eq!(materialised.n_rows, 3);
assert_eq!(materialised.col(0), &[1.0, 2.0, 3.0]);
assert_eq!(materialised.col(1), &[11.0, 12.0, 13.0]);
}
#[test]
fn converting_to_a_table_carries_the_windowed_rows() {
use crate::Table;
let value = Value::Matrix(Arc::new(sample())).slice(2, 2);
let table = Table::try_from(value).unwrap();
assert_eq!(table.n_rows, 2);
assert_eq!(table.n_cols(), 2);
}
}
#[cfg(feature = "decimal")]
mod decimal_value_tests {
use super::*;
fn decimal_array(n: usize) -> Array {
let vals: Vec<i32> = (0..n as i32).map(|i| i * 100).collect();
Array::from_decimal32(crate::DecimalArray::from_slice(&vals, 10, 2))
}
#[test]
fn decimal_len_counts_correctly() {
let value = Value::Array(Arc::new(decimal_array(5)));
assert_eq!(value.len(), 5);
}
#[cfg(feature = "views")]
#[test]
fn decimal_slice_returns_array_view() {
let value = Value::Array(Arc::new(decimal_array(10)));
let sliced = value.slice(3, 4);
if let Value::ArrayView(av) = &sliced {
assert_eq!(av.len(), 4);
assert_eq!(av.offset, 3);
} else {
panic!("Expected Value::ArrayView");
}
assert_eq!(sliced.len(), 4);
}
#[cfg(feature = "views")]
#[test]
fn decimal_view_re_slices() {
let value = Value::Array(Arc::new(decimal_array(10)));
let view = value.slice(2, 6);
let sub = view.slice(1, 3);
assert_eq!(sub.len(), 3);
}
#[test]
fn decimal_round_trips_through_value() {
let arr = decimal_array(3);
let value = Value::Array(Arc::new(arr.clone()));
let extracted = Array::try_from(value).unwrap();
assert_eq!(extracted, arr);
}
}
}