Skip to main content

wolfram_serialize/
complex.rs

1//! Complex number primitives suitable as element types of `NumericArray` and
2//! `PackedArray`.
3//!
4//! [`Complex64`] is byte-layout-compatible with the C ABI's `_Complex double`
5//! (and thus `wolfram_library_link_sys::mcomplex`, which re-exports this same
6//! type via `pub use`). [`Complex32`] is the `_Complex float` analog.
7//!
8//! Both types are `#[repr(C)]` with two interleaved real/imaginary scalar fields,
9//! matching WXF's wire layout for `ComplexReal{32,64}` array elements.
10
11/// Generic complex number — pair of identical scalars `(re, im)` with
12/// C-compatible interleaved layout. Use the [`Complex64`] / [`Complex32`]
13/// aliases in code; this generic exists so the impl block and layout
14/// invariants are written once.
15#[repr(C)]
16#[derive(Debug, Copy, Clone, PartialEq, Default)]
17pub struct Complex<F> {
18    /// Real part.
19    pub re: F,
20    /// Imaginary part.
21    pub im: F,
22}
23
24impl<F> Complex<F> {
25    /// Construct from `(real, imaginary)` parts.
26    pub const fn new(re: F, im: F) -> Self {
27        Complex { re, im }
28    }
29}
30
31impl<F: Copy> Complex<F> {
32    /// Real part.
33    pub const fn re(self) -> F {
34        self.re
35    }
36    /// Imaginary part.
37    pub const fn im(self) -> F {
38        self.im
39    }
40}
41
42/// Single 64-bit complex number — pair of `f64` (real, imaginary).
43///
44/// Layout matches the C ABI `_Complex double` and the WXF `ComplexReal64`
45/// element wire format. `wolfram-library-link-sys::mcomplex` is `pub use`'d as
46/// an alias for this type, so `wll::NumericArray<sys::mcomplex>` and
47/// `wll::NumericArray<wolfram_expr::Complex64>` are the same instantiation.
48pub type Complex64 = Complex<f64>;
49
50/// Single 32-bit complex number — pair of `f32` (real, imaginary). Layout matches
51/// the WXF `ComplexReal32` element wire format. No `_Complex float` typedef
52/// exists in `WolframLibrary.h`, so this type is wolfram-expr-only.
53pub type Complex32 = Complex<f32>;
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use std::convert::TryInto;
59
60    #[test]
61    fn layout_matches_c_complex_double() {
62        // C `_Complex double` is two contiguous doubles, 16 bytes, aligned to f64.
63        assert_eq!(std::mem::size_of::<Complex64>(), 16);
64        assert_eq!(
65            std::mem::align_of::<Complex64>(),
66            std::mem::align_of::<f64>()
67        );
68        // Field offsets: re at byte 0, im at byte 8.
69        let z = Complex64::new(1.0, 2.0);
70        let bytes: [u8; 16] = unsafe { std::mem::transmute(z) };
71        let re_back = f64::from_le_bytes(bytes[..8].try_into().unwrap());
72        let im_back = f64::from_le_bytes(bytes[8..].try_into().unwrap());
73        assert_eq!(re_back, 1.0);
74        assert_eq!(im_back, 2.0);
75    }
76
77    #[test]
78    fn layout_matches_c_complex_float() {
79        assert_eq!(std::mem::size_of::<Complex32>(), 8);
80        assert_eq!(
81            std::mem::align_of::<Complex32>(),
82            std::mem::align_of::<f32>()
83        );
84    }
85}