This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new c6cc7f8597 arrow-buffer: implement num-traits numeric operations
(#8977)
c6cc7f8597 is described below
commit c6cc7f85970527ade716a220c698fa3471b38595
Author: theirix <[email protected]>
AuthorDate: Sat Dec 13 14:27:45 2025 +0000
arrow-buffer: implement num-traits numeric operations (#8977)
# Which issue does this PR close?
- Closes #8976
# Rationale for this change
`i256` doesn't implement some numeric traits. It'd be good to have it
supported alongside other standard types.
# What changes are included in this PR?
- Trait implementations (checked ops, `Num`, `One`, `Zero`) using
already written methods
- Unit tests
Not all traits are implemented (checked shl/shr, pow is not here yet).
The main point of this PR is to provide a starting implementation.
# Are these changes tested?
Tested via new unit test
# Are there any user-facing changes?
Only new trait implementation.
---
arrow-buffer/src/bigint/mod.rs | 214 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 213 insertions(+), 1 deletion(-)
diff --git a/arrow-buffer/src/bigint/mod.rs b/arrow-buffer/src/bigint/mod.rs
index e83265047b..15faed43a1 100644
--- a/arrow-buffer/src/bigint/mod.rs
+++ b/arrow-buffer/src/bigint/mod.rs
@@ -18,7 +18,11 @@
use crate::arith::derive_arith;
use crate::bigint::div::div_rem;
use num_bigint::BigInt;
-use num_traits::{FromPrimitive, ToPrimitive, cast::AsPrimitive};
+use num_traits::{
+ Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem,
CheckedSub, FromPrimitive,
+ Num, One, Signed, ToPrimitive, WrappingAdd, WrappingMul, WrappingNeg,
WrappingSub, Zero,
+ cast::AsPrimitive,
+};
use std::cmp::Ordering;
use std::num::ParseIntError;
use std::ops::{BitAnd, BitOr, BitXor, Neg, Shl, Shr};
@@ -869,6 +873,137 @@ impl ToPrimitive for i256 {
}
}
+// num_traits checked implementations
+
+impl CheckedNeg for i256 {
+ fn checked_neg(&self) -> Option<Self> {
+ (*self).checked_neg()
+ }
+}
+
+impl CheckedAdd for i256 {
+ fn checked_add(&self, v: &i256) -> Option<Self> {
+ (*self).checked_add(*v)
+ }
+}
+
+impl CheckedSub for i256 {
+ fn checked_sub(&self, v: &i256) -> Option<Self> {
+ (*self).checked_sub(*v)
+ }
+}
+
+impl CheckedDiv for i256 {
+ fn checked_div(&self, v: &i256) -> Option<Self> {
+ (*self).checked_div(*v)
+ }
+}
+
+impl CheckedMul for i256 {
+ fn checked_mul(&self, v: &i256) -> Option<Self> {
+ (*self).checked_mul(*v)
+ }
+}
+
+impl CheckedRem for i256 {
+ fn checked_rem(&self, v: &i256) -> Option<Self> {
+ (*self).checked_rem(*v)
+ }
+}
+
+impl WrappingAdd for i256 {
+ fn wrapping_add(&self, v: &Self) -> Self {
+ (*self).wrapping_add(*v)
+ }
+}
+
+impl WrappingSub for i256 {
+ fn wrapping_sub(&self, v: &Self) -> Self {
+ (*self).wrapping_sub(*v)
+ }
+}
+
+impl WrappingMul for i256 {
+ fn wrapping_mul(&self, v: &Self) -> Self {
+ (*self).wrapping_mul(*v)
+ }
+}
+
+impl WrappingNeg for i256 {
+ fn wrapping_neg(&self) -> Self {
+ (*self).wrapping_neg()
+ }
+}
+
+impl Zero for i256 {
+ fn zero() -> Self {
+ i256::ZERO
+ }
+
+ fn is_zero(&self) -> bool {
+ *self == i256::ZERO
+ }
+}
+
+impl One for i256 {
+ fn one() -> Self {
+ i256::ONE
+ }
+
+ fn is_one(&self) -> bool {
+ *self == i256::ONE
+ }
+}
+
+impl Num for i256 {
+ type FromStrRadixErr = ParseI256Error;
+
+ fn from_str_radix(str: &str, radix: u32) -> Result<Self,
Self::FromStrRadixErr> {
+ if radix == 10 {
+ str.parse()
+ } else {
+ // Parsing from non-10 baseseeĆ is not supported
+ Err(ParseI256Error {})
+ }
+ }
+}
+
+impl Signed for i256 {
+ fn abs(&self) -> Self {
+ self.wrapping_abs()
+ }
+
+ fn abs_sub(&self, other: &Self) -> Self {
+ if self > other {
+ self.wrapping_sub(other)
+ } else {
+ i256::ZERO
+ }
+ }
+
+ fn signum(&self) -> Self {
+ (*self).signum()
+ }
+
+ fn is_positive(&self) -> bool {
+ (*self).is_positive()
+ }
+
+ fn is_negative(&self) -> bool {
+ (*self).is_negative()
+ }
+}
+
+impl Bounded for i256 {
+ fn min_value() -> Self {
+ i256::MIN
+ }
+
+ fn max_value() -> Self {
+ i256::MAX
+ }
+}
+
#[cfg(all(test, not(miri)))] // llvm.x86.subborrow.64 not supported by MIRI
mod tests {
use super::*;
@@ -1337,6 +1472,83 @@ mod tests {
assert!(out.is_finite() && out.is_sign_negative());
}
+ #[test]
+ fn test_num_traits() {
+ let value = i256::from_i128(-5);
+ assert_eq!(
+ <i256 as CheckedNeg>::checked_neg(&value),
+ Some(i256::from(5))
+ );
+
+ assert_eq!(
+ <i256 as CheckedAdd>::checked_add(&value, &value),
+ Some(i256::from(-10))
+ );
+
+ assert_eq!(
+ <i256 as CheckedSub>::checked_sub(&value, &value),
+ Some(i256::from(0))
+ );
+
+ assert_eq!(
+ <i256 as CheckedMul>::checked_mul(&value, &value),
+ Some(i256::from(25))
+ );
+
+ assert_eq!(
+ <i256 as CheckedDiv>::checked_div(&value, &value),
+ Some(i256::from(1))
+ );
+
+ assert_eq!(
+ <i256 as CheckedRem>::checked_rem(&value, &value),
+ Some(i256::from(0))
+ );
+
+ assert_eq!(
+ <i256 as WrappingAdd>::wrapping_add(&value, &value),
+ i256::from(-10)
+ );
+
+ assert_eq!(
+ <i256 as WrappingSub>::wrapping_sub(&value, &value),
+ i256::from(0)
+ );
+
+ assert_eq!(
+ <i256 as WrappingMul>::wrapping_mul(&value, &value),
+ i256::from(25)
+ );
+
+ assert_eq!(<i256 as WrappingNeg>::wrapping_neg(&value), i256::from(5));
+
+ // A single check for wrapping behavior, rely on trait implementation
for others
+ let result = <i256 as WrappingAdd>::wrapping_add(&i256::MAX,
&i256::ONE);
+ assert_eq!(result, i256::MIN);
+
+ assert_eq!(<i256 as Signed>::abs(&value), i256::from(5));
+
+ assert_eq!(<i256 as One>::one(), i256::from(1));
+ assert_eq!(<i256 as Zero>::zero(), i256::from(0));
+
+ assert_eq!(<i256 as Bounded>::min_value(), i256::MIN);
+ assert_eq!(<i256 as Bounded>::max_value(), i256::MAX);
+ }
+
+ #[test]
+ fn test_numtraits_from_str_radix() {
+ assert_eq!(
+ i256::from_str_radix("123456789", 10).expect("parsed"),
+ i256::from(123456789)
+ );
+ assert_eq!(
+ i256::from_str_radix("0", 10).expect("parsed"),
+ i256::from(0)
+ );
+ assert!(i256::from_str_radix("abc", 10).is_err());
+ assert!(i256::from_str_radix("0", 16).is_err());
+ }
+
#[test]
fn test_leading_zeros() {
// Without high part