Skip to main content

rill_core/math/vector/
macros.rs

1//! Macros for convenient construction of vector expressions.
2//!
3//! This module provides macros that simplify working with the vector eDSL,
4//! allowing expressions in natural mathematical notation.
5//!
6//! ## Examples
7//! ```
8//! use rill_core::vector::prelude::*;
9//! use rill_core::vector::macros::*;
10//!
11//! let a = ScalarVector4::splat(1.0);
12//! let b = ScalarVector4::splat(2.0);
13//! let c = a + b; // regular vector operation
14//! assert_eq!(c, ScalarVector4::splat(3.0));
15//!
16//! // Apply expression to the entire slice
17//! let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
18//! let mut output = [0.0f32; 8];
19//! vec_map!(&input, &mut output, |x| x * 2.0 + 1.0);
20//! // output = [3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0]
21//! ```
22//!
23//! ## Available macros
24//! - [`vec_map!`] – applies a vector expression to the entire slice.
25//! - [`vec_expr!`] – creates a lazy vector expression (stub, requires fixing the expr module).
26//! - [`vec_eval!`] – immediately evaluates a vector expression (stub).
27
28use crate::math::vector::scalar::ScalarVector4;
29use crate::math::vector::traits::Vector;
30
31/// Map over SIMD vector chunks of size 4, applying a closure to each chunk.
32#[macro_export]
33macro_rules! vec_map {
34    ($input:expr, $output:expr, |$x:ident| $($body:tt)*) => {{
35        use $crate::math::vector::traits::Vector;
36        use $crate::math::vector::scalar::ScalarVector4;
37        const N: usize = 4;
38        let input: &[_] = $input;
39        let output: &mut [_] = $output;
40        assert_eq!(input.len(), output.len(), "input and output slices must have equal length");
41
42        if input.is_empty() {
43            return;
44        }
45
46        let closure = |$x: ScalarVector4<_>| -> ScalarVector4<_> { $($body)* };
47
48        let chunks = input.len() / N;
49        let remainder = input.len() % N;
50
51        #[allow(clippy::needless_range_loop)]
52        for i in 0..chunks {
53            let start = i * N;
54            let x = <ScalarVector4<_>>::load(&input[start..start + N]);
55            let y = closure(x);
56            y.store(&mut output[start..start + N]);
57        }
58
59        if remainder > 0 {
60            let start = chunks * N;
61            let mut temp_input = [Default::default(); 4];
62            #[allow(clippy::needless_range_loop)]
63            for i in 0..remainder {
64                temp_input[i] = input[start + i];
65            }
66            let x = <ScalarVector4<_>>::load(&temp_input[0..4]);
67            let y = closure(x);
68            #[allow(clippy::needless_range_loop)]
69            for i in 0..remainder {
70                output[start + i] = y.extract(i);
71            }
72        }
73    }};
74}
75
76/// Creates a lazy vector expression (stub).
77///
78/// In the current implementation, the `expr` module is temporarily disabled due to compilation
79/// errors, so this macro returns the passed value unchanged.
80#[macro_export]
81macro_rules! vec_expr {
82    ($val:expr) => {
83        $val
84    };
85}
86
87/// Immediately evaluates a vector expression (stub).
88///
89/// In the current implementation, simply returns the passed expression.
90#[macro_export]
91macro_rules! vec_eval {
92    ($($t:tt)*) => {
93        $($t)*
94    };
95}
96
97pub use crate::vec_eval;
98pub use crate::vec_expr;
99pub use crate::vec_map;
100
101// -----------------------------------------------------------------------------
102// Tests
103// -----------------------------------------------------------------------------
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use crate::math::vector::scalar::ScalarVector4;
109
110    #[test]
111    fn test_vec_map_f32() {
112        let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
113        let mut output = [0.0f32; 8];
114
115        // Closure: x * 2.0 + 1.0
116        vec_map!(&input, &mut output, |x| x * 2.0 + 1.0);
117
118        assert_eq!(output[0], 3.0); // 1*2 + 1
119        assert_eq!(output[1], 5.0); // 2*2 + 1
120        assert_eq!(output[2], 7.0);
121        assert_eq!(output[3], 9.0);
122        assert_eq!(output[4], 11.0);
123        assert_eq!(output[5], 13.0);
124        assert_eq!(output[6], 15.0);
125        assert_eq!(output[7], 17.0);
126    }
127
128    #[test]
129    fn test_vec_map_f64() {
130        let input = [1.0f64, 2.0, 3.0, 4.0];
131        let mut output = [0.0f64; 4];
132
133        vec_map!(&input, &mut output, |x| x * 3.0 - 1.0);
134
135        assert_eq!(output[0], 2.0); // 1*3 - 1
136        assert_eq!(output[1], 5.0); // 2*3 - 1
137        assert_eq!(output[2], 8.0);
138        assert_eq!(output[3], 11.0);
139    }
140
141    #[test]
142    fn test_vec_map_empty() {
143        let input: [f32; 0] = [];
144        let mut output: [f32; 0] = [];
145        vec_map!(&input, &mut output, |x| x * 2.0); // should not panic
146    }
147
148    #[test]
149    fn test_vec_map_remainder() {
150        let input = [1.0f32, 2.0, 3.0]; // three elements
151        let mut output = [0.0f32; 3];
152
153        vec_map!(&input, &mut output, |x| x + 10.0);
154
155        assert_eq!(output[0], 11.0);
156        assert_eq!(output[1], 12.0);
157        assert_eq!(output[2], 13.0);
158    }
159
160    #[test]
161    fn test_vec_expr_stub() {
162        let vec = ScalarVector4::splat(5.0);
163        let result = vec_expr!(vec);
164        assert_eq!(result, vec);
165    }
166
167    #[test]
168    fn test_vec_eval_stub() {
169        let a = ScalarVector4::splat(2.0);
170        let b = ScalarVector4::splat(3.0);
171        let result = vec_eval!(a + b);
172        assert_eq!(result, ScalarVector4::splat(5.0));
173    }
174}