use crate::CBig;
use core::convert::TryFrom;
use oxinum_core::{OxiNumError, OxiNumResult};
use oxinum_float::precision::with_precision;
use oxinum_float::DBig;
#[inline]
fn exact_dbig(n: i64) -> DBig {
with_precision(&DBig::from(n), 0)
}
impl From<(DBig, DBig)> for CBig {
fn from((re, im): (DBig, DBig)) -> Self {
CBig::from_parts(re, im)
}
}
impl From<DBig> for CBig {
fn from(re: DBig) -> Self {
CBig::from_real(re)
}
}
impl From<&DBig> for CBig {
fn from(re: &DBig) -> Self {
CBig::from_real(re.clone())
}
}
impl From<(i64, i64)> for CBig {
fn from((re, im): (i64, i64)) -> Self {
CBig::from_parts(exact_dbig(re), exact_dbig(im))
}
}
impl From<i64> for CBig {
fn from(re: i64) -> Self {
CBig::from_real(exact_dbig(re))
}
}
impl CBig {
pub fn to_f64_parts(&self) -> (f64, f64) {
(self.re.to_f64().value(), self.im.to_f64().value())
}
}
impl TryFrom<&CBig> for (f64, f64) {
type Error = OxiNumError;
fn try_from(z: &CBig) -> 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 super::exact_dbig;
use crate::CBig;
use num_complex::Complex;
impl From<Complex<f64>> for CBig {
fn from(z: Complex<f64>) -> Self {
CBig::from_f64(z.re, z.im)
.expect("Complex<f64> → CBig: components must be finite (no NaN/Inf)")
}
}
impl From<Complex<i64>> for CBig {
fn from(z: Complex<i64>) -> Self {
CBig::from_parts(exact_dbig(z.re), exact_dbig(z.im))
}
}
impl From<&CBig> for Complex<f64> {
fn from(z: &CBig) -> Self {
let (re, im) = z.to_f64_parts();
Complex::new(re, im)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_dbig_pair() {
let re = DBig::from(7);
let im = DBig::from(-4);
let z: CBig = (re, im).into();
assert_eq!(z.re().to_string(), "7");
assert_eq!(z.im().to_string(), "-4");
}
#[test]
fn from_dbig_lands_on_real_axis() {
let d = DBig::from(5);
let z: CBig = d.into();
assert_eq!(z.re().to_string(), "5");
assert_eq!(z.im().to_string(), "0");
assert!(z.is_real());
}
#[test]
fn from_dbig_ref_lands_on_real_axis() {
let d = DBig::from(9);
let z: CBig = (&d).into();
assert_eq!(z.re().to_string(), "9");
assert_eq!(z.im().to_string(), "0");
assert_eq!(d.to_string(), "9");
}
#[test]
fn from_integer_pair() {
let z: CBig = (1i64, 2i64).into();
assert_eq!(z.re().to_string(), "1");
assert_eq!(z.im().to_string(), "2");
}
#[test]
fn from_integer_lands_on_real_axis() {
let z: CBig = 42i64.into();
assert_eq!(z.re().to_string(), "42");
assert_eq!(z.im().to_string(), "0");
assert!(z.is_real());
}
#[test]
fn to_f64_parts_round_trips() {
let z = CBig::from_f64(3.5, -1.25).expect("finite parts");
assert_eq!(z.to_f64_parts(), (3.5, -1.25));
}
#[test]
fn integer_parts_carry_unlimited_precision() {
let z: CBig = (3i64, 4i64).into();
assert_eq!(
z.re().precision(),
0,
"real part must be unlimited-precision"
);
assert_eq!(
z.im().precision(),
0,
"imag part must be unlimited-precision"
);
let r: CBig = 7i64.into();
assert_eq!(
r.re().precision(),
0,
"real-axis part must be unlimited-precision"
);
assert_eq!(r.im().to_string(), "0");
}
#[test]
fn integer_norm_sqr_is_exact() {
let z: CBig = (3i64, 4i64).into();
assert_eq!(z.norm_sqr().to_string(), "25");
}
#[test]
fn integer_product_is_exact() {
let prod = CBig::from((1i64, 2i64)) * CBig::from((3i64, 4i64));
assert_eq!(prod.re().to_string(), "-5");
assert_eq!(prod.im().to_string(), "10");
}
#[test]
fn integer_large_magnitude_norm_sqr_is_exact() {
let z: CBig = (1_000_000_007i64, 0i64).into();
assert_eq!(z.norm_sqr().to_string(), "1000000014000000049");
}
#[test]
fn integer_i64_max_norm_sqr_is_exact() {
let z: CBig = (i64::MAX, 0i64).into();
assert_eq!(
z.norm_sqr().to_string(),
"85070591730234615847396907784232501249"
);
}
#[test]
fn try_from_finite_ok() {
let z = CBig::from_f64(1.5, -2.25).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 = CBig::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() {
const PREC: usize = 64; let base = with_precision(&DBig::from(10i64), PREC);
let mut acc = with_precision(&DBig::from(1i64), PREC);
for _ in 0..400 {
acc = with_precision(&(&acc * &base), PREC);
}
let huge = CBig::from_parts(acc, DBig::from(0i64));
let r = <(f64, f64)>::try_from(&huge);
assert!(r.is_err(), "should fail on overflow: {r:?}");
}
#[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 = CBig::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_is_exact() {
let nc = Complex::new(3i64, 4i64);
let z = CBig::from(nc);
assert_eq!(z.norm_sqr().to_string(), "25");
}
#[test]
fn from_complex_i64_large_is_exact() {
let nc = Complex::new(i64::MAX, 1i64);
let z = CBig::from(nc);
assert_eq!(z.re().to_string(), i64::MAX.to_string());
assert_eq!(z.im().to_string(), "1");
}
#[test]
fn from_cbig_ref_to_complex_f64() {
let z = CBig::from_f64(3.0, -4.0).expect("finite");
let nc = Complex::<f64>::from(&z);
assert_eq!(nc.re, 3.0);
assert_eq!(nc.im, -4.0);
}
#[test]
fn from_complex_i64_norm_sqr_exact() {
let nc = Complex::new(5i64, 12i64);
let z = CBig::from(nc);
assert_eq!(z.norm_sqr().to_string(), "169");
}
}
}