use crate::{UBigInt, BigInt, Ratio};
pub fn sqrt_iter(x: &Ratio, iter: usize) -> Ratio {
    Ratio::from_denom_and_numer(
        x.denom.shift_left(1 + iter).sqrt(),
        x.numer.shift_left(1 + iter).sqrt(),
    )
}
pub fn cbrt_iter(x: &Ratio, iter: usize) -> Ratio {
    if x.is_zero() {
        return Ratio::zero();
    }
    let mut approx = Ratio::from_denom_and_numer(
        BigInt::from_ubi(UBigInt::exp2_accurate(&x.denom.shift_left(1).log2_accurate().div_i32(3).to_ubi().unwrap()), false),
        BigInt::from_ubi(UBigInt::exp2_accurate(&x.numer.shift_left(1).log2_accurate().div_i32(3).to_ubi().unwrap()), x.is_neg())
    );
    let x_third = x.div_i32(3);
    let two_third = Ratio::from_denom_and_numer_i32(3, 2);
    for _ in 0..iter {
        let len_pre = approx.denom.len().min(approx.numer.len());
        approx = approx.mul_rat(&two_third).add_rat(&x_third.div_rat(&approx).div_rat(&approx));
        let len_post = approx.denom.len().min(approx.numer.len());
        if len_pre + 4 < len_post {
            approx.denom.shift_right_mut(len_post - len_pre - 4);
            approx.numer.shift_right_mut(len_post - len_pre - 4);
            approx.fit();
        }
    }
    approx
}
#[cfg(test)]
mod tests {
    use crate::{Ratio, sqrt_iter, cbrt_iter, pow_iter};
    use crate::utils::are_close;
    use crate::consts::RUN_ALL_TESTS;
    #[test]
    fn root_test() {
        assert_eq!("0.3162277660168379331998", sqrt_iter(&Ratio::from_string("0.1").unwrap(), 4).to_approx_string(24));
        assert_eq!("10", cbrt_iter(&Ratio::from_i32(1000), 3).to_approx_string(24));
        if !RUN_ALL_TESTS { return; }
        let numbers = vec![
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
            16, 25, 27, 28, 29, 30, 1000, 2000, 3000,
            4000, 5000, 6000, 0x1000, 0x2000, 0x3000,
            0x3001, 0x3002, 0x123456789
        ];
        let iter = 4;
        let accuracy = 0.005;
        let half = Ratio::from_denom_and_numer_i32(2, 1);
        let third = Ratio::from_denom_and_numer_i32(3, 1);
        for number in numbers.into_iter() {
            let number = Ratio::from_i128(number);
            let sqrt1 = sqrt_iter(&number, iter);
            let sqrt2 = pow_iter(&number, &half, iter * 4);
            if !are_close(&sqrt1, &sqrt2, accuracy) {
                panic!("{sqrt1}, {sqrt2}, {number}");
            }
            let cbrt1 = cbrt_iter(&number, iter);
            let cbrt2 = pow_iter(&number, &third, iter * 4);
            if !are_close(&cbrt1, &cbrt2, accuracy) {
                panic!("{cbrt1}, {cbrt2}, {number}");
            }
        }
    }
}