use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hash::Hash;
use prost::Message;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_err;
use vortex_session::VortexSession;
use crate::ArrayRef;
use crate::Columnar;
use crate::ExecutionCtx;
use crate::aggregate_fn::AggregateFn;
use crate::aggregate_fn::AggregateFnId;
use crate::aggregate_fn::AggregateFnRef;
use crate::aggregate_fn::AggregateFnSatisfaction;
use crate::dtype::DType;
use crate::proto::expr as pb;
use crate::scalar::Scalar;
#[derive(Clone, Debug)]
pub struct AggregateDTypes {
pub dtype: DType,
pub return_dtype: DType,
pub partial_dtype: DType,
}
impl AggregateDTypes {
pub fn new(dtype: DType, return_dtype: DType, partial_dtype: DType) -> Self {
Self {
dtype,
return_dtype,
partial_dtype,
}
}
pub fn try_new<V: AggregateFnVTable>(
vtable: &V,
options: &V::Options,
dtype: DType,
) -> VortexResult<Self> {
let return_dtype = vtable.return_dtype(options, &dtype).ok_or_else(|| {
vortex_err!(
"Aggregate function {} cannot be applied to dtype {}",
vtable.id(),
dtype
)
})?;
let partial_dtype = vtable.partial_dtype(options, &dtype).ok_or_else(|| {
vortex_err!(
"Aggregate function {} cannot be applied to dtype {}",
vtable.id(),
dtype
)
})?;
Ok(Self::new(dtype, return_dtype, partial_dtype))
}
pub fn args<'a, O>(&'a self, options: &'a O) -> AggregateArgs<'a, O> {
AggregateArgs {
options,
dtype: &self.dtype,
return_dtype: &self.return_dtype,
partial_dtype: &self.partial_dtype,
}
}
}
#[derive(Debug)]
pub struct AggregateArgs<'a, O> {
pub options: &'a O,
pub dtype: &'a DType,
pub return_dtype: &'a DType,
pub partial_dtype: &'a DType,
}
impl<O> Clone for AggregateArgs<'_, O> {
fn clone(&self) -> Self {
*self
}
}
impl<O> Copy for AggregateArgs<'_, O> {}
pub trait AggregateFnVTable: 'static + Sized + Clone + Send + Sync {
type Options: 'static + Send + Sync + Clone + Debug + Display + PartialEq + Eq + Hash;
type Partial: 'static + Send;
fn id(&self) -> AggregateFnId;
fn serialize(&self, options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
_ = options;
Ok(None)
}
fn deserialize(
&self,
_metadata: &[u8],
_session: &VortexSession,
) -> VortexResult<Self::Options> {
vortex_bail!("Aggregate function {} is not deserializable", self.id());
}
fn can_satisfy(
&self,
options: &Self::Options,
requested: &AggregateFnRef,
) -> AggregateFnSatisfaction {
if requested
.as_opt::<Self>()
.is_some_and(|other| other == options)
{
AggregateFnSatisfaction::Exact
} else {
AggregateFnSatisfaction::No
}
}
fn return_dtype(&self, options: &Self::Options, input_dtype: &DType) -> Option<DType>;
fn zone_stat_default(&self, _input_dtype: &DType) -> Option<AggregateFnRef> {
None
}
fn partial_dtype(&self, options: &Self::Options, input_dtype: &DType) -> Option<DType>;
fn empty_partial(&self, args: AggregateArgs<'_, Self::Options>) -> VortexResult<Self::Partial>;
fn partial_from_scalar(
&self,
args: AggregateArgs<'_, Self::Options>,
scalar: &Scalar,
) -> VortexResult<Self::Partial>;
fn merge_partials(
&self,
args: AggregateArgs<'_, Self::Options>,
first: Self::Partial,
second: Self::Partial,
) -> VortexResult<Self::Partial>;
fn to_scalar(
&self,
args: AggregateArgs<'_, Self::Options>,
partial: &Self::Partial,
) -> VortexResult<Scalar>;
fn is_saturated(&self, args: AggregateArgs<'_, Self::Options>, partial: &Self::Partial)
-> bool;
fn try_accumulate(
&self,
_args: AggregateArgs<'_, Self::Options>,
_state: &mut Self::Partial,
_batch: &ArrayRef,
_ctx: &mut ExecutionCtx,
) -> VortexResult<bool> {
Ok(false)
}
fn accumulate(
&self,
args: AggregateArgs<'_, Self::Options>,
state: &mut Self::Partial,
batch: &Columnar,
ctx: &mut ExecutionCtx,
) -> VortexResult<()>;
fn finalize(
&self,
args: AggregateArgs<'_, Self::Options>,
states: ArrayRef,
) -> VortexResult<ArrayRef>;
fn finalize_scalar(
&self,
args: AggregateArgs<'_, Self::Options>,
partial: &Self::Partial,
) -> VortexResult<Scalar>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EmptyOptions;
impl Display for EmptyOptions {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NumericalAggregateOpts {
pub skip_nans: bool,
}
impl NumericalAggregateOpts {
pub const fn skip_nans() -> Self {
Self { skip_nans: true }
}
pub const fn include_nans() -> Self {
Self { skip_nans: false }
}
pub fn serialize(&self) -> Vec<u8> {
pb::NumericalAggregateOpts {
skip_nans: self.skip_nans,
}
.encode_to_vec()
}
pub fn deserialize(metadata: &[u8]) -> VortexResult<Self> {
let opts = pb::NumericalAggregateOpts::decode(metadata)?;
Ok(Self {
skip_nans: opts.skip_nans,
})
}
}
impl Default for NumericalAggregateOpts {
fn default() -> Self {
Self::skip_nans()
}
}
impl Display for NumericalAggregateOpts {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if !self.skip_nans {
write!(f, "skip_nans=false")?;
}
Ok(())
}
}
pub trait AggregateFnVTableExt: AggregateFnVTable {
fn bind(&self, options: Self::Options) -> AggregateFnRef {
AggregateFn::new(self.clone(), options).erased()
}
}
impl<V: AggregateFnVTable> AggregateFnVTableExt for V {}