use crate::types::{Decimal128Type, Decimal256Type, DecimalType};
use arrow_data::decimal::{DECIMAL256_MAX_PRECISION, DECIMAL_DEFAULT_SCALE};
use arrow_schema::{ArrowError, DataType};
use num::{BigInt, Signed};
use std::cmp::{min, Ordering};
pub struct Decimal<T: DecimalType> {
precision: u8,
scale: u8,
value: T::Native,
}
impl<T: DecimalType> std::fmt::Debug for Decimal<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Decimal")
.field("scale", &self.precision)
.field("precision", &self.precision)
.field("value", &self.value.as_ref())
.finish()
}
}
impl<T: DecimalType> Clone for Decimal<T> {
fn clone(&self) -> Self {
Self {
precision: self.precision,
scale: self.scale,
value: self.value,
}
}
}
impl<T: DecimalType> Copy for Decimal<T> {}
impl<T: DecimalType> Decimal<T> {
pub const MAX_PRECISION: u8 = T::MAX_PRECISION;
pub const MAX_SCALE: u8 = T::MAX_SCALE;
pub const TYPE_CONSTRUCTOR: fn(u8, u8) -> DataType = T::TYPE_CONSTRUCTOR;
pub const DEFAULT_TYPE: DataType = T::DEFAULT_TYPE;
pub fn try_new_from_bytes(
precision: u8,
scale: u8,
bytes: &T::Native,
) -> Result<Self, ArrowError>
where
Self: Sized,
{
if precision > Self::MAX_PRECISION {
return Err(ArrowError::InvalidArgumentError(format!(
"precision {} is greater than max {}",
precision,
Self::MAX_PRECISION
)));
}
if scale > Self::MAX_SCALE {
return Err(ArrowError::InvalidArgumentError(format!(
"scale {} is greater than max {}",
scale,
Self::MAX_SCALE
)));
}
if precision < scale {
return Err(ArrowError::InvalidArgumentError(format!(
"Precision {} is less than scale {}",
precision, scale
)));
}
Ok(Self::new(precision, scale, bytes))
}
pub fn new(precision: u8, scale: u8, bytes: &T::Native) -> Self {
Self {
precision,
scale,
value: *bytes,
}
}
pub fn raw_value(&self) -> &T::Native {
&self.value
}
pub fn precision(&self) -> u8 {
self.precision
}
pub fn scale(&self) -> u8 {
self.scale
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
let raw_bytes = self.raw_value();
let integer = BigInt::from_signed_bytes_le(raw_bytes.as_ref());
let value_str = integer.to_string();
let (sign, rest) =
value_str.split_at(if integer >= BigInt::from(0) { 0 } else { 1 });
let bound = min(usize::from(self.precision()), rest.len()) + sign.len();
let value_str = &value_str[0..bound];
let scale_usize = usize::from(self.scale());
if self.scale() == 0 {
value_str.to_string()
} else if rest.len() > scale_usize {
let (whole, decimal) = value_str.split_at(value_str.len() - scale_usize);
format!("{}.{}", whole, decimal)
} else {
format!("{}0.{:0>width$}", sign, rest, width = scale_usize)
}
}
}
impl<T: DecimalType> PartialOrd for Decimal<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
assert_eq!(
self.scale, other.scale,
"Cannot compare two Decimals with different scale: {}, {}",
self.scale, other.scale
);
Some(singed_cmp_le_bytes(
self.value.as_ref(),
other.value.as_ref(),
))
}
}
impl<T: DecimalType> Ord for Decimal<T> {
fn cmp(&self, other: &Self) -> Ordering {
assert_eq!(
self.scale, other.scale,
"Cannot compare two Decimals with different scale: {}, {}",
self.scale, other.scale
);
singed_cmp_le_bytes(self.value.as_ref(), other.value.as_ref())
}
}
impl<T: DecimalType> PartialEq<Self> for Decimal<T> {
fn eq(&self, other: &Self) -> bool {
assert_eq!(
self.scale, other.scale,
"Cannot compare two Decimals with different scale: {}, {}",
self.scale, other.scale
);
self.value.as_ref().eq(other.value.as_ref())
}
}
impl<T: DecimalType> Eq for Decimal<T> {}
pub type Decimal128 = Decimal<Decimal128Type>;
impl Decimal128 {
pub fn new_from_i128(precision: u8, scale: u8, value: i128) -> Self {
Decimal128 {
precision,
scale,
value: value.to_le_bytes(),
}
}
pub fn as_i128(&self) -> i128 {
i128::from_le_bytes(self.value)
}
}
impl From<Decimal128> for i128 {
fn from(decimal: Decimal128) -> Self {
decimal.as_i128()
}
}
pub type Decimal256 = Decimal<Decimal256Type>;
impl Decimal256 {
pub fn from_big_int(
num: &BigInt,
precision: u8,
scale: u8,
) -> Result<Decimal256, ArrowError> {
let mut bytes = if num.is_negative() {
[255_u8; 32]
} else {
[0; 32]
};
let num_bytes = &num.to_signed_bytes_le();
bytes[0..num_bytes.len()].clone_from_slice(num_bytes);
Decimal256::try_new_from_bytes(precision, scale, &bytes)
}
pub fn to_big_int(self) -> BigInt {
BigInt::from_signed_bytes_le(&self.value)
}
}
impl From<BigInt> for Decimal256 {
fn from(bigint: BigInt) -> Self {
Decimal256::from_big_int(&bigint, DECIMAL256_MAX_PRECISION, DECIMAL_DEFAULT_SCALE)
.unwrap()
}
}
#[inline]
pub(crate) fn singed_cmp_le_bytes(left: &[u8], right: &[u8]) -> Ordering {
assert_eq!(
left.len(),
right.len(),
"Can't compare bytes array with different len: {}, {}",
left.len(),
right.len()
);
assert_ne!(left.len(), 0, "Can't compare bytes array of length 0");
let len = left.len();
let left_negative = left[len - 1] >= 0x80_u8;
let right_negative = right[len - 1] >= 0x80_u8;
if left_negative != right_negative {
return match left_negative {
true => {
Ordering::Less
}
false => Ordering::Greater,
};
}
for i in 0..len {
let l_byte = left[len - 1 - i];
let r_byte = right[len - 1 - i];
match l_byte.cmp(&r_byte) {
Ordering::Less => {
return Ordering::Less;
}
Ordering::Greater => {
return Ordering::Greater;
}
Ordering::Equal => {}
}
}
Ordering::Equal
}
#[cfg(test)]
mod tests {
use super::*;
use num::{BigInt, Num};
use rand::random;
#[test]
fn decimal_128_to_string() {
let mut value = Decimal128::new_from_i128(5, 2, 100);
assert_eq!(value.to_string(), "1.00");
value = Decimal128::new_from_i128(5, 3, 100);
assert_eq!(value.to_string(), "0.100");
}
#[test]
fn decimal_invalid_precision_scale() {
let bytes = 100_i128.to_le_bytes();
let err = Decimal128::try_new_from_bytes(5, 6, &bytes);
assert!(err.is_err());
}
#[test]
fn decimal_128_from_bytes() {
let mut bytes = 100_i128.to_le_bytes();
let value = Decimal128::try_new_from_bytes(5, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "1.00");
bytes = (-1_i128).to_le_bytes();
let value = Decimal128::try_new_from_bytes(5, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "-0.01");
bytes = i128::MAX.to_le_bytes();
let value = Decimal128::try_new_from_bytes(38, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "170141183460469231731687303715884105.72");
bytes = i128::MIN.to_le_bytes();
let value = Decimal128::try_new_from_bytes(38, 2, &bytes).unwrap();
assert_eq!(
value.to_string(),
"-170141183460469231731687303715884105.72"
);
bytes = 12345_i128.to_le_bytes();
let value = Decimal128::try_new_from_bytes(3, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "1.23");
bytes = (-12345_i128).to_le_bytes();
let value = Decimal128::try_new_from_bytes(3, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "-1.23");
}
#[test]
fn decimal_256_from_bytes() {
let mut bytes = [0_u8; 32];
bytes[0..16].clone_from_slice(&100_i128.to_le_bytes());
let value = Decimal256::try_new_from_bytes(5, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "1.00");
bytes[0..16].clone_from_slice(&i128::MAX.to_le_bytes());
let value = Decimal256::try_new_from_bytes(40, 4, &bytes).unwrap();
assert_eq!(
value.to_string(),
"17014118346046923173168730371588410.5727"
);
bytes[0..16].clone_from_slice(&0_i128.to_le_bytes());
bytes[15] = 128;
let value = Decimal256::try_new_from_bytes(40, 4, &bytes).unwrap();
assert_eq!(
value.to_string(),
"17014118346046923173168730371588410.5728"
);
bytes = [255; 32];
bytes[31] = 128;
let value = Decimal256::try_new_from_bytes(76, 4, &bytes).unwrap();
assert_eq!(
value.to_string(),
"-574437317700748313234121683441537667865831564552201235664496608164256541.5731"
);
bytes = [255; 32];
let value = Decimal256::try_new_from_bytes(5, 2, &bytes).unwrap();
assert_eq!(value.to_string(), "-0.01");
}
fn i128_func(value: impl Into<i128>) -> i128 {
value.into()
}
#[test]
fn decimal_128_to_i128() {
let value = Decimal128::new_from_i128(5, 2, 100);
let integer = i128_func(value);
assert_eq!(integer, 100);
}
#[test]
fn bigint_to_decimal256() {
let num = BigInt::from_str_radix("123456789", 10).unwrap();
let value = Decimal256::from_big_int(&num, 30, 2).unwrap();
assert_eq!(value.to_string(), "1234567.89");
let num = BigInt::from_str_radix("-5744373177007483132341216834415376678658315645522012356644966081642565415731", 10).unwrap();
let value = Decimal256::from_big_int(&num, 76, 4).unwrap();
assert_eq!(value.to_string(), "-574437317700748313234121683441537667865831564552201235664496608164256541.5731");
}
#[test]
fn test_lt_cmp_byte() {
for _i in 0..100 {
let left = random::<i128>();
let right = random::<i128>();
let result = singed_cmp_le_bytes(
left.to_le_bytes().as_slice(),
right.to_le_bytes().as_slice(),
);
assert_eq!(left.cmp(&right), result);
}
for _i in 0..100 {
let left = random::<i32>();
let right = random::<i32>();
let result = singed_cmp_le_bytes(
left.to_le_bytes().as_slice(),
right.to_le_bytes().as_slice(),
);
assert_eq!(left.cmp(&right), result);
}
}
#[test]
fn compare_decimal128() {
let v1 = -100_i128;
let v2 = 10000_i128;
let right = Decimal128::new_from_i128(20, 3, v2);
for v in v1..v2 {
let left = Decimal128::new_from_i128(20, 3, v);
assert!(left < right);
}
for _i in 0..100 {
let left = random::<i128>();
let right = random::<i128>();
let left_decimal = Decimal128::new_from_i128(38, 2, left);
let right_decimal = Decimal128::new_from_i128(38, 2, right);
assert_eq!(left < right, left_decimal < right_decimal);
assert_eq!(left == right, left_decimal == right_decimal)
}
}
#[test]
fn compare_decimal256() {
let v1 = -100_i128;
let v2 = 10000_i128;
let right = Decimal256::from_big_int(&BigInt::from(v2), 75, 2).unwrap();
for v in v1..v2 {
let left = Decimal256::from_big_int(&BigInt::from(v), 75, 2).unwrap();
assert!(left < right);
}
for _i in 0..100 {
let left = random::<i128>();
let right = random::<i128>();
let left_decimal =
Decimal256::from_big_int(&BigInt::from(left), 75, 2).unwrap();
let right_decimal =
Decimal256::from_big_int(&BigInt::from(right), 75, 2).unwrap();
assert_eq!(left < right, left_decimal < right_decimal);
assert_eq!(left == right, left_decimal == right_decimal)
}
}
}