| fpconv | ? | Boost | YES | many changes | | | fpconv | ? | Boost | YES | many changes | | ||||
| fastutf8 | ? | MIT | YES | many changes | | | fastutf8 | ? | MIT | YES | many changes | | ||||
| expected | v1.0 | Public Domain / CC0 | NO | | | | expected | v1.0 | Public Domain / CC0 | NO | | | ||||
| robin-hood | 3.9.1 | MIT | NO | | | |||||
| robin-hood | 3.9.1 | MIT | NO | | | |||||
| frozen | 1.0.1 | Apache 2 | NO | | |
serge-sans-paille <sguelton@quarkslab.com> | |||||
Jérôme Dumesnil <jerome.dumesnil@gmail.com> | |||||
Chris Beck <chbeck@tesla.com> |
target_sources(frozen-headers INTERFACE | |||||
"${prefix}/frozen/algorithm.h" | |||||
"${prefix}/frozen/map.h" | |||||
"${prefix}/frozen/random.h" | |||||
"${prefix}/frozen/set.h" | |||||
"${prefix}/frozen/string.h" | |||||
"${prefix}/frozen/unordered_map.h" | |||||
"${prefix}/frozen/unordered_set.h" | |||||
"${prefix}/frozen/bits/algorithms.h" | |||||
"${prefix}/frozen/bits/basic_types.h" | |||||
"${prefix}/frozen/bits/elsa.h" | |||||
"${prefix}/frozen/bits/pmh.h") |
Apache License | |||||
Version 2.0, January 2004 | |||||
http://www.apache.org/licenses/ | |||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||||
1. Definitions. | |||||
"License" shall mean the terms and conditions for use, reproduction, | |||||
and distribution as defined by Sections 1 through 9 of this document. | |||||
"Licensor" shall mean the copyright owner or entity authorized by | |||||
the copyright owner that is granting the License. | |||||
"Legal Entity" shall mean the union of the acting entity and all | |||||
other entities that control, are controlled by, or are under common | |||||
control with that entity. For the purposes of this definition, | |||||
"control" means (i) the power, direct or indirect, to cause the | |||||
direction or management of such entity, whether by contract or | |||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||||
outstanding shares, or (iii) beneficial ownership of such entity. | |||||
"You" (or "Your") shall mean an individual or Legal Entity | |||||
exercising permissions granted by this License. | |||||
"Source" form shall mean the preferred form for making modifications, | |||||
including but not limited to software source code, documentation | |||||
source, and configuration files. | |||||
"Object" form shall mean any form resulting from mechanical | |||||
transformation or translation of a Source form, including but | |||||
not limited to compiled object code, generated documentation, | |||||
and conversions to other media types. | |||||
"Work" shall mean the work of authorship, whether in Source or | |||||
Object form, made available under the License, as indicated by a | |||||
copyright notice that is included in or attached to the work | |||||
(an example is provided in the Appendix below). | |||||
"Derivative Works" shall mean any work, whether in Source or Object | |||||
form, that is based on (or derived from) the Work and for which the | |||||
editorial revisions, annotations, elaborations, or other modifications | |||||
represent, as a whole, an original work of authorship. For the purposes | |||||
of this License, Derivative Works shall not include works that remain | |||||
separable from, or merely link (or bind by name) to the interfaces of, | |||||
the Work and Derivative Works thereof. | |||||
"Contribution" shall mean any work of authorship, including | |||||
the original version of the Work and any modifications or additions | |||||
to that Work or Derivative Works thereof, that is intentionally | |||||
submitted to Licensor for inclusion in the Work by the copyright owner | |||||
or by an individual or Legal Entity authorized to submit on behalf of | |||||
the copyright owner. For the purposes of this definition, "submitted" | |||||
means any form of electronic, verbal, or written communication sent | |||||
to the Licensor or its representatives, including but not limited to | |||||
communication on electronic mailing lists, source code control systems, | |||||
and issue tracking systems that are managed by, or on behalf of, the | |||||
Licensor for the purpose of discussing and improving the Work, but | |||||
excluding communication that is conspicuously marked or otherwise | |||||
designated in writing by the copyright owner as "Not a Contribution." | |||||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||||
on behalf of whom a Contribution has been received by Licensor and | |||||
subsequently incorporated within the Work. | |||||
2. Grant of Copyright License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
copyright license to reproduce, prepare Derivative Works of, | |||||
publicly display, publicly perform, sublicense, and distribute the | |||||
Work and such Derivative Works in Source or Object form. | |||||
3. Grant of Patent License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
(except as stated in this section) patent license to make, have made, | |||||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||||
where such license applies only to those patent claims licensable | |||||
by such Contributor that are necessarily infringed by their | |||||
Contribution(s) alone or by combination of their Contribution(s) | |||||
with the Work to which such Contribution(s) was submitted. If You | |||||
institute patent litigation against any entity (including a | |||||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||||
or a Contribution incorporated within the Work constitutes direct | |||||
or contributory patent infringement, then any patent licenses | |||||
granted to You under this License for that Work shall terminate | |||||
as of the date such litigation is filed. | |||||
4. Redistribution. You may reproduce and distribute copies of the | |||||
Work or Derivative Works thereof in any medium, with or without | |||||
modifications, and in Source or Object form, provided that You | |||||
meet the following conditions: | |||||
(a) You must give any other recipients of the Work or | |||||
Derivative Works a copy of this License; and | |||||
(b) You must cause any modified files to carry prominent notices | |||||
stating that You changed the files; and | |||||
(c) You must retain, in the Source form of any Derivative Works | |||||
that You distribute, all copyright, patent, trademark, and | |||||
attribution notices from the Source form of the Work, | |||||
excluding those notices that do not pertain to any part of | |||||
the Derivative Works; and | |||||
(d) If the Work includes a "NOTICE" text file as part of its | |||||
distribution, then any Derivative Works that You distribute must | |||||
include a readable copy of the attribution notices contained | |||||
within such NOTICE file, excluding those notices that do not | |||||
pertain to any part of the Derivative Works, in at least one | |||||
of the following places: within a NOTICE text file distributed | |||||
as part of the Derivative Works; within the Source form or | |||||
documentation, if provided along with the Derivative Works; or, | |||||
within a display generated by the Derivative Works, if and | |||||
wherever such third-party notices normally appear. The contents | |||||
of the NOTICE file are for informational purposes only and | |||||
do not modify the License. You may add Your own attribution | |||||
notices within Derivative Works that You distribute, alongside | |||||
or as an addendum to the NOTICE text from the Work, provided | |||||
that such additional attribution notices cannot be construed | |||||
as modifying the License. | |||||
You may add Your own copyright statement to Your modifications and | |||||
may provide additional or different license terms and conditions | |||||
for use, reproduction, or distribution of Your modifications, or | |||||
for any such Derivative Works as a whole, provided Your use, | |||||
reproduction, and distribution of the Work otherwise complies with | |||||
the conditions stated in this License. | |||||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||||
any Contribution intentionally submitted for inclusion in the Work | |||||
by You to the Licensor shall be under the terms and conditions of | |||||
this License, without any additional terms or conditions. | |||||
Notwithstanding the above, nothing herein shall supersede or modify | |||||
the terms of any separate license agreement you may have executed | |||||
with Licensor regarding such Contributions. | |||||
6. Trademarks. This License does not grant permission to use the trade | |||||
names, trademarks, service marks, or product names of the Licensor, | |||||
except as required for reasonable and customary use in describing the | |||||
origin of the Work and reproducing the content of the NOTICE file. | |||||
7. Disclaimer of Warranty. Unless required by applicable law or | |||||
agreed to in writing, Licensor provides the Work (and each | |||||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
implied, including, without limitation, any warranties or conditions | |||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||||
appropriateness of using or redistributing the Work and assume any | |||||
risks associated with Your exercise of permissions under this License. | |||||
8. Limitation of Liability. In no event and under no legal theory, | |||||
whether in tort (including negligence), contract, or otherwise, | |||||
unless required by applicable law (such as deliberate and grossly | |||||
negligent acts) or agreed to in writing, shall any Contributor be | |||||
liable to You for damages, including any direct, indirect, special, | |||||
incidental, or consequential damages of any character arising as a | |||||
result of this License or out of the use or inability to use the | |||||
Work (including but not limited to damages for loss of goodwill, | |||||
work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses), even if such Contributor | |||||
has been advised of the possibility of such damages. | |||||
9. Accepting Warranty or Additional Liability. While redistributing | |||||
the Work or Derivative Works thereof, You may choose to offer, | |||||
and charge a fee for, acceptance of support, warranty, indemnity, | |||||
or other liability obligations and/or rights consistent with this | |||||
License. However, in accepting such obligations, You may act only | |||||
on Your own behalf and on Your sole responsibility, not on behalf | |||||
of any other Contributor, and only if You agree to indemnify, | |||||
defend, and hold each Contributor harmless for any liability | |||||
incurred by, or claims asserted against, such Contributor by reason | |||||
of your accepting any such warranty or additional liability. | |||||
END OF TERMS AND CONDITIONS | |||||
APPENDIX: How to apply the Apache License to your work. | |||||
To apply the Apache License to your work, attach the following | |||||
boilerplate notice, with the fields enclosed by brackets "[]" | |||||
replaced with your own identifying information. (Don't include | |||||
the brackets!) The text should be enclosed in the appropriate | |||||
comment syntax for the file format. We also recommend that a | |||||
file or class name and description of purpose be included on the | |||||
same "printed page" as the copyright notice for easier | |||||
identification within third-party archives. | |||||
Copyright 2017 Quarkslab | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_ALGORITHM_H | |||||
#define FROZEN_LETITGO_ALGORITHM_H | |||||
#include "frozen/bits/basic_types.h" | |||||
#include "frozen/bits/version.h" | |||||
#include "frozen/string.h" | |||||
namespace frozen { | |||||
// 'search' implementation if C++17 is not available | |||||
// https://en.cppreference.com/w/cpp/algorithm/search | |||||
template<class ForwardIterator, class Searcher> | |||||
ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher) | |||||
{ | |||||
return searcher(first, last).first; | |||||
} | |||||
// text book implementation from | |||||
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm | |||||
template <std::size_t size> class knuth_morris_pratt_searcher { | |||||
bits::carray<std::ptrdiff_t, size> step_; | |||||
bits::carray<char, size> needle_; | |||||
static constexpr bits::carray<std::ptrdiff_t, size> | |||||
build_kmp_cache(char const (&needle)[size + 1]) { | |||||
std::ptrdiff_t cnd = 0; | |||||
bits::carray<std::ptrdiff_t, size> cache; | |||||
cache.fill(-1); | |||||
for (std::size_t pos = 1; pos < size; ++pos) { | |||||
if (needle[pos] == needle[cnd]) { | |||||
cache[pos] = cache[cnd]; | |||||
cnd += 1; | |||||
} else { | |||||
cache[pos] = cnd; | |||||
cnd = cache[cnd]; | |||||
while (cnd >= 0 && needle[pos] != needle[cnd]) | |||||
cnd = cache[cnd]; | |||||
cnd += 1; | |||||
} | |||||
} | |||||
return cache; | |||||
} | |||||
public: | |||||
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1]) | |||||
: step_{build_kmp_cache(needle)}, needle_(needle) {} | |||||
template <class ForwardIterator> | |||||
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const { | |||||
std::size_t i = 0; | |||||
ForwardIterator iter = first; | |||||
while (iter != last) { | |||||
if (needle_[i] == *iter) { | |||||
if (i == (size - 1)) | |||||
return { iter - i, iter - i + size }; | |||||
++i; | |||||
++iter; | |||||
} else { | |||||
if (step_[i] > -1) { | |||||
i = step_[i]; | |||||
} else { | |||||
++iter; | |||||
i = 0; | |||||
} | |||||
} | |||||
} | |||||
return { last, last }; | |||||
} | |||||
}; | |||||
template <std::size_t N> | |||||
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(char const (&needle)[N]) { | |||||
return {needle}; | |||||
} | |||||
// text book implementation from | |||||
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm | |||||
template <std::size_t size> class boyer_moore_searcher { | |||||
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>; | |||||
using suffix_table_type = bits::carray<std::ptrdiff_t, size>; | |||||
skip_table_type skip_table_; | |||||
suffix_table_type suffix_table_; | |||||
bits::carray<char, size> needle_; | |||||
constexpr auto build_skip_table(char const (&needle)[size + 1]) { | |||||
skip_table_type skip_table; | |||||
skip_table.fill(size); | |||||
for (std::size_t i = 0; i < size - 1; ++i) | |||||
skip_table[needle[i]] -= i + 1; | |||||
return skip_table; | |||||
} | |||||
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) { | |||||
std::size_t suffixlen = size - pos; | |||||
for (std::size_t i = 0; i < suffixlen; i++) { | |||||
if (needle[i] != needle[pos + i]) | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
constexpr std::size_t suffix_length(char const (&needle)[size + 1], | |||||
std::size_t pos) { | |||||
// increment suffix length slen to the first mismatch or beginning | |||||
// of the word | |||||
for (std::size_t slen = 0; slen < pos ; slen++) | |||||
if (needle[pos - slen] != needle[size - 1 - slen]) | |||||
return slen; | |||||
return pos; | |||||
} | |||||
constexpr auto build_suffix_table(char const (&needle)[size + 1]) { | |||||
suffix_table_type suffix; | |||||
std::ptrdiff_t last_prefix_index = size - 1; | |||||
// first loop | |||||
for (std::ptrdiff_t p = size - 1; p >= 0; p--) { | |||||
if (is_prefix(needle, p + 1)) | |||||
last_prefix_index = p + 1; | |||||
suffix[p] = last_prefix_index + (size - 1 - p); | |||||
} | |||||
// second loop | |||||
for (std::size_t p = 0; p < size - 1; p++) { | |||||
auto slen = suffix_length(needle, p); | |||||
if (needle[p - slen] != needle[size - 1 - slen]) | |||||
suffix[size - 1 - slen] = size - 1 - p + slen; | |||||
} | |||||
return suffix; | |||||
} | |||||
public: | |||||
constexpr boyer_moore_searcher(char const (&needle)[size + 1]) | |||||
: skip_table_{build_skip_table(needle)}, | |||||
suffix_table_{build_suffix_table(needle)}, | |||||
needle_(needle) {} | |||||
template <class ForwardIterator> | |||||
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const { | |||||
if (size == 0) | |||||
return { first, first + size }; | |||||
ForwardIterator iter = first + size - 1; | |||||
while (iter < last) { | |||||
std::ptrdiff_t j = size - 1; | |||||
while (j > 0 && (*iter == needle_[j])) { | |||||
--iter; | |||||
--j; | |||||
} | |||||
if (*iter == needle_[0]) | |||||
return { iter, iter + size}; | |||||
iter += std::max(skip_table_[*iter], suffix_table_[j]); | |||||
} | |||||
return { last, last + size}; | |||||
} | |||||
}; | |||||
template <std::size_t N> | |||||
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(char const (&needle)[N]) { | |||||
return {needle}; | |||||
} | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H | |||||
#define FROZEN_LETITGO_BITS_ALGORITHMS_H | |||||
#include "frozen/bits/basic_types.h" | |||||
#include <limits> | |||||
#include <tuple> | |||||
namespace frozen { | |||||
namespace bits { | |||||
auto constexpr next_highest_power_of_two(std::size_t v) { | |||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 | |||||
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits; | |||||
v--; | |||||
for(std::size_t i = 1; i < trip_count; i <<= 1) | |||||
v |= v >> i; | |||||
v++; | |||||
return v; | |||||
} | |||||
template<class T> | |||||
auto constexpr log(T v) { | |||||
std::size_t n = 0; | |||||
while (v > 1) { | |||||
n += 1; | |||||
v >>= 1; | |||||
} | |||||
return n; | |||||
} | |||||
constexpr std::size_t bit_weight(std::size_t n) { | |||||
return (n <= 8*sizeof(unsigned int)) | |||||
+ (n <= 8*sizeof(unsigned long)) | |||||
+ (n <= 8*sizeof(unsigned long long)) | |||||
+ (n <= 128); | |||||
} | |||||
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>); | |||||
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>); | |||||
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>); | |||||
template<std::size_t N> | |||||
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) { | |||||
static_assert(N < 2, "unsupported type size"); | |||||
return {}; | |||||
} | |||||
template<std::size_t N> | |||||
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>())); | |||||
template <typename Iter, typename Compare> | |||||
constexpr auto min_element(Iter begin, const Iter end, | |||||
Compare const &compare) { | |||||
auto result = begin; | |||||
while (begin != end) { | |||||
if (compare(*begin, *result)) { | |||||
result = begin; | |||||
} | |||||
++begin; | |||||
} | |||||
return result; | |||||
} | |||||
template <class T> | |||||
constexpr void cswap(T &a, T &b) { | |||||
auto tmp = a; | |||||
a = b; | |||||
b = tmp; | |||||
} | |||||
template <class T, class U> | |||||
constexpr void cswap(std::pair<T, U> & a, std::pair<T, U> & b) { | |||||
cswap(a.first, b.first); | |||||
cswap(a.second, b.second); | |||||
} | |||||
template <class... Tys, std::size_t... Is> | |||||
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b, std::index_sequence<Is...>) { | |||||
using swallow = int[]; | |||||
(void) swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...}; | |||||
} | |||||
template <class... Tys> | |||||
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) { | |||||
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>()); | |||||
} | |||||
template <typename Iterator, class Compare> | |||||
constexpr Iterator partition(Iterator left, Iterator right, Compare const &compare) { | |||||
auto pivot = left + (right - left) / 2; | |||||
auto value = *pivot; | |||||
cswap(*right, *pivot); | |||||
for (auto it = left; 0 < right - it; ++it) { | |||||
if (compare(*it, value)) { | |||||
cswap(*it, *left); | |||||
left++; | |||||
} | |||||
} | |||||
cswap(*right, *left); | |||||
return left; | |||||
} | |||||
template <typename Iterator, class Compare> | |||||
constexpr void quicksort(Iterator left, Iterator right, Compare const &compare) { | |||||
while (0 < right - left) { | |||||
auto new_pivot = bits::partition(left, right, compare); | |||||
quicksort(left, new_pivot, compare); | |||||
left = new_pivot + 1; | |||||
} | |||||
} | |||||
template <typename T, std::size_t N, class Compare> | |||||
constexpr bits::carray<T, N> quicksort(bits::carray<T, N> const &array, | |||||
Compare const &compare) { | |||||
bits::carray<T, N> res = array; | |||||
quicksort(res.begin(), res.end() - 1, compare); | |||||
return res; | |||||
} | |||||
template <class T, class Compare> struct LowerBound { | |||||
T const &value_; | |||||
Compare const &compare_; | |||||
constexpr LowerBound(T const &value, Compare const &compare) | |||||
: value_(value), compare_(compare) {} | |||||
template <class ForwardIt> | |||||
inline constexpr ForwardIt doit_fast(ForwardIt first, | |||||
std::integral_constant<std::size_t, 0>) { | |||||
return first; | |||||
} | |||||
template <class ForwardIt, std::size_t N> | |||||
inline constexpr ForwardIt doit_fast(ForwardIt first, | |||||
std::integral_constant<std::size_t, N>) { | |||||
auto constexpr step = N / 2; | |||||
static_assert(N/2 == N - N / 2 - 1, "power of two minus 1"); | |||||
auto it = first + step; | |||||
auto next_it = compare_(*it, value_) ? it + 1 : first; | |||||
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{}); | |||||
} | |||||
template <class ForwardIt, std::size_t N> | |||||
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, true>) { | |||||
return doit_fast(first, std::integral_constant<std::size_t, N>{}); | |||||
} | |||||
template <class ForwardIt, std::size_t N> | |||||
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, false>) { | |||||
auto constexpr next_power = next_highest_power_of_two(N); | |||||
auto constexpr next_start = next_power / 2 - 1; | |||||
auto it = first + next_start; | |||||
if (compare_(*it, value_)) { | |||||
auto constexpr next = N - next_start - 1; | |||||
return doitfirst(it + 1, std::integral_constant<std::size_t, next>{}, std::integral_constant<bool, next_highest_power_of_two(next) - 1 == next>{}); | |||||
} | |||||
else | |||||
return doit_fast(first, std::integral_constant<std::size_t, next_start>{}); | |||||
} | |||||
template <class ForwardIt> | |||||
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, 1>, std::integral_constant<bool, false>) { | |||||
return doit_fast(first, std::integral_constant<std::size_t, 1>{}); | |||||
} | |||||
}; | |||||
template <std::size_t N, class ForwardIt, class T, class Compare> | |||||
constexpr ForwardIt lower_bound(ForwardIt first, const T &value, Compare const &compare) { | |||||
return LowerBound<T, Compare>{value, compare}.doitfirst(first, std::integral_constant<std::size_t, N>{}, std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{}); | |||||
} | |||||
template <std::size_t N, class Compare, class ForwardIt, class T> | |||||
constexpr bool binary_search(ForwardIt first, const T &value, | |||||
Compare const &compare) { | |||||
ForwardIt where = lower_bound<N>(first, value, compare); | |||||
return (!(where == first + N) && !(compare(value, *where))); | |||||
} | |||||
template<class InputIt1, class InputIt2> | |||||
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) | |||||
{ | |||||
for (; first1 != last1; ++first1, ++first2) { | |||||
if (!(*first1 == *first2)) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
template<class InputIt1, class InputIt2> | |||||
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) | |||||
{ | |||||
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) { | |||||
if (*first1 < *first2) | |||||
return true; | |||||
if (*first2 < *first1) | |||||
return false; | |||||
} | |||||
return (first1 == last1) && (first2 != last2); | |||||
} | |||||
} // namespace bits | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_BASIC_TYPES_H | |||||
#define FROZEN_LETITGO_BASIC_TYPES_H | |||||
#include "frozen/bits/exceptions.h" | |||||
#include <utility> | |||||
#include <iterator> | |||||
#include <string> | |||||
namespace frozen { | |||||
namespace bits { | |||||
// used as a fake argument for frozen::make_set and frozen::make_map in the case of N=0 | |||||
struct ignored_arg {}; | |||||
template <class T, std::size_t N> | |||||
class cvector { | |||||
T data [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise | |||||
std::size_t dsize = 0; | |||||
public: | |||||
// Container typdefs | |||||
using value_type = T; | |||||
using reference = value_type &; | |||||
using const_reference = const value_type &; | |||||
using pointer = value_type *; | |||||
using const_pointer = const value_type *; | |||||
using iterator = pointer; | |||||
using const_iterator = const_pointer; | |||||
using size_type = std::size_t; | |||||
using difference_type = std::ptrdiff_t; | |||||
// Constructors | |||||
constexpr cvector(void) = default; | |||||
constexpr cvector(size_type count, const T& value) : dsize(count) { | |||||
for (std::size_t i = 0; i < N; ++i) | |||||
data[i] = value; | |||||
} | |||||
// Iterators | |||||
constexpr iterator begin() noexcept { return data; } | |||||
constexpr iterator end() noexcept { return data + dsize; } | |||||
// Capacity | |||||
constexpr size_type size() const { return dsize; } | |||||
// Element access | |||||
constexpr reference operator[](std::size_t index) { return data[index]; } | |||||
constexpr const_reference operator[](std::size_t index) const { return data[index]; } | |||||
constexpr reference back() { return data[dsize - 1]; } | |||||
constexpr const_reference back() const { return data[dsize - 1]; } | |||||
// Modifiers | |||||
constexpr void push_back(const T & a) { data[dsize++] = a; } | |||||
constexpr void push_back(T && a) { data[dsize++] = std::move(a); } | |||||
constexpr void pop_back() { --dsize; } | |||||
constexpr void clear() { dsize = 0; } | |||||
}; | |||||
template <class T, std::size_t N> | |||||
class carray { | |||||
T data_ [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise | |||||
template <std::size_t M, std::size_t... I> | |||||
constexpr carray(T const (&init)[M], std::index_sequence<I...>) | |||||
: data_{init[I]...} {} | |||||
template <class Iter, std::size_t... I> | |||||
constexpr carray(Iter iter, std::index_sequence<I...>) | |||||
: data_{((void)I, *iter++)...} {} | |||||
public: | |||||
// Container typdefs | |||||
using value_type = T; | |||||
using reference = value_type &; | |||||
using const_reference = const value_type &; | |||||
using pointer = value_type *; | |||||
using const_pointer = const value_type *; | |||||
using iterator = pointer; | |||||
using const_iterator = const_pointer; | |||||
using reverse_iterator = std::reverse_iterator<iterator>; | |||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||||
using size_type = std::size_t; | |||||
using difference_type = std::ptrdiff_t; | |||||
// Constructors | |||||
constexpr carray(void) = default; | |||||
template <std::size_t M> | |||||
constexpr carray(T const (&init)[M]) | |||||
: carray(init, std::make_index_sequence<N>()) | |||||
{ | |||||
static_assert(M >= N, "Cannot initialize a carray with an smaller array"); | |||||
} | |||||
constexpr carray(std::initializer_list<T> init) | |||||
: carray(init.begin(), std::make_index_sequence<N>()) | |||||
{ | |||||
// clang & gcc doesn't recognize init.size() as a constexpr | |||||
// static_assert(init.size() >= N, "Cannot initialize a carray with an smaller initializer list"); | |||||
} | |||||
// Iterators | |||||
constexpr iterator begin() noexcept { return data_; } | |||||
constexpr const_iterator begin() const noexcept { return data_; } | |||||
constexpr const_iterator cbegin() const noexcept { return data_; } | |||||
constexpr iterator end() noexcept { return data_ + N; } | |||||
constexpr const_iterator end() const noexcept { return data_ + N; } | |||||
constexpr const_iterator cend() const noexcept { return data_ + N; } | |||||
constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } | |||||
constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } | |||||
constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } | |||||
constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } | |||||
constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } | |||||
constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } | |||||
// Capacity | |||||
constexpr size_type size() const { return N; } | |||||
constexpr size_type max_size() const { return N; } | |||||
// Element access | |||||
constexpr reference operator[](std::size_t index) { return data_[index]; } | |||||
constexpr const_reference operator[](std::size_t index) const { return data_[index]; } | |||||
constexpr reference at(std::size_t index) { | |||||
if (index > N) | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')')); | |||||
return data_[index]; | |||||
} | |||||
constexpr const_reference at(std::size_t index) const { | |||||
if (index > N) | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')')); | |||||
return data_[index]; | |||||
} | |||||
constexpr reference front() { return data_[0]; } | |||||
constexpr const_reference front() const { return data_[0]; } | |||||
constexpr reference back() { return data_[N - 1]; } | |||||
constexpr const_reference back() const { return data_[N - 1]; } | |||||
constexpr value_type* data() noexcept { return data_; } | |||||
constexpr const value_type* data() const noexcept { return data_; } | |||||
// Modifiers | |||||
constexpr void fill(const value_type& val) { | |||||
for (std::size_t i = 0; i < N; ++i) | |||||
data_[i] = val; | |||||
} | |||||
}; | |||||
template <class T> | |||||
class carray<T, 0> { | |||||
public: | |||||
// Container typdefs | |||||
using value_type = T; | |||||
using reference = value_type &; | |||||
using const_reference = const value_type &; | |||||
using pointer = value_type *; | |||||
using const_pointer = const value_type *; | |||||
using iterator = pointer; | |||||
using const_iterator = const_pointer; | |||||
using reverse_iterator = std::reverse_iterator<iterator>; | |||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||||
using size_type = std::size_t; | |||||
using difference_type = std::ptrdiff_t; | |||||
// Constructors | |||||
constexpr carray(void) = default; | |||||
}; | |||||
} // namespace bits | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H | |||||
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H | |||||
#include <cassert> | |||||
#ifdef _MSC_VER | |||||
// FIXME: find a way to implement that correctly for msvc | |||||
#define constexpr_assert(cond, msg) | |||||
#else | |||||
#define constexpr_assert(cond, msg)\ | |||||
assert(cond && msg); | |||||
#endif | |||||
#endif | |||||
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_DEFINES_H | |||||
#define FROZEN_LETITGO_DEFINES_H | |||||
#if defined(_MSVC_LANG) && !(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681 | |||||
#define FROZEN_LETITGO_IS_MSVC | |||||
#endif | |||||
// Code taken from https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have | |||||
#if defined(FROZEN_LETITGO_IS_MSVC) | |||||
#if _MSVC_LANG > 201402 | |||||
#define FROZEN_LETITGO_HAS_CXX17 1 | |||||
#else /* _MSVC_LANG > 201402 */ | |||||
#define FROZEN_LETITGO_HAS_CXX17 0 | |||||
#endif /* _MSVC_LANG > 201402 */ | |||||
#else /* _MSVC_LANG etc. */ | |||||
#if __cplusplus > 201402 | |||||
#define FROZEN_LETITGO_HAS_CXX17 1 | |||||
#else /* __cplusplus > 201402 */ | |||||
#define FROZEN_LETITGO_HAS_CXX17 0 | |||||
#endif /* __cplusplus > 201402 */ | |||||
#endif /* _MSVC_LANG etc. */ | |||||
// End if taken code | |||||
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC) | |||||
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has string_view in C++17 | |||||
#else | |||||
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>) | |||||
#define FROZEN_LETITGO_HAS_STRING_VIEW | |||||
#endif | |||||
#endif | |||||
#ifdef __cpp_char8_t | |||||
#define FROZEN_LETITGO_HAS_CHAR8T | |||||
#endif | |||||
#endif // FROZEN_LETITGO_DEFINES_H |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_ELSA_H | |||||
#define FROZEN_LETITGO_ELSA_H | |||||
#include <type_traits> | |||||
namespace frozen { | |||||
template <class T> struct elsa { | |||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, | |||||
"only supports integral types, specialize for other types"); | |||||
constexpr std::size_t operator()(T const &value, std::size_t seed) const { | |||||
std::size_t key = seed ^ static_cast<std::size_t>(value); | |||||
key = (~key) + (key << 21); // key = (key << 21) - key - 1; | |||||
key = key ^ (key >> 24); | |||||
key = (key + (key << 3)) + (key << 8); // key * 265 | |||||
key = key ^ (key >> 14); | |||||
key = (key + (key << 2)) + (key << 4); // key * 21 | |||||
key = key ^ (key >> 28); | |||||
key = key + (key << 31); | |||||
return key; | |||||
} | |||||
}; | |||||
template <class T> using anna = elsa<T>; | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_EXCEPTIONS_H | |||||
#define FROZEN_LETITGO_EXCEPTIONS_H | |||||
#if defined(FROZEN_NO_EXCEPTIONS) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) || (!defined(_MSC_VER) && !defined(__cpp_exceptions)) | |||||
#include <cstdlib> | |||||
#define FROZEN_THROW_OR_ABORT(_) std::abort() | |||||
#else | |||||
#include <stdexcept> | |||||
#define FROZEN_THROW_OR_ABORT(err) throw err | |||||
#endif | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
// inspired from http://stevehanov.ca/blog/index.php?id=119 | |||||
#ifndef FROZEN_LETITGO_PMH_H | |||||
#define FROZEN_LETITGO_PMH_H | |||||
#include "frozen/bits/algorithms.h" | |||||
#include "frozen/bits/basic_types.h" | |||||
#include <array> | |||||
#include <limits> | |||||
namespace frozen { | |||||
namespace bits { | |||||
// Function object for sorting buckets in decreasing order of size | |||||
struct bucket_size_compare { | |||||
template <typename B> | |||||
bool constexpr operator()(B const &b0, | |||||
B const &b1) const { | |||||
return b0.size() > b1.size(); | |||||
} | |||||
}; | |||||
// Step One in pmh routine is to take all items and hash them into buckets, | |||||
// with some collisions. Then process those buckets further to build a perfect | |||||
// hash function. | |||||
// pmh_buckets represents the initial placement into buckets. | |||||
template <size_t M> | |||||
struct pmh_buckets { | |||||
// Step 0: Bucket max is 2 * sqrt M | |||||
// TODO: Come up with justification for this, should it not be O(log M)? | |||||
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2)); | |||||
using bucket_t = cvector<std::size_t, bucket_max>; | |||||
carray<bucket_t, M> buckets; | |||||
uint64_t seed; | |||||
// Represents a reference to a bucket. This is used because the buckets | |||||
// have to be sorted, but buckets are big, making it slower than sorting refs | |||||
struct bucket_ref { | |||||
unsigned hash; | |||||
const bucket_t * ptr; | |||||
// Forward some interface of bucket | |||||
using value_type = typename bucket_t::value_type; | |||||
using const_iterator = typename bucket_t::const_iterator; | |||||
constexpr auto size() const { return ptr->size(); } | |||||
constexpr const auto & operator[](std::size_t idx) const { return (*ptr)[idx]; } | |||||
constexpr auto begin() const { return ptr->begin(); } | |||||
constexpr auto end() const { return ptr->end(); } | |||||
}; | |||||
// Make a bucket_ref for each bucket | |||||
template <std::size_t... Is> | |||||
carray<bucket_ref, M> constexpr make_bucket_refs(std::index_sequence<Is...>) const { | |||||
return {{ bucket_ref{Is, &buckets[Is]}... }}; | |||||
} | |||||
// Makes a bucket_ref for each bucket and sorts them by size | |||||
carray<bucket_ref, M> constexpr get_sorted_buckets() const { | |||||
carray<bucket_ref, M> result{this->make_bucket_refs(std::make_index_sequence<M>())}; | |||||
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{}); | |||||
return result; | |||||
} | |||||
}; | |||||
template <size_t M, class Item, size_t N, class Hash, class Key, class PRG> | |||||
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> & items, | |||||
Hash const & hash, | |||||
Key const & key, | |||||
PRG & prg) { | |||||
using result_t = pmh_buckets<M>; | |||||
result_t result{}; | |||||
bool rejected = false; | |||||
// Continue until all items are placed without exceeding bucket_max | |||||
while (1) { | |||||
for (auto & b : result.buckets) { | |||||
b.clear(); | |||||
} | |||||
result.seed = prg(); | |||||
rejected = false; | |||||
for (std::size_t i = 0; i < N; ++i) { | |||||
auto & bucket = result.buckets[hash(key(items[i]), static_cast<size_t>(result.seed)) % M]; | |||||
if (bucket.size() >= result_t::bucket_max) { | |||||
rejected = true; | |||||
break; | |||||
} | |||||
bucket.push_back(i); | |||||
} | |||||
if (!rejected) { return result; } | |||||
} | |||||
} | |||||
// Check if an item appears in a cvector | |||||
template<class T, size_t N> | |||||
constexpr bool all_different_from(cvector<T, N> & data, T & a) { | |||||
for (std::size_t i = 0; i < data.size(); ++i) | |||||
if (data[i] == a) | |||||
return false; | |||||
return true; | |||||
} | |||||
// Represents either an index to a data item array, or a seed to be used with | |||||
// a hasher. Seed must have high bit of 1, value has high bit of zero. | |||||
struct seed_or_index { | |||||
using value_type = uint64_t; | |||||
private: | |||||
static constexpr value_type MINUS_ONE = std::numeric_limits<value_type>::max(); | |||||
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1); | |||||
value_type value_ = 0; | |||||
public: | |||||
constexpr value_type value() const { return value_; } | |||||
constexpr bool is_seed() const { return value_ & HIGH_BIT; } | |||||
constexpr seed_or_index(bool is_seed, value_type value) | |||||
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {} | |||||
constexpr seed_or_index() = default; | |||||
constexpr seed_or_index(const seed_or_index &) = default; | |||||
constexpr seed_or_index & operator =(const seed_or_index &) = default; | |||||
}; | |||||
// Represents the perfect hash function created by pmh algorithm | |||||
template <std::size_t M, class Hasher> | |||||
struct pmh_tables { | |||||
uint64_t first_seed_; | |||||
carray<seed_or_index, M> first_table_; | |||||
carray<std::size_t, M> second_table_; | |||||
Hasher hash_; | |||||
// Looks up a given key, to find its expected index in carray<Item, N> | |||||
// Always returns a valid index, must use KeyEqual test after to confirm. | |||||
template <typename KeyType> | |||||
constexpr std::size_t lookup(const KeyType & key) const { | |||||
auto const d = first_table_[hash_(key, static_cast<size_t>(first_seed_)) % M]; | |||||
if (!d.is_seed()) { return static_cast<std::size_t>(d.value()); } // this is narrowing uint64 -> size_t but should be fine | |||||
else { return second_table_[hash_(key, static_cast<std::size_t>(d.value())) % M]; } | |||||
} | |||||
}; | |||||
// Make pmh tables for given items, hash function, prg, etc. | |||||
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG> | |||||
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> & | |||||
items, | |||||
Hash const &hash, | |||||
Key const &key, | |||||
PRG prg) { | |||||
// Step 1: Place all of the keys into buckets | |||||
auto step_one = make_pmh_buckets<M>(items, hash, key, prg); | |||||
// Step 2: Sort the buckets to process the ones with the most items first. | |||||
auto buckets = step_one.get_sorted_buckets(); | |||||
// G becomes the first hash table in the resulting pmh function | |||||
carray<seed_or_index, M> G; // Default constructed to "index 0" | |||||
// H becomes the second hash table in the resulting pmh function | |||||
constexpr std::size_t UNUSED = std::numeric_limits<std::size_t>::max(); | |||||
carray<std::size_t, M> H; | |||||
H.fill(UNUSED); | |||||
// Step 3: Map the items in buckets into hash tables. | |||||
for (const auto & bucket : buckets) { | |||||
auto const bsize = bucket.size(); | |||||
if (bsize == 1) { | |||||
// Store index to the (single) item in G | |||||
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M); | |||||
G[bucket.hash] = {false, static_cast<uint64_t>(bucket[0])}; | |||||
} else if (bsize > 1) { | |||||
// Repeatedly try different H of d until we find a hash function | |||||
// that places all items in the bucket into free slots | |||||
seed_or_index d{true, prg()}; | |||||
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots; | |||||
while (bucket_slots.size() < bsize) { | |||||
auto slot = hash(key(items[bucket[bucket_slots.size()]]), static_cast<size_t>(d.value())) % M; | |||||
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) { | |||||
bucket_slots.clear(); | |||||
d = {true, prg()}; | |||||
continue; | |||||
} | |||||
bucket_slots.push_back(slot); | |||||
} | |||||
// Put successful seed in G, and put indices to items in their slots | |||||
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M); | |||||
G[bucket.hash] = d; | |||||
for (std::size_t i = 0; i < bsize; ++i) | |||||
H[bucket_slots[i]] = bucket[i]; | |||||
} | |||||
} | |||||
// Any unused entries in the H table have to get changed to zero. | |||||
// This is because hashing should not fail or return an out-of-bounds entry. | |||||
// A lookup fails after we apply user-supplied KeyEqual to the query and the | |||||
// key found by hashing. Sending such queries to zero cannot hurt. | |||||
for (std::size_t i = 0; i < M; ++i) | |||||
if (H[i] == UNUSED) | |||||
H[i] = 0; | |||||
return {step_one.seed, G, H, hash}; | |||||
} | |||||
} // namespace bits | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_VERSION_H | |||||
#define FROZEN_LETITGO_VERSION_H | |||||
#define FROZEN_MAJOR_VERSION 1 | |||||
#define FROZEN_MINOR_VERSION 0 | |||||
#define FROZEN_PATCH_VERSION 1 | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_MAP_H | |||||
#define FROZEN_LETITGO_MAP_H | |||||
#include "frozen/bits/algorithms.h" | |||||
#include "frozen/bits/basic_types.h" | |||||
#include "frozen/bits/constexpr_assert.h" | |||||
#include "frozen/bits/exceptions.h" | |||||
#include "frozen/bits/version.h" | |||||
#include <utility> | |||||
namespace frozen { | |||||
namespace impl { | |||||
template <class Comparator> class CompareKey { | |||||
Comparator const comparator_; | |||||
public: | |||||
constexpr CompareKey(Comparator const &comparator) | |||||
: comparator_(comparator) {} | |||||
template <class Key, class Value> | |||||
constexpr int operator()(std::pair<Key, Value> const &self, | |||||
std::pair<Key, Value> const &other) const { | |||||
return comparator_(std::get<0>(self), std::get<0>(other)); | |||||
} | |||||
template <class Key, class Value> | |||||
constexpr int operator()(Key const &self_key, | |||||
std::pair<Key, Value> const &other) const { | |||||
return comparator_(self_key, std::get<0>(other)); | |||||
} | |||||
template <class Key, class Value> | |||||
constexpr int operator()(std::pair<Key, Value> const &self, | |||||
Key const &other_key) const { | |||||
return comparator_(std::get<0>(self), other_key); | |||||
} | |||||
template <class Key> | |||||
constexpr int operator()(Key const &self_key, Key const &other_key) const { | |||||
return comparator_(self_key, other_key); | |||||
} | |||||
}; | |||||
} // namespace impl | |||||
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>> | |||||
class map { | |||||
using container_type = bits::carray<std::pair<Key, Value>, N>; | |||||
impl::CompareKey<Compare> less_than_; | |||||
container_type items_; | |||||
public: | |||||
using key_type = Key; | |||||
using mapped_type = Value; | |||||
using value_type = typename container_type::value_type; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::difference_type; | |||||
using key_compare = decltype(less_than_); | |||||
using reference = typename container_type::reference; | |||||
using const_reference = typename container_type::const_reference; | |||||
using pointer = typename container_type::pointer; | |||||
using const_pointer = typename container_type::const_pointer; | |||||
using iterator = typename container_type::iterator; | |||||
using const_iterator = typename container_type::const_iterator; | |||||
using reverse_iterator = typename container_type::reverse_iterator; | |||||
using const_reverse_iterator = | |||||
typename container_type::const_reverse_iterator; | |||||
public: | |||||
/* constructors */ | |||||
constexpr map(container_type items, Compare const &compare) | |||||
: less_than_{compare} | |||||
, items_{bits::quicksort(items, less_than_)} {} | |||||
explicit constexpr map(container_type items) | |||||
: map{items, Compare{}} {} | |||||
constexpr map(std::initializer_list<value_type> items, Compare const &compare) | |||||
: map{container_type {items}, compare} { | |||||
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument"); | |||||
} | |||||
constexpr map(std::initializer_list<value_type> items) | |||||
: map{items, Compare{}} {} | |||||
/* element access */ | |||||
constexpr Value const& at(Key const &key) const { | |||||
return at_impl(*this, key); | |||||
} | |||||
constexpr Value& at(Key const &key) { | |||||
return at_impl(*this, key); | |||||
} | |||||
/* iterators */ | |||||
constexpr iterator begin() { return items_.begin(); } | |||||
constexpr const_iterator begin() const { return items_.begin(); } | |||||
constexpr const_iterator cbegin() const { return items_.cbegin(); } | |||||
constexpr iterator end() { return items_.end(); } | |||||
constexpr const_iterator end() const { return items_.end(); } | |||||
constexpr const_iterator cend() const { return items_.cend(); } | |||||
constexpr reverse_iterator rbegin() { return items_.rbegin(); } | |||||
constexpr const_reverse_iterator rbegin() const { return items_.rbegin(); } | |||||
constexpr const_reverse_iterator crbegin() const { return items_.crbegin(); } | |||||
constexpr reverse_iterator rend() { return items_.rend(); } | |||||
constexpr const_reverse_iterator rend() const { return items_.rend(); } | |||||
constexpr const_reverse_iterator crend() const { return items_.crend(); } | |||||
/* capacity */ | |||||
constexpr bool empty() const { return !N; } | |||||
constexpr size_type size() const { return N; } | |||||
constexpr size_type max_size() const { return N; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &key) const { | |||||
return bits::binary_search<N>(items_.begin(), key, less_than_); | |||||
} | |||||
constexpr const_iterator find(Key const &key) const { | |||||
return find_impl(*this, key); | |||||
} | |||||
constexpr iterator find(Key const &key) { | |||||
return find_impl(*this, key); | |||||
} | |||||
constexpr std::pair<const_iterator, const_iterator> | |||||
equal_range(Key const &key) const { | |||||
return equal_range_impl(*this, key); | |||||
} | |||||
constexpr std::pair<iterator, iterator> equal_range(Key const &key) { | |||||
return equal_range_impl(*this, key); | |||||
} | |||||
constexpr const_iterator lower_bound(Key const &key) const { | |||||
return lower_bound_impl(*this, key); | |||||
} | |||||
constexpr iterator lower_bound(Key const &key) { | |||||
return lower_bound_impl(*this, key); | |||||
} | |||||
constexpr const_iterator upper_bound(Key const &key) const { | |||||
return upper_bound_impl(*this, key); | |||||
} | |||||
constexpr iterator upper_bound(Key const &key) { | |||||
return upper_bound_impl(*this, key); | |||||
} | |||||
/* observers */ | |||||
constexpr key_compare key_comp() const { return less_than_; } | |||||
constexpr key_compare value_comp() const { return less_than_; } | |||||
private: | |||||
template <class This> | |||||
static inline constexpr auto& at_impl(This&& self, Key const &key) { | |||||
auto where = self.lower_bound(key); | |||||
if (where != self.end()) | |||||
return where->second; | |||||
else | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto find_impl(This&& self, Key const &key) { | |||||
auto where = self.lower_bound(key); | |||||
if ((where != self.end()) && !self.less_than_(key, *where)) | |||||
return where; | |||||
else | |||||
return self.end(); | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto equal_range_impl(This&& self, Key const &key) { | |||||
auto lower = self.lower_bound(key); | |||||
using lower_t = decltype(lower); | |||||
if (lower == self.end()) | |||||
return std::pair<lower_t, lower_t>{lower, lower}; | |||||
else | |||||
return std::pair<lower_t, lower_t>{lower, lower + 1}; | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto lower_bound_impl(This&& self, Key const &key) -> decltype(self.end()) { | |||||
auto where = bits::lower_bound<N>(self.items_.begin(), key, self.less_than_); | |||||
if ((where != self.end()) && !self.less_than_(key, *where)) | |||||
return where; | |||||
else | |||||
return self.end(); | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto upper_bound_impl(This&& self, Key const &key) -> decltype(self.end()) { | |||||
auto where = bits::lower_bound<N>(self.items_.begin(), key, self.less_than_); | |||||
if ((where != self.end()) && !self.less_than_(key, *where)) | |||||
return where + 1; | |||||
else | |||||
return self.end(); | |||||
} | |||||
}; | |||||
template <class Key, class Value, class Compare> | |||||
class map<Key, Value, 0, Compare> { | |||||
using container_type = bits::carray<std::pair<Key, Value>, 0>; | |||||
impl::CompareKey<Compare> less_than_; | |||||
public: | |||||
using key_type = Key; | |||||
using mapped_type = Value; | |||||
using value_type = typename container_type::value_type; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::difference_type; | |||||
using key_compare = decltype(less_than_); | |||||
using reference = typename container_type::reference; | |||||
using const_reference = typename container_type::const_reference; | |||||
using pointer = typename container_type::pointer; | |||||
using const_pointer = typename container_type::const_pointer; | |||||
using iterator = pointer; | |||||
using const_iterator = const_pointer; | |||||
using reverse_iterator = pointer; | |||||
using const_reverse_iterator = const_pointer; | |||||
public: | |||||
/* constructors */ | |||||
constexpr map(const map &other) = default; | |||||
constexpr map(std::initializer_list<value_type>, Compare const &compare) | |||||
: less_than_{compare} {} | |||||
constexpr map(std::initializer_list<value_type> items) | |||||
: map{items, Compare{}} {} | |||||
/* element access */ | |||||
constexpr mapped_type at(Key const &) const { | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key")); | |||||
} | |||||
constexpr mapped_type at(Key const &) { | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key")); | |||||
} | |||||
/* iterators */ | |||||
constexpr iterator begin() { return nullptr; } | |||||
constexpr const_iterator begin() const { return nullptr; } | |||||
constexpr const_iterator cbegin() const { return nullptr; } | |||||
constexpr iterator end() { return nullptr; } | |||||
constexpr const_iterator end() const { return nullptr; } | |||||
constexpr const_iterator cend() const { return nullptr; } | |||||
constexpr reverse_iterator rbegin() { return nullptr; } | |||||
constexpr const_reverse_iterator rbegin() const { return nullptr; } | |||||
constexpr const_reverse_iterator crbegin() const { return nullptr; } | |||||
constexpr reverse_iterator rend() { return nullptr; } | |||||
constexpr const_reverse_iterator rend() const { return nullptr; } | |||||
constexpr const_reverse_iterator crend() const { return nullptr; } | |||||
/* capacity */ | |||||
constexpr bool empty() const { return true; } | |||||
constexpr size_type size() const { return 0; } | |||||
constexpr size_type max_size() const { return 0; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &) const { return 0; } | |||||
constexpr const_iterator find(Key const &) const { return end(); } | |||||
constexpr iterator find(Key const &) { return end(); } | |||||
constexpr std::pair<const_iterator, const_iterator> | |||||
equal_range(Key const &) const { | |||||
return {end(), end()}; | |||||
} | |||||
constexpr std::pair<iterator, iterator> | |||||
equal_range(Key const &) { | |||||
return {end(), end()}; | |||||
} | |||||
constexpr const_iterator lower_bound(Key const &) const { return end(); } | |||||
constexpr iterator lower_bound(Key const &) { return end(); } | |||||
constexpr const_iterator upper_bound(Key const &) const { return end(); } | |||||
constexpr iterator upper_bound(Key const &) { return end(); } | |||||
/* observers */ | |||||
constexpr key_compare key_comp() const { return less_than_; } | |||||
constexpr key_compare value_comp() const { return less_than_; } | |||||
}; | |||||
template <typename T, typename U> | |||||
constexpr auto make_map(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) { | |||||
return map<T, U, 0>{}; | |||||
} | |||||
template <typename T, typename U, std::size_t N> | |||||
constexpr auto make_map(std::pair<T, U> const (&items)[N]) { | |||||
return map<T, U, N>{items}; | |||||
} | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_RANDOM_H | |||||
#define FROZEN_LETITGO_RANDOM_H | |||||
#include "frozen/bits/algorithms.h" | |||||
#include "frozen/bits/version.h" | |||||
#include <cstdint> | |||||
#include <type_traits> | |||||
namespace frozen { | |||||
template <class UIntType, UIntType a, UIntType c, UIntType m> | |||||
class linear_congruential_engine { | |||||
static_assert(std::is_unsigned<UIntType>::value, | |||||
"UIntType must be an unsigned integral type"); | |||||
public: | |||||
using result_type = UIntType; | |||||
static constexpr result_type multiplier = a; | |||||
static constexpr result_type increment = c; | |||||
static constexpr result_type modulus = m; | |||||
static constexpr result_type default_seed = 1u; | |||||
linear_congruential_engine() = default; | |||||
constexpr linear_congruential_engine(result_type s) { seed(s); } | |||||
void seed(result_type s = default_seed) { state_ = s; } | |||||
constexpr result_type operator()() { | |||||
using uint_least_t = bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>; | |||||
uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment; | |||||
// the static cast below may end up doing a truncation | |||||
if(modulus != 0) | |||||
state_ = static_cast<result_type>(tmp % modulus); | |||||
else | |||||
state_ = static_cast<result_type>(tmp); | |||||
return state_; | |||||
} | |||||
constexpr void discard(unsigned long long n) { | |||||
while (n--) | |||||
operator()(); | |||||
} | |||||
static constexpr result_type min() { return increment == 0u ? 1u : 0u; }; | |||||
static constexpr result_type max() { return modulus - 1u; }; | |||||
friend constexpr bool operator==(linear_congruential_engine const &self, | |||||
linear_congruential_engine const &other) { | |||||
return self.state_ == other.state_; | |||||
} | |||||
friend constexpr bool operator!=(linear_congruential_engine const &self, | |||||
linear_congruential_engine const &other) { | |||||
return !(self == other); | |||||
} | |||||
private: | |||||
result_type state_ = default_seed; | |||||
}; | |||||
using minstd_rand0 = | |||||
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>; | |||||
using minstd_rand = | |||||
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>; | |||||
// This generator is used by default in unordered frozen containers | |||||
using default_prg_t = minstd_rand; | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_SET_H | |||||
#define FROZEN_SET_H | |||||
#include "frozen/bits/algorithms.h" | |||||
#include "frozen/bits/basic_types.h" | |||||
#include "frozen/bits/constexpr_assert.h" | |||||
#include "frozen/bits/version.h" | |||||
#include <utility> | |||||
namespace frozen { | |||||
template <class Key, std::size_t N, class Compare = std::less<Key>> class set { | |||||
using container_type = bits::carray<Key, N>; | |||||
Compare less_than_; | |||||
container_type keys_; | |||||
public: | |||||
/* container typedefs*/ | |||||
using key_type = Key; | |||||
using value_type = Key; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::size_type; | |||||
using key_compare = Compare; | |||||
using value_compare = Compare; | |||||
using reference = typename container_type::const_reference; | |||||
using const_reference = reference; | |||||
using pointer = typename container_type::const_pointer; | |||||
using const_pointer = pointer; | |||||
using iterator = typename container_type::const_iterator; | |||||
using reverse_iterator = typename container_type::const_reverse_iterator; | |||||
using const_iterator = iterator; | |||||
using const_reverse_iterator = reverse_iterator; | |||||
public: | |||||
/* constructors */ | |||||
constexpr set(const set &other) = default; | |||||
constexpr set(container_type keys, Compare const & comp) | |||||
: less_than_{comp} | |||||
, keys_(bits::quicksort(keys, less_than_)) { | |||||
} | |||||
explicit constexpr set(container_type keys) | |||||
: set{keys, Compare{}} {} | |||||
constexpr set(std::initializer_list<Key> keys, Compare const & comp) | |||||
: set{container_type{keys}, comp} { | |||||
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument"); | |||||
} | |||||
constexpr set(std::initializer_list<Key> keys) | |||||
: set{keys, Compare{}} {} | |||||
/* capacity */ | |||||
constexpr bool empty() const { return !N; } | |||||
constexpr size_type size() const { return N; } | |||||
constexpr size_type max_size() const { return N; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &key) const { | |||||
return bits::binary_search<N>(keys_.begin(), key, less_than_); | |||||
} | |||||
constexpr const_iterator find(Key const &key) const { | |||||
const_iterator where = lower_bound(key); | |||||
if ((where != end()) && !less_than_(key, *where)) | |||||
return where; | |||||
else | |||||
return end(); | |||||
} | |||||
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const { | |||||
auto const lower = lower_bound(key); | |||||
if (lower == end()) | |||||
return {lower, lower}; | |||||
else | |||||
return {lower, lower + 1}; | |||||
} | |||||
constexpr const_iterator lower_bound(Key const &key) const { | |||||
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_); | |||||
if ((where != end()) && !less_than_(key, *where)) | |||||
return where; | |||||
else | |||||
return end(); | |||||
} | |||||
constexpr const_iterator upper_bound(Key const &key) const { | |||||
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_); | |||||
if ((where != end()) && !less_than_(key, *where)) | |||||
return where + 1; | |||||
else | |||||
return end(); | |||||
} | |||||
/* observers */ | |||||
constexpr key_compare key_comp() const { return less_than_; } | |||||
constexpr key_compare value_comp() const { return less_than_; } | |||||
/* iterators */ | |||||
constexpr const_iterator begin() const { return keys_.begin(); } | |||||
constexpr const_iterator cbegin() const { return keys_.cbegin(); } | |||||
constexpr const_iterator end() const { return keys_.end(); } | |||||
constexpr const_iterator cend() const { return keys_.cend(); } | |||||
constexpr const_reverse_iterator rbegin() const { return keys_.rbegin(); } | |||||
constexpr const_reverse_iterator crbegin() const { return keys_.crbegin(); } | |||||
constexpr const_reverse_iterator rend() const { return keys_.rend(); } | |||||
constexpr const_reverse_iterator crend() const { return keys_.crend(); } | |||||
/* comparison */ | |||||
constexpr bool operator==(set const& rhs) const { return bits::equal(begin(), end(), rhs.begin()); } | |||||
constexpr bool operator!=(set const& rhs) const { return !(*this == rhs); } | |||||
constexpr bool operator<(set const& rhs) const { return bits::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end()); } | |||||
constexpr bool operator<=(set const& rhs) const { return (*this < rhs) || (*this == rhs); } | |||||
constexpr bool operator>(set const& rhs) const { return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(), end()); } | |||||
constexpr bool operator>=(set const& rhs) const { return (*this > rhs) || (*this == rhs); } | |||||
}; | |||||
template <class Key, class Compare> class set<Key, 0, Compare> { | |||||
using container_type = bits::carray<Key, 0>; // just for the type definitions | |||||
Compare less_than_; | |||||
public: | |||||
/* container typedefs*/ | |||||
using key_type = Key; | |||||
using value_type = Key; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::size_type; | |||||
using key_compare = Compare; | |||||
using value_compare = Compare; | |||||
using reference = typename container_type::const_reference; | |||||
using const_reference = reference; | |||||
using pointer = typename container_type::const_pointer; | |||||
using const_pointer = pointer; | |||||
using iterator = pointer; | |||||
using reverse_iterator = pointer; | |||||
using const_iterator = const_pointer; | |||||
using const_reverse_iterator = const_pointer; | |||||
public: | |||||
/* constructors */ | |||||
constexpr set(const set &other) = default; | |||||
constexpr set(bits::carray<Key, 0>, Compare const &) {} | |||||
explicit constexpr set(bits::carray<Key, 0>) {} | |||||
constexpr set(std::initializer_list<Key>, Compare const &comp) | |||||
: less_than_{comp} {} | |||||
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {} | |||||
/* capacity */ | |||||
constexpr bool empty() const { return true; } | |||||
constexpr size_type size() const { return 0; } | |||||
constexpr size_type max_size() const { return 0; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &) const { return 0; } | |||||
constexpr const_iterator find(Key const &) const { return end(); } | |||||
constexpr std::pair<const_iterator, const_iterator> | |||||
equal_range(Key const &) const { return {end(), end()}; } | |||||
constexpr const_iterator lower_bound(Key const &) const { return end(); } | |||||
constexpr const_iterator upper_bound(Key const &) const { return end(); } | |||||
/* observers */ | |||||
constexpr key_compare key_comp() const { return less_than_; } | |||||
constexpr key_compare value_comp() const { return less_than_; } | |||||
/* iterators */ | |||||
constexpr const_iterator begin() const { return nullptr; } | |||||
constexpr const_iterator cbegin() const { return nullptr; } | |||||
constexpr const_iterator end() const { return nullptr; } | |||||
constexpr const_iterator cend() const { return nullptr; } | |||||
constexpr const_reverse_iterator rbegin() const { return nullptr; } | |||||
constexpr const_reverse_iterator crbegin() const { return nullptr; } | |||||
constexpr const_reverse_iterator rend() const { return nullptr; } | |||||
constexpr const_reverse_iterator crend() const { return nullptr; } | |||||
}; | |||||
template <typename T> | |||||
constexpr auto make_set(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) { | |||||
return set<T, 0>{}; | |||||
} | |||||
template <typename T, std::size_t N> | |||||
constexpr auto make_set(const T (&args)[N]) { | |||||
return set<T, N>(args); | |||||
} | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_STRING_H | |||||
#define FROZEN_LETITGO_STRING_H | |||||
#include "frozen/bits/elsa.h" | |||||
#include "frozen/bits/version.h" | |||||
#include "frozen/bits/defines.h" | |||||
#include <functional> | |||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW | |||||
#include <string_view> | |||||
#endif | |||||
namespace frozen { | |||||
template <typename _CharT> | |||||
class basic_string { | |||||
using chr_t = _CharT; | |||||
chr_t const *data_; | |||||
std::size_t size_; | |||||
public: | |||||
template <std::size_t N> | |||||
constexpr basic_string(chr_t const (&data)[N]) | |||||
: data_(data), size_(N - 1) {} | |||||
constexpr basic_string(chr_t const *data, std::size_t size) | |||||
: data_(data), size_(size) {} | |||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW | |||||
constexpr basic_string(std::basic_string_view<chr_t> data) | |||||
: data_(data.data()), size_(data.size()) {} | |||||
#endif | |||||
constexpr basic_string(const basic_string &) noexcept = default; | |||||
constexpr basic_string &operator=(const basic_string &) noexcept = default; | |||||
constexpr std::size_t size() const { return size_; } | |||||
constexpr chr_t operator[](std::size_t i) const { return data_[i]; } | |||||
constexpr bool operator==(basic_string other) const { | |||||
if (size_ != other.size_) | |||||
return false; | |||||
for (std::size_t i = 0; i < size_; ++i) | |||||
if (data_[i] != other.data_[i]) | |||||
return false; | |||||
return true; | |||||
} | |||||
constexpr bool operator<(const basic_string &other) const { | |||||
unsigned i = 0; | |||||
while (i < size() && i < other.size()) { | |||||
if ((*this)[i] < other[i]) { | |||||
return true; | |||||
} | |||||
if ((*this)[i] > other[i]) { | |||||
return false; | |||||
} | |||||
++i; | |||||
} | |||||
return size() < other.size(); | |||||
} | |||||
constexpr const chr_t *data() const { return data_; } | |||||
}; | |||||
template <typename _CharT> struct elsa<basic_string<_CharT>> { | |||||
constexpr std::size_t operator()(basic_string<_CharT> value) const { | |||||
std::size_t d = 5381; | |||||
for (std::size_t i = 0; i < value.size(); ++i) | |||||
d = d * 33 + value[i]; | |||||
return d; | |||||
} | |||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function | |||||
// With the lowest bits removed, based on experimental setup. | |||||
constexpr std::size_t operator()(basic_string<_CharT> value, std::size_t seed) const { | |||||
std::size_t d = (0x811c9dc5 ^ seed) * 0x01000193; | |||||
for (std::size_t i = 0; i < value.size(); ++i) | |||||
d = (d ^ value[i]) * 0x01000193; | |||||
return d >> 8 ; | |||||
} | |||||
}; | |||||
using string = basic_string<char>; | |||||
using wstring = basic_string<wchar_t>; | |||||
using u16string = basic_string<char16_t>; | |||||
using u32string = basic_string<char32_t>; | |||||
#ifdef FROZEN_LETITGO_HAS_CHAR8T | |||||
using u8string = basic_string<char8_t>; | |||||
#endif | |||||
namespace string_literals { | |||||
constexpr string operator"" _s(const char *data, std::size_t size) { | |||||
return {data, size}; | |||||
} | |||||
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) { | |||||
return {data, size}; | |||||
} | |||||
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) { | |||||
return {data, size}; | |||||
} | |||||
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) { | |||||
return {data, size}; | |||||
} | |||||
#ifdef FROZEN_LETITGO_HAS_CHAR8T | |||||
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) { | |||||
return {data, size}; | |||||
} | |||||
#endif | |||||
} // namespace string_literals | |||||
} // namespace frozen | |||||
namespace std { | |||||
template <typename _CharT> struct hash<frozen::basic_string<_CharT>> { | |||||
size_t operator()(frozen::basic_string<_CharT> s) const { | |||||
return frozen::elsa<frozen::basic_string<_CharT>>{}(s); | |||||
} | |||||
}; | |||||
} // namespace std | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H | |||||
#define FROZEN_LETITGO_UNORDERED_MAP_H | |||||
#include "frozen/bits/basic_types.h" | |||||
#include "frozen/bits/constexpr_assert.h" | |||||
#include "frozen/bits/elsa.h" | |||||
#include "frozen/bits/exceptions.h" | |||||
#include "frozen/bits/pmh.h" | |||||
#include "frozen/bits/version.h" | |||||
#include "frozen/random.h" | |||||
#include <tuple> | |||||
#include <functional> | |||||
namespace frozen { | |||||
namespace bits { | |||||
struct GetKey { | |||||
template <class KV> constexpr auto const &operator()(KV const &kv) const { | |||||
return kv.first; | |||||
} | |||||
}; | |||||
} // namespace bits | |||||
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>, | |||||
class KeyEqual = std::equal_to<Key>> | |||||
class unordered_map { | |||||
static constexpr std::size_t storage_size = | |||||
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets | |||||
using container_type = bits::carray<std::pair<Key, Value>, N>; | |||||
using tables_type = bits::pmh_tables<storage_size, Hash>; | |||||
KeyEqual const equal_; | |||||
container_type items_; | |||||
tables_type tables_; | |||||
public: | |||||
/* typedefs */ | |||||
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>; | |||||
using key_type = Key; | |||||
using mapped_type = Value; | |||||
using value_type = typename container_type::value_type; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::difference_type; | |||||
using hasher = Hash; | |||||
using key_equal = KeyEqual; | |||||
using reference = typename container_type::reference; | |||||
using const_reference = typename container_type::const_reference; | |||||
using pointer = typename container_type::pointer; | |||||
using const_pointer = typename container_type::const_pointer; | |||||
using iterator = typename container_type::iterator; | |||||
using const_iterator = typename container_type::const_iterator; | |||||
public: | |||||
/* constructors */ | |||||
unordered_map(unordered_map const &) = default; | |||||
constexpr unordered_map(container_type items, | |||||
Hash const &hash, KeyEqual const &equal) | |||||
: equal_{equal} | |||||
, items_{items} | |||||
, tables_{ | |||||
bits::make_pmh_tables<storage_size>( | |||||
items_, hash, bits::GetKey{}, default_prg_t{})} {} | |||||
explicit constexpr unordered_map(container_type items) | |||||
: unordered_map{items, Hash{}, KeyEqual{}} {} | |||||
constexpr unordered_map(std::initializer_list<value_type> items, | |||||
Hash const & hash, KeyEqual const & equal) | |||||
: unordered_map{container_type{items}, hash, equal} { | |||||
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument"); | |||||
} | |||||
constexpr unordered_map(std::initializer_list<value_type> items) | |||||
: unordered_map{items, Hash{}, KeyEqual{}} {} | |||||
/* iterators */ | |||||
constexpr iterator begin() { return items_.begin(); } | |||||
constexpr iterator end() { return items_.end(); } | |||||
constexpr const_iterator begin() const { return items_.begin(); } | |||||
constexpr const_iterator end() const { return items_.end(); } | |||||
constexpr const_iterator cbegin() const { return items_.cbegin(); } | |||||
constexpr const_iterator cend() const { return items_.cend(); } | |||||
/* capacity */ | |||||
constexpr bool empty() const { return !N; } | |||||
constexpr size_type size() const { return N; } | |||||
constexpr size_type max_size() const { return N; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &key) const { | |||||
auto const &kv = lookup(key); | |||||
return equal_(kv.first, key); | |||||
} | |||||
constexpr Value const &at(Key const &key) const { | |||||
return at_impl(*this, key); | |||||
} | |||||
constexpr Value &at(Key const &key) { | |||||
return at_impl(*this, key); | |||||
} | |||||
constexpr const_iterator find(Key const &key) const { | |||||
return find_impl(*this, key); | |||||
} | |||||
constexpr iterator find(Key const &key) { | |||||
return find_impl(*this, key); | |||||
} | |||||
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const { | |||||
return equal_range_impl(*this, key); | |||||
} | |||||
constexpr std::pair<iterator, iterator> equal_range(Key const &key) { | |||||
return equal_range_impl(*this, key); | |||||
} | |||||
/* bucket interface */ | |||||
constexpr std::size_t bucket_count() const { return storage_size; } | |||||
constexpr std::size_t max_bucket_count() const { return storage_size; } | |||||
/* observers*/ | |||||
constexpr hasher hash_function() const { return tables_.hash_; } | |||||
constexpr key_equal key_eq() const { return equal_; } | |||||
private: | |||||
template <class This> | |||||
static inline constexpr auto& at_impl(This&& self, Key const &key) { | |||||
auto& kv = self.lookup(key); | |||||
if (self.equal_(kv.first, key)) | |||||
return kv.second; | |||||
else | |||||
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto find_impl(This&& self, Key const &key) { | |||||
auto& kv = self.lookup(key); | |||||
if (self.equal_(kv.first, key)) | |||||
return &kv; | |||||
else | |||||
return self.items_.end(); | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto equal_range_impl(This&& self, Key const &key) { | |||||
auto& kv = self.lookup(key); | |||||
using kv_ptr = decltype(&kv); | |||||
if (self.equal_(kv.first, key)) | |||||
return std::pair<kv_ptr, kv_ptr>{&kv, &kv + 1}; | |||||
else | |||||
return std::pair<kv_ptr, kv_ptr>{self.items_.end(), self.items_.end()}; | |||||
} | |||||
template <class This> | |||||
static inline constexpr auto& lookup_impl(This&& self, Key const &key) { | |||||
return self.items_[self.tables_.lookup(key)]; | |||||
} | |||||
constexpr auto const& lookup(Key const &key) const { | |||||
return lookup_impl(*this, key); | |||||
} | |||||
constexpr auto& lookup(Key const &key) { | |||||
return lookup_impl(*this, key); | |||||
} | |||||
}; | |||||
template <typename T, typename U, std::size_t N> | |||||
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) { | |||||
return unordered_map<T, U, N>{items}; | |||||
} | |||||
} // namespace frozen | |||||
#endif |
/* | |||||
* Frozen | |||||
* Copyright 2016 QuarksLab | |||||
* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
#ifndef FROZEN_LETITGO_UNORDERED_SET_H | |||||
#define FROZEN_LETITGO_UNORDERED_SET_H | |||||
#include "frozen/bits/basic_types.h" | |||||
#include "frozen/bits/constexpr_assert.h" | |||||
#include "frozen/bits/elsa.h" | |||||
#include "frozen/bits/pmh.h" | |||||
#include "frozen/bits/version.h" | |||||
#include "frozen/random.h" | |||||
#include <utility> | |||||
namespace frozen { | |||||
namespace bits { | |||||
struct Get { | |||||
template <class T> constexpr T const &operator()(T const &key) const { | |||||
return key; | |||||
} | |||||
}; | |||||
} // namespace bits | |||||
template <class Key, std::size_t N, typename Hash = elsa<Key>, | |||||
class KeyEqual = std::equal_to<Key>> | |||||
class unordered_set { | |||||
static constexpr std::size_t storage_size = | |||||
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets | |||||
using container_type = bits::carray<Key, N>; | |||||
using tables_type = bits::pmh_tables<storage_size, Hash>; | |||||
KeyEqual const equal_; | |||||
container_type keys_; | |||||
tables_type tables_; | |||||
public: | |||||
/* typedefs */ | |||||
using key_type = Key; | |||||
using value_type = Key; | |||||
using size_type = typename container_type::size_type; | |||||
using difference_type = typename container_type::difference_type; | |||||
using hasher = Hash; | |||||
using key_equal = KeyEqual; | |||||
using const_reference = typename container_type::const_reference; | |||||
using reference = const_reference; | |||||
using const_pointer = typename container_type::const_pointer; | |||||
using pointer = const_pointer; | |||||
using const_iterator = const_pointer; | |||||
using iterator = const_iterator; | |||||
public: | |||||
/* constructors */ | |||||
unordered_set(unordered_set const &) = default; | |||||
constexpr unordered_set(container_type keys, Hash const &hash, | |||||
KeyEqual const &equal) | |||||
: equal_{equal} | |||||
, keys_{keys} | |||||
, tables_{bits::make_pmh_tables<storage_size>( | |||||
keys_, hash, bits::Get{}, default_prg_t{})} {} | |||||
explicit constexpr unordered_set(container_type keys) | |||||
: unordered_set{keys, Hash{}, KeyEqual{}} {} | |||||
constexpr unordered_set(std::initializer_list<Key> keys) | |||||
: unordered_set{keys, Hash{}, KeyEqual{}} {} | |||||
constexpr unordered_set(std::initializer_list<Key> keys, Hash const & hash, KeyEqual const & equal) | |||||
: unordered_set{container_type{keys}, hash, equal} { | |||||
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument"); | |||||
} | |||||
/* iterators */ | |||||
constexpr const_iterator begin() const { return keys_.begin(); } | |||||
constexpr const_iterator end() const { return keys_.end(); } | |||||
constexpr const_iterator cbegin() const { return keys_.cbegin(); } | |||||
constexpr const_iterator cend() const { return keys_.cend(); } | |||||
/* capacity */ | |||||
constexpr bool empty() const { return !N; } | |||||
constexpr size_type size() const { return N; } | |||||
constexpr size_type max_size() const { return N; } | |||||
/* lookup */ | |||||
constexpr std::size_t count(Key const &key) const { | |||||
auto const k = lookup(key); | |||||
return equal_(k, key); | |||||
} | |||||
constexpr const_iterator find(Key const &key) const { | |||||
auto const &k = lookup(key); | |||||
if (equal_(k, key)) | |||||
return &k; | |||||
else | |||||
return keys_.end(); | |||||
} | |||||
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const { | |||||
auto const &k = lookup(key); | |||||
if (equal_(k, key)) | |||||
return {&k, &k + 1}; | |||||
else | |||||
return {keys_.end(), keys_.end()}; | |||||
} | |||||
/* bucket interface */ | |||||
constexpr std::size_t bucket_count() const { return storage_size; } | |||||
constexpr std::size_t max_bucket_count() const { return storage_size; } | |||||
/* observers*/ | |||||
constexpr hasher hash_function() const { return tables_.hash_; } | |||||
constexpr key_equal key_eq() const { return equal_; } | |||||
private: | |||||
constexpr auto const &lookup(Key const &key) const { | |||||
return keys_[tables_.lookup(key)]; | |||||
} | |||||
}; | |||||
template <typename T, std::size_t N> | |||||
constexpr auto make_unordered_set(T const (&keys)[N]) { | |||||
return unordered_set<T, N>{keys}; | |||||
} | |||||
} // namespace frozen | |||||
#endif |