#[cfg(feature = "cube")]
use crate::Cube;
#[cfg(feature = "chunked")]
use crate::SuperArrayV;
use crate::enums::error::MinarrowError;
use crate::enums::operators::ArithmeticOperator;
use crate::kernels::broadcast::broadcast_value;
use crate::kernels::routing::arithmetic::resolve_binary_arithmetic;
use crate::structs::field_array::create_field_for_array;
use crate::{
Array, BooleanArray, CategoricalArray, DatetimeArray, FieldArray, FloatArray, IntegerArray,
Scalar, StringArray, Table, TableV, TextArray, Value,
};
#[cfg(feature = "views")]
use crate::{NumericArrayV, TemporalArrayV, TextArrayV};
#[cfg(feature = "chunked")]
use crate::{SuperArray, SuperTable, SuperTableV};
use std::sync::Arc;
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_table(
op: ArithmeticOperator,
scalar: &Scalar,
table: &Table,
) -> Result<Table, MinarrowError> {
let new_cols: Result<Vec<_>, _> = table
.cols
.iter()
.map(|field_array| {
let col_array = &field_array.array;
let result_array = match (
Value::Scalar(scalar.clone()),
Value::Array(Arc::new(col_array.clone())),
) {
(a, b) => broadcast_value(op, a, b)?,
};
match result_array {
Value::Array(result_array) => {
let result_array = Arc::unwrap_or_clone(result_array);
let new_field = create_field_for_array(
&field_array.field.name,
&result_array,
Some(&col_array),
Some(field_array.field.metadata.clone()),
);
Ok(FieldArray::new(new_field, result_array))
}
_ => Err(MinarrowError::TypeError {
from: "scalar-table broadcasting",
to: "Array result",
message: Some("Expected Array result from broadcasting".to_string()),
}),
}
})
.collect();
let table_out = Table::new(table.name.clone(), Some(new_cols?));
#[cfg(feature = "table_metadata")]
{
let mut t = table_out;
t.metadata = table.metadata.clone();
return Ok(t);
}
#[cfg(not(feature = "table_metadata"))]
Ok(table_out)
}
#[cfg(all(feature = "scalar_type", feature = "views"))]
pub fn broadcast_scalar_to_tableview(
op: ArithmeticOperator,
scalar: &Scalar,
table_view: &TableV,
) -> Result<Table, MinarrowError> {
let new_cols: Result<Vec<_>, _> = table_view
.cols
.iter()
.map(|col_view| {
let scalar_value = Value::Scalar(scalar.clone());
let result = broadcast_value(
op,
scalar_value,
Value::ArrayView(Arc::new(col_view.clone())),
)?;
match result {
Value::Array(arr) => Ok(Arc::unwrap_or_clone(arr)),
_ => Err(MinarrowError::TypeError {
from: "scalar-tableview broadcasting",
to: "Array result",
message: Some("Expected Array result from broadcasting".to_string()),
}),
}
})
.collect();
let field_arrays: Vec<FieldArray> = table_view
.fields
.iter()
.zip(new_cols?)
.map(|(field, array)| FieldArray::new_arc(field.clone(), array))
.collect();
Ok(Table::new(table_view.name.clone(), Some(field_arrays)))
}
#[cfg(all(feature = "scalar_type", feature = "chunked", feature = "views"))]
pub fn broadcast_scalar_to_supertableview(
op: ArithmeticOperator,
scalar: &Scalar,
super_table_view: &SuperTableV,
) -> Result<SuperTableV, MinarrowError> {
let result_slices: Result<Vec<_>, _> = super_table_view
.slices
.iter()
.map(|table_slice| {
let result = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Value::TableView(Arc::new(table_slice.clone())),
)?;
match result {
Value::Table(table) => {
let table = Arc::unwrap_or_clone(table);
let n_rows = table.n_rows;
Ok(TableV::from_table(table, 0, n_rows))
}
_ => Err(MinarrowError::TypeError {
from: "scalar-supertableview broadcasting",
to: "TableView result",
message: Some("Expected Table result from broadcasting".to_string()),
}),
}
})
.collect();
Ok(SuperTableV {
slices: result_slices?,
len: super_table_view.len,
})
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_array(
op: ArithmeticOperator,
scalar: &Scalar,
array: &Array,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
Scalar::String32(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
#[cfg(feature = "large_string")]
Scalar::String64(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
Scalar::Boolean(val) => Array::from_bool(BooleanArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int8(val) => Array::from_int8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int16(val) => Array::from_int16(IntegerArray::from_slice(&[*val])),
Scalar::UInt32(val) => Array::from_uint32(IntegerArray::from_slice(&[*val])),
Scalar::UInt64(val) => Array::from_uint64(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt8(val) => Array::from_uint8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt16(val) => Array::from_uint16(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "datetime")]
Scalar::Datetime32(val) => {
Array::from_datetime_i32(DatetimeArray::from_slice(&[*val], None))
}
#[cfg(feature = "datetime")]
Scalar::Datetime64(val) => {
Array::from_datetime_i64(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Null => Array::Null,
#[cfg(feature = "datetime")]
Scalar::Interval => {
return Err(MinarrowError::NotImplemented {
feature: "Interval scalar broadcasting not yet supported".to_string(),
});
}
};
resolve_binary_arithmetic(op, scalar_array, array.clone(), None)
}
#[cfg(all(feature = "scalar_type", feature = "chunked"))]
pub fn broadcast_scalar_to_superarray(
op: ArithmeticOperator,
scalar: &Scalar,
super_array: &SuperArray,
) -> Result<SuperArray, MinarrowError> {
let result_chunks: Result<Vec<_>, _> = super_array
.chunks()
.iter()
.map(|chunk| {
let chunk_result = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Value::Array(Arc::new(chunk.clone())),
)?;
match chunk_result {
Value::Array(arr) => Ok(FieldArray::new(
super_array.field_ref().clone(),
Arc::unwrap_or_clone(arr),
)),
_ => Err(MinarrowError::TypeError {
from: "Scalar + Array chunk",
to: "Array",
message: Some("Expected Array result from chunk operation".to_string()),
}),
}
})
.collect();
Ok(SuperArray::from_chunks(result_chunks?))
}
#[cfg(all(feature = "scalar_type", feature = "chunked", feature = "views"))]
pub fn broadcast_scalar_to_superarrayview(
op: ArithmeticOperator,
scalar: &Scalar,
super_array_view: &SuperArrayV,
) -> Result<SuperArray, MinarrowError> {
let result_chunks: Result<Vec<_>, _> = super_array_view
.slices
.iter()
.map(|slice| {
let chunk_result = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Value::ArrayView(Arc::new(slice.clone())),
)?;
match chunk_result {
Value::Array(arr) => Ok(FieldArray::new(
(*super_array_view.field).clone(),
Arc::unwrap_or_clone(arr),
)),
_ => Err(MinarrowError::TypeError {
from: "Scalar + ArrayView chunk",
to: "Array",
message: Some("Expected Array result from chunk operation".to_string()),
}),
}
})
.collect();
Ok(SuperArray::from_chunks(result_chunks?))
}
#[cfg(all(feature = "scalar_type", feature = "chunked"))]
pub fn broadcast_scalar_to_supertable(
op: ArithmeticOperator,
scalar: &Scalar,
super_table: &SuperTable,
) -> Result<SuperTable, MinarrowError> {
let new_tables: Result<Vec<_>, _> = super_table
.batches
.iter()
.map(|table| broadcast_scalar_to_table(op, scalar, table).map(Arc::new))
.collect();
Ok(SuperTable::from_batches(
new_tables?,
Some(super_table.name.clone()),
))
}
#[cfg(all(feature = "scalar_type", feature = "cube"))]
pub fn broadcast_scalar_to_cube(
op: ArithmeticOperator,
scalar: &Scalar,
cube: &Cube,
) -> Result<Cube, MinarrowError> {
let mut result_tables = Vec::with_capacity(cube.tables.len());
for table in &cube.tables {
let broadcasted = broadcast_scalar_to_table(op, scalar, table)?;
result_tables.push(broadcasted);
}
Ok(Cube::from_tables(result_tables, cube.name.clone(), cube.third_dim_index.clone()))
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_tuple2(
op: ArithmeticOperator,
scalar: &Scalar,
tuple: (Arc<Value>, Arc<Value>),
) -> Result<(Arc<Value>, Arc<Value>), MinarrowError> {
let res1 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.0),
)?;
let res2 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.1),
)?;
Ok((Arc::new(res1), Arc::new(res2)))
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_tuple3(
op: ArithmeticOperator,
scalar: &Scalar,
tuple: (Arc<Value>, Arc<Value>, Arc<Value>),
) -> Result<(Arc<Value>, Arc<Value>, Arc<Value>), MinarrowError> {
let res1 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.0),
)?;
let res2 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.1),
)?;
let res3 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.2),
)?;
Ok((Arc::new(res1), Arc::new(res2), Arc::new(res3)))
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_tuple4(
op: ArithmeticOperator,
scalar: &Scalar,
tuple: (Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>),
) -> Result<(Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>), MinarrowError> {
let res1 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.0),
)?;
let res2 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.1),
)?;
let res3 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.2),
)?;
let res4 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.3),
)?;
Ok((
Arc::new(res1),
Arc::new(res2),
Arc::new(res3),
Arc::new(res4),
))
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_tuple5(
op: ArithmeticOperator,
scalar: &Scalar,
tuple: (Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>),
) -> Result<(Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>, Arc<Value>), MinarrowError> {
let res1 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.0),
)?;
let res2 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.1),
)?;
let res3 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.2),
)?;
let res4 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.3),
)?;
let res5 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.4),
)?;
Ok((
Arc::new(res1),
Arc::new(res2),
Arc::new(res3),
Arc::new(res4),
Arc::new(res5),
))
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_tuple6(
op: ArithmeticOperator,
scalar: &Scalar,
tuple: (
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
),
) -> Result<
(
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
Arc<Value>,
),
MinarrowError,
> {
let res1 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.0),
)?;
let res2 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.1),
)?;
let res3 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.2),
)?;
let res4 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.3),
)?;
let res5 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.4),
)?;
let res6 = broadcast_value(
op,
Value::Scalar(scalar.clone()),
Arc::unwrap_or_clone(tuple.5),
)?;
Ok((
Arc::new(res1),
Arc::new(res2),
Arc::new(res3),
Arc::new(res4),
Arc::new(res5),
Arc::new(res6),
))
}
#[cfg(all(feature = "scalar_type", feature = "views"))]
pub fn broadcast_scalar_to_numeric_arrayview(
op: ArithmeticOperator,
scalar: &Scalar,
numeric_view: &NumericArrayV,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
_ => {
return Err(MinarrowError::NotImplemented {
feature: "Non-numeric scalar with NumericArrayView".to_string(),
});
}
};
resolve_binary_arithmetic(op, scalar_array, numeric_view.clone(), None)
}
#[cfg(all(feature = "scalar_type", feature = "views"))]
pub fn broadcast_numeric_arrayview_to_scalar(
op: ArithmeticOperator,
numeric_view: &NumericArrayV,
scalar: &Scalar,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
_ => {
return Err(MinarrowError::NotImplemented {
feature: "Non-numeric scalar with NumericArrayView".to_string(),
});
}
};
resolve_binary_arithmetic(op, numeric_view.clone(), scalar_array, None)
}
#[cfg(all(feature = "scalar_type", feature = "views"))]
pub fn broadcast_scalar_to_text_arrayview(
op: ArithmeticOperator,
scalar: &Scalar,
text_view: &TextArrayV,
) -> Result<Array, MinarrowError> {
let scalar_array = match (scalar, &text_view.array) {
(Scalar::String32(val), TextArray::String32(_)) => {
Array::from_string32(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(Scalar::String64(val), TextArray::String32(_)) => {
Array::from_string32(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(Scalar::String32(val), TextArray::String64(_)) => {
Array::from_string64(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(Scalar::String64(val), TextArray::String64(_)) => {
Array::from_string64(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "default_categorical_8")]
(Scalar::String32(val), TextArray::Categorical8(_)) => {
Array::from_categorical8(CategoricalArray::<u8>::from_values(vec![val.as_str()]))
}
#[cfg(feature = "extended_categorical")]
(Scalar::String32(val), TextArray::Categorical16(_)) => {
Array::from_categorical16(CategoricalArray::<u16>::from_values(vec![val.as_str()]))
}
#[cfg(any(not(feature = "default_categorical_8"), feature = "extended_categorical"))]
(Scalar::String32(val), TextArray::Categorical32(_)) => {
Array::from_categorical32(CategoricalArray::<u32>::from_values(vec![val.as_str()]))
}
#[cfg(feature = "extended_categorical")]
(Scalar::String32(val), TextArray::Categorical64(_)) => {
Array::from_categorical64(CategoricalArray::<u64>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "default_categorical_8"))]
(Scalar::String64(val), TextArray::Categorical8(_)) => {
Array::from_categorical8(CategoricalArray::<u8>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "extended_categorical"))]
(Scalar::String64(val), TextArray::Categorical16(_)) => {
Array::from_categorical16(CategoricalArray::<u16>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", any(not(feature = "default_categorical_8"), feature = "extended_categorical")))]
(Scalar::String64(val), TextArray::Categorical32(_)) => {
Array::from_categorical32(CategoricalArray::<u32>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "extended_categorical"))]
(Scalar::String64(val), TextArray::Categorical64(_)) => {
Array::from_categorical64(CategoricalArray::<u64>::from_values(vec![val.as_str()]))
}
(Scalar::Null, _) | (Scalar::Boolean(_), _) => {
return Err(MinarrowError::NotImplemented {
feature: "Non-string scalar with TextArrayView".to_string(),
});
}
#[cfg(feature = "extended_numeric_types")]
(Scalar::Int8(_), _)
| (Scalar::Int16(_), _)
| (Scalar::UInt8(_), _)
| (Scalar::UInt16(_), _) => {
return Err(MinarrowError::NotImplemented {
feature: "Numeric scalar with TextArrayView".to_string(),
});
}
(Scalar::Int32(_), _)
| (Scalar::Int64(_), _)
| (Scalar::UInt32(_), _)
| (Scalar::UInt64(_), _) => {
return Err(MinarrowError::NotImplemented {
feature: "Numeric scalar with TextArrayView".to_string(),
});
}
(Scalar::Float32(_), _) | (Scalar::Float64(_), _) => {
return Err(MinarrowError::NotImplemented {
feature: "Float scalar with TextArrayView".to_string(),
});
}
#[cfg(feature = "datetime")]
(Scalar::Datetime32(_), _) | (Scalar::Datetime64(_), _) | (Scalar::Interval, _) => {
return Err(MinarrowError::NotImplemented {
feature: "Datetime scalar with TextArrayView".to_string(),
});
}
(Scalar::String32(_), TextArray::Null) => {
return Err(MinarrowError::NullError { message: None });
}
#[cfg(feature = "large_string")]
(Scalar::String64(_), TextArray::Null) => {
return Err(MinarrowError::NullError { message: None });
}
};
resolve_binary_arithmetic(op, scalar_array, text_view.clone(), None)
}
#[cfg(all(feature = "scalar_type", feature = "views"))]
pub fn broadcast_text_arrayview_to_scalar(
op: ArithmeticOperator,
text_view: &TextArrayV,
scalar: &Scalar,
) -> Result<Array, MinarrowError> {
let scalar_array = match (&text_view.array, scalar) {
(TextArray::String32(_), Scalar::String32(val)) => {
Array::from_string32(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(TextArray::String32(_), Scalar::String64(val)) => {
Array::from_string32(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(TextArray::String64(_), Scalar::String32(val)) => {
Array::from_string64(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "large_string")]
(TextArray::String64(_), Scalar::String64(val)) => {
Array::from_string64(StringArray::from_slice(&[val.as_str()]))
}
#[cfg(feature = "default_categorical_8")]
(TextArray::Categorical8(_), Scalar::String32(val)) => {
Array::from_categorical8(CategoricalArray::<u8>::from_values(vec![val.as_str()]))
}
#[cfg(feature = "extended_categorical")]
(TextArray::Categorical16(_), Scalar::String32(val)) => {
Array::from_categorical16(CategoricalArray::<u16>::from_values(vec![val.as_str()]))
}
#[cfg(any(not(feature = "default_categorical_8"), feature = "extended_categorical"))]
(TextArray::Categorical32(_), Scalar::String32(val)) => {
Array::from_categorical32(CategoricalArray::<u32>::from_values(vec![val.as_str()]))
}
#[cfg(feature = "extended_categorical")]
(TextArray::Categorical64(_), Scalar::String32(val)) => {
Array::from_categorical64(CategoricalArray::<u64>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "default_categorical_8"))]
(TextArray::Categorical8(_), Scalar::String64(val)) => {
Array::from_categorical8(CategoricalArray::<u8>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "extended_categorical"))]
(TextArray::Categorical16(_), Scalar::String64(val)) => {
Array::from_categorical16(CategoricalArray::<u16>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", any(not(feature = "default_categorical_8"), feature = "extended_categorical")))]
(TextArray::Categorical32(_), Scalar::String64(val)) => {
Array::from_categorical32(CategoricalArray::<u32>::from_values(vec![val.as_str()]))
}
#[cfg(all(feature = "large_string", feature = "extended_categorical"))]
(TextArray::Categorical64(_), Scalar::String64(val)) => {
Array::from_categorical64(CategoricalArray::<u64>::from_values(vec![val.as_str()]))
}
(_, Scalar::Null) | (_, Scalar::Boolean(_)) => {
return Err(MinarrowError::NotImplemented {
feature: "Non-string scalar with TextArrayView".to_string(),
});
}
#[cfg(feature = "extended_numeric_types")]
(_, Scalar::Int8(_))
| (_, Scalar::Int16(_))
| (_, Scalar::UInt8(_))
| (_, Scalar::UInt16(_)) => {
return Err(MinarrowError::NotImplemented {
feature: "Numeric scalar with TextArrayView".to_string(),
});
}
(_, Scalar::Int32(_))
| (_, Scalar::Int64(_))
| (_, Scalar::UInt32(_))
| (_, Scalar::UInt64(_)) => {
return Err(MinarrowError::NotImplemented {
feature: "Numeric scalar with TextArrayView".to_string(),
});
}
(_, Scalar::Float32(_)) | (_, Scalar::Float64(_)) => {
return Err(MinarrowError::NotImplemented {
feature: "Float scalar with TextArrayView".to_string(),
});
}
#[cfg(feature = "datetime")]
(_, Scalar::Datetime32(_)) | (_, Scalar::Datetime64(_)) | (_, Scalar::Interval) => {
return Err(MinarrowError::NotImplemented {
feature: "Datetime scalar with TextArrayView".to_string(),
});
}
(TextArray::Null, Scalar::String32(_)) => {
return Err(MinarrowError::NullError { message: None });
}
#[cfg(feature = "large_string")]
(TextArray::Null, Scalar::String64(_)) => {
return Err(MinarrowError::NullError { message: None });
}
};
resolve_binary_arithmetic(op, text_view.clone(), scalar_array, None)
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_scalar_to_fieldarray(
op: ArithmeticOperator,
scalar: &Scalar,
field_array: &FieldArray,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
Scalar::String32(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
#[cfg(feature = "large_string")]
Scalar::String64(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
Scalar::Boolean(val) => Array::from_bool(BooleanArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int8(val) => Array::from_int8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int16(val) => Array::from_int16(IntegerArray::from_slice(&[*val])),
Scalar::UInt32(val) => Array::from_uint32(IntegerArray::from_slice(&[*val])),
Scalar::UInt64(val) => Array::from_uint64(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt8(val) => Array::from_uint8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt16(val) => Array::from_uint16(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "datetime")]
Scalar::Datetime32(val) => {
Array::from_datetime_i32(DatetimeArray::from_slice(&[*val], None))
}
#[cfg(feature = "datetime")]
Scalar::Datetime64(val) => {
Array::from_datetime_i64(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Null => Array::Null,
#[cfg(feature = "datetime")]
Scalar::Interval => {
return Err(MinarrowError::NotImplemented {
feature: "Interval scalar broadcasting not yet supported".to_string(),
});
}
};
resolve_binary_arithmetic(op, scalar_array, field_array.clone(), None)
}
#[cfg(feature = "scalar_type")]
pub fn broadcast_fieldarray_to_scalar(
op: ArithmeticOperator,
field_array: &FieldArray,
scalar: &Scalar,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
Scalar::String32(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
#[cfg(feature = "large_string")]
Scalar::String64(val) => Array::from_string32(StringArray::from_slice(&[val.as_str()])),
Scalar::Boolean(val) => Array::from_bool(BooleanArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int8(val) => Array::from_int8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int16(val) => Array::from_int16(IntegerArray::from_slice(&[*val])),
Scalar::UInt32(val) => Array::from_uint32(IntegerArray::from_slice(&[*val])),
Scalar::UInt64(val) => Array::from_uint64(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt8(val) => Array::from_uint8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt16(val) => Array::from_uint16(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "datetime")]
Scalar::Datetime32(val) => {
Array::from_datetime_i32(DatetimeArray::from_slice(&[*val], None))
}
#[cfg(feature = "datetime")]
Scalar::Datetime64(val) => {
Array::from_datetime_i64(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Null => Array::Null,
#[cfg(feature = "datetime")]
Scalar::Interval => {
return Err(MinarrowError::NotImplemented {
feature: "Interval scalar broadcasting not yet supported".to_string(),
});
}
};
resolve_binary_arithmetic(op, field_array.clone(), scalar_array, None)
}
#[cfg(all(feature = "scalar_type", feature = "datetime"))]
pub fn broadcast_scalar_to_temporal_arrayview(
op: ArithmeticOperator,
scalar: &Scalar,
temporal_view: &TemporalArrayV,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
#[cfg(feature = "extended_numeric_types")]
Scalar::Int8(val) => Array::from_int8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int16(val) => Array::from_int16(IntegerArray::from_slice(&[*val])),
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt8(val) => Array::from_uint8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt16(val) => Array::from_uint16(IntegerArray::from_slice(&[*val])),
Scalar::UInt32(val) => Array::from_uint32(IntegerArray::from_slice(&[*val])),
Scalar::UInt64(val) => Array::from_uint64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
Scalar::Datetime32(val) => {
Array::from_datetime_i32(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Datetime64(val) => {
Array::from_datetime_i64(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Interval => {
return Err(MinarrowError::NotImplemented {
feature: "Interval scalar broadcasting not yet supported".to_string(),
});
}
Scalar::Boolean(val) => Array::from_bool(BooleanArray::from_slice(&[*val])),
Scalar::String32(_) => {
return Err(MinarrowError::NotImplemented {
feature: "String scalar with TemporalArrayView".to_string(),
});
}
#[cfg(feature = "large_string")]
Scalar::String64(_) => {
return Err(MinarrowError::NotImplemented {
feature: "String scalar with TemporalArrayView".to_string(),
});
}
Scalar::Null => {
return Err(MinarrowError::NullError { message: None });
}
};
resolve_binary_arithmetic(op, scalar_array, temporal_view.clone(), None)
}
#[cfg(all(feature = "scalar_type", feature = "datetime"))]
pub fn broadcast_temporal_arrayview_to_scalar(
op: ArithmeticOperator,
temporal_view: &TemporalArrayV,
scalar: &Scalar,
) -> Result<Array, MinarrowError> {
let scalar_array = match scalar {
#[cfg(feature = "extended_numeric_types")]
Scalar::Int8(val) => Array::from_int8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::Int16(val) => Array::from_int16(IntegerArray::from_slice(&[*val])),
Scalar::Int32(val) => Array::from_int32(IntegerArray::from_slice(&[*val])),
Scalar::Int64(val) => Array::from_int64(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt8(val) => Array::from_uint8(IntegerArray::from_slice(&[*val])),
#[cfg(feature = "extended_numeric_types")]
Scalar::UInt16(val) => Array::from_uint16(IntegerArray::from_slice(&[*val])),
Scalar::UInt32(val) => Array::from_uint32(IntegerArray::from_slice(&[*val])),
Scalar::UInt64(val) => Array::from_uint64(IntegerArray::from_slice(&[*val])),
Scalar::Float32(val) => Array::from_float32(FloatArray::from_slice(&[*val])),
Scalar::Float64(val) => Array::from_float64(FloatArray::from_slice(&[*val])),
Scalar::Datetime32(val) => {
Array::from_datetime_i32(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Datetime64(val) => {
Array::from_datetime_i64(DatetimeArray::from_slice(&[*val], None))
}
Scalar::Interval => {
return Err(MinarrowError::NotImplemented {
feature: "Interval scalar broadcasting not yet supported".to_string(),
});
}
Scalar::Boolean(val) => Array::from_bool(BooleanArray::from_slice(&[*val])),
Scalar::String32(_) => {
return Err(MinarrowError::NotImplemented {
feature: "String scalar with TemporalArrayView".to_string(),
});
}
#[cfg(feature = "large_string")]
Scalar::String64(_) => {
return Err(MinarrowError::NotImplemented {
feature: "String scalar with TemporalArrayView".to_string(),
});
}
Scalar::Null => {
return Err(MinarrowError::NullError { message: None });
}
};
resolve_binary_arithmetic(op, temporal_view.clone(), scalar_array, None)
}
#[cfg(all(test, feature = "scalar_type"))]
mod tests {
use super::*;
use crate::ffi::arrow_dtype::ArrowType;
use crate::{Array, Field, FieldArray, IntegerArray, NumericArray, vec64};
#[test]
fn test_scalar_to_table_add() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3]));
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30]));
let table = Table::build(
vec![
FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr1,
),
FieldArray::new(
Field::new("col2".to_string(), ArrowType::Int32, false, None),
arr2,
),
],
3,
"test".to_string(),
);
let scalar = Scalar::Int32(5);
let result = broadcast_scalar_to_table(ArithmeticOperator::Add, &scalar, &table).unwrap();
assert_eq!(result.n_rows, 3);
assert_eq!(result.n_cols(), 2);
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[0].array {
assert_eq!(arr.data.as_slice(), &[6, 7, 8]);
} else {
panic!("Expected Int32 array in col1");
}
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[1].array {
assert_eq!(arr.data.as_slice(), &[15, 25, 35]);
} else {
panic!("Expected Int32 array in col2");
}
}
#[test]
fn test_scalar_to_table_multiply() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![2, 3, 4]));
let table = Table::build(
vec![FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr1,
)],
3,
"test".to_string(),
);
let scalar = Scalar::Int32(10);
let result =
broadcast_scalar_to_table(ArithmeticOperator::Multiply, &scalar, &table).unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[0].array {
assert_eq!(arr.data.as_slice(), &[20, 30, 40]);
} else {
panic!("Expected Int32 array");
}
}
#[cfg(feature = "views")]
#[test]
fn test_scalar_to_tableview_subtract() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![100, 200, 300]));
let table = Table::build(
vec![FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr1,
)],
3,
"test".to_string(),
);
let table_view = TableV::from_table(table, 0, 3);
let scalar = Scalar::Int32(50);
let result =
broadcast_scalar_to_tableview(ArithmeticOperator::Subtract, &scalar, &table_view)
.unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[0].array {
assert_eq!(arr.data.as_slice(), &[-50, -150, -250]);
} else {
panic!("Expected Int32 array");
}
}
#[cfg(feature = "views")]
#[test]
fn test_scalar_to_tableview_divide() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30]));
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![100, 200, 300]));
let table = Table::build(
vec![
FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr1,
),
FieldArray::new(
Field::new("col2".to_string(), ArrowType::Int32, false, None),
arr2,
),
],
3,
"test".to_string(),
);
let table_view = TableV::from_table(table, 0, 3);
let scalar = Scalar::Int32(1000);
let result =
broadcast_scalar_to_tableview(ArithmeticOperator::Divide, &scalar, &table_view)
.unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[0].array {
assert_eq!(arr.data.as_slice(), &[100, 50, 33]);
} else {
panic!("Expected Int32 array");
}
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.cols[1].array {
assert_eq!(arr.data.as_slice(), &[10, 5, 3]);
} else {
panic!("Expected Int32 array");
}
}
#[test]
fn test_scalar_to_array_add() {
let array = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30]));
let scalar = Scalar::Int32(5);
let result = broadcast_scalar_to_array(ArithmeticOperator::Add, &scalar, &array).unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = result {
assert_eq!(arr.data.as_slice(), &[15, 25, 35]);
} else {
panic!("Expected Int32 array");
}
}
#[test]
fn test_scalar_to_array_multiply() {
let array = Array::from_int32(IntegerArray::from_slice(&vec64![2, 3, 4]));
let scalar = Scalar::Int32(10);
let result =
broadcast_scalar_to_array(ArithmeticOperator::Multiply, &scalar, &array).unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = result {
assert_eq!(arr.data.as_slice(), &[20, 30, 40]);
} else {
panic!("Expected Int32 array");
}
}
#[cfg(feature = "chunked")]
#[test]
fn test_scalar_to_superarray() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3]));
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![4, 5, 6]));
let field = Field::new("col".to_string(), ArrowType::Int32, false, None);
let chunks = vec![
FieldArray::new(field.clone(), arr1),
FieldArray::new(field.clone(), arr2),
];
let super_array = SuperArray::from_chunks(chunks);
let scalar = Scalar::Int32(10);
let result =
broadcast_scalar_to_superarray(ArithmeticOperator::Add, &scalar, &super_array).unwrap();
assert_eq!(result.chunks().len(), 2);
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.chunks()[0] {
assert_eq!(arr.data.as_slice(), &[11, 12, 13]);
} else {
panic!("Expected Int32 array in chunk 0");
}
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.chunks()[1] {
assert_eq!(arr.data.as_slice(), &[14, 15, 16]);
} else {
panic!("Expected Int32 array in chunk 1");
}
}
#[cfg(all(feature = "chunked", feature = "views"))]
#[test]
fn test_scalar_to_superarrayview() {
use crate::ArrayV;
let arr = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30, 40, 50, 60]));
let field = Field::new("col".to_string(), ArrowType::Int32, false, None);
let slices = vec![
ArrayV::from(arr.clone()).slice(0, 3),
ArrayV::from(arr.clone()).slice(3, 3),
];
let super_array_view = SuperArrayV {
slices,
field: Arc::new(field),
len: 6,
};
let scalar = Scalar::Int32(5);
let result = broadcast_scalar_to_superarrayview(
ArithmeticOperator::Multiply,
&scalar,
&super_array_view,
)
.unwrap();
assert_eq!(result.chunks().len(), 2);
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.chunks()[0] {
assert_eq!(arr.data.as_slice(), &[50, 100, 150]);
} else {
panic!("Expected Int32 array in chunk 0");
}
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.chunks()[1] {
assert_eq!(arr.data.as_slice(), &[200, 250, 300]);
} else {
panic!("Expected Int32 array in chunk 1");
}
}
#[cfg(feature = "chunked")]
#[test]
fn test_scalar_to_supertable() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3]));
let table1 = Table::build(
vec![FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr1,
)],
3,
"test".to_string(),
);
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![4, 5, 6]));
let table2 = Table::build(
vec![FieldArray::new(
Field::new("col1".to_string(), ArrowType::Int32, false, None),
arr2,
)],
3,
"test".to_string(),
);
let super_table = SuperTable::from_batches(
vec![Arc::new(table1), Arc::new(table2)],
Some("test".to_string()),
);
let scalar = Scalar::Int32(100);
let result =
broadcast_scalar_to_supertable(ArithmeticOperator::Subtract, &scalar, &super_table)
.unwrap();
assert_eq!(result.batches.len(), 2);
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.batches[0].cols[0].array {
assert_eq!(arr.data.as_slice(), &[99, 98, 97]);
} else {
panic!("Expected Int32 array in batch 0");
}
if let Array::NumericArray(NumericArray::Int32(arr)) = &result.batches[1].cols[0].array {
assert_eq!(arr.data.as_slice(), &[96, 95, 94]);
} else {
panic!("Expected Int32 array in batch 1");
}
}
#[test]
fn test_scalar_to_tuple2() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3]));
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30]));
let tuple = (
Arc::new(Value::Array(Arc::new(arr1))),
Arc::new(Value::Array(Arc::new(arr2))),
);
let scalar = Scalar::Int32(5);
let result = broadcast_scalar_to_tuple2(ArithmeticOperator::Add, &scalar, tuple).unwrap();
if let Value::Array(arc_arr) = &*result.0 {
if let Array::NumericArray(NumericArray::Int32(arr)) = arc_arr.as_ref() {
assert_eq!(arr.data.as_slice(), &[6, 7, 8]);
} else {
panic!("Expected Int32 array in tuple element 0");
}
} else {
panic!("Expected Array value");
}
if let Value::Array(arc_arr) = &*result.1 {
if let Array::NumericArray(NumericArray::Int32(arr)) = arc_arr.as_ref() {
assert_eq!(arr.data.as_slice(), &[15, 25, 35]);
} else {
panic!("Expected Int32 array in tuple element 1");
}
} else {
panic!("Expected Array value");
}
}
#[test]
fn test_scalar_to_tuple3() {
let arr1 = Array::from_int32(IntegerArray::from_slice(&vec64![2, 4, 6]));
let arr2 = Array::from_int32(IntegerArray::from_slice(&vec64![3, 6, 9]));
let arr3 = Array::from_int32(IntegerArray::from_slice(&vec64![4, 8, 12]));
let tuple = (
Arc::new(Value::Array(Arc::new(arr1))),
Arc::new(Value::Array(Arc::new(arr2))),
Arc::new(Value::Array(Arc::new(arr3))),
);
let scalar = Scalar::Int32(2);
let result =
broadcast_scalar_to_tuple3(ArithmeticOperator::Multiply, &scalar, tuple).unwrap();
if let Value::Array(arc_arr) = &*result.0 {
if let Array::NumericArray(NumericArray::Int32(arr)) = arc_arr.as_ref() {
assert_eq!(arr.data.as_slice(), &[4, 8, 12]);
} else {
panic!("Expected Int32 array in tuple element 0");
}
} else {
panic!("Expected Array value");
}
if let Value::Array(arc_arr) = &*result.1 {
if let Array::NumericArray(NumericArray::Int32(arr)) = arc_arr.as_ref() {
assert_eq!(arr.data.as_slice(), &[6, 12, 18]);
} else {
panic!("Expected Int32 array in tuple element 1");
}
} else {
panic!("Expected Array value");
}
if let Value::Array(arc_arr) = &*result.2 {
if let Array::NumericArray(NumericArray::Int32(arr)) = arc_arr.as_ref() {
assert_eq!(arr.data.as_slice(), &[8, 16, 24]);
} else {
panic!("Expected Int32 array in tuple element 2");
}
} else {
panic!("Expected Array value");
}
}
#[test]
fn test_scalar_to_fieldarray() {
let array = Array::from_int32(IntegerArray::from_slice(&vec64![100, 200, 300]));
let field = Field::new("myfield".to_string(), ArrowType::Int32, false, None);
let field_array = FieldArray::new(field, array);
let scalar = Scalar::Int32(50);
let result =
broadcast_scalar_to_fieldarray(ArithmeticOperator::Divide, &scalar, &field_array)
.unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = result {
assert_eq!(arr.data.as_slice(), &[0, 0, 0]);
} else {
panic!("Expected Int32 array");
}
}
#[test]
fn test_fieldarray_to_scalar() {
let array = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30]));
let field = Field::new("myfield".to_string(), ArrowType::Int32, false, None);
let field_array = FieldArray::new(field, array);
let scalar = Scalar::Int32(5);
let result =
broadcast_fieldarray_to_scalar(ArithmeticOperator::Multiply, &field_array, &scalar)
.unwrap();
if let Array::NumericArray(NumericArray::Int32(arr)) = result {
assert_eq!(arr.data.as_slice(), &[50, 100, 150]);
} else {
panic!("Expected Int32 array");
}
}
}