1use crate::cbig::CBig;
4use crate::repr::{exact, CfpResult, Context};
5use core::ops::Neg;
6use dashu_base::Sign;
7use dashu_float::round::Round;
8use dashu_float::{FBig, FpResult, Repr};
9use dashu_int::Word;
10
11const NORM_GUARD: usize = 8;
14
15const ABS_GUARD: usize = 8;
18
19impl<R: Round, const B: Word> CBig<R, B> {
20 #[inline]
22 pub fn conj(&self) -> Self {
23 self.context.unwrap_cfp(self.context.conj(self))
24 }
25
26 #[inline]
29 pub fn proj(&self) -> Self {
30 self.context.unwrap_cfp(self.context.proj(self))
31 }
32
33 #[inline]
36 pub fn mul_i(&self, negative: bool) -> Self {
37 self.context.unwrap_cfp(self.context.mul_i(self, negative))
38 }
39
40 #[inline]
43 pub fn norm(&self) -> FBig<R, B> {
44 self.context.float().unwrap_fp(self.context.norm(self))
45 }
46
47 #[inline]
55 pub fn abs(&self) -> FBig<R, B> {
56 self.context.float().unwrap_fp(self.context.abs(self))
57 }
58
59 #[inline]
62 pub fn arg(&self) -> FBig<R, B> {
63 self.context.float().unwrap_fp(self.context.arg(self, None))
64 }
65}
66
67impl<R: Round, const B: Word> Neg for CBig<R, B> {
68 type Output = CBig<R, B>;
69 #[inline]
70 fn neg(self) -> Self::Output {
71 self.context().unwrap_cfp(self.context().neg(&self))
72 }
73}
74
75impl<R: Round, const B: Word> Neg for &CBig<R, B> {
76 type Output = CBig<R, B>;
77 #[inline]
78 fn neg(self) -> Self::Output {
79 self.context().unwrap_cfp(self.context().neg(self))
80 }
81}
82
83impl<R: Round> Context<R> {
84 pub fn neg<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
86 Ok(exact(
87 FBig::from_repr(-z.re.clone(), self.float()),
88 FBig::from_repr(-z.im.clone(), self.float()),
89 ))
90 }
91
92 pub fn conj<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
94 Ok(exact(
95 FBig::from_repr(z.re.clone(), self.float()),
96 FBig::from_repr(-z.im.clone(), self.float()),
97 ))
98 }
99
100 pub fn proj<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
102 if z.is_infinite() {
103 let im = if z.im().sign() == Sign::Negative {
105 Repr::neg_zero()
106 } else {
107 Repr::zero()
108 };
109 Ok(exact(
110 FBig::from_repr(Repr::infinity(), self.float()),
111 FBig::from_repr(im, self.float()),
112 ))
113 } else {
114 Ok(exact(
115 FBig::from_repr(z.re.clone(), self.float()),
116 FBig::from_repr(z.im.clone(), self.float()),
117 ))
118 }
119 }
120
121 pub fn mul_i<const B: Word>(&self, z: &CBig<R, B>, negative: bool) -> CfpResult<R, B> {
123 let (re, im) = if negative {
124 (z.im.clone(), -z.re.clone())
126 } else {
127 (-z.im.clone(), z.re.clone())
129 };
130 Ok(exact(FBig::from_repr(re, self.float()), FBig::from_repr(im, self.float())))
131 }
132
133 pub fn norm<const B: Word>(&self, z: &CBig<R, B>) -> FpResult<FBig<R, B>> {
136 if z.is_infinite() {
137 return Ok(dashu_base::Approximation::Exact(FBig::from_repr(
138 Repr::infinity(),
139 self.float(),
140 )));
141 }
142 let gctx = self.guard(NORM_GUARD);
143 let re2 = gctx.unwrap_fp(gctx.sqr(z.re()));
144 let im2 = gctx.unwrap_fp(gctx.sqr(z.im()));
145 let n = gctx.unwrap_fp(gctx.add(re2.repr(), im2.repr()));
146 Ok(n.with_precision(self.precision()))
147 }
148
149 pub fn arg<const B: Word>(
152 &self,
153 z: &CBig<R, B>,
154 cache: Option<&mut dashu_float::ConstCache>,
155 ) -> FpResult<FBig<R, B>> {
156 self.float().atan2(z.im(), z.re(), cache)
157 }
158
159 pub fn abs<const B: Word>(&self, z: &CBig<R, B>) -> FpResult<FBig<R, B>> {
166 let gctx = self.guard(ABS_GUARD);
167 let h = gctx.hypot(z.re(), z.im())?;
168 Ok(h.value().with_precision(self.precision()))
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use dashu_float::round::mode;
176
177 type C = CBig<mode::HalfAway, 10>;
178
179 #[test]
180 fn neg_and_conj() {
181 let z = C::from_parts(3.into(), 4.into());
182 let n = -&z; assert_eq!(n.re().significand(), &(-3i32).into());
184 assert_eq!(n.im().significand(), &(-4i32).into());
185 let c = z.conj();
186 assert_eq!(c.re().significand(), &3.into());
187 assert_eq!(c.im().significand(), &(-4i32).into());
188 }
189
190 #[test]
191 fn mul_i_rotation() {
192 let z = C::from_parts(3.into(), 4.into());
193 let zi = z.mul_i(false);
195 assert_eq!(zi.re().significand(), &(-4i32).into());
196 assert_eq!(zi.im().significand(), &3.into());
197 let zni = z.mul_i(true);
199 assert_eq!(zni.re().significand(), &4.into());
200 assert_eq!(zni.im().significand(), &(-3i32).into());
201 let id = z.mul_i(false).mul_i(false).mul_i(false).mul_i(false);
203 assert!(id == z);
204 }
205
206 #[test]
207 fn proj_finite_unchanged() {
208 let z = C::from_parts(3.into(), 4.into());
209 assert!(z.proj() == z);
210 }
211
212 #[test]
213 fn proj_infinite_is_riemann_point() {
214 let inf = C::new(Repr::infinity(), Repr::<10>::new(5.into(), 0), Context::new(53));
215 let p = inf.proj();
216 assert!(p.re().is_infinite());
217 assert_eq!(p.re().sign(), Sign::Positive);
218 assert!(p.im().is_pos_zero());
219 }
220
221 #[test]
222 fn norm_of_3_4_is_25() {
223 type F = FBig<mode::HalfAway, 10>;
225 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
226 let z = C::from_parts(mk(3), mk(4));
227 let n = z.norm();
228 assert_eq!(n.repr().significand(), &25.into());
229 }
230
231 #[test]
232 fn arg_of_1_1_is_pi_quarter() {
233 type F = FBig<mode::HalfAway, 10>;
234 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
235 let z = C::from_parts(mk(1), mk(1));
236 let a = z.arg();
237 assert!(a > F::ZERO);
239 assert!(a < F::ONE);
240 }
241}