Blender V5.0
BLI_index_mask_expression_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "BLI_array.hh"
7#include "BLI_rand.hh"
8#include "BLI_set.hh"
9#include "BLI_timeit.hh"
10
11#include "testing/testing.h"
12
13#include "BLI_strict_flags.h" /* IWYU pragma: keep */
14
16
17TEST(index_mask_expression, Union)
18{
19 IndexMaskMemory memory;
20 const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
21 const IndexMask mask_b = IndexMask::from_initializers({IndexRange(10, 10), 60, 200}, memory);
22
23 ExprBuilder builder;
24 const Expr &expr = builder.merge({&mask_a, &mask_b});
25 const IndexMask union_mask = evaluate_expression(expr, memory);
26
27 EXPECT_EQ(union_mask,
29 {5, IndexRange(10, 10), IndexRange(50, 100), 200, 100'000}, memory));
30}
31
32TEST(index_mask_expression, UnionMulti)
33{
34 IndexMaskMemory memory;
35 const IndexMask mask_a = IndexMask::from_initializers({3, 5, 6, 8, 9}, memory);
36 const IndexMask mask_b = IndexMask::from_initializers({4, 6, 7, 12}, memory);
37 const IndexMask mask_c = IndexMask::from_initializers({0, 5}, memory);
38 const IndexMask mask_d = IndexMask::from_initializers({6, 7, 10}, memory);
39
40 ExprBuilder builder;
41 const Expr &expr = builder.merge({&mask_a, &mask_b, &mask_c, &mask_d});
42 const IndexMask union_mask = evaluate_expression(expr, memory);
43
44 EXPECT_EQ(union_mask, IndexMask::from_initializers({0, 3, 4, 5, 6, 7, 8, 9, 10, 12}, memory));
45}
46
47TEST(index_mask_expression, IntersectMulti)
48{
49 IndexMaskMemory memory;
50 const IndexMask mask_a = IndexMask::from_initializers({3, 5, 6, 8, 9}, memory);
51 const IndexMask mask_b = IndexMask::from_initializers({2, 5, 6, 10}, memory);
52 const IndexMask mask_c = IndexMask::from_initializers({4, 5, 6}, memory);
53 const IndexMask mask_d = IndexMask::from_initializers({1, 5, 10}, memory);
54
55 ExprBuilder builder;
56 const Expr &expr = builder.intersect({&mask_a, &mask_b, &mask_c, &mask_d});
57 const IndexMask intersect_mask = evaluate_expression(expr, memory);
58
59 EXPECT_EQ(intersect_mask, IndexMask::from_initializers({5}, memory));
60}
61
62TEST(index_mask_expression, DifferenceMulti)
63{
64 IndexMaskMemory memory;
65 const IndexMask mask_a = IndexMask::from_initializers({1, 2, 3, 5, 6, 7, 9, 10}, memory);
66 const IndexMask mask_b = IndexMask::from_initializers({2, 5, 6, 10}, memory);
67 const IndexMask mask_c = IndexMask::from_initializers({4, 5, 6}, memory);
68 const IndexMask mask_d = IndexMask::from_initializers({1, 5, 10}, memory);
69
70 ExprBuilder builder;
71 const Expr &expr = builder.subtract(&mask_a, {&mask_b, &mask_c, &mask_d});
72 const IndexMask difference_mask = evaluate_expression(expr, memory);
73
74 EXPECT_EQ(difference_mask, IndexMask::from_initializers({3, 7, 9}, memory));
75}
76
77TEST(index_mask_expression, Intersection)
78{
79 IndexMaskMemory memory;
80 const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
82 {5, 6, IndexRange(100, 100), 80000, 100'000}, memory);
83
84 ExprBuilder builder;
85 const Expr &expr = builder.intersect({&mask_a, &mask_b});
86 const IndexMask intersection_mask = evaluate_expression(expr, memory);
87
88 EXPECT_EQ(intersection_mask,
89 IndexMask::from_initializers({5, IndexRange(100, 50), 100'000}, memory));
90}
91
92TEST(index_mask_expression, Difference)
93{
94 IndexMaskMemory memory;
95 const IndexMask mask_a = IndexMask::from_initializers({5, IndexRange(50, 100), 100'000}, memory);
96 const IndexMask mask_b = IndexMask::from_initializers({5, 60, IndexRange(100, 20)}, memory);
97
98 ExprBuilder builder;
99 const Expr &expr = builder.subtract(&mask_a, {&mask_b});
100 const IndexMask difference_mask = evaluate_expression(expr, memory);
101
102 EXPECT_EQ(difference_mask,
104 {IndexRange(50, 10), IndexRange(61, 39), IndexRange(120, 30), 100'000}, memory));
105}
106
107TEST(index_mask_expression, FizzBuzz)
108{
109 IndexMaskMemory memory;
110 const IndexMask mask_3 = IndexMask::from_every_nth(3, 11, 0, memory); /* 0 - 30 */
111 const IndexMask mask_5 = IndexMask::from_every_nth(5, 11, 0, memory); /* 0 - 50 */
112
113 {
114 ExprBuilder builder;
115 const Expr &expr = builder.merge({&mask_3, &mask_5});
116 const IndexMask result = evaluate_expression(expr, memory);
117 EXPECT_EQ(
118 result,
120 {0, 3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 35, 40, 45, 50}, memory));
121 }
122 {
123 ExprBuilder builder;
124 const Expr &expr = builder.intersect({&mask_3, &mask_5});
125 const IndexMask result = evaluate_expression(expr, memory);
126 EXPECT_EQ(result, IndexMask::from_initializers({0, 15, 30}, memory));
127 }
128 {
129 ExprBuilder builder;
130 const Expr &expr = builder.subtract(&mask_3, {&mask_5});
131 const IndexMask result = evaluate_expression(expr, memory);
132 EXPECT_EQ(result, IndexMask::from_initializers({3, 6, 9, 12, 18, 21, 24, 27}, memory));
133 }
134 {
135 ExprBuilder builder;
136 const Expr &expr = builder.merge(
137 {&builder.intersect({&mask_3, &mask_5}), &builder.subtract(&mask_3, {&mask_5})});
138 const IndexMask &result = evaluate_expression(expr, memory);
140 IndexMask::from_initializers({0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30}, memory));
141 }
142}
143
144TEST(index_mask_expression, UnionToFullRange)
145{
146 IndexMaskMemory memory;
147 const IndexMask mask_1 = IndexMask::from_initializers({2, 4, 5, 7}, memory);
148 const IndexMask mask_2 = IndexMask::from_initializers({6, 8}, memory);
149 const IndexMask mask_3 = IndexMask::from_initializers({1, 3}, memory);
150
151 ExprBuilder builder;
152 const Expr &expr = builder.merge({&mask_1, &mask_2, &mask_3});
153 const IndexMask result = evaluate_expression(expr, memory);
154 EXPECT_TRUE(result.to_range().has_value());
156 EXPECT_EQ(result.segments_num(), 1);
157}
158
159TEST(index_mask_expression, UnionIndividualIndices)
160{
161 IndexMaskMemory memory;
162 const IndexMask mask_1 = IndexMask::from_initializers({3}, memory);
163 const IndexMask mask_2 = IndexMask::from_initializers({6}, memory);
164 const IndexMask mask_3 = IndexMask::from_initializers({5}, memory);
165
166 ExprBuilder builder;
167 const Expr &expr = builder.merge({&mask_1, &mask_2, &mask_3});
168 const IndexMask result = evaluate_expression(expr, memory);
169 EXPECT_EQ(result, IndexMask::from_initializers({3, 5, 6}, memory));
170 EXPECT_EQ(result.segments_num(), 1);
171}
172
173TEST(index_mask_expression, UnionLargeRanges)
174{
175 IndexMaskMemory memory;
176 const IndexMask mask_a(IndexRange(0, 1'000'000));
177 const IndexMask mask_b(IndexRange(900'000, 1'100'000));
178
179 ExprBuilder builder;
180 const Expr &expr = builder.merge({&mask_a, &mask_b});
181 const IndexMask result_mask = evaluate_expression(expr, memory);
182
183 EXPECT_EQ(result_mask, IndexMask(IndexRange(0, 2'000'000)));
184}
185
186TEST(index_mask_expression, SubtractSmall)
187{
188 IndexMaskMemory memory;
189 const IndexMask mask_a = IndexMask::from_initializers({3, 4, 5, 6, 7, 8, 9}, memory);
190 const IndexMask mask_b = IndexMask::from_initializers({5, 7}, memory);
191 const IndexMask mask_c = IndexMask::from_initializers({8}, memory);
192
193 ExprBuilder builder;
194 const Expr &expr = builder.subtract(&mask_a, {&mask_b, &mask_c});
195 const IndexMask result = evaluate_expression(expr, memory);
196
197 EXPECT_EQ(result, IndexMask::from_initializers({3, 4, 6, 9}, memory));
198 EXPECT_EQ(result.segments_num(), 1);
199}
200
201TEST(index_mask_expression, RangeTerms)
202{
203 IndexMaskMemory memory;
204 ExprBuilder builder;
205
206 const IndexRange range_a = IndexRange::from_begin_end(30'000, 50'000);
207 const IndexRange range_b = IndexRange::from_begin_end(40'000, 100'000);
208 const IndexRange range_c = IndexRange::from_begin_end(45'000, 48'000);
209
210 const Expr &expr = builder.subtract(&builder.merge({range_a, range_b}), {range_c});
211 const IndexMask result_mask = evaluate_expression(expr, memory);
212
213 EXPECT_EQ(result_mask,
215 IndexRange::from_begin_end(48'000, 100'000)},
216 memory));
217}
218
219TEST(index_mask_expression, SingleMask)
220{
221 IndexMaskMemory memory;
222 const IndexMask mask = IndexMask::from_initializers({5, 6, 8, 9}, memory);
223
224 ExprBuilder builder;
225 const Expr &expr = builder.merge({&mask});
226 const IndexMask result = evaluate_expression(expr, memory);
227
229}
230
231TEST(index_mask_expression, SubtractSelf)
232{
233 IndexMaskMemory memory;
234 const IndexMask mask = IndexMask ::from_initializers({6, 8, 10, 100}, memory);
235
236 ExprBuilder builder;
237 const Expr &expr = builder.subtract(&mask, {&mask});
238 const IndexMask result = evaluate_expression(expr, memory);
239
240 EXPECT_TRUE(result.is_empty());
241}
242
243/* Disable benchmark by default. */
244#if 0
245TEST(index_mask_expression, Benchmark)
246{
247# ifdef NDEBUG
248 const int64_t iterations = 100;
249# else
250 const int64_t iterations = 1;
251# endif
252
253 for ([[maybe_unused]] const int64_t _1 : IndexRange(5)) {
255 const IndexMask a = IndexMask::from_every_nth(3, 1'000'000, 0, m);
256 const IndexMask b = IndexMask::from_every_nth(100, 5'000, 0, m);
257 ExprBuilder builder;
258 const Expr &expr = builder.merge({&a, &b});
259
260 SCOPED_TIMER("benchmark");
261 for ([[maybe_unused]] const int64_t _2 : IndexRange(iterations)) {
262 IndexMaskMemory memory;
263 const IndexMask result = evaluate_expression(expr, memory);
265 }
266 }
267}
268#endif
269
270} // namespace blender::index_mask::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define SCOPED_TIMER(name)
Definition BLI_timeit.hh:70
#define UNUSED_VARS(...)
long long int int64_t
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
const IntersectionExpr & intersect(const Span< Term > terms)
const UnionExpr & merge(const Span< Term > terms)
const DifferenceExpr & subtract(const Term &main_term, const Span< Term > subtract_terms)
static IndexMask from_every_nth(int64_t n, int64_t indices_num, const int64_t initial_offset, IndexMaskMemory &memory)
static IndexMask from_initializers(const Span< Initializer > initializers, IndexMaskMemory &memory)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
IndexMask evaluate_expression(const Expr &expression, IndexMaskMemory &memory)