use crate::compute::arithmetics::basic::check_same_len;
use crate::{
array::{Array, PrimitiveArray},
buffer::Buffer,
compute::{
arithmetics::{ArrayCheckedMul, ArrayMul, ArraySaturatingMul},
arity::{binary, binary_checked},
utils::combine_validities,
},
datatypes::DataType,
error::{ArrowError, Result},
};
use super::{adjusted_precision_scale, max_value, number_digits};
pub fn mul(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: i128, b: i128| {
let res: i128 = a.checked_mul(b).expect("Mayor overflow for multiplication");
let res = res / 10i128.pow(*lhs_s as u32);
assert!(
!(res.abs() > max_value(*lhs_p)),
"Overflow in multiplication 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_mul(
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: i128, b: i128| match a.checked_mul(b) {
Some(res) => {
let res = res / 10i128.pow(*lhs_s as u32);
let max = max_value(*lhs_p);
match res {
res if res.abs() > max => {
if res > 0 {
max
} else {
-max
}
}
_ => res,
}
}
None => max_value(*lhs_p),
};
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 checked_mul(
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: i128, b: i128| match a.checked_mul(b) {
Some(res) => {
let res = res / 10i128.pow(*lhs_s as u32);
match res {
res if res.abs() > max_value(*lhs_p) => None,
_ => Some(res),
}
}
None => None,
};
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(),
)),
}
}
impl ArrayMul<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn mul(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
mul(self, rhs)
}
}
impl ArrayCheckedMul<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn checked_mul(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
checked_mul(self, rhs)
}
}
impl ArraySaturatingMul<PrimitiveArray<i128>> for PrimitiveArray<i128> {
type Output = Self;
fn saturating_mul(&self, rhs: &PrimitiveArray<i128>) -> Result<Self::Output> {
saturating_mul(self, rhs)
}
}
pub fn adaptive_mul(
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 = if lhs_s > rhs_s {
l.checked_mul(r * 10i128.pow(diff as u32))
.expect("Mayor overflow for multiplication")
} else {
(l * 10i128.pow(diff as u32))
.checked_mul(*r)
.expect("Mayor overflow for multiplication")
};
let res = res / 10i128.pow(res_s as u32);
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(),
))
}
}