Skip to main content

rill_core/math/vector/
macros.rs

1//! Макросы для удобного построения векторных выражений.
2//!
3//! Этот модуль предоставляет макросы, которые упрощают работу с векторным eDSL,
4//! позволяя писать выражения в естественной математической нотации.
5//!
6//! ## Примеры
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; // обычная векторная операция
14//! assert_eq!(c, ScalarVector4::splat(3.0));
15//!
16//! // Применение выражения ко всему слайсу
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//! ## Доступные макросы
24//! - [`vec_map!`] – применяет векторное выражение ко всему слайсу.
25//! - [`vec_expr!`] – создаёт ленивое векторное выражение (заглушка, требует исправления модуля expr).
26//! - [`vec_eval!`] – немедленно вычисляет векторное выражение (заглушка).
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/// Создаёт ленивое векторное выражение (заглушка).
77///
78/// В текущей реализации модуль `expr` временно отключён из-за ошибок компиляции,
79/// поэтому этот макрос возвращает переданное значение без изменений.
80#[macro_export]
81macro_rules! vec_expr {
82    ($val:expr) => {
83        $val
84    };
85}
86
87/// Немедленно вычисляет векторное выражение (заглушка).
88///
89/// В текущей реализации просто возвращает переданное выражение.
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// Тесты
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        // Замыкание: 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); // не должно паниковать
146    }
147
148    #[test]
149    fn test_vec_map_remainder() {
150        let input = [1.0f32, 2.0, 3.0]; // три элемента
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}