From 97028451344b172150ed458290afbbe61780614e Mon Sep 17 00:00:00 2001 From: Randalphwa <38287198+Randalphwa@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:55:31 -0700 Subject: [PATCH] Refactor for C++23, reformat, bug fixes --- .clang-format | 100 ++++ CMakeLists.txt | 7 +- CMakePresets.json | 28 + README.md | 64 +- cmake/detect_cxx_version.cmake | 49 +- examples/CMakeLists.txt | 2 +- examples/constinit_config.cpp | 47 ++ examples/enum_to_string.cpp | 260 ++++---- examples/html_entities_map.cpp | 15 +- examples/string_view_lookup.cpp | 48 ++ examples/value_modification.cpp | 50 +- file_list.md | 20 + include/frozen/algorithm.h | 394 +++++++----- include/frozen/bits/algorithms.h | 435 +++++++------- include/frozen/bits/basic_types.h | 387 ++++++------ include/frozen/bits/constexpr_assert.h | 3 +- include/frozen/bits/defines.h | 51 +- include/frozen/bits/elsa.h | 67 ++- include/frozen/bits/elsa_std.h | 64 +- include/frozen/bits/exceptions.h | 12 +- include/frozen/bits/hash_string.h | 41 +- include/frozen/bits/mpl.h | 55 +- include/frozen/bits/pmh.h | 460 +++++++------- include/frozen/bits/version.h | 15 +- include/frozen/map.h | 743 +++++++++++++---------- include/frozen/random.h | 141 +++-- include/frozen/set.h | 509 +++++++++------- include/frozen/string.h | 181 ++---- include/frozen/unordered_map.h | 399 +++++++------ include/frozen/unordered_set.h | 324 +++++----- tests/CMakeLists.txt | 13 +- tests/no_exceptions.cpp | 23 +- tests/test_cpp23_features.cpp | 198 ++++++ tests/test_elsa_std.cpp | 71 ++- tests/test_map.cpp | 795 +++++++++++++------------ tests/test_set.cpp | 505 ++++++++-------- tests/test_str.cpp | 534 +++++++++-------- tests/test_str_set.cpp | 117 ++-- tests/test_unordered_map.cpp | 446 +++++++------- tests/test_unordered_map_str.cpp | 136 ++--- tests/test_unordered_set.cpp | 294 ++++----- tests/test_unordered_str_set.cpp | 129 ++-- 42 files changed, 4593 insertions(+), 3639 deletions(-) create mode 100644 .clang-format create mode 100644 CMakePresets.json create mode 100644 examples/constinit_config.cpp create mode 100644 examples/string_view_lookup.cpp create mode 100644 file_list.md create mode 100644 tests/test_cpp23_features.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f7dbb45 --- /dev/null +++ b/.clang-format @@ -0,0 +1,100 @@ +# https://releases.llvm.org/15.0.0/tools/clang/docs/ClangFormatStyleOptions.html + +AlignConsecutiveMacros: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowShortLambdasOnASingleLine: Empty +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^.*(precomp|pch|stdafx)' + Priority: -1 + - Regex: '^".*"' + Priority: 1 + - Regex: '^<.*>' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseBlocks : true +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b746f0..8c01481 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,11 +67,10 @@ add_library(frozen::frozen-headers ALIAS frozen-headers) target_link_libraries(frozen-headers INTERFACE frozen::frozen) # -# frozen requires C++ 14 support, at a minimum. Setting the `cxx_std_14` compile -# features ensures that the corresponding C++ standard flag is populated in -# targets linking to frozen +# frozen requires C++23. Setting the `cxx_std_23` compile feature ensures that +# the corresponding C++ standard flag is populated in all targets linking to frozen. # -target_compile_features(frozen INTERFACE cxx_std_14) +target_compile_features(frozen INTERFACE cxx_std_23) # # The include directory for frozen can be expected to vary between build diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..6ec1313 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,28 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 30, + "patch": 0 + }, + "configurePresets": [ + { + "name": "ninja-msvc", + "displayName": "Ninja Multi-Config", + "description": "Default build using Ninja Multi-Config generator", + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/build" + }, + { + "name": "ninja-clang", + "displayName": "Ninja Multi-Config (Clang)", + "description": "Build using Clang toolchain with Ninja Multi-Config generator", + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_RC_COMPILER": "llvm-rc" + } + } + ] +} diff --git a/README.md b/README.md index 114d11f..779b564 100644 --- a/README.md +++ b/README.md @@ -67,26 +67,30 @@ template std::enable_if_t< frozen::set{{1,11,111}}.count(N), int> foo(); ``` -String support is built-in: +String support is built-in via `std::string_view`: ```cpp #include -#include +#include // provides frozen::string (= std::string_view) +#include // hash specialization for std::string_view -constexpr frozen::unordered_map olaf = { +constexpr frozen::unordered_map olaf = { {"19", 19}, {"31", 31}, }; constexpr auto val = olaf.at("19"); ``` +`frozen::string` is a type alias for `std::string_view`. You can use either interchangeably. Include `` when using `std::string_view` or `std::string` as container keys — it provides the hash specialization. + The associative containers have different functionality with and without `constexpr`. With `constexpr`, frozen maps have immutable keys and values. Without `constexpr`, the values can be updated in runtime (the keys, however, remain immutable): ```cpp #include #include +#include -static constinit frozen::unordered_map voice = { +static constinit frozen::unordered_map voice = { {"Anna", "???"}, {"Elsa", "???"} }; @@ -113,7 +117,33 @@ For compatibility with STL's API, Frozen may eventually throw exceptions, as in ## Extending -Just like the regular C++23 container, you can specialize the hash function, the key equality comparator for `unordered_*` containers, and the comparison functions for the ordered version. +Just like the regular C++23 containers, you can specialize the hash function, the key equality comparator for `unordered_*` containers, and the comparison functions for the ordered version. + +### Heterogeneous Lookup (Default) + +All frozen containers now use transparent comparators by default (`std::less<>` for ordered containers, `std::equal_to<>` for unordered containers). This means you can look up keys with any type that is comparable to the key type, without explicit conversion: + +```cpp +#include +#include + +constexpr frozen::map m = { + {"hello", 1}, {"world", 2} +}; + +// All of these work — no std::string_view construction needed: +std::string key = "hello"; +m.count(key); // lookup with std::string +m.count("hello"); // lookup with const char* +m.at(std::string_view{"hello"}); // lookup with string_view +``` + +For ordered containers with numeric keys, you can also look up with implicitly convertible types: + +```cpp +constexpr frozen::set s = {10L, 20L, 30L}; +static_assert(s.count(20) == 1); // int lookup in long set +``` It's also possible to specialize the `frozen::elsa` structure used for hashing. Note that unlike `std::hash`, the hasher also takes a seed in addition to the value being hashed. @@ -124,6 +154,8 @@ template struct elsa { }; ``` +Hash specializations for `std::string_view` and `std::string` are provided in ``. Include this header when using standard string types as container keys. + Ideally, the hash function should have nice statistical properties like *pairwise-independence*: If `x` and `y` are different values, the chance that `elsa{}(x, seed) == elsa{}(y, seed)` should be very low for a random value of `seed`. @@ -143,10 +175,10 @@ Then either you've got a very big container and you should increase Clang's thre ```cpp struct olaf { - constexpr std::size_t operator()(frozen::string const &value, std::size_t seed) const { return seed ^ value[0];} + constexpr std::size_t operator()(std::string_view value, std::size_t seed) const { return seed ^ value[0];} }; -constexpr frozen::unordered_set hans = { "a", "b" }; +constexpr frozen::unordered_set hans = { "a", "b" }; ``` ## Tests and Benchmarks @@ -197,6 +229,24 @@ cmake --build . --target benchmark ## Credits +## Migration from v1.x + +This version requires **C++23** and includes the following breaking changes: + +| v1.x | v2.x (this version) | +|---|---| +| `frozen::string` (custom type) | `frozen::string` = `std::string_view` (alias) | +| Explicit `std::less` for transparent lookup | `std::less<>` is now the default comparator | +| Explicit `std::equal_to` for transparent lookup | `std::equal_to<>` is now the default equality | +| `#include ` for string keys | Also need `#include ` for hash | +| C++14/17 with feature-detection macros | C++23 required, no feature-detection macros | +| `FROZEN_LETITGO_HAS_DEDUCTION_GUIDES` | Deduction guides are always available | +| `FROZEN_LETITGO_HAS_STRING_VIEW` | `std::string_view` is always available | +| `FROZEN_LETITGO_HAS_CHAR8T` | `char8_t` is always available | +| `FROZEN_LETITGO_HAS_CONSTEVAL` | `consteval` is always available | + +## Credits + Thanks to Serge sans Paille for creating the original [frozen](https://github.com/serge-sans-paille/frozen) library. In the original frozen library, thanks to Jérôme Dumesnil for his high-quality reviews, and to Chris Beck for his contributions on perfect hashing. diff --git a/cmake/detect_cxx_version.cmake b/cmake/detect_cxx_version.cmake index 2021650..a4c1e3b 100644 --- a/cmake/detect_cxx_version.cmake +++ b/cmake/detect_cxx_version.cmake @@ -1,45 +1,22 @@ -set(cxx17_minimum_msvc_version 19.14) -set(cxx17_minimum_gcc_version 7.0) -set(cxx17_minimum_clang_version 4.0) -set(cxx17_minimum_appleclang_version 6.0) +set(cxx23_minimum_msvc_version 19.38) +set(cxx23_minimum_gcc_version 13.0) +set(cxx23_minimum_clang_version 16.0) +set(cxx23_minimum_appleclang_version 15.0) -set(cxx20_minimum_msvc_version 19.22) -set(cxx20_minimum_gcc_version 9.0) -set(cxx20_minimum_clang_version 7.0) -set(cxx20_minimum_appleclang_version 10.0) +set(cxx_23_supported OFF) -set(cxx_17_supported OFF) - -if(MSVC AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx17_minimum_msvc_version}) - set(cxx_17_supported ON) -endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx17_minimum_gcc_version}) - set(cxx_17_supported ON) -endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx17_minimum_clang_version}) - set(cxx_17_supported ON) -endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx17_minimum_appleclang_version}) - set(cxx_17_supported ON) -endif() - -set(cxx_20_supported OFF) - -if(MSVC AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx20_minimum_msvc_version}) - set(cxx_20_supported ON) +if(MSVC AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx23_minimum_msvc_version}) + set(cxx_23_supported ON) endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx20_minimum_gcc_version}) - set(cxx_20_supported ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx23_minimum_gcc_version}) + set(cxx_23_supported ON) endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx20_minimum_clang_version}) - set(cxx_20_supported ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx23_minimum_clang_version}) + set(cxx_23_supported ON) endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx20_minimum_appleclang_version}) - set(cxx_20_supported ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx23_minimum_appleclang_version}) + set(cxx_23_supported ON) endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2ae5917..2cd8a09 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -set(frozen.examples.srcs enum_to_string enum_to_string_hash pixel_art static_assert value_modification) +set(frozen.examples.srcs enum_to_string enum_to_string_hash pixel_art static_assert value_modification string_view_lookup constinit_config) if ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") list(APPEND frozen.examples.srcs html_entities_map) diff --git a/examples/constinit_config.cpp b/examples/constinit_config.cpp new file mode 100644 index 0000000..c9a8cd0 --- /dev/null +++ b/examples/constinit_config.cpp @@ -0,0 +1,47 @@ +/// @file constinit_config.cpp +/// Demonstrates a constinit frozen::unordered_map as a configuration table +/// with compile-time-selected keys and mutable runtime values. + +#include +#include +#include + +#include +#include + +// The keys are fixed at compile time (perfect hash, zero collision). +// Values start with defaults and can be mutated at runtime. +// constinit ensures the map is fully constructed during static initialization +// with no runtime overhead and no static-init-order fiasco. +constinit frozen::unordered_map config = { + { "max_connections", 100 }, + { "port", 8080 }, + { "timeout_ms", 5000 }, + { "retries", 3 }, +}; + +void print_config() +{ + std::cout << "Current configuration:\n"; + for (const auto& [key, value]: config) + { + std::cout << " " << key << " = " << value << "\n"; + } +} + +int main() +{ + print_config(); + + // Update values at runtime — keys remain immutable + config.at("port") = 443; + config.at("timeout_ms") = 30000; + config.at("retries") = 5; + + std::cout << "\nAfter update:\n"; + print_config(); + + // Lookup with const char* (heterogeneous, transparent equal_to<>) + const char* key = "max_connections"; + std::cout << "\n" << key << " = " << config.at(key) << "\n"; +} diff --git a/examples/enum_to_string.cpp b/examples/enum_to_string.cpp index a602400..d9285f8 100644 --- a/examples/enum_to_string.cpp +++ b/examples/enum_to_string.cpp @@ -1,16 +1,18 @@ -#include // for std::puts +#include // for std::puts /* ELF Relocations */ #define ELF_RELOC(name, value) name = value, /** i386 relocations. */ -enum RELOC_i386 { +enum RELOC_i386 +{ #ifndef ELF_RELOC -#error "ELF_RELOC must be defined" + #error "ELF_RELOC must be defined" #endif + // clang-format off /* TODO: this is just a subset */ ELF_RELOC(R_386_NONE, 0) ELF_RELOC(R_386_32, 1) @@ -54,119 +56,163 @@ ELF_RELOC(R_386_TLS_DESC, 41) ELF_RELOC(R_386_IRELATIVE, 42) ELF_RELOC(R_386_NUM, 43) }; +// clang-format on #ifndef SWITCH_VERSION -#ifdef FROZEN_VERSION -#include "frozen/map.h" -#else -#include -#endif + #ifdef FROZEN_VERSION + #include "frozen/map.h" + #else + #include + #endif -#ifdef FROZEN_VERSION -constexpr -frozen::map -#else -const -std::map -#endif -e2s = { - { RELOC_i386::R_386_NONE, "NONE"}, - { RELOC_i386::R_386_32, "R32"}, - { RELOC_i386::R_386_PC32, "PC32"}, - { RELOC_i386::R_386_GOT32, "GOT32"}, - { RELOC_i386::R_386_PLT32, "PLT32"}, - { RELOC_i386::R_386_COPY, "COPY"}, - { RELOC_i386::R_386_GLOB_DAT, "GLOB_DAT"}, - { RELOC_i386::R_386_JUMP_SLOT, "JUMP_SLOT"}, - { RELOC_i386::R_386_RELATIVE, "RELATIVE"}, - { RELOC_i386::R_386_GOTOFF, "GOTOFF"}, - { RELOC_i386::R_386_GOTPC, "GOTPC"}, - { RELOC_i386::R_386_32PLT, "R32PLT"}, - { RELOC_i386::R_386_TLS_TPOFF, "TLS_TPOFF"}, - { RELOC_i386::R_386_TLS_IE, "TLS_IE"}, - { RELOC_i386::R_386_TLS_GOTIE, "TLS_GOTIE"}, - { RELOC_i386::R_386_TLS_LE, "TLS_LE"}, - { RELOC_i386::R_386_TLS_GD, "TLS_GD"}, - { RELOC_i386::R_386_TLS_LDM, "TLS_LDM"}, - { RELOC_i386::R_386_16, "R16"}, - { RELOC_i386::R_386_PC16, "PC16"}, - { RELOC_i386::R_386_8, "R8"}, - { RELOC_i386::R_386_PC8, "PC8"}, - { RELOC_i386::R_386_TLS_GD_32, "TLS_GD_32"}, - { RELOC_i386::R_386_TLS_GD_PUSH, "TLS_GD_PUSH"}, - { RELOC_i386::R_386_TLS_GD_CALL, "TLS_GD_CALL"}, - { RELOC_i386::R_386_TLS_GD_POP, "TLS_GD_POP"}, - { RELOC_i386::R_386_TLS_LDM_32, "TLS_LDM_32"}, - { RELOC_i386::R_386_TLS_LDM_PUSH, "TLS_LDM_PUSH"}, - { RELOC_i386::R_386_TLS_LDM_CALL, "TLS_LDM_CALL"}, - { RELOC_i386::R_386_TLS_LDM_POP, "TLS_LDM_POP"}, - { RELOC_i386::R_386_TLS_LDO_32, "TLS_LDO_32"}, - { RELOC_i386::R_386_TLS_IE_32, "TLS_IE_32"}, - { RELOC_i386::R_386_TLS_LE_32, "TLS_LE_32"}, - { RELOC_i386::R_386_TLS_DTPMOD32, "TLS_DTPMOD32"}, - { RELOC_i386::R_386_TLS_DTPOFF32, "TLS_DTPOFF32"}, - { RELOC_i386::R_386_TLS_TPOFF32, "TLS_TPOFF32"}, - { RELOC_i386::R_386_TLS_GOTDESC, "TLS_GOTDESC"}, - { RELOC_i386::R_386_TLS_DESC_CALL, "TLS_DESC_CALL"}, - { RELOC_i386::R_386_TLS_DESC, "TLS_DESC"}, - { RELOC_i386::R_386_IRELATIVE, "IRELATIVE"}, - { RELOC_i386::R_386_NUM, "NUM"}, -}; + #ifdef FROZEN_VERSION +constexpr frozen::map + #else +const std::map + #endif + e2s = { + { RELOC_i386::R_386_NONE, "NONE" }, + { RELOC_i386::R_386_32, "R32" }, + { RELOC_i386::R_386_PC32, "PC32" }, + { RELOC_i386::R_386_GOT32, "GOT32" }, + { RELOC_i386::R_386_PLT32, "PLT32" }, + { RELOC_i386::R_386_COPY, "COPY" }, + { RELOC_i386::R_386_GLOB_DAT, "GLOB_DAT" }, + { RELOC_i386::R_386_JUMP_SLOT, "JUMP_SLOT" }, + { RELOC_i386::R_386_RELATIVE, "RELATIVE" }, + { RELOC_i386::R_386_GOTOFF, "GOTOFF" }, + { RELOC_i386::R_386_GOTPC, "GOTPC" }, + { RELOC_i386::R_386_32PLT, "R32PLT" }, + { RELOC_i386::R_386_TLS_TPOFF, "TLS_TPOFF" }, + { RELOC_i386::R_386_TLS_IE, "TLS_IE" }, + { RELOC_i386::R_386_TLS_GOTIE, "TLS_GOTIE" }, + { RELOC_i386::R_386_TLS_LE, "TLS_LE" }, + { RELOC_i386::R_386_TLS_GD, "TLS_GD" }, + { RELOC_i386::R_386_TLS_LDM, "TLS_LDM" }, + { RELOC_i386::R_386_16, "R16" }, + { RELOC_i386::R_386_PC16, "PC16" }, + { RELOC_i386::R_386_8, "R8" }, + { RELOC_i386::R_386_PC8, "PC8" }, + { RELOC_i386::R_386_TLS_GD_32, "TLS_GD_32" }, + { RELOC_i386::R_386_TLS_GD_PUSH, "TLS_GD_PUSH" }, + { RELOC_i386::R_386_TLS_GD_CALL, "TLS_GD_CALL" }, + { RELOC_i386::R_386_TLS_GD_POP, "TLS_GD_POP" }, + { RELOC_i386::R_386_TLS_LDM_32, "TLS_LDM_32" }, + { RELOC_i386::R_386_TLS_LDM_PUSH, "TLS_LDM_PUSH" }, + { RELOC_i386::R_386_TLS_LDM_CALL, "TLS_LDM_CALL" }, + { RELOC_i386::R_386_TLS_LDM_POP, "TLS_LDM_POP" }, + { RELOC_i386::R_386_TLS_LDO_32, "TLS_LDO_32" }, + { RELOC_i386::R_386_TLS_IE_32, "TLS_IE_32" }, + { RELOC_i386::R_386_TLS_LE_32, "TLS_LE_32" }, + { RELOC_i386::R_386_TLS_DTPMOD32, "TLS_DTPMOD32" }, + { RELOC_i386::R_386_TLS_DTPOFF32, "TLS_DTPOFF32" }, + { RELOC_i386::R_386_TLS_TPOFF32, "TLS_TPOFF32" }, + { RELOC_i386::R_386_TLS_GOTDESC, "TLS_GOTDESC" }, + { RELOC_i386::R_386_TLS_DESC_CALL, "TLS_DESC_CALL" }, + { RELOC_i386::R_386_TLS_DESC, "TLS_DESC" }, + { RELOC_i386::R_386_IRELATIVE, "IRELATIVE" }, + { RELOC_i386::R_386_NUM, "NUM" }, + }; -char const * enum_to_string(RELOC_i386 e) { - return e2s.at(e); +char const* enum_to_string(RELOC_i386 e) +{ + return e2s.at(e); } #else -char const * enum_to_string(RELOC_i386 e) { - switch(e) { - case RELOC_i386::R_386_NONE: return "NONE"; - case RELOC_i386::R_386_32: return "R32"; - case RELOC_i386::R_386_PC32: return "PC32"; - case RELOC_i386::R_386_GOT32: return "GOT32"; - case RELOC_i386::R_386_PLT32: return "PLT32"; - case RELOC_i386::R_386_COPY: return "COPY"; - case RELOC_i386::R_386_GLOB_DAT: return "GLOB_DAT"; - case RELOC_i386::R_386_JUMP_SLOT: return "JUMP_SLOT"; - case RELOC_i386::R_386_RELATIVE: return "RELATIVE"; - case RELOC_i386::R_386_GOTOFF: return "GOTOFF"; - case RELOC_i386::R_386_GOTPC: return "GOTPC"; - case RELOC_i386::R_386_32PLT: return "R32PLT"; - case RELOC_i386::R_386_TLS_TPOFF: return "TLS_TPOFF"; - case RELOC_i386::R_386_TLS_IE: return "TLS_IE"; - case RELOC_i386::R_386_TLS_GOTIE: return "TLS_GOTIE"; - case RELOC_i386::R_386_TLS_LE: return "TLS_LE"; - case RELOC_i386::R_386_TLS_GD: return "TLS_GD"; - case RELOC_i386::R_386_TLS_LDM: return "TLS_LDM"; - case RELOC_i386::R_386_16: return "R16"; - case RELOC_i386::R_386_PC16: return "PC16"; - case RELOC_i386::R_386_8: return "R8"; - case RELOC_i386::R_386_PC8: return "PC8"; - case RELOC_i386::R_386_TLS_GD_32: return "TLS_GD_32"; - case RELOC_i386::R_386_TLS_GD_PUSH: return "TLS_GD_PUSH"; - case RELOC_i386::R_386_TLS_GD_CALL: return "TLS_GD_CALL"; - case RELOC_i386::R_386_TLS_GD_POP: return "TLS_GD_POP"; - case RELOC_i386::R_386_TLS_LDM_32: return "TLS_LDM_32"; - case RELOC_i386::R_386_TLS_LDM_PUSH: return "TLS_LDM_PUSH"; - case RELOC_i386::R_386_TLS_LDM_CALL: return "TLS_LDM_CALL"; - case RELOC_i386::R_386_TLS_LDM_POP: return "TLS_LDM_POP"; - case RELOC_i386::R_386_TLS_LDO_32: return "TLS_LDO_32"; - case RELOC_i386::R_386_TLS_IE_32: return "TLS_IE_32"; - case RELOC_i386::R_386_TLS_LE_32: return "TLS_LE_32"; - case RELOC_i386::R_386_TLS_DTPMOD32: return "TLS_DTPMOD32"; - case RELOC_i386::R_386_TLS_DTPOFF32: return "TLS_DTPOFF32"; - case RELOC_i386::R_386_TLS_TPOFF32: return "TLS_TPOFF32"; - case RELOC_i386::R_386_TLS_GOTDESC: return "TLS_GOTDESC"; - case RELOC_i386::R_386_TLS_DESC_CALL: return "TLS_DESC_CALL"; - case RELOC_i386::R_386_TLS_DESC: return "TLS_DESC"; - case RELOC_i386::R_386_IRELATIVE: return "IRELATIVE"; - case RELOC_i386::R_386_NUM: return "NUM"; - } +char const* enum_to_string(RELOC_i386 e) +{ + switch (e) + { + case RELOC_i386::R_386_NONE: + return "NONE"; + case RELOC_i386::R_386_32: + return "R32"; + case RELOC_i386::R_386_PC32: + return "PC32"; + case RELOC_i386::R_386_GOT32: + return "GOT32"; + case RELOC_i386::R_386_PLT32: + return "PLT32"; + case RELOC_i386::R_386_COPY: + return "COPY"; + case RELOC_i386::R_386_GLOB_DAT: + return "GLOB_DAT"; + case RELOC_i386::R_386_JUMP_SLOT: + return "JUMP_SLOT"; + case RELOC_i386::R_386_RELATIVE: + return "RELATIVE"; + case RELOC_i386::R_386_GOTOFF: + return "GOTOFF"; + case RELOC_i386::R_386_GOTPC: + return "GOTPC"; + case RELOC_i386::R_386_32PLT: + return "R32PLT"; + case RELOC_i386::R_386_TLS_TPOFF: + return "TLS_TPOFF"; + case RELOC_i386::R_386_TLS_IE: + return "TLS_IE"; + case RELOC_i386::R_386_TLS_GOTIE: + return "TLS_GOTIE"; + case RELOC_i386::R_386_TLS_LE: + return "TLS_LE"; + case RELOC_i386::R_386_TLS_GD: + return "TLS_GD"; + case RELOC_i386::R_386_TLS_LDM: + return "TLS_LDM"; + case RELOC_i386::R_386_16: + return "R16"; + case RELOC_i386::R_386_PC16: + return "PC16"; + case RELOC_i386::R_386_8: + return "R8"; + case RELOC_i386::R_386_PC8: + return "PC8"; + case RELOC_i386::R_386_TLS_GD_32: + return "TLS_GD_32"; + case RELOC_i386::R_386_TLS_GD_PUSH: + return "TLS_GD_PUSH"; + case RELOC_i386::R_386_TLS_GD_CALL: + return "TLS_GD_CALL"; + case RELOC_i386::R_386_TLS_GD_POP: + return "TLS_GD_POP"; + case RELOC_i386::R_386_TLS_LDM_32: + return "TLS_LDM_32"; + case RELOC_i386::R_386_TLS_LDM_PUSH: + return "TLS_LDM_PUSH"; + case RELOC_i386::R_386_TLS_LDM_CALL: + return "TLS_LDM_CALL"; + case RELOC_i386::R_386_TLS_LDM_POP: + return "TLS_LDM_POP"; + case RELOC_i386::R_386_TLS_LDO_32: + return "TLS_LDO_32"; + case RELOC_i386::R_386_TLS_IE_32: + return "TLS_IE_32"; + case RELOC_i386::R_386_TLS_LE_32: + return "TLS_LE_32"; + case RELOC_i386::R_386_TLS_DTPMOD32: + return "TLS_DTPMOD32"; + case RELOC_i386::R_386_TLS_DTPOFF32: + return "TLS_DTPOFF32"; + case RELOC_i386::R_386_TLS_TPOFF32: + return "TLS_TPOFF32"; + case RELOC_i386::R_386_TLS_GOTDESC: + return "TLS_GOTDESC"; + case RELOC_i386::R_386_TLS_DESC_CALL: + return "TLS_DESC_CALL"; + case RELOC_i386::R_386_TLS_DESC: + return "TLS_DESC"; + case RELOC_i386::R_386_IRELATIVE: + return "IRELATIVE"; + case RELOC_i386::R_386_NUM: + return "NUM"; + } } #endif -int main() { - std::puts(enum_to_string(RELOC_i386::R_386_8)); - return 0; +int main() +{ + std::puts(enum_to_string(RELOC_i386::R_386_8)); + return 0; } diff --git a/examples/html_entities_map.cpp b/examples/html_entities_map.cpp index ac7d922..6702c8b 100644 --- a/examples/html_entities_map.cpp +++ b/examples/html_entities_map.cpp @@ -1,14 +1,16 @@ -#include -#include +#include #include +#include struct codes_t { - uint32_t iCodepoint1; - uint32_t iCodepoint2{0}; + uint32_t iCodepoint1; + uint32_t iCodepoint2 { 0 }; }; +// clang-format off + static constexpr std::pair s_Entities[] { { "AElig" , { 0xC6 }}, @@ -2138,10 +2140,11 @@ static constexpr std::pair s_Entities[] { "zwnj" , { 0x200C }}, }; +// clang-format on + static constexpr auto s_NamedEntitiesHTML4 = frozen::make_unordered_map(s_Entities); int main(int argv, char** argc) { - return (s_NamedEntitiesHTML4.find("real") == s_NamedEntitiesHTML4.end()); + return (s_NamedEntitiesHTML4.find("real") == s_NamedEntitiesHTML4.end()); } - diff --git a/examples/string_view_lookup.cpp b/examples/string_view_lookup.cpp new file mode 100644 index 0000000..c628020 --- /dev/null +++ b/examples/string_view_lookup.cpp @@ -0,0 +1,48 @@ +/// @file string_view_lookup.cpp +/// Demonstrates frozen::map with std::string_view keys, heterogeneous lookup, +/// and compile-time access. + +#include +#include +#include + +#include +#include +#include + +// A frozen map with string_view keys, initialized from string literals. +// The transparent comparator (std::less<>, now the default) enables +// heterogeneous lookup — you can search with const char*, std::string, +// or std::string_view without conversion overhead. +constexpr frozen::map http_status = { + {"OK", 200}, + {"Created", 201}, + {"Not Found", 404}, + {"Internal Server Error", 500}, + {"Bad Gateway", 502}, +}; + +// Compile-time lookup: verified at compile time, zero runtime cost. +static_assert(http_status.at("OK") == 200); +static_assert(http_status.at("Not Found") == 404); +static_assert(http_status.count("Missing") == 0); + +int main() { + // Runtime lookup with const char* + const char *key1 = "Created"; + std::cout << key1 << " -> " << http_status.at(key1) << "\n"; + + // Runtime lookup with std::string (heterogeneous — no conversion needed) + std::string key2 = "Internal Server Error"; + std::cout << key2 << " -> " << http_status.at(key2) << "\n"; + + // Runtime lookup with std::string_view + std::string_view key3 = "Bad Gateway"; + std::cout << key3 << " -> " << http_status.at(key3) << "\n"; + + // Iterate all entries + std::cout << "\nAll HTTP statuses:\n"; + for (const auto &[name, code] : http_status) { + std::cout << " " << name << " = " << code << "\n"; + } +} diff --git a/examples/value_modification.cpp b/examples/value_modification.cpp index 2584efd..2896ee4 100644 --- a/examples/value_modification.cpp +++ b/examples/value_modification.cpp @@ -1,37 +1,33 @@ +#include #include #include #include #include - -/// MAYBE_CONSTINIT expands to `constinit` if available. -#if __cpp_constinit -#define MAYBE_CONSTINIT constinit -#else -#define MAYBE_CONSTINIT -#endif +#include // To make a frozen::unordered_map where you can modify the values, make a -// non-constexpr instance. If available, prefer to use constinit. It will -// initialize the map during compilation and detect any exceptions. -MAYBE_CONSTINIT static frozen::unordered_map fruits = { - {"n_apples", 0}, - {"n_pears", 0}, +// non-constexpr instance. constinit initializes the map during compilation +// and detects any exceptions. +constinit static frozen::unordered_map fruits = { + { "n_apples", 0 }, + { "n_pears", 0 }, }; -int main() { - // Update the values using at() - fruits.at("n_apples") = 10; - fruits.at("n_pears") = fruits.at("n_apples") * 2; - std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; - std::cout << "n_pears: " << fruits.at("n_pears") << std::endl; +int main() +{ + // Update the values using at() + fruits.at("n_apples") = 10; + fruits.at("n_pears") = fruits.at("n_apples") * 2; + std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; + std::cout << "n_pears: " << fruits.at("n_pears") << std::endl; - // You can also update values via the iterator returned by find() - auto found = fruits.find("n_apples"); - found->second = 0; - std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; + // You can also update values via the iterator returned by find() + auto found = fruits.find("n_apples"); + found->second = 0; + std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; - // Range also works - auto range = fruits.equal_range("n_apples"); - range.first->second = 1337; - std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; -} \ No newline at end of file + // Range also works + auto range = fruits.equal_range("n_apples"); + range.first->second = 1337; + std::cout << "n_apples: " << fruits.at("n_apples") << std::endl; +} diff --git a/file_list.md b/file_list.md new file mode 100644 index 0000000..a1c3bf1 --- /dev/null +++ b/file_list.md @@ -0,0 +1,20 @@ +# Files + +- include/frozen/algorithm.h - Constexpr search with Knuth-Morris-Pratt matching +- include/frozen/map.h - Compile-time sorted map using binary search +- include/frozen/random.h - Linear congruential random number generator +- include/frozen/set.h - Compile-time sorted set using binary search +- include/frozen/string.h - Compile-time string view and literal operators +- include/frozen/unordered_map.h - Hash-based unordered map with perfect hashing +- include/frozen/unordered_set.h - Hash-based unordered set with perfect hashing +- include/frozen/bits/algorithms.h - Utility algorithms (next power of 2, log2) +- include/frozen/bits/basic_types.h - Core constexpr types: cvector, carray, cbit_set +- include/frozen/bits/constexpr_assert.h - Compile-time assertion macros +- include/frozen/bits/defines.h - Feature detection and configuration macros +- include/frozen/bits/elsa.h - ELSA hash function for integral and enum types +- include/frozen/bits/elsa_std.h - ELSA hash specializations for std::string types +- include/frozen/bits/exceptions.h - Exception handling macros (throw or abort) +- include/frozen/bits/hash_string.h - String hashing with DJB2 and Fowler-Noll-Vo +- include/frozen/bits/mpl.h - Template metaprogramming utilities +- include/frozen/bits/pmh.h - Perfect minimal hashing algorithm implementation +- include/frozen/bits/version.h - Library version macros and C++23 checks diff --git a/include/frozen/algorithm.h b/include/frozen/algorithm.h index 3abd529..ffcbcc9 100644 --- a/include/frozen/algorithm.h +++ b/include/frozen/algorithm.h @@ -23,176 +23,252 @@ #ifndef FROZEN_LETITGO_ALGORITHM_H #define FROZEN_LETITGO_ALGORITHM_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error \ + "frozen/algorithm.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/algorithm.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #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 -ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher) +namespace frozen { - return searcher(first, last).first; -} - -// text book implementation from -// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm - -template class knuth_morris_pratt_searcher { - bits::carray step_; - bits::carray needle_; - - static constexpr bits::carray - build_kmp_cache(char const (&needle)[size + 1]) { - std::ptrdiff_t cnd = 0; - bits::carray cache(-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; - } + + // Equivalent to std::search (C++17 and later) + // https://en.cppreference.com/w/cpp/algorithm/search + template + ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher& searcher) + { + return searcher(first, last).first; } - return cache; - } - -public: - constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1]) - : step_{build_kmp_cache(needle)}, needle_(needle) {} - - template - constexpr std::pair 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; + + // text book implementation from + // https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm + + template + class knuth_morris_pratt_searcher + { + bits::carray step_; + bits::carray needle_; + + static constexpr bits::carray + build_kmp_cache(char const (&needle)[size + 1]) + { + std::ptrdiff_t cnd = 0; + bits::carray cache(-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; } - } - } - return { last, last }; - } -}; - -template -constexpr knuth_morris_pratt_searcher 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 class boyer_moore_searcher { - using skip_table_type = bits::carray; - using suffix_table_type = bits::carray; - - skip_table_type skip_table_; - suffix_table_type suffix_table_; - bits::carray needle_; - - constexpr auto build_skip_table(char const (&needle)[size + 1]) { - skip_table_type skip_table(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; + public: + // C-style array reference is required to bind string literals directly; + // std::array has no implicit conversion from string literals. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1]) : + step_ { build_kmp_cache(needle) }, needle_(needle) + { + } + template + constexpr std::pair + operator()(RandomAccessIterator first, RandomAccessIterator last) const + { + if (size == 0) + { + return { first, first }; + } + std::size_t i = 0; + RandomAccessIterator 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 + constexpr knuth_morris_pratt_searcher + make_knuth_morris_pratt_searcher(char const (&needle)[N]) + { + return { needle }; } - 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 - constexpr std::pair operator()(RandomAccessIterator first, RandomAccessIterator last) const { - if (size == 0) - return { first, first }; - - if (size > size_t(last - first)) - return { last, last }; - - RandomAccessIterator iter = first + size - 1; - while (true) { - std::ptrdiff_t j = size - 1; - while (j > 0 && (*iter == needle_[j])) { - --iter; - --j; - } - if (j == 0 && *iter == needle_[0]) - return { iter, iter + size}; - - std::ptrdiff_t jump = std::max(skip_table_[*iter], suffix_table_[j]); - if (jump >= last - iter) - return { last, last }; - iter += jump; - } - } -}; -template -constexpr boyer_moore_searcher make_boyer_moore_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 + class boyer_moore_searcher + { + using skip_table_type = bits::carray; + using suffix_table_type = bits::carray; + + skip_table_type skip_table_; + suffix_table_type suffix_table_; + bits::carray needle_; + + static constexpr auto build_skip_table(char const (&needle)[size + 1]) + { + skip_table_type skip_table(size); + for (std::size_t i = 0; i < size - 1; ++i) + { + skip_table[static_cast(needle[i])] -= i + 1; + } + return skip_table; + } + + static 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; + } + + static 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; + } + + static 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 pos = size - 1; pos >= 0; pos--) + { + if (is_prefix(needle, pos + 1)) + { + last_prefix_index = pos + 1; + } + + suffix[pos] = last_prefix_index + (size - 1 - pos); + } + + // second loop + for (std::size_t pos = 0; pos < size - 1; pos++) + { + auto slen = suffix_length(needle, pos); + if (needle[pos - slen] != needle[size - 1 - slen]) + { + suffix[size - 1 - slen] = size - 1 - pos + 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 + constexpr std::pair + operator()(RandomAccessIterator first, RandomAccessIterator last) const + { + if (size == 0) + { + return { first, first }; + } + + if (size > size_t(last - first)) + { + return { last, last }; + } + + RandomAccessIterator iter = first + size - 1; + while (true) + { + std::ptrdiff_t j = size - 1; + while (j > 0 && (*iter == needle_[j])) + { + --iter; + --j; + } + if (j == 0 && *iter == needle_[0]) + { + return { iter, iter + size }; + } + + std::ptrdiff_t jump = + std::max(skip_table_[static_cast(*iter)], suffix_table_[j]); + if (jump >= last - iter) + { + return { last, last }; + } + iter += jump; + } + } + }; + + template + constexpr boyer_moore_searcher make_boyer_moore_searcher(char const (&needle)[N]) + { + return { needle }; + } -} // namespace frozen +} // namespace frozen #endif diff --git a/include/frozen/bits/algorithms.h b/include/frozen/bits/algorithms.h index 4efa61b..50d2eec 100644 --- a/include/frozen/bits/algorithms.h +++ b/include/frozen/bits/algorithms.h @@ -28,208 +28,243 @@ #include #include -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::digits; - v--; - for(std::size_t i = 1; i < trip_count; i <<= 1) - v |= v >> i; - v++; - return v; -} - -template -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); -unsigned long select_uint_least(std::integral_constant); -unsigned long long select_uint_least(std::integral_constant); -template -unsigned long long select_uint_least(std::integral_constant) { - static_assert(N < 2, "unsupported type size"); - return {}; -} - - -template -using select_uint_least_t = decltype(select_uint_least(std::integral_constant())); - -template -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 -constexpr void cswap(T &a, T &b) { - auto tmp = a; - a = b; - b = tmp; -} - -template -constexpr void cswap(std::pair & a, std::pair & b) { - cswap(a.first, b.first); - cswap(a.second, b.second); -} - -template -constexpr void cswap(std::tuple &a, std::tuple &b, std::index_sequence) { - using swallow = int[]; - (void) swallow{(cswap(std::get(a), std::get(b)), 0)...}; -} - -template -constexpr void cswap(std::tuple &a, std::tuple &b) { - cswap(a, b, std::make_index_sequence()); -} - -template -constexpr void iter_swap(Iter a, Iter b) { - cswap(*a, *b); -} - -template -constexpr Iterator partition(Iterator left, Iterator right, Compare const &compare) { - auto pivot = left + (right - left) / 2; - iter_swap(right, pivot); - pivot = right; - for (auto it = left; 0 < right - it; ++it) { - if (compare(*it, *pivot)) { - iter_swap(it, left); - left++; - } - } - iter_swap(pivot, left); - pivot = left; - return pivot; -} - -template -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 -constexpr Container quicksort(Container const &array, - Compare const &compare) { - Container res = array; - quicksort(res.begin(), res.end() - 1, compare); - return res; -} - -template struct LowerBound { - T const &value_; - Compare const &compare_; - constexpr LowerBound(T const &value, Compare const &compare) - : value_(value), compare_(compare) {} - - template - inline constexpr ForwardIt doit_fast(ForwardIt first, - std::integral_constant) { - return first; - } - - template - inline constexpr ForwardIt doit_fast(ForwardIt first, - std::integral_constant) { - 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{}); - } - - template - inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant, std::integral_constant) { - return doit_fast(first, std::integral_constant{}); - } - - template - inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant, std::integral_constant) { - 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::integral_constant{}); - } - else - return doit_fast(first, std::integral_constant{}); - } - - template - inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant, std::integral_constant) { - return doit_fast(first, std::integral_constant{}); - } -}; - -template -constexpr ForwardIt lower_bound(ForwardIt first, const T &value, Compare const &compare) { - return LowerBound{value, compare}.doitfirst(first, std::integral_constant{}, std::integral_constant{}); -} - -template -constexpr bool binary_search(ForwardIt first, const T &value, - Compare const &compare) { - ForwardIt where = lower_bound(first, value, compare); - return (!(where == first + N) && !(compare(value, *where))); -} - - -template -constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) +namespace frozen::bits { - for (; first1 != last1; ++first1, ++first2) { - if (!(*first1 == *first2)) { - return false; + + 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::digits; + v--; + for (std::size_t i = 1; i < trip_count; i <<= 1) + v |= v >> i; + v++; + return v; } - } - return true; -} -template -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 + template + 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); + unsigned long select_uint_least(std::integral_constant); + unsigned long long select_uint_least(std::integral_constant); + template + unsigned long long select_uint_least(std::integral_constant) + { + static_assert(N == 1, "unsupported type size"); + return {}; + } + + template + using select_uint_least_t = + decltype(select_uint_least(std::integral_constant())); + + template + 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 + constexpr void cswap(T& a, T& b) + { + auto tmp = std::move(a); + a = std::move(b); + b = std::move(tmp); + } + + template + constexpr void cswap(std::pair& a, std::pair& b) + { + cswap(a.first, b.first); + cswap(a.second, b.second); + } + + template + constexpr void cswap(std::tuple& a, std::tuple& b, std::index_sequence) + { + ((cswap(std::get(a), std::get(b))), ...); + } + + template + constexpr void cswap(std::tuple& a, std::tuple& b) + { + cswap(a, b, std::make_index_sequence()); + } + + template + constexpr void iter_swap(Iter a, Iter b) + { + cswap(*a, *b); + } + + template + constexpr Iterator partition(Iterator left, Iterator right, Compare const& compare) + { + auto pivot = left + (right - left) / 2; + iter_swap(right, pivot); + pivot = right; + for (auto it = left; 0 < right - it; ++it) + { + if (compare(*it, *pivot)) + { + iter_swap(it, left); + left++; + } + } + iter_swap(pivot, left); + pivot = left; + return pivot; + } + + template + 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 + constexpr Container quicksort(Container const& array, Compare const& compare) + { + Container res = array; + if (res.size() > 0) + quicksort(res.begin(), res.end() - 1, compare); + return res; + } + + template + struct LowerBound + { + T const& value_; + Compare const& compare_; + constexpr LowerBound(T const& value, Compare const& compare) : + value_(value), compare_(compare) + { + } + + template + constexpr ForwardIt doit_fast(ForwardIt first, std::integral_constant) + { + return first; + } + + template + constexpr ForwardIt doit_fast(ForwardIt first, std::integral_constant) + { + 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 {}); + } + + template + inline constexpr ForwardIt doitfirst(ForwardIt first, + std::integral_constant, + std::integral_constant) + { + return doit_fast(first, std::integral_constant {}); + } + + template + inline constexpr ForwardIt doitfirst(ForwardIt first, + std::integral_constant, + std::integral_constant) + { + 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::integral_constant {}); + } + else + return doit_fast(first, std::integral_constant {}); + } + + template + inline constexpr ForwardIt doitfirst(ForwardIt first, + std::integral_constant, + std::integral_constant) + { + return doit_fast(first, std::integral_constant {}); + } + }; + + template + constexpr ForwardIt lower_bound(ForwardIt first, const T& value, Compare const& compare) + { + return LowerBound { value, compare }.doitfirst( + first, std::integral_constant {}, + std::integral_constant {}); + } + + template + constexpr bool binary_search(ForwardIt first, const T& value, Compare const& compare) + { + ForwardIt where = lower_bound(first, value, compare); + return (!(where == first + N) && !(compare(value, *where))); + } + + template + constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) + { + for (; first1 != last1; ++first1, ++first2) + { + if (!(*first1 == *first2)) + { + return false; + } + } + return true; + } + + template + 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 frozen::bits #endif diff --git a/include/frozen/bits/basic_types.h b/include/frozen/bits/basic_types.h index 2d77604..e1bf0d4 100644 --- a/include/frozen/bits/basic_types.h +++ b/include/frozen/bits/basic_types.h @@ -23,180 +23,229 @@ #ifndef FROZEN_LETITGO_BASIC_TYPES_H #define FROZEN_LETITGO_BASIC_TYPES_H -#include "frozen/bits/exceptions.h" #include "frozen/bits/constexpr_assert.h" +#include "frozen/bits/exceptions.h" #include -#include +#include #include #include +#include -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 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; } - constexpr const_iterator begin() const noexcept { return data; } - constexpr const_iterator end() const 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 carray { - T data_ [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise - - template - constexpr carray(Iter iter, std::index_sequence) - : data_{((void)I, *iter++)...} {} - template - constexpr carray(const T& value, std::index_sequence) - : data_{((void)I, value)...} {} - - static constexpr void check_initializer(std::initializer_list init) { - (void)init; - constexpr_assert(init.size() == N, "Cannot initialize a carray with an initializer list of different size."); - } - -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 carray() = default; - constexpr carray(const value_type& val) - : carray(val, std::make_index_sequence()) {} - template ::value, std::size_t> M> - constexpr carray(U const (&init)[M]) - : carray(init, std::make_index_sequence()) - { - static_assert(M >= N, "Cannot initialize a carray with an smaller array"); - } - template ::value, std::size_t> M> - constexpr carray(std::array const &init) - : carray(init.begin(), std::make_index_sequence()) - { - static_assert(M >= N, "Cannot initialize a carray with an smaller array"); - } - template ::value>* = nullptr> - constexpr carray(std::initializer_list init) - : carray((check_initializer(init), init.begin()), std::make_index_sequence()) - { - } - template ::value>* = nullptr> - constexpr carray(const carray& rhs) - : carray(rhs.begin(), std::make_index_sequence()) - { - } - - // Iterators - constexpr iterator begin() noexcept { return data_; } - constexpr const_iterator begin() const noexcept { return data_; } - constexpr iterator end() noexcept { return data_ + N; } - constexpr const_iterator end() const noexcept { return data_ + N; } - - // 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_; } -}; -template -class carray { - -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 carray(void) = default; - -}; - -} // namespace bits - -} // namespace frozen +namespace frozen::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 cvector + { + std::array data_ = {}; + std::size_t dsize_ = 0; + + public: + // Container typedefs + 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() = default; + constexpr cvector(size_type count, const T& value) : dsize_(count) + { + for (std::size_t i = 0; i < N; ++i) + { + data_[i] = value; + } + } + + // Iterators + [[nodiscard]] constexpr auto begin() noexcept -> iterator { return data_.data(); } + [[nodiscard]] constexpr auto end() noexcept -> iterator { return data_.data() + dsize_; } + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return data_.data(); + } + [[nodiscard]] constexpr auto end() const noexcept -> const_iterator + { + return data_.data() + dsize_; + } + + // Capacity + [[nodiscard]] constexpr auto size() const -> size_type { return dsize_; } + + // Element access + constexpr auto operator[](std::size_t index) -> reference { return data_[index]; } + [[nodiscard]] constexpr auto operator[](std::size_t index) const -> const_reference + { + return data_[index]; + } + + constexpr auto back() -> reference { return data_[dsize_ - 1]; } + [[nodiscard]] constexpr auto back() const -> const_reference { return data_[dsize_ - 1]; } + + // Modifiers + constexpr auto push_back(const T& val) -> void { data_[dsize_++] = val; } + constexpr auto push_back(T&& val) -> void { data_[dsize_++] = std::move(val); } + constexpr auto pop_back() -> void { --dsize_; } + + constexpr auto clear() -> void { dsize_ = 0; } + }; + + template + class carray + { + std::array data_ = {}; + + template + constexpr carray(Iter iter, std::index_sequence) : data_ { ((void) I, *iter++)... } + { + } + + template + constexpr carray(const T& value, std::index_sequence) : data_ { ((void) I, value)... } + { + } + + static constexpr auto check_initializer(std::initializer_list init) -> void + { + (void) init; + constexpr_assert( + init.size() == N, + "Cannot initialize a carray with an initializer list of different size."); + } + + public: + // Container typedefs + 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 carray() = default; + constexpr carray(const value_type& val) : carray(val, std::make_index_sequence()) {} + + template + requires std::convertible_to + constexpr carray(U const (&init)[M]) : carray(init, std::make_index_sequence()) + { + static_assert(M >= N, "Cannot initialize a carray with a smaller array"); + } + + template + requires std::convertible_to + constexpr carray(std::array const& init) : + carray(init.begin(), std::make_index_sequence()) + { + static_assert(M >= N, "Cannot initialize a carray with a smaller array"); + } + + template + requires std::convertible_to + constexpr carray(std::initializer_list init) : + carray((check_initializer(init), init.begin()), std::make_index_sequence()) + { + } + + template + requires std::convertible_to + constexpr carray(const carray& rhs) : + carray(rhs.begin(), std::make_index_sequence()) + { + } + + // Iterators + [[nodiscard]] constexpr auto begin() noexcept -> iterator { return data_.data(); } + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return data_.data(); + } + [[nodiscard]] constexpr auto end() noexcept -> iterator { return data_.data() + N; } + [[nodiscard]] constexpr auto end() const noexcept -> const_iterator + { + return data_.data() + N; + } + + // Capacity + [[nodiscard]] constexpr auto size() const -> size_type { return N; } + [[nodiscard]] constexpr auto max_size() const -> size_type { return N; } + + // Element access + constexpr auto operator[](std::size_t index) -> reference { return data_[index]; } + [[nodiscard]] constexpr auto operator[](std::size_t index) const -> const_reference + { + return data_[index]; + } + + [[nodiscard]] constexpr auto at(std::size_t index) -> reference + { + 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]; + } + [[nodiscard]] constexpr auto at(std::size_t index) const -> const_reference + { + 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 auto front() -> reference { return data_[0]; } + [[nodiscard]] constexpr auto front() const -> const_reference { return data_[0]; } + + constexpr auto back() -> reference { return data_[N - 1]; } + [[nodiscard]] constexpr auto back() const -> const_reference { return data_[N - 1]; } + + [[nodiscard]] constexpr auto data() noexcept -> value_type* { return data_.data(); } + [[nodiscard]] constexpr auto data() const noexcept -> const value_type* + { + return data_.data(); + } + + constexpr operator std::array() const { return data_; } + }; + template + class carray + { + 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 carray() = default; + }; + +} // namespace frozen::bits #endif diff --git a/include/frozen/bits/constexpr_assert.h b/include/frozen/bits/constexpr_assert.h index bd0bb10..95543f3 100644 --- a/include/frozen/bits/constexpr_assert.h +++ b/include/frozen/bits/constexpr_assert.h @@ -28,7 +28,6 @@ inline void constexpr_assert_failed() {} -#define constexpr_assert(cond, msg) ((void)((cond) ? 0 : (constexpr_assert_failed(), 0))) +#define constexpr_assert(cond, msg) ((void) ((cond) ? 0 : (constexpr_assert_failed(), 0))) #endif - diff --git a/include/frozen/bits/defines.h b/include/frozen/bits/defines.h index 839f4e8..d9aa2d1 100644 --- a/include/frozen/bits/defines.h +++ b/include/frozen/bits/defines.h @@ -23,44 +23,13 @@ #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() - #define FROZEN_LETITGO_HAS_STRING_VIEW - #endif -#endif - -#ifdef __cpp_char8_t - #define FROZEN_LETITGO_HAS_CHAR8T -#endif - -#if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703L - #define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES -#endif - -#if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L - #define FROZEN_LETITGO_HAS_CONSTEXPR_STRING -#endif - -#endif // FROZEN_LETITGO_DEFINES_H +// C++23 guarantees all previously-detected features: +// - string_view (C++17) +// - char8_t (C++20) +// - deduction guides (C++17) +// - constexpr string (C++20) +// The feature-detection macros have been removed. This header +// is here only for backward compatibility to the original +// frozen implementation. + +#endif // FROZEN_LETITGO_DEFINES_H diff --git a/include/frozen/bits/elsa.h b/include/frozen/bits/elsa.h index 6c9ecb7..c07ccfd 100644 --- a/include/frozen/bits/elsa.h +++ b/include/frozen/bits/elsa.h @@ -23,35 +23,46 @@ #ifndef FROZEN_LETITGO_ELSA_H #define FROZEN_LETITGO_ELSA_H +#include #include -namespace frozen { - -template struct elsa { - static_assert(std::is_integral::value || std::is_enum::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(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 <> struct elsa { - template - constexpr std::size_t operator()(T const &value, std::size_t seed) const { - return elsa{}(value, seed); - } -}; - -template using anna = elsa; -} // namespace frozen +namespace frozen +{ + + template + concept elsa_hashable = std::integral || std::is_enum_v; + + template + struct elsa + { + static_assert(elsa_hashable, "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(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 <> + struct elsa + { + template + constexpr std::size_t operator()(T const& value, std::size_t seed) const + { + return elsa {}(value, seed); + } + }; + + template + using anna = elsa; +} // namespace frozen #endif diff --git a/include/frozen/bits/elsa_std.h b/include/frozen/bits/elsa_std.h index df1a9cf..84bcad6 100644 --- a/include/frozen/bits/elsa_std.h +++ b/include/frozen/bits/elsa_std.h @@ -1,41 +1,43 @@ #ifndef FROZEN_LETITGO_BITS_ELSA_STD_H #define FROZEN_LETITGO_BITS_ELSA_STD_H -#include "defines.h" #include "elsa.h" #include "hash_string.h" -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW -#include -#endif #include +#include -namespace frozen { - -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW - -template struct elsa> -{ - constexpr std::size_t operator()(const std::basic_string_view& value) const { - return hash_string(value); - } - constexpr std::size_t operator()(const std::basic_string_view& value, std::size_t seed) const { - return hash_string(value, seed); - } -}; - -#endif - -template struct elsa> +namespace frozen { - constexpr std::size_t operator()(const std::basic_string& value) const { - return hash_string(value); - } - constexpr std::size_t operator()(const std::basic_string& value, std::size_t seed) const { - return hash_string(value, seed); - } -}; - -} // namespace frozen -#endif // FROZEN_LETITGO_BITS_ELSA_STD_H + template + struct elsa> + { + constexpr std::size_t operator()(const std::basic_string_view& value) const + { + return hash_string(value); + } + constexpr std::size_t operator()(const std::basic_string_view& value, + std::size_t seed) const + { + return hash_string(value, seed); + } + }; + + template + struct elsa> + { + constexpr std::size_t operator()(const std::basic_string& value) const + { + return hash_string(value); + } + constexpr std::size_t operator()(const std::basic_string& value, + std::size_t seed) const + { + return hash_string(value, seed); + } + }; + +} // namespace frozen + +#endif // FROZEN_LETITGO_BITS_ELSA_STD_H diff --git a/include/frozen/bits/exceptions.h b/include/frozen/bits/exceptions.h index b43e3e6..9c71346 100644 --- a/include/frozen/bits/exceptions.h +++ b/include/frozen/bits/exceptions.h @@ -23,16 +23,16 @@ #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)) +#if defined(FROZEN_NO_EXCEPTIONS) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) || \ + (!defined(_MSC_VER) && !defined(__cpp_exceptions)) -#include -#define FROZEN_THROW_OR_ABORT(_) std::abort() + #include + #define FROZEN_THROW_OR_ABORT(_) std::abort() #else -#include -#define FROZEN_THROW_OR_ABORT(err) throw err - + #include + #define FROZEN_THROW_OR_ABORT(err) throw err #endif diff --git a/include/frozen/bits/hash_string.h b/include/frozen/bits/hash_string.h index b2f7e90..43ee83c 100644 --- a/include/frozen/bits/hash_string.h +++ b/include/frozen/bits/hash_string.h @@ -3,26 +3,29 @@ #include -namespace frozen { +namespace frozen +{ -template -constexpr std::size_t hash_string(const String& value) { - std::size_t d = 5381; - for (const auto& c : value) - d = d * 33 + static_cast(c); - return d; -} + template + constexpr std::size_t hash_string(const String& value) + { + std::size_t d = 5381; + for (const auto& c: value) + d = d * 33 + static_cast(c); + 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. -template -constexpr std::size_t hash_string(const String& value, std::size_t seed) { - std::size_t d = (0x811c9dc5 ^ seed) * static_cast(0x01000193); - for (const auto& c : value) - d = (d ^ static_cast(c)) * static_cast(0x01000193); - return d >> 8 ; -} + // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + // With the lowest bits removed, based on experimental setup. + template + constexpr std::size_t hash_string(const String& value, std::size_t seed) + { + std::size_t d = (0x811c9dc5 ^ seed) * static_cast(0x01000193); + for (const auto& c: value) + d = (d ^ static_cast(c)) * static_cast(0x01000193); + return d >> 8; + } -} // namespace frozen +} // namespace frozen -#endif // FROZEN_LETITGO_BITS_HASH_STRING_H \ No newline at end of file +#endif // FROZEN_LETITGO_BITS_HASH_STRING_H diff --git a/include/frozen/bits/mpl.h b/include/frozen/bits/mpl.h index 8f87f99..1f405b2 100644 --- a/include/frozen/bits/mpl.h +++ b/include/frozen/bits/mpl.h @@ -25,32 +25,33 @@ #include -namespace frozen { - -namespace bits { - -// Forward declarations -template -class carray; - -template -struct remove_cv : std::remove_cv {}; - -template -struct remove_cv> { - using type = std::pair::type...>; -}; - -template -struct remove_cv> { - using type = carray::type, N>; -}; - -template -using remove_cv_t = typename remove_cv::type; - -} // namespace bits - -} // namespace frozen +namespace frozen::bits +{ + + // Forward declarations + template + class carray; + + template + struct remove_cv : std::remove_cv + { + }; + + template + struct remove_cv> + { + using type = std::pair::type...>; + }; + + template + struct remove_cv> + { + using type = carray::type, N>; + }; + + template + using remove_cv_t = typename remove_cv::type; + +} // namespace frozen::bits #endif diff --git a/include/frozen/bits/pmh.h b/include/frozen/bits/pmh.h index fbdf664..e7864be 100644 --- a/include/frozen/bits/pmh.h +++ b/include/frozen/bits/pmh.h @@ -32,229 +32,255 @@ #include #include -namespace frozen { - -namespace bits { - -// Function object for sorting buckets in decreasing order of size -struct bucket_size_compare { - template - 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 -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; - carray buckets; - std::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 - carray constexpr make_bucket_refs(std::index_sequence) const { - return {{ bucket_ref{Is, &buckets[Is]}... }}; - } - - // Makes a bucket_ref for each bucket and sorts them by size - carray constexpr get_sorted_buckets() const { - carray result{this->make_bucket_refs(std::make_index_sequence())}; - bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{}); - return result; - } -}; - -template -pmh_buckets constexpr make_pmh_buckets(const carray & items, - Hash const & hash, - Key const & key, - PRG & prg) { - using result_t = pmh_buckets; - // Continue until all items are placed without exceeding bucket_max - while (1) { - result_t result{}; - result.seed = prg(); - bool rejected = false; - for (std::size_t i = 0; i < items.size(); ++i) { - auto & bucket = result.buckets[hash(key(items[i]), static_cast(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 -constexpr bool all_different_from(cvector & 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 = std::uint64_t; - -private: - static constexpr value_type MINUS_ONE = std::numeric_limits::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 -struct pmh_tables : private Hasher { - std::uint64_t first_seed_; - carray first_table_; - carray second_table_; - - constexpr pmh_tables( - std::uint64_t first_seed, - carray first_table, - carray second_table, - Hasher hash) noexcept - : Hasher(hash) - , first_seed_(first_seed) - , first_table_(first_table) - , second_table_(second_table) - {} - - constexpr Hasher const& hash_function() const noexcept { - return static_cast(*this); - } - - template - constexpr std::size_t lookup(const KeyType & key) const { - return lookup(key, hash_function()); - } - - // Looks up a given key, to find its expected index in carray - // Always returns a valid index, must use KeyEqual test after to confirm. - template - constexpr std::size_t lookup(const KeyType & key, const HasherType& hasher) const { - auto const d = first_table_[hasher(key, static_cast(first_seed_)) % M]; - if (!d.is_seed()) { return static_cast(d.value()); } // this is narrowing std::uint64 -> std::size_t but should be fine - else { return second_table_[hasher(key, static_cast(d.value())) % M]; } - } -}; - -// Make pmh tables for given items, hash function, prg, etc. -template -pmh_tables constexpr make_pmh_tables(const carray & - items, - Hash const &hash, - KeyEqual const &equal, - Key const &key, - PRG prg) { - // Step 1: Place all of the keys into buckets - auto step_one = make_pmh_buckets(items, hash, key, prg); - - // Step 1.5: Detect redundant keys. - for(auto const& bucket : step_one.buckets) - for(std::size_t i = 1; i < bucket.size(); ++i) - constexpr_assert(!equal(key(items[0]), key(items[i])), "structure keys should be unique"); - - // Step 2: Sort the buckets to process the ones with the most items first. - auto buckets = step_one.get_sorted_buckets(); - - // Special value for unused slots. This is purposefully the index - // one-past-the-end of 'items' to function as a sentinel value. Both to avoid - // the need to apply the KeyEqual predicate and to be easily convertible to - // end(). - // Unused entries in both hash tables (G and H) have to contain this value. - const auto UNUSED = items.size(); - - // G becomes the first hash table in the resulting pmh function - carray G({false, UNUSED}); - - // H becomes the second hash table in the resulting pmh function - carray H(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(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 bucket_slots; - - while (bucket_slots.size() < bsize) { - auto slot = hash(key(items[bucket[bucket_slots.size()]]), static_cast(d.value())) % M; - - if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) { - bucket_slots.clear(); - d = {true, prg()}; - continue; +namespace frozen::bits +{ + + // Function object for sorting buckets in decreasing order of size + struct bucket_size_compare + { + template + constexpr bool 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 + 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; + carray buckets; + std::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 + { + std::size_t 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 + carray constexpr make_bucket_refs(std::index_sequence) const + { + return { { bucket_ref { Is, &buckets[Is] }... } }; + } + + // Makes a bucket_ref for each bucket and sorts them by size + carray constexpr get_sorted_buckets() const + { + carray result { this->make_bucket_refs(std::make_index_sequence()) }; + if constexpr (M > 1) + bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare {}); + return result; + } + }; + + template + pmh_buckets constexpr make_pmh_buckets(const carray& items, Hash const& hash, + Key const& key, PRG& prg) + { + using result_t = pmh_buckets; + // Continue until all items are placed without exceeding bucket_max + while (true) + { + result_t result {}; + result.seed = prg(); + bool rejected = false; + for (std::size_t i = 0; i < items.size(); ++i) + { + auto& bucket = + result.buckets[hash(key(items[i]), static_cast(result.seed)) % M]; + if (bucket.size() >= result_t::bucket_max) + { + rejected = true; + break; + } + bucket.push_back(i); + } + if (!rejected) + { + return result; + } } + } - bucket_slots.push_back(slot); - } + // Check if an item appears in a cvector + template + constexpr bool all_different_from(cvector const& data, T const& a) + { + for (std::size_t i = 0; i < data.size(); ++i) + if (data[i] == a) + return false; - // 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]; + return true; } - } - return {step_one.seed, G, H, hash}; -} + // 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 = std::uint64_t; + + private: + static constexpr value_type MINUS_ONE = std::numeric_limits::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)) + { + } -} // namespace bits + 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 + struct pmh_tables : private Hasher + { + std::uint64_t first_seed_; + carray first_table_; + carray second_table_; + + constexpr pmh_tables(std::uint64_t first_seed, carray first_table, + carray second_table, Hasher hash) noexcept : + Hasher(hash), first_seed_(first_seed), first_table_(first_table), + second_table_(second_table) + { + } + + constexpr Hasher const& hash_function() const noexcept + { + return static_cast(*this); + } + + template + constexpr std::size_t lookup(const KeyType& key) const + { + return lookup(key, hash_function()); + } + + // Looks up a given key, to find its expected index in carray + // Always returns a valid index, must use KeyEqual test after to confirm. + template + constexpr std::size_t lookup(const KeyType& key, const HasherType& hasher) const + { + auto const d = first_table_[hasher(key, static_cast(first_seed_)) % M]; + if (!d.is_seed()) + { + return static_cast(d.value()); + } // this is narrowing std::uint64 -> std::size_t but should be fine + else + { + return second_table_[hasher(key, static_cast(d.value())) % M]; + } + } + }; + + // Make pmh tables for given items, hash function, prg, etc. + template + pmh_tables constexpr make_pmh_tables(const carray& items, Hash const& hash, + KeyEqual const& equal, Key const& key, PRG prg) + { + // Step 1: Place all of the keys into buckets + auto step_one = make_pmh_buckets(items, hash, key, prg); + + // Step 1.5: Detect redundant keys. + for (auto const& bucket: step_one.buckets) + for (std::size_t j = 0; j < bucket.size(); ++j) + for (std::size_t i = j + 1; i < bucket.size(); ++i) + constexpr_assert(!equal(key(items[bucket[j]]), key(items[bucket[i]])), + "structure keys should be unique"); + + // Step 2: Sort the buckets to process the ones with the most items first. + auto buckets = step_one.get_sorted_buckets(); + + // Special value for unused slots. This is purposefully the index + // one-past-the-end of 'items' to function as a sentinel value. Both to avoid + // the need to apply the KeyEqual predicate and to be easily convertible to + // end(). + // Unused entries in both hash tables (G and H) have to contain this value. + const auto UNUSED = items.size(); + + // G becomes the first hash table in the resulting pmh function + carray G({ false, UNUSED }); + + // H becomes the second hash table in the resulting pmh function + carray H(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(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 bucket_slots; + + while (bucket_slots.size() < bsize) + { + auto slot = hash(key(items[bucket[bucket_slots.size()]]), + static_cast(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]; + } + } + + return { step_one.seed, G, H, hash }; + } -} // namespace frozen +} // namespace frozen::bits #endif diff --git a/include/frozen/bits/version.h b/include/frozen/bits/version.h index 7e57d70..cc6f1e7 100644 --- a/include/frozen/bits/version.h +++ b/include/frozen/bits/version.h @@ -23,8 +23,17 @@ #ifndef FROZEN_LETITGO_VERSION_H #define FROZEN_LETITGO_VERSION_H -#define FROZEN_MAJOR_VERSION 1 -#define FROZEN_MINOR_VERSION 1 -#define FROZEN_PATCH_VERSION 1 +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error "frozen 2.0 requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen 2.0 requires C++23 or later. Compile with -std=c++23 or later." +#endif + +#define FROZEN_MAJOR_VERSION 2 +#define FROZEN_MINOR_VERSION 0 +#define FROZEN_PATCH_VERSION 0 #endif diff --git a/include/frozen/map.h b/include/frozen/map.h index cb3e3a2..dc203c9 100644 --- a/include/frozen/map.h +++ b/include/frozen/map.h @@ -23,6 +23,15 @@ #ifndef FROZEN_LETITGO_MAP_H #define FROZEN_LETITGO_MAP_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error "frozen/map.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/map.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/algorithms.h" #include "frozen/bits/basic_types.h" #include "frozen/bits/exceptions.h" @@ -32,324 +41,420 @@ #include #include -namespace frozen { - -namespace impl { - -template class CompareKey : private Comparator { -public: - constexpr Comparator const& key_comp() const noexcept { - return static_cast(*this); - } - - constexpr CompareKey(Comparator const &comparator) - : Comparator(comparator) {} - - template - constexpr auto operator()(std::pair const &self, - std::pair const &other) const -> decltype(key_comp()(std::get<0>(self), std::get<0>(other))) { - return key_comp()(std::get<0>(self), std::get<0>(other)); - } - - template - constexpr auto operator()(Key1 const &self_key, - std::pair const &other) const -> decltype(key_comp()(self_key, std::get<0>(other))) { - return key_comp()(self_key, std::get<0>(other)); - } - - template - constexpr auto operator()(std::pair const &self, - Key2 const &other_key) const -> decltype(key_comp()(std::get<0>(self), other_key)) { - return key_comp()(std::get<0>(self), other_key); - } - - template - constexpr auto operator()(Key1 const &self_key, Key2 const &other_key) const -> decltype(key_comp()(self_key, other_key)) { - return key_comp()(self_key, other_key); - } -}; - -} // namespace impl - -template > -class map : private impl::CompareKey { - using container_type = bits::carray, N>; - 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 = Compare; - using value_compare = impl::CompareKey; - 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 = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - -public: - /* constructors */ - constexpr map(container_type items, Compare const &compare) - : impl::CompareKey{compare} - , items_{bits::quicksort(bits::remove_cv_t(items), value_comp())} {} - - explicit constexpr map(container_type items) - : map{items, Compare{}} {} - - constexpr map(std::initializer_list items, Compare const &compare) - : map{container_type {items}, compare} { - } - - constexpr map(std::initializer_list 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_.begin(); } - constexpr iterator end() { return items_.end(); } - constexpr const_iterator end() const { return items_.end(); } - constexpr const_iterator cend() const { return items_.end(); } - - constexpr reverse_iterator rbegin() { return reverse_iterator{items_.end()}; } - constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator{items_.end()}; } - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{items_.end()}; } - constexpr reverse_iterator rend() { return reverse_iterator{items_.begin()}; } - constexpr const_reverse_iterator rend() const { return const_reverse_iterator{items_.begin()}; } - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{items_.begin()}; } - - /* capacity */ - constexpr bool empty() const { return !N; } - constexpr size_type size() const { return N; } - constexpr size_type max_size() const { return N; } - - /* lookup */ - - template - constexpr std::size_t count(KeyType const &key) const { - return bits::binary_search(items_.begin(), key, value_comp()); - } - - template - constexpr const_iterator find(KeyType const &key) const { - return map::find_impl(*this, key); - } - template - constexpr iterator find(KeyType const &key) { - return map::find_impl(*this, key); - } - - template - constexpr bool contains(KeyType const &key) const { - return this->find(key) != this->end(); - } - - template - constexpr std::pair - equal_range(KeyType const &key) const { - return equal_range_impl(*this, key); - } - template - constexpr std::pair equal_range(KeyType const &key) { - return equal_range_impl(*this, key); - } - - template - constexpr const_iterator lower_bound(KeyType const &key) const { - return lower_bound_impl(*this, key); - } - template - constexpr iterator lower_bound(KeyType const &key) { - return lower_bound_impl(*this, key); - } - - template - constexpr const_iterator upper_bound(KeyType const &key) const { - return upper_bound_impl(*this, key); - } - template - constexpr iterator upper_bound(KeyType const &key) { - return upper_bound_impl(*this, key); - } - - /* observers */ - constexpr const key_compare& key_comp() const { return value_comp().key_comp(); } - constexpr const value_compare& value_comp() const { return static_cast const&>(*this); } - - private: - template - static inline constexpr auto& at_impl(This&& self, KeyType const &key) { - auto where = self.find(key); - if (where != self.end()) - return where->second; - else - FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); - } - - template - static inline constexpr auto find_impl(This&& self, KeyType const &key) { - auto where = self.lower_bound(key); - if (where != self.end() && !self.value_comp()(key, *where)) - return where; - else - return self.end(); - } - - template - static inline constexpr auto equal_range_impl(This&& self, KeyType const &key) { - auto lower = self.lower_bound(key); - using lower_t = decltype(lower); - if (lower != self.end() && !self.value_comp()(key, *lower)) - return std::pair{lower, lower + 1}; - else - return std::pair{lower, lower}; - } - - template - static inline constexpr auto lower_bound_impl(This&& self, KeyType const &key) -> decltype(self.end()) { - return bits::lower_bound(self.items_.begin(), key, self.value_comp()); - } - - template - static inline constexpr auto upper_bound_impl(This&& self, KeyType const &key) { - auto lower = self.lower_bound(key); - if (lower != self.end() && !self.value_comp()(key, *lower)) - return lower + 1; - else - return lower; - } -}; - -template -class map : private impl::CompareKey { - using container_type = bits::carray, 0>; - -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 = Compare; - using value_compare = impl::CompareKey; - 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, Compare const &compare) - : impl::CompareKey{compare} {} - constexpr map(std::initializer_list items) - : map{items, Compare{}} {} - - /* element access */ - template - constexpr mapped_type at(KeyType const &) const { - FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key")); - } - template - constexpr mapped_type at(KeyType 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 */ - - template - constexpr std::size_t count(KeyType const &) const { return 0; } - - template - constexpr const_iterator find(KeyType const &) const { return end(); } - template - constexpr iterator find(KeyType const &) { return end(); } - - template - constexpr std::pair - equal_range(KeyType const &) const { return {end(), end()}; } - template - constexpr std::pair - equal_range(KeyType const &) { return {end(), end()}; } - - template - constexpr const_iterator lower_bound(KeyType const &) const { return end(); } - template - constexpr iterator lower_bound(KeyType const &) { return end(); } - - template - constexpr const_iterator upper_bound(KeyType const &) const { return end(); } - template - constexpr iterator upper_bound(KeyType const &) { return end(); } - -/* observers */ - constexpr key_compare const& key_comp() const { return value_comp().key_comp(); } - constexpr value_compare const& value_comp() const { return static_cast const&>(*this); } -}; - -template > -constexpr auto make_map(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) { - return map{}; -} - -template -constexpr auto make_map(std::pair const (&items)[N]) { - return map{items}; -} - -template -constexpr auto make_map(std::array, N> const &items) { - return map{items}; -} - -template -constexpr auto make_map(std::pair const (&items)[N], Compare const& compare = Compare{}) { - return map{items, compare}; -} - -template -constexpr auto make_map(std::array, N> const &items, Compare const& compare = Compare{}) { - return map{items, compare}; -} - -} // namespace frozen +namespace frozen +{ + + namespace impl + { + + template + class CompareKey : private Comparator + { + public: + constexpr Comparator const& key_comp() const noexcept + { + return static_cast(*this); + } + + constexpr CompareKey(Comparator const& comparator) : Comparator(comparator) {} + + template + constexpr auto operator()(std::pair const& self, + std::pair const& other) const + -> decltype(key_comp()(std::get<0>(self), std::get<0>(other))) + { + return key_comp()(std::get<0>(self), std::get<0>(other)); + } + + template + constexpr auto operator()(Key1 const& self_key, + std::pair const& other) const + -> decltype(key_comp()(self_key, std::get<0>(other))) + { + return key_comp()(self_key, std::get<0>(other)); + } + + template + constexpr auto operator()(std::pair const& self, + Key2 const& other_key) const + -> decltype(key_comp()(std::get<0>(self), other_key)) + { + return key_comp()(std::get<0>(self), other_key); + } + + template + constexpr auto operator()(Key1 const& self_key, Key2 const& other_key) const + -> decltype(key_comp()(self_key, other_key)) + { + return key_comp()(self_key, other_key); + } + }; + + } // namespace impl + + template > + class map : private impl::CompareKey + { + using container_type = bits::carray, N>; + 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 = Compare; + using value_compare = impl::CompareKey; + 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 = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + public: + /* constructors */ + constexpr map(container_type items, Compare const& compare) : + impl::CompareKey { compare }, + items_ { bits::quicksort(bits::remove_cv_t(items), value_comp()) } + { + } + + explicit constexpr map(container_type items) : map { items, Compare {} } {} + + constexpr map(std::initializer_list items, Compare const& compare) : + map { container_type { items }, compare } + { + } + + constexpr map(std::initializer_list items) : map { items, Compare {} } {} + + /* element access */ + [[nodiscard]] constexpr Value const& at(Key const& key) const + { + return at_impl(*this, key); + } + [[nodiscard]] constexpr Value& at(Key const& key) { return at_impl(*this, key); } + + /* iterators */ + [[nodiscard]] constexpr iterator begin() { return items_.begin(); } + [[nodiscard]] constexpr const_iterator begin() const { return items_.begin(); } + [[nodiscard]] constexpr const_iterator cbegin() const { return items_.begin(); } + [[nodiscard]] constexpr iterator end() { return items_.end(); } + [[nodiscard]] constexpr const_iterator end() const { return items_.end(); } + [[nodiscard]] constexpr const_iterator cend() const { return items_.end(); } + + [[nodiscard]] constexpr reverse_iterator rbegin() + { + return reverse_iterator { items_.end() }; + } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const + { + return const_reverse_iterator { items_.end() }; + } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator { items_.end() }; + } + [[nodiscard]] constexpr reverse_iterator rend() + { + return reverse_iterator { items_.begin() }; + } + [[nodiscard]] constexpr const_reverse_iterator rend() const + { + return const_reverse_iterator { items_.begin() }; + } + [[nodiscard]] constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator { items_.begin() }; + } + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return !N; } + [[nodiscard]] constexpr size_type size() const { return N; } + [[nodiscard]] constexpr size_type max_size() const { return N; } + + /* lookup */ + + template + [[nodiscard]] constexpr std::size_t count(KeyType const& key) const + { + return bits::binary_search(items_.begin(), key, value_comp()); + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const& key) const + { + return map::find_impl(*this, key); + } + template + [[nodiscard]] constexpr iterator find(KeyType const& key) + { + return map::find_impl(*this, key); + } + + template + [[nodiscard]] constexpr bool contains(KeyType const& key) const + { + return this->find(key) != this->end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const& key) const + { + return equal_range_impl(*this, key); + } + template + [[nodiscard]] constexpr std::pair equal_range(KeyType const& key) + { + return equal_range_impl(*this, key); + } + + template + [[nodiscard]] constexpr const_iterator lower_bound(KeyType const& key) const + { + return lower_bound_impl(*this, key); + } + template + [[nodiscard]] constexpr iterator lower_bound(KeyType const& key) + { + return lower_bound_impl(*this, key); + } + + template + [[nodiscard]] constexpr const_iterator upper_bound(KeyType const& key) const + { + return upper_bound_impl(*this, key); + } + template + [[nodiscard]] constexpr iterator upper_bound(KeyType const& key) + { + return upper_bound_impl(*this, key); + } + + /* observers */ + [[nodiscard]] constexpr const key_compare& key_comp() const + { + return value_comp().key_comp(); + } + [[nodiscard]] constexpr const value_compare& value_comp() const + { + return static_cast const&>(*this); + } + + private: + template + static inline constexpr auto& at_impl(This&& self, KeyType const& key) + { + auto where = self.find(key); + if (where != self.end()) + return where->second; + else + FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); + } + + template + static inline constexpr auto find_impl(This&& self, KeyType const& key) + { + auto where = self.lower_bound(key); + if (where != self.end() && !self.value_comp()(key, *where)) + return where; + else + return self.end(); + } + + template + static inline constexpr auto equal_range_impl(This&& self, KeyType const& key) + { + auto lower = self.lower_bound(key); + using lower_t = decltype(lower); + if (lower != self.end() && !self.value_comp()(key, *lower)) + return std::pair { lower, lower + 1 }; + else + return std::pair { lower, lower }; + } + + template + static inline constexpr auto lower_bound_impl(This&& self, KeyType const& key) + -> decltype(self.end()) + { + return bits::lower_bound(self.items_.begin(), key, self.value_comp()); + } + + template + static inline constexpr auto upper_bound_impl(This&& self, KeyType const& key) + { + auto lower = self.lower_bound(key); + if (lower != self.end() && !self.value_comp()(key, *lower)) + return lower + 1; + else + return lower; + } + }; + + template + class map : private impl::CompareKey + { + using container_type = bits::carray, 0>; + + 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 = Compare; + using value_compare = impl::CompareKey; + 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, Compare const& compare) : + impl::CompareKey { compare } + { + } + constexpr map(std::initializer_list items) : map { items, Compare {} } {} + + /* element access */ + template + [[nodiscard]] constexpr mapped_type at(KeyType const&) const + { + FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key")); + } + template + [[nodiscard]] constexpr mapped_type at(KeyType const&) + { + FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key")); + } + + /* iterators */ + [[nodiscard]] constexpr iterator begin() { return nullptr; } + [[nodiscard]] constexpr const_iterator begin() const { return nullptr; } + [[nodiscard]] constexpr const_iterator cbegin() const { return nullptr; } + [[nodiscard]] constexpr iterator end() { return nullptr; } + [[nodiscard]] constexpr const_iterator end() const { return nullptr; } + [[nodiscard]] constexpr const_iterator cend() const { return nullptr; } + + [[nodiscard]] constexpr reverse_iterator rbegin() { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const { return nullptr; } + [[nodiscard]] constexpr reverse_iterator rend() { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator rend() const { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator crend() const { return nullptr; } + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return true; } + [[nodiscard]] constexpr size_type size() const { return 0; } + [[nodiscard]] constexpr size_type max_size() const { return 0; } + + /* lookup */ + + template + [[nodiscard]] constexpr std::size_t count(KeyType const&) const + { + return 0; + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const&) const + { + return end(); + } + template + [[nodiscard]] constexpr iterator find(KeyType const&) + { + return end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const&) const + { + return { end(), end() }; + } + template + [[nodiscard]] constexpr std::pair equal_range(KeyType const&) + { + return { end(), end() }; + } + + template + [[nodiscard]] constexpr const_iterator lower_bound(KeyType const&) const + { + return end(); + } + template + [[nodiscard]] constexpr iterator lower_bound(KeyType const&) + { + return end(); + } + + template + [[nodiscard]] constexpr const_iterator upper_bound(KeyType const&) const + { + return end(); + } + template + [[nodiscard]] constexpr iterator upper_bound(KeyType const&) + { + return end(); + } + + /* observers */ + [[nodiscard]] constexpr key_compare const& key_comp() const + { + return value_comp().key_comp(); + } + [[nodiscard]] constexpr value_compare const& value_comp() const + { + return static_cast const&>(*this); + } + }; + + template > + constexpr auto + make_map(bits::ignored_arg = {} /* for consistency with the initializer below for N = 0*/) + { + return map {}; + } + + template + constexpr auto make_map(std::pair const (&items)[N]) + { + return map { items }; + } + + template + constexpr auto make_map(std::array, N> const& items) + { + return map { items }; + } + + template + constexpr auto make_map(std::pair const (&items)[N], Compare const& compare = Compare {}) + { + return map { items, compare }; + } + + template + constexpr auto make_map(std::array, N> const& items, + Compare const& compare = Compare {}) + { + return map { items, compare }; + } + +} // namespace frozen #endif diff --git a/include/frozen/random.h b/include/frozen/random.h index 727133b..5a57c35 100644 --- a/include/frozen/random.h +++ b/include/frozen/random.h @@ -23,75 +23,86 @@ #ifndef FROZEN_LETITGO_RANDOM_H #define FROZEN_LETITGO_RANDOM_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error "frozen/random.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/random.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/algorithms.h" #include "frozen/bits/version.h" +#include #include -#include - -namespace frozen { -template -class linear_congruential_engine { - - static_assert(std::is_unsigned::value, - "UIntType must be an unsigned integral type"); - - template - static constexpr UIntType modulo(T val, std::integral_constant) { - return static_cast(val); - } - - template - static constexpr UIntType modulo(T val, std::integral_constant) { - // the static cast below may end up doing a truncation - return static_cast(val % M); - } - -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; - uint_least_t tmp = static_cast(multiplier) * state_ + increment; - - state_ = modulo(tmp, std::integral_constant()); - 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; -using minstd_rand = - linear_congruential_engine; - -// This generator is used by default in unordered frozen containers -using default_prg_t = minstd_rand; -} // namespace frozen +namespace frozen +{ + template + class linear_congruential_engine + { + template + static constexpr UIntType modulo(T val, std::integral_constant) + { + return static_cast(val); + } + + template + static constexpr UIntType modulo(T val, std::integral_constant) + { + // the static cast below may end up doing a truncation + return static_cast(val % M); + } + + 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; + uint_least_t tmp = static_cast(multiplier) * state_ + increment; + + state_ = modulo(tmp, std::integral_constant()); + 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; + using minstd_rand = linear_congruential_engine; + + // This generator is used by default in unordered frozen containers + using default_prg_t = minstd_rand; + +} // namespace frozen #endif diff --git a/include/frozen/set.h b/include/frozen/set.h index edf1ad1..184e202 100644 --- a/include/frozen/set.h +++ b/include/frozen/set.h @@ -23,236 +23,297 @@ #ifndef FROZEN_SET_H #define FROZEN_SET_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error "frozen/set.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/set.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/algorithms.h" #include "frozen/bits/basic_types.h" #include "frozen/bits/version.h" -#include "frozen/bits/defines.h" #include #include -namespace frozen { - -template > class set : private Compare { - using container_type = bits::carray; - 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 = std::reverse_iterator; - using const_iterator = iterator; - using const_reverse_iterator = std::reverse_iterator; - -public: - /* constructors */ - constexpr set(const set &other) = default; - - constexpr set(container_type keys, Compare const & comp) - : Compare{comp} - , keys_(bits::quicksort(keys, value_comp())) { - } - - explicit constexpr set(container_type keys) - : set{keys, Compare{}} {} - - constexpr set(std::initializer_list keys, Compare const & comp) - : set{container_type{keys}, comp} { - } - - constexpr set(std::initializer_list keys) - : set{keys, Compare{}} {} - - constexpr set& operator=(const set &other) = default; - - /* capacity */ - constexpr bool empty() const { return !N; } - constexpr size_type size() const { return N; } - constexpr size_type max_size() const { return N; } - - /* lookup */ - template - constexpr std::size_t count(KeyType const &key) const { - return bits::binary_search(keys_.begin(), key, value_comp()); - } - - template - constexpr const_iterator find(KeyType const &key) const { - const_iterator where = lower_bound(key); - if ((where != end()) && !value_comp()(key, *where)) - return where; - else - return end(); - } - - template - constexpr bool contains(KeyType const &key) const { - return this->find(key) != keys_.end(); - } - - template - constexpr std::pair equal_range(KeyType const &key) const { - auto const lower = lower_bound(key); - if (lower == end()) - return {lower, lower}; - else - return {lower, lower + 1}; - } - - template - constexpr const_iterator lower_bound(KeyType const &key) const { - auto const where = bits::lower_bound(keys_.begin(), key, value_comp()); - if ((where != end()) && !value_comp()(key, *where)) - return where; - else - return end(); - } - - template - constexpr const_iterator upper_bound(KeyType const &key) const { - auto const where = bits::lower_bound(keys_.begin(), key, value_comp()); - if ((where != end()) && !value_comp()(key, *where)) - return where + 1; - else - return end(); - } - - /* observers */ - constexpr const key_compare& key_comp() const { return value_comp(); } - constexpr const key_compare& value_comp() const { return static_cast(*this); } - - /* iterators */ - constexpr const_iterator begin() const { return keys_.begin(); } - constexpr const_iterator cbegin() const { return keys_.begin(); } - constexpr const_iterator end() const { return keys_.end(); } - constexpr const_iterator cend() const { return keys_.end(); } - - constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator{keys_.end()}; } - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{keys_.end()}; } - constexpr const_reverse_iterator rend() const { return const_reverse_iterator{keys_.begin()}; } - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{keys_.begin()}; } - - /* 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 set : private Compare { - using container_type = bits::carray; // just for the type definitions - -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, Compare const &) {} - explicit constexpr set(bits::carray) {} - - constexpr set(std::initializer_list, Compare const &comp) - : Compare{comp} {} - constexpr set(std::initializer_list keys) : set{keys, Compare{}} {} - - constexpr set& operator=(const set &other) = default; - - /* capacity */ - constexpr bool empty() const { return true; } - constexpr size_type size() const { return 0; } - constexpr size_type max_size() const { return 0; } - - /* lookup */ - template - constexpr std::size_t count(KeyType const &) const { return 0; } - - template - constexpr const_iterator find(KeyType const &) const { return end(); } - - template - constexpr std::pair - equal_range(KeyType const &) const { return {end(), end()}; } - - template - constexpr const_iterator lower_bound(KeyType const &) const { return end(); } - - template - constexpr const_iterator upper_bound(KeyType const &) const { return end(); } - - /* observers */ - constexpr const key_compare& key_comp() const { return value_comp(); } - constexpr const key_compare& value_comp() const { return static_cast(*this); } - - /* 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 -constexpr auto make_set(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) { - return set{}; -} - -template -constexpr auto make_set(const T (&args)[N]) { - return set(args); -} - -template -constexpr auto make_set(std::array const &args) { - return set(args); -} - -template -constexpr auto make_set(const T (&args)[N], Compare const& compare = Compare{}) { - return set(args, compare); -} - -template -constexpr auto make_set(std::array const &args, Compare const& compare = Compare{}) { - return set(args, compare); -} - -#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES - -template -set(T, Args...) -> set; - -#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES - -} // namespace frozen +namespace frozen +{ + + template > + class set : private Compare + { + using container_type = bits::carray; + 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 = std::reverse_iterator; + using const_iterator = iterator; + using const_reverse_iterator = std::reverse_iterator; + + /* constructors */ + constexpr set(const set& other) = default; + + constexpr set(container_type keys, Compare const& comp) : + Compare { comp }, keys_(bits::quicksort(keys, value_comp())) + { + } + + explicit constexpr set(container_type keys) : set { keys, Compare {} } {} + + constexpr set(std::initializer_list keys, Compare const& comp) : + set { container_type { keys }, comp } + { + } + + constexpr set(std::initializer_list keys) : set { keys, Compare {} } {} + + constexpr set& operator=(const set& other) = default; + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return !N; } + [[nodiscard]] constexpr size_type size() const { return N; } + [[nodiscard]] constexpr size_type max_size() const { return N; } + + /* lookup */ + template + [[nodiscard]] constexpr std::size_t count(KeyType const& key) const + { + return bits::binary_search(keys_.begin(), key, value_comp()); + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const& key) const + { + const_iterator where = lower_bound(key); + if ((where != end()) && !value_comp()(key, *where)) + return where; + else + return end(); + } + + template + [[nodiscard]] constexpr bool contains(KeyType const& key) const + { + return this->find(key) != keys_.end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const& key) const + { + auto const lower = lower_bound(key); + if (lower == end()) + return { lower, lower }; + else + return { lower, lower + 1 }; + } + + template + [[nodiscard]] constexpr const_iterator lower_bound(KeyType const& key) const + { + auto const where = bits::lower_bound(keys_.begin(), key, value_comp()); + if ((where != end()) && !value_comp()(key, *where)) + return where; + else + return end(); + } + + template + [[nodiscard]] constexpr const_iterator upper_bound(KeyType const& key) const + { + auto const where = bits::lower_bound(keys_.begin(), key, value_comp()); + if ((where != end()) && !value_comp()(key, *where)) + return where + 1; + else + return end(); + } + + /* observers */ + [[nodiscard]] constexpr const key_compare& key_comp() const { return value_comp(); } + [[nodiscard]] constexpr const key_compare& value_comp() const + { + return static_cast(*this); + } + + /* iterators */ + [[nodiscard]] constexpr const_iterator begin() const { return keys_.begin(); } + [[nodiscard]] constexpr const_iterator cbegin() const { return keys_.begin(); } + [[nodiscard]] constexpr const_iterator end() const { return keys_.end(); } + [[nodiscard]] constexpr const_iterator cend() const { return keys_.end(); } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const + { + return const_reverse_iterator { keys_.end() }; + } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator { keys_.end() }; + } + [[nodiscard]] constexpr const_reverse_iterator rend() const + { + return const_reverse_iterator { keys_.begin() }; + } + [[nodiscard]] constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator { keys_.begin() }; + } + + /* 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 set : private Compare + { + using container_type = bits::carray; // just for the type definitions + + 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, Compare const&) {} + explicit constexpr set(bits::carray) {} + + constexpr set(std::initializer_list, Compare const& comp) : Compare { comp } {} + constexpr set(std::initializer_list keys) : set { keys, Compare {} } {} + + constexpr set& operator=(const set& other) = default; + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return true; } + [[nodiscard]] constexpr size_type size() const { return 0; } + [[nodiscard]] constexpr size_type max_size() const { return 0; } + + /* lookup */ + template + [[nodiscard]] constexpr std::size_t count(KeyType const&) const + { + return 0; + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const&) const + { + return end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const&) const + { + return { end(), end() }; + } + + template + [[nodiscard]] constexpr const_iterator lower_bound(KeyType const&) const + { + return end(); + } + + template + [[nodiscard]] constexpr const_iterator upper_bound(KeyType const&) const + { + return end(); + } + + /* observers */ + [[nodiscard]] constexpr const key_compare& key_comp() const { return value_comp(); } + [[nodiscard]] constexpr const key_compare& value_comp() const + { + return static_cast(*this); + } + + /* iterators */ + [[nodiscard]] constexpr const_iterator begin() const { return nullptr; } + [[nodiscard]] constexpr const_iterator cbegin() const { return nullptr; } + [[nodiscard]] constexpr const_iterator end() const { return nullptr; } + [[nodiscard]] constexpr const_iterator cend() const { return nullptr; } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator rend() const { return nullptr; } + [[nodiscard]] constexpr const_reverse_iterator crend() const { return nullptr; } + }; + + template + constexpr auto + make_set(bits::ignored_arg = {} /* for consistency with the initializer below for N = 0*/) + { + return set {}; + } + + template + constexpr auto make_set(const T (&args)[N]) + { + return set(args); + } + + template + constexpr auto make_set(std::array const& args) + { + return set(args); + } + + template + constexpr auto make_set(const T (&args)[N], Compare const& compare = Compare {}) + { + return set(args, compare); + } + + template + constexpr auto make_set(std::array const& args, Compare const& compare = Compare {}) + { + return set(args, compare); + } + + template + set(T, Args...) -> set; + +} // namespace frozen #endif diff --git a/include/frozen/string.h b/include/frozen/string.h index 12334ef..560fc6f 100644 --- a/include/frozen/string.h +++ b/include/frozen/string.h @@ -1,163 +1,62 @@ -/* - * 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 +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error "frozen/string.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/string.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/elsa.h" #include "frozen/bits/hash_string.h" #include "frozen/bits/version.h" -#include "frozen/bits/defines.h" #include #include - -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW #include -#endif - -namespace frozen { - -template -class basic_string { - using chr_t = _CharT; - - chr_t const *data_; - std::size_t size_; - -public: - template - 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 data) - : data_(data.data()), size_(data.size()) {} - - explicit constexpr operator std::basic_string_view() const { - return std::basic_string_view(data_, size_); - } -#endif - constexpr basic_string(const basic_string &) noexcept = default; - constexpr basic_string &operator=(const basic_string &) noexcept = default; +namespace frozen +{ - constexpr std::size_t length() const { return size_; } - constexpr std::size_t size() const { return size_; } + using string = std::string_view; + using wstring = std::wstring_view; + using u16string = std::u16string_view; + using u32string = std::u32string_view; + using u8string = std::u8string_view; - constexpr chr_t operator[](std::size_t i) const { return data_[i]; } + namespace string_literals + { - 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 std::string_view operator""_s(const char* data, std::size_t size) + { + return { data, size }; + } - 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 std::wstring_view operator""_s(const wchar_t* data, std::size_t size) + { + return { data, size }; + } - friend constexpr bool operator>(const basic_string& lhs, const basic_string& rhs) { - return rhs < lhs; - } - friend constexpr bool operator>=(const basic_string& lhs, const basic_string& rhs) { - return !(lhs < rhs); - } - friend constexpr bool operator<=(const basic_string& lhs, const basic_string& rhs) { - return !(lhs > rhs); - } + constexpr std::u16string_view operator""_s(const char16_t* data, std::size_t size) + { + return { data, size }; + } - constexpr const chr_t *data() const { return data_; } - constexpr const chr_t *begin() const { return data(); } - constexpr const chr_t *end() const { return data() + size(); } -}; - -template struct elsa> { - constexpr std::size_t operator()(basic_string<_CharT> value) const { - return hash_string(value); - } - constexpr std::size_t operator()(basic_string<_CharT> value, std::size_t seed) const { - return hash_string(value, seed); - } -}; - -using string = basic_string; -using wstring = basic_string; -using u16string = basic_string; -using u32string = basic_string; - -#ifdef FROZEN_LETITGO_HAS_CHAR8T -using u8string = basic_string; -#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 + constexpr std::u32string_view operator""_s(const char32_t* data, std::size_t size) + { + return { data, size }; + } -} // namespace string_literals + constexpr std::u8string_view operator""_s(const char8_t* data, std::size_t size) + { + return { data, size }; + } -} // namespace frozen + } // namespace string_literals -namespace std { -template struct hash> { - std::size_t operator()(frozen::basic_string<_CharT> s) const { - return frozen::elsa>{}(s); - } -}; -} // namespace std +} // namespace frozen #endif diff --git a/include/frozen/unordered_map.h b/include/frozen/unordered_map.h index 2405385..4fadde8 100644 --- a/include/frozen/unordered_map.h +++ b/include/frozen/unordered_map.h @@ -23,6 +23,16 @@ #ifndef FROZEN_LETITGO_UNORDERED_MAP_H #define FROZEN_LETITGO_UNORDERED_MAP_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error \ + "frozen/unordered_map.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/unordered_map.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/basic_types.h" #include "frozen/bits/elsa.h" #include "frozen/bits/exceptions.h" @@ -30,187 +40,218 @@ #include "frozen/bits/version.h" #include "frozen/random.h" -#include #include +#include #include -namespace frozen { - -namespace bits { - -struct GetKey { - template constexpr auto const &operator()(KV const &kv) const { - return kv.first; - } -}; - -} // namespace bits - -template , - class KeyEqual = std::equal_to> -class unordered_map : private KeyEqual { - 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, N>; - using tables_type = bits::pmh_tables; - - container_type items_; - tables_type tables_; - -public: - /* typedefs */ - using Self = unordered_map; - 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) - : KeyEqual{equal} - , items_{items} - , tables_{ - bits::make_pmh_tables( - items_, hash, equal, bits::GetKey{}, default_prg_t{})} {} - explicit constexpr unordered_map(container_type items) - : unordered_map{items, Hash{}, KeyEqual{}} { - } - - constexpr unordered_map(std::initializer_list items, - Hash const & hash, KeyEqual const & equal) - : unordered_map{container_type{items}, hash, equal} { - } - - constexpr unordered_map(std::initializer_list 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_.begin(); } - constexpr const_iterator cend() const { return items_.end(); } - - /* capacity */ - constexpr bool empty() const { return !N; } - constexpr size_type size() const { return N; } - constexpr size_type max_size() const { return N; } - - /* lookup */ - template - constexpr std::size_t count(KeyType const &key) const { - return find(key) != end(); - } - - template - constexpr Value const &at(KeyType const &key) const { - return at_impl(*this, key); - } - template - constexpr Value &at(KeyType const &key) { - return at_impl(*this, key); - } - - template - constexpr const_iterator find(KeyType const &key) const { - return find_impl(*this, key, hash_function(), key_eq()); - } - template - constexpr iterator find(KeyType const &key) { - return find_impl(*this, key, hash_function(), key_eq()); - } - - template - constexpr bool contains(KeyType const &key) const { - return this->find(key) != this->end(); - } - - template - constexpr std::pair equal_range(KeyType const &key) const { - return equal_range_impl(*this, key); - } - template - constexpr std::pair equal_range(KeyType 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 const hasher& hash_function() const { return tables_.hash_function(); } - constexpr const key_equal& key_eq() const { return static_cast(*this); } - -private: - template - static inline constexpr auto& at_impl(This&& self, KeyType const &key) { - auto it = self.find(key); - if (it != self.end()) - return it->second; - else - FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); - } - - template - static inline constexpr auto find_impl(This&& self, KeyType const &key, Hasher const &hash, Equal const &equal) { - auto const pos = self.tables_.lookup(key, hash); - auto it = self.items_.begin() + pos; - if (it != self.items_.end() && equal(it->first, key)) - return it; - else - return self.items_.end(); - } - - template - static inline constexpr auto equal_range_impl(This&& self, KeyType const &key) { - auto const it = self.find(key); - if (it != self.end()) - return std::make_pair(it, it + 1); - else - return std::make_pair(self.end(), self.end()); - } -}; - -template -constexpr auto make_unordered_map(std::pair const (&items)[N]) { - return unordered_map{items}; -} - -template -constexpr auto make_unordered_map( - std::pair const (&items)[N], - Hasher const &hash = elsa{}, - Equal const &equal = std::equal_to{}) { - return unordered_map{items, hash, equal}; -} - -template -constexpr auto make_unordered_map(std::array, N> const &items) { - return unordered_map{items}; -} - -template -constexpr auto make_unordered_map( - std::array, N> const &items, - Hasher const &hash = elsa{}, - Equal const &equal = std::equal_to{}) { - return unordered_map{items, hash, equal}; -} - -} // namespace frozen +namespace frozen +{ + + namespace bits + { + + struct GetKey + { + template + constexpr auto const& operator()(KV const& kv) const + { + return kv.first; + } + }; + + } // namespace bits + + template , + class KeyEqual = std::equal_to<>> + class unordered_map : private KeyEqual + { + 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, N>; + using tables_type = bits::pmh_tables; + + container_type items_; + tables_type tables_; + + public: + /* typedefs */ + using Self = unordered_map; + 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) : + KeyEqual { equal }, items_ { items }, + tables_ { bits::make_pmh_tables(items_, hash, equal, bits::GetKey {}, + default_prg_t {}) } + { + } + explicit constexpr unordered_map(container_type items) : + unordered_map { items, Hash {}, KeyEqual {} } + { + } + + constexpr unordered_map(std::initializer_list items, Hash const& hash, + KeyEqual const& equal) : + unordered_map { container_type { items }, hash, equal } + { + } + + constexpr unordered_map(std::initializer_list items) : + unordered_map { items, Hash {}, KeyEqual {} } + { + } + + /* iterators */ + [[nodiscard]] constexpr iterator begin() { return items_.begin(); } + [[nodiscard]] constexpr iterator end() { return items_.end(); } + [[nodiscard]] constexpr const_iterator begin() const { return items_.begin(); } + [[nodiscard]] constexpr const_iterator end() const { return items_.end(); } + [[nodiscard]] constexpr const_iterator cbegin() const { return items_.begin(); } + [[nodiscard]] constexpr const_iterator cend() const { return items_.end(); } + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return !N; } + [[nodiscard]] constexpr size_type size() const { return N; } + [[nodiscard]] constexpr size_type max_size() const { return N; } + + /* lookup */ + template + [[nodiscard]] constexpr std::size_t count(KeyType const& key) const + { + return find(key) != end(); + } + + template + [[nodiscard]] constexpr Value const& at(KeyType const& key) const + { + return at_impl(*this, key); + } + template + [[nodiscard]] constexpr Value& at(KeyType const& key) + { + return at_impl(*this, key); + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const& key) const + { + return find_impl(*this, key, hash_function(), key_eq()); + } + template + [[nodiscard]] constexpr iterator find(KeyType const& key) + { + return find_impl(*this, key, hash_function(), key_eq()); + } + + template + [[nodiscard]] constexpr bool contains(KeyType const& key) const + { + return this->find(key) != this->end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const& key) const + { + return equal_range_impl(*this, key); + } + template + [[nodiscard]] constexpr std::pair equal_range(KeyType const& key) + { + return equal_range_impl(*this, key); + } + + /* bucket interface */ + [[nodiscard]] constexpr std::size_t bucket_count() const { return storage_size; } + [[nodiscard]] constexpr std::size_t max_bucket_count() const { return storage_size; } + + /* observers*/ + [[nodiscard]] constexpr const hasher& hash_function() const + { + return tables_.hash_function(); + } + [[nodiscard]] constexpr const key_equal& key_eq() const + { + return static_cast(*this); + } + + private: + template + static inline constexpr auto& at_impl(This&& self, KeyType const& key) + { + auto it = self.find(key); + if (it != self.end()) + return it->second; + else + FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key")); + } + + template + static inline constexpr auto find_impl(This&& self, KeyType const& key, Hasher const& hash, + Equal const& equal) + { + auto const pos = self.tables_.lookup(key, hash); + auto it = self.items_.begin() + pos; + if (it != self.items_.end() && equal(it->first, key)) + return it; + else + return self.items_.end(); + } + + template + static inline constexpr auto equal_range_impl(This&& self, KeyType const& key) + { + auto const it = self.find(key); + if (it != self.end()) + return std::make_pair(it, it + 1); + else + return std::make_pair(self.end(), self.end()); + } + }; + + template + constexpr auto make_unordered_map(std::pair const (&items)[N]) + { + return unordered_map { items }; + } + + template + constexpr auto make_unordered_map(std::pair const (&items)[N], + Hasher const& hash = elsa {}, + Equal const& equal = std::equal_to {}) + { + return unordered_map { items, hash, equal }; + } + + template + constexpr auto make_unordered_map(std::array, N> const& items) + { + return unordered_map { items }; + } + + template + constexpr auto make_unordered_map(std::array, N> const& items, + Hasher const& hash = elsa {}, + Equal const& equal = std::equal_to {}) + { + return unordered_map { items, hash, equal }; + } + +} // namespace frozen #endif diff --git a/include/frozen/unordered_set.h b/include/frozen/unordered_set.h index 482034b..5d7236f 100644 --- a/include/frozen/unordered_set.h +++ b/include/frozen/unordered_set.h @@ -23,6 +23,16 @@ #ifndef FROZEN_LETITGO_UNORDERED_SET_H #define FROZEN_LETITGO_UNORDERED_SET_H +// Require C++23 or later +#if defined(_MSVC_LANG) + #if _MSVC_LANG < 202302L + #error \ + "frozen/unordered_set.h requires C++23 or later. Compile with /std:c++latest or /std:c++23." + #endif +#elif __cplusplus < 202302L + #error "frozen/unordered_set.h requires C++23 or later. Compile with -std=c++23 or later." +#endif + #include "frozen/bits/basic_types.h" #include "frozen/bits/elsa.h" #include "frozen/bits/pmh.h" @@ -31,149 +41,175 @@ #include -namespace frozen { - -namespace bits { - -struct Get { - template constexpr T const &operator()(T const &key) const { - return key; - } -}; - -} // namespace bits - -template , - class KeyEqual = std::equal_to> -class unordered_set : private KeyEqual { - 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; - using tables_type = bits::pmh_tables; - - 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 = typename container_type::const_iterator; - using iterator = const_iterator; - -public: - /* constructors */ - unordered_set(unordered_set const &) = default; - constexpr unordered_set(container_type keys, Hash const &hash, - KeyEqual const &equal) - : KeyEqual{equal} - , keys_{keys} - , tables_{bits::make_pmh_tables( - keys_, hash, equal, bits::Get{}, default_prg_t{})} {} - explicit constexpr unordered_set(container_type keys) - : unordered_set{keys, Hash{}, KeyEqual{}} {} - - constexpr unordered_set(std::initializer_list keys) - : unordered_set{keys, Hash{}, KeyEqual{}} {} - - constexpr unordered_set(std::initializer_list keys, Hash const & hash, KeyEqual const & equal) - : unordered_set{container_type{keys}, hash, equal} { - } - - /* iterators */ - constexpr const_iterator begin() const { return keys_.begin(); } - constexpr const_iterator end() const { return keys_.end(); } - constexpr const_iterator cbegin() const { return keys_.begin(); } - constexpr const_iterator cend() const { return keys_.end(); } - - /* capacity */ - constexpr bool empty() const { return !N; } - constexpr size_type size() const { return N; } - constexpr size_type max_size() const { return N; } - - /* lookup */ - template - constexpr std::size_t count(KeyType const &key) const { - return find(key, hash_function(), key_eq()) != end(); - } - - template - constexpr const_iterator find(KeyType const &key, Hasher const &hash, Equal const &equal) const { - auto const pos = tables_.lookup(key, hash); - auto it = keys_.begin() + pos; - if (it != keys_.end() && equal(*it, key)) - return it; - else - return keys_.end(); - } - template - constexpr const_iterator find(KeyType const &key) const { - auto const pos = tables_.lookup(key, hash_function()); - auto it = keys_.begin() + pos; - if (it != keys_.end() && key_eq()(*it, key)) - return it; - else - return keys_.end(); - } - - template - constexpr bool contains(KeyType const &key) const { - return this->find(key) != keys_.end(); - } - - template - constexpr std::pair equal_range(KeyType const &key) const { - auto const it = find(key); - if (it != end()) - return {it, it + 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 const hasher& hash_function() const { return tables_.hash_function(); } - constexpr const key_equal& key_eq() const { return static_cast(*this); } -}; - -template -constexpr auto make_unordered_set(T const (&keys)[N]) { - return unordered_set{keys}; -} - -template -constexpr auto make_unordered_set(T const (&keys)[N], Hasher const& hash, Equal const& equal) { - return unordered_set{keys, hash, equal}; -} - -template -constexpr auto make_unordered_set(std::array const &keys) { - return unordered_set{keys}; -} - -template -constexpr auto make_unordered_set(std::array const &keys, Hasher const& hash, Equal const& equal) { - return unordered_set{keys, hash, equal}; -} - -#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES - -template -unordered_set(T, Args...) -> unordered_set; - -#endif - -} // namespace frozen +namespace frozen +{ + + namespace bits + { + + struct Get + { + template + constexpr T const& operator()(T const& key) const + { + return key; + } + }; + + } // namespace bits + + template , class KeyEqual = std::equal_to<>> + class unordered_set : private KeyEqual + { + 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; + using tables_type = bits::pmh_tables; + + 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 = typename container_type::const_iterator; + using iterator = const_iterator; + + public: + /* constructors */ + unordered_set(unordered_set const&) = default; + constexpr unordered_set(container_type keys, Hash const& hash, KeyEqual const& equal) : + KeyEqual { equal }, keys_ { keys }, + tables_ { bits::make_pmh_tables(keys_, hash, equal, bits::Get {}, + default_prg_t {}) } + { + } + explicit constexpr unordered_set(container_type keys) : + unordered_set { keys, Hash {}, KeyEqual {} } + { + } + + constexpr unordered_set(std::initializer_list keys) : + unordered_set { keys, Hash {}, KeyEqual {} } + { + } + + constexpr unordered_set(std::initializer_list keys, Hash const& hash, + KeyEqual const& equal) : + unordered_set { container_type { keys }, hash, equal } + { + } + + /* iterators */ + [[nodiscard]] constexpr const_iterator begin() const { return keys_.begin(); } + [[nodiscard]] constexpr const_iterator end() const { return keys_.end(); } + [[nodiscard]] constexpr const_iterator cbegin() const { return keys_.begin(); } + [[nodiscard]] constexpr const_iterator cend() const { return keys_.end(); } + + /* capacity */ + [[nodiscard]] constexpr bool empty() const { return !N; } + [[nodiscard]] constexpr size_type size() const { return N; } + [[nodiscard]] constexpr size_type max_size() const { return N; } + + /* lookup */ + template + [[nodiscard]] constexpr std::size_t count(KeyType const& key) const + { + return find(key, hash_function(), key_eq()) != end(); + } + + template + [[nodiscard]] constexpr const_iterator find(KeyType const& key, Hasher const& hash, + Equal const& equal) const + { + auto const pos = tables_.lookup(key, hash); + auto it = keys_.begin() + pos; + if (it != keys_.end() && equal(*it, key)) + return it; + else + return keys_.end(); + } + template + [[nodiscard]] constexpr const_iterator find(KeyType const& key) const + { + auto const pos = tables_.lookup(key, hash_function()); + auto it = keys_.begin() + pos; + if (it != keys_.end() && key_eq()(*it, key)) + return it; + else + return keys_.end(); + } + + template + [[nodiscard]] constexpr bool contains(KeyType const& key) const + { + return this->find(key) != keys_.end(); + } + + template + [[nodiscard]] constexpr std::pair + equal_range(KeyType const& key) const + { + auto const it = find(key); + if (it != end()) + return { it, it + 1 }; + else + return { keys_.end(), keys_.end() }; + } + + /* bucket interface */ + [[nodiscard]] constexpr std::size_t bucket_count() const { return storage_size; } + [[nodiscard]] constexpr std::size_t max_bucket_count() const { return storage_size; } + + /* observers*/ + [[nodiscard]] constexpr const hasher& hash_function() const + { + return tables_.hash_function(); + } + [[nodiscard]] constexpr const key_equal& key_eq() const + { + return static_cast(*this); + } + }; + + template + constexpr auto make_unordered_set(T const (&keys)[N]) + { + return unordered_set { keys }; + } + + template + constexpr auto make_unordered_set(T const (&keys)[N], Hasher const& hash, Equal const& equal) + { + return unordered_set { keys, hash, equal }; + } + + template + constexpr auto make_unordered_set(std::array const& keys) + { + return unordered_set { keys }; + } + + template + constexpr auto make_unordered_set(std::array const& keys, Hasher const& hash, + Equal const& equal) + { + return unordered_set { keys, hash, equal }; + } + + template + unordered_set(T, Args...) -> unordered_set; + +} // namespace frozen #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2e79297..60a439a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,7 +20,8 @@ target_sources(frozen.tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_unordered_map.cpp ${CMAKE_CURRENT_LIST_DIR}/test_unordered_map_str.cpp ${CMAKE_CURRENT_LIST_DIR}/test_unordered_set.cpp - ${CMAKE_CURRENT_LIST_DIR}/test_unordered_str_set.cpp) + ${CMAKE_CURRENT_LIST_DIR}/test_unordered_str_set.cpp + ${CMAKE_CURRENT_LIST_DIR}/test_cpp23_features.cpp) string(CONCAT generator # msvc gives invalid integral overflow warning for unsigned type @@ -36,11 +37,7 @@ string(CONCAT generator target_compile_options(frozen.tests PUBLIC ${generator}) -if(cxx_20_supported) - target_compile_features(frozen.tests PUBLIC cxx_std_20) -elseif(cxx_17_supported) - target_compile_features(frozen.tests PUBLIC cxx_std_17) -endif() +target_compile_features(frozen.tests PUBLIC cxx_std_23) string(CONCAT generator "$<$" @@ -55,7 +52,9 @@ add_test(NAME frozen.tests COMMAND frozen.tests) add_executable(test_no_expections ${CMAKE_CURRENT_LIST_DIR}/no_exceptions.cpp) target_link_libraries(test_no_expections PUBLIC frozen::frozen) -target_compile_options(test_no_expections PUBLIC "-fno-exceptions") +target_compile_options(test_no_expections PUBLIC + $<$:/EHs-c-> + $<$>:-fno-exceptions>) add_test(no_exceptions test_no_expections) set_tests_properties(no_exceptions PROPERTIES WILL_FAIL TRUE) diff --git a/tests/no_exceptions.cpp b/tests/no_exceptions.cpp index ee270d6..fd5e0b0 100644 --- a/tests/no_exceptions.cpp +++ b/tests/no_exceptions.cpp @@ -5,18 +5,21 @@ // https://discourse.cmake.org/t/tests-that-are-meant-to-abort/537/3 // This is a hack to implement death tests in CTest. -extern "C" void error_test_handle_abort(int) { std::_Exit(EXIT_FAILURE); } +extern "C" void error_test_handle_abort(int) +{ + std::_Exit(EXIT_FAILURE); +} -struct test_override_abort { - test_override_abort() noexcept { - std::signal(SIGABRT, error_test_handle_abort); - } +struct test_override_abort +{ + test_override_abort() noexcept { std::signal(SIGABRT, error_test_handle_abort); } }; -test_override_abort handler{}; +test_override_abort handler {}; -int main() { - constexpr frozen::unordered_map ce = {{1, 2}, {3, 4}}; - ce.at(0xDEAD); - return 0; +int main() +{ + constexpr frozen::unordered_map ce = { { 1, 2 }, { 3, 4 } }; + (void) ce.at(0xDEAD); + return 0; } diff --git a/tests/test_cpp23_features.cpp b/tests/test_cpp23_features.cpp new file mode 100644 index 0000000..893797f --- /dev/null +++ b/tests/test_cpp23_features.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include + +#include "catch.hpp" + +#include +#include + +using namespace std::string_view_literals; + +// --------------------------------------------------------------------------- +// string_view as container key +// --------------------------------------------------------------------------- + +TEST_CASE("string_view keys in frozen::set", "[cpp23]") { + constexpr frozen::set s = {"alpha", "beta", "gamma"}; + + static_assert(s.count("alpha") == 1); + static_assert(s.count("delta") == 0); + REQUIRE(s.count("beta") == 1); + REQUIRE(s.size() == 3); +} + +TEST_CASE("string_view keys in frozen::map", "[cpp23]") { + constexpr frozen::map m = { + {"one", 1}, {"two", 2}, {"three", 3}}; + + static_assert(m.at("one") == 1); + static_assert(m.at("three") == 3); + REQUIRE(m.at("two") == 2); +} + +TEST_CASE("string_view keys in frozen::unordered_set", "[cpp23]") { + constexpr frozen::unordered_set s = { + "alpha", "beta", "gamma"}; + + static_assert(s.count("alpha") == 1); + static_assert(s.count("missing") == 0); + REQUIRE(s.count("gamma") == 1); +} + +TEST_CASE("string_view keys in frozen::unordered_map", "[cpp23]") { + constexpr frozen::unordered_map m = { + {"one", 1}, {"two", 2}, {"three", 3}}; + + static_assert(m.at("one") == 1); + REQUIRE(m.at("two") == 2); +} + +// --------------------------------------------------------------------------- +// Heterogeneous lookup: const char* into string_view-keyed containers +// --------------------------------------------------------------------------- + +TEST_CASE("heterogeneous lookup: const char* into string_view set", "[cpp23]") { + constexpr frozen::set s = {"alpha", "beta", "gamma"}; + + // const char* lookup into string_view-keyed ordered set + const char *key = "beta"; + REQUIRE(s.count(key) == 1); + REQUIRE(s.find(key) != s.end()); +} + +TEST_CASE("heterogeneous lookup: const char* into string_view map", "[cpp23]") { + constexpr frozen::map m = { + {"one", 1}, {"two", 2}, {"three", 3}}; + + const char *key = "two"; + REQUIRE(m.at(key) == 2); + REQUIRE(m.find(key) != m.end()); +} + +TEST_CASE("heterogeneous lookup: const char* into string_view unordered_set", "[cpp23]") { + constexpr frozen::unordered_set s = { + "alpha", "beta", "gamma"}; + + const char *key = "gamma"; + REQUIRE(s.count(key) == 1); + REQUIRE(s.find(key) != s.end()); +} + +TEST_CASE("heterogeneous lookup: const char* into string_view unordered_map", "[cpp23]") { + constexpr frozen::unordered_map m = { + {"one", 1}, {"two", 2}, {"three", 3}}; + + const char *key = "three"; + REQUIRE(m.at(key) == 3); + REQUIRE(m.find(key) != m.end()); +} + +// --------------------------------------------------------------------------- +// std::string lookup into string_view-keyed containers +// --------------------------------------------------------------------------- + +TEST_CASE("heterogeneous lookup: std::string into string_view containers", "[cpp23]") { + constexpr frozen::map m = { + {"one", 1}, {"two", 2}, {"three", 3}}; + constexpr frozen::unordered_map um = { + {"one", 1}, {"two", 2}, {"three", 3}}; + + std::string key = "two"; + REQUIRE(m.at(key) == 2); + REQUIRE(um.at(key) == 2); +} + +// --------------------------------------------------------------------------- +// Transparent comparator default: lookup with different but comparable types +// --------------------------------------------------------------------------- + +TEST_CASE("transparent comparator: int lookup in long set", "[cpp23]") { + constexpr frozen::set s = {10L, 20L, 30L}; + + // int is implicitly comparable to long via std::less<> + constexpr int key = 20; + static_assert(s.count(key) == 1); + REQUIRE(s.count(key) == 1); +} + +TEST_CASE("transparent comparator: int key in long map", "[cpp23]") { + constexpr frozen::map m = {{10L, 1}, {20L, 2}, {30L, 3}}; + + constexpr int key = 20; + static_assert(m.at(key) == 2); + REQUIRE(m.at(key) == 2); +} + +// --------------------------------------------------------------------------- +// constexpr construction with string_view keys +// --------------------------------------------------------------------------- + +TEST_CASE("constexpr construction with string_view", "[cpp23]") { + // Ordered containers + constexpr auto set = frozen::make_set({"a", "b", "c"}); + static_assert(set.size() == 3); + static_assert(set.count("b") == 1); + + constexpr auto map = + frozen::make_map({{"x", 1}, {"y", 2}}); + static_assert(map.size() == 2); + static_assert(map.at("x") == 1); + + // Unordered containers + constexpr auto uset = + frozen::make_unordered_set({"a", "b", "c"}); + static_assert(uset.size() == 3); + static_assert(uset.count("b") == 1); + + constexpr auto umap = + frozen::make_unordered_map({{"x", 1}, {"y", 2}}); + static_assert(umap.size() == 2); + static_assert(umap.at("x") == 1); +} + +// --------------------------------------------------------------------------- +// [[nodiscard]] — just exercise the lookup patterns; the attribute is tested +// by the compiler warning if values are discarded (covered by -Werror). +// --------------------------------------------------------------------------- + +TEST_CASE("nodiscard lookups compile cleanly", "[cpp23]") { + constexpr frozen::set s = {1, 2, 3}; + constexpr frozen::map m = {{1, 10}, {2, 20}}; + + // These must compile without unused-result warnings under -Werror + [[maybe_unused]] auto c = s.count(1); + [[maybe_unused]] auto it = s.find(2); + [[maybe_unused]] auto sz = s.size(); + [[maybe_unused]] auto e = s.empty(); + [[maybe_unused]] auto v = m.at(1); +} + +// --------------------------------------------------------------------------- +// constinit containers with string_view keys +// --------------------------------------------------------------------------- + +// Ordered map — mutable values at runtime +constinit frozen::map ci_map = { + {"width", 1920}, {"height", 1080}, {"fps", 60}}; + +// Unordered map — mutable values at runtime +constinit frozen::unordered_map ci_umap = { + {"width", 1920}, {"height", 1080}, {"fps", 60}}; + +TEST_CASE("constinit ordered map with string_view keys", "[cpp23]") { + REQUIRE(ci_map.at("width") == 1920); + ci_map.at("width") = 2560; + REQUIRE(ci_map.at("width") == 2560); + ci_map.at("width") = 1920; // restore for re-runs +} + +TEST_CASE("constinit unordered_map with string_view keys", "[cpp23]") { + REQUIRE(ci_umap.at("fps") == 60); + ci_umap.at("fps") = 144; + REQUIRE(ci_umap.at("fps") == 144); + ci_umap.at("fps") = 60; // restore for re-runs +} diff --git a/tests/test_elsa_std.cpp b/tests/test_elsa_std.cpp index 5838726..ab8a0c8 100644 --- a/tests/test_elsa_std.cpp +++ b/tests/test_elsa_std.cpp @@ -1,7 +1,3 @@ -#include - -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW - #include #include #include @@ -10,35 +6,50 @@ #include "catch.hpp" -#include - -#ifdef FROZEN_LETITGO_HAS_CONSTEXPR_STRING - #include +#include using StringTypes = std::tuple; -#else - -using StringTypes = std::tuple; - -#endif - -TEMPLATE_LIST_TEST_CASE("frozen containers work with standard string types", "[elsa_std]", StringTypes) +TEMPLATE_LIST_TEST_CASE("frozen containers work with standard string types", "[elsa_std]", + StringTypes) { - constexpr TestType str = "string"; - - const auto is_found_in = [&str](const auto& container){ return container.count(str) == 1; }; - - constexpr frozen::set set{str}; - constexpr frozen::unordered_set unordered_set{str}; - constexpr auto map = frozen::make_map({{str, 0}}); - constexpr auto unordered_map = frozen::make_unordered_map({{str, 0}}); - - REQUIRE(is_found_in(set)); - REQUIRE(is_found_in(unordered_set)); - REQUIRE(is_found_in(map)); - REQUIRE(is_found_in(unordered_map)); + const TestType str = "string"; + + const auto is_found_in = [&str](const auto& container) + { + return container.count(str) == 1; + }; + + if constexpr (std::is_same_v) + { + constexpr frozen::set set { std::string_view { "string" } }; + constexpr frozen::unordered_set unordered_set { std::string_view { + "string" } }; + constexpr auto map = + frozen::make_map({ { std::string_view { "string" }, 0 } }); + constexpr auto unordered_map = + frozen::make_unordered_map({ { std::string_view { "string" }, 0 } }); + + REQUIRE(is_found_in(set)); + REQUIRE(is_found_in(unordered_set)); + REQUIRE(is_found_in(map)); + REQUIRE(is_found_in(unordered_map)); + } + else + { + // std::string can't be used in constexpr containers (heap allocation) + const frozen::set set { std::string_view { "string" } }; + const frozen::unordered_set unordered_set { std::string_view { + "string" } }; + const auto map = + frozen::make_map({ { std::string_view { "string" }, 0 } }); + const auto unordered_map = frozen::make_unordered_map( + { { std::string_view { "string" }, 0 } }); + + REQUIRE(is_found_in(set)); + REQUIRE(is_found_in(unordered_set)); + REQUIRE(is_found_in(map)); + REQUIRE(is_found_in(unordered_map)); + } } - -#endif // FROZEN_LETITGO_HAS_STRING_VIEW diff --git a/tests/test_map.cpp b/tests/test_map.cpp index 98be136..6c09f03 100644 --- a/tests/test_map.cpp +++ b/tests/test_map.cpp @@ -7,504 +7,535 @@ #include "bench.hpp" #include "catch.hpp" -TEST_CASE("empty frozen map", "[map]") { - constexpr frozen::map ze_map{}; +TEST_CASE("empty frozen map", "[map]") +{ + constexpr frozen::map ze_map {}; - constexpr auto empty = ze_map.empty(); - REQUIRE(empty); + constexpr auto empty = ze_map.empty(); + REQUIRE(empty); - constexpr auto size = ze_map.size(); - REQUIRE(size == 0); + constexpr auto size = ze_map.size(); + REQUIRE(size == 0); - constexpr auto max_size = ze_map.max_size(); - REQUIRE(max_size == 0); + constexpr auto max_size = ze_map.max_size(); + REQUIRE(max_size == 0); - constexpr auto count = ze_map.count(3); - REQUIRE(count == 0); + constexpr auto count = ze_map.count(3); + REQUIRE(count == 0); - constexpr auto find = ze_map.find(5); - REQUIRE(find == ze_map.end()); + constexpr auto find = ze_map.find(5); + REQUIRE(find == ze_map.end()); - constexpr auto range = ze_map.equal_range(0); - REQUIRE(std::get<0>(range) == ze_map.end()); - REQUIRE(std::get<1>(range) == ze_map.end()); + constexpr auto range = ze_map.equal_range(0); + REQUIRE(std::get<0>(range) == ze_map.end()); + REQUIRE(std::get<1>(range) == ze_map.end()); - constexpr auto lower_bound = ze_map.lower_bound(1); - REQUIRE(lower_bound == ze_map.end()); + constexpr auto lower_bound = ze_map.lower_bound(1); + REQUIRE(lower_bound == ze_map.end()); - constexpr auto upper_bound = ze_map.upper_bound(1); - REQUIRE(upper_bound == ze_map.end()); + constexpr auto upper_bound = ze_map.upper_bound(1); + REQUIRE(upper_bound == ze_map.end()); - auto constexpr begin = ze_map.begin(), end = ze_map.end(); - REQUIRE(begin == end); + auto constexpr begin = ze_map.begin(), end = ze_map.end(); + REQUIRE(begin == end); - auto constexpr key_comp = ze_map.key_comp(); - auto constexpr key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto constexpr key_comp = ze_map.key_comp(); + auto constexpr key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto constexpr value_comp = ze_map.value_comp(); - auto constexpr value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto constexpr value_comp = ze_map.value_comp(); + auto constexpr value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto constexpr cbegin = ze_map.cbegin(), cend = ze_map.cend(); - REQUIRE(cbegin == cend); + auto constexpr cbegin = ze_map.cbegin(), cend = ze_map.cend(); + REQUIRE(cbegin == cend); - std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); - REQUIRE(std::distance(ze_map.rbegin(), ze_map.rend()) == 0); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type(3, 5.3f)) == 0); + std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); + REQUIRE(std::distance(ze_map.rbegin(), ze_map.rend()) == 0); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), decltype(ze_map)::value_type(3, 5.3f)) == + 0); } -TEST_CASE("singleton frozen map", "[map]") { - constexpr frozen::map ze_map = {{1, 3.14}}; +TEST_CASE("singleton frozen map", "[map]") +{ + constexpr frozen::map ze_map = { { 1, 3.14 } }; - constexpr auto empty = ze_map.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_map.empty(); + REQUIRE(!empty); - constexpr auto size = ze_map.size(); - REQUIRE(size == 1); + constexpr auto size = ze_map.size(); + REQUIRE(size == 1); - constexpr auto max_size = ze_map.max_size(); - REQUIRE(max_size == 1); + constexpr auto max_size = ze_map.max_size(); + REQUIRE(max_size == 1); - constexpr auto count1 = ze_map.count(1); - REQUIRE(count1 == 1); + constexpr auto count1 = ze_map.count(1); + REQUIRE(count1 == 1); - constexpr auto count2 = ze_map.count(2); - REQUIRE(count2 == 0); + constexpr auto count2 = ze_map.count(2); + REQUIRE(count2 == 0); - const auto find1 = ze_map.find(1); - REQUIRE(find1 == ze_map.begin()); - REQUIRE(std::get<0>(*find1) == 1); - REQUIRE(std::get<1>(*find1) == 3.14); + const auto find1 = ze_map.find(1); + REQUIRE(find1 == ze_map.begin()); + REQUIRE(std::get<0>(*find1) == 1); + REQUIRE(std::get<1>(*find1) == 3.14); - const auto find5 = ze_map.find(5); - REQUIRE(find5 == ze_map.end()); + const auto find5 = ze_map.find(5); + REQUIRE(find5 == ze_map.end()); - const auto range0 = ze_map.equal_range(0); - REQUIRE(std::get<0>(range0) == ze_map.begin()); - REQUIRE(std::get<1>(range0) == ze_map.begin()); + const auto range0 = ze_map.equal_range(0); + REQUIRE(std::get<0>(range0) == ze_map.begin()); + REQUIRE(std::get<1>(range0) == ze_map.begin()); - const auto range1 = ze_map.equal_range(1); - REQUIRE(std::get<0>(range1) == ze_map.begin()); - REQUIRE(std::get<1>(range1) == ze_map.end()); + const auto range1 = ze_map.equal_range(1); + REQUIRE(std::get<0>(range1) == ze_map.begin()); + REQUIRE(std::get<1>(range1) == ze_map.end()); - const auto range2 = ze_map.equal_range(2); - REQUIRE(std::get<0>(range2) == ze_map.end()); - REQUIRE(std::get<1>(range2) == ze_map.end()); + const auto range2 = ze_map.equal_range(2); + REQUIRE(std::get<0>(range2) == ze_map.end()); + REQUIRE(std::get<1>(range2) == ze_map.end()); - const auto lower_bound0 = ze_map.lower_bound(0); - REQUIRE(lower_bound0 == ze_map.begin()); + const auto lower_bound0 = ze_map.lower_bound(0); + REQUIRE(lower_bound0 == ze_map.begin()); - const auto lower_bound1 = ze_map.lower_bound(1); - REQUIRE(lower_bound0 == ze_map.begin()); - REQUIRE(lower_bound1 == ze_map.find(1)); + const auto lower_bound1 = ze_map.lower_bound(1); + REQUIRE(lower_bound0 == ze_map.begin()); + REQUIRE(lower_bound1 == ze_map.find(1)); - const auto lower_bound2 = ze_map.lower_bound(2); - REQUIRE(lower_bound2 == ze_map.end()); + const auto lower_bound2 = ze_map.lower_bound(2); + REQUIRE(lower_bound2 == ze_map.end()); - const auto upper_bound0 = ze_map.upper_bound(0); - REQUIRE(upper_bound0 == ze_map.begin()); + const auto upper_bound0 = ze_map.upper_bound(0); + REQUIRE(upper_bound0 == ze_map.begin()); - const auto upper_bound1 = ze_map.upper_bound(1); - REQUIRE(upper_bound1 == ze_map.end()); + const auto upper_bound1 = ze_map.upper_bound(1); + REQUIRE(upper_bound1 == ze_map.end()); - const auto upper_bound2 = ze_map.upper_bound(2); - REQUIRE(upper_bound2 == ze_map.end()); + const auto upper_bound2 = ze_map.upper_bound(2); + REQUIRE(upper_bound2 == ze_map.end()); - auto const begin = ze_map.begin(), end = ze_map.end(); - REQUIRE((begin + 1) == end); + auto const begin = ze_map.begin(), end = ze_map.end(); + REQUIRE((begin + 1) == end); - auto const key_comp = ze_map.key_comp(); - auto const key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto const key_comp = ze_map.key_comp(); + auto const key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto const value_comp = ze_map.value_comp(); - auto const value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto const value_comp = ze_map.value_comp(); + auto const value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); - REQUIRE(cbegin == (cend - 1)); + auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); + REQUIRE(cbegin == (cend - 1)); - std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); - REQUIRE(std::distance(ze_map.rbegin(), ze_map.rend()) == 1); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type(3, 14)) == 0); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type(1, 3.14)) == 1); + std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); + REQUIRE(std::distance(ze_map.rbegin(), ze_map.rend()) == 1); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), decltype(ze_map)::value_type(3, 14)) == 0); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), decltype(ze_map)::value_type(1, 3.14)) == + 1); } -TEST_CASE("triple frozen map", "[map]") { - constexpr frozen::map ze_map{ - {10, true}, {20, false}, {30, true}}; +TEST_CASE("triple frozen map", "[map]") +{ + constexpr frozen::map ze_map { { 10, true }, { 20, false }, { 30, true } }; - constexpr auto empty = ze_map.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_map.empty(); + REQUIRE(!empty); - constexpr auto size = ze_map.size(); - REQUIRE(size == 3); + constexpr auto size = ze_map.size(); + REQUIRE(size == 3); - constexpr auto max_size = ze_map.max_size(); - REQUIRE(max_size == 3); + constexpr auto max_size = ze_map.max_size(); + REQUIRE(max_size == 3); - constexpr auto count1 = ze_map.count(1); - REQUIRE(count1 == 0); + constexpr auto count1 = ze_map.count(1); + REQUIRE(count1 == 0); - constexpr auto count10 = ze_map.count(10); - REQUIRE(count10 == 1); + constexpr auto count10 = ze_map.count(10); + REQUIRE(count10 == 1); - const auto find10 = ze_map.find(10); - REQUIRE(find10 == ze_map.begin()); - REQUIRE(std::get<0>(*find10) == 10); - REQUIRE(std::get<1>(*find10) == true); + const auto find10 = ze_map.find(10); + REQUIRE(find10 == ze_map.begin()); + REQUIRE(std::get<0>(*find10) == 10); + REQUIRE(std::get<1>(*find10) == true); - const auto find15 = ze_map.find(15); - REQUIRE(find15 == ze_map.end()); + const auto find15 = ze_map.find(15); + REQUIRE(find15 == ze_map.end()); - const auto range0 = ze_map.equal_range(0); - REQUIRE(std::get<0>(range0) == ze_map.begin()); - REQUIRE(std::get<1>(range0) == ze_map.begin()); + const auto range0 = ze_map.equal_range(0); + REQUIRE(std::get<0>(range0) == ze_map.begin()); + REQUIRE(std::get<1>(range0) == ze_map.begin()); - const auto range1 = ze_map.equal_range(10); - REQUIRE(std::get<0>(range1) == ze_map.begin()); - REQUIRE(std::get<1>(range1) == ze_map.begin() + 1); + const auto range1 = ze_map.equal_range(10); + REQUIRE(std::get<0>(range1) == ze_map.begin()); + REQUIRE(std::get<1>(range1) == ze_map.begin() + 1); - const auto lower_bound0 = ze_map.lower_bound(0); - REQUIRE(lower_bound0 == ze_map.begin()); + const auto lower_bound0 = ze_map.lower_bound(0); + REQUIRE(lower_bound0 == ze_map.begin()); - const auto lower_bound1 = ze_map.lower_bound(20); - REQUIRE(lower_bound1 == ze_map.begin() + 1); + const auto lower_bound1 = ze_map.lower_bound(20); + REQUIRE(lower_bound1 == ze_map.begin() + 1); - const auto lower_bound2 = ze_map.lower_bound(25); - REQUIRE(lower_bound2 == ze_map.begin() + 2); + const auto lower_bound2 = ze_map.lower_bound(25); + REQUIRE(lower_bound2 == ze_map.begin() + 2); - const auto lower_bound3 = ze_map.lower_bound(40); - REQUIRE(lower_bound3 == ze_map.end()); + const auto lower_bound3 = ze_map.lower_bound(40); + REQUIRE(lower_bound3 == ze_map.end()); - for (auto val : ze_map) { - const auto lower_bound = ze_map.lower_bound(std::get<0>(val)); - REQUIRE(lower_bound == ze_map.find(std::get<0>(val))); - } + for (auto val: ze_map) + { + const auto lower_bound = ze_map.lower_bound(std::get<0>(val)); + REQUIRE(lower_bound == ze_map.find(std::get<0>(val))); + } - const auto upper_bound0 = ze_map.upper_bound(0); - REQUIRE(upper_bound0 == ze_map.begin()); + const auto upper_bound0 = ze_map.upper_bound(0); + REQUIRE(upper_bound0 == ze_map.begin()); - const auto upper_bound1 = ze_map.upper_bound(10); - REQUIRE(upper_bound1 == (ze_map.begin() + 1)); + const auto upper_bound1 = ze_map.upper_bound(10); + REQUIRE(upper_bound1 == (ze_map.begin() + 1)); - const auto upper_bound2 = ze_map.upper_bound(15); - REQUIRE(upper_bound2 == ze_map.begin() + 1); + const auto upper_bound2 = ze_map.upper_bound(15); + REQUIRE(upper_bound2 == ze_map.begin() + 1); - const auto upper_bound3 = ze_map.upper_bound(40); - REQUIRE(upper_bound3 == ze_map.end()); + const auto upper_bound3 = ze_map.upper_bound(40); + REQUIRE(upper_bound3 == ze_map.end()); - auto const begin = ze_map.begin(), end = ze_map.end(); - REQUIRE((begin + ze_map.size()) == end); + auto const begin = ze_map.begin(), end = ze_map.end(); + REQUIRE((begin + ze_map.size()) == end); - auto const key_comp = ze_map.key_comp(); - auto const key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto const key_comp = ze_map.key_comp(); + auto const key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto const value_comp = ze_map.value_comp(); - auto const value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto const value_comp = ze_map.value_comp(); + auto const value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); - REQUIRE(cbegin == (cend - ze_map.size())); + auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); + REQUIRE(cbegin == (cend - ze_map.size())); - std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); - REQUIRE((std::size_t)std::distance(ze_map.rbegin(), ze_map.rend()) == ze_map.size()); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type(3, 14)) == 0); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type(20, false)) == 1); + std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); + REQUIRE((std::size_t) std::distance(ze_map.rbegin(), ze_map.rend()) == ze_map.size()); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), decltype(ze_map)::value_type(3, 14)) == 0); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), decltype(ze_map)::value_type(20, false)) == + 1); } -TEST_CASE("frozen::map <> std::map", "[map]") { -#define INIT_SEQ \ - {19, 12}, {1, 12}, {2, 12}, {4, 12}, {5, 12}, {6, 12}, {7, 12}, {8, 12}, \ - {9, 12}, {10, 12}, {11, 12}, {111, 12}, {1112, 12}, {1115, 12}, \ - {1118, 12}, {1110, 12}, {1977, 12}, {177, 12}, {277, 12}, {477, 12}, \ - {577, 12}, {677, 12}, {777, 12}, {877, 12}, {977, 12}, {1077, 12}, \ - {1177, 12}, {11177, 12}, {111277, 12}, {111577, 12}, {111877, 12}, \ - {111077, 12}, {1999, 12}, {199, 12}, {299, 12}, {499, 12}, {599, 12}, \ - {699, 12}, {799, 12}, {899, 12}, {999, 12}, {1099, 12}, {1199, 12}, \ - {11199, 12}, {111299, 12}, {111599, 12}, {111899, 12}, {111099, 12}, \ - {197799, 12}, {17799, 12}, {27799, 12}, {47799, 12}, {57799, 12}, \ - {67799, 12}, {77799, 12}, {87799, 12}, {97799, 12}, {107799, 12}, \ - {117799, 12}, {1117799, 12}, {11127799, 12}, {11157799, 12}, \ - {11187799, 12}, {11107799, 12}, {1988, 12}, {188, 12}, {288, 12}, \ - {488, 12}, {588, 12}, {688, 12}, {788, 12}, {888, 12}, {988, 12}, \ - {1088, 12}, {1188, 12}, {11188, 12}, {111288, 12}, {111588, 12}, \ - {111888, 12}, {111088, 12}, {197788, 12}, {17788, 12}, {27788, 12}, \ - {47788, 12}, {57788, 12}, {67788, 12}, {77788, 12}, {87788, 12}, \ - {97788, 12}, {107788, 12}, {117788, 12}, {1117788, 12}, \ - {11127788, 12}, {11157788, 12}, {11187788, 12}, {11107788, 12}, \ - {199988, 12}, {19988, 12}, {29988, 12}, {49988, 12}, {59988, 12}, \ - {69988, 12}, {79988, 12}, {89988, 12}, {99988, 12}, {109988, 12}, \ - {119988, 12}, {1119988, 12}, {11129988, 12}, {11159988, 12}, \ - {11189988, 12}, {11109988, 12}, {19779988, 12}, {1779988, 12}, \ - {2779988, 12}, {4779988, 12}, {5779988, 12}, {6779988, 12}, \ - {7779988, 12}, {8779988, 12}, {9779988, 12}, {10779988, 12}, \ - {11779988, 12}, {111779988, 12}, {1112779988, 12}, {1115779988, 12}, \ - {1118779988, 12}, { \ - 1110779988, 13 \ +TEST_CASE("frozen::map <> std::map", "[map]") +{ +#define INIT_SEQ \ + { 19, 12 }, { 1, 12 }, { 2, 12 }, { 4, 12 }, { 5, 12 }, { 6, 12 }, { 7, 12 }, { 8, 12 }, \ + { 9, 12 }, { 10, 12 }, { 11, 12 }, { 111, 12 }, { 1112, 12 }, { 1115, 12 }, { 1118, 12 }, \ + { 1110, 12 }, { 1977, 12 }, { 177, 12 }, { 277, 12 }, { 477, 12 }, { 577, 12 }, \ + { 677, 12 }, { 777, 12 }, { 877, 12 }, { 977, 12 }, { 1077, 12 }, { 1177, 12 }, \ + { 11177, 12 }, { 111277, 12 }, { 111577, 12 }, { 111877, 12 }, { 111077, 12 }, \ + { 1999, 12 }, { 199, 12 }, { 299, 12 }, { 499, 12 }, { 599, 12 }, { 699, 12 }, \ + { 799, 12 }, { 899, 12 }, { 999, 12 }, { 1099, 12 }, { 1199, 12 }, { 11199, 12 }, \ + { 111299, 12 }, { 111599, 12 }, { 111899, 12 }, { 111099, 12 }, { 197799, 12 }, \ + { 17799, 12 }, { 27799, 12 }, { 47799, 12 }, { 57799, 12 }, { 67799, 12 }, { 77799, 12 }, \ + { 87799, 12 }, { 97799, 12 }, { 107799, 12 }, { 117799, 12 }, { 1117799, 12 }, \ + { 11127799, 12 }, { 11157799, 12 }, { 11187799, 12 }, { 11107799, 12 }, { 1988, 12 }, \ + { 188, 12 }, { 288, 12 }, { 488, 12 }, { 588, 12 }, { 688, 12 }, { 788, 12 }, { 888, 12 }, \ + { 988, 12 }, { 1088, 12 }, { 1188, 12 }, { 11188, 12 }, { 111288, 12 }, { 111588, 12 }, \ + { 111888, 12 }, { 111088, 12 }, { 197788, 12 }, { 17788, 12 }, { 27788, 12 }, \ + { 47788, 12 }, { 57788, 12 }, { 67788, 12 }, { 77788, 12 }, { 87788, 12 }, { 97788, 12 }, \ + { 107788, 12 }, { 117788, 12 }, { 1117788, 12 }, { 11127788, 12 }, { 11157788, 12 }, \ + { 11187788, 12 }, { 11107788, 12 }, { 199988, 12 }, { 19988, 12 }, { 29988, 12 }, \ + { 49988, 12 }, { 59988, 12 }, { 69988, 12 }, { 79988, 12 }, { 89988, 12 }, { 99988, 12 }, \ + { 109988, 12 }, { 119988, 12 }, { 1119988, 12 }, { 11129988, 12 }, { 11159988, 12 }, \ + { 11189988, 12 }, { 11109988, 12 }, { 19779988, 12 }, { 1779988, 12 }, { 2779988, 12 }, \ + { 4779988, 12 }, { 5779988, 12 }, { 6779988, 12 }, { 7779988, 12 }, { 8779988, 12 }, \ + { 9779988, 12 }, { 10779988, 12 }, { 11779988, 12 }, { 111779988, 12 }, \ + { 1112779988, 12 }, { 1115779988, 12 }, { 1118779988, 12 }, { 1110779988, 13 } + + const std::map std_map = { INIT_SEQ }; + constexpr frozen::map frozen_map = { INIT_SEQ }; + SECTION("checking size and content") + { + REQUIRE(std_map.size() == frozen_map.size()); + for (auto v: std_map) + { + REQUIRE(frozen_map.count(std::get<0>(v))); + REQUIRE(frozen_map.find(std::get<0>(v))->second == v.second); + } + for (auto v: frozen_map) + REQUIRE(std_map.count(std::get<0>(v))); } - const std::map std_map = { INIT_SEQ }; - constexpr frozen::map frozen_map = { INIT_SEQ }; - SECTION("checking size and content") { - REQUIRE(std_map.size() == frozen_map.size()); - for (auto v : std_map) { - REQUIRE(frozen_map.count(std::get<0>(v))); - REQUIRE(frozen_map.find(std::get<0>(v))->second == v.second); - } - for (auto v : frozen_map) - REQUIRE(std_map.count(std::get<0>(v))); - } - - static_assert(std::is_same::value, ""); - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, + ""); + static_assert(std::is_same::value, + ""); } -TEST_CASE("frozen::map <> frozen::make_map", "[map]") { - constexpr frozen::map frozen_map = { INIT_SEQ }; - constexpr auto frozen_map2 = frozen::make_map({INIT_SEQ}); - constexpr auto frozen_map3 = frozen::make_map(std::array, 128>{{INIT_SEQ}}); - REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); - - SECTION("checking size and content") { - REQUIRE(frozen_map.size() == frozen_map2.size()); - for (auto v : frozen_map2) - REQUIRE(frozen_map.count(std::get<0>(v))); - for (auto v : frozen_map) - REQUIRE(frozen_map2.count(std::get<0>(v))); - } - - constexpr frozen::map frozen_empty_map = {}; - constexpr auto frozen_empty_map2 = frozen::make_map(); - constexpr auto frozen_empty_map3 = frozen::make_map({}); - - SECTION("checking empty map") { - REQUIRE(frozen_empty_map.empty()); - REQUIRE(frozen_empty_map.size() == 0); - REQUIRE(frozen_empty_map.begin() == frozen_empty_map.end()); - - REQUIRE(frozen_empty_map2.empty()); - REQUIRE(frozen_empty_map2.size() == 0); - REQUIRE(frozen_empty_map2.begin() == frozen_empty_map2.end()); - - REQUIRE(frozen_empty_map3.empty()); - REQUIRE(frozen_empty_map3.size() == 0); - REQUIRE(frozen_empty_map3.begin() == frozen_empty_map3.end()); - } +TEST_CASE("frozen::map <> frozen::make_map", "[map]") +{ + constexpr frozen::map frozen_map = { INIT_SEQ }; + constexpr auto frozen_map2 = frozen::make_map({ INIT_SEQ }); + constexpr auto frozen_map3 = + frozen::make_map(std::array, 128> { { INIT_SEQ } }); + REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); + + SECTION("checking size and content") + { + REQUIRE(frozen_map.size() == frozen_map2.size()); + for (auto v: frozen_map2) + REQUIRE(frozen_map.count(std::get<0>(v))); + for (auto v: frozen_map) + REQUIRE(frozen_map2.count(std::get<0>(v))); + } + + constexpr frozen::map frozen_empty_map = {}; + constexpr auto frozen_empty_map2 = frozen::make_map(); + constexpr auto frozen_empty_map3 = frozen::make_map({}); + + SECTION("checking empty map") + { + REQUIRE(frozen_empty_map.empty()); + REQUIRE(frozen_empty_map.size() == 0); + REQUIRE(frozen_empty_map.begin() == frozen_empty_map.end()); + + REQUIRE(frozen_empty_map2.empty()); + REQUIRE(frozen_empty_map2.size() == 0); + REQUIRE(frozen_empty_map2.begin() == frozen_empty_map2.end()); + + REQUIRE(frozen_empty_map3.empty()); + REQUIRE(frozen_empty_map3.size() == 0); + REQUIRE(frozen_empty_map3.begin() == frozen_empty_map3.end()); + } } -TEST_CASE("frozen::map constexpr", "[map]") { - constexpr frozen::map ce = {{11,12}, {3,4}}; - static_assert(*ce.begin() == std::pair{3,4}, ""); - static_assert(*(ce.begin() + 1) == std::pair{11,12}, ""); - static_assert(ce.size() == 2, ""); - static_assert(ce.count(3), ""); - static_assert(!ce.count(0), ""); - static_assert(ce.find(0) == ce.end(), ""); - static_assert(ce.at(3) == 4, ""); - static_assert(ce.contains(3), ""); - static_assert(!ce.contains(0), ""); +TEST_CASE("frozen::map constexpr", "[map]") +{ + constexpr frozen::map ce = { { 11, 12 }, { 3, 4 } }; + static_assert(*ce.begin() == std::pair { 3, 4 }, ""); + static_assert(*(ce.begin() + 1) == std::pair { 11, 12 }, ""); + static_assert(ce.size() == 2, ""); + static_assert(ce.count(3), ""); + static_assert(!ce.count(0), ""); + static_assert(ce.find(0) == ce.end(), ""); + static_assert(ce.at(3) == 4, ""); + static_assert(ce.contains(3), ""); + static_assert(!ce.contains(0), ""); } -TEST_CASE("Modifiable frozen::map", "[map]") { - frozen::map frozen_map = {{0, 1}, {2, 3}, {4, 5}}; +TEST_CASE("Modifiable frozen::map", "[map]") +{ + frozen::map frozen_map = { { 0, 1 }, { 2, 3 }, { 4, 5 } }; - SECTION("Lookup existing values") { - REQUIRE(frozen_map.at(0) == 1); - REQUIRE(frozen_map.find(0)->second == 1); - REQUIRE(frozen_map.equal_range(0).first->second == 1); + SECTION("Lookup existing values") + { + REQUIRE(frozen_map.at(0) == 1); + REQUIRE(frozen_map.find(0)->second == 1); + REQUIRE(frozen_map.equal_range(0).first->second == 1); - REQUIRE(frozen_map.at(2) == 3); - REQUIRE(frozen_map.find(2)->second == 3); - REQUIRE(frozen_map.equal_range(2).first->second == 3); + REQUIRE(frozen_map.at(2) == 3); + REQUIRE(frozen_map.find(2)->second == 3); + REQUIRE(frozen_map.equal_range(2).first->second == 3); - REQUIRE(frozen_map.at(4) == 5); - REQUIRE(frozen_map.find(4)->second == 5); - REQUIRE(frozen_map.equal_range(4).first->second == 5); - } + REQUIRE(frozen_map.at(4) == 5); + REQUIRE(frozen_map.find(4)->second == 5); + REQUIRE(frozen_map.equal_range(4).first->second == 5); + } - SECTION("Lookup failure") { - REQUIRE(frozen_map.find(3) == frozen_map.end()); - REQUIRE_THROWS_AS(frozen_map.at(3), std::out_of_range); + SECTION("Lookup failure") + { + REQUIRE(frozen_map.find(3) == frozen_map.end()); + REQUIRE_THROWS_AS(frozen_map.at(3), std::out_of_range); - REQUIRE(frozen_map.find(5) == frozen_map.end()); - REQUIRE_THROWS_AS(frozen_map.at(5), std::out_of_range); - } + REQUIRE(frozen_map.find(5) == frozen_map.end()); + REQUIRE_THROWS_AS(frozen_map.at(5), std::out_of_range); + } - SECTION("Modify value") { - frozen_map.at(0) = -1; - REQUIRE(frozen_map.at(0) == -1); + SECTION("Modify value") + { + frozen_map.at(0) = -1; + REQUIRE(frozen_map.at(0) == -1); - frozen_map.begin()->second = -2; - REQUIRE(frozen_map.begin()->second == -2); + frozen_map.begin()->second = -2; + REQUIRE(frozen_map.begin()->second == -2); - (frozen_map.end() - 1)->second = -3; - REQUIRE((frozen_map.end() - 1)->second == -3); + (frozen_map.end() - 1)->second = -3; + REQUIRE((frozen_map.end() - 1)->second == -3); - frozen_map.equal_range(4).first->second = -5; - REQUIRE(frozen_map.at(4) == -5); + frozen_map.equal_range(4).first->second = -5; + REQUIRE(frozen_map.at(4) == -5); - frozen_map.lower_bound(2)->second = -22; - REQUIRE(frozen_map.at(2) == -22); + frozen_map.lower_bound(2)->second = -22; + REQUIRE(frozen_map.at(2) == -22); - frozen_map.upper_bound(2)->second = -33; - REQUIRE(frozen_map.at(4) == -33); - } + frozen_map.upper_bound(2)->second = -33; + REQUIRE(frozen_map.at(4) == -33); + } } -struct S { - int x; +struct S +{ + int x; }; -constexpr inline bool operator==(S const &a, S const &b) { return a.x == b.x; } -constexpr inline bool operator<(S const &a, S const &b) { return a.x < b.x; } -constexpr inline bool operator<(S const &a, int b) { return a.x < b; } -constexpr inline bool operator<(int a, S const &b) { return a < b.x; } +constexpr inline bool operator==(S const& a, S const& b) +{ + return a.x == b.x; +} +constexpr inline bool operator<(S const& a, S const& b) +{ + return a.x < b.x; +} +constexpr inline bool operator<(S const& a, int b) +{ + return a.x < b; +} +constexpr inline bool operator<(int a, S const& b) +{ + return a < b.x; +} -TEST_CASE("empty frozen transparent map", "[map]") { - constexpr frozen::map> ze_map{}; +TEST_CASE("empty frozen transparent map", "[map]") +{ + constexpr frozen::map ze_map {}; - constexpr auto count = ze_map.count(3); - REQUIRE(count == 0); + constexpr auto count = ze_map.count(3); + REQUIRE(count == 0); - constexpr auto find = ze_map.find(5); - REQUIRE(find == ze_map.end()); + constexpr auto find = ze_map.find(5); + REQUIRE(find == ze_map.end()); - constexpr auto range = ze_map.equal_range(0); - REQUIRE(std::get<0>(range) == ze_map.end()); - REQUIRE(std::get<1>(range) == ze_map.end()); + constexpr auto range = ze_map.equal_range(0); + REQUIRE(std::get<0>(range) == ze_map.end()); + REQUIRE(std::get<1>(range) == ze_map.end()); - constexpr auto lower_bound = ze_map.lower_bound(1); - REQUIRE(lower_bound == ze_map.end()); + constexpr auto lower_bound = ze_map.lower_bound(1); + REQUIRE(lower_bound == ze_map.end()); - constexpr auto upper_bound = ze_map.upper_bound(1); - REQUIRE(upper_bound == ze_map.end()); + constexpr auto upper_bound = ze_map.upper_bound(1); + REQUIRE(upper_bound == ze_map.end()); } -TEST_CASE("frozen transparent map", "[map]") { - constexpr frozen::map> ze_map{ - {S{10}, true}, {S{20}, false}, {S{30}, true}, {S{40}, false}}; +TEST_CASE("frozen transparent map", "[map]") +{ + constexpr frozen::map ze_map { + { S { 10 }, true }, { S { 20 }, false }, { S { 30 }, true }, { S { 40 }, false } + }; - constexpr auto empty = ze_map.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_map.empty(); + REQUIRE(!empty); - constexpr auto size = ze_map.size(); - REQUIRE(size == 4); + constexpr auto size = ze_map.size(); + REQUIRE(size == 4); - constexpr auto max_size = ze_map.max_size(); - REQUIRE(max_size == 4); + constexpr auto max_size = ze_map.max_size(); + REQUIRE(max_size == 4); - constexpr auto count1 = ze_map.count(1); - REQUIRE(count1 == 0); + constexpr auto count1 = ze_map.count(1); + REQUIRE(count1 == 0); - constexpr auto count10 = ze_map.count(10); - REQUIRE(count10 == 1); + constexpr auto count10 = ze_map.count(10); + REQUIRE(count10 == 1); - const auto find10 = ze_map.find(10); - REQUIRE(find10 == ze_map.begin()); - REQUIRE(std::get<0>(*find10) == S{10}); - REQUIRE(std::get<1>(*find10) == true); + const auto find10 = ze_map.find(10); + REQUIRE(find10 == ze_map.begin()); + REQUIRE(std::get<0>(*find10) == S { 10 }); + REQUIRE(std::get<1>(*find10) == true); - const auto find15 = ze_map.find(15); - REQUIRE(find15 == ze_map.end()); + const auto find15 = ze_map.find(15); + REQUIRE(find15 == ze_map.end()); - const auto range0 = ze_map.equal_range(0); - REQUIRE(std::get<0>(range0) == ze_map.begin()); - REQUIRE(std::get<1>(range0) == ze_map.begin()); + const auto range0 = ze_map.equal_range(0); + REQUIRE(std::get<0>(range0) == ze_map.begin()); + REQUIRE(std::get<1>(range0) == ze_map.begin()); - const auto range1 = ze_map.equal_range(10); - REQUIRE(std::get<0>(range1) == ze_map.begin()); - REQUIRE(std::get<1>(range1) == ze_map.begin() + 1); + const auto range1 = ze_map.equal_range(10); + REQUIRE(std::get<0>(range1) == ze_map.begin()); + REQUIRE(std::get<1>(range1) == ze_map.begin() + 1); - const auto lower_bound0 = ze_map.lower_bound(0); - REQUIRE(lower_bound0 == ze_map.begin()); + const auto lower_bound0 = ze_map.lower_bound(0); + REQUIRE(lower_bound0 == ze_map.begin()); - for (auto val : ze_map) { - const auto lower_bound = ze_map.lower_bound(std::get<0>(val)); - REQUIRE(lower_bound == ze_map.find(std::get<0>(val))); - } + for (auto val: ze_map) + { + const auto lower_bound = ze_map.lower_bound(std::get<0>(val)); + REQUIRE(lower_bound == ze_map.find(std::get<0>(val))); + } - const auto lower_bound2 = ze_map.lower_bound(50); - REQUIRE(lower_bound2 == ze_map.end()); + const auto lower_bound2 = ze_map.lower_bound(50); + REQUIRE(lower_bound2 == ze_map.end()); - const auto upper_bound0 = ze_map.upper_bound(0); - REQUIRE(upper_bound0 == ze_map.begin()); + const auto upper_bound0 = ze_map.upper_bound(0); + REQUIRE(upper_bound0 == ze_map.begin()); - const auto upper_bound1 = ze_map.upper_bound(10); - REQUIRE(upper_bound1 == (ze_map.begin() + 1)); + const auto upper_bound1 = ze_map.upper_bound(10); + REQUIRE(upper_bound1 == (ze_map.begin() + 1)); - const auto upper_bound2 = ze_map.upper_bound(50); - REQUIRE(upper_bound2 == ze_map.end()); + const auto upper_bound2 = ze_map.upper_bound(50); + REQUIRE(upper_bound2 == ze_map.end()); - auto const begin = ze_map.begin(), end = ze_map.end(); - REQUIRE((begin + ze_map.size()) == end); + auto const begin = ze_map.begin(), end = ze_map.end(); + REQUIRE((begin + ze_map.size()) == end); - auto const key_comp = ze_map.key_comp(); - auto const key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto const key_comp = ze_map.key_comp(); + auto const key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto const value_comp = ze_map.value_comp(); - auto const value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto const value_comp = ze_map.value_comp(); + auto const value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); - REQUIRE(cbegin == (cend - ze_map.size())); + auto const cbegin = ze_map.cbegin(), cend = ze_map.cend(); + REQUIRE(cbegin == (cend - ze_map.size())); - std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); - REQUIRE((std::size_t)std::distance(ze_map.rbegin(), ze_map.rend()) == ze_map.size()); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type{S{20}, true}) == 0); - REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), - decltype(ze_map)::value_type{S{20}, false}) == 1); + std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); + REQUIRE((std::size_t) std::distance(ze_map.rbegin(), ze_map.rend()) == ze_map.size()); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), + decltype(ze_map)::value_type { S { 20 }, true }) == 0); + REQUIRE(std::count(ze_map.crbegin(), ze_map.crend(), + decltype(ze_map)::value_type { S { 20 }, false }) == 1); } -TEST_CASE("frozen::map <> frozen::make_map transparent", "[map]") { - constexpr frozen::map> frozen_map = { INIT_SEQ }; - constexpr auto frozen_map2 = frozen::make_map>({INIT_SEQ}); - constexpr auto frozen_map3 = frozen::make_map>(std::array, 128>{{INIT_SEQ}}); - REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); - - SECTION("checking size and content") { - REQUIRE(frozen_map.size() == frozen_map2.size()); - for (auto v : frozen_map2) - REQUIRE(frozen_map.count(std::get<0>(v))); - for (auto v : frozen_map) - REQUIRE(frozen_map2.count(std::get<0>(v))); - } - - constexpr frozen::map> frozen_empty_map = {}; - constexpr auto frozen_empty_map2 = frozen::make_map>(); - constexpr auto frozen_empty_map3 = frozen::make_map>({}); - - SECTION("checking empty map") { - REQUIRE(frozen_empty_map.empty()); - REQUIRE(frozen_empty_map.size() == 0); - REQUIRE(frozen_empty_map.begin() == frozen_empty_map.end()); - - REQUIRE(frozen_empty_map2.empty()); - REQUIRE(frozen_empty_map2.size() == 0); - REQUIRE(frozen_empty_map2.begin() == frozen_empty_map2.end()); - - REQUIRE(frozen_empty_map3.empty()); - REQUIRE(frozen_empty_map3.size() == 0); - REQUIRE(frozen_empty_map3.begin() == frozen_empty_map3.end()); - } +TEST_CASE("frozen::map <> frozen::make_map transparent", "[map]") +{ + constexpr frozen::map frozen_map = { INIT_SEQ }; + constexpr auto frozen_map2 = frozen::make_map({ INIT_SEQ }); + constexpr auto frozen_map3 = + frozen::make_map(std::array, 128> { { INIT_SEQ } }); + REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); + + SECTION("checking size and content") + { + REQUIRE(frozen_map.size() == frozen_map2.size()); + for (auto v: frozen_map2) + REQUIRE(frozen_map.count(std::get<0>(v))); + for (auto v: frozen_map) + REQUIRE(frozen_map2.count(std::get<0>(v))); + } + + constexpr frozen::map frozen_empty_map = {}; + constexpr auto frozen_empty_map2 = frozen::make_map(); + constexpr auto frozen_empty_map3 = frozen::make_map({}); + + SECTION("checking empty map") + { + REQUIRE(frozen_empty_map.empty()); + REQUIRE(frozen_empty_map.size() == 0); + REQUIRE(frozen_empty_map.begin() == frozen_empty_map.end()); + + REQUIRE(frozen_empty_map2.empty()); + REQUIRE(frozen_empty_map2.size() == 0); + REQUIRE(frozen_empty_map2.begin() == frozen_empty_map2.end()); + + REQUIRE(frozen_empty_map3.empty()); + REQUIRE(frozen_empty_map3.size() == 0); + REQUIRE(frozen_empty_map3.begin() == frozen_empty_map3.end()); + } } -TEST_CASE("frozen::map constexpr std::pair key", "[map]") { - constexpr frozen::map, bool, 2> ce1 = { - {{1, 2}, true}, {{3, 4}, false}}; - static_assert(ce1.find(std::pair{1, 3}) == ce1.end(), ""); +TEST_CASE("frozen::map constexpr std::pair key", "[map]") +{ + constexpr frozen::map, bool, 2> ce1 = { { { 1, 2 }, true }, + { { 3, 4 }, false } }; + static_assert(ce1.find(std::pair { 1, 3 }) == ce1.end(), ""); - constexpr frozen::map, int, 2> ce2 = { - {{1, 2}, 5}, {{3, 4}, 6}}; - static_assert(ce2.at({3, 4}) == 6, ""); + constexpr frozen::map, int, 2> ce2 = { { { 1, 2 }, 5 }, { { 3, 4 }, 6 } }; + static_assert(ce2.at({ 3, 4 }) == 6, ""); } diff --git a/tests/test_set.cpp b/tests/test_set.cpp index fbbd403..6a7e42e 100644 --- a/tests/test_set.cpp +++ b/tests/test_set.cpp @@ -7,312 +7,345 @@ #include "bench.hpp" #include "catch.hpp" -TEST_CASE("empty frozen set", "[set]") { - constexpr frozen::set ze_set{}; +TEST_CASE("empty frozen set", "[set]") +{ + constexpr frozen::set ze_set {}; - constexpr auto empty = ze_set.empty(); - REQUIRE(empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 0); + constexpr auto size = ze_set.size(); + REQUIRE(size == 0); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 0); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 0); - constexpr auto count = ze_set.count(3); - REQUIRE(count == 0); + constexpr auto count = ze_set.count(3); + REQUIRE(count == 0); - constexpr auto find = ze_set.find(5); - REQUIRE(find == ze_set.end()); + constexpr auto find = ze_set.find(5); + REQUIRE(find == ze_set.end()); - constexpr auto range = ze_set.equal_range(0); - REQUIRE(std::get<0>(range) == ze_set.end()); - REQUIRE(std::get<1>(range) == ze_set.end()); + constexpr auto range = ze_set.equal_range(0); + REQUIRE(std::get<0>(range) == ze_set.end()); + REQUIRE(std::get<1>(range) == ze_set.end()); - constexpr auto lower_bound = ze_set.lower_bound(1); - REQUIRE(lower_bound == ze_set.end()); + constexpr auto lower_bound = ze_set.lower_bound(1); + REQUIRE(lower_bound == ze_set.end()); - constexpr auto upper_bound = ze_set.upper_bound(1); - REQUIRE(upper_bound == ze_set.end()); + constexpr auto upper_bound = ze_set.upper_bound(1); + REQUIRE(upper_bound == ze_set.end()); - auto constexpr begin = ze_set.begin(), end = ze_set.end(); - REQUIRE(begin == end); + auto constexpr begin = ze_set.begin(), end = ze_set.end(); + REQUIRE(begin == end); - auto constexpr key_comp = ze_set.key_comp(); - auto constexpr key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto constexpr key_comp = ze_set.key_comp(); + auto constexpr key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto constexpr value_comp = ze_set.value_comp(); - auto constexpr value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto constexpr value_comp = ze_set.value_comp(); + auto constexpr value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto constexpr cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin == cend); + auto constexpr cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin == cend); - std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); - REQUIRE(std::distance(ze_set.rbegin(), ze_set.rend()) == 0); - REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); + std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); + REQUIRE(std::distance(ze_set.rbegin(), ze_set.rend()) == 0); + REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); } -TEST_CASE("singleton frozen set", "[set]") { - constexpr frozen::set ze_set{1}; +TEST_CASE("singleton frozen set", "[set]") +{ + constexpr frozen::set ze_set { 1 }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 1); + constexpr auto size = ze_set.size(); + REQUIRE(size == 1); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 1); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 1); - constexpr auto count1 = ze_set.count(1); - REQUIRE(count1 == 1); + constexpr auto count1 = ze_set.count(1); + REQUIRE(count1 == 1); - constexpr auto count2 = ze_set.count(2); - REQUIRE(count2 == 0); + constexpr auto count2 = ze_set.count(2); + REQUIRE(count2 == 0); - const auto find1 = ze_set.find(1); - REQUIRE(find1 == ze_set.begin()); + const auto find1 = ze_set.find(1); + REQUIRE(find1 == ze_set.begin()); - const auto find5 = ze_set.find(5); - REQUIRE(find5 == ze_set.end()); + const auto find5 = ze_set.find(5); + REQUIRE(find5 == ze_set.end()); - const auto range0 = ze_set.equal_range(0); - REQUIRE(std::get<0>(range0) == ze_set.end()); - REQUIRE(std::get<1>(range0) == ze_set.end()); + const auto range0 = ze_set.equal_range(0); + REQUIRE(std::get<0>(range0) == ze_set.end()); + REQUIRE(std::get<1>(range0) == ze_set.end()); - const auto range1 = ze_set.equal_range(1); - REQUIRE(std::get<0>(range1) == ze_set.begin()); - REQUIRE(std::get<1>(range1) == ze_set.end()); + const auto range1 = ze_set.equal_range(1); + REQUIRE(std::get<0>(range1) == ze_set.begin()); + REQUIRE(std::get<1>(range1) == ze_set.end()); - const auto lower_bound0 = ze_set.lower_bound(0); - REQUIRE(lower_bound0 == ze_set.end()); + const auto lower_bound0 = ze_set.lower_bound(0); + REQUIRE(lower_bound0 == ze_set.end()); - const auto lower_bound1 = ze_set.lower_bound(1); - REQUIRE(lower_bound1 == ze_set.find(1)); + const auto lower_bound1 = ze_set.lower_bound(1); + REQUIRE(lower_bound1 == ze_set.find(1)); - const auto lower_bound2 = ze_set.lower_bound(2); - REQUIRE(lower_bound2 == ze_set.end()); + const auto lower_bound2 = ze_set.lower_bound(2); + REQUIRE(lower_bound2 == ze_set.end()); - const auto upper_bound0 = ze_set.upper_bound(0); - REQUIRE(upper_bound0 == ze_set.end()); + const auto upper_bound0 = ze_set.upper_bound(0); + REQUIRE(upper_bound0 == ze_set.end()); - const auto upper_bound1 = ze_set.upper_bound(1); - REQUIRE(upper_bound1 == ze_set.end()); + const auto upper_bound1 = ze_set.upper_bound(1); + REQUIRE(upper_bound1 == ze_set.end()); - const auto upper_bound2 = ze_set.upper_bound(2); - REQUIRE(upper_bound2 == ze_set.end()); + const auto upper_bound2 = ze_set.upper_bound(2); + REQUIRE(upper_bound2 == ze_set.end()); - auto const begin = ze_set.begin(), end = ze_set.end(); - REQUIRE((begin + 1) == end); + auto const begin = ze_set.begin(), end = ze_set.end(); + REQUIRE((begin + 1) == end); - auto const key_comp = ze_set.key_comp(); - auto const key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto const key_comp = ze_set.key_comp(); + auto const key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto const value_comp = ze_set.value_comp(); - auto const value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto const value_comp = ze_set.value_comp(); + auto const value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto const cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin == (cend - 1)); + auto const cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin == (cend - 1)); - std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); - REQUIRE(std::distance(ze_set.rbegin(), ze_set.rend()) == 1); - REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); - REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 1) == 1); + std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); + REQUIRE(std::distance(ze_set.rbegin(), ze_set.rend()) == 1); + REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); + REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 1) == 1); } -TEST_CASE("triple frozen set", "[set]") { - constexpr frozen::set ze_set{10, 20, 30}; +TEST_CASE("triple frozen set", "[set]") +{ + constexpr frozen::set ze_set { 10, 20, 30 }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 3); + constexpr auto size = ze_set.size(); + REQUIRE(size == 3); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 3); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 3); - constexpr auto count1 = ze_set.count(1); - REQUIRE(count1 == 0); + constexpr auto count1 = ze_set.count(1); + REQUIRE(count1 == 0); - constexpr auto count10 = ze_set.count(10); - REQUIRE(count10 == 1); + constexpr auto count10 = ze_set.count(10); + REQUIRE(count10 == 1); - const auto find10 = ze_set.find(10); - REQUIRE(find10 == ze_set.begin()); + const auto find10 = ze_set.find(10); + REQUIRE(find10 == ze_set.begin()); - const auto find15 = ze_set.find(15); - REQUIRE(find15 == ze_set.end()); + const auto find15 = ze_set.find(15); + REQUIRE(find15 == ze_set.end()); - const auto range0 = ze_set.equal_range(0); - REQUIRE(std::get<0>(range0) == ze_set.end()); - REQUIRE(std::get<1>(range0) == ze_set.end()); + const auto range0 = ze_set.equal_range(0); + REQUIRE(std::get<0>(range0) == ze_set.end()); + REQUIRE(std::get<1>(range0) == ze_set.end()); - const auto range1 = ze_set.equal_range(10); - REQUIRE(std::get<0>(range1) == ze_set.begin()); - REQUIRE(std::get<1>(range1) == ze_set.begin() + 1); + const auto range1 = ze_set.equal_range(10); + REQUIRE(std::get<0>(range1) == ze_set.begin()); + REQUIRE(std::get<1>(range1) == ze_set.begin() + 1); - const auto lower_bound0 = ze_set.lower_bound(0); - REQUIRE(lower_bound0 == ze_set.end()); + const auto lower_bound0 = ze_set.lower_bound(0); + REQUIRE(lower_bound0 == ze_set.end()); - for (auto val : ze_set) { - const auto lower_bound = ze_set.lower_bound(val); - REQUIRE(lower_bound == ze_set.find(val)); - } + for (auto val: ze_set) + { + const auto lower_bound = ze_set.lower_bound(val); + REQUIRE(lower_bound == ze_set.find(val)); + } - const auto lower_bound2 = ze_set.lower_bound(40); - REQUIRE(lower_bound2 == ze_set.end()); + const auto lower_bound2 = ze_set.lower_bound(40); + REQUIRE(lower_bound2 == ze_set.end()); - const auto upper_bound0 = ze_set.upper_bound(0); - REQUIRE(upper_bound0 == ze_set.end()); + const auto upper_bound0 = ze_set.upper_bound(0); + REQUIRE(upper_bound0 == ze_set.end()); - const auto upper_bound1 = ze_set.upper_bound(10); - REQUIRE(upper_bound1 == (ze_set.begin() + 1)); + const auto upper_bound1 = ze_set.upper_bound(10); + REQUIRE(upper_bound1 == (ze_set.begin() + 1)); - const auto upper_bound2 = ze_set.upper_bound(40); - REQUIRE(upper_bound2 == ze_set.end()); + const auto upper_bound2 = ze_set.upper_bound(40); + REQUIRE(upper_bound2 == ze_set.end()); - auto const begin = ze_set.begin(), end = ze_set.end(); - REQUIRE((begin + ze_set.size()) == end); + auto const begin = ze_set.begin(), end = ze_set.end(); + REQUIRE((begin + ze_set.size()) == end); - auto const key_comp = ze_set.key_comp(); - auto const key_comparison = key_comp(1, 2); - REQUIRE(key_comparison); + auto const key_comp = ze_set.key_comp(); + auto const key_comparison = key_comp(1, 2); + REQUIRE(key_comparison); - auto const value_comp = ze_set.value_comp(); - auto const value_comparison = value_comp(11, 12); - REQUIRE(value_comparison); + auto const value_comp = ze_set.value_comp(); + auto const value_comparison = value_comp(11, 12); + REQUIRE(value_comparison); - auto const cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin == (cend - ze_set.size())); + auto const cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin == (cend - ze_set.size())); - std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); - REQUIRE((std::size_t)std::distance(ze_set.rbegin(), ze_set.rend()) == ze_set.size()); - REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); - REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 20) == 1); + std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); + REQUIRE((std::size_t) std::distance(ze_set.rbegin(), ze_set.rend()) == ze_set.size()); + REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 3) == 0); + REQUIRE(std::count(ze_set.crbegin(), ze_set.crend(), 20) == 1); } -TEST_CASE("frozen::set <> std::set", "[set]") { -#define INIT_SEQ \ - 19, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 111, 1112, 1115, 1118, 1110, 1977, 177, \ - 277, 477, 577, 677, 777, 877, 977, 1077, 1177, 11177, 111277, 111577, \ - 111877, 111077, 1999, 199, 299, 499, 599, 699, 799, 899, 999, 1099, \ - 1199, 11199, 111299, 111599, 111899, 111099, 197799, 17799, 27799, \ - 47799, 57799, 67799, 77799, 87799, 97799, 107799, 117799, 1117799, \ - 11127799, 11157799, 11187799, 11107799, 1988, 188, 288, 488, 588, 688, \ - 788, 888, 988, 1088, 1188, 11188, 111288, 111588, 111888, 111088, \ - 197788, 17788, 27788, 47788, 57788, 67788, 77788, 87788, 97788, 107788, \ - 117788, 1117788, 11127788, 11157788, 11187788, 11107788, 199988, 19988, \ - 29988, 49988, 59988, 69988, 79988, 89988, 99988, 109988, 119988, \ - 1119988, 11129988, 11159988, 11189988, 11109988, 19779988, 1779988, \ - 2779988, 4779988, 5779988, 6779988, 7779988, 8779988, 9779988, 10779988, \ - 11779988, 111779988, 1112779988, 1115779988, 1118779988, 1110779988 - - const std::set std_set = {INIT_SEQ}; - constexpr frozen::set frozen_set = {INIT_SEQ}; - - SECTION("checking size and content") { - REQUIRE(std_set.size() == frozen_set.size()); - for (auto v : std_set) - REQUIRE(frozen_set.count(v)); - for (auto v : frozen_set) - REQUIRE(std_set.count(v)); - } - +TEST_CASE("frozen::set <> std::set", "[set]") +{ +#define INIT_SEQ \ + 19, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 111, 1112, 1115, 1118, 1110, 1977, 177, 277, 477, 577, \ + 677, 777, 877, 977, 1077, 1177, 11177, 111277, 111577, 111877, 111077, 1999, 199, 299, \ + 499, 599, 699, 799, 899, 999, 1099, 1199, 11199, 111299, 111599, 111899, 111099, 197799, \ + 17799, 27799, 47799, 57799, 67799, 77799, 87799, 97799, 107799, 117799, 1117799, 11127799, \ + 11157799, 11187799, 11107799, 1988, 188, 288, 488, 588, 688, 788, 888, 988, 1088, 1188, \ + 11188, 111288, 111588, 111888, 111088, 197788, 17788, 27788, 47788, 57788, 67788, 77788, \ + 87788, 97788, 107788, 117788, 1117788, 11127788, 11157788, 11187788, 11107788, 199988, \ + 19988, 29988, 49988, 59988, 69988, 79988, 89988, 99988, 109988, 119988, 1119988, 11129988, \ + 11159988, 11189988, 11109988, 19779988, 1779988, 2779988, 4779988, 5779988, 6779988, \ + 7779988, 8779988, 9779988, 10779988, 11779988, 111779988, 1112779988, 1115779988, \ + 1118779988, 1110779988 + + const std::set std_set = { INIT_SEQ }; + constexpr frozen::set frozen_set = { INIT_SEQ }; + + SECTION("checking size and content") + { + REQUIRE(std_set.size() == frozen_set.size()); + for (auto v: std_set) + REQUIRE(frozen_set.count(v)); + for (auto v: frozen_set) + REQUIRE(std_set.count(v)); + } } -TEST_CASE("frozen::set <> frozen::make_set", "[set]") { - constexpr frozen::set from_ctor = { INIT_SEQ }; - constexpr int init_array[]{INIT_SEQ}; - constexpr auto from_c_array = frozen::make_set(init_array); - constexpr auto from_std_array = frozen::make_set(std::array{{INIT_SEQ}}); - REQUIRE(std::equal(from_c_array.begin(), from_c_array.end(), from_std_array.begin())); - - SECTION("checking size and content") { - REQUIRE(from_ctor.size() == from_c_array.size()); - for (auto v : from_c_array) - REQUIRE(from_ctor.count(v)); - for (auto v : from_ctor) - REQUIRE(from_c_array.count(v)); - } - - constexpr frozen::set frozen_empty_set = {}; - constexpr auto frozen_empty_set2 = frozen::make_set(); - constexpr auto frozen_empty_set3 = frozen::make_set({}); - - SECTION("checking empty set") { - REQUIRE(frozen_empty_set.empty()); - REQUIRE(frozen_empty_set.size() == 0); - REQUIRE(frozen_empty_set.begin() == frozen_empty_set.end()); - - REQUIRE(frozen_empty_set2.empty()); - REQUIRE(frozen_empty_set2.size() == 0); - REQUIRE(frozen_empty_set2.begin() == frozen_empty_set2.end()); - - REQUIRE(frozen_empty_set3.empty()); - REQUIRE(frozen_empty_set3.size() == 0); - REQUIRE(frozen_empty_set3.begin() == frozen_empty_set3.end()); - } +TEST_CASE("frozen::set <> frozen::make_set", "[set]") +{ + constexpr frozen::set from_ctor = { INIT_SEQ }; + constexpr int init_array[] { INIT_SEQ }; + constexpr auto from_c_array = frozen::make_set(init_array); + constexpr auto from_std_array = frozen::make_set(std::array { { INIT_SEQ } }); + REQUIRE(std::equal(from_c_array.begin(), from_c_array.end(), from_std_array.begin())); + + SECTION("checking size and content") + { + REQUIRE(from_ctor.size() == from_c_array.size()); + for (auto v: from_c_array) + REQUIRE(from_ctor.count(v)); + for (auto v: from_ctor) + REQUIRE(from_c_array.count(v)); + } + + constexpr frozen::set frozen_empty_set = {}; + constexpr auto frozen_empty_set2 = frozen::make_set(); + constexpr auto frozen_empty_set3 = frozen::make_set({}); + + SECTION("checking empty set") + { + REQUIRE(frozen_empty_set.empty()); + REQUIRE(frozen_empty_set.size() == 0); + REQUIRE(frozen_empty_set.begin() == frozen_empty_set.end()); + + REQUIRE(frozen_empty_set2.empty()); + REQUIRE(frozen_empty_set2.size() == 0); + REQUIRE(frozen_empty_set2.begin() == frozen_empty_set2.end()); + + REQUIRE(frozen_empty_set3.empty()); + REQUIRE(frozen_empty_set3.size() == 0); + REQUIRE(frozen_empty_set3.begin() == frozen_empty_set3.end()); + } } -TEST_CASE("frozen::set constexpr", "[set]") { - constexpr frozen::set ce = {3, 11}; - static_assert(*ce.begin() == 3, ""); - static_assert(*(ce.begin() + 1) == 11, ""); - static_assert(ce.size() == 2, ""); - static_assert(ce.count(3), ""); - static_assert(!ce.count(0), ""); - static_assert(ce.find(0) == ce.end(), ""); - static_assert(ce.contains(3), ""); - static_assert(!ce.contains(0), ""); +TEST_CASE("frozen::set constexpr", "[set]") +{ + constexpr frozen::set ce = { 3, 11 }; + static_assert(*ce.begin() == 3, ""); + static_assert(*(ce.begin() + 1) == 11, ""); + static_assert(ce.size() == 2, ""); + static_assert(ce.count(3), ""); + static_assert(!ce.count(0), ""); + static_assert(ce.find(0) == ce.end(), ""); + static_assert(ce.contains(3), ""); + static_assert(!ce.contains(0), ""); } -TEST_CASE("frozen::set of frozen::set", "[set]") { - using s1 = frozen::set; - constexpr frozen::set ce = {{3}, {11}}; - static_assert(*ce.begin() == s1({3}), ""); - static_assert(*(ce.begin() + 1) == s1({11}), ""); - static_assert(ce.size() == 2, ""); - static_assert(ce.count(s1({3})), ""); - static_assert(!ce.count(s1({0})), ""); - static_assert(ce.find(s1({0})) == ce.end(), ""); - static_assert(ce.contains(s1({3})), ""); - static_assert(!ce.contains(s1({0})), ""); +TEST_CASE("frozen::set of frozen::set", "[set]") +{ + using s1 = frozen::set; + constexpr frozen::set ce = { { 3 }, { 11 } }; + static_assert(*ce.begin() == s1({ 3 }), ""); + static_assert(*(ce.begin() + 1) == s1({ 11 }), ""); + static_assert(ce.size() == 2, ""); + static_assert(ce.count(s1({ 3 })), ""); + static_assert(!ce.count(s1({ 0 })), ""); + static_assert(ce.find(s1({ 0 })) == ce.end(), ""); + static_assert(ce.contains(s1({ 3 })), ""); + static_assert(!ce.contains(s1({ 0 })), ""); } -struct Foo { - int x; +struct Foo +{ + int x; }; -constexpr inline bool operator==(Foo const &a, Foo const &b) { return a.x == b.x; } -constexpr inline bool operator<(Foo const &a, Foo const &b) { return a.x < b.x; } -constexpr inline bool operator<(Foo const &a, int b) { return a.x < b; } -constexpr inline bool operator<(int a, Foo const &b) { return a < b.x; } - -TEST_CASE("frozen::set heterogeneous container", "[set]") { - constexpr std::array std_array{{{1}, {2}, {3}}}; - constexpr Foo c_array[]{{1}, {2}, {3}}; - constexpr auto from_std_array = frozen::make_set>(std_array); - constexpr auto from_c_array = frozen::make_set>(c_array); - - REQUIRE(from_std_array == from_c_array); - for (const auto& set : {from_std_array, from_c_array}) { - REQUIRE(set.find(1) != set.end()); - REQUIRE(set.count(2) == 1); - REQUIRE(set.count(42) == 0); - } +constexpr inline bool operator==(Foo const& a, Foo const& b) +{ + return a.x == b.x; +} +constexpr inline bool operator<(Foo const& a, Foo const& b) +{ + return a.x < b.x; +} +constexpr inline bool operator<(Foo const& a, int b) +{ + return a.x < b; +} +constexpr inline bool operator<(int a, Foo const& b) +{ + return a < b.x; } -#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES +TEST_CASE("frozen::set heterogeneous container", "[set]") +{ + constexpr std::array std_array { { { 1 }, { 2 }, { 3 } } }; + constexpr Foo c_array[] { { 1 }, { 2 }, { 3 } }; + constexpr auto from_std_array = frozen::make_set(std_array); + constexpr auto from_c_array = frozen::make_set(c_array); + + REQUIRE(from_std_array == from_c_array); + for (const auto& set: { from_std_array, from_c_array }) + { + REQUIRE(set.find(1) != set.end()); + REQUIRE(set.count(2) == 1); + REQUIRE(set.count(42) == 0); + } +} -TEST_CASE("frozen::set deduction guide", "[set]") { - constexpr frozen::set integersSet{1,2,3,4,5}; - static_assert(std::is_same< - std::remove_cv_t, - frozen::set>::value, "wrong type deduced"); +TEST_CASE("frozen::set deduction guide", "[set]") +{ + constexpr frozen::set integersSet { 1, 2, 3, 4, 5 }; + static_assert(std::is_same, frozen::set>::value, + "wrong type deduced"); } -#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES +TEST_CASE("frozen::set default heterogeneous lookup", "[set]") +{ + // std::less<> is now the default comparator, enabling heterogeneous lookup + constexpr frozen::set long_set { 10L, 20L, 30L }; + + // Lookup with int in a set — works because std::less<> is transparent + REQUIRE(long_set.count(20) == 1); + REQUIRE(long_set.count(99) == 0); + REQUIRE(long_set.find(10) != long_set.end()); + REQUIRE(long_set.find(99) == long_set.end()); + REQUIRE(long_set.contains(30)); + REQUIRE(!long_set.contains(42)); +} diff --git a/tests/test_str.cpp b/tests/test_str.cpp index 551e050..ca1815d 100644 --- a/tests/test_str.cpp +++ b/tests/test_str.cpp @@ -1,13 +1,10 @@ -#include #include -#include +#include #include - -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW +#include #include -#include #include -#endif +#include #include "bench.hpp" #include "catch.hpp" @@ -15,282 +12,303 @@ using namespace frozen::string_literals; using namespace std::literals; -template -void test_string_view() { -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW - constexpr auto strings = []() -> std::tuple< - const Char(&)[12], frozen::basic_string, std::basic_string_view, - const Char(&)[23], frozen::basic_string, std::basic_string_view - > { - if constexpr (std::is_same_v) { - return { - "Let it go !", "Let it go !"_s, "Let it go !"sv, - "Let it go, let it go !", "Let it go, let it go !"_s, "Let it go, let it go !"sv - }; - } else if constexpr (std::is_same_v) { - return { - L"Let it go !", L"Let it go !"_s, L"Let it go !"sv, - L"Let it go, let it go !", L"Let it go, let it go !"_s, L"Let it go, let it go !"sv - }; - } else if constexpr (std::is_same_v) { - return { - u"Let it go !", u"Let it go !"_s, u"Let it go !"sv, - u"Let it go, let it go !", u"Let it go, let it go !"_s, u"Let it go, let it go !"sv - }; - } else if constexpr (std::is_same_v) { - return { - U"Let it go !", U"Let it go !"_s, U"Let it go !"sv, - U"Let it go, let it go !", U"Let it go, let it go !"_s, U"Let it go, let it go !"sv - }; +template +void test_string_view() +{ + constexpr auto strings = []() + -> std::tuple, std::basic_string_view, + const Char(&)[23], std::basic_string_view, std::basic_string_view> + { + if constexpr (std::is_same_v) + { + return { "Let it go !", + "Let it go !"_s, + "Let it go !"sv, + "Let it go, let it go !", + "Let it go, let it go !"_s, + "Let it go, let it go !"sv }; + } + else if constexpr (std::is_same_v) + { + return { L"Let it go !", + L"Let it go !"_s, + L"Let it go !"sv, + L"Let it go, let it go !", + L"Let it go, let it go !"_s, + L"Let it go, let it go !"sv }; + } + else if constexpr (std::is_same_v) + { + return { u"Let it go !", + u"Let it go !"_s, + u"Let it go !"sv, + u"Let it go, let it go !", + u"Let it go, let it go !"_s, + u"Let it go, let it go !"sv }; + } + else if constexpr (std::is_same_v) + { + return { U"Let it go !", + U"Let it go !"_s, + U"Let it go !"sv, + U"Let it go, let it go !", + U"Let it go, let it go !"_s, + U"Let it go, let it go !"sv }; + } + else if constexpr (std::is_same_v) + { + return { u8"Let it go !", + u8"Let it go !"_s, + u8"Let it go !"sv, + u8"Let it go, let it go !", + u8"Let it go, let it go !"_s, + u8"Let it go, let it go !"sv }; + } + }(); + + const auto [letitgo, letitgo_s, letitgo_sv, letitgoletitgo, letitgoletitgo_s, + letitgoletitgo_sv] = strings; + + { + std::basic_string_view letItGo = letitgo_sv; + REQUIRE(letItGo == std::basic_string_view(letitgo, 11)); + REQUIRE(letItGo == letitgo_s); + REQUIRE(letItGo == letitgo_sv); + + letItGo = letitgoletitgo_sv; + REQUIRE(letItGo == std::basic_string_view(letitgoletitgo, 22)); + REQUIRE(letItGo == letitgoletitgo_s); + REQUIRE(letItGo == letitgoletitgo_sv); } -#ifdef FROZEN_LETITGO_HAS_CHAR8T - else if constexpr (std::is_same_v) { - return { - u8"Let it go !", u8"Let it go !"_s, u8"Let it go !"sv, - u8"Let it go, let it go !", u8"Let it go, let it go !"_s, u8"Let it go, let it go !"sv - }; + + { + constexpr std::basic_string_view letItGo = std::get<2>(strings); + static_assert(letItGo == std::get<1>(strings), "frozen::string constexpr literal"); + static_assert(letItGo == std::get<2>(strings), "frozen::string constexpr string view"); } -#endif - }(); - - const auto [ - letitgo, - letitgo_s, - letitgo_sv, - letitgoletitgo, - letitgoletitgo_s, - letitgoletitgo_sv - ] = strings; - - { - frozen::basic_string letItGo = letitgo_sv; - REQUIRE(letItGo == letitgo); - REQUIRE(letItGo == letitgo_s); - REQUIRE(letItGo == letitgo_sv); - - letItGo = letitgoletitgo_sv; - REQUIRE(letItGo == letitgoletitgo); - REQUIRE(letItGo == letitgoletitgo_s); - REQUIRE(letItGo == letitgoletitgo_sv); - } - - { - constexpr frozen::basic_string letItGo = std::get<2>(strings); - static_assert(letItGo == std::get<0>(strings), "frozen::string constexpr"); - static_assert(letItGo == std::get<1>(strings), "frozen::string constexpr literal"); - static_assert(letItGo == std::get<2>(strings), "frozen::string constexpr string view"); - } -#endif } -TEST_CASE("Various string operation", "[string]") { - { - frozen::string letItGo = "Let it go !"; - REQUIRE(letItGo == "Let it go !"); - REQUIRE(letItGo == "Let it go !"_s); - - letItGo = "Let it go, let it go !"; - REQUIRE(letItGo == "Let it go, let it go !"); - REQUIRE(letItGo == "Let it go, let it go !"_s); - } - - { - static const char seed[] = "hello"; - std::string ref(seed); - constexpr frozen::string obj(seed); - REQUIRE(ref.size() == obj.size()); - REQUIRE(ref.length() == obj.length()); - REQUIRE(ref[1] == obj[1]); - REQUIRE((ref =="h") == (obj == "h")); - REQUIRE((ref !="h") == (obj != "h")); - REQUIRE((ref > "h") == (obj > "h")); - REQUIRE((ref >= "h") == (obj >= "h")); - REQUIRE((ref < "h") == (obj < "h")); - REQUIRE((ref <= "h") == (obj <= "h")); -#ifdef FROZEN_LETITGO_HAS_STRING_VIEW - REQUIRE((std::string_view)ref == (std::string_view)obj); -#endif - } - - { - constexpr frozen::string letItGo = "Let it go !"; - static_assert(letItGo == "Let it go !", "frozen::string constexpr"); - static_assert(letItGo == "Let it go !"_s, "frozen::string constexpr literal"); - } - - test_string_view(); +TEST_CASE("Various string operation", "[string]") +{ + { + frozen::string letItGo = "Let it go !"; + REQUIRE(letItGo == "Let it go !"); + REQUIRE(letItGo == "Let it go !"_s); + + letItGo = "Let it go, let it go !"; + REQUIRE(letItGo == "Let it go, let it go !"); + REQUIRE(letItGo == "Let it go, let it go !"_s); + } + + { + static const char seed[] = "hello"; + std::string ref(seed); + constexpr frozen::string obj(seed, 5); + REQUIRE(ref.size() == obj.size()); + REQUIRE(ref.length() == obj.length()); + REQUIRE(ref[1] == obj[1]); + REQUIRE((ref == "h") == (obj == "h")); + REQUIRE((ref != "h") == (obj != "h")); + REQUIRE((ref > "h") == (obj > "h")); + REQUIRE((ref >= "h") == (obj >= "h")); + REQUIRE((ref < "h") == (obj < "h")); + REQUIRE((ref <= "h") == (obj <= "h")); + REQUIRE(std::string_view(ref) == obj); + } + + { + constexpr frozen::string letItGo = "Let it go !"; + static_assert(letItGo == "Let it go !", "frozen::string constexpr"); + static_assert(letItGo == "Let it go !"_s, "frozen::string constexpr literal"); + } + + test_string_view(); } -TEST_CASE("Various wstring operation", "[string]") { - { - frozen::wstring letItGo = L"Let it go !"; - REQUIRE(letItGo == L"Let it go !"); - REQUIRE(letItGo == L"Let it go !"_s); +TEST_CASE("Various wstring operation", "[string]") +{ + { + frozen::wstring letItGo = L"Let it go !"; + REQUIRE(letItGo == L"Let it go !"); + REQUIRE(letItGo == L"Let it go !"_s); - letItGo = L"Let it go, let it go !"; - REQUIRE(letItGo == L"Let it go, let it go !"); - REQUIRE(letItGo == L"Let it go, let it go !"_s); - } + letItGo = L"Let it go, let it go !"; + REQUIRE(letItGo == L"Let it go, let it go !"); + REQUIRE(letItGo == L"Let it go, let it go !"_s); + } - { - constexpr frozen::wstring letItGo = L"Let it go !"; - static_assert(letItGo == L"Let it go !", "frozen::wstring constexpr"); - static_assert(letItGo == L"Let it go !"_s, "frozen::wstring constexpr literal"); - } + { + constexpr frozen::wstring letItGo = L"Let it go !"; + static_assert(letItGo == L"Let it go !", "frozen::wstring constexpr"); + static_assert(letItGo == L"Let it go !"_s, "frozen::wstring constexpr literal"); + } - test_string_view(); + test_string_view(); } -TEST_CASE("Various u16string operation", "[string]") { - { - frozen::u16string letItGo = u"Let it go !"; - REQUIRE(letItGo == u"Let it go !"); - REQUIRE(letItGo == u"Let it go !"_s); +TEST_CASE("Various u16string operation", "[string]") +{ + { + frozen::u16string letItGo = u"Let it go !"; + REQUIRE(letItGo == u"Let it go !"); + REQUIRE(letItGo == u"Let it go !"_s); - letItGo = u"Let it go, let it go !"; - REQUIRE(letItGo == u"Let it go, let it go !"); - REQUIRE(letItGo == u"Let it go, let it go !"_s); - } + letItGo = u"Let it go, let it go !"; + REQUIRE(letItGo == u"Let it go, let it go !"); + REQUIRE(letItGo == u"Let it go, let it go !"_s); + } - { - constexpr frozen::u16string letItGo = u"Let it go !"; - static_assert(letItGo == u"Let it go !", "frozen::u16string constexpr"); - static_assert(letItGo == u"Let it go !"_s, "frozen::u16string constexpr literal"); - } + { + constexpr frozen::u16string letItGo = u"Let it go !"; + static_assert(letItGo == u"Let it go !", "frozen::u16string constexpr"); + static_assert(letItGo == u"Let it go !"_s, "frozen::u16string constexpr literal"); + } - test_string_view(); + test_string_view(); } -TEST_CASE("Various u32string operation", "[string]") { - { - frozen::u32string letItGo = U"Let it go !"; - REQUIRE(letItGo == U"Let it go !"); - REQUIRE(letItGo == U"Let it go !"_s); +TEST_CASE("Various u32string operation", "[string]") +{ + { + frozen::u32string letItGo = U"Let it go !"; + REQUIRE(letItGo == U"Let it go !"); + REQUIRE(letItGo == U"Let it go !"_s); - letItGo = U"Let it go, let it go !"; - REQUIRE(letItGo == U"Let it go, let it go !"); - REQUIRE(letItGo == U"Let it go, let it go !"_s); - } + letItGo = U"Let it go, let it go !"; + REQUIRE(letItGo == U"Let it go, let it go !"); + REQUIRE(letItGo == U"Let it go, let it go !"_s); + } - { - constexpr frozen::u32string letItGo = U"Let it go !"; - static_assert(letItGo == U"Let it go !", "frozen::u32string constexpr"); - static_assert(letItGo == U"Let it go !"_s, "frozen::u32string constexpr literal"); - } + { + constexpr frozen::u32string letItGo = U"Let it go !"; + static_assert(letItGo == U"Let it go !", "frozen::u32string constexpr"); + static_assert(letItGo == U"Let it go !"_s, "frozen::u32string constexpr literal"); + } - test_string_view(); + test_string_view(); } -#ifdef FROZEN_LETITGO_HAS_CHAR8T -TEST_CASE("Various u8string operation", "[string]") { - { - frozen::u8string letItGo = u8"Let it go !"; - REQUIRE(letItGo == u8"Let it go !"); - REQUIRE(letItGo == u8"Let it go !"_s); - - letItGo = u8"Let it go, let it go !"; - REQUIRE(letItGo == u8"Let it go, let it go !"); - REQUIRE(letItGo == u8"Let it go, let it go !"_s); - } - - { - constexpr frozen::u8string letItGo = u8"Let it go !"; - static_assert(letItGo == u8"Let it go !", "frozen::u8string constexpr"); - static_assert(letItGo == u8"Let it go !"_s, "frozen::u8string constexpr literal"); - } - - test_string_view(); +TEST_CASE("Various u8string operation", "[string]") +{ + { + frozen::u8string letItGo = u8"Let it go !"; + REQUIRE(letItGo == u8"Let it go !"); + REQUIRE(letItGo == u8"Let it go !"_s); + + letItGo = u8"Let it go, let it go !"; + REQUIRE(letItGo == u8"Let it go, let it go !"); + REQUIRE(letItGo == u8"Let it go, let it go !"_s); + } + + { + constexpr frozen::u8string letItGo = u8"Let it go !"; + static_assert(letItGo == u8"Let it go !", "frozen::u8string constexpr"); + static_assert(letItGo == u8"Let it go !"_s, "frozen::u8string constexpr literal"); + } + + test_string_view(); } -#endif - -TEST_CASE("Knuth-Morris-Pratt str search", "[str-search]") { - - { - std::string haystack = "n"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("n")); - REQUIRE(index == haystack.begin()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("nn")); - REQUIRE(std::distance(haystack.begin(), index) == 2); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("mm")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("n*")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("nnnn")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("nmnn*")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "ABC ABCDAB ABCDABCDABDE"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_knuth_morris_pratt_searcher("ABCDABD")); - REQUIRE(std::distance(haystack.begin(), index) == 15); - } +TEST_CASE("Knuth-Morris-Pratt str search", "[str-search]") +{ + { + std::string haystack = "n"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("n")); + REQUIRE(index == haystack.begin()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("nn")); + REQUIRE(std::distance(haystack.begin(), index) == 2); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("mm")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("n*")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("nnnn")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("nmnn*")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "ABC ABCDAB ABCDABCDABDE"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_knuth_morris_pratt_searcher("ABCDABD")); + REQUIRE(std::distance(haystack.begin(), index) == 15); + } } -TEST_CASE("Boyer-Moore str search", "[str-search]") { - - { - std::string haystack = "n"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("n")); - REQUIRE(index == haystack.begin()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("nn")); - REQUIRE(std::distance(haystack.begin(), index) == 2); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("mm")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("n*")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("nnnn")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "nmnn"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("nmnn*")); - REQUIRE(index == haystack.end()); - } - - { - std::string haystack = "ABC ABCDAB ABCDABCDABDE"; - auto index = frozen::search(haystack.begin(), haystack.end(), frozen::make_boyer_moore_searcher("ABCDABD")); - REQUIRE(std::distance(haystack.begin(), index) == 15); - } +TEST_CASE("Boyer-Moore str search", "[str-search]") +{ + { + std::string haystack = "n"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("n")); + REQUIRE(index == haystack.begin()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("nn")); + REQUIRE(std::distance(haystack.begin(), index) == 2); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("mm")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("n*")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("nnnn")); + REQUIRE(index == haystack.end()); + } + { + std::string haystack = "nmnn"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("nmnn*")); + REQUIRE(index == haystack.end()); + } + + { + std::string haystack = "ABC ABCDAB ABCDABCDABDE"; + auto index = frozen::search(haystack.begin(), haystack.end(), + frozen::make_boyer_moore_searcher("ABCDABD")); + REQUIRE(std::distance(haystack.begin(), index) == 15); + } } diff --git a/tests/test_str_set.cpp b/tests/test_str_set.cpp index cae019d..2f745cc 100644 --- a/tests/test_str_set.cpp +++ b/tests/test_str_set.cpp @@ -1,82 +1,77 @@ -#include +#include #include +#include #include #include -#include +#include #include "bench.hpp" #include "catch.hpp" -TEST_CASE("tripleton int frozen ordered set", "[set]") { - constexpr frozen::set ze_set{"1", "2", "3"}; +TEST_CASE("tripleton int frozen ordered set", "[set]") +{ + constexpr frozen::set ze_set { "1", "2", "3" }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 3); + constexpr auto size = ze_set.size(); + REQUIRE(size == 3); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 3); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 3); - constexpr auto nocount = ze_set.count("4"); - REQUIRE(nocount == 0); + constexpr auto nocount = ze_set.count("4"); + REQUIRE(nocount == 0); - constexpr auto count = ze_set.count("1"); - REQUIRE(count == 1); + constexpr auto count = ze_set.count("1"); + REQUIRE(count == 1); - auto notfound = ze_set.find("4"); - REQUIRE(notfound == ze_set.end()); + auto notfound = ze_set.find("4"); + REQUIRE(notfound == ze_set.end()); - auto found = ze_set.find("1"); - REQUIRE(found == ze_set.begin()); + auto found = ze_set.find("1"); + REQUIRE(found == ze_set.begin()); - auto range = ze_set.equal_range("1"); - REQUIRE(std::get<0>(range) != ze_set.end()); + auto range = ze_set.equal_range("1"); + REQUIRE(std::get<0>(range) != ze_set.end()); - auto begin = ze_set.begin(), end = ze_set.end(); - REQUIRE(begin != end); + auto begin = ze_set.begin(), end = ze_set.end(); + REQUIRE(begin != end); - auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin != cend); + auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin != cend); - std::for_each(ze_set.begin(), ze_set.end(), [](frozen::string const &) {}); + std::for_each(ze_set.begin(), ze_set.end(), [](frozen::string const&) {}); } -TEST_CASE("frozen::set <> std::set", - "[set]") { -#define INIT_SEQ \ - "19", "1", "2", "4", "5", "6", "7", "8", "9", "10", \ - "11", "111", "1112", "1115", "1118", "1110", "1977", \ - "177", "277", "477", "577", "677", "777", "877", "977", \ - "1077", "1177", "11177", "111277", "111577", "111877", \ - "111077", "1999", "199", "299", "499", "599", "699", \ - "799", "899", "999", "1099", "1199", "11199", "111299", \ - "111599", "111899", "111099", "197799", "17799", "27799", \ - "47799", "57799", "67799", "77799", "87799", "97799", \ - "107799", "117799", "1117799", "11127799", "11157799", \ - "11187799", "11107799", "1988", "188", "288", "488", \ - "588", "688", "788", "888", "988", "1088", "1188", \ - "11188", "111288", "111588", "111888", "111088", "197788", \ - "17788", "27788", "47788", "57788", "67788", "77788", \ - "87788", "97788", "107788", "117788", "1117788", "11127788", \ - "11157788", "11187788", "11107788", "199988", "19988", \ - "29988", "49988", "59988", "69988", "79988", "89988", \ - "99988", "109988", "119988", "1119988", "11129988", \ - "11159988", "11189988", "11109988", "19779988", "1779988", \ - "2779988", "4779988", "5779988", "6779988", "7779988", \ - "8779988", "9779988", "10779988", "11779988", "111779988", \ - "1112779988", "1115779988", "1118779988", "1110779988" - - const std::set std_set = {INIT_SEQ}; - constexpr frozen::set frozen_set = {INIT_SEQ}; - SECTION("checking size and content") { - REQUIRE(std_set.size() == frozen_set.size()); - for (auto v : std_set) - REQUIRE(frozen_set.count(v)); - - for (auto v : frozen_set) - REQUIRE(std_set.count(v)); - } - +TEST_CASE("frozen::set <> std::set", "[set]") +{ +#define INIT_SEQ \ + "19", "1", "2", "4", "5", "6", "7", "8", "9", "10", "11", "111", "1112", "1115", "1118", \ + "1110", "1977", "177", "277", "477", "577", "677", "777", "877", "977", "1077", "1177", \ + "11177", "111277", "111577", "111877", "111077", "1999", "199", "299", "499", "599", \ + "699", "799", "899", "999", "1099", "1199", "11199", "111299", "111599", "111899", \ + "111099", "197799", "17799", "27799", "47799", "57799", "67799", "77799", "87799", \ + "97799", "107799", "117799", "1117799", "11127799", "11157799", "11187799", "11107799", \ + "1988", "188", "288", "488", "588", "688", "788", "888", "988", "1088", "1188", "11188", \ + "111288", "111588", "111888", "111088", "197788", "17788", "27788", "47788", "57788", \ + "67788", "77788", "87788", "97788", "107788", "117788", "1117788", "11127788", "11157788", \ + "11187788", "11107788", "199988", "19988", "29988", "49988", "59988", "69988", "79988", \ + "89988", "99988", "109988", "119988", "1119988", "11129988", "11159988", "11189988", \ + "11109988", "19779988", "1779988", "2779988", "4779988", "5779988", "6779988", "7779988", \ + "8779988", "9779988", "10779988", "11779988", "111779988", "1112779988", "1115779988", \ + "1118779988", "1110779988" + + const std::set std_set = { INIT_SEQ }; + constexpr frozen::set frozen_set = { INIT_SEQ }; + SECTION("checking size and content") + { + REQUIRE(std_set.size() == frozen_set.size()); + for (auto v: std_set) + REQUIRE(frozen_set.count(v)); + + for (auto v: frozen_set) + REQUIRE(std_set.count(v)); + } } diff --git a/tests/test_unordered_map.cpp b/tests/test_unordered_map.cpp index ccc4471..95d495c 100644 --- a/tests/test_unordered_map.cpp +++ b/tests/test_unordered_map.cpp @@ -1,277 +1,303 @@ -#include -#include #include +#include +#include #include -#include #include +#include -#include #include +#include #include "bench.hpp" #include "catch.hpp" -TEST_CASE("singleton frozen unordered map", "[unordered map]") { - constexpr frozen::unordered_map ze_map{{1, 2.}}; +TEST_CASE("singleton frozen unordered map", "[unordered map]") +{ + constexpr frozen::unordered_map ze_map { { 1, 2. } }; - constexpr auto empty = ze_map.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_map.empty(); + REQUIRE(!empty); - constexpr auto size = ze_map.size(); - REQUIRE(size == 1); + constexpr auto size = ze_map.size(); + REQUIRE(size == 1); - constexpr auto max_size = ze_map.max_size(); - REQUIRE(max_size == 1); + constexpr auto max_size = ze_map.max_size(); + REQUIRE(max_size == 1); - constexpr auto nocount = ze_map.count(3); - REQUIRE(nocount == 0); + constexpr auto nocount = ze_map.count(3); + REQUIRE(nocount == 0); - constexpr auto count = ze_map.count(1); - REQUIRE(count == 1); + constexpr auto count = ze_map.count(1); + REQUIRE(count == 1); - constexpr auto find = ze_map.at(1); - REQUIRE(find == 2.); + constexpr auto find = ze_map.at(1); + REQUIRE(find == 2.); - auto notfound = ze_map.find(3); - REQUIRE(notfound == ze_map.end()); + auto notfound = ze_map.find(3); + REQUIRE(notfound == ze_map.end()); - auto found = ze_map.find(1); - REQUIRE(found == ze_map.begin()); + auto found = ze_map.find(1); + REQUIRE(found == ze_map.begin()); - auto range = ze_map.equal_range(1); - REQUIRE(std::get<0>(range) == ze_map.begin()); - REQUIRE(std::get<1>(range) == ze_map.end()); + auto range = ze_map.equal_range(1); + REQUIRE(std::get<0>(range) == ze_map.begin()); + REQUIRE(std::get<1>(range) == ze_map.end()); - auto begin = ze_map.begin(), end = ze_map.end(); - REQUIRE(begin != end); + auto begin = ze_map.begin(), end = ze_map.end(); + REQUIRE(begin != end); - // auto constexpr key_hash = ze_map.hash_function(); - // auto constexpr key_hashed = key_hash(1); - // REQUIRE(key_hashed); + // auto constexpr key_hash = ze_map.hash_function(); + // auto constexpr key_hashed = key_hash(1); + // REQUIRE(key_hashed); - auto constexpr key_eq = ze_map.key_eq(); - auto constexpr value_comparison = key_eq(11, 11); - REQUIRE(value_comparison); + auto constexpr key_eq = ze_map.key_eq(); + auto constexpr value_comparison = key_eq(11, 11); + REQUIRE(value_comparison); - auto cbegin = ze_map.cbegin(), cend = ze_map.cend(); - REQUIRE(cbegin < cend); + auto cbegin = ze_map.cbegin(), cend = ze_map.cend(); + REQUIRE(cbegin < cend); - std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); + std::for_each(ze_map.begin(), ze_map.end(), [](std::tuple) {}); - static_assert(std::is_same::value, ""); - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); } // This is mainly a test that construction does not time out -TEST_CASE("frozen::unordered_map powers of two", "[unordered_map]") { - constexpr frozen::unordered_map frozen_map = { - {1, 0}, {2, 1}, {4, 2}, {8, 3}, {16, 4}, {32, 5}, {64, 6}, {128, 7}, - {256, 8}, {512, 9}, {1024, 10}, {2048, 11}, {4096, 12}, {8192, 13} - }; - - for (const auto & pair : frozen_map) { - REQUIRE(pair.first == (1 << pair.second)); - } +TEST_CASE("frozen::unordered_map powers of two", "[unordered_map]") +{ + constexpr frozen::unordered_map frozen_map = { + { 1, 0 }, { 2, 1 }, { 4, 2 }, { 8, 3 }, { 16, 4 }, { 32, 5 }, { 64, 6 }, + { 128, 7 }, { 256, 8 }, { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 }, { 8192, 13 } + }; + + for (const auto& pair: frozen_map) + { + REQUIRE(pair.first == (1 << pair.second)); + } } // This is also mainly a test that construction does not time out #define M64(X) { X * 64, X } -TEST_CASE("frozen::unordered_map multiples of 64", "[unordered_map]") { - constexpr frozen::unordered_map frozen_map = { - M64(0), M64(1), M64(2), M64(3), M64(4), M64(5), M64(6), M64(7), M64(8), - M64(9), M64(10), M64(11), M64(12), M64(13), M64(14), M64(15), M64(16), - M64(17), M64(18), M64(19), M64(20), M64(21), M64(22), M64(23), M64(24), - M64(25), M64(26), M64(27), M64(28), M64(29), M64(30), M64(31), M64(32), - M64(33), M64(34), M64(35), M64(36), M64(37), M64(38), M64(39), M64(40), - M64(41), M64(42), M64(43), M64(44), M64(45), M64(46), M64(47), M64(48), - M64(49), M64(50), M64(51), M64(52), M64(53), M64(54), M64(55), M64(56), - }; - -# undef M64 - - for (const auto & pair : frozen_map) { - REQUIRE(pair.first == 64 * pair.second); - } +TEST_CASE("frozen::unordered_map multiples of 64", "[unordered_map]") +{ + constexpr frozen::unordered_map frozen_map = { + M64(0), M64(1), M64(2), M64(3), M64(4), M64(5), M64(6), M64(7), M64(8), M64(9), + M64(10), M64(11), M64(12), M64(13), M64(14), M64(15), M64(16), M64(17), M64(18), M64(19), + M64(20), M64(21), M64(22), M64(23), M64(24), M64(25), M64(26), M64(27), M64(28), M64(29), + M64(30), M64(31), M64(32), M64(33), M64(34), M64(35), M64(36), M64(37), M64(38), M64(39), + M64(40), M64(41), M64(42), M64(43), M64(44), M64(45), M64(46), M64(47), M64(48), M64(49), + M64(50), M64(51), M64(52), M64(53), M64(54), M64(55), M64(56), + }; + +#undef M64 + + for (const auto& pair: frozen_map) + { + REQUIRE(pair.first == 64 * pair.second); + } } -TEST_CASE("frozen::unordered_map <> std::unordered_map", "[unordered_map]") { -#define INIT_SEQ \ - {19, 12}, {1, 12}, {2, 12}, {4, 12}, {5, 12}, {6, 12}, {7, 12}, {8, 12}, \ - {9, 12}, {10, 12}, {11, 12}, {111, 12}, {1112, 12}, {1115, 12}, \ - {1118, 12}, {1110, 12}, {1977, 12}, {177, 12}, {277, 12}, {477, 12}, \ - {577, 12}, {677, 12}, {777, 12}, {877, 12}, {977, 12}, {1077, 12}, \ - {1177, 12}, {11177, 12}, {111277, 12}, {111577, 12}, {111877, 12}, \ - {111077, 12}, {1999, 12}, {199, 12}, {299, 12}, {499, 12}, {599, 12}, \ - {699, 12}, {799, 12}, {899, 12}, {999, 12}, {1099, 12}, {1199, 12}, \ - {11199, 12}, {111299, 12}, {111599, 12}, {111899, 12}, {111099, 12}, \ - {197799, 12}, {17799, 12}, {27799, 12}, {47799, 12}, {57799, 12}, \ - {67799, 12}, {77799, 12}, {87799, 12}, {97799, 12}, {107799, 12}, \ - {117799, 12}, {1117799, 12}, {11127799, 12}, {11157799, 12}, \ - {11187799, 12}, {11107799, 12}, {1988, 12}, {188, 12}, {288, 12}, \ - {488, 12}, {588, 12}, {688, 12}, {788, 12}, {888, 12}, {988, 12}, \ - {1088, 12}, {1188, 12}, {11188, 12}, {111288, 12}, {111588, 12}, \ - {111888, 12}, {111088, 12}, {197788, 12}, {17788, 12}, {27788, 12}, \ - {47788, 12}, {57788, 12}, {67788, 12}, {77788, 12}, {87788, 12}, \ - {97788, 12}, {107788, 12}, {117788, 12}, {1117788, 12}, \ - {11127788, 12}, {11157788, 12}, {11187788, 12}, {11107788, 12}, \ - {199988, 12}, {19988, 12}, {29988, 12}, {49988, 12}, {59988, 12}, \ - {69988, 12}, {79988, 12}, {89988, 12}, {99988, 12}, {109988, 12}, \ - {119988, 12}, {1119988, 12}, {11129988, 12}, {11159988, 12}, \ - {11189988, 12}, {11109988, 12}, {19779988, 12}, {1779988, 12}, \ - {2779988, 12}, {4779988, 12}, {5779988, 12}, {6779988, 12}, \ - {7779988, 12}, {8779988, 12}, {9779988, 12}, {10779988, 12}, \ - {11779988, 12}, {111779988, 12}, {1112779988, 12}, {1115779988, 12}, \ - {1118779988, 12}, { \ - 1110779988, 13 \ +TEST_CASE("frozen::unordered_map <> std::unordered_map", "[unordered_map]") +{ +#define INIT_SEQ \ + { 19, 12 }, { 1, 12 }, { 2, 12 }, { 4, 12 }, { 5, 12 }, { 6, 12 }, { 7, 12 }, { 8, 12 }, \ + { 9, 12 }, { 10, 12 }, { 11, 12 }, { 111, 12 }, { 1112, 12 }, { 1115, 12 }, { 1118, 12 }, \ + { 1110, 12 }, { 1977, 12 }, { 177, 12 }, { 277, 12 }, { 477, 12 }, { 577, 12 }, \ + { 677, 12 }, { 777, 12 }, { 877, 12 }, { 977, 12 }, { 1077, 12 }, { 1177, 12 }, \ + { 11177, 12 }, { 111277, 12 }, { 111577, 12 }, { 111877, 12 }, { 111077, 12 }, \ + { 1999, 12 }, { 199, 12 }, { 299, 12 }, { 499, 12 }, { 599, 12 }, { 699, 12 }, \ + { 799, 12 }, { 899, 12 }, { 999, 12 }, { 1099, 12 }, { 1199, 12 }, { 11199, 12 }, \ + { 111299, 12 }, { 111599, 12 }, { 111899, 12 }, { 111099, 12 }, { 197799, 12 }, \ + { 17799, 12 }, { 27799, 12 }, { 47799, 12 }, { 57799, 12 }, { 67799, 12 }, { 77799, 12 }, \ + { 87799, 12 }, { 97799, 12 }, { 107799, 12 }, { 117799, 12 }, { 1117799, 12 }, \ + { 11127799, 12 }, { 11157799, 12 }, { 11187799, 12 }, { 11107799, 12 }, { 1988, 12 }, \ + { 188, 12 }, { 288, 12 }, { 488, 12 }, { 588, 12 }, { 688, 12 }, { 788, 12 }, { 888, 12 }, \ + { 988, 12 }, { 1088, 12 }, { 1188, 12 }, { 11188, 12 }, { 111288, 12 }, { 111588, 12 }, \ + { 111888, 12 }, { 111088, 12 }, { 197788, 12 }, { 17788, 12 }, { 27788, 12 }, \ + { 47788, 12 }, { 57788, 12 }, { 67788, 12 }, { 77788, 12 }, { 87788, 12 }, { 97788, 12 }, \ + { 107788, 12 }, { 117788, 12 }, { 1117788, 12 }, { 11127788, 12 }, { 11157788, 12 }, \ + { 11187788, 12 }, { 11107788, 12 }, { 199988, 12 }, { 19988, 12 }, { 29988, 12 }, \ + { 49988, 12 }, { 59988, 12 }, { 69988, 12 }, { 79988, 12 }, { 89988, 12 }, { 99988, 12 }, \ + { 109988, 12 }, { 119988, 12 }, { 1119988, 12 }, { 11129988, 12 }, { 11159988, 12 }, \ + { 11189988, 12 }, { 11109988, 12 }, { 19779988, 12 }, { 1779988, 12 }, { 2779988, 12 }, \ + { 4779988, 12 }, { 5779988, 12 }, { 6779988, 12 }, { 7779988, 12 }, { 8779988, 12 }, \ + { 9779988, 12 }, { 10779988, 12 }, { 11779988, 12 }, { 111779988, 12 }, \ + { 1112779988, 12 }, { 1115779988, 12 }, { 1118779988, 12 }, { 1110779988, 13 } + + const std::unordered_map std_map = { INIT_SEQ }; + constexpr frozen::unordered_map frozen_map = { INIT_SEQ }; + SECTION("checking size and content") + { + REQUIRE(std_map.size() == frozen_map.size()); + for (auto v: std_map) + REQUIRE(frozen_map.count(std::get<0>(v))); + for (auto v: frozen_map) + REQUIRE(std_map.count(std::get<0>(v))); } - const std::unordered_map std_map = { INIT_SEQ }; - constexpr frozen::unordered_map frozen_map = { INIT_SEQ }; - SECTION("checking size and content") { - REQUIRE(std_map.size() == frozen_map.size()); - for (auto v : std_map) - REQUIRE(frozen_map.count(std::get<0>(v))); - for (auto v : frozen_map) - REQUIRE(std_map.count(std::get<0>(v))); - } - - static_assert(std::is_same::value, ""); - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, + ""); + static_assert(std::is_same::value, + ""); } -TEST_CASE("frozen::unordered_map <> frozen::make_unordered_map", "[unordered_map]") { - constexpr frozen::unordered_map frozen_map = { INIT_SEQ }; - constexpr auto frozen_map2 = frozen::make_unordered_map({INIT_SEQ}); - constexpr auto frozen_map3 = frozen::make_unordered_map(std::array, 128>{{INIT_SEQ}}); - REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); - - SECTION("checking size and content") { - REQUIRE(frozen_map.size() == frozen_map2.size()); - for (auto v : frozen_map2) - REQUIRE(frozen_map.count(std::get<0>(v))); - for (auto v : frozen_map) - REQUIRE(frozen_map2.count(std::get<0>(v))); - } +TEST_CASE("frozen::unordered_map <> frozen::make_unordered_map", "[unordered_map]") +{ + constexpr frozen::unordered_map frozen_map = { INIT_SEQ }; + constexpr auto frozen_map2 = frozen::make_unordered_map({ INIT_SEQ }); + constexpr auto frozen_map3 = + frozen::make_unordered_map(std::array, 128> { { INIT_SEQ } }); + REQUIRE(std::equal(frozen_map2.begin(), frozen_map2.end(), frozen_map3.begin())); + + SECTION("checking size and content") + { + REQUIRE(frozen_map.size() == frozen_map2.size()); + for (auto v: frozen_map2) + REQUIRE(frozen_map.count(std::get<0>(v))); + for (auto v: frozen_map) + REQUIRE(frozen_map2.count(std::get<0>(v))); + } } -TEST_CASE("frozen::unordered_map <> std::unordered_map /small", "[unordered_map]") { -#define INIT_SEQ_SMALL \ -{"0", 0},{"1", 1},{"2", 2},{"3", 3},{"5", 4},{"8", 5},{"9", 6},{"A", 7},{"W", 8},{"X", 9},{"r", 10},{"y", 11},{"BF", 12},{"AP", 13} - - const std::unordered_map std_map = { INIT_SEQ_SMALL }; - constexpr frozen::unordered_map frozen_map = { INIT_SEQ_SMALL }; - SECTION("checking size and content") { - REQUIRE(std_map.size() == frozen_map.size()); - for (auto v : std_map) - REQUIRE(frozen_map.count(std::get<0>(v))); - for (auto v : frozen_map) - REQUIRE(std_map.count(std::get<0>(v))); - } - - static_assert(std::is_same::value, ""); - static_assert(std::is_same::value, ""); +TEST_CASE("frozen::unordered_map <> std::unordered_map /small", "[unordered_map]") +{ +#define INIT_SEQ_SMALL \ + { "0", 0 }, { "1", 1 }, { "2", 2 }, { "3", 3 }, { "5", 4 }, { "8", 5 }, { "9", 6 }, \ + { "A", 7 }, { "W", 8 }, { "X", 9 }, { "r", 10 }, { "y", 11 }, { "BF", 12 }, { "AP", 13 } + + const std::unordered_map std_map = { INIT_SEQ_SMALL }; + constexpr frozen::unordered_map frozen_map = { INIT_SEQ_SMALL }; + SECTION("checking size and content") + { + REQUIRE(std_map.size() == frozen_map.size()); + for (auto v: std_map) + REQUIRE(frozen_map.count(std::get<0>(v))); + for (auto v: frozen_map) + REQUIRE(std_map.count(std::get<0>(v))); + } + + static_assert(std::is_same::value, + ""); + static_assert(std::is_same::value, + ""); } -TEST_CASE("frozen::unordered_map constexpr", "[unordered_map]") { - constexpr frozen::unordered_map ce = {{3,4}, {11,12}}; - static_assert(ce.begin() +2 == ce.end(), ""); - static_assert(ce.size() == 2, ""); - static_assert(ce.count(3), ""); - static_assert(!ce.count(0), ""); - static_assert(ce.find(0) == ce.end(), ""); - static_assert(ce.contains(3), ""); - static_assert(!ce.contains(0), ""); +TEST_CASE("frozen::unordered_map constexpr", "[unordered_map]") +{ + constexpr frozen::unordered_map ce = { { 3, 4 }, { 11, 12 } }; + static_assert(ce.begin() + 2 == ce.end(), ""); + static_assert(ce.size() == 2, ""); + static_assert(ce.count(3), ""); + static_assert(!ce.count(0), ""); + static_assert(ce.find(0) == ce.end(), ""); + static_assert(ce.contains(3), ""); + static_assert(!ce.contains(0), ""); } -TEST_CASE("access", "[unordered_map]") { - constexpr frozen::unordered_map ce = {{3,4}, {11,12}}; - REQUIRE(4 == ce.at(3)); - REQUIRE_THROWS(ce.at(33)); +TEST_CASE("access", "[unordered_map]") +{ + constexpr frozen::unordered_map ce = { { 3, 4 }, { 11, 12 } }; + REQUIRE(4 == ce.at(3)); + REQUIRE_THROWS(ce.at(33)); } -TEST_CASE("Modifiable frozen::unordered_map", "[unordered_map]") { - frozen::unordered_map frozen_map = {{0, 1}, {2, 3}, {4, 5}}; +TEST_CASE("Modifiable frozen::unordered_map", "[unordered_map]") +{ + frozen::unordered_map frozen_map = { { 0, 1 }, { 2, 3 }, { 4, 5 } }; - SECTION("Lookup existing values") { - REQUIRE(frozen_map.at(0) == 1); - REQUIRE(frozen_map.find(0)->second == 1); - REQUIRE(frozen_map.equal_range(0).first->second == 1); + SECTION("Lookup existing values") + { + REQUIRE(frozen_map.at(0) == 1); + REQUIRE(frozen_map.find(0)->second == 1); + REQUIRE(frozen_map.equal_range(0).first->second == 1); - REQUIRE(frozen_map.at(2) == 3); - REQUIRE(frozen_map.find(2)->second == 3); - REQUIRE(frozen_map.equal_range(2).first->second == 3); + REQUIRE(frozen_map.at(2) == 3); + REQUIRE(frozen_map.find(2)->second == 3); + REQUIRE(frozen_map.equal_range(2).first->second == 3); - REQUIRE(frozen_map.at(4) == 5); - REQUIRE(frozen_map.find(4)->second == 5); - REQUIRE(frozen_map.equal_range(4).first->second == 5); - } + REQUIRE(frozen_map.at(4) == 5); + REQUIRE(frozen_map.find(4)->second == 5); + REQUIRE(frozen_map.equal_range(4).first->second == 5); + } - SECTION("Lookup failure") { - REQUIRE(frozen_map.find(5) == frozen_map.end()); - REQUIRE_THROWS_AS(frozen_map.at(5), std::out_of_range); - } + SECTION("Lookup failure") + { + REQUIRE(frozen_map.find(5) == frozen_map.end()); + REQUIRE_THROWS_AS(frozen_map.at(5), std::out_of_range); + } - SECTION("Modify value") { - frozen_map.at(0) = -1; - REQUIRE(frozen_map.at(0) == -1); + SECTION("Modify value") + { + frozen_map.at(0) = -1; + REQUIRE(frozen_map.at(0) == -1); - frozen_map.begin()->second = -2; - REQUIRE(frozen_map.begin()->second == -2); + frozen_map.begin()->second = -2; + REQUIRE(frozen_map.begin()->second == -2); - (frozen_map.end() - 1)->second = -3; - REQUIRE((frozen_map.end() - 1)->second == -3); + (frozen_map.end() - 1)->second = -3; + REQUIRE((frozen_map.end() - 1)->second == -3); - frozen_map.equal_range(4).first->second = -5; - REQUIRE(frozen_map.at(4) == -5); - } + frozen_map.equal_range(4).first->second = -5; + REQUIRE(frozen_map.at(4) == -5); + } } -struct eq { - template - constexpr auto operator()(const frozen::string &frozen, const StrTy &str) const { - return frozen == frozen::string{str.data(), str.size()}; - } +struct eq +{ + template + constexpr auto operator()(const frozen::string& frozen, const StrTy& str) const + { + return frozen == frozen::string { str.data(), str.size() }; + } }; -TEST_CASE("frozen::unordered_map heterogeneous container", "[unordered_map]") { - constexpr auto map = frozen::make_unordered_map( - {{"one", 1}, {"two", 2}, {"three", 3}}, - frozen::elsa<>{}, eq{}); +TEST_CASE("frozen::unordered_map heterogeneous container", "[unordered_map]") +{ + constexpr auto map = frozen::make_unordered_map( + { { "one", 1 }, { "two", 2 }, { "three", 3 } }, frozen::elsa<> {}, eq {}); - REQUIRE(map.find(std::string{"two"})->second == 2); - REQUIRE(map.find(frozen::string{"two"})->second == 2); + REQUIRE(map.find(std::string { "two" })->second == 2); + REQUIRE(map.find(frozen::string { "two" })->second == 2); } - // Dummy structure to check/showcase transparent comparison -struct case_insensitive { - const char data[3]; - case_insensitive(const char (&s)[4]) : data{s[0], s[1], s[2]} {} - - friend bool operator==(frozen::string self, case_insensitive other) { - return std::equal(self.begin(), self.end(), other.data, other.data + 3, - [](char s, char o) { return std::tolower(s) == std::tolower(o);}); - } +struct case_insensitive +{ + const char data[3]; + case_insensitive(const char (&s)[4]) : data { s[0], s[1], s[2] } {} + + friend bool operator==(frozen::string self, case_insensitive other) + { + return std::equal(self.begin(), self.end(), other.data, other.data + 3, + [](char s, char o) + { + return std::tolower(s) == std::tolower(o); + }); + } }; template <> -struct frozen::elsa { - constexpr std::size_t operator()(case_insensitive const &value, std::size_t seed) const { - char tmp[3]; - for(size_t i = 0; i < 3; ++i) - tmp[i] = tolower(value.data[i]); - return frozen::elsa<>()(frozen::string(tmp, 3), seed); - } +struct frozen::elsa +{ + constexpr std::size_t operator()(case_insensitive const& value, std::size_t seed) const + { + char tmp[3]; + for (size_t i = 0; i < 3; ++i) + tmp[i] = tolower(value.data[i]); + return frozen::elsa<>()(frozen::string(tmp, 3), seed); + } }; -TEST_CASE("frozen::unordered_map transparent container", "[unordered_map]") { - constexpr frozen::unordered_map, std::equal_to<>> map = {{"one", 1}}; +TEST_CASE("frozen::unordered_map transparent container", "[unordered_map]") +{ + constexpr frozen::unordered_map> map = { { "one", 1 } }; - REQUIRE(map.find(case_insensitive("OnE"))->second == 1); - REQUIRE(map.count(case_insensitive("One")) == 1); - REQUIRE(map.count(case_insensitive("TwO")) == 0); - REQUIRE(map.count(case_insensitive("333")) == 0); + REQUIRE(map.find(case_insensitive("OnE"))->second == 1); + REQUIRE(map.count(case_insensitive("One")) == 1); + REQUIRE(map.count(case_insensitive("TwO")) == 0); + REQUIRE(map.count(case_insensitive("333")) == 0); } diff --git a/tests/test_unordered_map_str.cpp b/tests/test_unordered_map_str.cpp index 35bb9db..1586612 100644 --- a/tests/test_unordered_map_str.cpp +++ b/tests/test_unordered_map_str.cpp @@ -1,88 +1,78 @@ +#include #include #include #include +#include #include #include "bench.hpp" #include "catch.hpp" -TEST_CASE("frozen::unordered_map <> std::unordered_map", - "[unordered_map]") { -#define INIT_SEQ \ - {"19", 19}, {"1", 1}, {"2", 2}, {"4", 4}, {"5", 5}, {"6", 6}, \ - {"7", 7}, {"8", 8}, {"9", 9}, {"10", 10}, {"11", 11}, \ - {"111", 111}, {"1112", 1112}, {"1115", 1115}, {"1118", 1118}, \ - {"1110", 1110}, {"1977", 1977}, {"177", 177}, {"277", 277}, \ - {"477", 477}, {"577", 577}, {"677", 677}, {"777", 777}, \ - {"877", 877}, {"977", 977}, {"1077", 1077}, {"1177", 1177}, \ - {"11177", 11177}, {"111277", 111277}, {"111577", 111577}, \ - {"111877", 111877}, {"111077", 111077}, {"1999", 1999}, \ - {"199", 199}, {"299", 299}, {"499", 499}, {"599", 599}, \ - {"699", 699}, {"799", 799}, {"899", 899}, {"999", 999}, \ - {"1099", 1099}, {"1199", 1199}, {"11199", 11199}, \ - {"111299", 111299}, {"111599", 111599}, {"111899", 111899}, \ - {"111099", 111099}, {"197799", 197799}, {"17799", 17799}, \ - {"27799", 27799}, {"47799", 47799}, {"57799", 57799}, \ - {"67799", 67799}, {"77799", 77799}, {"87799", 87799}, \ - {"97799", 97799}, {"107799", 107799}, {"117799", 117799}, \ - {"1117799", 1117799}, {"11127799", 11127799}, \ - {"11157799", 11157799}, {"11187799", 11187799}, \ - {"11107799", 11107799}, {"1988", 1988}, {"188", 188}, \ - {"288", 288}, {"488", 488}, {"588", 588}, {"688", 688}, \ - {"788", 788}, {"888", 888}, {"988", 988}, {"1088", 1088}, \ - {"1188", 1188}, {"11188", 11188}, {"111288", 111288}, \ - {"111588", 111588}, {"111888", 111888}, {"111088", 111088}, \ - {"197788", 197788}, {"17788", 17788}, {"27788", 27788}, \ - {"47788", 47788}, {"57788", 57788}, {"67788", 67788}, \ - {"77788", 77788}, {"87788", 87788}, {"97788", 97788}, \ - {"107788", 107788}, {"117788", 117788}, {"1117788", 1117788}, \ - {"11127788", 11127788}, {"11157788", 11157788}, \ - {"11187788", 11187788}, {"11107788", 11107788}, \ - {"199988", 199988}, {"19988", 19988}, {"29988", 29988}, \ - {"49988", 49988}, {"59988", 59988}, {"69988", 69988}, \ - {"79988", 79988}, {"89988", 89988}, {"99988", 99988}, \ - {"109988", 109988}, {"119988", 119988}, {"1119988", 1119988}, \ - {"11129988", 11129988}, {"11159988", 11159988}, \ - {"11189988", 11189988}, {"11109988", 11109988}, \ - {"19779988", 19779988}, {"1779988", 1779988}, \ - {"2779988", 2779988}, {"4779988", 4779988}, {"5779988", 5779988}, \ - {"6779988", 6779988}, {"7779988", 7779988}, {"8779988", 8779988}, \ - {"9779988", 9779988}, {"10779988", 10779988}, \ - {"11779988", 11779988}, {"111779988", 111779988}, \ - {"1112779988", 1112779988}, {"1115779988", 1115779988}, \ - {"1118779988", 1118779988}, { \ - "1110779988", 1110779988 \ - } +TEST_CASE("frozen::unordered_map <> std::unordered_map", "[unordered_map]") +{ +#define INIT_SEQ \ + { "19", 19 }, { "1", 1 }, { "2", 2 }, { "4", 4 }, { "5", 5 }, { "6", 6 }, { "7", 7 }, \ + { "8", 8 }, { "9", 9 }, { "10", 10 }, { "11", 11 }, { "111", 111 }, { "1112", 1112 }, \ + { "1115", 1115 }, { "1118", 1118 }, { "1110", 1110 }, { "1977", 1977 }, { "177", 177 }, \ + { "277", 277 }, { "477", 477 }, { "577", 577 }, { "677", 677 }, { "777", 777 }, \ + { "877", 877 }, { "977", 977 }, { "1077", 1077 }, { "1177", 1177 }, { "11177", 11177 }, \ + { "111277", 111277 }, { "111577", 111577 }, { "111877", 111877 }, { "111077", 111077 }, \ + { "1999", 1999 }, { "199", 199 }, { "299", 299 }, { "499", 499 }, { "599", 599 }, \ + { "699", 699 }, { "799", 799 }, { "899", 899 }, { "999", 999 }, { "1099", 1099 }, \ + { "1199", 1199 }, { "11199", 11199 }, { "111299", 111299 }, { "111599", 111599 }, \ + { "111899", 111899 }, { "111099", 111099 }, { "197799", 197799 }, { "17799", 17799 }, \ + { "27799", 27799 }, { "47799", 47799 }, { "57799", 57799 }, { "67799", 67799 }, \ + { "77799", 77799 }, { "87799", 87799 }, { "97799", 97799 }, { "107799", 107799 }, \ + { "117799", 117799 }, { "1117799", 1117799 }, { "11127799", 11127799 }, \ + { "11157799", 11157799 }, { "11187799", 11187799 }, { "11107799", 11107799 }, \ + { "1988", 1988 }, { "188", 188 }, { "288", 288 }, { "488", 488 }, { "588", 588 }, \ + { "688", 688 }, { "788", 788 }, { "888", 888 }, { "988", 988 }, { "1088", 1088 }, \ + { "1188", 1188 }, { "11188", 11188 }, { "111288", 111288 }, { "111588", 111588 }, \ + { "111888", 111888 }, { "111088", 111088 }, { "197788", 197788 }, { "17788", 17788 }, \ + { "27788", 27788 }, { "47788", 47788 }, { "57788", 57788 }, { "67788", 67788 }, \ + { "77788", 77788 }, { "87788", 87788 }, { "97788", 97788 }, { "107788", 107788 }, \ + { "117788", 117788 }, { "1117788", 1117788 }, { "11127788", 11127788 }, \ + { "11157788", 11157788 }, { "11187788", 11187788 }, { "11107788", 11107788 }, \ + { "199988", 199988 }, { "19988", 19988 }, { "29988", 29988 }, { "49988", 49988 }, \ + { "59988", 59988 }, { "69988", 69988 }, { "79988", 79988 }, { "89988", 89988 }, \ + { "99988", 99988 }, { "109988", 109988 }, { "119988", 119988 }, { "1119988", 1119988 }, \ + { "11129988", 11129988 }, { "11159988", 11159988 }, { "11189988", 11189988 }, \ + { "11109988", 11109988 }, { "19779988", 19779988 }, { "1779988", 1779988 }, \ + { "2779988", 2779988 }, { "4779988", 4779988 }, { "5779988", 5779988 }, \ + { "6779988", 6779988 }, { "7779988", 7779988 }, { "8779988", 8779988 }, \ + { "9779988", 9779988 }, { "10779988", 10779988 }, { "11779988", 11779988 }, \ + { "111779988", 111779988 }, { "1112779988", 1112779988 }, { "1115779988", 1115779988 }, \ + { "1118779988", 1118779988 }, { "1110779988", 1110779988 } - const std::unordered_map std_map = {INIT_SEQ}; - constexpr frozen::unordered_map frozen_map = { - INIT_SEQ}; - REQUIRE(std_map.size() == frozen_map.size()); - - SECTION("checking size and content") { + const std::unordered_map std_map = { INIT_SEQ }; + constexpr frozen::unordered_map frozen_map = { INIT_SEQ }; REQUIRE(std_map.size() == frozen_map.size()); - for (auto v : std_map) - REQUIRE(frozen_map.count(v.first)); - for (auto v : frozen_map) - REQUIRE(std_map.count(v.first)); - } + SECTION("checking size and content") + { + REQUIRE(std_map.size() == frozen_map.size()); + for (auto v: std_map) + REQUIRE(frozen_map.count(v.first)); + for (auto v: frozen_map) + REQUIRE(std_map.count(v.first)); + } } -TEST_CASE("various frozen::unordered_map config", "[unordered_map]") { - constexpr frozen::unordered_map olaf0 = { - {"19", 19}, - {"31", 31}, - }; - constexpr frozen::unordered_map olaf1 = { - {"19", 19}, - {"31", 31}, - {"coucou", 10}, - {"hello my world!!", 1000}, - {"nice damn shit", 14}, - {"pouet", 11}, - }; - (void)olaf0; - (void)olaf1; +TEST_CASE("various frozen::unordered_map config", "[unordered_map]") +{ + constexpr frozen::unordered_map olaf0 = { + { "19", 19 }, + { "31", 31 }, + }; + constexpr frozen::unordered_map olaf1 = { + { "19", 19 }, + { "31", 31 }, + { "coucou", 10 }, + { "hello my world!!", 1000 }, + { "nice damn shit", 14 }, + { "pouet", 11 }, + }; + (void) olaf0; + (void) olaf1; } diff --git a/tests/test_unordered_set.cpp b/tests/test_unordered_set.cpp index 7d86350..d5d8e35 100644 --- a/tests/test_unordered_set.cpp +++ b/tests/test_unordered_set.cpp @@ -1,200 +1,206 @@ +#include #include #include -#include #include -#include #include +#include #include "bench.hpp" #include "catch.hpp" -TEST_CASE("singleton frozen unordered set", "[unordered set]") { - constexpr frozen::unordered_set ze_set{1}; +TEST_CASE("singleton frozen unordered set", "[unordered set]") +{ + constexpr frozen::unordered_set ze_set { 1 }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 1); + constexpr auto size = ze_set.size(); + REQUIRE(size == 1); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 1); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 1); - constexpr auto nocount = ze_set.count(3); - REQUIRE(nocount == 0); + constexpr auto nocount = ze_set.count(3); + REQUIRE(nocount == 0); - constexpr auto count = ze_set.count(1); - REQUIRE(count == 1); + constexpr auto count = ze_set.count(1); + REQUIRE(count == 1); - auto notfound = ze_set.find(3); - REQUIRE(notfound == ze_set.end()); + auto notfound = ze_set.find(3); + REQUIRE(notfound == ze_set.end()); - auto found = ze_set.find(1); - REQUIRE(found == ze_set.begin()); + auto found = ze_set.find(1); + REQUIRE(found == ze_set.begin()); - auto range = ze_set.equal_range(1); - REQUIRE(std::get<0>(range) == ze_set.begin()); - REQUIRE(std::get<1>(range) == ze_set.end()); + auto range = ze_set.equal_range(1); + REQUIRE(std::get<0>(range) == ze_set.begin()); + REQUIRE(std::get<1>(range) == ze_set.end()); - auto begin = ze_set.begin(), end = ze_set.end(); - REQUIRE(begin != end); + auto begin = ze_set.begin(), end = ze_set.end(); + REQUIRE(begin != end); - // auto constexpr key_hash = ze_set.hash_function(); - // auto constexpr key_hashed = key_hash(1); - // REQUIRE(key_hashed); + // auto constexpr key_hash = ze_set.hash_function(); + // auto constexpr key_hashed = key_hash(1); + // REQUIRE(key_hashed); - auto constexpr key_eq = ze_set.key_eq(); - auto constexpr value_comparison = key_eq(11, 11); - REQUIRE(value_comparison); + auto constexpr key_eq = ze_set.key_eq(); + auto constexpr value_comparison = key_eq(11, 11); + REQUIRE(value_comparison); - auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin < cend); + auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin < cend); - std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); + std::for_each(ze_set.begin(), ze_set.end(), [](int) {}); } -TEST_CASE("tripleton str frozen unordered set", "[unordered set]") { - constexpr frozen::unordered_set ze_set{1, 2, 3}; +TEST_CASE("tripleton str frozen unordered set", "[unordered set]") +{ + constexpr frozen::unordered_set ze_set { 1, 2, 3 }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 3); + constexpr auto size = ze_set.size(); + REQUIRE(size == 3); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 3); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 3); - const auto nocount = ze_set.count(4); - REQUIRE(nocount == 0); + const auto nocount = ze_set.count(4); + REQUIRE(nocount == 0); - constexpr auto count = ze_set.count(1); - REQUIRE(count == 1); + constexpr auto count = ze_set.count(1); + REQUIRE(count == 1); - auto notfound = ze_set.find(4); - REQUIRE(notfound == ze_set.end()); + auto notfound = ze_set.find(4); + REQUIRE(notfound == ze_set.end()); - auto found = ze_set.find(1); - REQUIRE(found == ze_set.begin()); + auto found = ze_set.find(1); + REQUIRE(found == ze_set.begin()); - auto range = ze_set.equal_range(1); - REQUIRE(std::get<0>(range) != ze_set.end()); + auto range = ze_set.equal_range(1); + REQUIRE(std::get<0>(range) != ze_set.end()); - auto begin = ze_set.begin(), end = ze_set.end(); - REQUIRE(begin != end); + auto begin = ze_set.begin(), end = ze_set.end(); + REQUIRE(begin != end); - // auto constexpr key_hash = ze_set.hash_function(); - // auto constexpr key_hashed = key_hash(1); - // REQUIRE(key_hashed); + // auto constexpr key_hash = ze_set.hash_function(); + // auto constexpr key_hashed = key_hash(1); + // REQUIRE(key_hashed); - auto constexpr key_eq = ze_set.key_eq(); - auto constexpr value_comparison = key_eq(11, 11); - REQUIRE(value_comparison); + auto constexpr key_eq = ze_set.key_eq(); + auto constexpr value_comparison = key_eq(11, 11); + REQUIRE(value_comparison); - auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin != cend); + auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin != cend); - std::for_each(ze_set.begin(), ze_set.end(), [](uint16_t const &) {}); + std::for_each(ze_set.begin(), ze_set.end(), [](uint16_t const&) {}); } -TEST_CASE("frozen::unordered_set <> std::unordered_set", - "[unordered_set]") { - -#define INIT_SEQ \ - 19, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 111, 1112, 1115, 1118, 1110, 1977, 177, \ - 277, 477, 577, 677, 777, 877, 977, 1077, 1177, 11177, 111277, 111577, \ - 111877, 111077, 1999, 199, 299, 499, 599, 699, 799, 899, 999, 1099, \ - 1199, 11199, 111299, 111599, 111899, 111099, 197799, 17799, 27799, \ - 47799, 57799, 67799, 77799, 87799, 97799, 107799, 117799, 1117799, \ - 11127799, 11157799, 11187799, 11107799, 1988, 188, 288, 488, 588, 688, \ - 788, 888, 988, 1088, 1188, 11188, 111288, 111588, 111888, 111088, \ - 197788, 17788, 27788, 47788, 57788, 67788, 77788, 87788, 97788, 107788, \ - 117788, 1117788, 11127788, 11157788, 11187788, 11107788, 199988, 19988, \ - 29988, 49988, 59988, 69988, 79988, 89988, 99988, 109988, 119988, \ - 1119988, 11129988, 11159988, 11189988, 11109988, 19779988, 1779988, \ - 2779988, 4779988, 5779988, 6779988, 7779988, 8779988, 9779988, 10779988, \ - 11779988, 111779988, 1112779988, 1115779988, 1118779988, 1110779988, 456 - - const std::unordered_set std_set = {INIT_SEQ}; - constexpr frozen::unordered_set frozen_set = {INIT_SEQ}; - { - REQUIRE(std_set.size() == frozen_set.size()); - for (auto v : std_set) - REQUIRE(frozen_set.count(v)); - for (auto v : frozen_set) - REQUIRE(std_set.count(v)); - } - +TEST_CASE("frozen::unordered_set <> std::unordered_set", "[unordered_set]") +{ +#define INIT_SEQ \ + 19, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 111, 1112, 1115, 1118, 1110, 1977, 177, 277, 477, 577, \ + 677, 777, 877, 977, 1077, 1177, 11177, 111277, 111577, 111877, 111077, 1999, 199, 299, \ + 499, 599, 699, 799, 899, 999, 1099, 1199, 11199, 111299, 111599, 111899, 111099, 197799, \ + 17799, 27799, 47799, 57799, 67799, 77799, 87799, 97799, 107799, 117799, 1117799, 11127799, \ + 11157799, 11187799, 11107799, 1988, 188, 288, 488, 588, 688, 788, 888, 988, 1088, 1188, \ + 11188, 111288, 111588, 111888, 111088, 197788, 17788, 27788, 47788, 57788, 67788, 77788, \ + 87788, 97788, 107788, 117788, 1117788, 11127788, 11157788, 11187788, 11107788, 199988, \ + 19988, 29988, 49988, 59988, 69988, 79988, 89988, 99988, 109988, 119988, 1119988, 11129988, \ + 11159988, 11189988, 11109988, 19779988, 1779988, 2779988, 4779988, 5779988, 6779988, \ + 7779988, 8779988, 9779988, 10779988, 11779988, 111779988, 1112779988, 1115779988, \ + 1118779988, 1110779988, 456 + + const std::unordered_set std_set = { INIT_SEQ }; + constexpr frozen::unordered_set frozen_set = { INIT_SEQ }; + { + REQUIRE(std_set.size() == frozen_set.size()); + for (auto v: std_set) + REQUIRE(frozen_set.count(v)); + for (auto v: frozen_set) + REQUIRE(std_set.count(v)); + } } -TEST_CASE("frozen::unordered_set with enum keys", "[unordered_set]") { - enum class some_enum { - A,B,C - }; - constexpr frozen::unordered_set frozen_set = { some_enum::A, some_enum::B }; - REQUIRE(frozen_set.count(some_enum::A) == 1); - REQUIRE(frozen_set.count(some_enum::C) == 0); +TEST_CASE("frozen::unordered_set with enum keys", "[unordered_set]") +{ + enum class some_enum + { + A, + B, + C + }; + constexpr frozen::unordered_set frozen_set = { some_enum::A, some_enum::B }; + REQUIRE(frozen_set.count(some_enum::A) == 1); + REQUIRE(frozen_set.count(some_enum::C) == 0); } -TEST_CASE("frozen::unordered_set <> frozen::make_unordered_set", "[unordered_set]") { - constexpr frozen::unordered_set from_ctor = { INIT_SEQ }; - constexpr int init_array[]{INIT_SEQ}; - constexpr auto from_c_array = frozen::make_unordered_set(init_array); - constexpr auto from_std_array = frozen::make_unordered_set(std::array{{INIT_SEQ}}); - REQUIRE(std::equal(from_c_array.begin(), from_c_array.end(), from_std_array.begin())); - - SECTION("checking size and content") { - REQUIRE(from_ctor.size() == from_c_array.size()); - for (auto v : from_c_array) - REQUIRE(from_ctor.count(v)); - for (auto v : from_ctor) - REQUIRE(from_c_array.count(v)); - } +TEST_CASE("frozen::unordered_set <> frozen::make_unordered_set", "[unordered_set]") +{ + constexpr frozen::unordered_set from_ctor = { INIT_SEQ }; + constexpr int init_array[] { INIT_SEQ }; + constexpr auto from_c_array = frozen::make_unordered_set(init_array); + constexpr auto from_std_array = + frozen::make_unordered_set(std::array { { INIT_SEQ } }); + REQUIRE(std::equal(from_c_array.begin(), from_c_array.end(), from_std_array.begin())); + + SECTION("checking size and content") + { + REQUIRE(from_ctor.size() == from_c_array.size()); + for (auto v: from_c_array) + REQUIRE(from_ctor.count(v)); + for (auto v: from_ctor) + REQUIRE(from_c_array.count(v)); + } } -TEST_CASE("frozen::unordered_set constexpr", "[unordered_set]") { - constexpr frozen::unordered_set ce = {3, 11}; - static_assert(ce.begin() +2 == ce.end(), ""); - static_assert(ce.size() == 2, ""); - static_assert(ce.count(3), ""); - static_assert(!ce.count(0), ""); - static_assert(ce.find(0) == ce.end(), ""); - static_assert(ce.contains(3), ""); - static_assert(!ce.contains(0), ""); +TEST_CASE("frozen::unordered_set constexpr", "[unordered_set]") +{ + constexpr frozen::unordered_set ce = { 3, 11 }; + static_assert(ce.begin() + 2 == ce.end(), ""); + static_assert(ce.size() == 2, ""); + static_assert(ce.count(3), ""); + static_assert(!ce.count(0), ""); + static_assert(ce.find(0) == ce.end(), ""); + static_assert(ce.contains(3), ""); + static_assert(!ce.contains(0), ""); } -#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES - -TEST_CASE("frozen::unordered_set deduction guide", "[unordered_set]") { - constexpr frozen::unordered_set integersSet{1,2,3,4,5}; - static_assert(std::is_same< - std::remove_cv_t, - frozen::unordered_set>::value, "wrong type deduced"); +TEST_CASE("frozen::unordered_set deduction guide", "[unordered_set]") +{ + constexpr frozen::unordered_set integersSet { 1, 2, 3, 4, 5 }; + static_assert( + std::is_same, frozen::unordered_set>::value, + "wrong type deduced"); } -#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES - -struct eq { - template - constexpr auto operator()(const frozen::string &frozen, const StrTy &str) const { - return frozen == frozen::string{str.data(), str.size()}; - } +struct eq +{ + template + constexpr auto operator()(const frozen::string& frozen, const StrTy& str) const + { + return frozen == frozen::string { str.data(), str.size() }; + } }; -TEST_CASE("frozen::unordered_set heterogeneous lookup", "[unordered_set]") { - using namespace frozen::string_literals; +TEST_CASE("frozen::unordered_set heterogeneous lookup", "[unordered_set]") +{ + using namespace frozen::string_literals; - constexpr frozen::unordered_set set{"one"_s, "two"_s, "three"_s}; + constexpr frozen::unordered_set set { "one"_s, "two"_s, "three"_s }; - REQUIRE(set.find(std::string{"two"}, frozen::elsa{}, eq{}) != set.end()); + REQUIRE(set.find(std::string { "two" }, frozen::elsa {}, eq {}) != set.end()); } -TEST_CASE("frozen::unordered_set heterogeneous container", "[unordered_set]") { - using namespace frozen::string_literals; +TEST_CASE("frozen::unordered_set heterogeneous container", "[unordered_set]") +{ + using namespace frozen::string_literals; - constexpr auto set = frozen::make_unordered_set( - {"one"_s, "two"_s, "three"_s}, - frozen::elsa<>{}, eq{}); + constexpr auto set = frozen::make_unordered_set({ "one"_s, "two"_s, "three"_s }, + frozen::elsa<> {}, eq {}); - REQUIRE(set.find(std::string{"two"}) != set.end()); - REQUIRE(set.find(frozen::string{"two"}) != set.end()); + REQUIRE(set.find(std::string { "two" }) != set.end()); + REQUIRE(set.find(frozen::string { "two" }) != set.end()); } diff --git a/tests/test_unordered_str_set.cpp b/tests/test_unordered_str_set.cpp index 840b976..838eef9 100644 --- a/tests/test_unordered_str_set.cpp +++ b/tests/test_unordered_str_set.cpp @@ -1,90 +1,87 @@ +#include +#include #include #include #include +#include #include -#include #include "bench.hpp" #include "catch.hpp" -TEST_CASE("tripleton int frozen unordered set", "[unordered set]") { - constexpr frozen::unordered_set ze_set{"1", "2", "3"}; +TEST_CASE("tripleton int frozen unordered set", "[unordered set]") +{ + constexpr frozen::unordered_set ze_set { "1", "2", "3" }; - constexpr auto empty = ze_set.empty(); - REQUIRE(!empty); + constexpr auto empty = ze_set.empty(); + REQUIRE(!empty); - constexpr auto size = ze_set.size(); - REQUIRE(size == 3); + constexpr auto size = ze_set.size(); + REQUIRE(size == 3); - constexpr auto max_size = ze_set.max_size(); - REQUIRE(max_size == 3); + constexpr auto max_size = ze_set.max_size(); + REQUIRE(max_size == 3); - constexpr auto nocount = ze_set.count("4"); - REQUIRE(nocount == 0); + constexpr auto nocount = ze_set.count("4"); + REQUIRE(nocount == 0); - constexpr auto count = ze_set.count("1"); - REQUIRE(count == 1); + constexpr auto count = ze_set.count("1"); + REQUIRE(count == 1); - auto notfound = ze_set.find("4"); - REQUIRE(notfound == ze_set.end()); + auto notfound = ze_set.find("4"); + REQUIRE(notfound == ze_set.end()); - auto found = ze_set.find("1"); - REQUIRE(found == ze_set.begin()); + auto found = ze_set.find("1"); + REQUIRE(found == ze_set.begin()); - auto range = ze_set.equal_range("1"); - REQUIRE(std::get<0>(range) != ze_set.end()); + auto range = ze_set.equal_range("1"); + REQUIRE(std::get<0>(range) != ze_set.end()); - auto begin = ze_set.begin(), end = ze_set.end(); - REQUIRE(begin != end); + auto begin = ze_set.begin(), end = ze_set.end(); + REQUIRE(begin != end); - auto constexpr key_hash = ze_set.hash_function(); - auto constexpr key_hashed = key_hash("1"); - REQUIRE(key_hashed); + auto constexpr key_hash = ze_set.hash_function(); + auto constexpr key_hashed = key_hash("1"); + REQUIRE(key_hashed); - auto constexpr key_eq = ze_set.key_eq(); - auto constexpr value_comparison = key_eq("11", "11"); - REQUIRE(value_comparison); + auto constexpr key_eq = ze_set.key_eq(); + using namespace std::string_view_literals; + auto constexpr value_comparison = key_eq("11"sv, "11"sv); + REQUIRE(value_comparison); - auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); - REQUIRE(cbegin != cend); + auto cbegin = ze_set.cbegin(), cend = ze_set.cend(); + REQUIRE(cbegin != cend); - std::for_each(ze_set.begin(), ze_set.end(), [](frozen::string const &) {}); + std::for_each(ze_set.begin(), ze_set.end(), [](frozen::string const&) {}); } -TEST_CASE("frozen::unordered_set <> std::unordered_set", - "[unordered_set]") { -#define INIT_SEQ \ - "19", "1", "2", "4", "5", "6", "7", "8", "9", "10", \ - "11", "111", "1112", "1115", "1118", "1110", "1977", \ - "177", "277", "477", "577", "677", "777", "877", "977", \ - "1077", "1177", "11177", "111277", "111577", "111877", \ - "111077", "1999", "199", "299", "499", "599", "699", \ - "799", "899", "999", "1099", "1199", "11199", "111299", \ - "111599", "111899", "111099", "197799", "17799", "27799", \ - "47799", "57799", "67799", "77799", "87799", "97799", \ - "107799", "117799", "1117799", "11127799", "11157799", \ - "11187799", "11107799", "1988", "188", "288", "488", \ - "588", "688", "788", "888", "988", "1088", "1188", \ - "11188", "111288", "111588", "111888", "111088", "197788", \ - "17788", "27788", "47788", "57788", "67788", "77788", \ - "87788", "97788", "107788", "117788", "1117788", "11127788", \ - "11157788", "11187788", "11107788", "199988", "19988", \ - "29988", "49988", "59988", "69988", "79988", "89988", \ - "99988", "109988", "119988", "1119988", "11129988", \ - "11159988", "11189988", "11109988", "19779988", "1779988", \ - "2779988", "4779988", "5779988", "6779988", "7779988", \ - "8779988", "9779988", "10779988", "11779988", "111779988", \ - "1112779988", "1115779988", "1118779988", "1110779988" - - const std::unordered_set std_set = {INIT_SEQ}; - constexpr frozen::unordered_set frozen_set = {INIT_SEQ}; - SECTION("checking size and content") { - REQUIRE(std_set.size() == frozen_set.size()); - for (auto v : std_set) - REQUIRE(frozen_set.count(v)); - - for (auto v : frozen_set) - REQUIRE(std_set.count(v)); - } - +TEST_CASE("frozen::unordered_set <> std::unordered_set", "[unordered_set]") +{ +#define INIT_SEQ \ + "19", "1", "2", "4", "5", "6", "7", "8", "9", "10", "11", "111", "1112", "1115", "1118", \ + "1110", "1977", "177", "277", "477", "577", "677", "777", "877", "977", "1077", "1177", \ + "11177", "111277", "111577", "111877", "111077", "1999", "199", "299", "499", "599", \ + "699", "799", "899", "999", "1099", "1199", "11199", "111299", "111599", "111899", \ + "111099", "197799", "17799", "27799", "47799", "57799", "67799", "77799", "87799", \ + "97799", "107799", "117799", "1117799", "11127799", "11157799", "11187799", "11107799", \ + "1988", "188", "288", "488", "588", "688", "788", "888", "988", "1088", "1188", "11188", \ + "111288", "111588", "111888", "111088", "197788", "17788", "27788", "47788", "57788", \ + "67788", "77788", "87788", "97788", "107788", "117788", "1117788", "11127788", "11157788", \ + "11187788", "11107788", "199988", "19988", "29988", "49988", "59988", "69988", "79988", \ + "89988", "99988", "109988", "119988", "1119988", "11129988", "11159988", "11189988", \ + "11109988", "19779988", "1779988", "2779988", "4779988", "5779988", "6779988", "7779988", \ + "8779988", "9779988", "10779988", "11779988", "111779988", "1112779988", "1115779988", \ + "1118779988", "1110779988" + + const std::unordered_set std_set = { INIT_SEQ }; + constexpr frozen::unordered_set frozen_set = { INIT_SEQ }; + SECTION("checking size and content") + { + REQUIRE(std_set.size() == frozen_set.size()); + for (auto v: std_set) + REQUIRE(frozen_set.count(v)); + + for (auto v: frozen_set) + REQUIRE(std_set.count(v)); + } }