use crate::constants::U32_MASK;
#[inline(always)]
pub(crate) fn rescale_internal(value: &mut [u32; 3], value_scale: &mut u32, new_scale: u32) {
if *value_scale == new_scale {
return;
}
if is_all_zero(value) {
*value_scale = new_scale;
return;
}
if *value_scale > new_scale {
let mut diff = *value_scale - new_scale;
let mut remainder = 0;
while diff > 0 {
if is_all_zero(value) {
*value_scale = new_scale;
return;
}
diff -= 1;
remainder = div_by_u32(value, 10);
}
if remainder >= 5 {
for part in value.iter_mut() {
let digit = u64::from(*part) + 1u64;
remainder = if digit > 0xFFFF_FFFF { 1 } else { 0 };
*part = (digit & 0xFFFF_FFFF) as u32;
if remainder == 0 {
break;
}
}
}
*value_scale = new_scale;
} else {
let mut diff = new_scale - *value_scale;
let mut working = [value[0], value[1], value[2]];
while diff > 0 && mul_by_10(&mut working) == 0 {
value.copy_from_slice(&working);
diff -= 1;
}
*value_scale = new_scale - diff;
}
}
pub(crate) fn add_by_internal(value: &mut [u32], by: &[u32]) -> u32 {
let mut carry: u64 = 0;
let vl = value.len();
let bl = by.len();
if vl >= bl {
let mut sum: u64;
for i in 0..bl {
sum = u64::from(value[i]) + u64::from(by[i]) + carry;
value[i] = (sum & U32_MASK) as u32;
carry = sum >> 32;
}
if vl > bl && carry > 0 {
for i in value.iter_mut().skip(bl) {
sum = u64::from(*i) + carry;
*i = (sum & U32_MASK) as u32;
carry = sum >> 32;
if carry == 0 {
break;
}
}
}
} else if vl + 1 == bl {
let mut sum: u64;
for i in 0..vl {
sum = u64::from(value[i]) + u64::from(by[i]) + carry;
value[i] = (sum & U32_MASK) as u32;
carry = sum >> 32;
}
if by[vl] > 0 {
carry += u64::from(by[vl]);
}
} else {
panic!("Internal error: add using incompatible length arrays. {} <- {}", vl, bl);
}
carry as u32
}
#[inline]
pub(crate) fn add_one_internal(value: &mut [u32]) -> u32 {
let mut carry: u64 = 1; let mut sum: u64;
for i in value.iter_mut() {
sum = (*i as u64) + carry;
*i = (sum & U32_MASK) as u32;
carry = sum >> 32;
}
carry as u32
}
pub(crate) fn sub_by_internal(value: &mut [u32], by: &[u32]) -> u32 {
let mut overflow = 0;
let vl = value.len();
let bl = by.len();
for i in 0..vl {
if i >= bl {
break;
}
let (lo, hi) = sub_part(value[i], by[i], overflow);
value[i] = lo;
overflow = hi;
}
overflow
}
fn sub_part(left: u32, right: u32, overflow: u32) -> (u32, u32) {
let part = 0x1_0000_0000u64 + u64::from(left) - (u64::from(right) + u64::from(overflow));
let lo = part as u32;
let hi = 1 - ((part >> 32) as u32);
(lo, hi)
}
#[inline]
pub(crate) fn mul_by_10(bits: &mut [u32; 3]) -> u32 {
let mut overflow = 0u64;
for b in bits.iter_mut() {
let result = u64::from(*b) * 10u64 + overflow;
let hi = (result >> 32) & U32_MASK;
let lo = (result & U32_MASK) as u32;
*b = lo;
overflow = hi;
}
overflow as u32
}
pub(crate) fn mul_by_u32(bits: &mut [u32], m: u32) -> u32 {
let mut overflow = 0;
for b in bits.iter_mut() {
let (lo, hi) = mul_part(*b, m, overflow);
*b = lo;
overflow = hi;
}
overflow
}
pub(crate) fn mul_part(left: u32, right: u32, high: u32) -> (u32, u32) {
let result = u64::from(left) * u64::from(right) + u64::from(high);
let hi = ((result >> 32) & U32_MASK) as u32;
let lo = (result & U32_MASK) as u32;
(lo, hi)
}
pub(crate) fn div_by_u32(bits: &mut [u32], divisor: u32) -> u32 {
if divisor == 0 {
panic!("Internal error: divide by zero");
} else if divisor == 1 {
0
} else {
let mut remainder = 0u32;
let divisor = u64::from(divisor);
for part in bits.iter_mut().rev() {
let temp = (u64::from(remainder) << 32) + u64::from(*part);
remainder = (temp % divisor) as u32;
*part = (temp / divisor) as u32;
}
remainder
}
}
#[inline]
pub(crate) fn shl1_internal(bits: &mut [u32], carry: u32) -> u32 {
let mut carry = carry;
for part in bits.iter_mut() {
let b = *part >> 31;
*part = (*part << 1) | carry;
carry = b;
}
carry
}
#[inline]
pub(crate) fn cmp_internal(left: &[u32; 3], right: &[u32; 3]) -> core::cmp::Ordering {
let left_hi: u32 = left[2];
let right_hi: u32 = right[2];
let left_lo: u64 = u64::from(left[1]) << 32 | u64::from(left[0]);
let right_lo: u64 = u64::from(right[1]) << 32 | u64::from(right[0]);
if left_hi < right_hi || (left_hi <= right_hi && left_lo < right_lo) {
core::cmp::Ordering::Less
} else if left_hi == right_hi && left_lo == right_lo {
core::cmp::Ordering::Equal
} else {
core::cmp::Ordering::Greater
}
}
#[inline]
pub(crate) fn is_all_zero(bits: &[u32]) -> bool {
bits.iter().all(|b| *b == 0)
}
#[cfg(test)]
mod test {
use super::*;
use crate::prelude::*;
#[test]
fn it_can_rescale_internal() {
fn extract(value: &str) -> ([u32; 3], u32) {
let v = Decimal::from_str(value).unwrap();
(v.mantissa_array3(), v.scale())
}
let tests = &[
("1", 0, "1"),
("1", 1, "1.0"),
("1", 5, "1.00000"),
("1", 10, "1.0000000000"),
("1", 20, "1.00000000000000000000"),
("0.6386554621848739495798319328", 27, "0.638655462184873949579831933"),
(
"843.65000000", 25, "843.6500000000000000000000000", ),
(
"843.65000000", 30, "843.6500000000000000000000000000", ),
];
for &(value_raw, new_scale, expected_value) in tests {
let (expected_value, _) = extract(expected_value);
let (mut value, mut value_scale) = extract(value_raw);
rescale_internal(&mut value, &mut value_scale, new_scale);
assert_eq!(value, expected_value);
}
}
#[test]
fn test_shl1_internal() {
struct TestCase {
given: [u32; 3],
given_carry: u32,
expected: [u32; 3],
expected_carry: u32,
}
let tests = [
TestCase {
given: [1, 0, 0],
given_carry: 0,
expected: [2, 0, 0],
expected_carry: 0,
},
TestCase {
given: [1, 0, 2147483648],
given_carry: 1,
expected: [3, 0, 0],
expected_carry: 1,
},
];
for case in &tests {
let mut test = [case.given[0], case.given[1], case.given[2]];
let carry = shl1_internal(&mut test, case.given_carry);
assert_eq!(
test, case.expected,
"Bits: {:?} << 1 | {}",
case.given, case.given_carry
);
assert_eq!(
carry, case.expected_carry,
"Carry: {:?} << 1 | {}",
case.given, case.given_carry
)
}
}
}