1use crate::cbig::CBig;
4use crate::repr::{combine_parts, exact, CfpResult, Context};
5use dashu_base::Sign;
6use dashu_float::round::Round;
7use dashu_float::{FBig, Repr};
8use dashu_int::Word;
9
10const SQRT_GUARD: usize = 12;
13
14fn signed_inf<const B: Word>(sign: Sign) -> Repr<B> {
16 match sign {
17 Sign::Positive => Repr::infinity(),
18 Sign::Negative => Repr::neg_infinity(),
19 }
20}
21
22impl<R: Round> Context<R> {
23 pub fn sqrt<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
29 if let Some(special) = sqrt_special(z, *self) {
30 return special;
31 }
32
33 let gctx = self.guard(SQRT_GUARD);
34 let p = self.precision();
35 let two = FBig::from_repr(Repr::new(2.into(), 0), gctx);
36 let x = z.re();
37 let y = z.im();
38
39 let r = gctx.hypot(x, y)?.value();
43 let (a, b) = if x.sign() != Sign::Negative {
44 let rpx = gctx.add(r.repr(), x)?.value();
46 let half_rpx = gctx.div(rpx.repr(), two.repr())?.value();
47 let a = gctx.sqrt(half_rpx.repr())?.value();
48 let two_a = gctx.mul(two.repr(), a.repr())?.value();
49 let b = gctx.div(y, two_a.repr())?.value();
50 (a, b)
51 } else {
52 let rmx = gctx.sub(r.repr(), x)?.value(); let half_rmx = gctx.div(rmx.repr(), two.repr())?.value();
55 let b_mag = gctx.sqrt(half_rmx.repr())?.value();
56 let b = if y.sign() == Sign::Negative {
57 -b_mag
58 } else {
59 b_mag
60 };
61 let two_b = gctx.mul(two.repr(), b.repr())?.value();
62 let a = gctx.div(y, two_b.repr())?.value();
63 (a, b)
64 };
65 let re = a.with_precision(p);
66 let im = b.with_precision(p);
67 Ok(combine_parts(re, im))
68 }
69}
70
71impl<R: Round, const B: Word> CBig<R, B> {
72 #[inline]
78 pub fn sqrt(&self) -> Self {
79 self.context().unwrap_cfp(self.context().sqrt(self))
80 }
81}
82
83fn sqrt_special<R: Round, const B: Word>(
85 z: &CBig<R, B>,
86 ctx: Context<R>,
87) -> Option<CfpResult<R, B>> {
88 let f = ctx.float();
89 if z.is_zero() {
91 return Some(Ok(exact(
92 FBig::from_repr(z.re().clone(), f),
93 FBig::from_repr(z.im().clone(), f),
94 )));
95 }
96 if !z.is_infinite() {
97 return None;
98 }
99
100 let x_pos_inf = z.re().is_infinite() && z.re().sign() == Sign::Positive;
101 let y_sign = z.im().sign();
102
103 let (re, im) = if z.im().is_infinite() {
104 (Repr::infinity(), signed_inf::<B>(y_sign))
108 } else if x_pos_inf {
109 (
111 Repr::infinity(),
112 if y_sign == Sign::Negative {
113 Repr::neg_zero()
114 } else {
115 Repr::zero()
116 },
117 )
118 } else {
119 (Repr::zero(), signed_inf::<B>(y_sign))
121 };
122 Some(Ok(exact(FBig::from_repr(re, f), FBig::from_repr(im, f))))
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use dashu_float::round::mode;
129
130 type C = CBig<mode::HalfAway, 10>;
131 type F = FBig<mode::HalfAway, 10>;
132
133 fn c(re: i32, im: i32) -> C {
134 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
135 CBig::from_parts(mk(re), mk(im))
136 }
137
138 #[test]
139 fn sqrt_basic() {
140 let z = c(3, 4);
142 let s = z.sqrt();
143 let chk = &s * &s;
144 assert!(chk == z);
145 }
146
147 #[test]
148 fn sqrt_infinite_imaginary_dominates() {
149 let ctx = Context::<mode::HalfAway>::new(53);
152 let cases = [
153 C::new(Repr::infinity(), Repr::infinity(), ctx),
154 C::new(Repr::neg_infinity(), Repr::infinity(), ctx),
155 ];
156 for z in cases {
157 let s = z.sqrt();
158 assert!(s.re().is_infinite() && s.re().sign() == Sign::Positive);
159 assert!(s.im().is_infinite() && s.im().sign() == Sign::Positive);
160 }
161 }
162
163 #[test]
164 fn sqrt_real() {
165 let z = c(9, 0);
167 let s = z.sqrt();
168 assert!(s == c(3, 0));
169 }
170
171 #[test]
172 fn sqrt_negative_real_is_imaginary() {
173 let z = c(-4, 0);
175 let s = z.sqrt();
176 assert!(s.re().significand().is_zero());
177 assert_eq!(s.im().significand(), &2.into());
178 }
179
180 #[test]
181 fn sqrt_conj_identity() {
182 let z = c(3, 4);
184 let lhs = z.conj().sqrt();
185 let rhs = z.sqrt().conj();
186 assert!(lhs == rhs);
187 }
188
189 #[test]
190 fn sqrt_zero() {
191 let s = C::ZERO.sqrt();
192 assert!(s.is_zero());
193 }
194
195 #[test]
196 fn sqrt_pos_infinity() {
197 let inf = CBig::from(F::INFINITY);
198 let s = inf.sqrt();
199 assert!(s.re().is_infinite());
200 assert!(s.im().is_pos_zero());
201 }
202}