Project
Loading...
Searching...
No Matches
test_ASoA.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <cstdio>
13#include "Framework/ASoA.h"
17#include "gandiva/tree_expr_builder.h"
18#include "arrow/status.h"
19#include "gandiva/filter.h"
20#include <catch_amalgamated.hpp>
21#include <arrow/util/key_value_metadata.h>
22
23using namespace o2::framework;
24using namespace arrow;
25using namespace o2::soa;
26
27namespace o2::aod
28{
29namespace test
30{
35DECLARE_SOA_DYNAMIC_COLUMN(Sum, sum, [](int x, int y) { return x + y; });
36DECLARE_SOA_EXPRESSION_COLUMN(ESum, esum, int, test::x + test::y);
37} // namespace test
38
39DECLARE_SOA_TABLE(Points, "TEST", "POINTS", test::X, test::Y);
40DECLARE_SOA_TABLE(Points3Ds, "TEST", "PTS3D", o2::soa::Index<>, test::X, test::Y, test::Z);
41
42DECLARE_SOA_TABLE_VERSIONED(Points3DMk1s, "TEST", "PTS3D", 1, o2::soa::Index<>, o2::soa::Marker<1>, test::X, test::Y, test::Z);
43DECLARE_SOA_TABLE_VERSIONED(Points3DMk2s, "TEST", "PTS3D", 2, o2::soa::Index<>, o2::soa::Marker<2>, test::X, test::Y, test::Z);
44DECLARE_SOA_TABLE_VERSIONED(Points3DMk3s, "TEST", "PTS3D", 3, o2::soa::Index<>, o2::soa::Marker<3>, test::X, test::Y, test::Z);
45
46namespace test
47{
48DECLARE_SOA_COLUMN_FULL(SomeBool, someBool, bool, "someBool");
49DECLARE_SOA_COLUMN_FULL(Color, color, int32_t, "color");
50} // namespace test
51
52DECLARE_SOA_TABLE(Infos, "TEST", "INFOS", test::Color, test::SomeBool);
53
54namespace test
55{
58DECLARE_SOA_INDEX_COLUMN_FULL(PointA, pointA, int, Points, "_A");
59DECLARE_SOA_INDEX_COLUMN_FULL(PointB, pointB, int, Points, "_B");
60DECLARE_SOA_COLUMN_FULL(Thickness, thickness, int, "thickness");
61} // namespace test
62
63DECLARE_SOA_TABLE(Segments, "TEST", "SEGMENTS", test::N, test::PointAId, test::PointBId, test::InfoId);
64DECLARE_SOA_TABLE(SegmentsExtras, "TEST", "SEGMENTSPLUS", test::Thickness);
65
66namespace test
67{
68DECLARE_SOA_COLUMN(L1, l1, std::vector<float>);
69DECLARE_SOA_COLUMN(L2, l2, std::vector<int>);
70} // namespace test
71
72DECLARE_SOA_TABLE(Lists, "TEST", "LISTS", o2::soa::Index<>, test::L1, test::L2);
73} // namespace o2::aod
74
75TEST_CASE("TestMarkers")
76{
77 TableBuilder b1;
78 auto pwriter = b1.cursor<o2::aod::Points3Ds>();
79 for (auto i = 0; i < 20; ++i) {
80 pwriter(0, -1 * i, (int)(i / 2), 2 * i);
81 }
82 auto t1 = b1.finalize();
83
84 auto pt = o2::aod::Points3Ds{t1};
85 auto pt1 = o2::aod::Points3DMk1s{t1};
86 auto pt2 = o2::aod::Points3DMk2s{t1};
87 auto pt3 = o2::aod::Points3DMk3s{t1};
88 REQUIRE(pt1.begin().mark() == (size_t)1);
89 REQUIRE(pt2.begin().mark() == (size_t)2);
90 REQUIRE(pt3.begin().mark() == (size_t)3);
91}
92
93TEST_CASE("TestTableIteration")
94{
95 TableBuilder builder;
96 auto rowWriter = builder.persist<int32_t, int32_t>({"fX", "fY"});
97 rowWriter(0, 0, 0);
98 rowWriter(0, 0, 1);
99 rowWriter(0, 0, 2);
100 rowWriter(0, 0, 3);
101 rowWriter(0, 1, 4);
102 rowWriter(0, 1, 5);
103 rowWriter(0, 1, 6);
104 rowWriter(0, 1, 7);
105 auto table = builder.finalize();
106
107 auto i = ColumnIterator<int32_t>(table->column(0).get());
108 int64_t pos = 0;
109 uint64_t offset = 0;
110 i.mCurrentPos = &pos;
111 i.mGlobalOffset = &offset;
112 REQUIRE(*i == 0);
113 pos++;
114 REQUIRE(*i == 0);
115 pos++;
116 REQUIRE(*i == 0);
117 pos++;
118 REQUIRE(*i == 0);
119 pos++;
120 REQUIRE(*i == 1);
121 pos++;
122 REQUIRE(*i == 1);
123 pos++;
124 REQUIRE(*i == 1);
125 pos++;
126 REQUIRE(*i == 1);
127
128 arrow::ChunkedArray* chunks[2] = {
129 table->column(0).get(),
130 table->column(1).get()};
131 o2::aod::Points::iterator tests(chunks, {table->num_rows(), 0});
132 REQUIRE(tests.x() == 0);
133 REQUIRE(tests.y() == 0);
134 ++tests;
135 REQUIRE(tests.x() == 0);
136 REQUIRE(tests.y() == 1);
137 using Test = InPlaceTable<"T/0"_h, o2::aod::test::X, o2::aod::test::Y>;
138 Test tests2{table};
139 size_t value = 0;
140 auto b = tests2.begin();
141 auto e = tests2.end();
142 REQUIRE(b != e);
143 ++b;
144 ++b;
145 ++b;
146 ++b;
147 ++b;
148 ++b;
149 ++b;
150 ++b;
151 REQUIRE(b == e);
152
153 b = tests2.begin();
154 REQUIRE(b != e);
155 REQUIRE(((b + 1) == (b + 1)));
156 REQUIRE(((b + 7) != b));
157 REQUIRE(((b + 7) != e));
158 REQUIRE(((b + 8) == e));
159
160 for (auto& t : tests2) {
161 REQUIRE(t.x() == value / 4);
162 REQUIRE((size_t)t.y() == value);
163 REQUIRE(value < 8);
164 value++;
165 }
166
167 for (auto t1 = tests2.begin(); t1 != tests2.end(); ++t1) {
168 for (auto t2 = t1 + 1; t2 != tests2.end(); ++t2) {
169 }
170 }
171}
172
173TEST_CASE("TestDynamicColumns")
174{
175 TableBuilder builder;
176 auto rowWriter = builder.persist<int32_t, int32_t>({"fX", "fY"});
177 rowWriter(0, 0, 0);
178 rowWriter(0, 0, 1);
179 rowWriter(0, 0, 2);
180 rowWriter(0, 0, 3);
181 rowWriter(0, 1, 4);
182 rowWriter(0, 1, 5);
183 rowWriter(0, 1, 6);
184 rowWriter(0, 1, 7);
185 auto table = builder.finalize();
186
187 using Test1 = InPlaceTable<"A"_h, o2::aod::test::X, o2::aod::test::Y, o2::aod::test::Sum<o2::aod::test::X, o2::aod::test::Y>>;
188
189 Test1 tests1{table};
190 for (auto& test : tests1) {
191 REQUIRE(test.sum() == (test.x() + test.y()));
192 }
193
194 using Test2 = InPlaceTable<"B"_h, o2::aod::test::X, o2::aod::test::Y, o2::aod::test::Sum<o2::aod::test::Y, o2::aod::test::Y>>;
195
196 Test2 tests2{table};
197 for (auto& test : tests2) {
198 CHECK(test.sum() == (test.y() + test.y()));
199 }
200}
201
202TEST_CASE("TestColumnIterators")
203{
204 TableBuilder builder;
205 auto rowWriter = builder.persist<int32_t, int32_t>({"fX", "fY"});
206 rowWriter(0, 0, 0);
207 rowWriter(0, 0, 1);
208 rowWriter(0, 0, 2);
209 rowWriter(0, 0, 3);
210 rowWriter(0, 1, 4);
211 rowWriter(0, 1, 5);
212 rowWriter(0, 1, 6);
213 rowWriter(0, 1, 7);
214 auto table = builder.finalize();
215
216 int64_t index1 = 0;
217 int64_t index2 = 0;
218 ColumnIterator<int32_t> foo{table->column(1).get()};
219 foo.mCurrentPos = &index1;
220 auto bar{foo};
221 bar.mCurrentPos = &index2;
222 REQUIRE(foo.mCurrent == bar.mCurrent);
223 REQUIRE(foo.mLast == bar.mLast);
224 REQUIRE(foo.mColumn == bar.mColumn);
225 REQUIRE(foo.mFirstIndex == bar.mFirstIndex);
226 REQUIRE(foo.mCurrentChunk == bar.mCurrentChunk);
227
228 auto foobar = std::move(foo);
229 REQUIRE(foobar.mCurrent == bar.mCurrent);
230 REQUIRE(foobar.mLast == bar.mLast);
231 REQUIRE(foobar.mColumn == bar.mColumn);
232 REQUIRE(foobar.mFirstIndex == bar.mFirstIndex);
233 REQUIRE(foobar.mCurrentChunk == bar.mCurrentChunk);
234}
235
236TEST_CASE("TestJoinedTables")
237{
238 TableBuilder builderX;
239 auto rowWriterX = builderX.persist<int32_t>({"fX"});
240 rowWriterX(0, 0);
241 rowWriterX(0, 1);
242 rowWriterX(0, 2);
243 rowWriterX(0, 3);
244 rowWriterX(0, 4);
245 rowWriterX(0, 5);
246 rowWriterX(0, 6);
247 rowWriterX(0, 7);
248 auto tableX = builderX.finalize();
249
250 TableBuilder builderY;
251 auto rowWriterY = builderY.persist<int32_t>({"fY"});
252 rowWriterY(0, 7);
253 rowWriterY(0, 6);
254 rowWriterY(0, 5);
255 rowWriterY(0, 4);
256 rowWriterY(0, 3);
257 rowWriterY(0, 2);
258 rowWriterY(0, 1);
259 rowWriterY(0, 0);
260 auto tableY = builderY.finalize();
261
262 TableBuilder builderZ;
263 auto rowWriterZ = builderZ.persist<int32_t>({"fZ"});
264 rowWriterZ(0, 8);
265 rowWriterZ(0, 8);
266 rowWriterZ(0, 8);
267 rowWriterZ(0, 8);
268 rowWriterZ(0, 8);
269 rowWriterZ(0, 8);
270 rowWriterZ(0, 8);
271 rowWriterZ(0, 8);
272 auto tableZ = builderZ.finalize();
273
274 TableBuilder builderW;
275 auto rowWriterW = builderW.persist<int32_t>({"fW"});
276 rowWriterW(0, 8);
277 rowWriterW(0, 8);
278 rowWriterW(0, 8);
279 auto tableW = builderW.finalize();
280
281 using TestX = InPlaceTable<"A0"_h, o2::aod::test::X>;
282 using TestY = InPlaceTable<"A1"_h, o2::aod::test::Y>;
283 using TestZ = InPlaceTable<"A2"_h, o2::aod::test::Z>;
284 using TestW = InPlaceTable<"A3"_h, o2::aod::test::W>;
285 using Test = Join<TestX, TestY>;
286
287 REQUIRE(Test::contains<TestX>());
288 REQUIRE(Test::contains<TestY>());
289 REQUIRE(!Test::contains<TestZ>());
290
291 Test tests{{tableX, tableY}};
292
293 REQUIRE(tests.contains<TestX>());
294 REQUIRE(tests.contains<TestY>());
295 REQUIRE(!tests.contains<TestZ>());
296
297 for (auto& test : tests) {
298 REQUIRE(7 == test.x() + test.y());
299 }
300
301 auto tests2 = join(TestX{tableX}, TestY{tableY});
302 static_assert(std::same_as<Test::self_t, decltype(tests2)>, "Joined tables should have the same type, regardless how we construct them");
303 for (auto& test : tests2) {
304 REQUIRE(7 == test.x() + test.y());
305 }
306
307 auto tests3 = join(TestX{tableX}, TestY{tableY}, TestZ{tableZ});
308
309 for (auto& test : tests3) {
310 REQUIRE(15 == test.x() + test.y() + test.z());
311 }
312 using TestMoreThanTwo = Join<TestX, TestY, TestZ>;
313 TestMoreThanTwo tests4{{tableX, tableY, tableZ}};
314 for (auto& test : tests4) {
315 REQUIRE(15 == test.x() + test.y() + test.z());
316 }
317
318 try {
319 auto testF = join(TestZ{tableZ}, TestW{tableW});
320 } catch (RuntimeErrorRef ref) {
321 REQUIRE(std::string{error_from_ref(ref).what} == "Tables TEST and TEST have different sizes (8 vs 3) and cannot be joined!");
322 }
323}
324
325TEST_CASE("TestConcatTables")
326{
327 TableBuilder builderA;
328 auto rowWriterA = builderA.persist<int32_t, int32_t>({"fX", "fY"});
329 rowWriterA(0, 0, 0);
330 rowWriterA(0, 1, 0);
331 rowWriterA(0, 2, 0);
332 rowWriterA(0, 3, 0);
333 rowWriterA(0, 4, 0);
334 rowWriterA(0, 5, 0);
335 rowWriterA(0, 6, 0);
336 rowWriterA(0, 7, 0);
337 auto tableA = builderA.finalize();
338 REQUIRE(tableA->num_rows() == 8);
339
340 TableBuilder builderB;
341 auto rowWriterB = builderB.persist<int32_t>({"fX"});
342 rowWriterB(0, 8);
343 rowWriterB(0, 9);
344 rowWriterB(0, 10);
345 rowWriterB(0, 11);
346 rowWriterB(0, 12);
347 rowWriterB(0, 13);
348 rowWriterB(0, 14);
349 rowWriterB(0, 15);
350 auto tableB = builderB.finalize();
351
352 TableBuilder builderC;
353 auto rowWriterC = builderC.persist<int32_t>({"fZ"});
354 rowWriterC(0, 8);
355 rowWriterC(0, 9);
356 rowWriterC(0, 10);
357 rowWriterC(0, 11);
358 rowWriterC(0, 12);
359 rowWriterC(0, 13);
360 rowWriterC(0, 14);
361 rowWriterC(0, 15);
362 auto tableC = builderC.finalize();
363
364 TableBuilder builderD;
365 auto rowWriterD = builderD.persist<int32_t, int32_t>({"fX", "fZ"});
366 rowWriterD(0, 16, 8);
367 rowWriterD(0, 17, 9);
368 rowWriterD(0, 18, 10);
369 rowWriterD(0, 19, 11);
370 rowWriterD(0, 20, 12);
371 rowWriterD(0, 21, 13);
372 rowWriterD(0, 22, 14);
373 rowWriterD(0, 23, 15);
374 auto tableD = builderD.finalize();
375
376 using TestA = InPlaceTable<0, o2::soa::Index<>, o2::aod::test::X, o2::aod::test::Y>; // o2::aod::TestA;
377 using TestB = InPlaceTable<0, o2::soa::Index<>, o2::aod::test::X>; // o2::aod::TestB;
378 using TestC = InPlaceTable<0, o2::aod::test::Z>; // o2::aod::TestC;
379 using TestD = InPlaceTable<0, o2::aod::test::X, o2::aod::test::Z>; // o2::aod::TestD;
380 using ConcatTest = Concat<TestA, TestB>;
381 using JoinedTest = Join<TestA, TestC>;
382 using NestedJoinTest = Join<JoinedTest, TestD>;
383 using NestedConcatTest = Concat<Join<TestA, TestB>, TestD>;
384
385 static_assert(std::same_as<NestedJoinTest::columns_t, o2::framework::pack<o2::soa::Index<>, o2::aod::test::Y, o2::aod::test::X, o2::aod::test::Z>>, "Bad nested join");
386
387 static_assert(std::same_as<ConcatTest::columns_t, o2::framework::pack<o2::soa::Index<>, o2::aod::test::X>>, "Bad intersection of columns");
388 ConcatTest tests{{tableA, tableB}};
389 REQUIRE(16 == tests.size());
390 for (auto& test : tests) {
391 REQUIRE(test.index() == test.x());
392 }
393
394 static_assert(std::same_as<NestedConcatTest::columns_t, o2::framework::pack<o2::aod::test::X>>, "Bad nested concat");
395
396 // Hardcode a selection for the first 5 odd numbers
397 using FilteredTest = Filtered<TestA>;
398 using namespace o2::framework;
399 expressions::Filter testf = (o2::aod::test::x == 1) || (o2::aod::test::x == 3);
400 gandiva::Selection selection;
401 auto status = gandiva::SelectionVector::MakeInt64(tests.size(), arrow::default_memory_pool(), &selection);
402 REQUIRE(status.ok());
403
404 auto fptr = tableA->schema()->GetFieldByName("fX");
405 REQUIRE(fptr != nullptr);
406 REQUIRE(fptr->name() == "fX");
407 REQUIRE(fptr->type()->id() == arrow::Type::INT32);
408
409 auto node_x = gandiva::TreeExprBuilder::MakeField(fptr);
410 auto literal_1 = gandiva::TreeExprBuilder::MakeLiteral(static_cast<int32_t>(1));
411 auto literal_3 = gandiva::TreeExprBuilder::MakeLiteral(static_cast<int32_t>(3));
412 auto equals_to_1 = gandiva::TreeExprBuilder::MakeFunction("equal", {node_x, literal_1}, arrow::boolean());
413 auto equals_to_3 = gandiva::TreeExprBuilder::MakeFunction("equal", {node_x, literal_3}, arrow::boolean());
414 auto node_or = gandiva::TreeExprBuilder::MakeOr({equals_to_1, equals_to_3});
415 auto condition = gandiva::TreeExprBuilder::MakeCondition(node_or);
416 REQUIRE(condition->ToString() == "bool equal((int32) fX, (const int32) 1) || bool equal((int32) fX, (const int32) 3)");
417 std::shared_ptr<gandiva::Filter> filter;
418 status = gandiva::Filter::Make(tableA->schema(), condition, &filter);
419 REQUIRE(status.ToString() == "OK");
420
421 arrow::TableBatchReader reader(*tableA);
422 std::shared_ptr<RecordBatch> batch;
423 auto s = reader.ReadNext(&batch);
424 REQUIRE(s.ok());
425 REQUIRE(batch != nullptr);
426 REQUIRE(batch->num_rows() == 8);
427 auto st = filter->Evaluate(*batch, selection);
428 REQUIRE(st.ToString() == "OK");
429
430 gandiva::Selection selection_f = expressions::createSelection(tableA, testf);
431
432 TestA testA{tableA};
433 FilteredTest filtered{{testA.asArrowTableRef()}, selection_f};
434 REQUIRE(2 == filtered.size());
435
436 auto i = 0;
437 REQUIRE(filtered.begin() != filtered.end());
438 for (auto& f : filtered) {
439 REQUIRE(i * 2 + 1 == f.x());
440 REQUIRE(i * 2 + 1 == f.index());
441 i++;
442 }
443 REQUIRE(i == 2);
444
445 // Hardcode a selection for the first 5 odd numbers
446 using FilteredConcatTest = Filtered<ConcatTest::table_t>;
447 using namespace o2::framework;
448 gandiva::Selection selectionConcat;
449 status = gandiva::SelectionVector::MakeInt64(tests.size(), arrow::default_memory_pool(), &selectionConcat);
450 REQUIRE(status.ok() == true);
451 selectionConcat->SetIndex(0, 0);
452 selectionConcat->SetIndex(1, 5);
453 selectionConcat->SetIndex(2, 10);
454 selectionConcat->SetNumSlots(3);
455 ConcatTest concatTest{tableA, tableB};
456 FilteredConcatTest concatTestTable{{concatTest.asArrowTableRef()}, selectionConcat};
457 REQUIRE(3 == concatTestTable.size());
458
459 i = 0;
460 auto b = concatTestTable.begin();
461 auto e = concatTestTable.end();
462
463 REQUIRE(b.mRowIndex == 0);
464 REQUIRE(b.getSelectionRow() == 0);
465 REQUIRE(e.index == 3);
466
467 REQUIRE(concatTestTable.begin() != concatTestTable.end());
468 for (auto& f : concatTestTable) {
469 REQUIRE(i * 5 == f.x());
470 REQUIRE(i * 5 == f.index());
471 REQUIRE(i == f.filteredIndex());
472 i++;
473 }
474 REQUIRE(i == 3);
475
476 // Test with a Joined table
477 using FilteredJoinTest = Filtered<JoinedTest::table_t>;
478 gandiva::Selection selectionJoin;
479 status = gandiva::SelectionVector::MakeInt64(tests.size(), arrow::default_memory_pool(), &selectionJoin);
480 REQUIRE(status.ok() == true);
481 selectionJoin->SetIndex(0, 0);
482 selectionJoin->SetIndex(1, 2);
483 selectionJoin->SetIndex(2, 4);
484 selectionJoin->SetNumSlots(3);
485 JoinedTest testJoin{{tableA, tableC}};
486 FilteredJoinTest filteredJoin{{testJoin.asArrowTableRef()}, selectionJoin};
487
488 i = 0;
489 REQUIRE(filteredJoin.begin() != filteredJoin.end());
490 for (auto& f : filteredJoin) {
491 REQUIRE(i * 2 == f.x());
492 REQUIRE(i * 2 == f.index());
493 i++;
494 }
495 REQUIRE(i == 3);
496}
497
498TEST_CASE("TestDereference")
499{
500 TableBuilder builderA;
501 auto pointsWriter = builderA.cursor<o2::aod::Points>();
502 pointsWriter(0, 0, 0);
503 pointsWriter(0, 3, 4);
504 auto pointsT = builderA.finalize();
505 o2::aod::Points points{pointsT};
506 REQUIRE(pointsT->num_rows() == 2);
507
508 TableBuilder builderA2;
509 auto infoWriter = builderA2.cursor<o2::aod::Infos>();
510 infoWriter(0, 0, true);
511 infoWriter(0, 1, false);
512 infoWriter(0, 4, true);
513 auto infosT = builderA2.finalize();
514 o2::aod::Infos infos{infosT};
515 REQUIRE(infos.begin().someBool() == true);
516 REQUIRE((infos.begin() + 1).someBool() == false);
517 REQUIRE((infos.begin() + 2).someBool() == true);
518 REQUIRE((infos.begin() + 2).color() == 4);
519 REQUIRE(infosT->num_rows() == 3);
520
521 TableBuilder builderB;
522 auto segmentsWriter = builderB.cursor<o2::aod::Segments>();
523 segmentsWriter(0, 10, 0, 1, 2);
524 auto segmentsT = builderB.finalize();
525 o2::aod::Segments segments{segmentsT};
526 REQUIRE(segmentsT->num_rows() == 1);
527
528 TableBuilder builderC;
529 auto segmentsExtraWriter = builderC.cursor<o2::aod::SegmentsExtras>();
530 segmentsExtraWriter(0, 1);
531 auto segmentsExtraT = builderC.finalize();
532 o2::aod::SegmentsExtras segmentsExtras{segmentsExtraT};
533 REQUIRE(segmentsExtraT->num_rows() == 1);
534
535 REQUIRE(segments.begin().pointAId() == 0);
536 REQUIRE(segments.begin().pointBId() == 1);
537 static_assert(std::same_as<decltype(segments.begin().pointA()), o2::aod::Points::iterator>);
538 auto i = segments.begin();
539 using namespace o2::framework;
540 i.bindExternalIndices(&points, &infos);
541 REQUIRE(i.n() == 10);
542 REQUIRE(i.info().color() == 4);
543 REQUIRE(i.info().someBool() == true);
544 REQUIRE(i.pointA().x() == 0);
545 REQUIRE(i.pointA().y() == 0);
546 REQUIRE(i.pointB().x() == 3);
547 REQUIRE(i.pointB().y() == 4);
548
549 segments.bindExternalIndices(&points, &infos);
550 auto j = segments.begin();
551 REQUIRE(j.n() == 10);
552 REQUIRE(j.info().color() == 4);
553 REQUIRE(j.info().someBool() == true);
554 REQUIRE(j.pointA().x() == 0);
555 REQUIRE(j.pointA().y() == 0);
556 REQUIRE(j.pointB().x() == 3);
557 REQUIRE(j.pointB().y() == 4);
558
559 auto joined = join(segments, segmentsExtras);
560 joined.bindExternalIndices(&points, &infos);
561 auto se = joined.begin();
562 REQUIRE(se.n() == 10);
563 REQUIRE(se.info().color() == 4);
564 REQUIRE(se.pointA().x() == 0);
565 REQUIRE(se.pointA().y() == 0);
566 REQUIRE(se.pointB().x() == 3);
567 REQUIRE(se.pointB().y() == 4);
568 REQUIRE(se.thickness() == 1);
569}
570
571TEST_CASE("TestSchemaCreation")
572{
573 auto schema = std::make_shared<arrow::Schema>(createFieldsFromColumns(o2::aod::Points::persistent_columns_t{}));
574 REQUIRE(schema->num_fields() == 2);
575 REQUIRE(schema->field(0)->name() == "fX");
576 REQUIRE(schema->field(1)->name() == "fY");
577}
578
579TEST_CASE("TestFilteredOperators")
580{
581 TableBuilder builderA;
582 auto rowWriterA = builderA.persist<int32_t, int32_t>({"fX", "fY"});
583 rowWriterA(0, 0, 8);
584 rowWriterA(0, 1, 9);
585 rowWriterA(0, 2, 10);
586 rowWriterA(0, 3, 11);
587 rowWriterA(0, 4, 12);
588 rowWriterA(0, 5, 13);
589 rowWriterA(0, 6, 14);
590 rowWriterA(0, 7, 15);
591 auto tableA = builderA.finalize();
592 REQUIRE(tableA->num_rows() == 8);
593
594 using TestA = InPlaceTable<0, o2::soa::Index<>, o2::aod::test::X, o2::aod::test::Y>; // o2::soa::Table<OriginEnc{"AOD"}, o2::soa::Index<>, o2::aod::test::X, o2::aod::test::Y>;
595 using FilteredTest = Filtered<TestA>;
596 using namespace o2::framework;
597
598 expressions::Filter f1 = o2::aod::test::x < 4;
599 expressions::Filter f2 = o2::aod::test::y > 13;
600
601 TestA testA{tableA};
602 auto s1 = expressions::createSelection(testA.asArrowTable(), f1);
603 FilteredTest filtered1{{testA.asArrowTableRef()}, s1};
604 REQUIRE(4 == filtered1.size());
605 REQUIRE(filtered1.begin() != filtered1.end());
606
607 auto s2 = expressions::createSelection(testA.asArrowTable(), f2);
608 FilteredTest filtered2{{testA.asArrowTableRef()}, s2};
609 REQUIRE(2 == filtered2.size());
610 REQUIRE(filtered2.begin() != filtered2.end());
611
612 FilteredTest filteredUnion = filtered1 + filtered2;
613 REQUIRE(6 == filteredUnion.size());
614
615 std::vector<std::tuple<int32_t, int32_t>> expectedUnion{{0, 8}, {1, 9}, {2, 10}, {3, 11}, {6, 14}, {7, 15}};
616 auto i = 0;
617 for (auto& f : filteredUnion) {
618 REQUIRE(std::get<0>(expectedUnion[i]) == f.x());
619 REQUIRE(std::get<1>(expectedUnion[i]) == f.y());
620 REQUIRE(std::get<0>(expectedUnion[i]) == f.index());
621 i++;
622 }
623 REQUIRE(i == 6);
624
625 FilteredTest filteredIntersection = filtered1 * filtered2;
626 REQUIRE(0 == filteredIntersection.size());
627
628 i = 0;
629 for (auto const& _ : filteredIntersection) {
630 i++;
631 }
632 REQUIRE(i == 0);
633
634 expressions::Filter f3 = o2::aod::test::x < 3;
635 auto s3 = expressions::createSelection(testA.asArrowTable(), f3);
636 FilteredTest filtered3{{testA.asArrowTableRef()}, s3};
637 REQUIRE(3 == filtered3.size());
638 REQUIRE(filtered3.begin() != filtered3.end());
639
640 FilteredTest unionIntersection = (filtered1 + filtered2) * filtered3;
641 REQUIRE(3 == unionIntersection.size());
642
643 i = 0;
644 for (auto& f : unionIntersection) {
645 REQUIRE(i == f.x());
646 REQUIRE(i + 8 == f.y());
647 REQUIRE(i == f.index());
648 i++;
649 }
650 REQUIRE(i == 3);
651}
652
653TEST_CASE("TestNestedFiltering")
654{
655 TableBuilder builderA;
656 auto rowWriterA = builderA.persist<int32_t, int32_t>({"fX", "fY"});
657 rowWriterA(0, 0, 8);
658 rowWriterA(0, 1, 9);
659 rowWriterA(0, 2, 10);
660 rowWriterA(0, 3, 11);
661 rowWriterA(0, 4, 12);
662 rowWriterA(0, 5, 13);
663 rowWriterA(0, 6, 14);
664 rowWriterA(0, 7, 15);
665 auto tableA = builderA.finalize();
666 REQUIRE(tableA->num_rows() == 8);
667
668 using TestA = InPlaceTable<0, o2::soa::Index<>, o2::aod::test::X, o2::aod::test::Y>;
669 using FilteredTest = Filtered<TestA>;
670 using NestedFilteredTest = Filtered<Filtered<TestA>>;
671 using TripleNestedFilteredTest = Filtered<Filtered<Filtered<TestA>>>;
672 using namespace o2::framework;
673
674 expressions::Filter f1 = o2::aod::test::x < 4;
675 expressions::Filter f2 = o2::aod::test::y > 9;
676 expressions::Filter f3 = o2::aod::test::x < 3;
677
678 TestA testA{tableA};
679 auto s1 = expressions::createSelection(testA.asArrowTable(), f1);
680 FilteredTest filtered{{testA.asArrowTableRef()}, s1};
681 REQUIRE(4 == filtered.size());
682 REQUIRE(filtered.begin() != filtered.end());
683
684 auto s2 = expressions::createSelection(filtered.asArrowTable(), f2);
685 NestedFilteredTest nestedFiltered{{filtered}, s2};
686 REQUIRE(2 == nestedFiltered.size());
687 auto i = 0;
688 for (auto& f : nestedFiltered) {
689 REQUIRE(i + 2 == f.x());
690 REQUIRE(i + 10 == f.y());
691 REQUIRE(i + 2 == f.index());
692 i++;
693 }
694 REQUIRE(i == 2);
695
696 auto s3 = expressions::createSelection(nestedFiltered.asArrowTable(), f3);
697 TripleNestedFilteredTest tripleFiltered{{nestedFiltered}, s3};
698 REQUIRE(1 == tripleFiltered.size());
699 i = 0;
700 for (auto& f : tripleFiltered) {
701 REQUIRE(i + 2 == f.x());
702 REQUIRE(i + 10 == f.y());
703 REQUIRE(i + 2 == f.index());
704 i++;
705 }
706 REQUIRE(i == 1);
707}
708
709TEST_CASE("TestEmptyTables")
710{
711 TableBuilder bPoints;
712 [[maybe_unused]] auto pwriter = bPoints.cursor<o2::aod::Points>();
713 auto pempty = bPoints.finalize();
714
715 TableBuilder bInfos;
716 [[maybe_unused]] auto iwriter = bInfos.cursor<o2::aod::Infos>();
717 auto iempty = bInfos.finalize();
718
719 o2::aod::Points p{pempty};
720 o2::aod::Infos i{iempty};
721
723 PI pi{{pempty, iempty}};
724 REQUIRE(pi.size() == 0);
725 auto spawned = Extend<o2::aod::Points, o2::aod::test::ESum>(p);
726 REQUIRE(spawned.size() == 0);
727}
728
729namespace o2::aod
730{
731DECLARE_SOA_TABLE(Origints, "TEST", "ORIG", o2::soa::Index<>, test::X, test::SomeBool);
732namespace test
733{
734DECLARE_SOA_INDEX_COLUMN(Origint, origint);
735DECLARE_SOA_INDEX_COLUMN_FULL(AltOrigint, altOrigint, int, Origints, "_alt");
737} // namespace test
738
739DECLARE_SOA_TABLE(References, "TEST", "REFS", o2::soa::Index<>, test::OrigintId);
740DECLARE_SOA_TABLE(OtherReferences, "TEST", "OREFS", o2::soa::Index<>, test::AltOrigintId);
741DECLARE_SOA_TABLE(ManyReferences, "TEST", "MREFS", o2::soa::Index<>, test::OrigintIds);
742} // namespace o2::aod
743
744TEST_CASE("TestIndexToFiltered")
745{
747 auto writer = b.cursor<o2::aod::Origints>();
748 for (auto i = 0; i < 20; ++i) {
749 writer(0, i, i % 3 == 0);
750 }
751 auto origins = b.finalize();
752 o2::aod::Origints o{origins};
753
755 auto writer_z = z.cursor<o2::aod::ManyReferences>();
756 std::vector<int> ids;
757 for (auto i = 0; i < 5; ++i) {
758 ids.clear();
759 for (auto j = 0; j < 20; ++j) {
760 ids.push_back(j);
761 }
762 writer_z(0, ids);
763 }
764 auto mrefs = z.finalize();
765 o2::aod::ManyReferences m{mrefs};
766
768 auto writer_w = w.cursor<o2::aod::References>();
769 for (auto i = 0; i < 5 * 20; ++i) {
770 writer_w(0, i % 20);
771 }
772 auto refs = w.finalize();
773 o2::aod::References r{refs};
774 expressions::Filter flt = o2::aod::test::someBool == true;
776 auto selection = expressions::createSelection(o.asArrowTable(), flt);
777 Flt f{{o.asArrowTableRef()}, selection};
778 r.bindExternalIndices(&f);
779 auto it = r.begin();
780 it.moveByIndex(23);
781 REQUIRE(it.origint_as<Flt>().globalIndex() == 3);
782 it++;
783 REQUIRE(it.origint_as<Flt>().globalIndex() == 4);
784 it++;
785 REQUIRE(it.origint_as<Flt>().globalIndex() == 5);
786
787 m.bindExternalIndices(&f);
788 for (auto const& row : m) {
789 auto os = row.origints_as<Flt>();
790 auto fos = row.filtered_origints_as<Flt>();
791 REQUIRE(os.size() == 20);
792 REQUIRE(fos.size() == 6);
793 }
794}
795namespace o2::aod
796{
797namespace test
798{
799DECLARE_SOA_INDEX_COLUMN_FULL(SinglePoint, singlePoint, int32_t, Points3Ds, "");
800DECLARE_SOA_ARRAY_INDEX_COLUMN(Points3D, pointGroup);
801DECLARE_SOA_SLICE_INDEX_COLUMN(Points3D, pointSlice);
802DECLARE_SOA_SELF_INDEX_COLUMN(OtherPoint, otherPoint);
805} // namespace test
806
807DECLARE_SOA_TABLE(PointsRef, "TEST", "PTSREF", test::Points3DIdSlice, test::Points3DIds);
808DECLARE_SOA_TABLE(PointsRefF, "TEST", "PTSREFF", test::SinglePointId, test::Points3DIdSlice, test::Points3DIds);
809DECLARE_SOA_TABLE(PointsSelfIndex, "TEST", "PTSSLF", o2::soa::Index<>, test::X, test::Y, test::Z, test::OtherPointId,
810 test::PointSeqIdSlice, test::PointSetIds);
811} // namespace o2::aod
812
813TEST_CASE("TestAdvancedIndices")
814{
815 TableBuilder b1;
816 auto pwriter = b1.persist<int, int, int>({"fX", "fY", "fZ"});
817 for (auto i = 0; i < 20; ++i) {
818 pwriter(0, -1 * i, (int)(i / 2), 2 * i);
819 }
820 auto tpts1 = b1.finalize();
821
822 TableBuilder b2;
823 auto prwriter = b2.cursor<o2::aod::PointsRef>();
824 auto a = std::array{0, 1};
825 auto aa = std::vector{2, 3, 4};
826 prwriter(0, &a[0], aa);
827 a = {4, 10};
828 aa = {12, 2, 19};
829 prwriter(0, &a[0], aa);
830 auto t2 = b2.finalize();
831
832 auto pt = o2::aod::Points3Ds{tpts1};
833 auto prt = o2::aod::PointsRef{t2};
834 prt.bindExternalIndices(&pt);
835
836 auto it = prt.begin();
837 auto s1 = it.pointSlice();
838 auto g1 = it.pointGroup();
839 auto bb = std::same_as<decltype(s1), o2::aod::Points3Ds>;
840 REQUIRE(bb);
841 REQUIRE(s1.size() == 2);
842 aa = {2, 3, 4};
843 for (int i = 0; i < 3; ++i) {
844 REQUIRE(g1[i].globalIndex() == aa[i]);
845 }
846
847 // Check the X coordinate of the points in the pointGroup
848 // for the first point.
849 for (auto& p : it.pointGroup_as<o2::aod::Points3Ds>()) {
850 REQUIRE(p.x() == -1 * p.globalIndex());
851 }
852
853 ++it;
854 auto s2 = it.pointSlice();
855 auto g2 = it.pointGroup();
856 REQUIRE(s2.size() == 7);
857 aa = {12, 2, 19};
858 for (int i = 0; i < 3; ++i) {
859 REQUIRE(g2[i].globalIndex() == aa[i]);
860 }
861
863 expressions::Filter fltx = (o2::aod::test::x <= -6);
864 Flt f{{tpts1}, expressions::createSelection(tpts1, fltx)};
865 prt.bindExternalIndices(&f);
866
867 auto it2 = prt.begin();
868 auto s1f = it2.pointSlice_as<Flt>();
869 auto g1f = it2.pointGroup_as<Flt>();
870 REQUIRE(s1f.size() == 2);
871 aa = {2, 3, 4};
872 for (int i = 0; i < 3; ++i) {
873 REQUIRE(g1f[i].globalIndex() == aa[i]);
874 }
875
876 ++it2;
877 auto s2f = it2.pointSlice_as<Flt>();
878 auto g2f = it2.pointGroup_as<Flt>();
879 REQUIRE(s2f.size() == 7);
880 aa = {12, 2, 19};
881 for (int i = 0; i < 3; ++i) {
882 REQUIRE(g2f[i].globalIndex() == aa[i]);
883 }
884
885 TableBuilder b3;
886 auto pswriter = b3.cursor<o2::aod::PointsSelfIndex>();
887 int references[] = {19, 2, 0, 13, 4, 6, 5, 5, 11, 9, 3, 8, 16, 14, 1, 18, 12, 18, 2, 7};
888 int slice[2] = {-1, -1};
889 std::vector<int> pset;
890 std::array<int, 4> withSlices = {3, 6, 13, 19};
891 std::array<std::pair<int, int>, 4> bounds = {std::pair{1, 5}, std::pair{3, 3}, std::pair{11, 11}, std::pair{10, 18}};
892 std::array<int, 4> withSets = {0, 1, 13, 14};
893 unsigned int sizes[] = {3, 1, 5, 4};
894 unsigned int c1 = 0;
895 unsigned int c2 = 0;
896 for (auto i = 0; i < 20; ++i) {
897 pset.clear();
898 slice[0] = -1;
899 slice[1] = -1;
900 if (c1 < withSlices.size() && i == withSlices[c1]) {
901 slice[0] = bounds[c1].first;
902 slice[1] = bounds[c1].second;
903 ++c1;
904 }
905 if (c2 < withSets.size() && i == withSets[c2]) {
906 for (auto z = 0U; z < sizes[c2]; ++z) {
907 pset.push_back(i + 1 + z);
908 }
909 ++c2;
910 }
911 pswriter(0, -1 * i, 0.5 * i, 2 * i, references[i], slice, pset);
912 }
913 auto t3 = b3.finalize();
914 auto pst = o2::aod::PointsSelfIndex{t3};
915 pst.bindInternalIndicesTo(&pst);
916 auto i = 0;
917 c1 = 0;
918 c2 = 0;
919 for (auto& p : pst) {
920 auto op = p.otherPoint_as<o2::aod::PointsSelfIndex>();
921 auto bbb = std::same_as<decltype(op), o2::aod::PointsSelfIndex::iterator>;
922 REQUIRE(bbb);
923 REQUIRE(op.globalIndex() == references[i]);
924
925 auto ops = p.pointSeq_as<o2::aod::PointsSelfIndex>();
926 auto bbbs = std::same_as<decltype(ops), o2::aod::PointsSelfIndex>;
927 REQUIRE(bbbs);
928
929 if (i == withSlices[c1]) {
930 auto it = ops.begin();
931 REQUIRE(ops.size() == bounds[c1].second - bounds[c1].first + 1);
932 REQUIRE(it.globalIndex() == bounds[c1].first);
933 for (auto j = 1; j < ops.size(); ++j) {
934 ++it;
935 }
936 REQUIRE(it.globalIndex() == bounds[c1].second);
937 ++c1;
938 } else {
939 REQUIRE(ops.size() == 0);
940 }
941
942 auto opss = p.pointSet_as<o2::aod::PointsSelfIndex>();
943 auto bbba = std::same_as<decltype(opss), std::vector<o2::aod::PointsSelfIndex::iterator>>;
944 REQUIRE(bbba);
945
946 auto opss_ids = p.pointSetIds();
947 if (c2 < withSets.size() && i == withSets[c2]) {
948 REQUIRE(opss.size() == sizes[c2]);
949 REQUIRE(opss.begin()->globalIndex() == i + 1);
950 REQUIRE(opss.back().globalIndex() == i + sizes[c2]);
951 int c3 = 0;
952 for (auto& id : opss_ids) {
953 REQUIRE(id == i + 1 + c3);
954 ++c3;
955 }
956 ++c2;
957 } else {
958 REQUIRE(opss.size() == 0);
959 }
960 ++i;
961 }
962}
963
964namespace o2::aod
965{
966DECLARE_SOA_TABLE(PointsSelfRef, "TEST", "PTSSR", test::OtherPointId, test::PointSeqIdSlice, test::PointSetIds);
967} // namespace o2::aod
968
969TEST_CASE("TestSelfIndexRecursion")
970{
971 TableBuilder b3;
972 auto pswriter = b3.cursor<o2::aod::PointsSelfIndex>();
973 int references[] = {19, 2, 0, 13, 4, 6, 5, 5, 11, 9, 3, 8, 16, 14, 1, 18, 12, 18, 2, 7};
974 int slice[2] = {-1, -1};
975 std::vector<int> pset;
976 std::array<int, 4> withSlices = {3, 6, 13, 19};
977 std::array<std::pair<int, int>, 4> bounds = {std::pair{1, 5}, std::pair{3, 3}, std::pair{11, 11}, std::pair{10, 18}};
978 std::array<int, 4> withSets = {0, 1, 13, 14};
979 unsigned int sizes[] = {3, 1, 5, 4};
980 unsigned int c1 = 0;
981 unsigned int c2 = 0;
982 for (auto i = 0; i < 20; ++i) {
983 pset.clear();
984 slice[0] = -1;
985 slice[1] = -1;
986 if (c1 < withSlices.size() && i == withSlices[c1]) {
987 slice[0] = bounds[c1].first;
988 slice[1] = bounds[c1].second;
989 ++c1;
990 }
991 if (c2 < withSets.size() && i == withSets[c2]) {
992 for (auto z = 0U; z < sizes[c2]; ++z) {
993 pset.push_back(i + 1 + z);
994 }
995 ++c2;
996 }
997 pswriter(0, -1 * i, 0.5 * i, 2 * i, references[i], slice, pset);
998 }
999 auto t3 = b3.finalize();
1000 auto pst = o2::aod::PointsSelfIndex{t3};
1001 pst.bindInternalIndicesTo(&pst);
1002
1003 // FIXME: only 4 levels of recursive self-index dereference are tested
1004 for (auto& p : pst) {
1005 auto ops = p.pointSeq_as<o2::aod::PointsSelfIndex>();
1006 for (auto& pp : ops) {
1007 auto bpp = std::same_as<std::decay_t<decltype(pp)>, o2::aod::PointsSelfIndex::iterator>;
1008 REQUIRE(bpp);
1009 auto opps = pp.pointSeq_as<o2::aod::PointsSelfIndex>();
1010 for (auto& ppp : opps) {
1011 auto bppp = std::same_as<std::decay_t<decltype(ppp)>, o2::aod::PointsSelfIndex::iterator>;
1012 REQUIRE(bppp);
1013 auto oppps = ppp.pointSeq_as<o2::aod::PointsSelfIndex>();
1014 for (auto& pppp : oppps) {
1015 auto bpppp = std::same_as<std::decay_t<decltype(pppp)>, o2::aod::PointsSelfIndex::iterator>;
1016 REQUIRE(bpppp);
1017 auto opppps = pppp.pointSeq_as<o2::aod::PointsSelfIndex>();
1018 }
1019 }
1020 }
1021 }
1022
1025 auto corewriter = b.cursor<o2::aod::Points3Ds>();
1026 for (auto i = 0; i < 20; ++i) {
1027 corewriter(0, -1 * i, 0.5 * i, 2 * i);
1028 }
1029 auto t1 = b.finalize();
1030
1031 c1 = 0;
1032 c2 = 0;
1033 TableBuilder be;
1034 auto extwriter = be.cursor<o2::aod::PointsSelfRef>();
1035 for (auto i = 0; i < 20; ++i) {
1036 pset.clear();
1037 slice[0] = -1;
1038 slice[1] = -1;
1039 if (c1 < withSlices.size() && i == withSlices[c1]) {
1040 slice[0] = bounds[c1].first;
1041 slice[1] = bounds[c1].second;
1042 ++c1;
1043 }
1044 if (c2 < withSets.size() && i == withSets[c2]) {
1045 for (auto z = 0U; z < sizes[c2]; ++z) {
1046 pset.push_back(i + 1 + z);
1047 }
1048 ++c2;
1049 }
1050 extwriter(0, references[i], slice, pset);
1051 }
1052 auto t2 = be.finalize();
1053
1054 FullPoints fp({t1, t2});
1055 fp.bindInternalIndicesTo(&fp);
1056
1057 // FIXME: only 4 levels of recursive self-index dereference are tested
1058 // self-index binding should stay the same for recursive dereferences
1059 for (auto& p : fp) {
1060 REQUIRE(std::same_as<std::decay_t<decltype(p)>, FullPoints::iterator>);
1061 auto ops = p.pointSeq_as<FullPoints>();
1062 for (auto& pp : ops) {
1063 REQUIRE(std::same_as<std::decay_t<decltype(pp)>, FullPoints::iterator>);
1064 auto opps = pp.pointSeq_as<FullPoints>();
1065 for (auto& ppp : opps) {
1066 REQUIRE(std::same_as<std::decay_t<decltype(ppp)>, FullPoints::iterator>);
1067 auto oppps = ppp.pointSeq_as<FullPoints>();
1068 for (auto& pppp : oppps) {
1069 REQUIRE(std::same_as<std::decay_t<decltype(pppp)>, FullPoints::iterator>);
1070 auto opppps = pppp.pointSeq_as<FullPoints>();
1071 }
1072 }
1073 }
1074 }
1075
1076 auto const& fpa = fp;
1077
1078 // iterators acquired through different means should have consistent types
1079 for (auto& it1 : fpa) {
1080 [[maybe_unused]] auto it2 = fpa.rawIteratorAt(0);
1081 [[maybe_unused]] auto it3 = fpa.iteratorAt(0);
1082 auto bit1 = std::same_as<std::decay_t<decltype(it1)>, std::decay_t<decltype(it2)>>;
1083 REQUIRE(bit1);
1084 auto bit2 = std::same_as<std::decay_t<decltype(it1)>, std::decay_t<decltype(it3)>>;
1085 REQUIRE(bit2);
1086 }
1087
1088 using FilteredPoints = o2::soa::Filtered<FullPoints>;
1089 FilteredPoints ffp({t1, t2}, SelectionVector{1, 2, 3});
1090 ffp.bindInternalIndicesTo(&ffp);
1091
1092 // Filter should not interfere with self-index and the binding should stay the same
1093 for (auto& p : ffp) {
1094 REQUIRE(std::same_as<std::decay_t<decltype(p)>, FilteredPoints::iterator>);
1095 REQUIRE(std::same_as<std::decay_t<decltype(p)>::parent_t, FilteredPoints>);
1096 auto ops = p.pointSeq_as<typename std::decay_t<decltype(p)>::parent_t>();
1097 for (auto& pp : ops) {
1098 REQUIRE(std::same_as<std::decay_t<decltype(pp)>::parent_t, FilteredPoints>);
1099 auto opps = pp.pointSeq_as<FilteredPoints>();
1100 for (auto& ppp : opps) {
1101 REQUIRE(std::same_as<std::decay_t<decltype(ppp)>, FilteredPoints::iterator>);
1102 auto oppps = ppp.pointSeq_as<FilteredPoints>();
1103 for (auto& pppp : oppps) {
1104 REQUIRE(std::same_as<std::decay_t<decltype(pppp)>, FilteredPoints::iterator>);
1105 auto opppps = pppp.pointSeq_as<FilteredPoints>();
1106 }
1107 }
1108 }
1109 }
1110
1111 auto const& ffpa = ffp;
1112
1113 // rawIteratorAt() should create an unfiltered iterator, unlike begin() and iteratorAt()
1114 for (auto& it1 : ffpa) {
1115 [[maybe_unused]] auto it2 = ffpa.rawIteratorAt(0);
1116 [[maybe_unused]] auto it3 = ffpa.iteratorAt(0);
1117 using T1 = std::decay_t<decltype(it1)>;
1118 using T2 = std::decay_t<decltype(it2)>;
1119 using T3 = std::decay_t<decltype(it3)>;
1120 auto bit1 = !std::same_as<T1, T2>;
1121 REQUIRE(bit1);
1122 auto bit2 = !std::same_as<T1, T3>;
1123 REQUIRE(bit2);
1124 auto bit3 = std::same_as<typename T1::policy_t, typename T3::policy_t>;
1125 REQUIRE(bit3);
1126 auto bit4 = std::same_as<typename T1::policy_t, o2::soa::FilteredIndexPolicy>;
1127 REQUIRE(bit4);
1128 auto bit5 = std::same_as<typename T2::policy_t, o2::soa::DefaultIndexPolicy>;
1129 REQUIRE(bit5);
1130 }
1131}
1132
1133TEST_CASE("TestListColumns")
1134{
1136 auto writer = b.cursor<o2::aod::Lists>();
1137 std::vector<float> floats;
1138 std::vector<int> ints;
1139 for (auto i = 1; i < 11; ++i) {
1140 floats.clear();
1141 ints.clear();
1142 for (auto j = 0; j < i; ++j) {
1143 floats.push_back(0.1231233f * (float)j + 0.1982798f);
1144 ints.push_back(j + 10);
1145 }
1146
1147 writer(0, floats, ints);
1148 }
1149 auto lt = b.finalize();
1150 o2::aod::Lists tbl{lt};
1151 auto s = 1U;
1152 for (auto& row : tbl) {
1153 auto f = row.l1();
1154 auto i = row.l2();
1155 auto constexpr bf = std::same_as<decltype(f), gsl::span<const float, (size_t)-1>>;
1156 auto constexpr bi = std::same_as<decltype(i), gsl::span<const int, (size_t)-1>>;
1157 REQUIRE(bf);
1158 REQUIRE(bi);
1159 REQUIRE(f.size() == s);
1160 REQUIRE(i.size() == s);
1161
1162 for (auto j = 0u; j < f.size(); ++j) {
1163 REQUIRE(f[j] == 0.1231233f * (float)j + 0.1982798f);
1164 REQUIRE(i[j] == (int)j + 10);
1165 }
1166 ++s;
1167 }
1168}
1169
1170TEST_CASE("TestSliceByCached")
1171{
1173 auto writer = b.cursor<o2::aod::Origints>();
1174 for (auto i = 0; i < 20; ++i) {
1175 writer(0, i, i % 3 == 0);
1176 }
1177 auto origins = b.finalize();
1178 o2::aod::Origints o{origins};
1179
1181 auto writer_w = w.cursor<o2::aod::References>();
1182 auto step = -1;
1183 for (auto i = 0; i < 5 * 20; ++i) {
1184 if (i % 5 == 0) {
1185 ++step;
1186 }
1187 writer_w(0, step);
1188 }
1189 auto refs = w.finalize();
1190 o2::aod::References r{refs};
1191
1192 std::string key = "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType<o2::aod::Origints>());
1193 ArrowTableSlicingCache atscache({{o2::soa::getLabelFromType<o2::aod::References>(), o2::soa::getMatcherFromTypeForKey<o2::aod::References>(key), key}});
1194 auto s = atscache.updateCacheEntry(0, refs);
1195 SliceCache cache{&atscache};
1196
1197 for (auto& oi : o) {
1198 auto cachedSlice = r.sliceByCached(o2::aod::test::origintId, oi.globalIndex(), cache);
1199 REQUIRE(cachedSlice.size() == 5);
1200 for (auto& ri : cachedSlice) {
1201 REQUIRE(ri.origintId() == oi.globalIndex());
1202 }
1203 }
1204}
1205
1206TEST_CASE("TestSliceByCachedMismatched")
1207{
1209 auto writer = b.cursor<o2::aod::Origints>();
1210 for (auto i = 0; i < 20; ++i) {
1211 writer(0, i, i % 3 == 0);
1212 }
1213 auto origins = b.finalize();
1214 o2::aod::Origints o{origins};
1215
1217 auto writer_w = w.cursor<o2::aod::References>();
1218 auto step = -1;
1219 for (auto i = 0; i < 5 * 20; ++i) {
1220 if (i % 5 == 0) {
1221 ++step;
1222 }
1223 writer_w(0, step);
1224 }
1225 auto refs = w.finalize();
1226 o2::aod::References r{refs};
1227
1228 TableBuilder w2;
1229 auto writer_w2 = w2.cursor<o2::aod::OtherReferences>();
1230 step = -1;
1231 for (auto i = 0; i < 5 * 20; ++i) {
1232 if (i % 3 == 0) {
1233 ++step;
1234 }
1235 writer_w2(0, step);
1236 }
1237 auto refs2 = w2.finalize();
1238 o2::aod::OtherReferences r2{refs2};
1239
1241 J rr{{refs, refs2}};
1242
1243 auto key = "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType<o2::aod::Origints>()) + "_alt";
1244 ArrowTableSlicingCache atscache({{o2::soa::getLabelFromTypeForKey<J>(key), o2::soa::getMatcherFromTypeForKey<J>(key), key}});
1245 auto s = atscache.updateCacheEntry(0, refs2);
1246 SliceCache cache{&atscache};
1247
1248 for (auto& oi : o) {
1249 auto cachedSlice = rr.sliceByCached(o2::aod::test::altOrigintId, oi.globalIndex(), cache);
1250 REQUIRE(cachedSlice.size() == 3);
1251 for (auto& ri : cachedSlice) {
1252 REQUIRE(ri.altOrigintId() == oi.globalIndex());
1253 }
1254 }
1255}
1256
1257TEST_CASE("TestSliceByCachedFiltered")
1258{
1260 auto writer = b.cursor<o2::aod::Origints>();
1261 for (auto i = 0; i < 20; ++i) {
1262 writer(0, i, i % 3 == 0);
1263 }
1264 auto origins = b.finalize();
1265 o2::aod::Origints o{origins};
1266
1268 auto writer_w = w.cursor<o2::aod::References>();
1269 auto step = -1;
1270 for (auto i = 0; i < 5 * 20; ++i) {
1271 if (i % 5 == 0) {
1272 ++step;
1273 }
1274 writer_w(0, step);
1275 }
1276 auto refs = w.finalize();
1277 o2::aod::References r{refs};
1278
1279 TableBuilder w2;
1280 auto writer_w2 = w2.cursor<o2::aod::OtherReferences>();
1281 step = -1;
1282 for (auto i = 0; i < 5 * 20; ++i) {
1283 if (i % 3 == 0) {
1284 ++step;
1285 }
1286 writer_w2(0, step);
1287 }
1288 auto refs2 = w2.finalize();
1289 o2::aod::OtherReferences r2{refs2};
1290
1292 J rr{{refs, refs2}};
1293
1294 auto rrf = rr.select(o2::aod::test::altOrigintId > 2 && o2::aod::test::altOrigintId < 15);
1295
1296 auto key = "fIndex" + o2::framework::cutString(o2::soa::getLabelFromType<o2::aod::Origints>()) + "_alt";
1297 ArrowTableSlicingCache atscache({{o2::soa::getLabelFromTypeForKey<J>(key), o2::soa::getMatcherFromTypeForKey<J>(key), key}});
1298 auto s = atscache.updateCacheEntry(0, refs2);
1299 SliceCache cache{&atscache};
1300
1301 for (auto& oi : o) {
1302 auto cachedSlice = rrf.sliceByCached(o2::aod::test::altOrigintId, oi.globalIndex(), cache);
1303 if (oi.globalIndex() <= 2 || oi.globalIndex() >= 15) {
1304 CHECK(cachedSlice.size() == 0);
1305 } else {
1306 CHECK(cachedSlice.size() == 3);
1307 }
1308 for (auto& ri : cachedSlice) {
1309 REQUIRE(ri.altOrigintId() == oi.globalIndex());
1310 }
1311 }
1312}
1313
1314TEST_CASE("TestIndexUnboundExceptions")
1315{
1317 auto prwriter = b.cursor<o2::aod::PointsRefF>();
1318 auto a = std::array{0, 1};
1319 auto aa = std::vector{2, 3, 4};
1320 prwriter(0, 0, &a[0], aa);
1321 a = {4, 10};
1322 aa = {12, 2, 19};
1323 prwriter(0, 1, &a[0], aa);
1324 auto t = b.finalize();
1325 auto prt = o2::aod::PointsRefF{t};
1326
1327 for (auto& row : prt) {
1328 try {
1329 [[maybe_unused]] auto sp = row.singlePoint();
1330 } catch (RuntimeErrorRef ref) {
1331 REQUIRE(std::string{error_from_ref(ref).what} == "Index pointing to Points3Ds is not bound! Did you subscribe to the table?");
1332 }
1333 try {
1334 auto ps = row.pointSlice();
1335 } catch (RuntimeErrorRef ref) {
1336 REQUIRE(std::string{error_from_ref(ref).what} == "Index pointing to Points3Ds is not bound! Did you subscribe to the table?");
1337 }
1338 try {
1339 auto pg = row.pointGroup();
1340 } catch (RuntimeErrorRef ref) {
1341 REQUIRE(std::string{error_from_ref(ref).what} == "Index pointing to Points3Ds is not bound! Did you subscribe to the table?");
1342 }
1343 }
1344}
1345
1346namespace o2::aod
1347{
1348namespace test
1349{
1350DECLARE_SOA_COLUMN(SmallIntArray, smallIntArray, int8_t[32]);
1351DECLARE_SOA_BITMAP_COLUMN(BoolArray, boolArray, 32);
1352} // namespace test
1353
1354DECLARE_SOA_TABLE(BILists, "TEST", "BILISTS", o2::soa::Index<>, test::SmallIntArray, test::BoolArray);
1355} // namespace o2::aod
1356
1357TEST_CASE("TestArrayColumns")
1358{
1360 auto writer = b.cursor<o2::aod::BILists>();
1361 int8_t ii[32];
1362 uint32_t bb;
1363 for (auto i = 0; i < 20; ++i) {
1364 bb = 0;
1365 for (auto j = 0; j < 32; ++j) {
1366 ii[j] = j;
1367 if (j % 2 == 0) {
1368 bb |= 1 << j;
1369 }
1370 }
1371 writer(0, ii, bb);
1372 }
1373 auto t = b.finalize();
1374
1375 o2::aod::BILists li{t};
1376 for (auto const& row : li) {
1377 auto iir = row.smallIntArray();
1378 [[maybe_unused]] auto bbrr = row.boolArray_raw();
1379 REQUIRE(std::same_as<std::decay_t<decltype(iir)>, int8_t const*>);
1380 for (auto i = 0; i < 32; ++i) {
1381 REQUIRE(iir[i] == i);
1382 REQUIRE(row.boolArray_bit(i) == (i % 2 == 0));
1383 }
1384 }
1385}
1386
1387namespace o2::aod
1388{
1389namespace table
1390{
1392DECLARE_SOA_COLUMN(Two, two, float);
1393DECLARE_SOA_COLUMN(Three, three, double);
1394DECLARE_SOA_COLUMN(Four, four, int[2]);
1395DECLARE_SOA_DYNAMIC_COLUMN(Five, five, [](const int in[2]) -> float { return (float)in[0] / (float)in[1]; });
1396} // namespace table
1397
1398DECLARE_SOA_TABLE(MixTest, "AOD", "MIXTST",
1399 table::One, table::Two, table::Three, table::Four,
1400 table::Five<table::Four>);
1401} // namespace o2::aod
1402
1403TEST_CASE("TestCombinedGetter")
1404{
1406 auto writer = b.cursor<o2::aod::MixTest>();
1407 int f[2];
1408 for (auto i = 0; i < 20; ++i) {
1409 f[0] = i;
1410 f[1] = i + 1;
1412 }
1413 auto t = b.finalize();
1414 o2::aod::MixTest mt{t};
1415 auto count = 0;
1416 for (auto const& row : mt) {
1417 auto features1 = row.getValues<float, o2::aod::table::One, o2::aod::table::Three>();
1418 auto features2 = row.getValues<double, o2::aod::table::One, o2::aod::table::Two, o2::aod::table::Three>();
1419 auto features3 = row.getValues<float, o2::aod::table::Two, o2::aod::table::Five<o2::aod::table::Four>>();
1420 auto b1 = std::same_as<std::array<float, 2>, decltype(features1)>;
1421 REQUIRE(b1);
1422 auto b2 = std::same_as<std::array<double, 3>, decltype(features2)>;
1423 REQUIRE(b2);
1424 auto b3 = std::same_as<std::array<float, 2>, decltype(features3)>;
1425 REQUIRE(b3);
1426 REQUIRE(features1[0] == (float)count);
1427 REQUIRE(features1[1] == (float)(o2::constants::math::Almost0 * count));
1428
1429 REQUIRE(features2[0] == (double)count);
1430 REQUIRE(features2[1] == (double)(o2::constants::math::PI * count));
1431 REQUIRE(features2[2] == (double)(o2::constants::math::Almost0 * count));
1432
1433 REQUIRE(features3[0] == (float)(o2::constants::math::PI * count));
1434 REQUIRE(features3[1] == (float)((float)count / (float)(count + 1)));
1435 ++count;
1436 }
1437}
1438
1439TEST_CASE("TestWritingCursorLastIndexAndReserve")
1440{
1441 // Nails down the WritingCursor semantics the AOD-producer reserves depend on:
1442 // lastIndex() returns the *last index* (rows - 1), not the row count, and
1443 // reserve(newRows + lastIndex() + 1) reserves exactly the post-batch total so a
1444 // fully-filled, no-skip batch neither overruns (the fwdTrkCls crash) nor trips
1445 // the release() / per-row UnsafeAppend guard.
1446 Produces<o2::aod::Points> cursor; // Points has two persistent columns: X, Y
1447 auto* builder = new TableBuilder();
1449
1450 // Empty cursor: no row written, so the last index is -1 and rows == lastIndex()+1 == 0.
1451 REQUIRE(cursor.lastIndex() == -1);
1452
1453 // operator() increments before the append, but only to the index of the row it
1454 // writes: after N writes lastIndex() == N - 1, NOT N.
1455 cursor(10, 20);
1456 REQUIRE(cursor.lastIndex() == 0);
1457 cursor(11, 21);
1458 REQUIRE(cursor.lastIndex() == 1);
1459 cursor(12, 22);
1460 REQUIRE(cursor.lastIndex() == 2);
1461 REQUIRE(cursor.lastIndex() + 1 == 3); // rows-so-far == last index + 1
1462
1463 // Reserve a second batch the correct way: total = newRows + rowsSoFar
1464 // = newRows + (lastIndex() + 1).
1465 // The (buggy) newRows + lastIndex() would reserve 4 here and under-reserve the
1466 // 5th row; the + 1 makes it exactly 5.
1467 int64_t const newRows = 2;
1468 int64_t const reserved = newRows + cursor.lastIndex() + 1; // correct total -> reserve(5)
1469 cursor.reserve(reserved);
1470 cursor(13, 23); // row index 3
1471 cursor(14, 24); // row index 4 — fills the batch exactly (5 rows total)
1472 REQUIRE(cursor.lastIndex() == 4);
1473
1474 // The contract release() enforces: rows filled (lastIndex()+1) must not exceed
1475 // what was reserved. Correct (+1) gives reserved == 5 -> 5 <= 5 (green); the buggy
1476 // newRows + lastIndex() reserves only 4 -> 5 <= 4 fails (red).
1477 REQUIRE(cursor.lastIndex() + 1 <= reserved);
1478
1479 auto table = builder->finalize();
1480 REQUIRE(table->num_rows() == 5);
1481 REQUIRE(table->num_columns() == 2);
1482 cursor.release();
1483}
#define DECLARE_SOA_ARRAY_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:2833
#define DECLARE_SOA_SLICE_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:2692
#define DECLARE_SOA_DYNAMIC_COLUMN(_Name_, _Getter_,...)
Definition ASoA.h:3141
#define DECLARE_SOA_SELF_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:2976
#define DECLARE_SOA_TABLE(_Name_, _Origin_, _Desc_,...)
Definition ASoA.h:3229
#define DECLARE_SOA_EXPRESSION_COLUMN(_Name_, _Getter_, _Type_, _Expression_)
Definition ASoA.h:2547
#define DECLARE_SOA_COLUMN(_Name_, _Getter_, _Type_)
Definition ASoA.h:2473
#define DECLARE_SOA_INDEX_COLUMN_FULL(_Name_, _Getter_, _Type_, _Table_, _Suffix_)
Definition ASoA.h:2914
#define DECLARE_SOA_SELF_SLICE_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:3040
#define DECLARE_SOA_COLUMN_FULL(_Name_, _Getter_, _Type_, _Label_)
Definition ASoA.h:2346
#define DECLARE_SOA_TABLE_VERSIONED(_Name_, _Origin_, _Desc_, _Version_,...)
Definition ASoA.h:3232
#define DECLARE_SOA_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:2915
#define DECLARE_SOA_BITMAP_COLUMN(_Name_, _Getter_, _Size_)
Definition ASoA.h:2508
#define DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(_Name_, _Getter_)
Definition ASoA.h:3111
std::shared_ptr< arrow::Schema > schema
int32_t i
uint32_t op
const int16_t bb
Test
Definition Utils.h:55
useful math constants
uint16_t pos
Definition RawData.h:3
uint32_t one
Definition RawData.h:4
uint32_t j
Definition RawData.h:0
benchmark::State & st
StringRef key
std::shared_ptr< T > get(const HistName &histName)
auto persist(std::array< char const *, sizeof...(ARGS)+1 > const &columnNames)
std::shared_ptr< arrow::Table > finalize()
void bindInternalIndicesTo(I const *ptr)
Definition ASoA.h:3696
unfiltered_iterator begin()
Definition ASoA.h:2031
auto select(framework::expressions::Filter const &f) const
Definition ASoA.h:2156
#define CHECK
float sum(float s, o2::dcs::DataPointValue v)
Definition dcs-ccdb.cxx:39
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
GLint GLsizei count
Definition glcorearb.h:399
GLuint color
Definition glcorearb.h:1272
GLuint * ids
Definition glcorearb.h:647
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition glcorearb.h:2595
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition glcorearb.h:5034
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLintptr offset
Definition glcorearb.h:660
GLuint segments
Definition glcorearb.h:4946
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
std::shared_ptr< gandiva::SelectionVector > Selection
Definition Expressions.h:46
constexpr float Almost0
constexpr float PI
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
RuntimeError & error_from_ref(RuntimeErrorRef)
std::string cutString(std::string &&str)
Definition ASoA.cxx:314
auto createFieldsFromColumns(framework::pack< C... >)
Definition ASoA.h:79
constexpr auto join(Ts const &... t)
Definition ASoA.h:3512
std::vector< int64_t > SelectionVector
Definition ASoA.h:446
FIXME: do not use data model tables.
char what[MAX_RUNTIME_ERROR_SIZE]
int64_t lastIndex()
Last index inserted in the table.
bool resetCursor(LifetimeHolder< TableBuilder > builder)
A struct, containing the root of the expression tree.
TEST_CASE("TestMarkers")
Definition test_ASoA.cxx:75
HistogramRegistry foo()
std::vector< int > row