use std::fmt::Debug;
use std::fmt::Display;
use std::panic::RefUnwindSafe;
use num_traits::ConstOne;
use num_traits::ConstZero;
use paste::paste;
use vortex_error::VortexError;
use vortex_error::vortex_bail;
use crate::BigCast;
use crate::DecimalDType;
use crate::PType;
use crate::decimal::max_precision::MAX_DECIMAL256_FOR_EACH_PRECISION;
use crate::decimal::max_precision::MIN_DECIMAL256_FOR_EACH_PRECISION;
use crate::i256;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, prost::Enumeration)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[repr(u8)]
pub enum DecimalType {
I8 = 0,
I16 = 1,
I32 = 2,
I64 = 3,
I128 = 4,
I256 = 5,
}
impl DecimalType {
pub fn smallest_decimal_value_type(decimal_dtype: &DecimalDType) -> DecimalType {
match decimal_dtype.precision() {
1..=2 => DecimalType::I8,
3..=4 => DecimalType::I16,
5..=9 => DecimalType::I32,
10..=18 => DecimalType::I64,
19..=38 => DecimalType::I128,
39..=76 => DecimalType::I256,
0 => unreachable!("precision must be greater than 0"),
p => unreachable!("precision larger than 76 is invalid found precision {p}"),
}
}
pub fn byte_width(&self) -> usize {
match self {
DecimalType::I8 => size_of::<i8>(),
DecimalType::I16 => size_of::<i16>(),
DecimalType::I32 => size_of::<i32>(),
DecimalType::I64 => size_of::<i64>(),
DecimalType::I128 => size_of::<i128>(),
DecimalType::I256 => size_of::<i256>(),
}
}
pub fn is_compatible_decimal_value_type(self, dtype: DecimalDType) -> bool {
self >= Self::smallest_decimal_value_type(&dtype)
}
}
pub trait NativeDecimalType:
Send
+ Sync
+ Clone
+ Copy
+ Debug
+ Display
+ Default
+ RefUnwindSafe
+ Eq
+ Ord
+ BigCast
+ 'static
{
const DECIMAL_TYPE: DecimalType;
const MAX_PRECISION: u8;
const MAX_SCALE: i8;
const MIN_BY_PRECISION: &'static [Self];
const MAX_BY_PRECISION: &'static [Self];
fn downcast<V: DecimalTypeDowncast>(visitor: V) -> V::Output<Self>;
fn upcast<V: DecimalTypeUpcast>(input: V::Input<Self>) -> V;
}
pub trait DecimalTypeDowncast {
type Output<T: NativeDecimalType>;
fn into_i8(self) -> Self::Output<i8>;
fn into_i16(self) -> Self::Output<i16>;
fn into_i32(self) -> Self::Output<i32>;
fn into_i64(self) -> Self::Output<i64>;
fn into_i128(self) -> Self::Output<i128>;
fn into_i256(self) -> Self::Output<i256>;
}
pub trait DecimalTypeUpcast {
type Input<T: NativeDecimalType>;
fn from_i8(input: Self::Input<i8>) -> Self;
fn from_i16(input: Self::Input<i16>) -> Self;
fn from_i32(input: Self::Input<i32>) -> Self;
fn from_i64(input: Self::Input<i64>) -> Self;
fn from_i128(input: Self::Input<i128>) -> Self;
fn from_i256(input: Self::Input<i256>) -> Self;
}
macro_rules! impl_decimal {
($T:ty, $UPPER:ident) => {
paste! {
impl NativeDecimalType for $T {
const DECIMAL_TYPE: DecimalType = DecimalType::$UPPER;
const MAX_PRECISION: u8 = match DecimalType::$UPPER {
DecimalType::I8 => 2,
DecimalType::I16 => 4,
DecimalType::I32 => 9,
DecimalType::I64 => 18,
DecimalType::I128 => 38,
DecimalType::I256 => 76,
};
const MAX_SCALE: i8 = Self::MAX_PRECISION as i8;
const MIN_BY_PRECISION: &'static [Self] = &{
let mut mins = [$T::ZERO; Self::MAX_PRECISION as usize + 1];
let mut p = $T::ONE;
let mut i = 0;
while i < Self::MAX_PRECISION as usize {
p = p * 10;
mins[i + 1] = -(p - 1);
i += 1;
}
mins
};
const MAX_BY_PRECISION: &'static [Self] = &{
let mut maxs = [$T::ZERO; Self::MAX_PRECISION as usize + 1];
let mut p = $T::ONE;
let mut i = 0;
while i < Self::MAX_PRECISION as usize {
p = p * 10;
maxs[i + 1] = p - 1;
i += 1;
}
maxs
};
#[inline]
fn downcast<V: DecimalTypeDowncast>(visitor: V) -> V::Output<Self> {
paste::paste! { visitor.[<into_ $T>]() }
}
#[inline]
fn upcast<V: DecimalTypeUpcast>(input: V::Input<Self>) -> V {
paste::paste! { V::[<from_ $T>](input) }
}
}
}
};
}
impl_decimal!(i8, I8);
impl_decimal!(i16, I16);
impl_decimal!(i32, I32);
impl_decimal!(i64, I64);
impl_decimal!(i128, I128);
impl NativeDecimalType for i256 {
const DECIMAL_TYPE: DecimalType = DecimalType::I256;
const MAX_PRECISION: u8 = 76;
const MAX_SCALE: i8 = 76;
const MIN_BY_PRECISION: &'static [Self] = &MIN_DECIMAL256_FOR_EACH_PRECISION;
const MAX_BY_PRECISION: &'static [Self] = &MAX_DECIMAL256_FOR_EACH_PRECISION;
fn downcast<V: DecimalTypeDowncast>(visitor: V) -> V::Output<Self> {
visitor.into_i256()
}
fn upcast<V: DecimalTypeUpcast>(input: V::Input<Self>) -> V {
V::from_i256(input)
}
}
impl Display for DecimalType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DecimalType::I8 => write!(f, "i8"),
DecimalType::I16 => write!(f, "i16"),
DecimalType::I32 => write!(f, "i32"),
DecimalType::I64 => write!(f, "i64"),
DecimalType::I128 => write!(f, "i128"),
DecimalType::I256 => write!(f, "i256"),
}
}
}
impl TryFrom<PType> for DecimalType {
type Error = VortexError;
fn try_from(value: PType) -> Result<Self, Self::Error> {
Ok(match value {
PType::I8 => DecimalType::I8,
PType::I16 => DecimalType::I16,
PType::I32 => DecimalType::I32,
PType::I64 => DecimalType::I64,
p @ (PType::U8
| PType::U16
| PType::U32
| PType::U64
| PType::F16
| PType::F32
| PType::F64) => vortex_bail!("cannot convert ptype {p} to DecimalType"),
})
}
}