use std::ops::Mul;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct D256 {
pub words: [u64; 4],
}
impl D256 {
#[inline(always)]
pub const fn zero() -> Self {
D256 { words: [0, 0, 0, 0] }
}
#[inline(always)]
pub const fn from_words(words: [u64; 4]) -> Self {
D256 { words }
}
#[inline(always)]
pub const fn from_i128(value: i128) -> Self {
let words = if value < 0 {
[
value as u64,
(value >> 64) as u64,
u64::MAX,
u64::MAX,
]
} else {
[
value as u64,
(value >> 64) as u64,
0,
0,
]
};
D256 { words }
}
#[inline(always)]
pub fn as_i128(self) -> i128 {
((self.words[1] as i128) << 64) | (self.words[0] as i128)
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
self.words[0] == 0 && self.words[1] == 0 && self.words[2] == 0 && self.words[3] == 0
}
#[inline(always)]
pub fn is_negative(self) -> bool {
(self.words[3] & 0x8000_0000_0000_0000) != 0
}
#[inline(always)]
pub fn fits_in_i128(self) -> bool {
let is_negative = (self.words[1] as i64) < 0;
let expected_high = if is_negative { u64::MAX } else { 0 };
self.words[2] == expected_high && self.words[3] == expected_high
}
#[inline(always)]
pub const fn from_u128(value: u128) -> Self {
D256 {
words: [
value as u64,
(value >> 64) as u64,
0,
0,
]
}
}
#[inline(always)]
pub fn as_u128(self) -> u128 {
((self.words[1] as u128) << 64) | (self.words[0] as u128)
}
#[inline(always)]
pub const fn from_i64(value: i64) -> Self {
Self::from_i128(value as i128)
}
#[inline(always)]
pub const fn from_i32(value: i32) -> Self {
Self::from_i128(value as i128)
}
#[inline(always)]
pub const fn from_i16(value: i16) -> Self {
Self::from_i128(value as i128)
}
#[inline(always)]
pub const fn from_u8(value: u8) -> Self {
Self::from_i128(value as i128)
}
#[inline(always)]
pub fn abs(&self) -> Self {
if self.is_negative() {
negate_d256(*self)
} else {
*self
}
}
}
impl std::ops::Add for D256 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self {
let mut result = [0u64; 4];
let mut carry = 0u64;
for i in 0..4 {
let sum = (self.words[i] as u128) + (rhs.words[i] as u128) + (carry as u128);
result[i] = sum as u64;
carry = (sum >> 64) as u64;
}
D256 { words: result }
}
}
impl std::ops::Sub for D256 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self {
let mut result = [0u64; 4];
let mut borrow = 0u64;
for i in 0..4 {
let diff = (self.words[i] as u128).wrapping_sub((rhs.words[i] as u128) + (borrow as u128));
result[i] = diff as u64;
borrow = if diff > u128::MAX { 1 } else { 0 };
}
D256 { words: result }
}
}
impl Mul for D256 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self {
let result_d512 = self.mul_to_d512(rhs);
result_d512.as_d256()
}
}
impl std::ops::Div for D256 {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self {
divmod_d256_by_d256(self, rhs).0
}
}
impl std::ops::Rem for D256 {
type Output = Self;
#[inline(always)]
fn rem(self, rhs: Self) -> Self {
divmod_d256_by_d256(self, rhs).1
}
}
impl PartialOrd for D256 {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for D256 {
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let self_negative = self.is_negative();
let other_negative = other.is_negative();
match (self_negative, other_negative) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => {
for i in (0..4).rev() {
match self.words[i].cmp(&other.words[i]) {
std::cmp::Ordering::Equal => continue,
ordering => {
return if self_negative { ordering.reverse() } else { ordering };
}
}
}
std::cmp::Ordering::Equal
}
}
}
}
impl D256 {
#[inline(always)]
pub fn mul_to_d512(self, rhs: D256) -> super::d512::D512 {
mul_d256_to_d512(self, rhs)
}
}
#[inline(always)]
pub fn mul_i128_to_d256(a: i128, b: i128) -> D256 {
let a_neg = a < 0;
let b_neg = b < 0;
let result_neg = a_neg != b_neg;
let a_abs = a.unsigned_abs();
let b_abs = b.unsigned_abs();
let a_lo = a_abs as u64;
let a_hi = (a_abs >> 64) as u64;
let b_lo = b_abs as u64;
let b_hi = (b_abs >> 64) as u64;
let lo_lo = (a_lo as u128) * (b_lo as u128);
let lo_hi = (a_lo as u128) * (b_hi as u128);
let hi_lo = (a_hi as u128) * (b_lo as u128);
let hi_hi = (a_hi as u128) * (b_hi as u128);
let mut result = D256::zero();
result.words[0] = lo_lo as u64;
result.words[1] = (lo_lo >> 64) as u64;
let (sum1, carry1) = result.words[1].overflowing_add(lo_hi as u64);
result.words[1] = sum1;
result.words[2] = (lo_hi >> 64) as u64 + carry1 as u64;
let (sum2, carry2) = result.words[1].overflowing_add(hi_lo as u64);
result.words[1] = sum2;
let (sum3, carry3) = result.words[2].overflowing_add((hi_lo >> 64) as u64 + carry2 as u64);
result.words[2] = sum3;
result.words[3] = carry3 as u64;
let (sum4, carry4) = result.words[2].overflowing_add(hi_hi as u64);
result.words[2] = sum4;
result.words[3] += (hi_hi >> 64) as u64 + carry4 as u64;
if result_neg {
let mut borrow = 1u64;
for i in 0..4 {
let (val, b) = (!result.words[i]).overflowing_add(borrow);
result.words[i] = val;
borrow = b as u64;
}
}
result
}
#[inline(always)]
pub fn mul_d256_to_d512(a: D256, b: D256) -> super::d512::D512 {
use super::d512::D512;
let a_lo = ((a.words[1] as i128) << 64) | (a.words[0] as i128);
let a_hi = ((a.words[3] as i128) << 64) | (a.words[2] as i128);
let b_lo = ((b.words[1] as i128) << 64) | (b.words[0] as i128);
let b_hi = ((b.words[3] as i128) << 64) | (b.words[2] as i128);
let lo_lo = mul_i128_to_d256(a_lo, b_lo); let lo_hi = mul_i128_to_d256(a_lo, b_hi); let hi_lo = mul_i128_to_d256(a_hi, b_lo); let hi_hi = mul_i128_to_d256(a_hi, b_hi);
let mut result = D512::zero();
result.words[0] = lo_lo.words[0];
result.words[1] = lo_lo.words[1];
result.words[2] = lo_lo.words[2];
result.words[3] = lo_lo.words[3];
let mid_sum = D256::zero() + lo_hi + hi_lo;
let mut carry = 0u64;
for i in 0..4 {
let word_idx = i + 2; if word_idx < 8 {
let sum = (result.words[word_idx] as u128) + (mid_sum.words[i] as u128) + (carry as u128);
result.words[word_idx] = sum as u64;
carry = (sum >> 64) as u64;
}
}
carry = 0;
for i in 0..4 {
let word_idx = i + 4; if word_idx < 8 {
let sum = (result.words[word_idx] as u128) + (hi_hi.words[i] as u128) + (carry as u128);
result.words[word_idx] = sum as u64;
carry = (sum >> 64) as u64;
}
}
result
}
pub fn negate_d256(value: D256) -> D256 {
let mut result = D256 { words: [0; 4] };
let mut carry = 1u64;
for i in 0..4 {
let (negated, new_carry) = (!value.words[i]).overflowing_add(carry);
result.words[i] = negated;
carry = if new_carry { 1 } else { 0 };
}
result
}
pub fn divmod_d256_by_i128(dividend: D256, divisor: i128) -> (i128, i128) {
if divisor == 0 {
return (if dividend.words[3] as i64 >= 0 { i128::MAX } else { i128::MIN }, 0);
}
if dividend.fits_in_i128() {
let dividend_i128 = dividend.as_i128();
let quotient = dividend_i128 / divisor;
let remainder = dividend_i128 % divisor;
return (quotient, remainder);
}
let dividend_negative = dividend.is_negative();
let divisor_negative = divisor < 0;
let result_negative = dividend_negative != divisor_negative;
let abs_dividend = if dividend_negative {
negate_d256(dividend)
} else {
dividend
};
let abs_divisor = divisor.unsigned_abs() as u128;
let mut quotient = 0u128;
let mut remainder = 0u128;
for word_idx in (0..4).rev() {
remainder = (remainder << 64) + abs_dividend.words[word_idx] as u128;
let word_quotient = remainder / abs_divisor;
remainder = remainder % abs_divisor;
if word_idx < 2 { quotient = (quotient << 64) + word_quotient;
} else if word_quotient != 0 {
let saturated_quotient = if result_negative { i128::MIN } else { i128::MAX };
let saturated_remainder = if dividend_negative { -(remainder as i128) } else { remainder as i128 };
return (saturated_quotient, saturated_remainder);
}
}
let final_quotient = if quotient > i128::MAX as u128 {
if result_negative { i128::MIN } else { i128::MAX }
} else {
let signed_quotient = quotient as i128;
if result_negative { -signed_quotient } else { signed_quotient }
};
let final_remainder = if remainder > i128::MAX as u128 {
0
} else {
let signed_remainder = remainder as i128;
if dividend_negative { -signed_remainder } else { signed_remainder }
};
(final_quotient, final_remainder)
}
pub fn divmod_d256_by_d256(dividend: D256, divisor: D256) -> (D256, D256) {
if divisor.is_zero() {
let saturated_quotient = if dividend.is_negative() {
D256::from_i128(i128::MIN)
} else {
D256::from_i128(i128::MAX)
};
return (saturated_quotient, D256::zero());
}
if dividend.fits_in_i128() && divisor.fits_in_i128() {
let dividend_i128 = dividend.as_i128();
let divisor_i128 = divisor.as_i128();
let quotient = dividend_i128 / divisor_i128;
let remainder = dividend_i128 % divisor_i128;
return (D256::from_i128(quotient), D256::from_i128(remainder));
}
let dividend_negative = dividend.is_negative();
let divisor_negative = divisor.is_negative();
let quotient_negative = dividend_negative != divisor_negative;
let abs_dividend = if dividend_negative {
negate_d256(dividend)
} else {
dividend
};
let abs_divisor = if divisor_negative {
negate_d256(divisor)
} else {
divisor
};
let mut quotient_words = [0u64; 4];
let mut remainder = D256::zero();
for word_idx in (0..4).rev() {
for bit_idx in (0..64).rev() {
remainder = shift_left_d256_by_1(remainder);
let dividend_bit = (abs_dividend.words[word_idx] >> bit_idx) & 1;
remainder.words[0] |= dividend_bit;
if compare_d256_unsigned(remainder, abs_divisor) >= 0 {
remainder = subtract_d256_unsigned(remainder, abs_divisor);
quotient_words[word_idx] |= 1u64 << bit_idx;
}
}
}
let mut quotient = D256 { words: quotient_words };
if quotient_negative && !is_d256_zero(quotient) {
quotient = negate_d256(quotient);
}
if dividend_negative && !is_d256_zero(remainder) {
remainder = negate_d256(remainder);
}
(quotient, remainder)
}
#[inline(always)]
fn shift_left_d256_by_1(value: D256) -> D256 {
let mut result = [0u64; 4];
let mut carry = 0u64;
for i in 0..4 {
let word = value.words[i];
result[i] = (word << 1) | carry;
carry = word >> 63;
}
D256 { words: result }
}
#[inline(always)]
fn compare_d256_unsigned(a: D256, b: D256) -> i8 {
for i in (0..4).rev() {
if a.words[i] > b.words[i] {
return 1;
} else if a.words[i] < b.words[i] {
return -1;
}
}
0 }
#[inline(always)]
fn subtract_d256_unsigned(a: D256, b: D256) -> D256 {
let mut result = [0u64; 4];
let mut borrow = 0i128;
for i in 0..4 {
let diff = (a.words[i] as i128) - (b.words[i] as i128) - borrow;
if diff < 0 {
result[i] = (diff + (1i128 << 64)) as u64;
borrow = 1;
} else {
result[i] = diff as u64;
borrow = 0;
}
}
D256 { words: result }
}
#[inline(always)]
fn is_d256_zero(value: D256) -> bool {
value.words[0] == 0 && value.words[1] == 0 && value.words[2] == 0 && value.words[3] == 0
}
pub type DecimalD256 = D256;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_d256_basic_operations() {
let a = D256::from_i128(100);
let b = D256::from_i128(25);
let sum = a + b;
assert_eq!(sum.as_i128(), 125);
let diff = a - b;
assert_eq!(diff.as_i128(), 75);
let product = a * b;
assert_eq!(product.as_i128(), 2500);
}
#[test]
fn test_d256_multiplication() {
let a = 0x123456789ABCDEF0123456789ABCDEF_i128;
let b = 0x111111111111111111111111111111_i128;
let result = mul_i128_to_d256(a, b);
assert!(!result.is_zero());
}
#[test]
fn test_d256_decimal_specific() {
let decimal_val = D256::from_i128(1000000); let scale_10 = D256::from_i128(10);
let scaled = decimal_val * scale_10;
assert_eq!(scaled.as_i128(), 10000000); }
#[test]
fn test_d256_domain_separation() {
let d_val = D256::from_i128(1000);
assert_eq!(d_val.as_i128(), 1000);
let neg_val = negate_d256(d_val);
assert_eq!(neg_val.as_i128(), -1000);
}
#[test]
fn test_divmod_d256_by_i128() {
let dividend = D256::from_i128(1000);
let divisor = 10i128;
let (quotient, remainder) = divmod_d256_by_i128(dividend, divisor);
assert_eq!(quotient, 100);
assert_eq!(remainder, 0);
let dividend = D256::from_i128(1007);
let (quotient, remainder) = divmod_d256_by_i128(dividend, divisor);
assert_eq!(quotient, 100);
assert_eq!(remainder, 7);
}
}