use core::convert::TryFrom;
use oxinum_core::{OxiNumError, OxiNumResult};
use oxinum_float::native::BigFloat;
use super::BigComplex;
impl From<(BigFloat, BigFloat)> for BigComplex {
#[inline]
fn from((re, im): (BigFloat, BigFloat)) -> Self {
BigComplex::from_parts(re, im)
}
}
impl From<BigFloat> for BigComplex {
#[inline]
fn from(re: BigFloat) -> Self {
BigComplex::from_real(re)
}
}
impl BigComplex {
pub fn to_f64_parts(&self) -> (f64, f64) {
(self.re().to_f64(), self.im().to_f64())
}
}
impl TryFrom<&BigComplex> for (f64, f64) {
type Error = OxiNumError;
fn try_from(z: &BigComplex) -> OxiNumResult<(f64, f64)> {
let (re, im) = z.to_f64_parts();
if !re.is_finite() || !im.is_finite() {
return Err(OxiNumError::Overflow(
"component is non-finite (infinite or NaN) after f64 projection".into(),
));
}
Ok((re, im))
}
}
#[cfg(feature = "num-complex")]
mod num_complex_impls {
use num_complex::Complex;
use oxinum_float::native::{BigFloat, RoundingMode};
use super::BigComplex;
const DEFAULT_PREC: u32 = 80;
const DEFAULT_MODE: RoundingMode = RoundingMode::HalfEven;
impl From<Complex<f64>> for BigComplex {
fn from(z: Complex<f64>) -> Self {
BigComplex::from_f64(z.re, z.im, DEFAULT_PREC)
.expect("Complex<f64> → BigComplex: components must be finite (no NaN/Inf)")
}
}
impl From<Complex<i64>> for BigComplex {
fn from(z: Complex<i64>) -> Self {
let re = BigFloat::from_i64(z.re, DEFAULT_PREC, DEFAULT_MODE);
let im = BigFloat::from_i64(z.im, DEFAULT_PREC, DEFAULT_MODE);
BigComplex::from_parts(re, im)
}
}
impl From<&BigComplex> for Complex<f64> {
fn from(z: &BigComplex) -> Self {
let (re, im) = z.to_f64_parts();
Complex::new(re, im)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use oxinum_float::native::RoundingMode;
#[test]
fn from_pair() {
let re = BigFloat::from_f64(2.0, 80).expect("re");
let im = BigFloat::from_f64(-3.0, 80).expect("im");
let z = BigComplex::from((re, im));
assert_eq!(z.re().to_f64(), 2.0);
assert_eq!(z.im().to_f64(), -3.0);
}
#[test]
fn from_real_axis() {
let re = BigFloat::from_i64(7, 80, RoundingMode::HalfEven);
let z = BigComplex::from(re);
assert_eq!(z.re().to_f64(), 7.0);
assert!(z.im().is_zero());
}
#[test]
fn to_f64_parts_roundtrip() {
let z = BigComplex::from_f64(1.5, -2.25, 80).expect("finite");
let (re, im) = z.to_f64_parts();
assert_eq!(re, 1.5);
assert_eq!(im, -2.25);
}
#[test]
fn try_from_finite_ok() {
let z = BigComplex::from_f64(1.5, -2.25, 80).expect("finite");
let r: (f64, f64) = (&z).try_into().expect("should succeed");
assert!((r.0 - 1.5).abs() < 1e-12);
assert!((r.1 + 2.25).abs() < 1e-12);
}
#[test]
fn try_from_zero_ok() {
let z = BigComplex::from_f64(0.0, 0.0, 80).expect("zero");
let r: (f64, f64) = (&z).try_into().expect("zero should succeed");
assert_eq!(r, (0.0, 0.0));
}
#[test]
fn try_from_overflow_err() {
let prec = 200;
let mode = RoundingMode::HalfEven;
let large = BigFloat::from_i64(i64::MAX, prec, mode);
let large_c = BigComplex::from(large.clone());
let large_sq = large_c
.checked_div(
&BigComplex::from(BigFloat::from_i64(1, prec, mode)),
prec,
mode,
)
.expect("div by 1");
let v = BigFloat::from_i64(10, prec, mode);
let mut acc = BigFloat::from_i64(1, prec, mode);
for _ in 0..400 {
acc = acc.mul_ref_with_mode(&v, mode);
}
let huge_c = BigComplex::from(acc);
let r = <(f64, f64)>::try_from(&huge_c);
if let Ok((re, _)) = r {
assert!(re.is_finite(), "unexpected finite result for 10^400");
}
let _ = large_sq;
}
#[cfg(feature = "num-complex")]
mod num_complex_tests {
use super::*;
use num_complex::Complex;
#[test]
fn from_complex_f64_round_trip() {
let nc = Complex::new(1.5f64, -2.25f64);
let z = BigComplex::from(nc);
let back = Complex::<f64>::from(&z);
assert!((back.re - 1.5).abs() < 1e-12);
assert!((back.im + 2.25).abs() < 1e-12);
}
#[test]
fn from_complex_i64_values_correct() {
let nc = Complex::new(3i64, 4i64);
let z = BigComplex::from(nc);
assert!((z.re().to_f64() - 3.0).abs() < 1e-14);
assert!((z.im().to_f64() - 4.0).abs() < 1e-14);
}
#[test]
fn from_complex_i64_large_correct() {
let nc = Complex::new(1_000_000_007i64, -42i64);
let z = BigComplex::from(nc);
assert!((z.re().to_f64() - 1_000_000_007.0).abs() < 1.0);
assert!((z.im().to_f64() + 42.0).abs() < 1e-10);
}
#[test]
fn from_bigcomplex_ref_to_complex_f64() {
let z = BigComplex::from_f64(3.0, -4.0, 80).expect("finite");
let nc = Complex::<f64>::from(&z);
assert_eq!(nc.re, 3.0);
assert_eq!(nc.im, -4.0);
}
#[test]
fn round_trip_f64_identity() {
let nc = Complex::new(0.5f64, -0.5f64);
let z = BigComplex::from(nc);
let back = Complex::<f64>::from(&z);
assert!((back.re - 0.5).abs() < 1e-14);
assert!((back.im + 0.5).abs() < 1e-14);
}
}
}