Clingo
Loading...
Searching...
No Matches
hash.hh
1#pragma once
2
3#include <clingo/util/immutable_array.hh>
4#include <clingo/util/immutable_value.hh>
5#include <clingo/util/small_vector.hh>
6
7#include <cstring>
8#include <functional>
9#include <memory>
10#include <numeric>
11#include <optional>
12#include <span>
13#include <string>
14#include <string_view>
15#include <typeinfo>
16#include <variant>
17#include <vector>
18
19namespace CppClingo::Util {
20
23
25inline auto hash_mix(size_t h) -> size_t;
26
28template <class... T> inline auto hash_combine(T... a) -> size_t;
29
31auto value_hash(std::type_info const &x) -> size_t;
32
34template <class T> auto value_hash(T const &x) -> size_t;
35
37template <class T> auto value_hash(T *x) -> size_t;
38
40template <class T> auto value_hash(std::optional<T> const &x) -> size_t;
41
43template <class T> auto value_hash(std::reference_wrapper<T> const &x) -> size_t;
44
46template <class T, class D> auto value_hash(std::unique_ptr<T, D> const &x) -> size_t;
47
49template <class T> auto value_hash(immutable_value<T> const &x) -> size_t;
50
52template <class T, class U> auto value_hash(std::pair<T, U> const &x) -> size_t;
53
55template <class... T> auto value_hash(std::tuple<T...> const &x) -> size_t;
56
58template <class... T> auto value_hash(std::variant<T...> const &x) -> size_t;
59
61template <class T, size_t E> auto value_hash(std::span<T, E> const &x) -> size_t;
62
64template <class T, class A> auto value_hash(std::vector<T, A> const &x) -> size_t;
65
67template <class T> auto value_hash(Util::immutable_array<T> const &x) -> size_t;
68
70template <class T, size_t N> auto value_hash(Util::small_vector<T, N> const &x) -> size_t;
71
73auto value_hash(char const *x) -> size_t;
74
76auto value_hash(std::string_view const &x) -> size_t;
77
79auto value_hash(std::string const &x) -> size_t;
80
82template <class T> auto value_hash_range(T const &x) -> size_t;
83
85template <class T, class... Args> auto value_hash_record(Args const &...x) -> size_t;
86
90 template <class T> auto operator()(T const &x) const -> size_t { return value_hash(x); }
91};
92
96 using is_transparent = void;
97
99 template <class T, class U> auto operator()(T const &a, U const &b) const -> bool { return a == b; }
101 auto operator()(char const *a, char const *b) const -> bool { return std::strcmp(a, b) == 0; }
103 template <class T, class U>
104 auto operator()(std::reference_wrapper<T> const &a, std::reference_wrapper<U> const &b) const -> bool {
105 return operator()(a.get(), b.get());
106 }
108 template <class T, class U> auto operator()(std::optional<T> const &a, std::optional<U> const &b) const -> bool {
109 return (!a && !b) || (a && b && operator()(*a, *b));
110 }
112 template <class T, class U> auto operator()(T *a, U *b) const -> bool {
113 return a == b || (a != nullptr && b != nullptr && operator()(*a, *b));
114 }
116 template <class T, class D, class U, class E>
117 auto operator()(std::unique_ptr<T, D> const &a, std::unique_ptr<U, E> const &b) const -> bool {
118 return operator()(a.get(), b.get());
119 }
121 template <class T, class U, class V, class W>
122 auto operator()(std::pair<T, U> const &a, std::pair<V, W> const &b) const -> bool {
123 return operator()(a.first, b.first) && operator()(a.second, b.second);
124 }
126 template <class... T, class... U>
127 auto operator()(std::tuple<T...> const &a, std::tuple<U...> const &b) const -> bool {
128 static_assert(sizeof...(T) == sizeof...(U));
129 return [&, this]<size_t... I>([[maybe_unused]] std::index_sequence<I...> seq) {
130 return (this->operator()(std::get<I>(a), std::get<I>(b)) && ...);
131 }(std::index_sequence_for<T...>());
132 }
134 template <class... T, class... U>
135 auto operator()(std::variant<T...> const &a, std::variant<U...> const &b) const -> bool {
136 static_assert(sizeof...(T) == sizeof...(U));
137 auto i = a.index();
138 return i == b.index() && [&, this]<size_t... I>([[maybe_unused]] std::index_sequence<I...> seq) {
139 return ((i == I && this->operator()(std::get<I>(a), std::get<I>(b))) || ...);
140 }(std::index_sequence_for<T...>());
141 }
143 template <class T, size_t E, class U, size_t F>
144 auto operator()(std::span<T, E> const &a, std::span<U, F> const &b) const -> bool {
145 return std::equal(a.begin(), a.end(), b.begin(), b.end(), *this);
146 }
148 template <class T, class A, class U, class B>
149 auto operator()(std::vector<T, A> const &a, std::vector<U, B> const &b) const -> bool {
150 return std::equal(a.begin(), a.end(), b.begin(), b.end(), *this);
151 }
152};
153
158 public:
160 array_hash(size_t size) : size_{size} {}
162 template <class T> auto operator()(T const *sym) const -> size_t { return value_hash(std::span(sym, size_)); }
163
164 private:
165 size_t size_;
166};
167
172 public:
174 array_equal_to(size_t size) : size_{size} {}
176 template <class T> auto operator()(T const *a, T const *b) const -> bool {
177 return value_equal_to{}(std::span(a, size_), std::span(b, size_));
178 }
179
180 private:
181 size_t size_;
182};
183
184// Implementation of value_hash
185
186namespace Detail {
187
188inline auto hash_combine() -> size_t {
189 return 0;
190}
191
192inline auto hash_combine(size_t a) -> size_t {
193 return a;
194}
195
196inline auto hash_combine(size_t seed, size_t h) -> size_t {
197 // NOLINTBEGIN(readability-magic-numbers)
198 if constexpr (sizeof(size_t) == sizeof(uint64_t)) {
199 seed *= 0x87c37b91114253d5;
200 seed = (seed >> 31) | (seed << 33);
201 seed *= 0x4cf5ad432745937f;
202 seed ^= h;
203 seed = (seed >> 27) | (seed << 37);
204 seed = (seed * 5) + 0x52dce729;
205 } else {
206 seed *= 0xcc9e2d51;
207 seed = (seed >> 17) | (seed << 15);
208 seed *= 0x1b873593;
209 seed ^= h;
210 seed = (seed >> 19) | (seed << 13);
211 seed = (seed * 5) + 0xe6546b64;
212 }
213 return seed;
214 // NOLINTEND(readability-magic-numbers)
215}
216
217template <class... T> inline auto hash_combine(size_t a, size_t b, T... c) -> size_t {
218 return hash_combine(hash_combine(a, b), c...);
219}
220
221} // namespace Detail
222
223inline auto hash_mix(size_t h) -> size_t {
224 // NOLINTBEGIN(readability-magic-numbers)
225 if constexpr (sizeof(size_t) == sizeof(uint64_t)) {
226 h ^= h >> 33;
227 h *= 0xff51afd7ed558ccd;
228 h ^= h >> 33;
229 h *= 0xc4ceb9fe1a85ec53;
230 h ^= h >> 33;
231 } else {
232 h ^= h >> 16;
233 h *= 0x85ebca6b;
234 h ^= h >> 13;
235 h *= 0xc2b2ae35;
236 h ^= h >> 16;
237 }
238 return h;
239 // NOLINTEND(readability-magic-numbers)
240}
241
242template <class... T> inline auto hash_combine(T... a) -> size_t {
243 return Detail::hash_combine(a...);
244}
245
246template <class T> auto value_hash_range(T const &x) -> size_t {
247 // NOLINTBEGIN(readability-magic-numbers)
248 // note: we unroll the loop for small sizes, this compiles very well and leads to noticable speedups
249 // length 8 should cover typical sizes for argument tuples etc. in clingo
250 auto small = []<std::size_t... n>(std::index_sequence<n...>, auto const &x) {
251 return Util::hash_combine(Util::value_hash(x[n])...);
252 };
253 switch (x.size()) {
254 case 0:
255 return small(std::make_index_sequence<0>(), x);
256 case 1:
257 return small(std::make_index_sequence<1>(), x);
258 case 2:
259 return small(std::make_index_sequence<2>(), x);
260 case 3:
261 return small(std::make_index_sequence<3>(), x);
262 case 4:
263 return small(std::make_index_sequence<4>(), x);
264 case 5:
265 return small(std::make_index_sequence<5>(), x);
266 case 6:
267 return small(std::make_index_sequence<6>(), x);
268 case 7:
269 return small(std::make_index_sequence<7>(), x);
270 case 8:
271 return small(std::make_index_sequence<8>(), x);
272 default:
273 return std::accumulate(
274 x.begin() + 1, x.end(), Util::value_hash(*x.begin()),
275 [](auto const &seed, auto const &x) { return Util::hash_combine(seed, Util::value_hash(x)); });
276 }
277 // NOLINTEND(readability-magic-numbers)
278}
279
280inline auto value_hash(std::type_info const &x) -> size_t {
281 return hash_mix(x.hash_code());
282}
283
284template <class T> auto value_hash(T const &x) -> size_t {
285 if constexpr (requires { x.hash(); }) {
286 return x.hash();
287 } else if constexpr (std::is_arithmetic_v<T> || std::is_enum_v<T>) {
288 return hash_mix(std::hash<T>{}(x));
289 } else {
290 return std::hash<T>{}(x);
291 }
292}
293
294template <class T> auto value_hash(T *x) -> size_t {
295 if (x != nullptr) {
296 return value_hash(*x);
297 }
298 return 0;
299}
300
301template <class T> auto value_hash(std::optional<T> const &x) -> size_t {
302 if (x) {
303 return value_hash(*x);
304 }
305 return 0;
306}
307
308template <class T> auto value_hash(std::reference_wrapper<T> const &x) -> size_t {
309 return value_hash(x.get());
310}
311
312template <class T, class D> auto value_hash(std::unique_ptr<T, D> const &x) -> size_t {
313 if (x) {
314 return value_hash(*x);
315 }
316 return 0;
317}
318
319template <class T> auto value_hash(immutable_value<T> const &x) -> size_t {
320 if (x) {
321 return value_hash(*x);
322 }
323 return 0;
324}
325
326template <class T, class U> auto value_hash(std::pair<T, U> const &x) -> size_t {
327 return hash_combine(value_hash(x.first), value_hash(x.second));
328}
329
330template <class... T> auto value_hash(std::tuple<T...> const &x) -> size_t {
331 return [&x]<size_t... Indices>([[maybe_unused]] std::index_sequence<Indices...> indices) -> size_t {
332 return hash_combine(value_hash(std::get<Indices>(x))...);
333 }(std::index_sequence_for<T...>{});
334}
335
336template <class... T> auto value_hash(std::variant<T...> const &x) -> size_t {
337 return std::visit([](auto &&arg) { return hash_combine(value_hash(typeid(arg)), value_hash(arg)); }, x);
338}
339
340template <class T, size_t E> auto value_hash(std::span<T, E> const &x) -> size_t {
341 return value_hash_range(x);
342}
343
344template <class T, class A> auto value_hash(std::vector<T, A> const &x) -> size_t {
345 return value_hash_range(x);
346}
347
348template <class T> auto value_hash(Util::immutable_array<T> const &x) -> size_t {
349 return value_hash_range(x);
350}
351
352template <class T, size_t N> auto value_hash(Util::small_vector<T, N> const &x) -> size_t {
353 return value_hash_range(x);
354}
355
356inline auto value_hash(char const *x) -> size_t {
357 return std::hash<std::string_view>{}(x);
358}
359
360inline auto value_hash(std::string_view const &x) -> size_t {
361 return std::hash<std::string_view>{}(x);
362}
363
364inline auto value_hash(std::string const &x) -> size_t {
365 return std::hash<std::string_view>{}(x);
366}
367
368template <class T, class... Args> auto value_hash_record(Args const &...x) -> size_t {
369 return hash_combine(value_hash(x)...);
370}
371
372static constexpr unsigned int default_neighborhood_size = 62;
373
375
376} // namespace CppClingo::Util
Hasher for arrays of dynamic but fixed size.
Definition hash.hh:157
auto operator()(T const *sym) const -> size_t
Get the hash of the symbol array.
Definition hash.hh:162
array_hash(size_t size)
Initialize with the given size.
Definition hash.hh:160
An immutable array with efficient copying.
Definition immutable_array.hh:18
An immutable value imlementation.
Definition immutable_value.hh:19
A vector that misuses the begin, end and capacity pointers to store elements.
Definition small_vector.hh:26
auto value_hash_range(T const &x) -> size_t
Compute the hash for a given range of elements.
Definition hash.hh:246
auto value_hash(std::type_info const &x) -> size_t
Compute a hash for type_info.
Definition hash.hh:280
auto value_hash_record(Args const &...x) -> size_t
Compute and combine the hashes of the given arguments.
Definition hash.hh:368
auto hash_mix(size_t h) -> size_t
Perturb the given seed.
Definition hash.hh:223
auto hash_combine(T... a) -> size_t
Combine the given hashes.
Definition hash.hh:242
Comparison operator for arrays of dynamic but fixed size.
Definition hash.hh:171
array_equal_to(size_t size)
Initialize with the given size.
Definition hash.hh:174
auto operator()(T const *a, T const *b) const -> bool
Compare two symbols arrays.
Definition hash.hh:176
Helper class to compare pointers and some STL containers holding pointers by value.
Definition hash.hh:94
auto operator()(std::optional< T > const &a, std::optional< U > const &b) const -> bool
Compare optionals by value.
Definition hash.hh:108
auto operator()(std::unique_ptr< T, D > const &a, std::unique_ptr< U, E > const &b) const -> bool
Compare unique pointers by value.
Definition hash.hh:117
void is_transparent
Mark the comparison operator as transparent.
Definition hash.hh:96
auto operator()(T const &a, U const &b) const -> bool
Basic comparison.
Definition hash.hh:99
auto operator()(std::reference_wrapper< T > const &a, std::reference_wrapper< U > const &b) const -> bool
Compare reference wrappers by value.
Definition hash.hh:104
auto operator()(std::span< T, E > const &a, std::span< U, F > const &b) const -> bool
Compare spans by value.
Definition hash.hh:144
auto operator()(std::vector< T, A > const &a, std::vector< U, B > const &b) const -> bool
Compare vectors by value.
Definition hash.hh:149
auto operator()(char const *a, char const *b) const -> bool
Compare c-strings by value.
Definition hash.hh:101
auto operator()(std::tuple< T... > const &a, std::tuple< U... > const &b) const -> bool
Compare tuples by value.
Definition hash.hh:127
auto operator()(std::pair< T, U > const &a, std::pair< V, W > const &b) const -> bool
Compare pairs by value.
Definition hash.hh:122
auto operator()(std::variant< T... > const &a, std::variant< U... > const &b) const -> bool
Compare variants by value.
Definition hash.hh:135
auto operator()(T *a, U *b) const -> bool
Compare pointers by value.
Definition hash.hh:112
Compute a hash using std::hash.
Definition hash.hh:88
auto operator()(T const &x) const -> size_t
Compute the hash of the given value.
Definition hash.hh:90