Skip to content

Commit 8ebb4a1

Browse files
committed
Add test for std::unordered_multiset
1 parent 2f88ff6 commit 8ebb4a1

File tree

11 files changed

+448
-0
lines changed

11 files changed

+448
-0
lines changed

types/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
* [`set`](set): `std::set` with all `[Split]Index{32,64}` column types
88
* [`multiset`](multiset): `std::multiset` with all `[Split]Index{32,64}` column types
99
* [`unordered_set`](unordered_set): `std::unordered_set` with all `[Split]Index{32,64}` column types
10+
* [`unordered_multiset`](unordered_multiset): `std::unordered_multiset` with all `[Split]Index{32,64}` column types

types/unordered_multiset/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# `std::unordered_multiset`
2+
3+
* [`fundamental`](fundamental): `std::unordered_multiset<std::int32_t>`
4+
* [`nested`](nested): `std::unordered_multiset<std::unordered_multiset<std::int32_t>>`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# `std::unordered_multiset<std::int32_t>`
2+
3+
## Fields
4+
5+
* `[Split]Index{32,64}`
6+
7+
with the corresponding column type for the offset column of the collection parent field.
8+
All child fields use the default column encoding `Int32`.
9+
10+
## Entries
11+
12+
1. Single-element sets, with ascending values
13+
2. Empty sets
14+
3. Increasing number of elements in the set:
15+
one element in the first field, two elements in the second field, etc.
16+
4. Duplicate elements passed to the set constructor
17+
5. Unordered elements passed to the set constructor
18+
6. Duplicate and unordered elements passed to the set constructor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include <ROOT/REntry.hxx>
2+
#include <ROOT/RNTupleReader.hxx>
3+
4+
using ROOT::Experimental::REntry;
5+
using ROOT::Experimental::RNTupleReader;
6+
7+
#include <cstdint>
8+
#include <fstream>
9+
#include <ostream>
10+
#include <string>
11+
#include <string_view>
12+
#include <unordered_set>
13+
14+
using UnorderedMultiSet = std::unordered_multiset<std::int32_t>;
15+
16+
static void PrintUnorderedMultiSetValue(const REntry &entry,
17+
std::string_view name, std::ostream &os,
18+
bool last = false) {
19+
UnorderedMultiSet &value = *entry.GetPtr<UnorderedMultiSet>(name);
20+
os << " \"" << name << "\": [";
21+
bool first = true;
22+
for (auto element : value) {
23+
if (first) {
24+
first = false;
25+
} else {
26+
os << ",";
27+
}
28+
os << "\n " << element;
29+
}
30+
if (!value.empty()) {
31+
os << "\n ";
32+
}
33+
os << "]";
34+
if (!last) {
35+
os << ",";
36+
}
37+
os << "\n";
38+
}
39+
40+
void read(
41+
std::string_view input = "types.unordered_multiset.fundamental.root",
42+
std::string_view output = "types.unordered_multiset.fundamental.json") {
43+
std::ofstream os(std::string{output});
44+
os << "[\n";
45+
46+
auto reader = RNTupleReader::Open("ntpl", input);
47+
auto &entry = reader->GetModel().GetDefaultEntry();
48+
bool first = true;
49+
for (auto index : *reader) {
50+
reader->LoadEntry(index);
51+
52+
if (first) {
53+
first = false;
54+
} else {
55+
os << ",\n";
56+
}
57+
os << " {\n";
58+
59+
PrintUnorderedMultiSetValue(entry, "Index32", os);
60+
PrintUnorderedMultiSetValue(entry, "Index64", os);
61+
PrintUnorderedMultiSetValue(entry, "SplitIndex32", os);
62+
PrintUnorderedMultiSetValue(entry, "SplitIndex64", os, /*last=*/true);
63+
64+
os << " }";
65+
// Newline is intentionally missing, may need to print a comma before the
66+
// next entry.
67+
}
68+
os << "\n";
69+
os << "]\n";
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include <ROOT/RField.hxx>
2+
#include <ROOT/RNTupleModel.hxx>
3+
#include <ROOT/RNTupleUtil.hxx>
4+
#include <ROOT/RNTupleWriteOptions.hxx>
5+
#include <ROOT/RNTupleWriter.hxx>
6+
7+
using ROOT::Experimental::EColumnType;
8+
using ROOT::Experimental::RField;
9+
using ROOT::Experimental::RNTupleModel;
10+
using ROOT::Experimental::RNTupleWriteOptions;
11+
using ROOT::Experimental::RNTupleWriter;
12+
13+
#include <cstdint>
14+
#include <memory>
15+
#include <string_view>
16+
#include <unordered_set>
17+
18+
using UnorderedMultiSet = std::unordered_multiset<std::int32_t>;
19+
20+
static std::shared_ptr<UnorderedMultiSet>
21+
MakeUnorderedMultiSetField(RNTupleModel &model, std::string_view name,
22+
EColumnType indexType) {
23+
auto field = std::make_unique<RField<UnorderedMultiSet>>(name);
24+
field->SetColumnRepresentatives({{indexType}});
25+
model.AddField(std::move(field));
26+
return model.GetDefaultEntry().GetPtr<UnorderedMultiSet>(name);
27+
}
28+
29+
void write(
30+
std::string_view filename = "types.unordered_multiset.fundamental.root") {
31+
auto model = RNTupleModel::Create();
32+
33+
// Non-split index encoding
34+
auto Index32 =
35+
MakeUnorderedMultiSetField(*model, "Index32", EColumnType::kIndex32);
36+
auto Index64 =
37+
MakeUnorderedMultiSetField(*model, "Index64", EColumnType::kIndex64);
38+
39+
// Split index encoding
40+
auto SplitIndex32 = MakeUnorderedMultiSetField(*model, "SplitIndex32",
41+
EColumnType::kSplitIndex32);
42+
auto SplitIndex64 = MakeUnorderedMultiSetField(*model, "SplitIndex64",
43+
EColumnType::kSplitIndex64);
44+
45+
RNTupleWriteOptions options;
46+
options.SetCompression(0);
47+
auto writer =
48+
RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options);
49+
50+
// First entry: single-element sets, with ascending values
51+
*Index32 = {1};
52+
*Index64 = {2};
53+
*SplitIndex32 = {3};
54+
*SplitIndex64 = {4};
55+
writer->Fill();
56+
57+
// Second entry: empty sets
58+
*Index32 = {};
59+
*Index64 = {};
60+
*SplitIndex32 = {};
61+
*SplitIndex64 = {};
62+
writer->Fill();
63+
64+
// Third entry: increasing number of elements in the set
65+
*Index32 = {1};
66+
*Index64 = {2, 3};
67+
*SplitIndex32 = {4, 5, 6};
68+
*SplitIndex64 = {7, 8, 9, 10};
69+
writer->Fill();
70+
71+
// Fourth entry: duplicate elements in the set
72+
*Index32 = {1, 1};
73+
*Index64 = {2, 2};
74+
*SplitIndex32 = {3, 3};
75+
*SplitIndex64 = {4, 4};
76+
writer->Fill();
77+
78+
// Fifth entry: unordered elements in the set
79+
*Index32 = {2, 1};
80+
*Index64 = {4, 3};
81+
*SplitIndex32 = {6, 5};
82+
*SplitIndex64 = {8, 7};
83+
writer->Fill();
84+
85+
// Sixth entry: duplicate and unordered elements in the set
86+
*Index32 = {2, 1, 2};
87+
*Index64 = {4, 4, 3};
88+
*SplitIndex32 = {6, 6, 5};
89+
*SplitIndex64 = {7, 8, 8};
90+
writer->Fill();
91+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <cstdint>
2+
#include <unordered_set>
3+
4+
#ifdef __CLING__
5+
#pragma link off all globals;
6+
#pragma link off all classes;
7+
#pragma link off all functions;
8+
9+
#pragma link C++ nestedclasses;
10+
11+
#pragma link C++ class std::unordered_multiset<std::unordered_multiset<std::int32_t>>+;
12+
#endif
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CXX=g++
2+
CXXFLAGS_ROOT=$(shell root-config --cflags)
3+
ifeq ($(CXXFLAGS_ROOT),)
4+
$(error cannot find root-config: make sure to source thisroot.sh)
5+
endif
6+
CXXFLAGS=-Wall $(CXXFLAGS_ROOT)
7+
LDFLAGS=$(shell root-config --libs)
8+
9+
.PHONY: all clean
10+
11+
all: NestedUnorderedMultiset.cxx libNestedUnorderedMultiset.so
12+
13+
NestedUnorderedMultiset.cxx: NestedUnorderedMultiset.hxx LinkDef.h
14+
rootcling -f $@ $^
15+
16+
libNestedUnorderedMultiset.so: NestedUnorderedMultiset.cxx
17+
$(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
18+
19+
clean:
20+
rm -f NestedUnorderedMultiset.cxx NestedUnorderedMultiset_rdict.pcm libNestedUnorderedMultiset.so
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <unordered_set>
5+
6+
template<>
7+
struct std::hash<std::unordered_multiset<std::int32_t>> {
8+
std::size_t
9+
operator()(const std::unordered_multiset<std::int32_t> &s) const noexcept {
10+
std::size_t h = 0;
11+
for (const auto &el : s) {
12+
h ^= std::hash<std::int32_t>{}(el);
13+
}
14+
return h;
15+
}
16+
};
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# `std::unordered_multiset<std::unordered_multiset<std::int32_t>>`
2+
3+
## Fields
4+
5+
* `[Split]Index{32,64}`
6+
7+
with the corresponding column type for the offset column of the two collection parent fields.
8+
All child fields use the default column encoding `Int32`.
9+
10+
## Entries
11+
12+
1. Single-element sets, with ascending values
13+
2. Empty sets
14+
3. Increasing number of elements in the outer set, with arbitrary lengths of the inner sets
15+
4. Duplicate sets passed to the set constructor of the outer set
16+
5. Unordered sets (or arbitrary length) passed to the set constructor of the outer set
17+
18+
## Dictionaries
19+
20+
These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedUnorderedMultiset` shared object.
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#include <ROOT/REntry.hxx>
2+
#include <ROOT/RNTupleReader.hxx>
3+
4+
using ROOT::Experimental::REntry;
5+
using ROOT::Experimental::RNTupleReader;
6+
7+
#include <TSystem.h>
8+
9+
#include <cstdint>
10+
#include <filesystem>
11+
#include <fstream>
12+
#include <ostream>
13+
#include <string>
14+
#include <string_view>
15+
#include <unordered_set>
16+
17+
using UnorderedMultiset =
18+
std::unordered_multiset<std::unordered_multiset<std::int32_t>>;
19+
20+
static void PrintNestedUnorderedMultisetValue(const REntry &entry,
21+
std::string_view name,
22+
std::ostream &os,
23+
bool last = false) {
24+
UnorderedMultiset &value = *entry.GetPtr<UnorderedMultiset>(name);
25+
os << " \"" << name << "\": [";
26+
bool outerFirst = true;
27+
for (auto inner : value) {
28+
if (outerFirst) {
29+
outerFirst = false;
30+
} else {
31+
os << ",";
32+
}
33+
os << "\n [";
34+
bool innerFirst = true;
35+
for (auto element : inner) {
36+
if (innerFirst) {
37+
innerFirst = false;
38+
} else {
39+
os << ",";
40+
}
41+
os << "\n " << element;
42+
}
43+
if (!inner.empty()) {
44+
os << "\n ";
45+
}
46+
os << "]";
47+
}
48+
if (!value.empty()) {
49+
os << "\n ";
50+
}
51+
os << "]";
52+
if (!last) {
53+
os << ",";
54+
}
55+
os << "\n";
56+
}
57+
58+
void read(std::string_view input = "types.unordered_multiset.nested.root",
59+
std::string_view output = "types.unordered_multiset.nested.json") {
60+
if (!std::filesystem::exists("libNestedUnorderedMultiset.so")) {
61+
throw std::runtime_error("could not find the required ROOT dictionaries, "
62+
"please make sure to run `make` first");
63+
}
64+
gSystem->Load("libNestedUnorderedMultiset");
65+
66+
std::ofstream os(std::string{output});
67+
os << "[\n";
68+
69+
auto reader = RNTupleReader::Open("ntpl", input);
70+
auto &entry = reader->GetModel().GetDefaultEntry();
71+
bool first = true;
72+
for (auto index : *reader) {
73+
reader->LoadEntry(index);
74+
75+
if (first) {
76+
first = false;
77+
} else {
78+
os << ",\n";
79+
}
80+
os << " {\n";
81+
82+
PrintNestedUnorderedMultisetValue(entry, "Index32", os);
83+
PrintNestedUnorderedMultisetValue(entry, "Index64", os);
84+
PrintNestedUnorderedMultisetValue(entry, "SplitIndex32", os);
85+
PrintNestedUnorderedMultisetValue(entry, "SplitIndex64", os, /*last=*/true);
86+
87+
os << " }";
88+
// Newline is intentionally missing, may need to print a comma before the
89+
// next entry.
90+
}
91+
os << "\n";
92+
os << "]\n";
93+
}

0 commit comments

Comments
 (0)