use crate::compute::arithmetics::basic::check_same_len;
use crate::{
array::{Array, PrimitiveArray},
buffer::Buffer,
compute::{
arithmetics::{ArrayCheckedSub, ArraySaturatingSub, ArraySub},
arity::{binary, binary_checked},
utils::combine_validities,
},
datatypes::DataType,
error::{ArrowError, Result},
};
use super::{adjusted_precision_scale, max_value, number_digits};
pub fn sub(lhs: &PrimitiveArray<i128>, rhs: &PrimitiveArray<i128>) -> Result<PrimitiveArray<i128>> {
match (lhs.data_type(), rhs.data_type()) {
(DataType::Decimal(lhs_p, lhs_s), DataType::Decimal(rhs_p, rhs_s)) => {
if lhs_p == rhs_p && lhs_s == rhs_s {
let op = move |a, b| {
let res: i128 = a - b;
assert!(
!(res.abs() > max_value(*lhs_p)),
"Overflow in subtract presented for precision {}",
lhs_p
);
res
};
binary(lhs, rhs, lhs.data_type().clone(), op)
} else {
Err(ArrowError::InvalidArgumentError(
"Arrays must have the same precision and scale".to_string(),
))
}
}
_ => Err(ArrowError::InvalidArgumentError(
"Incorrect data type for the array".to_string(),
)),
}
}
pub fn saturating_sub(
lhs: &PrimitiveArray<i128>,
rhs: &PrimitiveArray<i128>,
) -> Result<PrimitiveArray<i128>> {
match (lhs.data_type(), rhs.data_type()) {
(DataType::Decimal(lhs_p, lhs_s), DataType::Decimal(rhs_p, rhs_s)) => {
if lhs_p == rhs_p && lhs_s == rhs_s {
let op = move |a, b| {
let res: i128 = a - b;
let max: i128 = max_value(*lhs_p);
match res {
res if res.abs() > max => {
if res > 0 {
max
} else {
-max
}
}
_ => res,
}
};
binary(lhs, rhs, lhs.data_type().clone(), op)
} else {
Err(ArrowError::InvalidArgumentError(
"Arrays must have the same precision and scale".to_string(),
))
}
}
_ => Err(ArrowError::InvalidArgumentError(
"Incorrect data type for the array".to_string(),
)),
}
}
impl ArraySub<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn sub(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
sub(self, rhs)
}
}
impl ArrayCheckedSub<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn checked_sub(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
checked_sub(self, rhs)
}
}
impl ArraySaturatingSub<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn saturating_sub(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
saturating_sub(self, rhs)
}
}
pub fn checked_sub(
lhs: &PrimitiveArray<i128>,
rhs: &PrimitiveArray<i128>,
) -> Result<PrimitiveArray<i128>> {
match (lhs.data_type(), rhs.data_type()) {
(DataType::Decimal(lhs_p, lhs_s), DataType::Decimal(rhs_p, rhs_s)) => {
if lhs_p == rhs_p && lhs_s == rhs_s {
let op = move |a, b| {
let res: i128 = a - b;
match res {
res if res.abs() > max_value(*lhs_p) => None,
_ => Some(res),
}
};
binary_checked(lhs, rhs, lhs.data_type().clone(), op)
} else {
Err(ArrowError::InvalidArgumentError(
"Arrays must have the same precision and scale".to_string(),
))
}
}
_ => Err(ArrowError::InvalidArgumentError(
"Incorrect data type for the array".to_string(),
)),
}
}
pub fn adaptive_sub(
lhs: &PrimitiveArray<i128>,
rhs: &PrimitiveArray<i128>,
) -> Result<PrimitiveArray<i128>> {
check_same_len(lhs, rhs)?;
if let (DataType::Decimal(lhs_p, lhs_s), DataType::Decimal(rhs_p, rhs_s)) =
(lhs.data_type(), rhs.data_type())
{
let (mut res_p, res_s, diff) = adjusted_precision_scale(*lhs_p, *lhs_s, *rhs_p, *rhs_s);
let mut result = Vec::new();
for (l, r) in lhs.values().iter().zip(rhs.values().iter()) {
let res: i128 = if lhs_s > rhs_s {
l - r * 10i128.pow(diff as u32)
} else {
l * 10i128.pow(diff as u32) - r
};
if res.abs() > max_value(res_p) {
res_p = number_digits(res);
}
result.push(res);
}
let validity = combine_validities(lhs.validity(), rhs.validity());
let values = Buffer::from(result);
Ok(PrimitiveArray::<i128>::from_data(
DataType::Decimal(res_p, res_s),
values,
validity,
))
} else {
Err(ArrowError::InvalidArgumentError(
"Incorrect data type for the array".to_string(),
))
}
}