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