File indexing completed on 2025-01-18 09:54:47
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include <algorithm>
0011 #include <cstddef>
0012 #include <unordered_set>
0013
0014 #include "corecel/math/HashUtils.hh"
0015
0016 #include "Collection.hh"
0017
0018 namespace celeritas
0019 {
0020
0021
0022
0023
0024 template<class T, class I = ItemId<T>>
0025 class DedupeCollectionBuilder
0026 {
0027 public:
0028
0029
0030 using CollectionT = Collection<T, Ownership::value, MemSpace::host, I>;
0031 using value_type = T;
0032 using size_type = typename CollectionT::size_type;
0033 using ItemIdT = typename CollectionT::ItemIdT;
0034 using ItemRangeT = typename CollectionT::ItemRangeT;
0035
0036
0037 public:
0038
0039 explicit DedupeCollectionBuilder(CollectionT* collection);
0040
0041
0042
0043
0044 size_type size() const { return this->storage().size(); }
0045
0046
0047 ItemIdT size_id() const { return ItemIdT{this->size()}; }
0048
0049
0050
0051
0052 inline void reserve(std::size_t count);
0053
0054
0055 template<class InputIterator>
0056 inline ItemRangeT insert_back(InputIterator first, InputIterator last);
0057
0058
0059 inline ItemRangeT insert_back(std::initializer_list<value_type> init);
0060
0061
0062 inline ItemIdT push_back(value_type const& element);
0063
0064 private:
0065
0066
0067 using StorageT = typename CollectionT::StorageT;
0068
0069 struct HashRange
0070 {
0071 StorageT* storage{nullptr};
0072 std::size_t operator()(ItemRangeT) const;
0073 };
0074 struct EqualRange
0075 {
0076 StorageT* storage{nullptr};
0077 bool operator()(ItemRangeT const&, ItemRangeT const&) const;
0078 };
0079
0080
0081
0082 CollectionT* col_;
0083 std::unordered_set<ItemRangeT, HashRange, EqualRange> ranges_;
0084
0085
0086
0087
0088
0089 CELER_FORCEINLINE StorageT& storage() { return col_->storage(); }
0090 CELER_FORCEINLINE StorageT const& storage() const
0091 {
0092 return col_->storage();
0093 }
0094
0095 };
0096
0097
0098
0099
0100
0101
0102
0103 template<class T, class I>
0104 DedupeCollectionBuilder<T, I>::DedupeCollectionBuilder(CollectionT* collection)
0105 : col_{collection}
0106 , ranges_{0, HashRange{&this->storage()}, EqualRange{&this->storage()}}
0107 {
0108 CELER_EXPECT(col_);
0109 }
0110
0111
0112
0113
0114
0115 template<class T, class I>
0116 void DedupeCollectionBuilder<T, I>::reserve(std::size_t count)
0117 {
0118 this->storage().reserve(count);
0119 ranges_.reserve(count);
0120 }
0121
0122
0123
0124
0125
0126 template<class T, class I>
0127 template<class InputIterator>
0128 auto DedupeCollectionBuilder<T, I>::insert_back(InputIterator first,
0129 InputIterator last) -> ItemRangeT
0130 {
0131 auto& s = this->storage();
0132
0133 auto start = this->size_id();
0134 s.insert(s.end(), first, last);
0135 ItemRangeT result{start, this->size_id()};
0136 auto [iter, inserted] = ranges_.insert(result);
0137 if (!inserted)
0138 {
0139
0140 s.erase(s.begin() + start.unchecked_get(), s.end());
0141
0142 return *iter;
0143 }
0144 return result;
0145 }
0146
0147
0148
0149
0150
0151 template<class T, class I>
0152 auto DedupeCollectionBuilder<T, I>::insert_back(std::initializer_list<T> init)
0153 -> ItemRangeT
0154 {
0155 return this->insert_back(init.begin(), init.end());
0156 }
0157
0158
0159
0160
0161
0162 template<class T, class I>
0163 auto DedupeCollectionBuilder<T, I>::push_back(T const& el) -> ItemIdT
0164 {
0165 auto result = this->size_id();
0166 this->storage().push_back(el);
0167 return result;
0168 }
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178 template<class T, class I>
0179 std::size_t
0180 DedupeCollectionBuilder<T, I>::HashRange::operator()(ItemRangeT r) const
0181 {
0182 CELER_EXPECT(*r.end() <= storage->size());
0183
0184
0185 Span<T const> data{storage->data() + r.begin()->unchecked_get(), r.size()};
0186 return std::hash<decltype(data)>{}(data);
0187 }
0188
0189
0190
0191
0192
0193 template<class T, class I>
0194 bool DedupeCollectionBuilder<T, I>::EqualRange::operator()(
0195 ItemRangeT const& a, ItemRangeT const& b) const
0196 {
0197 CELER_EXPECT(*a.end() <= storage->size());
0198 CELER_EXPECT(*b.end() <= storage->size());
0199
0200 auto values_equal = [&s = *storage](ItemIdT left, ItemIdT right) {
0201 return s[left.unchecked_get()] == s[right.unchecked_get()];
0202 };
0203 return std::equal(a.begin(), a.end(), b.begin(), b.end(), values_equal);
0204 }
0205
0206
0207 }