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}