#[allow(unused_imports)]
use super::word::DecimalWord;
#[allow(unused_imports)]
use crate::fixed_point::width::WideInt;
#[allow(unused_imports)]
use super::decimal_compute::{decimal_compute_one, decimal_compute_add, decimal_compute_div, decimal_compute_halve, decimal_compute_cmp};
use crate::fixed_point::domains::symbolic::rational::rational_number::OverflowDetected;
pub fn decimal_sqrt<C: DecimalWord>(x: C) -> Result<C, OverflowDetected> {
if C::decimal_compute_is_negative(&x) {
return Err(OverflowDetected::DomainError);
}
if C::decimal_compute_is_zero(&x) {
return Ok(C::decimal_compute_zero());
}
let bits = C::sqrt_bit_length(&x);
let one = decimal_compute_one::<C>();
let y_init = if bits == 0 {
decimal_compute_halve(one)
} else {
let dp_bits = (C::DP as u32) * 33219 / 10000; let target_bits = (bits + dp_bits) / 2;
C::shift_one_left(target_bits)
};
let mut y = if is_compute_zero_or_neg(&y_init) {
decimal_compute_halve(one)
} else {
y_init
};
const MAX_ITERATIONS: u32 = 200;
let mut prev = C::decimal_compute_zero();
for _ in 0..MAX_ITERATIONS {
let x_div_y = decimal_compute_div(x, y)?;
let sum = decimal_compute_add(y, x_div_y);
let new_y = decimal_compute_halve(sum);
if decimal_compute_cmp(&new_y, &y) == std::cmp::Ordering::Equal {
return Ok(new_y);
}
if decimal_compute_cmp(&new_y, &prev) == std::cmp::Ordering::Equal {
return Ok(new_y);
}
prev = y;
y = new_y;
}
Ok(y)
}
fn is_compute_zero_or_neg<C: DecimalWord>(v: &C) -> bool {
C::decimal_compute_is_zero(v) || C::decimal_compute_is_negative(v)
}
#[cfg(test)]
mod tests {
use super::*;
type ComputeStorage = crate::fixed_point::I256;
#[allow(unused_imports)]
use super::super::decimal_compute::*;
use super::super::decimal_compute::decimal_compute_from_int;
use crate::fixed_point::i256::I256;
fn parse_decimal_str(s: &str) -> I256 {
let mut result = I256::from_i128(0);
let ten = I256::from_i128(10);
for ch in s.chars() {
let digit = ch.to_digit(10).expect("non-digit");
result = result * ten + I256::from_i128(digit as i128);
}
result
}
#[test]
fn sqrt_zero() {
let result = decimal_sqrt(decimal_compute_zero::<ComputeStorage>()).unwrap();
assert_eq!(result, decimal_compute_zero::<ComputeStorage>());
}
#[test]
fn sqrt_one() {
let result = decimal_sqrt(decimal_compute_one::<ComputeStorage>()).unwrap();
assert_eq!(result, decimal_compute_one::<ComputeStorage>());
}
#[test]
fn sqrt_four_is_two() {
let four = decimal_compute_from_int::<ComputeStorage>(4);
let two = decimal_compute_from_int::<ComputeStorage>(2);
let result = decimal_sqrt(four).unwrap();
assert_eq!(result, two);
}
#[test]
fn sqrt_nine_is_three() {
let nine = decimal_compute_from_int::<ComputeStorage>(9);
let three = decimal_compute_from_int::<ComputeStorage>(3);
let result = decimal_sqrt(nine).unwrap();
assert_eq!(result, three);
}
#[test]
fn sqrt_two_mpmath() {
let two = decimal_compute_from_int::<ComputeStorage>(2);
let result = decimal_sqrt(two).unwrap();
let expected = parse_decimal_str("141421356237309504880168872420969807857");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(1000);
assert!(
diff < tolerance,
"sqrt(2) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
#[test]
fn sqrt_three_mpmath() {
let three = decimal_compute_from_int::<ComputeStorage>(3);
let result = decimal_sqrt(three).unwrap();
let expected = parse_decimal_str("173205080756887729352744634150587236694");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(1000);
assert!(
diff < tolerance,
"sqrt(3) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
}