oxinum_complex/native/
convert.rs1use core::convert::TryFrom;
9use oxinum_core::{OxiNumError, OxiNumResult};
10use oxinum_float::native::BigFloat;
11
12use super::BigComplex;
13
14impl From<(BigFloat, BigFloat)> for BigComplex {
15 #[inline]
17 fn from((re, im): (BigFloat, BigFloat)) -> Self {
18 BigComplex::from_parts(re, im)
19 }
20}
21
22impl From<BigFloat> for BigComplex {
23 #[inline]
25 fn from(re: BigFloat) -> Self {
26 BigComplex::from_real(re)
27 }
28}
29
30impl BigComplex {
31 pub fn to_f64_parts(&self) -> (f64, f64) {
47 (self.re().to_f64(), self.im().to_f64())
48 }
49}
50
51impl TryFrom<&BigComplex> for (f64, f64) {
59 type Error = OxiNumError;
60
61 fn try_from(z: &BigComplex) -> OxiNumResult<(f64, f64)> {
62 let (re, im) = z.to_f64_parts();
63 if !re.is_finite() || !im.is_finite() {
64 return Err(OxiNumError::Overflow(
65 "component is non-finite (infinite or NaN) after f64 projection".into(),
66 ));
67 }
68 Ok((re, im))
69 }
70}
71
72#[cfg(feature = "num-complex")]
77mod num_complex_impls {
78 use num_complex::Complex;
79 use oxinum_float::native::{BigFloat, RoundingMode};
80
81 use super::BigComplex;
82
83 const DEFAULT_PREC: u32 = 80;
84 const DEFAULT_MODE: RoundingMode = RoundingMode::HalfEven;
85
86 impl From<Complex<f64>> for BigComplex {
94 fn from(z: Complex<f64>) -> Self {
95 BigComplex::from_f64(z.re, z.im, DEFAULT_PREC)
96 .expect("Complex<f64> → BigComplex: components must be finite (no NaN/Inf)")
97 }
98 }
99
100 impl From<Complex<i64>> for BigComplex {
103 fn from(z: Complex<i64>) -> Self {
104 let re = BigFloat::from_i64(z.re, DEFAULT_PREC, DEFAULT_MODE);
105 let im = BigFloat::from_i64(z.im, DEFAULT_PREC, DEFAULT_MODE);
106 BigComplex::from_parts(re, im)
107 }
108 }
109
110 impl From<&BigComplex> for Complex<f64> {
113 fn from(z: &BigComplex) -> Self {
114 let (re, im) = z.to_f64_parts();
115 Complex::new(re, im)
116 }
117 }
118}
119
120#[cfg(test)]
125mod tests {
126 use super::*;
127 use oxinum_float::native::RoundingMode;
128
129 #[test]
130 fn from_pair() {
131 let re = BigFloat::from_f64(2.0, 80).expect("re");
132 let im = BigFloat::from_f64(-3.0, 80).expect("im");
133 let z = BigComplex::from((re, im));
134 assert_eq!(z.re().to_f64(), 2.0);
135 assert_eq!(z.im().to_f64(), -3.0);
136 }
137
138 #[test]
139 fn from_real_axis() {
140 let re = BigFloat::from_i64(7, 80, RoundingMode::HalfEven);
141 let z = BigComplex::from(re);
142 assert_eq!(z.re().to_f64(), 7.0);
143 assert!(z.im().is_zero());
144 }
145
146 #[test]
147 fn to_f64_parts_roundtrip() {
148 let z = BigComplex::from_f64(1.5, -2.25, 80).expect("finite");
149 let (re, im) = z.to_f64_parts();
150 assert_eq!(re, 1.5);
151 assert_eq!(im, -2.25);
152 }
153
154 #[test]
157 fn try_from_finite_ok() {
158 let z = BigComplex::from_f64(1.5, -2.25, 80).expect("finite");
159 let r: (f64, f64) = (&z).try_into().expect("should succeed");
160 assert!((r.0 - 1.5).abs() < 1e-12);
161 assert!((r.1 + 2.25).abs() < 1e-12);
162 }
163
164 #[test]
165 fn try_from_zero_ok() {
166 let z = BigComplex::from_f64(0.0, 0.0, 80).expect("zero");
167 let r: (f64, f64) = (&z).try_into().expect("zero should succeed");
168 assert_eq!(r, (0.0, 0.0));
169 }
170
171 #[test]
172 fn try_from_overflow_err() {
173 let prec = 200;
176 let mode = RoundingMode::HalfEven;
177 let large = BigFloat::from_i64(i64::MAX, prec, mode);
178 let large_c = BigComplex::from(large.clone());
179 let large_sq = large_c
181 .checked_div(
182 &BigComplex::from(BigFloat::from_i64(1, prec, mode)),
183 prec,
184 mode,
185 )
186 .expect("div by 1");
187 let v = BigFloat::from_i64(10, prec, mode);
191 let mut acc = BigFloat::from_i64(1, prec, mode);
192 for _ in 0..400 {
193 acc = acc.mul_ref_with_mode(&v, mode);
194 }
195 let huge_c = BigComplex::from(acc);
196 let r = <(f64, f64)>::try_from(&huge_c);
197 if let Ok((re, _)) = r {
201 assert!(re.is_finite(), "unexpected finite result for 10^400");
203 }
204 let _ = large_sq;
206 }
207
208 #[cfg(feature = "num-complex")]
211 mod num_complex_tests {
212 use super::*;
213 use num_complex::Complex;
214
215 #[test]
216 fn from_complex_f64_round_trip() {
217 let nc = Complex::new(1.5f64, -2.25f64);
218 let z = BigComplex::from(nc);
219 let back = Complex::<f64>::from(&z);
220 assert!((back.re - 1.5).abs() < 1e-12);
221 assert!((back.im + 2.25).abs() < 1e-12);
222 }
223
224 #[test]
225 fn from_complex_i64_values_correct() {
226 let nc = Complex::new(3i64, 4i64);
227 let z = BigComplex::from(nc);
228 assert!((z.re().to_f64() - 3.0).abs() < 1e-14);
229 assert!((z.im().to_f64() - 4.0).abs() < 1e-14);
230 }
231
232 #[test]
233 fn from_complex_i64_large_correct() {
234 let nc = Complex::new(1_000_000_007i64, -42i64);
235 let z = BigComplex::from(nc);
236 assert!((z.re().to_f64() - 1_000_000_007.0).abs() < 1.0);
237 assert!((z.im().to_f64() + 42.0).abs() < 1e-10);
238 }
239
240 #[test]
241 fn from_bigcomplex_ref_to_complex_f64() {
242 let z = BigComplex::from_f64(3.0, -4.0, 80).expect("finite");
243 let nc = Complex::<f64>::from(&z);
244 assert_eq!(nc.re, 3.0);
245 assert_eq!(nc.im, -4.0);
246 }
247
248 #[test]
249 fn round_trip_f64_identity() {
250 let nc = Complex::new(0.5f64, -0.5f64);
251 let z = BigComplex::from(nc);
252 let back = Complex::<f64>::from(&z);
253 assert!((back.re - 0.5).abs() < 1e-14);
254 assert!((back.im + 0.5).abs() < 1e-14);
255 }
256 }
257}