use arrow::array::{
Array, BooleanArray, Date32Array, Date64Array, Decimal128Array, Float32Array, Float64Array,
Int8Array, Int16Array, Int32Array, Int64Array, LargeStringArray, StringArray, StringViewArray,
TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray,
TimestampSecondArray, UInt8Array, UInt16Array, UInt32Array, UInt64Array,
};
use arrow::datatypes::TimeUnit;
use crate::{KernelKind, MonotonicDirectionAst, ProofFrameError};
use super::dataset_state::downcast;
#[derive(Debug, Clone, PartialEq)]
pub(super) enum Ordered {
Int(i128),
Uint(u64),
Float(f64),
Text(Vec<u8>),
}
impl Ordered {
fn compare(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Self::Int(left), Self::Int(right)) => Some(left.cmp(right)),
(Self::Uint(left), Self::Uint(right)) => Some(left.cmp(right)),
(Self::Float(left), Self::Float(right)) => Some(left.total_cmp(right)),
(Self::Text(left), Self::Text(right)) => Some(left.cmp(right)),
_ => None,
}
}
}
pub(super) fn ordered_value(
kernel: &KernelKind,
array: &dyn Array,
row: usize,
) -> Result<Ordered, ProofFrameError> {
macro_rules! int {
($variant:pat, $array:ty) => {
if matches!(kernel, $variant) {
return Ok(Ordered::Int(i128::from(
downcast::<$array>(array).value(row),
)));
}
};
}
macro_rules! uint {
($variant:pat, $array:ty) => {
if matches!(kernel, $variant) {
return Ok(Ordered::Uint(u64::from(
downcast::<$array>(array).value(row),
)));
}
};
}
int!(KernelKind::I8, Int8Array);
int!(KernelKind::I16, Int16Array);
int!(KernelKind::I32, Int32Array);
int!(KernelKind::I64, Int64Array);
int!(KernelKind::Date32, Date32Array);
int!(KernelKind::Date64, Date64Array);
int!(
KernelKind::Timestamp(TimeUnit::Second),
TimestampSecondArray
);
int!(
KernelKind::Timestamp(TimeUnit::Millisecond),
TimestampMillisecondArray
);
int!(
KernelKind::Timestamp(TimeUnit::Microsecond),
TimestampMicrosecondArray
);
int!(
KernelKind::Timestamp(TimeUnit::Nanosecond),
TimestampNanosecondArray
);
uint!(KernelKind::U8, UInt8Array);
uint!(KernelKind::U16, UInt16Array);
uint!(KernelKind::U32, UInt32Array);
match kernel {
KernelKind::U64 => Ok(Ordered::Uint(downcast::<UInt64Array>(array).value(row))),
KernelKind::Boolean => Ok(Ordered::Uint(u64::from(
downcast::<BooleanArray>(array).value(row),
))),
KernelKind::F32 => Ok(Ordered::Float(f64::from(
downcast::<Float32Array>(array).value(row),
))),
KernelKind::F64 => Ok(Ordered::Float(downcast::<Float64Array>(array).value(row))),
KernelKind::Decimal128 { .. } => {
Ok(Ordered::Int(downcast::<Decimal128Array>(array).value(row)))
}
KernelKind::Utf8 => Ok(Ordered::Text(
downcast::<StringArray>(array).value(row).into(),
)),
KernelKind::LargeUtf8 => Ok(Ordered::Text(
downcast::<LargeStringArray>(array).value(row).into(),
)),
KernelKind::Utf8View => Ok(Ordered::Text(
downcast::<StringViewArray>(array).value(row).into(),
)),
_ => Err(ProofFrameError::UnsupportedType(format!(
"monotonicity for {}",
array.data_type()
))),
}
}
pub(super) fn follows(
direction: MonotonicDirectionAst,
previous: &Ordered,
next: &Ordered,
) -> Result<bool, ProofFrameError> {
use std::cmp::Ordering::{Equal, Greater, Less};
let ordering = previous.compare(next).ok_or_else(|| {
ProofFrameError::CorruptData("Monotonicity compared two different value kinds".into())
})?;
Ok(match direction {
MonotonicDirectionAst::Increasing => matches!(ordering, Less | Equal),
MonotonicDirectionAst::StrictlyIncreasing => ordering == Less,
MonotonicDirectionAst::Decreasing => matches!(ordering, Greater | Equal),
MonotonicDirectionAst::StrictlyDecreasing => ordering == Greater,
})
}
pub(super) fn describe(direction: MonotonicDirectionAst) -> &'static str {
match direction {
MonotonicDirectionAst::Increasing => "must not decrease",
MonotonicDirectionAst::StrictlyIncreasing => "must increase",
MonotonicDirectionAst::Decreasing => "must not increase",
MonotonicDirectionAst::StrictlyDecreasing => "must decrease",
}
}
pub(super) fn distance(previous: &Ordered, next: &Ordered) -> Option<f64> {
match (previous, next) {
(Ordered::Int(left), Ordered::Int(right)) => Some((right - left) as f64),
(Ordered::Uint(left), Ordered::Uint(right)) => Some(*right as f64 - *left as f64),
(Ordered::Float(left), Ordered::Float(right)) => Some(right - left),
_ => None,
}
}