use vortex_buffer::BitBuffer;
use vortex_buffer::Buffer;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
use crate::ArrayRef;
use crate::ExecutionCtx;
use crate::IntoArray;
use crate::arrays::BoolArray;
use crate::arrays::Constant;
use crate::arrays::DecimalArray;
use crate::dtype::NativeDecimalType;
use crate::dtype::Nullability;
use crate::dtype::i256;
use crate::match_each_decimal_value_type;
use crate::scalar::DecimalValue;
use crate::scalar_fn::fns::binary::compare::collect_bits;
use crate::scalar_fn::fns::binary::compare::collect_zip_bits;
use crate::scalar_fn::fns::binary::compare::compare_validity;
use crate::scalar_fn::fns::operators::CompareOperator;
use crate::validity::Validity;
enum DecimalOperand {
Array {
values: DecimalArray,
validity: Validity,
},
Constant {
value: DecimalValue,
validity: Validity,
},
}
impl DecimalOperand {
fn try_new(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self> {
if let Some(constant) = array.as_opt::<Constant>() {
let value = constant
.scalar()
.as_decimal()
.decimal_value()
.ok_or_else(|| vortex_err!("null constant handled by execute_compare"))?;
return Ok(Self::Constant {
value,
validity: if constant.scalar().dtype().is_nullable() {
Validity::AllValid
} else {
Validity::NonNullable
},
});
}
let values = array.clone().execute::<DecimalArray>(ctx)?;
let validity = values.validity()?;
Ok(Self::Array { values, validity })
}
fn validity(&self) -> Validity {
match self {
Self::Array { validity, .. } | Self::Constant { validity, .. } => validity.clone(),
}
}
}
pub(super) fn compare_decimal(
lhs: &ArrayRef,
rhs: &ArrayRef,
op: CompareOperator,
nullability: Nullability,
ctx: &mut ExecutionCtx,
) -> VortexResult<ArrayRef> {
let len = lhs.len();
let lhs = DecimalOperand::try_new(lhs, ctx)?;
let rhs = DecimalOperand::try_new(rhs, ctx)?;
let validity = compare_validity(lhs.validity(), rhs.validity(), nullability)?;
let bits = match (lhs, rhs) {
(DecimalOperand::Array { values: l, .. }, DecimalOperand::Array { values: r, .. }) => {
compare_decimal_values(&l, &r, op)
}
(DecimalOperand::Array { values, .. }, DecimalOperand::Constant { value, .. }) => {
compare_decimal_constant(&values, value, op)
}
(DecimalOperand::Constant { value, .. }, DecimalOperand::Array { values, .. }) => {
compare_decimal_constant(&values, value, op.swap())
}
(DecimalOperand::Constant { value: l, .. }, DecimalOperand::Constant { value: r, .. }) => {
let ordering = l.as_i256().cmp(&r.as_i256());
BitBuffer::full(super::ordering_predicate(op)(ordering), len)
}
};
Ok(BoolArray::try_new(bits, validity)?.into_array())
}
fn compare_decimal_values(
lhs: &DecimalArray,
rhs: &DecimalArray,
op: CompareOperator,
) -> BitBuffer {
let common = lhs.values_type().max(rhs.values_type());
match_each_decimal_value_type!(common, |W| {
let lhs = widened_buffer::<W>(lhs);
let rhs = widened_buffer::<W>(rhs);
compare_slices::<W>(&lhs, &rhs, op)
})
}
pub(super) fn widened_buffer<W: NativeDecimalType>(array: &DecimalArray) -> Buffer<W> {
if array.values_type() == W::DECIMAL_TYPE {
return array.buffer::<W>();
}
match_each_decimal_value_type!(array.values_type(), |T| {
array
.buffer::<T>()
.iter()
.map(|v| W::from(*v).vortex_expect("widening decimal cast must succeed"))
.collect()
})
}
fn compare_decimal_constant(
array: &DecimalArray,
constant: DecimalValue,
op: CompareOperator,
) -> BitBuffer {
match_each_decimal_value_type!(array.values_type(), |T| {
match constant.cast::<T>() {
Some(value) => compare_slice_constant::<T>(&array.buffer::<T>(), value, op),
None => {
let constant_greater = constant.as_i256() > i256::ZERO;
let result = match op {
CompareOperator::Eq => false,
CompareOperator::NotEq => true,
CompareOperator::Lt | CompareOperator::Lte => constant_greater,
CompareOperator::Gt | CompareOperator::Gte => !constant_greater,
};
BitBuffer::full(result, array.len())
}
}
})
}
fn compare_slices<T: NativeDecimalType>(lhs: &[T], rhs: &[T], op: CompareOperator) -> BitBuffer {
match op {
CompareOperator::Eq => collect_zip_bits(lhs, rhs, |a: T, b: T| a == b),
CompareOperator::NotEq => collect_zip_bits(lhs, rhs, |a: T, b: T| a != b),
CompareOperator::Gt => collect_zip_bits(lhs, rhs, |a: T, b: T| a > b),
CompareOperator::Gte => collect_zip_bits(lhs, rhs, |a: T, b: T| a >= b),
CompareOperator::Lt => collect_zip_bits(lhs, rhs, |a: T, b: T| a < b),
CompareOperator::Lte => collect_zip_bits(lhs, rhs, |a: T, b: T| a <= b),
}
}
fn compare_slice_constant<T: NativeDecimalType>(
values: &[T],
constant: T,
op: CompareOperator,
) -> BitBuffer {
match op {
CompareOperator::Eq => collect_bits(values, |a: T| a == constant),
CompareOperator::NotEq => collect_bits(values, |a: T| a != constant),
CompareOperator::Gt => collect_bits(values, |a: T| a > constant),
CompareOperator::Gte => collect_bits(values, |a: T| a >= constant),
CompareOperator::Lt => collect_bits(values, |a: T| a < constant),
CompareOperator::Lte => collect_bits(values, |a: T| a <= constant),
}
}