use crate::{error::MathError, integer::Z, rational::Q};
impl Q {
pub fn sqrt(&self) -> Q {
let root_numerator = self.get_numerator().sqrt();
let root_denominator = self.get_denominator().sqrt();
root_numerator / root_denominator
}
pub fn sqrt_precision(&self, precision: &Z) -> Result<Q, MathError> {
let root_numerator = self.get_numerator().sqrt_precision(precision)?;
let root_denominator = self.get_denominator().sqrt_precision(precision)?;
Ok(root_numerator / root_denominator)
}
}
#[cfg(test)]
mod test_sqrt {
use crate::{integer::Z, rational::Q, traits::Pow};
#[test]
fn zero() {
let zero = Q::ZERO;
let res = zero.sqrt();
assert_eq!(res, Q::ZERO);
}
#[test]
fn negative_value() {
let value = Q::from(-10);
let res = value.sqrt_precision(&Z::from(10));
assert!(res.is_err());
}
#[test]
#[should_panic]
fn negative_value_precision() {
let value = Q::from(-10);
let _ = value.sqrt();
}
#[test]
fn square_rationals() {
let values = vec![
Q::ZERO,
Q::from((1, 3)),
Q::from((10, 3)),
Q::from((100000, 3)),
Q::from((u64::MAX, 3)),
Q::from((u64::MAX, u64::MAX - 1)),
];
for value in values {
let value_sqr = &value * &value;
let root = value_sqr.sqrt();
assert_eq!(value, root);
}
}
#[test]
fn precision_correct() {
let values = vec![
Q::from((1, 3)),
Q::from((10, 3)),
Q::from((100000, 3)),
Q::from(u64::MAX),
Q::from((1, u64::MAX)),
Q::from((u64::MAX, u64::MAX - 1)) * Q::from(u64::MAX),
];
let precisions = vec![
Z::from(1),
Z::from(10),
Z::from(100000),
Z::from(u64::MAX),
Z::from(u64::MAX).pow(5).unwrap(),
];
for value in values {
let num: f64 = value.get_numerator().to_string().parse().unwrap();
let den: f64 = value.get_denominator().to_string().parse().unwrap();
let root_float: f64 = (num / den).sqrt();
let root_float = Q::from(root_float);
for precision in &precisions {
let root = value.sqrt_precision(precision).unwrap();
let p_z = Q::from((1, 2 * precision));
let p_q = &root_float * (root_float.get_denominator() + 1) * &p_z
/ (root_float.get_denominator() - &p_z);
let root_squared = &root * &root;
let error_after_squaring = &root_squared - &value;
let precision_bound_after_squaring = 2 * &p_q * &root_float + &p_q * &p_q;
assert!(error_after_squaring > (-1 * &precision_bound_after_squaring));
assert!(error_after_squaring < precision_bound_after_squaring);
}
}
}
}