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); }
21 if self.value == 0 {
22 return Ok(SoroNum::new(0, SCALE_OUT)); }
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 let mut x = I256::from_i128(env, self.value)
31 .mul(&ten_pow_calc_scale)
32 .div(&two);
33
34 for _ in 0..30 {
36 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 break;
46 }
47 }
48
49 let scale_factor = I256::from_i128(env, 10).pow((CALC_SCALE - SCALE_OUT) / 2);
51 let scaled_result = x.div(&scale_factor);
52
53 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}