use crate::constants::{MAX_I32_SCALE, MAX_PRECISION_I32, POWERS_10}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::{Buf12, Buf16, Buf24, Dec64}; pub(crate) fn rem_impl(d1: &Decimal, d2: &Decimal) -> CalculationResult { if d2.is_zero() { return CalculationResult::DivByZero; } if d1.is_zero() { return CalculationResult::Ok(Decimal::ZERO); } // We handle the structs a bit different here. Firstly, we ignore both the sign/scale of d2. // This is because during a remainder operation we do not care about the sign of the divisor // and only concern ourselves with that of the dividend. let mut d1 = Dec64::new(d1); let d2_scale = d2.scale(); let mut d2 = Buf12::from_decimal(d2); let cmp = crate::ops::cmp::cmp_internal( &d1, &Dec64 { negative: d1.negative, scale: d2_scale, hi: d2.hi(), low64: d2.low64(), }, ); match cmp { core::cmp::Ordering::Equal => { // Same numbers meaning that remainder is zero return CalculationResult::Ok(Decimal::ZERO); } core::cmp::Ordering::Less => { // d1 < d2, e.g. 1/2. This means that the result is the value of d1 return CalculationResult::Ok(d1.to_decimal()); } core::cmp::Ordering::Greater => {} } // At this point we know that the dividend > divisor and that they are both non-zero. let mut scale = d1.scale as i32 - d2_scale as i32; if scale > 0 { // Scale up the divisor loop { let power = if scale >= MAX_I32_SCALE { POWERS_10[9] } else { POWERS_10[scale as usize] } as u64; let mut tmp = d2.lo() as u64 * power; d2.set_lo(tmp as u32); tmp >>= 32; tmp = tmp.wrapping_add((d2.mid() as u64 + ((d2.hi() as u64) << 32)) * power); d2.set_mid(tmp as u32); d2.set_hi((tmp >> 32) as u32); // Keep scaling if there is more to go scale -= MAX_I32_SCALE; if scale <= 0 { break; } } scale = 0; } loop { // If the dividend is smaller than the divisor then try to scale that up first if scale < 0 { let mut quotient = Buf12 { data: [d1.lo(), d1.mid(), d1.hi], }; loop { // Figure out how much we can scale by let power_scale; if let Some(u) = quotient.find_scale(MAX_PRECISION_I32 + scale) { if u >= POWERS_10.len() { power_scale = 9; } else { power_scale = u; } } else { return CalculationResult::Overflow; }; if power_scale == 0 { break; } let power = POWERS_10[power_scale] as u64; scale += power_scale as i32; let mut tmp = quotient.data[0] as u64 * power; quotient.data[0] = tmp as u32; tmp >>= 32; quotient.set_high64(tmp.wrapping_add(quotient.high64().wrapping_mul(power))); if power_scale != 9 { break; } if scale >= 0 { break; } } d1.low64 = quotient.low64(); d1.hi = quotient.data[2]; d1.scale = d2_scale; } // if the high portion is empty then return the modulus of the bottom portion if d1.hi == 0 { d1.low64 %= d2.low64(); return CalculationResult::Ok(d1.to_decimal()); } else if (d2.mid() | d2.hi()) == 0 { let mut tmp = d1.high64(); tmp = ((tmp % d2.lo() as u64) << 32) | (d1.lo() as u64); d1.low64 = tmp % d2.lo() as u64; d1.hi = 0; } else { // Divisor is > 32 bits return rem_full(&d1, &d2, scale); } if scale >= 0 { break; } } CalculationResult::Ok(d1.to_decimal()) } fn rem_full(d1: &Dec64, d2: &Buf12, scale: i32) -> CalculationResult { let mut scale = scale; // First normalize the divisor let shift = if d2.hi() == 0 { d2.mid().leading_zeros() } else { d2.hi().leading_zeros() }; let mut buffer = Buf24::zero(); let mut overflow = 0u32; buffer.set_low64(d1.low64 << shift); buffer.set_mid64(((d1.mid() as u64).wrapping_add((d1.hi as u64) << 32)) >> (32 - shift)); let mut upper = 3; // We start at 3 due to bit shifting while scale < 0 { let power = if -scale >= MAX_I32_SCALE { POWERS_10[9] } else { POWERS_10[-scale as usize] } as u64; let mut tmp64 = buffer.data[0] as u64 * power; buffer.data[0] = tmp64 as u32; for (index, part) in buffer.data.iter_mut().enumerate().skip(1) { if index > upper { break; } tmp64 >>= 32; tmp64 = tmp64.wrapping_add((*part as u64).wrapping_mul(power)); *part = tmp64 as u32; } // If we have overflow then also process that if upper == 6 { tmp64 >>= 32; tmp64 = tmp64.wrapping_add((overflow as u64).wrapping_mul(power)); overflow = tmp64 as u32; } // Make sure the high bit is not set if tmp64 > 0x7FFF_FFFF { upper += 1; if upper > 5 { overflow = (tmp64 >> 32) as u32; } else { buffer.data[upper] = (tmp64 >> 32) as u32; } } scale += MAX_I32_SCALE; } // TODO: Optimize slice logic let mut tmp = Buf16::zero(); let divisor = d2.low64() << shift; if d2.hi() == 0 { // Do some division if upper == 6 { upper -= 1; tmp.data = [buffer.data[4], buffer.data[5], overflow, 0]; tmp.partial_divide_64(divisor); buffer.data[4] = tmp.data[0]; buffer.data[5] = tmp.data[1]; } if upper == 5 { upper -= 1; tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], 0]; tmp.partial_divide_64(divisor); buffer.data[3] = tmp.data[0]; buffer.data[4] = tmp.data[1]; buffer.data[5] = tmp.data[2]; } if upper == 4 { tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], 0]; tmp.partial_divide_64(divisor); buffer.data[2] = tmp.data[0]; buffer.data[3] = tmp.data[1]; buffer.data[4] = tmp.data[2]; } tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], 0]; tmp.partial_divide_64(divisor); buffer.data[1] = tmp.data[0]; buffer.data[2] = tmp.data[1]; buffer.data[3] = tmp.data[2]; tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], 0]; tmp.partial_divide_64(divisor); buffer.data[0] = tmp.data[0]; buffer.data[1] = tmp.data[1]; buffer.data[2] = tmp.data[2]; let low64 = buffer.low64() >> shift; CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, 0, d1.negative, d1.scale, )) } else { let divisor_low64 = divisor; let divisor = Buf12 { data: [ divisor_low64 as u32, (divisor_low64 >> 32) as u32, (((d2.mid() as u64) + ((d2.hi() as u64) << 32)) >> (32 - shift)) as u32, ], }; // Do some division if upper == 6 { upper -= 1; tmp.data = [buffer.data[3], buffer.data[4], buffer.data[5], overflow]; tmp.partial_divide_96(&divisor); buffer.data[3] = tmp.data[0]; buffer.data[4] = tmp.data[1]; buffer.data[5] = tmp.data[2]; } if upper == 5 { upper -= 1; tmp.data = [buffer.data[2], buffer.data[3], buffer.data[4], buffer.data[5]]; tmp.partial_divide_96(&divisor); buffer.data[2] = tmp.data[0]; buffer.data[3] = tmp.data[1]; buffer.data[4] = tmp.data[2]; buffer.data[5] = tmp.data[3]; } if upper == 4 { tmp.data = [buffer.data[1], buffer.data[2], buffer.data[3], buffer.data[4]]; tmp.partial_divide_96(&divisor); buffer.data[1] = tmp.data[0]; buffer.data[2] = tmp.data[1]; buffer.data[3] = tmp.data[2]; buffer.data[4] = tmp.data[3]; } tmp.data = [buffer.data[0], buffer.data[1], buffer.data[2], buffer.data[3]]; tmp.partial_divide_96(&divisor); buffer.data[0] = tmp.data[0]; buffer.data[1] = tmp.data[1]; buffer.data[2] = tmp.data[2]; buffer.data[3] = tmp.data[3]; let low64 = (buffer.low64() >> shift) + ((buffer.data[2] as u64) << (32 - shift) << 32); CalculationResult::Ok(Decimal::from_parts( low64 as u32, (low64 >> 32) as u32, buffer.data[2] >> shift, d1.negative, d1.scale, )) } }