use crate::{error::ArithmeticError, SoroNum};
use soroban_sdk::{Env, I256};
pub trait Root {
fn sqrt<const CALC_SCALE: u32, const SCALE_OUT: u32>(
&self,
env: &Env,
) -> Result<Self, ArithmeticError>
where
Self: Sized;
}
impl Root for SoroNum<i128> {
fn sqrt<const CALC_SCALE: u32, const SCALE_OUT: u32>(
&self,
env: &Env,
) -> Result<Self, ArithmeticError> {
if self.value < 0 {
return Err(ArithmeticError::DomainError); }
if self.value == 0 {
return Ok(SoroNum::new(0, SCALE_OUT)); }
let two = I256::from_i128(env, 2);
let ten_pow_calc_scale = I256::from_i128(env, 10).pow(CALC_SCALE - SCALE_OUT);
let ten_pow_2_calc_scale = I256::from_i128(env, 10).pow((CALC_SCALE - SCALE_OUT) * 2);
let mut x = I256::from_i128(env, self.value)
.mul(&ten_pow_calc_scale)
.div(&two);
for _ in 0..30 {
let x_old = x.clone();
let temp = I256::from_i128(env, self.value)
.mul(&ten_pow_2_calc_scale)
.div(&x);
x = x.add(&temp).div(&two);
if x == x_old {
break;
}
}
let scale_factor = I256::from_i128(env, 10).pow((CALC_SCALE - SCALE_OUT) / 2);
let scaled_result = x.div(&scale_factor);
if scaled_result > I256::from_i128(env, i128::MAX)
|| scaled_result < I256::from_i128(env, i128::MIN)
{
Err(ArithmeticError::Overflow)
} else {
Ok(SoroNum {
value: scaled_result.to_i128().unwrap(),
scale: SCALE_OUT,
})
}
}
}