soroban_math/
root.rs

1use crate::{error::ArithmeticError, SoroNum};
2use soroban_sdk::{Env, I256};
3
4pub trait Root {
5    fn sqrt<const CALC_SCALE: u32, const SCALE_OUT: u32>(
6        &self,
7        env: &Env,
8    ) -> Result<Self, ArithmeticError>
9    where
10        Self: Sized;
11}
12
13impl Root for SoroNum<i128> {
14    fn sqrt<const CALC_SCALE: u32, const SCALE_OUT: u32>(
15        &self,
16        env: &Env,
17    ) -> Result<Self, ArithmeticError> {
18        if self.value < 0 {
19            return Err(ArithmeticError::DomainError); // Square root of a negative number is not defined
20        }
21        if self.value == 0 {
22            return Ok(SoroNum::new(0, SCALE_OUT)); // Square root of 0 is 0
23        }
24
25        let two = I256::from_i128(env, 2);
26        let ten_pow_calc_scale = I256::from_i128(env, 10).pow(CALC_SCALE - SCALE_OUT);
27        let ten_pow_2_calc_scale = I256::from_i128(env, 10).pow((CALC_SCALE - SCALE_OUT) * 2);
28
29        // Initial guess for Newton's method
30        let mut x = I256::from_i128(env, self.value)
31            .mul(&ten_pow_calc_scale)
32            .div(&two);
33
34        // Newton's iteration
35        for _ in 0..30 {
36            // Limit to a reasonable number of iterations
37            let x_old = x.clone();
38            let temp = I256::from_i128(env, self.value)
39                .mul(&ten_pow_2_calc_scale)
40                .div(&x);
41            x = x.add(&temp).div(&two);
42
43            if x == x_old {
44                // Convergence check
45                break;
46            }
47        }
48
49        // Calculate the scale factor for the final result
50        let scale_factor = I256::from_i128(env, 10).pow((CALC_SCALE - SCALE_OUT) / 2);
51        let scaled_result = x.div(&scale_factor);
52
53        // Check for overflow and return the result
54        if scaled_result > I256::from_i128(env, i128::MAX)
55            || scaled_result < I256::from_i128(env, i128::MIN)
56        {
57            Err(ArithmeticError::Overflow)
58        } else {
59            Ok(SoroNum {
60                value: scaled_result.to_i128().unwrap(),
61                scale: SCALE_OUT,
62            })
63        }
64    }
65}