├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── SConstruct ├── clean.sh └── src ├── SConscript ├── include ├── .gitignore ├── cx_algorithm.h ├── cx_array.h ├── cx_counter.h ├── cx_fnv1.h ├── cx_guid.h ├── cx_math.h ├── cx_md5.h ├── cx_murmur3.h ├── cx_numeric.h ├── cx_pcg32.h ├── cx_sha256.h ├── cx_strenc.h ├── cx_typeid.h └── cx_utils.h └── test ├── CMakeLists.txt ├── SConscript ├── cx_algorithm.cpp ├── cx_array.cpp ├── cx_counter.cpp ├── cx_guid.cpp ├── cx_hash.cpp ├── cx_math.cpp ├── cx_numeric.cpp ├── cx_pcg32.cpp ├── cx_strenc.cpp ├── cx_typeid.cpp ├── cx_utils.cpp └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .sconsign.dblite 2 | build/ 3 | export/ 4 | TAGS 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0.2) 2 | project (constexpr) 3 | include_directories ("${PROJECT_SOURCE_DIR}/src/include") 4 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 5 | 6 | set(MY_CXX_FLAGS "-pedantic \ 7 | -ffunction-sections \ 8 | -Wall \ 9 | -Wextra \ 10 | -Wcast-align \ 11 | -Wcast-qual \ 12 | -Wctor-dtor-privacy \ 13 | -Wdisabled-optimization \ 14 | -Wformat=2 \ 15 | -Winit-self \ 16 | -Wmissing-include-dirs \ 17 | -Wno-undefined-inline \ 18 | -Wno-undefined-internal \ 19 | -Wold-style-cast \ 20 | -Woverloaded-virtual \ 21 | -Wredundant-decls \ 22 | -Wshadow \ 23 | -Wsign-conversion \ 24 | -Wsign-promo \ 25 | -Wstrict-overflow=5 \ 26 | -Wswitch-default \ 27 | -Wundef \ 28 | -Werror") 29 | 30 | option(USE_CLANG "build application with clang" ON) 31 | 32 | if (USE_CLANG) 33 | SET (CMAKE_C_COMPILER "/usr/bin/clang") 34 | SET (CMAKE_C_FLAGS "-Wall -std=c99") 35 | SET (CMAKE_C_FLAGS_DEBUG "-g") 36 | SET (CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") 37 | SET (CMAKE_C_FLAGS_RELEASE "-O4 -DNDEBUG") 38 | SET (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") 39 | 40 | SET (CMAKE_CXX_COMPILER "/usr/bin/clang++") 41 | SET (CMAKE_CXX_FLAGS "${MY_CXX_FLAGS} -std=c++14 -stdlib=libc++") 42 | SET (CMAKE_CXX_FLAGS_DEBUG "-g") 43 | SET (CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") 44 | SET (CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG") 45 | SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") 46 | 47 | SET (CMAKE_AR "/usr/bin/llvm-ar") 48 | SET (CMAKE_LINKER "/usr/bin/llvm-ld") 49 | SET (CMAKE_NM "/usr/bin/llvm-nm") 50 | SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump") 51 | SET (CMAKE_RANLIB "/usr/bin/llvm-ranlib") 52 | else () 53 | SET (CMAKE_CXX_FLAGS "${MY_CXX_FLAGS} -std=c++14") 54 | endif () 55 | 56 | add_subdirectory (src/test) 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2016 Ben Deane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experiments with constexpr 2 | 3 | Everything (with the exception of noted functions) is written in C++11 constexpr 4 | style for maximum compatibility. 5 | 6 | All functions are within the `cx` namespace. 7 | 8 | This code is distributed under the MIT license. See LICENSE for details. 9 | 10 | ## Math functions 11 | 12 | * `abs`, `fabs` 13 | * `sqrt`, `cbrt`, `hypot` 14 | * `exp`, `log`, `log10`, `log2` 15 | * `sin`, `cos`, `tan` 16 | * `asin`, `acos`, `atan`, `atan2` 17 | * `floor`, `ceil`, `trunc`, `round` (long double variants require C++14) 18 | * `fmod`, `remainder` (long double variants require C++14) 19 | * `fmin`, `fmax`, `fdim` 20 | * `sinh`, `cosh`, `tanh` 21 | * `asinh`, `acosh`, `atanh` 22 | * `pow` 23 | * `erf` 24 | 25 | ## String hashing 26 | 27 | * `fnv1`, `fnv1a` 28 | * `murmur3_32` 29 | * `md5` 30 | * `sha256` 31 | 32 | ## Utility functions 33 | 34 | * `strlen` 35 | * `strcmp` 36 | * `endianswap` 37 | * `counter`: returns monotonically increasing integers with each call (within a given translation unit) 38 | 39 | ## Random number generation 40 | 41 | * `cx_pcg32`: a macro that returns a different random `uint32_t` with each call 42 | * `cx_guidgen`: a macro that generates a different random GUID with each call 43 | 44 | ## String encryption 45 | 46 | String encryption uses `std::make_index_sequence` therefore requires C++14. 47 | 48 | * `cx_make_encrypted_string`: a macro that encrypts a string literal, with a runtime conversion to plaintext `std::string` 49 | 50 | ## Arrays 51 | 52 | Arrays use `std::make_index_sequence` therefore require C++14. 53 | 54 | * `array`: a constexpr-friendly array type 55 | * `make_array`: create an `array` from e.g. a string literal 56 | * `transform`: like `std::transform` but works on constexpr `array`s 57 | * `reverse` 58 | * `sort`: an implementation of mergesort (stable) 59 | * `partition`: a stable partition (but use `count_if` to obtain the partition point) 60 | 61 | ## Algorithms (including Numeric Algorithms) 62 | 63 | * `accumulate`: like `std::accumulate` but works on constexpr `array`s 64 | 65 | Similarly: 66 | 67 | * `all_of`, `any_of`, `none_of` 68 | * `count`, `count_if` 69 | * `find`, `find_if`, find_if_not` 70 | * `equal`, `mismatch` 71 | * `find_first_of` 72 | * `adjacent_find` 73 | * `search`, `search_n` 74 | * `inner_product` 75 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | buildType = 'debug' 4 | 5 | include = '#build/$BUILDTYPE/include' 6 | lib = '#build/$BUILDTYPE/lib' 7 | 8 | env = Environment(BUILDTYPE = buildType, 9 | CPPPATH = [include], 10 | LIBPATH = [lib]) 11 | 12 | env.Append(CCFLAGS = "-g -std=c++14") 13 | env.Append(CCFLAGS = ["-pedantic" 14 | , "-ffunction-sections" 15 | , "-Wall" 16 | , "-Wextra" 17 | , "-Wcast-align" 18 | , "-Wcast-qual" 19 | , "-Wctor-dtor-privacy" 20 | , "-Wdisabled-optimization" 21 | , "-Wformat=2" 22 | , "-Winit-self" 23 | , "-Wno-undefined-inline" 24 | , "-Wno-undefined-internal" 25 | , "-Wmissing-include-dirs" 26 | , "-Wold-style-cast" 27 | , "-Woverloaded-virtual" 28 | , "-Wredundant-decls" 29 | , "-Wshadow" 30 | , "-Wsign-conversion" 31 | , "-Wsign-promo" 32 | , "-Wstrict-overflow=5" 33 | , "-Wswitch-default" 34 | , "-Wundef" 35 | , "-Werror"]) 36 | 37 | compiler = 'clang++' 38 | #compiler = 'g++' 39 | if 'CXX' in os.environ and os.environ['CXX']: 40 | compiler = os.environ['CXX'] 41 | env.Replace(CXX = compiler) 42 | 43 | if compiler[:5] == 'clang': 44 | env.Append(CCFLAGS = "-stdlib=libc++") 45 | env.Append(LINKFLAGS = "-lc++") 46 | else: 47 | env.Append(CCFLAGS = "-Wno-sign-conversion") 48 | 49 | env['PROJNAME'] = os.path.basename(Dir('.').srcnode().abspath) 50 | print env['PROJNAME'] 51 | 52 | Export('env') 53 | env.SConscript('src/SConscript', variant_dir='build/$BUILDTYPE') 54 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf ./build 4 | rm -rf ./export 5 | rm -f .sconsign.dblite 6 | -------------------------------------------------------------------------------- /src/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | env.SConscript('test/SConscript') 4 | -------------------------------------------------------------------------------- /src/include/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/constexpr/a98b1db39c909e0130d21d3910d4faf97035a625/src/include/.gitignore -------------------------------------------------------------------------------- /src/include/cx_algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | //---------------------------------------------------------------------------- 7 | // constexpr algorithms 8 | 9 | namespace cx 10 | { 11 | namespace err 12 | { 13 | namespace 14 | { 15 | extern const char* all_of_runtime_error; 16 | extern const char* any_of_runtime_error; 17 | extern const char* none_of_runtime_error; 18 | extern const char* count_runtime_error; 19 | extern const char* count_if_runtime_error; 20 | extern const char* find_runtime_error; 21 | extern const char* find_if_runtime_error; 22 | extern const char* find_if_not_runtime_error; 23 | extern const char* equal_runtime_error; 24 | extern const char* mismatch_runtime_error; 25 | extern const char* find_first_of_runtime_error; 26 | extern const char* adjacent_find_runtime_error; 27 | extern const char* search_runtime_error; 28 | extern const char* search_n_runtime_error; 29 | } 30 | } 31 | 32 | template 33 | constexpr size_t count(It first, It last, const T& value) 34 | { 35 | return first == last ? 0 : 36 | true ? (*first == value) + count(first+1, last, value) : 37 | throw err::count_runtime_error; 38 | } 39 | 40 | template 41 | constexpr size_t count_if(It first, It last, Pred p) 42 | { 43 | return first == last ? 0 : 44 | true ? p(*first) + count_if(first+1, last, p) : 45 | throw err::count_if_runtime_error; 46 | } 47 | 48 | template 49 | constexpr It find(It first, It last, const T& value) 50 | { 51 | return first == last || *first == value ? first : 52 | true ? find(first+1, last, value) : 53 | throw err::find_runtime_error; 54 | } 55 | 56 | template 57 | constexpr It find_if(It first, It last, Pred p) 58 | { 59 | return first == last || p(*first) ? first : 60 | true ? find_if(first+1, last, p) : 61 | throw err::find_if_runtime_error; 62 | } 63 | 64 | template 65 | constexpr It find_if_not(It first, It last, Pred p) 66 | { 67 | return first == last || !p(*first) ? first : 68 | true ? find_if_not(first+1, last, p) : 69 | throw err::find_if_not_runtime_error; 70 | } 71 | 72 | template< class It, class Pred> 73 | constexpr bool all_of(It first, It last, Pred p) 74 | { 75 | return true ? find_if_not(first, last, p) == last : 76 | throw err::all_of_runtime_error; 77 | } 78 | 79 | template< class It, class Pred > 80 | constexpr bool any_of(It first, It last, Pred p) 81 | { 82 | return true ? find_if(first, last, p) != last : 83 | throw err::any_of_runtime_error; 84 | } 85 | 86 | template< class It, class Pred > 87 | constexpr bool none_of(It first, It last, Pred p) 88 | { 89 | return true ? find_if(first, last, p) == last : 90 | throw err::none_of_runtime_error; 91 | } 92 | 93 | namespace detail 94 | { 95 | template 96 | constexpr bool equal(It1 first1, It1 last1, It2 first2) 97 | { 98 | return first1 == last1 ? true : 99 | *first1 != *first2 ? false : 100 | true ? equal(first1+1, last1, first2+1) : 101 | throw err::equal_runtime_error; 102 | } 103 | 104 | template 105 | constexpr bool equal(It1 first1, It1 last1, It2 first2, Pred p) 106 | { 107 | return first1 == last1 ? true : 108 | !p(*first1, *first2) ? false : 109 | true ? equal(first1+1, last1, first2+1, p) : 110 | throw err::equal_runtime_error; 111 | } 112 | } 113 | 114 | template 115 | constexpr bool equal(It1 first1, It1 last1, It2 first2) 116 | { 117 | return detail::equal(first1, last1, first2); 118 | } 119 | 120 | template 121 | constexpr bool equal(It1 first1, It1 last1, It2 first2, Pred p) 122 | { 123 | return detail::equal(first1, last1, first2, p); 124 | } 125 | 126 | template 127 | constexpr bool equal(It1 first1, It1 last1, It2 first2, It2 last2) 128 | { 129 | return (last1 - first1) != (last2 - first2) ? false : 130 | detail::equal(first1, last1, first2); 131 | } 132 | 133 | template 134 | constexpr bool equal(It1 first1, It1 last1, It2 first2, It2 last2, Pred p) 135 | { 136 | return (last1 - first1) != (last2 - first2) ? false : 137 | detail::equal(first1, last1, first2, p); 138 | } 139 | 140 | template 141 | struct pair 142 | { 143 | T1 first; 144 | T2 second; 145 | }; 146 | 147 | template 148 | constexpr pair mismatch(It1 first1, It1 last1, It2 first2) 149 | { 150 | return (first1 == last1 || *first1 != *first2) ? pair{ first1, first2 } : 151 | true ? mismatch(first1+1, last1, first2+1) : 152 | throw err::mismatch_runtime_error; 153 | } 154 | 155 | template 156 | constexpr pair mismatch(It1 first1, It1 last1, It2 first2, Pred p) 157 | { 158 | return (first1 == last1 || !p(*first1, *first2)) ? pair{ first1, first2 } : 159 | true ? mismatch(first1+1, last1, first2+1) : 160 | throw err::mismatch_runtime_error; 161 | } 162 | 163 | template 164 | constexpr pair mismatch(It1 first1, It1 last1, It2 first2, It2 last2) 165 | { 166 | return (first1 == last1 || first2 == last2 || *first1 != *first2) ? 167 | pair{ first1, first2 } : 168 | true ? mismatch(first1+1, last1, first2+1, last2) : 169 | throw err::mismatch_runtime_error; 170 | } 171 | 172 | template 173 | constexpr pair mismatch(It1 first1, It1 last1, It2 first2, It2 last2, Pred p) 174 | { 175 | return (first1 == last1 || first2 == last2 || !p(*first1, *first2)) ? 176 | pair{ first1, first2 } : 177 | true ? mismatch(first1+1, last1, first2+1, last2, p) : 178 | throw err::mismatch_runtime_error; 179 | } 180 | 181 | namespace detail 182 | { 183 | template ())> 184 | constexpr bool contains_match(It first, It last, T value, Pred p) 185 | { 186 | return first == last ? false : 187 | p(*first, value) || contains_match(first+1, last, value, p); 188 | } 189 | } 190 | 191 | template 192 | constexpr It1 find_first_of(It1 first1, It1 last1, 193 | It2 first2, It2 last2) 194 | { 195 | return first1 == last1 || find(first2, last2, *first1) != last2 ? first1 : 196 | true ? find_first_of(first1+1, last1, first2, last2) : 197 | throw err::find_first_of_runtime_error; 198 | } 199 | 200 | template 201 | constexpr It1 find_first_of(It1 first1, It1 last1, 202 | It2 first2, It2 last2, Pred p) 203 | { 204 | return first1 == last1 || detail::contains_match(first2, last2, *first1, p) ? first1 : 205 | true ? find_first_of(first1+1, last1, first2, last2, p) : 206 | throw err::find_first_of_runtime_error; 207 | } 208 | 209 | template 210 | constexpr It adjacent_find(It first, It last) 211 | { 212 | return last - first <= 1 ? last : 213 | *first == *(first + 1) ? first : 214 | true ? adjacent_find(first+1, last) : 215 | throw err::adjacent_find_runtime_error; 216 | } 217 | 218 | template 219 | constexpr It adjacent_find(It first, It last, Pred p) 220 | { 221 | return last - first <= 1 ? last : 222 | p(*first, *(first + 1)) ? first : 223 | true ? adjacent_find(first+1, last, p) : 224 | throw err::adjacent_find_runtime_error; 225 | } 226 | 227 | template 228 | constexpr It1 search(It1 first1, It2 last1, 229 | It2 first2, It2 last2) 230 | { 231 | return (last2 - first2 > last1 - first1) ? last1 : 232 | equal(first2, last2, first1) ? first1 : 233 | true ? search(first1+1, last1, first2, last2) : 234 | throw err::search_runtime_error; 235 | } 236 | 237 | template 238 | constexpr It1 search(It1 first1, It2 last1, 239 | It2 first2, It2 last2, Pred p) 240 | { 241 | return (last2 - first2 > last1 - first1) ? last1 : 242 | equal(first2, last2, first1, p) ? first1 : 243 | true ? search(first1+1, last1, first2, last2) : 244 | throw err::search_runtime_error; 245 | } 246 | 247 | namespace detail 248 | { 249 | template 250 | constexpr static It search_n(It first, It last, size_t count, 251 | const T& value, size_t sofar = 0) 252 | { 253 | return static_cast(count - sofar) > last - first ? last : 254 | sofar == count ? first - sofar : 255 | *first != value ? search_n(first+1, last, count, value) : 256 | search_n(first+1, last, count, value, sofar+1); 257 | } 258 | 259 | template 260 | constexpr static It search_np(It first, It last, size_t count, 261 | const T& value, Pred p, size_t sofar = 0) 262 | { 263 | return static_cast(count - sofar) > last - first ? last : 264 | sofar == count ? first - sofar : 265 | !p(*first, value) ? search_np(first+1, last, count, value, p) : 266 | search_np(first+1, last, count, value, p, sofar+1); 267 | } 268 | } 269 | 270 | template 271 | constexpr It search_n(It first, It last, size_t count, const T& value) 272 | { 273 | return true ? detail::search_n(first, last, count, value) : 274 | throw err::search_n_runtime_error; 275 | } 276 | 277 | template 278 | constexpr It search_n(It first, It last, size_t count, const T& value, Pred p) 279 | { 280 | return true ? detail::search_np(first, last, count, value, p) : 281 | throw err::search_n_runtime_error; 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/include/cx_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cx_algorithm.h" 9 | 10 | //---------------------------------------------------------------------------- 11 | // constexpr array 12 | 13 | namespace cx 14 | { 15 | namespace err 16 | { 17 | namespace 18 | { 19 | extern const char* array_runtime_error; 20 | extern const char* transform_runtime_error; 21 | extern const char* sort_runtime_error; 22 | extern const char* partition_runtime_error; 23 | extern const char* reverse_runtime_error; 24 | } 25 | } 26 | 27 | template 28 | class array 29 | { 30 | public: 31 | using const_iterator = const T* const; 32 | 33 | // default constructor 34 | constexpr array() {} 35 | 36 | // aggregate constructor 37 | template ::type> 39 | constexpr array(Es&&... e) 40 | : m_data { std::forward(e)... } 41 | {} 42 | 43 | // array constructor 44 | template ::type> 46 | constexpr array(const T(&a)[M]) 47 | : array{a, std::make_index_sequence()} 48 | {} 49 | 50 | // construct from pointer and index sequence 51 | template 52 | constexpr array(const T* p, std::index_sequence) 53 | : m_data { p[Is]... } 54 | {} 55 | 56 | // size, element access, begin, end 57 | constexpr size_t size() const { return N; } 58 | constexpr const T operator[](size_t n) const { return m_data[n]; } 59 | constexpr const_iterator begin() const { return &m_data[0]; } 60 | constexpr const_iterator cbegin() const { return &m_data[0]; } 61 | constexpr const_iterator end() const { return &m_data[N]; } 62 | constexpr const_iterator cend() const { return &m_data[N]; } 63 | 64 | // map a function over an array (or two) 65 | template 66 | constexpr auto map(F&& f) const -> array 67 | { 68 | return map(std::forward(f), std::make_index_sequence()); 69 | } 70 | 71 | template 72 | constexpr auto map(F&& f, const array& rhs) const 73 | -> array M ? M : N)> 74 | { 75 | return map(std::forward(f), rhs, std::make_index_sequence<(N > M ? M : N)>()); 76 | } 77 | 78 | // array comparison 79 | template 80 | constexpr bool less(const array& rhs) const 81 | { 82 | return less_r(rhs.begin(), rhs.end(), 0); 83 | } 84 | 85 | // push_back, push_front 86 | constexpr array push_back(const T& t) const 87 | { 88 | return push_back(t, std::make_index_sequence()); 89 | } 90 | 91 | constexpr array push_front(const T& t) const 92 | { 93 | return push_front(t, std::make_index_sequence()); 94 | } 95 | 96 | // concatenate two arrays 97 | template 98 | constexpr array concat(const array& a) const 99 | { 100 | return concat(a, std::make_index_sequence(), std::make_index_sequence()); 101 | } 102 | 103 | template ::type, 105 | typename = typename std::enable_if<(B <= N)>::type> 106 | constexpr array slice() const 107 | { 108 | return { &m_data[A], std::make_index_sequence<(B-A)>() }; 109 | } 110 | 111 | // tail (omit first M elements) or init (omit last M elements) 112 | template ::type> 114 | constexpr array tail() const 115 | { 116 | return slice(); 117 | } 118 | 119 | template ::type> 121 | constexpr array init() const 122 | { 123 | return slice<0, N-M>(); 124 | } 125 | 126 | // insert element at position 127 | template 128 | struct inserter; 129 | 130 | template 131 | constexpr array insert(const T& t) const 132 | { 133 | return inserter()(*this, t); 134 | } 135 | 136 | // mergesort 137 | template 138 | struct sorter; 139 | template 140 | struct merger; 141 | 142 | template 143 | constexpr array mergesort(F&& f) const 144 | { 145 | return sorter::sort(*this, std::forward(f)); 146 | } 147 | 148 | template 149 | constexpr array partition(P&& p) const 150 | { 151 | return mergesort(pred_to_less_t

(p)); 152 | } 153 | 154 | private: 155 | T m_data[N] = {}; 156 | 157 | template 158 | constexpr auto map(F&& f, std::index_sequence) const 159 | -> array 160 | { 161 | return array{ f(m_data[Is])... }; 162 | } 163 | 164 | template 165 | constexpr auto map(F&& f, const array& rhs, std::index_sequence) const 166 | -> array 167 | { 168 | return array 169 | { f(m_data[Is], rhs.m_data[Is])... }; 170 | } 171 | 172 | constexpr bool less_r(const T* b, const T* e, size_t i) const 173 | { 174 | return b == e ? false : // other has run out 175 | i == N ? true : // this has run out 176 | m_data[i] < *b ? true : // elementwise less 177 | less_r(b+1, e, i+1); // recurse 178 | } 179 | 180 | template 181 | constexpr array push_back(const T& t, std::index_sequence) const 182 | { 183 | return { m_data[Is]..., t }; 184 | } 185 | 186 | template 187 | constexpr array push_front(const T& t, std::index_sequence) const 188 | { 189 | return { t, m_data[Is]... }; 190 | } 191 | 192 | template 193 | constexpr array 194 | concat(const array& a, 195 | std::index_sequence, std::index_sequence) const 196 | { 197 | return { m_data[Is]..., a[Js]... }; 198 | } 199 | 200 | template 201 | constexpr array tail(std::index_sequence) const 202 | { 203 | return { m_data[Is + N - sizeof...(Is)]... }; 204 | } 205 | 206 | template 207 | constexpr array init(std::index_sequence) const 208 | { 209 | return { m_data[Is]... }; 210 | } 211 | 212 | // inserter for at front, in the middle somewhere, at end 213 | template 214 | struct inserter::type> 215 | { 216 | constexpr array operator()(const array& a, const T& t) const 217 | { 218 | return a.push_front(t, std::make_index_sequence()); 219 | } 220 | }; 221 | 222 | template 223 | struct inserter 0 && I < N)>::type> 224 | { 225 | constexpr array operator()(const array& a, const T& t) const 226 | { 227 | return a.slice<0, I>().concat(a.slice().push_front(t)); 228 | } 229 | }; 230 | 231 | template 232 | struct inserter::type> 233 | { 234 | constexpr array operator()(const array& a, const T& t) const 235 | { 236 | return a.push_back(t, std::make_index_sequence()); 237 | } 238 | }; 239 | 240 | // sorter: a 1-element array is sorted 241 | template 242 | struct sorter::type> 243 | { 244 | template 245 | constexpr static array sort(const array& a, F&&) 246 | { 247 | return a; 248 | } 249 | }; 250 | 251 | // otherwise proceed by sorting each half and merging them 252 | template 253 | struct sorter 1)>::type> 254 | { 255 | template 256 | constexpr static array sort(const array& a, const F& f) 257 | { 258 | return merger::merge( 259 | a.init(std::make_index_sequence()).mergesort(f), 260 | a.tail(std::make_index_sequence()).mergesort(f), 261 | f); 262 | } 263 | }; 264 | 265 | // merger: zero-length arrays aren't a thing, so allow for each or both to 266 | // be of size 1 267 | template 268 | struct merger::type> 270 | { 271 | template 272 | constexpr static array merge(const array& a, const array& b, 273 | const F& f) 274 | { 275 | return f(b[0], a[0]) ? 276 | array{ b[0], a[0] } : 277 | array{ a[0], b[0] }; 278 | } 279 | }; 280 | 281 | template 282 | struct merger 1)>::type> 284 | { 285 | template 286 | constexpr static array merge(const array& a, const array& b, 287 | const F& f) 288 | { 289 | return f(b[0], a[0]) ? 290 | merger::merge(a, b.tail(), f).push_front(b[0]) : 291 | b.push_front(a[0]); 292 | } 293 | }; 294 | 295 | template 296 | struct merger 1 && J == 1)>::type> 298 | { 299 | template 300 | constexpr static array merge(const array& a, const array& b, 301 | const F& f) 302 | { 303 | return f(b[0], a[0]) ? 304 | a.push_front(b[0]) : 305 | merger::merge(a.tail(), b, f).push_front(a[0]); 306 | } 307 | }; 308 | 309 | template 310 | struct merger 1 && J > 1)>::type> 312 | { 313 | template 314 | constexpr static array merge(const array& a, const array& b, 315 | const F& f) 316 | { 317 | return f(b[0], a[0]) ? 318 | merger::merge(a, b.tail(), f).push_front(b[0]) : 319 | merger::merge(a.tail(), b, f).push_front(a[0]); 320 | } 321 | }; 322 | 323 | // make a predicate into a comparison function suitable for sort 324 | template 325 | struct pred_to_less_t 326 | { 327 | constexpr pred_to_less_t(P&& p) : m_p(std::forward

(p)) {} 328 | constexpr bool operator()(const T& a, const T& b) const 329 | { 330 | return m_p(b) ? false : m_p(a); 331 | } 332 | 333 | P m_p; 334 | }; 335 | }; 336 | 337 | // make an array from e.g. a string literal 338 | template 339 | constexpr auto make_array(const T(&a)[N]) -> array 340 | { 341 | return true ? array(a) : 342 | throw err::array_runtime_error; 343 | } 344 | 345 | // make an array from some values: decay them so that we can easily have 346 | // arrays of string literals 347 | template 348 | constexpr auto make_array(E&& e, Es&&... es) 349 | -> array, 1 + sizeof...(Es)> 350 | { 351 | return true ? array, 1+sizeof...(Es)>( 352 | std::forward>(e), 353 | std::forward>(es)...) : 354 | throw err::array_runtime_error; 355 | } 356 | 357 | // array equality 358 | template 359 | constexpr bool operator==(const array& a, const array& b) 360 | { 361 | return equal(a.cbegin(), a.cend(), b.cbegin()); 362 | } 363 | template 364 | constexpr bool operator!=(const array& a, const array& b) 365 | { 366 | return !(a == b); 367 | } 368 | 369 | // array comparison 370 | template 371 | constexpr bool operator<(const array& a, const array& b) 372 | { 373 | return true ? a.less(b) : 374 | throw err::array_runtime_error; 375 | } 376 | 377 | // transform: 1-arg (map) and 2-arg (zip) variants 378 | template 379 | constexpr auto transform(const array& a, F&& f) -> decltype(a.map(f)) 380 | { 381 | return true ? a.map(std::forward(f)) : 382 | throw err::transform_runtime_error; 383 | } 384 | 385 | template 386 | constexpr auto transform(const array& a, const array& b, F&& f) 387 | -> decltype(a.map(f, b)) 388 | { 389 | return true ? a.map(std::forward(f), b) : 390 | throw err::transform_runtime_error; 391 | } 392 | 393 | // sort (mergesort) 394 | template 395 | constexpr array sort(const array& a, F&& lessFn) 396 | { 397 | return true ? a.mergesort(std::forward(lessFn)) : 398 | throw err::sort_runtime_error; 399 | } 400 | 401 | // partition 402 | template 403 | constexpr array partition(const array& a, P&& pred) 404 | { 405 | return true ? a.partition(std::forward

(pred)) : 406 | throw err::partition_runtime_error; 407 | } 408 | 409 | // reverse 410 | namespace detail 411 | { 412 | template 413 | constexpr array reverse( 414 | const array& a, std::integer_sequence) 415 | { 416 | return array{a.end()[-(Is+1)]...}; 417 | } 418 | } 419 | 420 | template 421 | constexpr array reverse(const array& a) 422 | { 423 | return true ? detail::reverse(a, std::make_integer_sequence()) : 424 | throw err::reverse_runtime_error; 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /src/include/cx_counter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ----------------------------------------------------------------------------- 4 | // A constexpr counter 5 | // see http://b.atch.se/posts/constexpr-counter/ 6 | // 7 | // The basic idea is to use a "writer" class template instantiation to control 8 | // progressive instantiations of function overloads, that are then used as 9 | // SFINAE parameters to a "reader" function template. 10 | 11 | namespace cx 12 | { 13 | namespace err 14 | { 15 | namespace 16 | { 17 | extern const char* counter_runtime_error; 18 | } 19 | } 20 | namespace 21 | { 22 | namespace detail_c 23 | { 24 | 25 | // to limit recursive template depth, count low and high bits separately 26 | // recursive template depth = MAX + 3 27 | // min counter value = 0 28 | // max counter value = 2^(2*BIT_DEPTH) - 1 29 | constexpr int BIT_DEPTH = 6; // max value = 4095 30 | constexpr int MAX = (1 << BIT_DEPTH); // recursive template depth = 67 31 | constexpr int BIT_MASK = MAX - 1; 32 | 33 | // the low (flag1) and high(flag2) bits of the count 34 | template 35 | struct flag1 36 | { 37 | friend constexpr int adl_flag1(flag1); 38 | }; 39 | template 40 | struct flag2 41 | { 42 | friend constexpr int adl_flag2(flag2); 43 | }; 44 | 45 | // readers for the flags: to read flag1 we need to wrap in a struct to 46 | // provide the high bits argument (cannot partially specialize a function) 47 | template 48 | struct r1 49 | { 50 | template {})> 51 | static constexpr int reader(int, flag1) 52 | { 53 | return L; 54 | } 55 | template 56 | static constexpr int reader( 57 | float, flag1, int R = reader(0, flag1{})) 58 | { 59 | return R; 60 | } 61 | static constexpr int reader(float, flag1) 62 | { 63 | return 0; 64 | } 65 | }; 66 | 67 | // to read flag2, just overload/specialize the function 68 | template {})> 69 | constexpr int reader(int, flag2) 70 | { 71 | return H; 72 | } 73 | template 74 | constexpr int reader( 75 | float, flag2, int R = reader(0, flag2{})) 76 | { 77 | return R; 78 | } 79 | constexpr int reader(float, flag2<0>) 80 | { 81 | return 0; 82 | } 83 | 84 | // write the low bits flag 85 | template 86 | struct writelo 87 | { 88 | friend constexpr int adl_flag1(flag1) 89 | { 90 | return L; 91 | } 92 | static constexpr int value = L; 93 | }; 94 | // write the high bits flag (if it should be written) 95 | template 96 | struct writehi 97 | { 98 | friend constexpr int adl_flag2(flag2) 99 | { 100 | return H; 101 | } 102 | static constexpr int value = H; 103 | }; 104 | template 105 | struct writehi 106 | { 107 | static constexpr int value = H; 108 | }; 109 | 110 | // write the complete value: write the high bits if the low bits are 111 | // rolling over, and write the low bits (qualified with the high bits) 112 | template 113 | struct writer 114 | { 115 | static constexpr int hi_value = 116 | writehi::value; 117 | static constexpr int lo_value = 118 | writelo::value; 119 | static constexpr int value = (H << BIT_DEPTH) + L; 120 | }; 121 | } 122 | } 123 | 124 | // Driver function: read the high and low bits, and instantiate the writer 125 | // template to write them. The template parameter N must be used to control 126 | // the instantiation of writer. 127 | template {}), 129 | int L = detail_c::r1::reader(0, detail_c::flag1{})> 130 | inline constexpr int counter( 131 | int R = detail_c::writer::value) 132 | { 133 | return true ? R - 1 : 134 | throw err::counter_runtime_error; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/include/cx_fnv1.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //---------------------------------------------------------------------------- 6 | // constexpr string hashing: fnv1 & fnv1a 7 | 8 | namespace cx 9 | { 10 | namespace err 11 | { 12 | namespace 13 | { 14 | extern const char* fnv1_runtime_error; 15 | extern const char* fnv1a_runtime_error; 16 | } 17 | } 18 | namespace detail 19 | { 20 | namespace fnv 21 | { 22 | constexpr uint64_t fnv1(uint64_t h, const char* s) 23 | { 24 | return (*s == 0) ? h : 25 | fnv1((h * 1099511628211ull) ^ static_cast(*s), s+1); 26 | } 27 | constexpr uint64_t fnv1a(uint64_t h, const char* s) 28 | { 29 | return (*s == 0) ? h : 30 | fnv1a((h ^ static_cast(*s)) * 1099511628211ull, s+1); 31 | } 32 | } 33 | } 34 | constexpr uint64_t fnv1(const char* s) 35 | { 36 | return true ? 37 | detail::fnv::fnv1(14695981039346656037ull, s) : 38 | throw err::fnv1_runtime_error; 39 | } 40 | constexpr uint64_t fnv1a(const char* s) 41 | { 42 | return true ? 43 | detail::fnv::fnv1a(14695981039346656037ull, s) : 44 | throw err::fnv1a_runtime_error; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/include/cx_guid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_pcg32.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | //---------------------------------------------------------------------------- 10 | // constexpr guid generation: see 11 | // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 12 | // Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where x is 13 | // any hexadecimal digit and y is one of 8, 9, A, or B 14 | 15 | namespace cx 16 | { 17 | namespace err 18 | { 19 | namespace 20 | { 21 | extern const char* guidgen_runtime_error; 22 | } 23 | } 24 | 25 | struct guid_t 26 | { 27 | uint32_t data1; 28 | uint16_t data2; 29 | uint16_t data3; 30 | uint64_t data4; 31 | }; 32 | 33 | template 34 | constexpr guid_t guidgen() 35 | { 36 | return true ? 37 | guid_t { 38 | cx::pcg::pcg32(), 39 | cx::pcg::pcg32() >> 16, 40 | 0x4000 | cx::pcg::pcg32() >> 20, 41 | (uint64_t{8 + (cx::pcg::pcg32() >> 30)} << 60) 42 | | uint64_t{cx::pcg::pcg32() & 0x0fffffff} << 32 43 | | uint64_t{cx::pcg::pcg32()} } : 44 | throw err::guidgen_runtime_error; 45 | } 46 | } 47 | 48 | namespace cx 49 | { 50 | // for convenience: output operator for guid 51 | std::ostream& operator<<(std::ostream& s, const guid_t& g) 52 | { 53 | auto f = s.flags(); 54 | auto c = s.fill('0'); 55 | auto w = s.width(8); 56 | s << std::hex << g.data1 << '-'; 57 | s.width(4); 58 | s << g.data2 << '-' << g.data3 << '-' << (g.data4 >> 48) << '-'; 59 | s.width(12); 60 | s << (g.data4 & 0xffffffffffffULL); 61 | s.width(w); 62 | s.fill(c); 63 | s.flags(f); 64 | return s; 65 | } 66 | } 67 | 68 | // generate a random guid 69 | #define cx_guid cx::guidgen 70 | -------------------------------------------------------------------------------- /src/include/cx_math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // ----------------------------------------------------------------------------- 7 | // constexpr math functions 8 | 9 | // Synopsis: all functions are in the cx namespace 10 | 11 | // ----------------------------------------------------------------------------- 12 | // absolute value functions 13 | 14 | // float abs(float x); 15 | // double abs(double x); 16 | // long double abs(long double x); 17 | 18 | // float fabs(float x); 19 | // double fabs(double x); 20 | // long double fabs(long double x); 21 | // double fabs(Integral x); 22 | 23 | // ----------------------------------------------------------------------------- 24 | // square root functions 25 | 26 | // float sqrt(float x); 27 | // double sqrt(double x); 28 | // long double sqrt(long double x); 29 | // double sqrt(Integral x); 30 | 31 | // ----------------------------------------------------------------------------- 32 | // cube root functions 33 | 34 | // float cbrt(float x); 35 | // double cbrt(double x); 36 | // long double cbrt(long double x); 37 | // double cbrt(Integral x); 38 | 39 | // ----------------------------------------------------------------------------- 40 | // hypotenuse function (returns the square root of the sum of the squares) 41 | 42 | // float hypot(float x, float y); 43 | // double hypot(double x, double y); 44 | // long double hypot(long double x, long double y); 45 | // Promoted hypot(Arithmetic1 x, Arithmetic2 y); 46 | 47 | // Promotion rules: 48 | // When either of Arithmetic1 or Arithmetic2 is long double, Promoted is long 49 | // double. Otherwise Promoted is double. 50 | 51 | // ----------------------------------------------------------------------------- 52 | // exponent function (e^x) 53 | 54 | // float exp(float x); 55 | // double exp(double x); 56 | // long double exp(long double x); 57 | // double exp(Integral x); 58 | 59 | // ----------------------------------------------------------------------------- 60 | // trigonometric functions 61 | 62 | // float sin(float x); 63 | // double sin(double x); 64 | // long double sin(long double x); 65 | // double sin(Integral x); 66 | 67 | // float cos(float x); 68 | // double cos(double x); 69 | // long double cos(long double x); 70 | // double cos(Integral x); 71 | 72 | // float tan(float x); 73 | // double tan(double x); 74 | // long double tan(long double x); 75 | // double tan(Integral x); 76 | 77 | // ----------------------------------------------------------------------------- 78 | // inverse trigonometric functions 79 | 80 | // float asin(float x); 81 | // double asin(double x); 82 | // long double asin(long double x); 83 | // double asin(Integral x); 84 | 85 | // float acos(float x); 86 | // double acos(double x); 87 | // long double acos(long double x); 88 | // double acos(Integral x); 89 | 90 | // float atan(float x); 91 | // double atan(double x); 92 | // long double atan(long double x); 93 | // double atan(Integral x); 94 | 95 | // float atan2(float x, float y); 96 | // double atan2(double x, double y); 97 | // long double atan2(long double x, long double y); 98 | // Promoted atan2(Arithmetic1 x, Arithmetic2 y); 99 | 100 | // ----------------------------------------------------------------------------- 101 | // rounding functions (long double versions exist only for C++14) 102 | 103 | // float floor(float x); 104 | // double floor(double x); 105 | // long double floor(long double x); 106 | // double floor(Integral x); 107 | 108 | // float ceil(float x); 109 | // double ceil(double x); 110 | // long double ceil(long double x); 111 | // double ceil(Integral x); 112 | 113 | // float trunc(float x); 114 | // double trunc(double x); 115 | // long double trunc(long double x); 116 | // double trunc(Integral x); 117 | 118 | // float round(float x); 119 | // double round(double x); 120 | // long double round(long double x); 121 | // double round(Integral x); 122 | 123 | // ----------------------------------------------------------------------------- 124 | // remainder functions (long double versions exist only for C++14) 125 | 126 | // float fmod(float x, float y); 127 | // double fmod(double x, double y); 128 | // long double fmod(long double x, long double y); 129 | // Promoted fmod(Arithmetic1 x, Arithmetic2 y); 130 | 131 | // float remainder(float x, float y); 132 | // double remainder(double x, double y); 133 | // long double remainder(long double x, long double y); 134 | // Promoted remainder(Arithmetic1 x, Arithmetic2 y); 135 | 136 | // ----------------------------------------------------------------------------- 137 | // max/min functions 138 | 139 | // float fmax(float x, float y); 140 | // double fmax(double x, double y); 141 | // long double fmax(long double x, long double y); 142 | // Promoted fmax(Arithmetic1 x, Arithmetic2 y); 143 | 144 | // float fmin(float x, float y); 145 | // double fmin(double x, double y); 146 | // long double fmin(long double x, long double y); 147 | // Promoted fmin(Arithmetic1 x, Arithmetic2 y); 148 | 149 | // float fdim(float x, float y); 150 | // double fdim(double x, double y); 151 | // long double fdim(long double x, long double y); 152 | // Promoted fdim(Arithmetic1 x, Arithmetic2 y); 153 | 154 | // ----------------------------------------------------------------------------- 155 | // logarithm functions 156 | 157 | // float log(float x); 158 | // double log(double x); 159 | // long double log(long double x); 160 | // double log(Integral x); 161 | 162 | // float log10(float x); 163 | // double log10(double x); 164 | // long double log10(long double x); 165 | // double log10(Integral x); 166 | 167 | // float log2(float x); 168 | // double log2(double x); 169 | // long double log2(long double x); 170 | // double log2(Integral x); 171 | 172 | // ----------------------------------------------------------------------------- 173 | // hyperbolic functions 174 | 175 | // float sinh(float x); 176 | // double sinh(double x); 177 | // long double sinh(long double x); 178 | // double sinh(Integral x); 179 | 180 | // float cosh(float x); 181 | // double cosh(double x); 182 | // long double cosh(long double x); 183 | // double cosh(Integral x); 184 | 185 | // float tanh(float x); 186 | // double tanh(double x); 187 | // long double tanh(long double x); 188 | // double tanh(Integral x); 189 | 190 | // ----------------------------------------------------------------------------- 191 | // inverse hyperbolic functions 192 | 193 | // float asinh(float x); 194 | // double asinh(double x); 195 | // long double asinh(long double x); 196 | // double asinh(Integral x); 197 | 198 | // float acosh(float x); 199 | // double acosh(double x); 200 | // long double acosh(long double x); 201 | // double acosh(Integral x); 202 | 203 | // float atanh(float x); 204 | // double atanh(double x); 205 | // long double atanh(long double x); 206 | // double atanh(Integral x); 207 | 208 | // ----------------------------------------------------------------------------- 209 | // power function 210 | 211 | // float pow(float x, float y); 212 | // double pow(double x, double y); 213 | // long double pow(long double x, long double y); 214 | // Promoted pow(Arithmetic1 x, Arithmetic2 y); 215 | 216 | // ----------------------------------------------------------------------------- 217 | // Gauss error function 218 | 219 | // float erf(float x); 220 | // double erf(double x); 221 | // long double erf(long double x); 222 | // double erf(Integral x); 223 | 224 | namespace cx 225 | { 226 | namespace err 227 | { 228 | namespace 229 | { 230 | extern const char* abs_runtime_error; 231 | extern const char* fabs_runtime_error; 232 | extern const char* sqrt_domain_error; 233 | extern const char* cbrt_runtime_error; 234 | extern const char* exp_runtime_error; 235 | extern const char* sin_runtime_error; 236 | extern const char* cos_runtime_error; 237 | extern const char* tan_domain_error; 238 | extern const char* atan_runtime_error; 239 | extern const char* atan2_domain_error; 240 | extern const char* asin_domain_error; 241 | extern const char* acos_domain_error; 242 | extern const char* floor_runtime_error; 243 | extern const char* ceil_runtime_error; 244 | extern const char* fmod_domain_error; 245 | extern const char* remainder_domain_error; 246 | extern const char* fmax_runtime_error; 247 | extern const char* fmin_runtime_error; 248 | extern const char* fdim_runtime_error; 249 | extern const char* log_domain_error; 250 | extern const char* tanh_domain_error; 251 | extern const char* acosh_domain_error; 252 | extern const char* atanh_domain_error; 253 | extern const char* pow_runtime_error; 254 | extern const char* erf_runtime_error; 255 | } 256 | } 257 | 258 | //---------------------------------------------------------------------------- 259 | template 260 | constexpr FloatingPoint abs( 261 | FloatingPoint x, 262 | typename std::enable_if::value>::type* = nullptr) 263 | { 264 | return x >= 0 ? x : 265 | x < 0 ? -x : 266 | throw err::abs_runtime_error; 267 | } 268 | 269 | namespace detail 270 | { 271 | // test whether values are within machine epsilon, used for algorithm 272 | // termination 273 | template 274 | constexpr bool feq(T x, T y) 275 | { 276 | return abs(x - y) <= std::numeric_limits::epsilon(); 277 | } 278 | } 279 | 280 | //---------------------------------------------------------------------------- 281 | template 282 | constexpr FloatingPoint fabs( 283 | FloatingPoint x, 284 | typename std::enable_if::value>::type* = nullptr) 285 | { 286 | return x >= 0 ? x : 287 | x < 0 ? -x : 288 | throw err::fabs_runtime_error; 289 | } 290 | template 291 | constexpr double fabs( 292 | Integral x, 293 | typename std::enable_if::value>::type* = nullptr) 294 | { 295 | return x >= 0 ? x : 296 | x < 0 ? -x : 297 | throw err::fabs_runtime_error; 298 | } 299 | 300 | //---------------------------------------------------------------------------- 301 | // raise to integer power 302 | namespace detail 303 | { 304 | template 305 | constexpr FloatingPoint ipow( 306 | FloatingPoint x, int n, 307 | typename std::enable_if::value>::type* = nullptr) 308 | { 309 | return (n == 0) ? FloatingPoint{1} : 310 | n == 1 ? x : 311 | n > 1 ? ((n & 1) ? x * ipow(x, n-1) : ipow(x, n/2) * ipow(x, n/2)) : 312 | FloatingPoint{1} / ipow(x, -n); 313 | } 314 | } 315 | 316 | //---------------------------------------------------------------------------- 317 | // square root by Newton-Raphson method 318 | namespace detail 319 | { 320 | template 321 | constexpr T sqrt(T x, T guess) 322 | { 323 | return feq(guess, (guess + x/guess)/T{2}) ? guess : 324 | sqrt(x, (guess + x/guess)/T{2}); 325 | } 326 | } 327 | template 328 | constexpr FloatingPoint sqrt( 329 | FloatingPoint x, 330 | typename std::enable_if::value>::type* = nullptr) 331 | { 332 | return x == 0 ? 0 : 333 | x > 0 ? detail::sqrt(x, x) : 334 | throw err::sqrt_domain_error; 335 | } 336 | template 337 | constexpr double sqrt( 338 | Integral x, 339 | typename std::enable_if::value>::type* = nullptr) 340 | { 341 | return sqrt(x); 342 | } 343 | 344 | //---------------------------------------------------------------------------- 345 | // cube root by Newton-Raphson method 346 | namespace detail 347 | { 348 | template 349 | constexpr T cbrt(T x, T guess) 350 | { 351 | return feq(guess, (T{2}*guess + x/(guess*guess))/T{3}) ? guess : 352 | cbrt(x, (T{2}*guess + x/(guess*guess))/T{3}); 353 | } 354 | } 355 | template 356 | constexpr FloatingPoint cbrt( 357 | FloatingPoint x, 358 | typename std::enable_if::value>::type* = nullptr) 359 | { 360 | return true ? detail::cbrt(x, FloatingPoint{1}) : 361 | throw err::cbrt_runtime_error; 362 | } 363 | template 364 | constexpr double cbrt( 365 | Integral x, 366 | typename std::enable_if::value>::type* = nullptr) 367 | { 368 | return detail::cbrt(x, 1.0); 369 | } 370 | 371 | //---------------------------------------------------------------------------- 372 | // hypot 373 | template 374 | constexpr FloatingPoint hypot( 375 | FloatingPoint x, FloatingPoint y, 376 | typename std::enable_if::value>::type* = nullptr) 377 | { 378 | return sqrt(x*x + y*y); 379 | } 380 | 381 | // hypot for general arithmetic types 382 | template 383 | struct promoted 384 | { 385 | using type = double; 386 | }; 387 | 388 | template 389 | struct promoted 390 | { 391 | using type = long double; 392 | }; 393 | template 394 | struct promoted 395 | { 396 | using type = long double; 397 | }; 398 | template <> 399 | struct promoted 400 | { 401 | using type = long double; 402 | }; 403 | 404 | template 405 | using promoted_t = typename promoted::type; 406 | 407 | template 408 | constexpr promoted_t hypot( 409 | Arithmetic1 x, Arithmetic2 y, 410 | typename std::enable_if< 411 | std::is_arithmetic::value 412 | && std::is_arithmetic::value>::type* = nullptr) 413 | { 414 | using P = promoted_t; 415 | return hypot(static_cast

(x), static_cast

(y)); 416 | } 417 | 418 | //---------------------------------------------------------------------------- 419 | // exp by Taylor series expansion 420 | namespace detail 421 | { 422 | template 423 | constexpr T exp(T x, T sum, T n, int i, T t) 424 | { 425 | return feq(sum, sum + t/n) ? 426 | sum : 427 | exp(x, sum + t/n, n * i, i+1, t * x); 428 | } 429 | } 430 | template 431 | constexpr FloatingPoint exp( 432 | FloatingPoint x, 433 | typename std::enable_if::value>::type* = nullptr) 434 | { 435 | return true ? detail::exp(x, FloatingPoint{1}, FloatingPoint{1}, 2, x) : 436 | throw err::exp_runtime_error; 437 | } 438 | template 439 | constexpr double exp( 440 | Integral x, 441 | typename std::enable_if::value>::type* = nullptr) 442 | { 443 | return detail::exp(x, 1.0, 1.0, 2, x); 444 | } 445 | 446 | //---------------------------------------------------------------------------- 447 | // sin by Taylor series expansion 448 | // The body of trig_series is basically the same for sin and cos. 449 | namespace detail 450 | { 451 | template 452 | constexpr T trig_series(T x, T sum, T n, int i, int s, T t) 453 | { 454 | return feq(sum, sum + t*s/n) ? 455 | sum : 456 | trig_series(x, sum + t*s/n, n*i*(i+1), i+2, -s, t*x*x); 457 | } 458 | } 459 | template 460 | constexpr FloatingPoint sin( 461 | FloatingPoint x, 462 | typename std::enable_if::value>::type* = nullptr) 463 | { 464 | return true ? 465 | detail::trig_series(x, x, FloatingPoint{6}, 4, -1, x*x*x) : 466 | throw err::sin_runtime_error; 467 | } 468 | template 469 | constexpr double sin( 470 | Integral x, 471 | typename std::enable_if::value>::type* = nullptr) 472 | { 473 | return sin(x); 474 | } 475 | 476 | //---------------------------------------------------------------------------- 477 | // cos by Taylor series expansion 478 | // Note that this function uses the same basic form as the sin expansion, so 479 | // trig_series with different inputs does the job. 480 | template 481 | constexpr FloatingPoint cos( 482 | FloatingPoint x, 483 | typename std::enable_if::value>::type* = nullptr) 484 | { 485 | return true ? 486 | detail::trig_series(x, FloatingPoint{1}, FloatingPoint{2}, 3, -1, x*x) : 487 | throw err::cos_runtime_error; 488 | } 489 | template 490 | constexpr double cos( 491 | Integral x, 492 | typename std::enable_if::value>::type* = nullptr) 493 | { 494 | return detail::trig_series( 495 | x, 1.0, 2.0, 3, -1, 496 | static_cast(x)*static_cast(x)); 497 | } 498 | 499 | //---------------------------------------------------------------------------- 500 | // tan(x) = sin(x)/cos(x) - cos(x) cannot be 0 501 | // the undefined symbol enforces that this function is evaluated at 502 | // compile-time (or it fails at link-time) 503 | template 504 | constexpr FloatingPoint tan( 505 | FloatingPoint x, 506 | typename std::enable_if::value>::type* = nullptr) 507 | { 508 | return cos(x) != 0 ? 509 | sin(x) / cos(x) : 510 | throw err::tan_domain_error; 511 | } 512 | template 513 | constexpr double tan( 514 | Integral x, 515 | typename std::enable_if::value>::type* = nullptr) 516 | { 517 | return cos(x) != 0.0 ? 518 | sin(x) / cos(x) : 519 | throw err::tan_domain_error; 520 | } 521 | 522 | //---------------------------------------------------------------------------- 523 | // arctan by Euler's series 524 | namespace detail 525 | { 526 | template 527 | constexpr T atan_term(T x2, int k) 528 | { 529 | return (T{2}*static_cast(k)*x2) 530 | / ((T{2}*static_cast(k)+T{1}) * (T{1}+x2)); 531 | } 532 | template 533 | constexpr T atan_product(T x, int k) 534 | { 535 | return k == 1 ? atan_term(x*x, k) : 536 | atan_term(x*x, k) * atan_product(x, k-1); 537 | } 538 | template 539 | constexpr T atan_sum(T x, T sum, int n) 540 | { 541 | return sum + atan_product(x, n) == sum ? 542 | sum : 543 | atan_sum(x, sum + atan_product(x, n), n+1); 544 | } 545 | constexpr long double pi() 546 | { 547 | return 3.1415926535897932385l; 548 | } 549 | } 550 | template 551 | constexpr FloatingPoint atan( 552 | FloatingPoint x, 553 | typename std::enable_if::value>::type* = nullptr) 554 | { 555 | return true ? 556 | x / (FloatingPoint{1} + x*x) * detail::atan_sum(x, FloatingPoint{1}, 1) : 557 | throw err::atan_runtime_error; 558 | } 559 | template 560 | constexpr double atan( 561 | Integral x, 562 | typename std::enable_if::value>::type* = nullptr) 563 | { 564 | return atan(x); 565 | } 566 | 567 | template 568 | constexpr FloatingPoint atan2( 569 | FloatingPoint x, FloatingPoint y, 570 | typename std::enable_if::value>::type* = nullptr) 571 | { 572 | return x > 0 ? atan(y/x) : 573 | y >= 0 && x < 0 ? atan(y/x) + static_cast(detail::pi()) : 574 | y < 0 && x < 0 ? atan(y/x) - static_cast(detail::pi()) : 575 | y > 0 && x == 0 ? static_cast(detail::pi()/2.0l) : 576 | y < 0 && x == 0 ? -static_cast(detail::pi()/2.0l) : 577 | throw err::atan2_domain_error; 578 | } 579 | 580 | // atan2 for general arithmetic types 581 | template 582 | constexpr promoted_t atan2( 583 | Arithmetic1 x, Arithmetic2 y, 584 | typename std::enable_if< 585 | std::is_arithmetic::value 586 | && std::is_arithmetic::value>::type* = nullptr) 587 | { 588 | using P = promoted_t; 589 | return atan2(static_cast

(x), static_cast

(y)); 590 | } 591 | 592 | //---------------------------------------------------------------------------- 593 | // inverse trig functions 594 | namespace detail 595 | { 596 | template 597 | constexpr T asin_series(T x, T sum, int n, T t) 598 | { 599 | return feq(sum, sum + t*static_cast(n)/(n+2)) ? 600 | sum : 601 | asin_series(x, sum + t*static_cast(n)/(n+2), n+2, 602 | t*x*x*static_cast(n)/(n+3)); 603 | } 604 | } 605 | template 606 | constexpr FloatingPoint asin( 607 | FloatingPoint x, 608 | typename std::enable_if::value>::type* = nullptr) 609 | { 610 | return x == FloatingPoint{-1} ? detail::pi()/FloatingPoint{-2} : 611 | x == FloatingPoint{1} ? detail::pi()/FloatingPoint{2} : 612 | x > FloatingPoint{-1} && x < FloatingPoint{1} ? 613 | detail::asin_series(x, x, 1, x*x*x/FloatingPoint{2}) : 614 | throw err::asin_domain_error; 615 | } 616 | template 617 | constexpr double asin( 618 | Integral x, 619 | typename std::enable_if::value>::type* = nullptr) 620 | { 621 | return asin(x); 622 | } 623 | 624 | template 625 | constexpr FloatingPoint acos( 626 | FloatingPoint x, 627 | typename std::enable_if::value>::type* = nullptr) 628 | { 629 | return x == FloatingPoint{-1} ? static_cast(detail::pi()) : 630 | x == FloatingPoint{1} ? 0 : 631 | x > FloatingPoint{-1} && x < FloatingPoint{1} ? detail::pi()/FloatingPoint{2} - asin(x) : 632 | throw err::acos_domain_error; 633 | } 634 | template 635 | constexpr double acos( 636 | Integral x, 637 | typename std::enable_if::value>::type* = nullptr) 638 | { 639 | return acos(x); 640 | } 641 | 642 | //---------------------------------------------------------------------------- 643 | // floor and ceil: each works in terms of the other for negative numbers 644 | // The algorithm proceeds by "binary search" on the increment. 645 | // But in order not to overflow the max compile-time recursion depth 646 | // (say 512) we need to perform an n-ary search, where: 647 | // n = 2^(numeric_limits::max_exponent/512 + 1) 648 | // (The +1 gives space for other functions in the stack.) 649 | // For float, a plain binary search is fine, because max_exponent = 128. 650 | // For double, max_exponent = 1024, so we need n = 2^3 = 8. 651 | // For long double, max_exponent = 16384, so we need n = 2^33. Oops. Looks 652 | // like floor/ceil for long double can only exist for C++14 where we are not 653 | // limited to recursion. 654 | namespace detail 655 | { 656 | template 657 | constexpr T floor2(T x, T guess, T inc) 658 | { 659 | return guess + inc <= x ? floor2(x, guess + inc, inc) : 660 | inc <= T{1} ? guess : floor2(x, guess, inc/T{2}); 661 | } 662 | template 663 | constexpr T floor(T x, T guess, T inc) 664 | { 665 | return 666 | inc < T{8} ? floor2(x, guess, inc) : 667 | guess + inc <= x ? floor(x, guess + inc, inc) : 668 | guess + (inc/T{8})*T{7} <= x ? floor(x, guess + (inc/T{8})*T{7}, inc/T{8}) : 669 | guess + (inc/T{8})*T{6} <= x ? floor(x, guess + (inc/T{8})*T{6}, inc/T{8}) : 670 | guess + (inc/T{8})*T{5} <= x ? floor(x, guess + (inc/T{8})*T{5}, inc/T{8}) : 671 | guess + (inc/T{8})*T{4} <= x ? floor(x, guess + (inc/T{8})*T{4}, inc/T{8}) : 672 | guess + (inc/T{8})*T{3} <= x ? floor(x, guess + (inc/T{8})*T{3}, inc/T{8}) : 673 | guess + (inc/T{8})*T{2} <= x ? floor(x, guess + (inc/T{8})*T{2}, inc/T{8}) : 674 | guess + inc/T{8} <= x ? floor(x, guess + inc/T{8}, inc/T{8}) : 675 | floor(x, guess, inc/T{8}); 676 | } 677 | template 678 | constexpr T ceil2(T x, T guess, T dec) 679 | { 680 | return guess - dec >= x ? ceil2(x, guess - dec, dec) : 681 | dec <= T{1} ? guess : ceil2(x, guess, dec/T{2}); 682 | } 683 | template 684 | constexpr T ceil(T x, T guess, T dec) 685 | { 686 | return 687 | dec < T{8} ? ceil2(x, guess, dec) : 688 | guess - dec >= x ? ceil(x, guess - dec, dec) : 689 | guess - (dec/T{8})*T{7} >= x ? ceil(x, guess - (dec/T{8})*T{7}, dec/T{8}) : 690 | guess - (dec/T{8})*T{6} >= x ? ceil(x, guess - (dec/T{8})*T{6}, dec/T{8}) : 691 | guess - (dec/T{8})*T{5} >= x ? ceil(x, guess - (dec/T{8})*T{5}, dec/T{8}) : 692 | guess - (dec/T{8})*T{4} >= x ? ceil(x, guess - (dec/T{8})*T{4}, dec/T{8}) : 693 | guess - (dec/T{8})*T{3} >= x ? ceil(x, guess - (dec/T{8})*T{3}, dec/T{8}) : 694 | guess - (dec/T{8})*T{2} >= x ? ceil(x, guess - (dec/T{8})*T{2}, dec/T{8}) : 695 | guess - dec/T{8} >= x ? ceil(x, guess - dec/T{8}, dec/T{8}) : 696 | ceil(x, guess, dec/T{8}); 697 | } 698 | } 699 | 700 | constexpr float ceil(float x); 701 | constexpr double ceil(double x); 702 | template 703 | constexpr double ceil( 704 | Integral x, 705 | typename std::enable_if::value>::type* = nullptr); 706 | 707 | constexpr float floor(float x) 708 | { 709 | return x < 0 ? -ceil(-x) : 710 | x >= 0 ? detail::floor( 711 | x, 0.0f, 712 | detail::ipow(2.0f, std::numeric_limits::max_exponent-1)) : 713 | throw err::floor_runtime_error; 714 | } 715 | constexpr double floor(double x) 716 | { 717 | return x < 0 ? -ceil(-x) : 718 | x >= 0 ? detail::floor( 719 | x, 0.0, 720 | detail::ipow(2.0, std::numeric_limits::max_exponent-1)) : 721 | throw err::floor_runtime_error; 722 | } 723 | template 724 | constexpr double floor( 725 | Integral x, 726 | typename std::enable_if::value>::type* = nullptr) 727 | { 728 | return true ? x : 729 | throw err::floor_runtime_error; 730 | } 731 | 732 | constexpr float ceil(float x) 733 | { 734 | return x < 0 ? -floor(-x) : 735 | x >= 0 ? detail::ceil( 736 | x, detail::ipow(2.0f, std::numeric_limits::max_exponent-1), 737 | detail::ipow(2.0f, std::numeric_limits::max_exponent-1)) : 738 | throw err::ceil_runtime_error; 739 | } 740 | constexpr double ceil(double x) 741 | { 742 | return x < 0 ? -floor(-x) : 743 | x >= 0 ? detail::ceil( 744 | x, detail::ipow(2.0, std::numeric_limits::max_exponent-1), 745 | detail::ipow(2.0, std::numeric_limits::max_exponent-1)) : 746 | throw err::ceil_runtime_error; 747 | } 748 | template 749 | constexpr double ceil( 750 | Integral x, 751 | typename std::enable_if::value>::type*) 752 | { 753 | return true ? x : 754 | throw err::ceil_runtime_error; 755 | } 756 | 757 | // See above: long double floor/ceil only available for C++14 constexpr 758 | #if __cplusplus == 201402L 759 | constexpr long double ceil(long double x); 760 | constexpr long double floor(long double x) 761 | { 762 | if (x < 0.0) return -ceil(-x); 763 | long double inc = detail::ipow(2.0l, std::numeric_limits::max_exponent - 1); 764 | long double guess = 0.0l; 765 | for (;;) 766 | { 767 | while (guess + inc > x) 768 | { 769 | inc /= 2.0l; 770 | if (inc < 1.0l) 771 | return guess; 772 | } 773 | guess += inc; 774 | } 775 | throw err::floor_runtime_error; 776 | } 777 | constexpr long double ceil(long double x) 778 | { 779 | if (x < 0.0l) return -floor(-x); 780 | long double dec = detail::ipow(2.0l, std::numeric_limits::max_exponent - 1); 781 | long double guess = dec; 782 | for (;;) 783 | { 784 | while (guess - dec < x) 785 | { 786 | dec /= 2.0l; 787 | if (dec < 1.0l) 788 | return guess; 789 | } 790 | guess -= dec; 791 | } 792 | throw err::ceil_runtime_error; 793 | } 794 | #endif 795 | 796 | constexpr float trunc(float x) 797 | { 798 | return x >= 0 ? floor(x) : -floor(-x); 799 | } 800 | constexpr double trunc(double x) 801 | { 802 | return x >= 0 ? floor(x) : -floor(-x); 803 | } 804 | #if __cplusplus == 201402L 805 | constexpr long double trunc(long double x) 806 | { 807 | return x >= 0 ? floor(x) : -floor(-x); 808 | } 809 | #endif 810 | 811 | template 812 | constexpr double trunc( 813 | Integral x, 814 | typename std::enable_if::value>::type* = nullptr) 815 | { 816 | return x; 817 | } 818 | 819 | constexpr float round(float x) 820 | { 821 | return x >= 0 ? floor(x + 0.5f) : 822 | ceil(x - 0.5f); 823 | } 824 | constexpr double round(double x) 825 | { 826 | return x >= 0 ? floor(x + 0.5) : 827 | ceil(x - 0.5); 828 | } 829 | #if __cplusplus == 201402L 830 | constexpr long double round(long double x) 831 | { 832 | return x >= 0 ? floor(x + 0.5l) : 833 | ceil(x - 0.5l); 834 | } 835 | #endif 836 | 837 | template 838 | constexpr double round( 839 | Integral x, 840 | typename std::enable_if::value>::type* = nullptr) 841 | { 842 | return x; 843 | } 844 | 845 | //---------------------------------------------------------------------------- 846 | // fmod: floating-point remainder function 847 | constexpr float fmod(float x, float y) 848 | { 849 | return y != 0 ? x - trunc(x/y)*y : 850 | throw err::fmod_domain_error; 851 | } 852 | constexpr double fmod(double x, double y) 853 | { 854 | return y != 0 ? x - trunc(x/y)*y : 855 | throw err::fmod_domain_error; 856 | } 857 | #if __cplusplus == 201402L 858 | constexpr long double fmod(long double x, long double y) 859 | { 860 | return y != 0 ? x - trunc(x/y)*y : 861 | throw err::fmod_domain_error; 862 | } 863 | #endif 864 | 865 | // fmod for general arithmetic types 866 | template 867 | struct cpp14_promoted 868 | { 869 | using type = double; 870 | }; 871 | 872 | #if __cplusplus == 201402L 873 | // Interestingly, this does not seem to produce a template instantiation 874 | // ambiguity with fmod_promoted 875 | template 876 | struct cpp14_promoted 877 | { 878 | using type = long double; 879 | }; 880 | template 881 | struct cpp14_promoted 882 | { 883 | using type = long double; 884 | }; 885 | #endif 886 | 887 | template 888 | using cpp14_promoted_t = typename cpp14_promoted::type; 889 | 890 | template 891 | constexpr cpp14_promoted_t fmod( 892 | Arithmetic1 x, Arithmetic2 y, 893 | typename std::enable_if< 894 | std::is_arithmetic::value 895 | && std::is_arithmetic::value>::type* = nullptr) 896 | { 897 | using P = cpp14_promoted_t; 898 | return fmod(static_cast

(x), static_cast

(y)); 899 | } 900 | 901 | //---------------------------------------------------------------------------- 902 | // remainder: signed floating-point remainder function 903 | constexpr float remainder(float x, float y) 904 | { 905 | return y != 0 ? x - y*round(x/y) : 906 | throw err::remainder_domain_error; 907 | } 908 | constexpr double remainder(double x, double y) 909 | { 910 | return y != 0 ? x - y*round(x/y) : 911 | throw err::remainder_domain_error; 912 | } 913 | #if __cplusplus == 201402L 914 | constexpr long double remainder(long double x, long double y) 915 | { 916 | return y != 0 ? x - y*round(x/y) : 917 | throw err::remainder_domain_error; 918 | } 919 | #endif 920 | 921 | // remainder for general arithmetic types 922 | template 923 | constexpr cpp14_promoted_t remainder( 924 | Arithmetic1 x, Arithmetic2 y, 925 | typename std::enable_if< 926 | std::is_arithmetic::value 927 | && std::is_arithmetic::value>::type* = nullptr) 928 | { 929 | using P = cpp14_promoted_t; 930 | return remainder(static_cast

(x), static_cast

(y)); 931 | } 932 | 933 | //---------------------------------------------------------------------------- 934 | // fmax/fmin: floating-point min/max function 935 | // fdim: positive difference 936 | template 937 | constexpr FloatingPoint fmax( 938 | FloatingPoint x, FloatingPoint y, 939 | typename std::enable_if::value>::type* = nullptr) 940 | { 941 | return x >= y ? x : 942 | x < y ? y : 943 | throw err::fmax_runtime_error; 944 | } 945 | template 946 | constexpr FloatingPoint fmin( 947 | FloatingPoint x, FloatingPoint y, 948 | typename std::enable_if::value>::type* = nullptr) 949 | { 950 | return y >= x ? x : 951 | y < x ? y : 952 | throw err::fmin_runtime_error; 953 | } 954 | template 955 | constexpr FloatingPoint fdim( 956 | FloatingPoint x, FloatingPoint y, 957 | typename std::enable_if::value>::type* = nullptr) 958 | { 959 | return x > y ? x-y : 960 | x <= y ? 0 : 961 | throw err::fdim_runtime_error; 962 | } 963 | 964 | // fmax/fmin/fdim for general arithmetic types 965 | template 966 | constexpr promoted_t fmax( 967 | Arithmetic1 x, Arithmetic2 y, 968 | typename std::enable_if< 969 | std::is_arithmetic::value 970 | && std::is_arithmetic::value>::type* = nullptr) 971 | { 972 | using P = promoted_t; 973 | return fmax(static_cast

(x), static_cast

(y)); 974 | } 975 | template 976 | constexpr promoted_t fmin( 977 | Arithmetic1 x, Arithmetic2 y, 978 | typename std::enable_if< 979 | std::is_arithmetic::value 980 | && std::is_arithmetic::value>::type* = nullptr) 981 | { 982 | using P = promoted_t; 983 | return fmin(static_cast

(x), static_cast

(y)); 984 | } 985 | template 986 | constexpr promoted_t fdim( 987 | Arithmetic1 x, Arithmetic2 y, 988 | typename std::enable_if< 989 | std::is_arithmetic::value 990 | && std::is_arithmetic::value>::type* = nullptr) 991 | { 992 | using P = promoted_t; 993 | return fdim(static_cast

(x), static_cast

(y)); 994 | } 995 | 996 | //---------------------------------------------------------------------------- 997 | // natural logarithm using 998 | // https://en.wikipedia.org/wiki/Natural_logarithm#High_precision 999 | // domain error occurs if x <= 0 1000 | namespace detail 1001 | { 1002 | template 1003 | constexpr T log_iter(T x, T y) 1004 | { 1005 | return y + T{2} * (x - cx::exp(y)) / (x + cx::exp(y)); 1006 | } 1007 | template 1008 | constexpr T log(T x, T y) 1009 | { 1010 | return feq(y, log_iter(x, y)) ? y : log(x, log_iter(x, y)); 1011 | } 1012 | constexpr long double e() 1013 | { 1014 | return 2.71828182845904523536l; 1015 | } 1016 | // For numerical stability, constrain the domain to be x > 0.25 && x < 1024 1017 | // - multiply/divide as necessary. To achieve the desired recursion depth 1018 | // constraint, we need to account for the max double. So we'll divide by 1019 | // e^5. If you want to compute a compile-time log of huge or tiny long 1020 | // doubles, YMMV. 1021 | 1022 | // if x <= 1, we will multiply by e^5 repeatedly until x > 1 1023 | template 1024 | constexpr T logGT(T x) 1025 | { 1026 | return x > T{0.25} ? log(x, T{0}) : 1027 | logGT(x * e() * e() * e() * e() * e()) - T{5}; 1028 | } 1029 | // if x >= 2e10, we will divide by e^5 repeatedly until x < 2e10 1030 | template 1031 | constexpr T logLT(T x) 1032 | { 1033 | return x < T{1024} ? log(x, T{0}) : 1034 | logLT(x / (e() * e() * e() * e() * e())) + T{5}; 1035 | } 1036 | } 1037 | template 1038 | constexpr FloatingPoint log( 1039 | FloatingPoint x, 1040 | typename std::enable_if::value>::type* = nullptr) 1041 | { 1042 | return x < 0 ? throw err::log_domain_error : 1043 | x >= FloatingPoint{1024} ? detail::logLT(x) : 1044 | detail::logGT(x); 1045 | } 1046 | template 1047 | constexpr double log( 1048 | Integral x, 1049 | typename std::enable_if::value>::type* = nullptr) 1050 | { 1051 | return log(static_cast(x)); 1052 | } 1053 | 1054 | //---------------------------------------------------------------------------- 1055 | // other logarithms 1056 | template 1057 | constexpr FloatingPoint log10( 1058 | FloatingPoint x, 1059 | typename std::enable_if::value>::type* = nullptr) 1060 | { 1061 | return log(x)/log(FloatingPoint{10}); 1062 | } 1063 | template 1064 | constexpr double log10( 1065 | Integral x, 1066 | typename std::enable_if::value>::type* = nullptr) 1067 | { 1068 | return log10(static_cast(x)); 1069 | } 1070 | 1071 | template 1072 | constexpr FloatingPoint log2( 1073 | FloatingPoint x, 1074 | typename std::enable_if::value>::type* = nullptr) 1075 | { 1076 | return log(x)/log(FloatingPoint{2}); 1077 | } 1078 | template 1079 | constexpr double log2( 1080 | Integral x, 1081 | typename std::enable_if::value>::type* = nullptr) 1082 | { 1083 | return log2(static_cast(x)); 1084 | } 1085 | 1086 | //---------------------------------------------------------------------------- 1087 | // hyperbolic functions 1088 | template 1089 | constexpr FloatingPoint sinh( 1090 | FloatingPoint x, 1091 | typename std::enable_if::value>::type* = nullptr) 1092 | { 1093 | return (exp(x) - exp(-x)) / FloatingPoint{2}; 1094 | } 1095 | template 1096 | constexpr double sinh( 1097 | Integral x, 1098 | typename std::enable_if::value>::type* = nullptr) 1099 | { 1100 | return (exp(x) - exp(-x)) / 2.0; 1101 | } 1102 | 1103 | template 1104 | constexpr FloatingPoint cosh( 1105 | FloatingPoint x, 1106 | typename std::enable_if::value>::type* = nullptr) 1107 | { 1108 | return (exp(x) + exp(-x)) / FloatingPoint{2}; 1109 | } 1110 | template 1111 | constexpr double cosh( 1112 | Integral x, 1113 | typename std::enable_if::value>::type* = nullptr) 1114 | { 1115 | return (exp(x) + exp(-x)) / 2.0; 1116 | } 1117 | 1118 | template 1119 | constexpr FloatingPoint tanh( 1120 | FloatingPoint x, 1121 | typename std::enable_if::value>::type* = nullptr) 1122 | { 1123 | return cosh(x) != 0 ? 1124 | sinh(x) / cosh(x) : 1125 | throw err::tanh_domain_error; 1126 | } 1127 | template 1128 | constexpr double tanh( 1129 | Integral x, 1130 | typename std::enable_if::value>::type* = nullptr) 1131 | { 1132 | return cosh(x) != 0.0 ? 1133 | sinh(x) / cosh(x) : 1134 | throw err::tanh_domain_error; 1135 | } 1136 | 1137 | //---------------------------------------------------------------------------- 1138 | // inverse hyperbolic functions 1139 | template 1140 | constexpr FloatingPoint asinh( 1141 | FloatingPoint x, 1142 | typename std::enable_if::value>::type* = nullptr) 1143 | { 1144 | return log(x + sqrt(x*x + FloatingPoint{1})); 1145 | } 1146 | template 1147 | constexpr double asinh( 1148 | Integral x, 1149 | typename std::enable_if::value>::type* = nullptr) 1150 | { 1151 | return asinh(x); 1152 | } 1153 | 1154 | template 1155 | constexpr FloatingPoint acosh( 1156 | FloatingPoint x, 1157 | typename std::enable_if::value>::type* = nullptr) 1158 | { 1159 | return x >= 1 ? log(x + sqrt(x*x - FloatingPoint{1})) : 1160 | throw err::acosh_domain_error; 1161 | } 1162 | template 1163 | constexpr double acosh( 1164 | Integral x, 1165 | typename std::enable_if::value>::type* = nullptr) 1166 | { 1167 | return acosh(x); 1168 | } 1169 | 1170 | template 1171 | constexpr FloatingPoint atanh( 1172 | FloatingPoint x, 1173 | typename std::enable_if::value>::type* = nullptr) 1174 | { 1175 | return 1176 | x > -1 && x < 1 ? 1177 | (FloatingPoint{1}/FloatingPoint{2}) 1178 | * log((FloatingPoint{1} + x) / (FloatingPoint{1} - x)) : 1179 | throw err::atanh_domain_error; 1180 | } 1181 | template 1182 | constexpr double atanh( 1183 | Integral x, 1184 | typename std::enable_if::value>::type* = nullptr) 1185 | { 1186 | return atanh(x); 1187 | } 1188 | 1189 | //---------------------------------------------------------------------------- 1190 | // pow: compute x^y 1191 | // a = x^y = (exp(log(x)))^y = exp(log(x)*y) 1192 | template 1193 | constexpr FloatingPoint pow( 1194 | FloatingPoint x, FloatingPoint y, 1195 | typename std::enable_if::value>::type* = nullptr) 1196 | { 1197 | return true ? exp(log(x)*y) : 1198 | throw err::pow_runtime_error; 1199 | } 1200 | template 1201 | constexpr FloatingPoint pow( 1202 | FloatingPoint x, int y, 1203 | typename std::enable_if::value>::type* = nullptr) 1204 | { 1205 | return true ? detail::ipow(x, y) : 1206 | throw err::pow_runtime_error; 1207 | } 1208 | 1209 | // pow for general arithmetic types 1210 | template 1211 | constexpr promoted_t pow( 1212 | Arithmetic1 x, Arithmetic2 y, 1213 | typename std::enable_if< 1214 | std::is_arithmetic::value 1215 | && std::is_arithmetic::value>::type* = nullptr) 1216 | { 1217 | using P = promoted_t; 1218 | return pow(static_cast

(x), static_cast

(y)); 1219 | } 1220 | template 1221 | constexpr promoted_t pow( 1222 | Integral x, int y, 1223 | typename std::enable_if< 1224 | std::is_integral::value>::type* = nullptr) 1225 | { 1226 | return true ? detail::ipow(static_cast(x), y) : 1227 | throw err::pow_runtime_error; 1228 | } 1229 | 1230 | //---------------------------------------------------------------------------- 1231 | // erf: the error function 1232 | namespace detail 1233 | { 1234 | constexpr long double two_over_root_pi() 1235 | { 1236 | return 1.128379167095512573896l; 1237 | } 1238 | 1239 | template 1240 | constexpr T erf(T x, T sum, T n, int i, int s, T t) 1241 | { 1242 | return feq(sum, sum + (t*s/n)/(2*i+1)) ? 1243 | sum : 1244 | erf(x, sum + (t*s/n)/(2*i+1), n*(i+1), i+1, -s, t*x*x); 1245 | } 1246 | } 1247 | template 1248 | constexpr FloatingPoint erf( 1249 | FloatingPoint x, 1250 | typename std::enable_if::value>::type* = nullptr) 1251 | { 1252 | return true ? detail::erf(x, x, FloatingPoint{1}, 1, -1, x*x*x) 1253 | * detail::two_over_root_pi() : 1254 | throw err::erf_runtime_error; 1255 | } 1256 | template 1257 | constexpr double erf( 1258 | Integral x, 1259 | typename std::enable_if::value>::type* = nullptr) 1260 | { 1261 | return erf(x); 1262 | } 1263 | } 1264 | -------------------------------------------------------------------------------- /src/include/cx_md5.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_utils.h" 4 | #include 5 | 6 | //---------------------------------------------------------------------------- 7 | // constexpr string hashing: md5 8 | 9 | namespace cx 10 | { 11 | // Result of an md5 calculation - conventionally an md5 string is hex bytes 12 | // from least significant to most significant. So to print out the md5sum: 13 | // 14 | // constexpr auto sum = cx::md5("abc"); 15 | // const uint8_t* m = reinterpret_cast(c.h); 16 | // cout << hex << setfill('0'); 17 | // for (size_t i = 0; i < sizeof(c.h); ++i) 18 | // { 19 | // cout << setw(2) << +m[i]; 20 | // } 21 | 22 | namespace err 23 | { 24 | namespace 25 | { 26 | extern const char* md5_runtime_error; 27 | } 28 | } 29 | 30 | struct md5sum 31 | { 32 | uint32_t h[4]; 33 | }; 34 | 35 | namespace detail 36 | { 37 | namespace md5 38 | { 39 | // shift amounts for the 4 rounds of the main function 40 | constexpr int r1shift[4] = { 7, 12, 17, 22 }; 41 | constexpr int r2shift[4] = { 5, 9, 14, 20 }; 42 | constexpr int r3shift[4] = { 4, 11, 16, 23 }; 43 | constexpr int r4shift[4] = { 6, 10, 15, 21 }; 44 | 45 | // magic constants for each round (actually the integer part of 46 | // abs(sin(i)) where i is the step number 47 | constexpr uint32_t r1const[16] = 48 | { 49 | 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 50 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 51 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 52 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 53 | }; 54 | constexpr uint32_t r2const[16] = 55 | { 56 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 57 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 58 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 59 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a 60 | }; 61 | constexpr uint32_t r3const[16] = 62 | { 63 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 64 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 65 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 66 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 67 | }; 68 | constexpr uint32_t r4const[16] = 69 | { 70 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 71 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 72 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 73 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 74 | }; 75 | 76 | // a schedule is the chunk of buffer to work on 77 | struct schedule 78 | { 79 | uint32_t w[16]; 80 | }; 81 | 82 | // add two md5sums 83 | constexpr md5sum sumadd(const md5sum& s1, const md5sum& s2) 84 | { 85 | return { { s1.h[0] + s2.h[0], s1.h[1] + s2.h[1], 86 | s1.h[2] + s2.h[2], s1.h[3] + s2.h[3] } }; 87 | } 88 | 89 | // the basic MD5 operations 90 | constexpr uint32_t F(uint32_t X, uint32_t Y, uint32_t Z) 91 | { 92 | return (X & Y) | (~X & Z); 93 | } 94 | constexpr uint32_t G(uint32_t X, uint32_t Y, uint32_t Z) 95 | { 96 | return (X & Z) | (Y & ~Z); 97 | } 98 | constexpr uint32_t H(uint32_t X, uint32_t Y, uint32_t Z) 99 | { 100 | return X ^ Y ^ Z; 101 | } 102 | constexpr uint32_t I(uint32_t X, uint32_t Y, uint32_t Z) 103 | { 104 | return Y ^ (X | ~Z); 105 | } 106 | constexpr uint32_t rotateL(uint32_t x, int n) 107 | { 108 | return (x << n) | (x >> (32-n)); 109 | } 110 | constexpr uint32_t FF(uint32_t a, uint32_t b, uint32_t c, uint32_t d, 111 | uint32_t x, int s, uint32_t ac) 112 | { 113 | return rotateL(a + F(b,c,d) + x + ac, s) + b; 114 | } 115 | constexpr uint32_t GG(uint32_t a, uint32_t b, uint32_t c, uint32_t d, 116 | uint32_t x, int s, uint32_t ac) 117 | { 118 | return rotateL(a + G(b,c,d) + x + ac, s) + b; 119 | } 120 | constexpr uint32_t HH(uint32_t a, uint32_t b, uint32_t c, uint32_t d, 121 | uint32_t x, int s, uint32_t ac) 122 | { 123 | return rotateL(a + H(b,c,d) + x + ac, s) + b; 124 | } 125 | constexpr uint32_t II(uint32_t a, uint32_t b, uint32_t c, uint32_t d, 126 | uint32_t x, int s, uint32_t ac) 127 | { 128 | return rotateL(a + I(b,c,d) + x + ac, s) + b; 129 | } 130 | 131 | // initial md5sum 132 | constexpr md5sum init() 133 | { 134 | return { { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 } }; 135 | } 136 | // schedule from an existing buffer 137 | constexpr schedule init(const char* buf) 138 | { 139 | return { { word32le(buf), word32le(buf+4), word32le(buf+8), word32le(buf+12), 140 | word32le(buf+16), word32le(buf+20), word32le(buf+24), word32le(buf+28), 141 | word32le(buf+32), word32le(buf+36), word32le(buf+40), word32le(buf+44), 142 | word32le(buf+48), word32le(buf+52), word32le(buf+56), word32le(buf+60) } }; 143 | } 144 | 145 | // computing leftovers is messy: we need to pad the empty space to a 146 | // multiple of 64 bytes. the first pad byte is 0x80, the rest are 0. 147 | // the original length (in bits) is the last 8 bytes of padding. 148 | constexpr uint32_t pad(int len) 149 | { 150 | return len == 0 ? 0x00000080 : 151 | len == 1 ? 0x00008000 : 152 | len == 2 ? 0x00800000 : 153 | len == 3 ? 0x80000000 : 154 | 0; 155 | } 156 | constexpr uint32_t origlenbytes(int origlen, int origlenpos) 157 | { 158 | return origlenpos == 0 ? 159 | static_cast(origlen)*8 & 0xffffffff : 160 | origlenpos == -4 ? 161 | (static_cast(origlen) >> 29) : 162 | 0; 163 | } 164 | constexpr schedule leftover(const char* buf, 165 | int len, int origlen, int origlenpos) 166 | { 167 | return { { word32le(buf, len) | pad(len) | origlenbytes(origlen, origlenpos), 168 | word32le(len >= 4 ? buf+4 : buf, len-4) 169 | | pad(len-4) | origlenbytes(origlen, origlenpos-4), 170 | word32le(len >= 8 ? buf+8 : buf, len-8) 171 | | pad(len-8) | origlenbytes(origlen, origlenpos-8), 172 | word32le(len >= 12 ? buf+12 : buf, len-12) 173 | | pad(len-12) | origlenbytes(origlen, origlenpos-12), 174 | word32le(len >= 16 ? buf+16 : buf, len-16) 175 | | pad(len-16) | origlenbytes(origlen, origlenpos-16), 176 | word32le(len >= 20 ? buf+20 : buf, len-20) 177 | | pad(len-20) | origlenbytes(origlen, origlenpos-20), 178 | word32le(len >= 24 ? buf+24 : buf, len-24) 179 | | pad(len-24) | origlenbytes(origlen, origlenpos-24), 180 | word32le(len >= 28 ? buf+28 : buf, len-28) 181 | | pad(len-28) | origlenbytes(origlen, origlenpos-28), 182 | word32le(len >= 32 ? buf+32 : buf, len-32) 183 | | pad(len-32) | origlenbytes(origlen, origlenpos-32), 184 | word32le(len >= 36 ? buf+36 : buf, len-36) 185 | | pad(len-36) | origlenbytes(origlen, origlenpos-36), 186 | word32le(len >= 40 ? buf+40 : buf, len-40) 187 | | pad(len-40) | origlenbytes(origlen, origlenpos-40), 188 | word32le(len >= 44 ? buf+44 : buf, len-44) 189 | | pad(len-44) | origlenbytes(origlen, origlenpos-44), 190 | word32le(len >= 48 ? buf+48 : buf, len-48) 191 | | pad(len-48) | origlenbytes(origlen, origlenpos-48), 192 | word32le(len >= 52 ? buf+52 : buf, len-52) 193 | | pad(len-52) | origlenbytes(origlen, origlenpos-52), 194 | word32le(len >= 56 ? buf+56 : buf, len-56) 195 | | pad(len-56) | origlenbytes(origlen, origlenpos-56), 196 | word32le(len >= 60 ? buf+60 : buf, len-60) 197 | | pad(len-60) | origlenbytes(origlen, origlenpos-60)} }; 198 | } 199 | 200 | // compute a step of each round 201 | constexpr md5sum round1step(const md5sum& sum, const uint32_t* block, int step) 202 | { 203 | return { { 204 | FF(sum.h[0], sum.h[1], sum.h[2], sum.h[3], 205 | block[step], r1shift[step&3], r1const[step]), 206 | sum.h[1], sum.h[2], sum.h[3] 207 | } }; 208 | } 209 | constexpr md5sum round2step(const md5sum& sum, const uint32_t* block, int step) 210 | { 211 | return { { 212 | GG(sum.h[0], sum.h[1], sum.h[2], sum.h[3], 213 | block[(1+step*5)%16], r2shift[step&3], r2const[step]), 214 | sum.h[1], sum.h[2], sum.h[3] 215 | } }; 216 | } 217 | constexpr md5sum round3step(const md5sum& sum, const uint32_t* block, int step) 218 | { 219 | return { { 220 | HH(sum.h[0], sum.h[1], sum.h[2], sum.h[3], 221 | block[(5+step*3)%16], r3shift[step&3], r3const[step]), 222 | sum.h[1], sum.h[2], sum.h[3] 223 | } }; 224 | } 225 | constexpr md5sum round4step(const md5sum& sum, const uint32_t* block, int step) 226 | { 227 | return { { 228 | II(sum.h[0], sum.h[1], sum.h[2], sum.h[3], 229 | block[(step*7)%16], r4shift[step&3], r4const[step]), 230 | sum.h[1], sum.h[2], sum.h[3] 231 | } }; 232 | } 233 | 234 | // rotate md5sums right and left (each round step does this) 235 | constexpr md5sum rotateCR(const md5sum& sum) 236 | { 237 | return { { sum.h[3], sum.h[0], sum.h[1], sum.h[2] } }; 238 | } 239 | constexpr md5sum rotateCL(const md5sum& sum) 240 | { 241 | return { { sum.h[1], sum.h[2], sum.h[3], sum.h[0] } }; 242 | } 243 | 244 | // the 4 rounds are each the result of recursively running the respective 245 | // round step (16 times for a block of 64 bytes) 246 | constexpr md5sum round1(const md5sum& sum, const uint32_t* msg, int n) 247 | { 248 | return n == 16 ? sum : 249 | rotateCL(round1(rotateCR(round1step(sum, msg, n)), msg, n+1)); 250 | } 251 | constexpr md5sum round2(const md5sum& sum, const uint32_t* msg, int n) 252 | { 253 | return n == 16 ? sum : 254 | rotateCL(round2(rotateCR(round2step(sum, msg, n)), msg, n+1)); 255 | } 256 | constexpr md5sum round3(const md5sum& sum, const uint32_t* msg, int n) 257 | { 258 | return n == 16 ? sum : 259 | rotateCL(round3(rotateCR(round3step(sum, msg, n)), msg, n+1)); 260 | } 261 | constexpr md5sum round4(const md5sum& sum, const uint32_t* msg, int n) 262 | { 263 | return n == 16 ? sum : 264 | rotateCL(round4(rotateCR(round4step(sum, msg, n)), msg, n+1)); 265 | } 266 | 267 | // the complete transform, for a schedule block 268 | constexpr md5sum md5transform(const md5sum& sum, const schedule& s) 269 | { 270 | return sumadd(sum, 271 | round4( 272 | round3( 273 | round2( 274 | round1(sum, s.w, 0), 275 | s.w, 0), 276 | s.w, 0), 277 | s.w, 0)); 278 | } 279 | 280 | // three conditions: 281 | // 1. as long as we have a 64-byte block to do, we'll recurse on that 282 | // 2. when we have 56 bytes or more, we need to do a whole empty block to 283 | // fit the 8 bytes of length after padding 284 | // 3. otherwise we have a block that will fit both padding and the length 285 | constexpr md5sum md5update(const md5sum& sum, const char* msg, 286 | int len, int origlen) 287 | { 288 | return 289 | len >= 64 ? 290 | md5update(md5transform(sum, init(msg)), msg+64, len-64, origlen) : 291 | len >= 56 ? 292 | md5update(md5transform(sum, leftover(msg, len, origlen, 64)), msg+len, -1, origlen) : 293 | md5transform(sum, leftover(msg, len, origlen, 56)); 294 | } 295 | constexpr md5sum md5withlen(const char* msg, int len) 296 | { 297 | return md5update(init(), msg, len, len); 298 | } 299 | constexpr md5sum md5(const char* msg) 300 | { 301 | return md5withlen(msg, strlen(msg)); 302 | } 303 | } 304 | } 305 | constexpr md5sum md5(const char* s) 306 | { 307 | return true ? detail::md5::md5(s) : 308 | throw err::md5_runtime_error; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/include/cx_murmur3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_utils.h" 4 | 5 | #include 6 | 7 | //---------------------------------------------------------------------------- 8 | // constexpr string hashing: murmur3_32 9 | 10 | namespace cx 11 | { 12 | namespace err 13 | { 14 | namespace 15 | { 16 | extern const char* murmur3_32_runtime_error; 17 | } 18 | } 19 | 20 | namespace detail 21 | { 22 | namespace murmur 23 | { 24 | constexpr uint32_t murmur3_32_k(uint32_t k) 25 | { 26 | return (((k * 0xcc9e2d51) << 15) | ((k * 0xcc9e2d51) >> 17)) * 0x1b873593; 27 | } 28 | 29 | constexpr uint32_t murmur3_32_hashround(uint32_t k, uint32_t hash) 30 | { 31 | return (((hash^k) << 13) | ((hash^k) >> 19)) * 5 + 0xe6546b64; 32 | } 33 | 34 | constexpr uint32_t murmur3_32_loop(const char* key, int len, uint32_t hash) 35 | { 36 | return len == 0 ? hash : 37 | murmur3_32_loop( 38 | key + 4, 39 | len - 1, 40 | murmur3_32_hashround( 41 | murmur3_32_k(word32le(key)), hash)); 42 | } 43 | 44 | constexpr uint32_t murmur3_32_end0(uint32_t k) 45 | { 46 | return (((k*0xcc9e2d51) << 15) | ((k*0xcc9e2d51) >> 17)) * 0x1b873593; 47 | } 48 | 49 | constexpr uint32_t murmur3_32_end1(uint32_t k, const char* key) 50 | { 51 | return murmur3_32_end0( 52 | k ^ static_cast(key[0])); 53 | } 54 | 55 | constexpr uint32_t murmur3_32_end2(uint32_t k, const char* key) 56 | { 57 | return murmur3_32_end1( 58 | k ^ (static_cast(key[1]) << 8), key); 59 | } 60 | constexpr uint32_t murmur3_32_end3(uint32_t k, const char* key) 61 | { 62 | return murmur3_32_end2( 63 | k ^ (static_cast(key[2]) << 16), key); 64 | } 65 | 66 | constexpr uint32_t murmur3_32_end(uint32_t hash, 67 | const char* key, int rem) 68 | { 69 | return rem == 0 ? hash : 70 | hash ^ (rem == 3 ? murmur3_32_end3(0, key) : 71 | rem == 2 ? murmur3_32_end2(0, key) : 72 | murmur3_32_end1(0, key)); 73 | } 74 | 75 | constexpr uint32_t murmur3_32_final1(uint32_t hash) 76 | { 77 | return (hash ^ (hash >> 16)) * 0x85ebca6b; 78 | } 79 | constexpr uint32_t murmur3_32_final2(uint32_t hash) 80 | { 81 | return (hash ^ (hash >> 13)) * 0xc2b2ae35; 82 | } 83 | constexpr uint32_t murmur3_32_final3(uint32_t hash) 84 | { 85 | return (hash ^ (hash >> 16)); 86 | } 87 | 88 | constexpr uint32_t murmur3_32_final(uint32_t hash, int len) 89 | { 90 | return 91 | murmur3_32_final3( 92 | murmur3_32_final2( 93 | murmur3_32_final1(hash ^ static_cast(len)))); 94 | } 95 | 96 | constexpr uint32_t murmur3_32_value(const char* key, int len, 97 | uint32_t seed) 98 | { 99 | return murmur3_32_final( 100 | murmur3_32_end( 101 | murmur3_32_loop(key, len/4, seed), 102 | key+(len/4)*4, len&3), 103 | len); 104 | } 105 | } 106 | } 107 | 108 | constexpr uint32_t murmur3_32(const char *key, uint32_t seed) 109 | { 110 | return true ? 111 | detail::murmur::murmur3_32_value(key, strlen(key), seed) : 112 | throw err::murmur3_32_runtime_error; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/include/cx_numeric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | //---------------------------------------------------------------------------- 7 | // constexpr numeric algorithms 8 | 9 | namespace cx 10 | { 11 | namespace err 12 | { 13 | namespace 14 | { 15 | extern const char* accumulate_runtime_error; 16 | extern const char* inner_product_runtime_error; 17 | } 18 | } 19 | 20 | // accumulate 21 | template 22 | constexpr T accumulate(It first, It last, T init) 23 | { 24 | return true ? 25 | first == last ? init : 26 | accumulate(first + 1, last, init + *first) : 27 | throw err::accumulate_runtime_error; 28 | } 29 | 30 | template 31 | constexpr T accumulate(It first, It last, T init, BinaryOp op) 32 | { 33 | return true ? 34 | first == last ? init : 35 | accumulate(first + 1, last, op(init, *first), op) : 36 | throw err::accumulate_runtime_error; 37 | } 38 | 39 | // inner_product 40 | template 41 | constexpr T inner_product(It1 first1, It1 last1, It2 first2, T value) 42 | { 43 | return true ? 44 | first1 == last1 ? value : 45 | inner_product(first1 + 1, last1, first2 + 1, 46 | value + *first1 * *first2) : 47 | throw err::inner_product_runtime_error; 48 | } 49 | 50 | template 52 | constexpr T inner_product(It1 first1, It1 last1, It2 first2, T value, 53 | BinaryOp1 op1, BinaryOp2 op2) 54 | { 55 | return true ? 56 | first1 == last1 ? value : 57 | inner_product(first1 + 1, last1, first2 + 1, 58 | op1(value, op2(*first1, *first2)), 59 | op1, op2) : 60 | throw err::inner_product_runtime_error; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/include/cx_pcg32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_fnv1.h" 4 | #include 5 | 6 | // ----------------------------------------------------------------------------- 7 | // A constexpr implementation of Melissa O'Neill's PCG32 8 | // see http://www.pcg-random.org/ 9 | // see cx_counter.h for the constexpr counter on which this is based 10 | namespace cx 11 | { 12 | namespace err 13 | { 14 | namespace 15 | { 16 | extern const char* pcg32_runtime_error; 17 | } 18 | } 19 | 20 | namespace pcg 21 | { 22 | // advance the pcg state 23 | constexpr uint64_t pcg32_advance(uint64_t s) 24 | { 25 | // for uint64_t, default multiplier = 6364136223846793005ULL 26 | // default increment = 1442695040888963407ULL 27 | return s * 6364136223846793005ULL + (1442695040888963407ULL | 1); 28 | } 29 | constexpr uint64_t pcg32_advance(uint64_t s, int n) 30 | { 31 | return n == 0 ? s : pcg32_advance(pcg32_advance(s), n-1); 32 | } 33 | 34 | // output function (XSH RR) 35 | constexpr uint32_t pcg32_xorshift(uint64_t s) 36 | { 37 | return ((s >> 18u) ^ s) >> 27u; 38 | } 39 | constexpr uint32_t pcg32_rot(uint64_t s) 40 | { 41 | return s >> 59u; 42 | } 43 | constexpr uint32_t pcg32_output(uint64_t s) 44 | { 45 | return (pcg32_xorshift(s) >> pcg32_rot(s)) 46 | | (pcg32_xorshift(s) << ((-pcg32_rot(s)) & 31)); 47 | } 48 | } 49 | 50 | namespace 51 | { 52 | namespace detail_pcg 53 | { 54 | // Everything that follows here is the same as in cx_counter.h except the 55 | // writer, which emits the pcg32 value, and the fact that we use the seed 56 | // as a template parameter so that the max applies per line (by default). 57 | // And here, recursive template depth = MAX + 5 58 | constexpr int BIT_DEPTH = 6; // max value = 4095 59 | constexpr int MAX = (1 << BIT_DEPTH); // recursive template depth = 69 60 | constexpr int BIT_MASK = MAX - 1; 61 | 62 | template 63 | struct flag1 64 | { 65 | friend constexpr int adl_flag1(flag1); 66 | }; 67 | template 68 | struct flag2 69 | { 70 | friend constexpr int adl_flag2(flag2); 71 | }; 72 | 73 | template 74 | struct r1 75 | { 76 | template {})> 77 | static constexpr int reader(int, flag1) 78 | { 79 | return L; 80 | } 81 | template 82 | static constexpr int reader( 83 | float, flag1, int R = reader(0, flag1{})) 84 | { 85 | return R; 86 | } 87 | static constexpr int reader(float, flag1) 88 | { 89 | return 0; 90 | } 91 | }; 92 | 93 | template 94 | struct r2 95 | { 96 | template {})> 97 | static constexpr int reader(int, flag2) 98 | { 99 | return H; 100 | } 101 | template 102 | static constexpr int reader( 103 | float, flag2, int R = reader(0, flag2{})) 104 | { 105 | return R; 106 | } 107 | static constexpr int reader(float, flag2) 108 | { 109 | return 0; 110 | } 111 | }; 112 | 113 | template 114 | struct writelo 115 | { 116 | friend constexpr int adl_flag1(flag1) 117 | { 118 | return L; 119 | } 120 | static constexpr int value = L; 121 | }; 122 | template 123 | struct writehi 124 | { 125 | friend constexpr int adl_flag2(flag2) 126 | { 127 | return H; 128 | } 129 | static constexpr int value = H; 130 | }; 131 | template 132 | struct writehi 133 | { 134 | static constexpr int value = H; 135 | }; 136 | 137 | // the pcg32 value: the output result of the advance of the initial state 138 | template 139 | struct writer 140 | { 141 | static constexpr int hi_value = 142 | writehi::value; 143 | static constexpr int lo_value = 144 | writelo::value; 145 | static constexpr uint32_t value = 146 | pcg::pcg32_output(pcg::pcg32_advance(S, (H << BIT_DEPTH) + L)); 147 | }; 148 | } 149 | } 150 | 151 | namespace pcg 152 | { 153 | template ::reader(0, detail_pcg::flag2{}), 156 | int L = detail_pcg::r1::reader(0, detail_pcg::flag1{})> 157 | inline constexpr uint32_t pcg32( 158 | uint32_t R = detail_pcg::writer::value) 159 | { 160 | return true ? R : 161 | throw err::pcg32_runtime_error; 162 | } 163 | } 164 | } 165 | 166 | // macro that seeds the rng differently every compile, and provides for 167 | // per-line instantiations 168 | #define cx_pcg32 cx::pcg::pcg32 169 | -------------------------------------------------------------------------------- /src/include/cx_sha256.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_utils.h" 4 | 5 | //---------------------------------------------------------------------------- 6 | // constexpr string hashing: sha-256 7 | 8 | namespace cx 9 | { 10 | // Result of a sha-256 calculation; to print it out: 11 | // 12 | // constexpr auto sum = cx::sha256("abc"); 13 | // const uint8_t* m = reinterpret_cast(c.h); 14 | // cout << hex << setfill('0'); 15 | // for (size_t i = 0; i < sizeof(c.h); ++i) 16 | // { 17 | // cout << setw(2) << +m[i]; 18 | // } 19 | 20 | namespace err 21 | { 22 | namespace 23 | { 24 | extern const char* sha256_runtime_error; 25 | } 26 | } 27 | 28 | struct sha256sum 29 | { 30 | uint32_t h[8]; 31 | }; 32 | 33 | namespace detail 34 | { 35 | namespace sha256 36 | { 37 | // magic round constants (actually the fractional parts of 38 | // the cubes roots of the first 64 primes 2..311) 39 | constexpr uint32_t roundconst[64] = 40 | { 41 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 42 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 43 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 44 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 45 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 46 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 47 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 48 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 49 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 50 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 51 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 52 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 53 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 54 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 55 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 56 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 57 | }; 58 | 59 | // a schedule is the chunk of buffer to work on, extended to 64 words 60 | struct schedule 61 | { 62 | uint32_t w[64]; 63 | }; 64 | 65 | // add two sha256sums 66 | constexpr sha256sum sumadd(const sha256sum& s1, const sha256sum& s2) 67 | { 68 | return { { s1.h[0] + s2.h[0], s1.h[1] + s2.h[1], 69 | s1.h[2] + s2.h[2], s1.h[3] + s2.h[3], 70 | s1.h[4] + s2.h[4], s1.h[5] + s2.h[5], 71 | s1.h[6] + s2.h[6], s1.h[7] + s2.h[7] } }; 72 | } 73 | // initial sha256sum 74 | constexpr sha256sum init() 75 | { 76 | return { { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 77 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 } }; 78 | } 79 | // schedule from an existing buffer 80 | constexpr schedule init(const char* buf) 81 | { 82 | return { { word32be(buf), word32be(buf+4), word32be(buf+8), word32be(buf+12), 83 | word32be(buf+16), word32be(buf+20), word32be(buf+24), word32be(buf+28), 84 | word32be(buf+32), word32be(buf+36), word32be(buf+40), word32be(buf+44), 85 | word32be(buf+48), word32be(buf+52), word32be(buf+56), word32be(buf+60) } }; 86 | } 87 | 88 | // computing leftovers is messy: we need to pad the empty space to a 89 | // multiple of 64 bytes. the first pad byte is 0x80, the rest are 0. 90 | // the original length (in bits) is the last 8 bytes of padding. 91 | constexpr uint32_t pad(int len) 92 | { 93 | return len == 3 ? 0x00000080 : 94 | len == 2 ? 0x00008000 : 95 | len == 1 ? 0x00800000 : 96 | len == 0 ? 0x80000000 : 97 | 0; 98 | } 99 | constexpr uint32_t origlenbytes(int origlen, int origlenpos) 100 | { 101 | return origlenpos == -4 ? 102 | static_cast(origlen)*8 & 0xffffffff : 103 | origlenpos == 0 ? 104 | (static_cast(origlen) >> 29) : 105 | 0; 106 | } 107 | constexpr schedule leftover(const char* buf, 108 | int len, int origlen, int origlenpos) 109 | { 110 | return { { word32be(buf, len) | pad(len) | origlenbytes(origlen, origlenpos), 111 | word32be(len >= 4 ? buf+4 : buf, len-4) 112 | | pad(len-4) | origlenbytes(origlen, origlenpos-4), 113 | word32be(len >= 8 ? buf+8 : buf, len-8) 114 | | pad(len-8) | origlenbytes(origlen, origlenpos-8), 115 | word32be(len >= 12 ? buf+12 : buf, len-12) 116 | | pad(len-12) | origlenbytes(origlen, origlenpos-12), 117 | word32be(len >= 16 ? buf+16 : buf, len-16) 118 | | pad(len-16) | origlenbytes(origlen, origlenpos-16), 119 | word32be(len >= 20 ? buf+20 : buf, len-20) 120 | | pad(len-20) | origlenbytes(origlen, origlenpos-20), 121 | word32be(len >= 24 ? buf+24 : buf, len-24) 122 | | pad(len-24) | origlenbytes(origlen, origlenpos-24), 123 | word32be(len >= 28 ? buf+28 : buf, len-28) 124 | | pad(len-28) | origlenbytes(origlen, origlenpos-28), 125 | word32be(len >= 32 ? buf+32 : buf, len-32) 126 | | pad(len-32) | origlenbytes(origlen, origlenpos-32), 127 | word32be(len >= 36 ? buf+36 : buf, len-36) 128 | | pad(len-36) | origlenbytes(origlen, origlenpos-36), 129 | word32be(len >= 40 ? buf+40 : buf, len-40) 130 | | pad(len-40) | origlenbytes(origlen, origlenpos-40), 131 | word32be(len >= 44 ? buf+44 : buf, len-44) 132 | | pad(len-44) | origlenbytes(origlen, origlenpos-44), 133 | word32be(len >= 48 ? buf+48 : buf, len-48) 134 | | pad(len-48) | origlenbytes(origlen, origlenpos-48), 135 | word32be(len >= 52 ? buf+52 : buf, len-52) 136 | | pad(len-52) | origlenbytes(origlen, origlenpos-52), 137 | word32be(len >= 56 ? buf+56 : buf, len-56) 138 | | pad(len-56) | origlenbytes(origlen, origlenpos-56), 139 | word32be(len >= 60 ? buf+60 : buf, len-60) 140 | | pad(len-60) | origlenbytes(origlen, origlenpos-60)} }; 141 | } 142 | 143 | constexpr uint32_t rotateR(uint32_t x, int n) 144 | { 145 | return (x << (32-n)) | (x >> n); 146 | } 147 | constexpr uint32_t s0(uint32_t x) 148 | { 149 | return rotateR(x, 7) ^ rotateR(x, 18) ^ (x >> 3); 150 | } 151 | constexpr uint32_t s1(uint32_t x) 152 | { 153 | return rotateR(x, 17) ^ rotateR(x, 19) ^ (x >> 10); 154 | } 155 | 156 | constexpr uint32_t extendvalue(const uint32_t* w, int i, int n) 157 | { 158 | return i < n ? w[i] : 159 | extendvalue(w, i-16, n) + extendvalue(w, i-7, n) 160 | + s0(extendvalue(w, i-15, n)) + s1(extendvalue(w, i-2, n)); 161 | } 162 | 163 | // extend the 16 words in the schedule to the whole 64 164 | // to avoid hitting the max step limit, we'll do this by 16s 165 | constexpr schedule sha256extend16(const schedule& s) 166 | { 167 | return { { s.w[0], s.w[1], s.w[2], s.w[3], 168 | s.w[4], s.w[5], s.w[6], s.w[7], 169 | s.w[8], s.w[9], s.w[10], s.w[11], 170 | s.w[12], s.w[13], s.w[14], s.w[15], 171 | extendvalue(s.w, 16, 16), extendvalue(s.w, 17, 16), 172 | extendvalue(s.w, 18, 16), extendvalue(s.w, 19, 16), 173 | extendvalue(s.w, 20, 16), extendvalue(s.w, 21, 16), 174 | extendvalue(s.w, 22, 16), extendvalue(s.w, 23, 16), 175 | extendvalue(s.w, 24, 16), extendvalue(s.w, 25, 16), 176 | extendvalue(s.w, 26, 16), extendvalue(s.w, 27, 16), 177 | extendvalue(s.w, 28, 16), extendvalue(s.w, 29, 16), 178 | extendvalue(s.w, 30, 16), extendvalue(s.w, 31, 16) } }; 179 | } 180 | constexpr schedule sha256extend32(const schedule& s) 181 | { 182 | return { { s.w[0], s.w[1], s.w[2], s.w[3], 183 | s.w[4], s.w[5], s.w[6], s.w[7], 184 | s.w[8], s.w[9], s.w[10], s.w[11], 185 | s.w[12], s.w[13], s.w[14], s.w[15], 186 | s.w[16], s.w[17], s.w[18], s.w[19], 187 | s.w[20], s.w[21], s.w[22], s.w[23], 188 | s.w[24], s.w[25], s.w[26], s.w[27], 189 | s.w[28], s.w[29], s.w[30], s.w[31], 190 | extendvalue(s.w, 32, 32), extendvalue(s.w, 33, 32), 191 | extendvalue(s.w, 34, 32), extendvalue(s.w, 35, 32), 192 | extendvalue(s.w, 36, 32), extendvalue(s.w, 37, 32), 193 | extendvalue(s.w, 38, 32), extendvalue(s.w, 39, 32), 194 | extendvalue(s.w, 40, 32), extendvalue(s.w, 41, 32), 195 | extendvalue(s.w, 42, 32), extendvalue(s.w, 43, 32), 196 | extendvalue(s.w, 44, 32), extendvalue(s.w, 45, 32), 197 | extendvalue(s.w, 46, 32), extendvalue(s.w, 47, 32) } }; 198 | } 199 | constexpr schedule sha256extend48(const schedule& s) 200 | { 201 | return { { s.w[0], s.w[1], s.w[2], s.w[3], 202 | s.w[4], s.w[5], s.w[6], s.w[7], 203 | s.w[8], s.w[9], s.w[10], s.w[11], 204 | s.w[12], s.w[13], s.w[14], s.w[15], 205 | s.w[16], s.w[17], s.w[18], s.w[19], 206 | s.w[20], s.w[21], s.w[22], s.w[23], 207 | s.w[24], s.w[25], s.w[26], s.w[27], 208 | s.w[28], s.w[29], s.w[30], s.w[31], 209 | s.w[32], s.w[33], s.w[34], s.w[35], 210 | s.w[36], s.w[37], s.w[38], s.w[39], 211 | s.w[40], s.w[41], s.w[42], s.w[43], 212 | s.w[44], s.w[45], s.w[46], s.w[47], 213 | extendvalue(s.w, 48, 48), extendvalue(s.w, 49, 48), 214 | extendvalue(s.w, 50, 48), extendvalue(s.w, 51, 48), 215 | extendvalue(s.w, 52, 48), extendvalue(s.w, 53, 48), 216 | extendvalue(s.w, 54, 48), extendvalue(s.w, 55, 48), 217 | extendvalue(s.w, 56, 48), extendvalue(s.w, 57, 48), 218 | extendvalue(s.w, 58, 48), extendvalue(s.w, 59, 48), 219 | extendvalue(s.w, 60, 48), extendvalue(s.w, 61, 48), 220 | extendvalue(s.w, 62, 48), extendvalue(s.w, 63, 48) } }; 221 | } 222 | constexpr schedule sha256extend(const schedule& s) 223 | { 224 | return sha256extend48(sha256extend32(sha256extend16(s))); 225 | } 226 | 227 | // the compression function, in 64 rounds 228 | constexpr uint32_t S1(uint32_t e) 229 | { 230 | return rotateR(e, 6) ^ rotateR(e, 11) ^ rotateR(e, 25); 231 | } 232 | constexpr uint32_t ch(uint32_t e, uint32_t f, uint32_t g) 233 | { 234 | return (e & f) ^ (~e & g); 235 | } 236 | constexpr uint32_t temp1(const sha256sum& sum, int i) 237 | { 238 | return sum.h[7] + S1(sum.h[4]) + ch(sum.h[4], sum.h[5], sum.h[6]) 239 | + roundconst[i]; 240 | } 241 | constexpr uint32_t S0(uint32_t a) 242 | { 243 | return rotateR(a, 2) ^ rotateR(a, 13) ^ rotateR(a, 22); 244 | } 245 | constexpr uint32_t maj(uint32_t a, uint32_t b, uint32_t c) 246 | { 247 | return (a & b) ^ (a & c) ^ (b & c); 248 | } 249 | constexpr uint32_t temp2(const sha256sum& sum) 250 | { 251 | return S0(sum.h[0]) + maj(sum.h[0], sum.h[1], sum.h[2]); 252 | } 253 | 254 | // rotate sha256sums right and left (each round step does this) 255 | constexpr sha256sum rotateCR(const sha256sum& sum) 256 | { 257 | return { { sum.h[7], sum.h[0], sum.h[1], sum.h[2], 258 | sum.h[3], sum.h[4], sum.h[5], sum.h[6] } }; 259 | } 260 | constexpr sha256sum rotateCL(const sha256sum& sum) 261 | { 262 | return { { sum.h[1], sum.h[2], sum.h[3], sum.h[4], 263 | sum.h[5], sum.h[6], sum.h[7], sum.h[0] } }; 264 | } 265 | 266 | constexpr sha256sum sha256round(const sha256sum& sum, uint32_t t1, uint32_t t2) 267 | { 268 | return { { sum.h[0], sum.h[1], sum.h[2], sum.h[3] + t1, 269 | sum.h[4], sum.h[5], sum.h[6], t1 + t2 } }; 270 | } 271 | constexpr sha256sum sha256compress(const sha256sum& sum, const schedule& s, int step) 272 | { 273 | return step == 64 ? sum : 274 | rotateCL( 275 | sha256compress( 276 | rotateCR(sha256round(sum, temp1(sum, step) + s.w[step], temp2(sum))), 277 | s, step + 1)); 278 | } 279 | 280 | // the complete transform, for a message that is a multiple of 64 bytes 281 | constexpr sha256sum sha256transform(const sha256sum& sum, const schedule& s) 282 | { 283 | return sumadd(sha256compress(sum, sha256extend(s), 0), sum); 284 | } 285 | 286 | // three conditions: 287 | // 1. as long as we have a 64-byte block to do, we'll recurse on that 288 | // 2. when we have 56 bytes or more, we need to do a whole empty block to 289 | // fit the 8 bytes of length after padding 290 | // 3. otherwise we have a block that will fit both padding and the length 291 | constexpr sha256sum sha256update(const sha256sum& sum, const char* msg, 292 | int len, int origlen) 293 | { 294 | return 295 | len >= 64 ? 296 | sha256update(sha256transform(sum, init(msg)), msg+64, len-64, origlen) : 297 | len >= 56 ? 298 | sha256update(sha256transform( 299 | sum, leftover(msg, len, origlen, 64)), msg+len, -1, origlen) : 300 | sha256transform(sum, leftover(msg, len, origlen, 56)); 301 | } 302 | constexpr sha256sum sha256withlen(const char* msg, int len) 303 | { 304 | return sha256update(init(), msg, len, len); 305 | } 306 | constexpr sha256sum sha256(const char* msg) 307 | { 308 | return sha256withlen(msg, strlen(msg)); 309 | } 310 | // convert a sha256sum to little-endian 311 | constexpr sha256sum sha256tole(const sha256sum& sum) 312 | { 313 | return { { 314 | endianswap(sum.h[0]), endianswap(sum.h[1]), 315 | endianswap(sum.h[2]), endianswap(sum.h[3]), 316 | endianswap(sum.h[4]), endianswap(sum.h[5]), 317 | endianswap(sum.h[6]), endianswap(sum.h[7]), 318 | } }; 319 | } 320 | } 321 | } 322 | constexpr sha256sum sha256(const char* s) 323 | { 324 | return true ? detail::sha256::sha256tole(detail::sha256::sha256(s)) : 325 | throw err::sha256_runtime_error; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/include/cx_strenc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_pcg32.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //---------------------------------------------------------------------------- 11 | // constexpr string encryption 12 | 13 | namespace cx 14 | { 15 | namespace err 16 | { 17 | namespace 18 | { 19 | extern const char* strenc_runtime_error; 20 | } 21 | } 22 | 23 | namespace detail 24 | { 25 | // encrypt/decrypt (it's symmetric, just XORing a random bytestream) a 26 | // single character of a given string under a seed that is used to advance 27 | // the rng to that position 28 | template 29 | constexpr char encrypt_at(const char* s, size_t idx) 30 | { 31 | return s[idx] ^ 32 | static_cast(pcg::pcg32_output(pcg::pcg32_advance(S, idx+1)) >> 24); 33 | } 34 | 35 | // store the string in a char_array for constexpr manipulation 36 | template 37 | struct char_array 38 | { 39 | char data[N]; 40 | }; 41 | 42 | // Decrypt and encrypt are really the same: just xor the RNG byte stream 43 | // with the characters. For convenience, decrypt returns a std::string. 44 | inline std::string decrypt(uint64_t S, const char* s, size_t n) 45 | { 46 | std::string ret; 47 | ret.reserve(n); 48 | for (size_t i = 0; i < n; ++i) 49 | { 50 | S = pcg::pcg32_advance(S); 51 | ret.push_back(s[i] ^ static_cast(pcg::pcg32_output(S) >> 24)); 52 | } 53 | return ret; 54 | } 55 | 56 | // Encrypt is constexpr where decrypt is not, because encrypt occurs at 57 | // compile time 58 | template 59 | constexpr char_array encrypt(const char *s, std::index_sequence) 60 | { 61 | return {{ encrypt_at(s, Is)... }}; 62 | } 63 | } 64 | 65 | // An encrypted string is just constructed by encrypting at compile time, 66 | // storing the encrypted array, and decrypting at runtime with a string 67 | // conversion. Note that the null terminator is not stored. 68 | template 69 | class encrypted_string 70 | { 71 | public: 72 | constexpr encrypted_string(const char(&a)[N]) 73 | : m_enc(detail::encrypt(a, std::make_index_sequence())) 74 | {} 75 | 76 | constexpr size_t size() const { return N-1; } 77 | 78 | operator std::string() const 79 | { 80 | return detail::decrypt(S, m_enc.data, N-1); 81 | } 82 | 83 | private: 84 | const detail::char_array m_enc; 85 | }; 86 | 87 | // convenience function for inferring the string size and ensuring no 88 | // accidental runtime encryption 89 | template 90 | constexpr encrypted_string make_encrypted_string(const char(&s)[N]) 91 | { 92 | return true ? encrypted_string(s) : 93 | throw err::strenc_runtime_error; 94 | } 95 | } 96 | 97 | // a macro will allow appropriate seeding 98 | #define CX_ENCSTR_RNGSEED uint64_t{cx::fnv1(__FILE__ __DATE__ __TIME__) + __LINE__} 99 | #define cx_make_encrypted_string cx::make_encrypted_string 100 | -------------------------------------------------------------------------------- /src/include/cx_typeid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cx_murmur3.h" 4 | 5 | //---------------------------------------------------------------------------- 6 | // constexpr typeid 7 | 8 | namespace cx 9 | { 10 | namespace err 11 | { 12 | namespace 13 | { 14 | extern const char* typeid_runtime_error; 15 | } 16 | } 17 | 18 | template 19 | struct typeid_t 20 | { 21 | template 22 | friend constexpr uint32_t type_id(); 23 | 24 | private: 25 | constexpr static const char* name() 26 | { 27 | #ifdef _MSC_VER 28 | // this is untested: __FUNCDNAME__ or __FUNCSIG__ ? 29 | return __FUNCDNAME__; 30 | #else 31 | return __PRETTY_FUNCTION__; 32 | #endif 33 | } 34 | constexpr static uint32_t id = murmur3_32(name(), 0); 35 | }; 36 | 37 | template 38 | constexpr uint32_t type_id() 39 | { 40 | return true ? typeid_t::id : 41 | throw err::typeid_runtime_error; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/include/cx_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //---------------------------------------------------------------------------- 6 | // constexpr utils 7 | 8 | namespace cx 9 | { 10 | // computing strlen with naive recursion will likely exceed the max recursion 11 | // depth on long strings, so compute in a way that limits the recursion depth 12 | // (a really long string will still have problems, but that's unavoidable: we 13 | // have to use recursion in C++11 after all) 14 | namespace err 15 | { 16 | namespace 17 | { 18 | extern const char* strlen_runtime_error; 19 | extern const char* strcmp_runtime_error; 20 | } 21 | } 22 | namespace detail_s 23 | { 24 | struct str 25 | { 26 | const char* s; 27 | int len; 28 | }; 29 | constexpr str stradd(const str& a, const str& b) 30 | { 31 | return { b.s, a.len + b.len }; 32 | } 33 | constexpr str strlen(const str p, int maxdepth) 34 | { 35 | return *p.s == 0 | maxdepth == 0 ? p : 36 | strlen({ p.s+1, p.len+1 }, maxdepth-1); 37 | } 38 | constexpr str strlen_bychunk(const str p, int maxdepth) 39 | { 40 | return *p.s == 0 ? p : 41 | strlen_bychunk(stradd({0, p.len}, strlen({ p.s, 0 }, maxdepth)), maxdepth); 42 | } 43 | } 44 | // max recursion = 256 (strlen, especially of a long string, often happens at 45 | // the beginning of an algorithm, so that should be fine) 46 | constexpr int strlen(const char* s) 47 | { 48 | return true ? 49 | detail_s::strlen_bychunk(detail_s::strlen({s, 0}, 256), 256).len : 50 | throw err::strlen_runtime_error; 51 | } 52 | 53 | constexpr int strcmp(const char* a, const char* b) 54 | { 55 | return *a == 0 && *b == 0 ? 0 : 56 | *a == 0 ? -1 : 57 | *b == 0 ? 1 : 58 | *a < *b ? -1 : 59 | *a > *b ? 1 : 60 | *a == *b ? strcmp(a+1, b+1) : 61 | throw err::strcmp_runtime_error; 62 | } 63 | constexpr int strless(const char* a, const char* b) 64 | { 65 | return strcmp(a, b) == -1; 66 | } 67 | 68 | // convert char* buffer (fragment) to uint32_t (little-endian) 69 | constexpr uint32_t word32le(const char* s, int len) 70 | { 71 | return 72 | (len > 0 ? static_cast(s[0]) : 0) 73 | + (len > 1 ? (static_cast(s[1]) << 8) : 0) 74 | + (len > 2 ? (static_cast(s[2]) << 16) : 0) 75 | + (len > 3 ? (static_cast(s[3]) << 24) : 0); 76 | } 77 | // convert char* buffer (complete) to uint32_t (little-endian) 78 | constexpr uint32_t word32le(const char* s) 79 | { 80 | return word32le(s, 4); 81 | } 82 | 83 | // convert char* buffer (fragment) to uint32_t (big-endian) 84 | constexpr uint32_t word32be(const char* s, int len) 85 | { 86 | return 87 | (len > 0 ? (static_cast(s[0]) << 24) : 0) 88 | + (len > 1 ? (static_cast(s[1]) << 16) : 0) 89 | + (len > 2 ? (static_cast(s[2]) << 8) : 0) 90 | + (len > 3 ? static_cast(s[3]) : 0); 91 | } 92 | // convert char* buffer (complete) to uint32_t (big-endian) 93 | constexpr uint32_t word32be(const char* s) 94 | { 95 | return word32be(s, 4); 96 | } 97 | 98 | // swap endianness of various size integral types 99 | constexpr uint64_t endianswap(uint64_t x) 100 | { 101 | return ((x & 0xff) << 56) 102 | | (((x >> 8) & 0xff) << 48) 103 | | (((x >> 16) & 0xff) << 40) 104 | | (((x >> 24) & 0xff) << 32) 105 | | (((x >> 32) & 0xff) << 24) 106 | | (((x >> 40) & 0xff) << 16) 107 | | (((x >> 48) & 0xff) << 8) 108 | | ((x >> 56) & 0xff); 109 | } 110 | constexpr uint32_t endianswap(uint32_t x) 111 | { 112 | return ((x & 0xff) << 24) 113 | | (((x >> 8) & 0xff) << 16) 114 | | (((x >> 16) & 0xff) << 8) 115 | | ((x >> 24) & 0xff); 116 | } 117 | constexpr uint16_t endianswap(uint16_t x) 118 | { 119 | return ((x & 0xff) << 8) 120 | | ((x >> 8) & 0xff); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_policy (SET CMP0037 OLD) 2 | add_executable (test_${PROJECT_NAME} main cx_algorithm cx_array cx_counter cx_guid cx_hash cx_math cx_numeric cx_pcg32 cx_strenc cx_typeid cx_utils) 3 | -------------------------------------------------------------------------------- /src/test/SConscript: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | name = env['PROJNAME'] + '_test' 4 | env.Program(name, Glob('*.cpp')) 5 | -------------------------------------------------------------------------------- /src/test/cx_algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | void test_cx_algorithm() 9 | { 10 | // count, count_if 11 | { 12 | struct even 13 | { 14 | constexpr bool operator()(int a) { return a % 2 == 0; } 15 | }; 16 | constexpr auto a = cx::make_array(1,2,3,4,5); 17 | 18 | static_assert(cx::count(a.cbegin(), a.cend(), 4) == 1, "count"); 19 | static_assert(cx::count_if(a.cbegin(), a.cend(), even{}) == 2, "count_if"); 20 | } 21 | 22 | // find, find_if, find_if_not 23 | { 24 | struct gt3 25 | { 26 | constexpr bool operator()(int a) { return a > 3; } 27 | }; 28 | struct lt3 29 | { 30 | constexpr bool operator()(int a) { return a < 3; } 31 | }; 32 | constexpr auto a = cx::make_array(1,2,3,4,5); 33 | 34 | static_assert(*cx::find(a.cbegin(), a.cend(), 4) == 4, "find"); 35 | static_assert(*cx::find_if(a.cbegin(), a.cend(), gt3{}) == 4, "find_if"); 36 | static_assert(*cx::find_if_not(a.cbegin(), a.cend(), lt3{}) == 3, "find_if_not"); 37 | } 38 | 39 | // any_of, all_of, none_of 40 | { 41 | struct gt0 42 | { 43 | constexpr bool operator()(int a) { return a > 0; } 44 | }; 45 | struct even 46 | { 47 | constexpr bool operator()(int a) { return a % 2 == 0; } 48 | }; 49 | struct gt5 50 | { 51 | constexpr bool operator()(int a) { return a > 5; } 52 | }; 53 | constexpr auto a = cx::make_array(1,2,3,4,5); 54 | 55 | static_assert(cx::all_of(a.cbegin(), a.cend(), gt0{}), "all_of"); 56 | static_assert(cx::any_of(a.cbegin(), a.cend(), even{}), "any_of"); 57 | static_assert(cx::none_of(a.cbegin(), a.cend(), gt5{}), "all_of"); 58 | } 59 | 60 | // equal 61 | { 62 | constexpr auto a = cx::make_array(1,2,3,4,5); 63 | constexpr auto b = cx::make_array(1,2,3,4,6); 64 | 65 | static_assert(cx::equal(a.cbegin(), a.cend(), a.cbegin()), "equal(1)"); 66 | static_assert(!cx::equal(a.cbegin(), a.cend(), b.cbegin()), "equal(1)"); 67 | 68 | static_assert(cx::equal(a.cbegin(), a.cend(), a.cbegin(), equal_to{}), "equal(2)"); 69 | static_assert(!cx::equal(a.cbegin(), a.cend(), b.cbegin(), equal_to{}), "equal(2)"); 70 | 71 | static_assert(cx::equal(a.cbegin(), a.cend(), 72 | a.cbegin(), a.cend()), "equal(3)"); 73 | static_assert(!cx::equal(a.cbegin(), a.cend(), 74 | a.cbegin(), a.cbegin()), "equal(3)"); 75 | 76 | static_assert(cx::equal(a.cbegin(), a.cend(), 77 | a.cbegin(), a.cend(), equal_to{}), "equal(4)"); 78 | static_assert(!cx::equal(a.cbegin(), a.cend(), 79 | a.cbegin(), a.cbegin(), equal_to{}), "equal(4)"); 80 | } 81 | 82 | // mismatch 83 | { 84 | constexpr auto a = cx::make_array(1,2,3,4,5); 85 | constexpr auto b = cx::make_array(1,2,3,4,6); 86 | 87 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 88 | a.cbegin()).first == a.end(), 89 | "mismatch (1)"); 90 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 91 | b.cbegin()).second == b.end()-1, 92 | "mismatch (1)"); 93 | 94 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 95 | a.cbegin(), equal_to{}).first == a.end(), 96 | "mismatch (2)"); 97 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 98 | b.cbegin(), equal_to{}).second == b.end()-1, 99 | "mismatch (2)"); 100 | 101 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 102 | a.cbegin(), a.cend()).first == a.end(), 103 | "mismatch (3)"); 104 | static_assert(cx::mismatch(a.cbegin(), a.cend()-1, 105 | b.cbegin(), b.cend()-1).first == a.end()-1, 106 | "mismatch (3)"); 107 | 108 | static_assert(cx::mismatch(a.cbegin(), a.cend(), 109 | a.cbegin(), a.cend(), equal_to{}).first == a.end(), 110 | "mismatch (4)"); 111 | static_assert(cx::mismatch(a.cbegin(), a.cend()-1, 112 | b.cbegin(), b.cend()-1, equal_to{}).first == a.end()-1, 113 | "mismatch (4)"); 114 | } 115 | 116 | // find_first_of 117 | { 118 | constexpr auto a = cx::make_array(1,2,3,4,5); 119 | constexpr auto b = cx::make_array(3,4); 120 | 121 | static_assert(cx::find_first_of(a.cbegin(), a.cend(), 122 | b.cbegin(), b.cend()) == a.cbegin()+2, 123 | "find_first_of (1)"); 124 | static_assert(cx::find_first_of(a.cbegin(), a.cend(), 125 | b.cbegin(), b.cend(), equal_to{}) == a.cbegin()+2, 126 | "find_first_of"); 127 | } 128 | 129 | // adjacent_find 130 | { 131 | constexpr auto a = cx::make_array(1,2,3,4,5); 132 | constexpr auto b = cx::make_array(1,2,3,3,4); 133 | static_assert(cx::adjacent_find(a.cbegin(), a.cend()) == a.cend(), 134 | "adjacent_find (1)"); 135 | static_assert(cx::adjacent_find(b.cbegin(), b.cend()) == b.cbegin()+2, 136 | "adjacent_find (1)"); 137 | static_assert(cx::adjacent_find(a.cbegin(), a.cend(), equal_to{}) == a.cend(), 138 | "adjacent_find (2)"); 139 | static_assert(cx::adjacent_find(b.cbegin(), b.cend(), equal_to{}) == b.cbegin()+2, 140 | "adjacent_find (2)"); 141 | } 142 | 143 | // search 144 | { 145 | constexpr auto a = cx::make_array(1,2,3,4,5); 146 | constexpr auto b = cx::make_array(1,3); 147 | constexpr auto c = cx::make_array(3,4); 148 | static_assert(cx::search(a.cbegin(), a.cend(), 149 | b.cbegin(), b.cend()) == a.cend(), 150 | "search (1)"); 151 | static_assert(cx::search(a.cbegin(), a.cend(), 152 | c.cbegin(), c.cend()) == a.cbegin()+2, 153 | "search (1)"); 154 | static_assert(cx::search(a.cbegin(), a.cend(), 155 | b.cbegin(), b.cend(), equal_to{}) == a.cend(), 156 | "search (2)"); 157 | static_assert(cx::search(a.cbegin(), a.cend(), 158 | c.cbegin(), c.cend(), equal_to{}) == a.cbegin()+2, 159 | "search (2)"); 160 | } 161 | 162 | // search_n 163 | { 164 | constexpr auto a = cx::make_array(1,2,3,3,4); 165 | static_assert(cx::search_n(a.cbegin(), a.cend(), 2, 3) == a.cbegin()+2, 166 | "search_n (1)"); 167 | static_assert(cx::search_n(a.cbegin(), a.cend(), 2, 4) == a.cend(), 168 | "search_n (1)"); 169 | static_assert(cx::search_n(a.cbegin(), a.cend(), 2, 3, equal_to{}) == a.cbegin()+2, 170 | "search_n (2)"); 171 | static_assert(cx::search_n(a.cbegin(), a.cend(), 2, 4, equal_to{}) == a.cend(), 172 | "search_n (2)"); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/test/cx_array.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | constexpr char to_upper(char c) 5 | { 6 | return c < 'a' || c > 'z' ? c : c - ('a' - 'A'); 7 | } 8 | 9 | constexpr int sum(int a, int b) 10 | { 11 | return a + b; 12 | } 13 | 14 | constexpr bool cless(char a, char b) 15 | { 16 | // for sorting strings-as-arrays: keep the null-terminator at the end 17 | return a != 0 && a < b; 18 | } 19 | 20 | constexpr bool even(int i) 21 | { 22 | return i % 2 == 0; 23 | } 24 | 25 | void test_cx_array() 26 | { 27 | constexpr auto test = cx::make_array(1,2,3,4,5); 28 | 29 | { 30 | constexpr auto a = cx::transform(cx::make_array("abcde"), to_upper); 31 | static_assert(a[0] == 'A' && a[4] == 'E', "array map"); 32 | 33 | constexpr auto b = cx::transform(test, test, sum); 34 | static_assert(b[0] == 2 && b[4] == 10, "array 2-arg map"); 35 | } 36 | 37 | { 38 | constexpr auto c = cx::make_array(1,2,3,4,6); 39 | static_assert(test < c, "array operator<"); 40 | 41 | constexpr auto d = cx::make_array(1,2,3,4,5); 42 | static_assert(test != c, "array operator!="); 43 | static_assert(test == d, "array operator=="); 44 | } 45 | 46 | { 47 | constexpr auto e = test.push_back(6); 48 | static_assert(e[0] == 1 && e[5] == 6, "array push_back"); 49 | 50 | constexpr auto f = test.push_front(6); 51 | static_assert(f[0] == 6 && f[1] == 1, "array push_front"); 52 | } 53 | 54 | { 55 | constexpr auto g = cx::make_array(6,7,8,9); 56 | constexpr auto h = test.concat(g); 57 | static_assert(h[0] == 1 && h[8] == 9, "array concat"); 58 | } 59 | 60 | { 61 | constexpr auto i = test.tail(); 62 | static_assert(i.size() == 4 && i[0] == 2, "array tail(1)"); 63 | 64 | constexpr auto j = test.tail<2>(); 65 | static_assert(j.size() == 3 && j[0] == 3, "array tail(2)"); 66 | } 67 | 68 | { 69 | constexpr auto k = test.init(); 70 | static_assert(k.size() == 4 && k[3] == 4, "array init(1)"); 71 | 72 | constexpr auto l = test.init<2>(); 73 | static_assert(l.size() == 3 && l[2] == 3, "array init(2)"); 74 | } 75 | 76 | { 77 | constexpr auto m = test.insert<1>(9); 78 | static_assert(m.size() == 6 && 79 | m[0] == 1 && m[1] == 9 && m[2] == 2, "array insert"); 80 | constexpr auto n = test.insert<0>(9); 81 | static_assert(n.size() == 6 && 82 | n[0] == 9 && n[1] == 1, "array insert(0)"); 83 | constexpr auto o = test.insert<5>(9); 84 | static_assert(o.size() == 6 && 85 | o[4] == 5 && o[5] == 9, "array insert(N)"); 86 | } 87 | 88 | { 89 | constexpr auto p = cx::make_array("jackdwslovemybigphnxfqurtz"); 90 | constexpr auto q = cx::sort(p, cless); 91 | static_assert(q[0] == 'a' && q[1] == 'b' && 92 | q[2] == 'c' && q[3] == 'd', "array sort"); 93 | } 94 | 95 | { 96 | constexpr auto r = cx::make_array("Mad Hatter", "Alice", 97 | "March Hare", "Dormouse"); 98 | constexpr auto s = cx::sort(r, cx::strless); 99 | static_assert(!cx::strcmp(s[0], "Alice") && 100 | !cx::strcmp(s[1], "Dormouse") && 101 | !cx::strcmp(s[2], "Mad Hatter") && 102 | !cx::strcmp(s[3], "March Hare"), "tea party"); 103 | } 104 | 105 | { 106 | constexpr auto a = cx::make_array(5,4,3,2,1); 107 | static_assert(a == cx::reverse(test), "array reverse"); 108 | } 109 | 110 | { 111 | constexpr auto a = cx::make_array(2,4,1,3,5); 112 | static_assert(a == cx::partition(test, even), "array partition"); 113 | } 114 | 115 | { 116 | // remove_if can be done something like this... 117 | constexpr size_t C = cx::count_if(test.cbegin(), test.cend(), even); 118 | constexpr auto r = cx::partition(test, even).slice<0, C>(); 119 | constexpr auto a = cx::make_array(2,4); 120 | static_assert(a == r, "array remove_if"); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/test/cx_counter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test_cx_counter() 4 | { 5 | static_assert(cx::counter() == 0, "counter(0)"); 6 | static_assert(cx::counter() == 1, "counter(1)"); 7 | static_assert(cx::counter() == 2, "counter(2)"); 8 | static_assert(cx::counter() == 3, "counter(3)"); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/cx_guid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test_cx_guid() 4 | { 5 | constexpr auto g = cx_guid(); 6 | static_assert(g.data3 >> 12 == 0x4, "4xxx"); 7 | static_assert(g.data4 >> 60 >= 0x8 && g.data4 >> 60 <= 0xb, "8,9,a,b"); 8 | } 9 | -------------------------------------------------------------------------------- /src/test/cx_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void test_cx_hash() 7 | { 8 | //---------------------------------------------------------------------------- 9 | // FNV1 / FNV1A 10 | static_assert(cx::fnv1("hello, world") == 733686394982303293ull, 11 | "fnv1(\"hello, world\")"); 12 | static_assert(cx::fnv1a("hello, world") == 1702823495152329533ull, 13 | "fnv1a(\"hello, world\")"); 14 | 15 | //---------------------------------------------------------------------------- 16 | // Murmur3 17 | static_assert(cx::murmur3_32("hello, world", 0) == 345750399, 18 | "murmur3(\"hello, world\")"); 19 | static_assert(cx::murmur3_32("hello, world1", 0) == 3714214180, 20 | "murmur3(\"hello, world\")"); 21 | static_assert(cx::murmur3_32("hello, world12", 0) == 83041023, 22 | "murmur3(\"hello, world\")"); 23 | static_assert(cx::murmur3_32("hello, world123", 0) == 209220029, 24 | "murmur3(\"hello, world\")"); 25 | static_assert(cx::murmur3_32("hello, world1234", 0) == 4241062699, 26 | "murmur3(\"hello, world\")"); 27 | static_assert(cx::murmur3_32("hello, world", 1) == 1868346089, 28 | "murmur3(\"hello, world\")"); 29 | 30 | //---------------------------------------------------------------------------- 31 | constexpr const char* const testinputs[8] = { 32 | "", 33 | "a", 34 | "abc", 35 | "message digest", 36 | "abcdefghijklmnopqrstuvwxyz", 37 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 38 | "12345678901234567890123456789012345678901234567890123456789012345678901234567890", 39 | "hello, world", 40 | }; 41 | 42 | //---------------------------------------------------------------------------- 43 | // MD5 44 | constexpr cx::md5sum md5sums[8] = { 45 | { { 0xd41d8cd9, 0x8f00b204, 0xe9800998, 0xecf8427e } }, 46 | { { 0x0cc175b9, 0xc0f1b6a8, 0x31c399e2, 0x69772661 } }, 47 | { { 0x90015098, 0x3cd24fb0, 0xd6963f7d, 0x28e17f72 } }, 48 | { { 0xf96b697d, 0x7cb7938d, 0x525a2f31, 0xaaf161d0 } }, 49 | { { 0xc3fcd3d7, 0x6192e400, 0x7dfb496c, 0xca67e13b } }, 50 | { { 0xd174ab98, 0xd277d9f5, 0xa5611c2c, 0x9f419d9f } }, 51 | { { 0x57edf4a2, 0x2be3c955, 0xac49da2e, 0x2107b67a } }, 52 | { { 0xe4d7f1b4, 0xed2e42d1, 0x5898f4b2, 0x7b019da4 } } 53 | }; 54 | 55 | static_assert(cx::endianswap(cx::md5(testinputs[0]).h[0]) == md5sums[0].h[0] && 56 | cx::endianswap(cx::md5(testinputs[0]).h[1]) == md5sums[0].h[1] && 57 | cx::endianswap(cx::md5(testinputs[0]).h[2]) == md5sums[0].h[2] && 58 | cx::endianswap(cx::md5(testinputs[0]).h[3]) == md5sums[0].h[3], 59 | "md5(\"\")"); 60 | static_assert(cx::endianswap(cx::md5(testinputs[1]).h[0]) == md5sums[1].h[0] && 61 | cx::endianswap(cx::md5(testinputs[1]).h[1]) == md5sums[1].h[1] && 62 | cx::endianswap(cx::md5(testinputs[1]).h[2]) == md5sums[1].h[2] && 63 | cx::endianswap(cx::md5(testinputs[1]).h[3]) == md5sums[1].h[3], 64 | "md5(\"a\")"); 65 | static_assert(cx::endianswap(cx::md5(testinputs[2]).h[0]) == md5sums[2].h[0] && 66 | cx::endianswap(cx::md5(testinputs[2]).h[1]) == md5sums[2].h[1] && 67 | cx::endianswap(cx::md5(testinputs[2]).h[2]) == md5sums[2].h[2] && 68 | cx::endianswap(cx::md5(testinputs[2]).h[3]) == md5sums[2].h[3], 69 | "md5(\"abc\")"); 70 | static_assert(cx::endianswap(cx::md5(testinputs[3]).h[0]) == md5sums[3].h[0] && 71 | cx::endianswap(cx::md5(testinputs[3]).h[1]) == md5sums[3].h[1] && 72 | cx::endianswap(cx::md5(testinputs[3]).h[2]) == md5sums[3].h[2] && 73 | cx::endianswap(cx::md5(testinputs[3]).h[3]) == md5sums[3].h[3], 74 | "md5(\"message digest\")"); 75 | static_assert(cx::endianswap(cx::md5(testinputs[4]).h[0]) == md5sums[4].h[0] && 76 | cx::endianswap(cx::md5(testinputs[4]).h[1]) == md5sums[4].h[1] && 77 | cx::endianswap(cx::md5(testinputs[4]).h[2]) == md5sums[4].h[2] && 78 | cx::endianswap(cx::md5(testinputs[4]).h[3]) == md5sums[4].h[3], 79 | "md5(\"abcdefghijklmnopqrstuvwxyz\")"); 80 | static_assert(cx::endianswap(cx::md5(testinputs[5]).h[0]) == md5sums[5].h[0] && 81 | cx::endianswap(cx::md5(testinputs[5]).h[1]) == md5sums[5].h[1] && 82 | cx::endianswap(cx::md5(testinputs[5]).h[2]) == md5sums[5].h[2] && 83 | cx::endianswap(cx::md5(testinputs[5]).h[3]) == md5sums[5].h[3], 84 | "md5(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\")"); 85 | static_assert(cx::endianswap(cx::md5(testinputs[6]).h[0]) == md5sums[6].h[0] && 86 | cx::endianswap(cx::md5(testinputs[6]).h[1]) == md5sums[6].h[1] && 87 | cx::endianswap(cx::md5(testinputs[6]).h[2]) == md5sums[6].h[2] && 88 | cx::endianswap(cx::md5(testinputs[6]).h[3]) == md5sums[6].h[3], 89 | "md5(\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\")"); 90 | static_assert(cx::endianswap(cx::md5(testinputs[7]).h[0]) == md5sums[7].h[0] && 91 | cx::endianswap(cx::md5(testinputs[7]).h[1]) == md5sums[7].h[1] && 92 | cx::endianswap(cx::md5(testinputs[7]).h[2]) == md5sums[7].h[2] && 93 | cx::endianswap(cx::md5(testinputs[7]).h[3]) == md5sums[7].h[3], 94 | "md5(\"hello, world\")"); 95 | 96 | //---------------------------------------------------------------------------- 97 | // SHA256 98 | constexpr cx::sha256sum sha256sums[8] = { 99 | { { 0xe3b0c442, 0x98fc1c14, 0x9afbf4c8, 0x996fb924, 100 | 0x27ae41e4, 0x649b934c, 0xa495991b, 0x7852b855 } }, 101 | { { 0xca978112, 0xca1bbdca, 0xfac231b3, 0x9a23dc4d, 102 | 0xa786eff8, 0x147c4e72, 0xb9807785, 0xafee48bb } }, 103 | { { 0xba7816bf, 0x8f01cfea, 0x414140de, 0x5dae2223, 104 | 0xb00361a3, 0x96177a9c, 0xb410ff61, 0xf20015ad } }, 105 | { { 0xf7846f55, 0xcf23e14e, 0xebeab5b4, 0xe1550cad, 106 | 0x5b509e33, 0x48fbc4ef, 0xa3a1413d, 0x393cb650 } }, 107 | { { 0x71c480df, 0x93d6ae2f, 0x1efad144, 0x7c66c952, 108 | 0x5e316218, 0xcf51fc8d, 0x9ed832f2, 0xdaf18b73 } }, 109 | { { 0xdb4bfcbd, 0x4da0cd85, 0xa60c3c37, 0xd3fbd880, 110 | 0x5c77f15f, 0xc6b1fdfe, 0x614ee0a7, 0xc8fdb4c0 } }, 111 | { { 0xf371bc4a, 0x311f2b00, 0x9eef952d, 0xd83ca80e, 112 | 0x2b60026c, 0x8e935592, 0xd0f9c308, 0x453c813e } }, 113 | { { 0x09ca7e4e, 0xaa6e8ae9, 0xc7d26116, 0x71291848, 114 | 0x83644d07, 0xdfba7cbf, 0xbc4c8a2e, 0x08360d5b } } 115 | }; 116 | 117 | static_assert(cx::endianswap(cx::sha256(testinputs[0]).h[0]) == sha256sums[0].h[0] && 118 | cx::endianswap(cx::sha256(testinputs[0]).h[1]) == sha256sums[0].h[1] && 119 | cx::endianswap(cx::sha256(testinputs[0]).h[2]) == sha256sums[0].h[2] && 120 | cx::endianswap(cx::sha256(testinputs[0]).h[3]) == sha256sums[0].h[3] && 121 | cx::endianswap(cx::sha256(testinputs[0]).h[4]) == sha256sums[0].h[4] && 122 | cx::endianswap(cx::sha256(testinputs[0]).h[5]) == sha256sums[0].h[5] && 123 | cx::endianswap(cx::sha256(testinputs[0]).h[6]) == sha256sums[0].h[6] && 124 | cx::endianswap(cx::sha256(testinputs[0]).h[7]) == sha256sums[0].h[7], 125 | "sha256(\"\")"); 126 | static_assert(cx::endianswap(cx::sha256(testinputs[1]).h[0]) == sha256sums[1].h[0] && 127 | cx::endianswap(cx::sha256(testinputs[1]).h[1]) == sha256sums[1].h[1] && 128 | cx::endianswap(cx::sha256(testinputs[1]).h[2]) == sha256sums[1].h[2] && 129 | cx::endianswap(cx::sha256(testinputs[1]).h[3]) == sha256sums[1].h[3] && 130 | cx::endianswap(cx::sha256(testinputs[1]).h[4]) == sha256sums[1].h[4] && 131 | cx::endianswap(cx::sha256(testinputs[1]).h[5]) == sha256sums[1].h[5] && 132 | cx::endianswap(cx::sha256(testinputs[1]).h[6]) == sha256sums[1].h[6] && 133 | cx::endianswap(cx::sha256(testinputs[1]).h[7]) == sha256sums[1].h[7], 134 | "sha256(\"a\")"); 135 | static_assert(cx::endianswap(cx::sha256(testinputs[2]).h[0]) == sha256sums[2].h[0] && 136 | cx::endianswap(cx::sha256(testinputs[2]).h[1]) == sha256sums[2].h[1] && 137 | cx::endianswap(cx::sha256(testinputs[2]).h[2]) == sha256sums[2].h[2] && 138 | cx::endianswap(cx::sha256(testinputs[2]).h[3]) == sha256sums[2].h[3] && 139 | cx::endianswap(cx::sha256(testinputs[2]).h[4]) == sha256sums[2].h[4] && 140 | cx::endianswap(cx::sha256(testinputs[2]).h[5]) == sha256sums[2].h[5] && 141 | cx::endianswap(cx::sha256(testinputs[2]).h[6]) == sha256sums[2].h[6] && 142 | cx::endianswap(cx::sha256(testinputs[2]).h[7]) == sha256sums[2].h[7], 143 | "sha256(\"abc\")"); 144 | static_assert(cx::endianswap(cx::sha256(testinputs[3]).h[0]) == sha256sums[3].h[0] && 145 | cx::endianswap(cx::sha256(testinputs[3]).h[1]) == sha256sums[3].h[1] && 146 | cx::endianswap(cx::sha256(testinputs[3]).h[2]) == sha256sums[3].h[2] && 147 | cx::endianswap(cx::sha256(testinputs[3]).h[3]) == sha256sums[3].h[3] && 148 | cx::endianswap(cx::sha256(testinputs[3]).h[4]) == sha256sums[3].h[4] && 149 | cx::endianswap(cx::sha256(testinputs[3]).h[5]) == sha256sums[3].h[5] && 150 | cx::endianswap(cx::sha256(testinputs[3]).h[6]) == sha256sums[3].h[6] && 151 | cx::endianswap(cx::sha256(testinputs[3]).h[7]) == sha256sums[3].h[7], 152 | "sha256(\"message digest\")"); 153 | static_assert(cx::endianswap(cx::sha256(testinputs[4]).h[0]) == sha256sums[4].h[0] && 154 | cx::endianswap(cx::sha256(testinputs[4]).h[1]) == sha256sums[4].h[1] && 155 | cx::endianswap(cx::sha256(testinputs[4]).h[2]) == sha256sums[4].h[2] && 156 | cx::endianswap(cx::sha256(testinputs[4]).h[3]) == sha256sums[4].h[3] && 157 | cx::endianswap(cx::sha256(testinputs[4]).h[4]) == sha256sums[4].h[4] && 158 | cx::endianswap(cx::sha256(testinputs[4]).h[5]) == sha256sums[4].h[5] && 159 | cx::endianswap(cx::sha256(testinputs[4]).h[6]) == sha256sums[4].h[6] && 160 | cx::endianswap(cx::sha256(testinputs[4]).h[7]) == sha256sums[4].h[7], 161 | "sha256(\"abcdefghijklmnopqrstuvwxyz\")"); 162 | static_assert(cx::endianswap(cx::sha256(testinputs[5]).h[0]) == sha256sums[5].h[0] && 163 | cx::endianswap(cx::sha256(testinputs[5]).h[1]) == sha256sums[5].h[1] && 164 | cx::endianswap(cx::sha256(testinputs[5]).h[2]) == sha256sums[5].h[2] && 165 | cx::endianswap(cx::sha256(testinputs[5]).h[3]) == sha256sums[5].h[3] && 166 | cx::endianswap(cx::sha256(testinputs[5]).h[4]) == sha256sums[5].h[4] && 167 | cx::endianswap(cx::sha256(testinputs[5]).h[5]) == sha256sums[5].h[5] && 168 | cx::endianswap(cx::sha256(testinputs[5]).h[6]) == sha256sums[5].h[6] && 169 | cx::endianswap(cx::sha256(testinputs[5]).h[7]) == sha256sums[5].h[7], 170 | "sha256(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\")"); 171 | static_assert(cx::endianswap(cx::sha256(testinputs[6]).h[0]) == sha256sums[6].h[0] && 172 | cx::endianswap(cx::sha256(testinputs[6]).h[1]) == sha256sums[6].h[1] && 173 | cx::endianswap(cx::sha256(testinputs[6]).h[2]) == sha256sums[6].h[2] && 174 | cx::endianswap(cx::sha256(testinputs[6]).h[3]) == sha256sums[6].h[3] && 175 | cx::endianswap(cx::sha256(testinputs[6]).h[4]) == sha256sums[6].h[4] && 176 | cx::endianswap(cx::sha256(testinputs[6]).h[5]) == sha256sums[6].h[5] && 177 | cx::endianswap(cx::sha256(testinputs[6]).h[6]) == sha256sums[6].h[6] && 178 | cx::endianswap(cx::sha256(testinputs[6]).h[7]) == sha256sums[6].h[7], 179 | "sha256(\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\")"); 180 | static_assert(cx::endianswap(cx::sha256(testinputs[7]).h[0]) == sha256sums[7].h[0] && 181 | cx::endianswap(cx::sha256(testinputs[7]).h[1]) == sha256sums[7].h[1] && 182 | cx::endianswap(cx::sha256(testinputs[7]).h[2]) == sha256sums[7].h[2] && 183 | cx::endianswap(cx::sha256(testinputs[7]).h[3]) == sha256sums[7].h[3] && 184 | cx::endianswap(cx::sha256(testinputs[7]).h[4]) == sha256sums[7].h[4] && 185 | cx::endianswap(cx::sha256(testinputs[7]).h[5]) == sha256sums[7].h[5] && 186 | cx::endianswap(cx::sha256(testinputs[7]).h[6]) == sha256sums[7].h[6] && 187 | cx::endianswap(cx::sha256(testinputs[7]).h[7]) == sha256sums[7].h[7], 188 | "sha256(\"hello, world\")"); 189 | } 190 | -------------------------------------------------------------------------------- /src/test/cx_math.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //------------------------------------------------------------------------------ 4 | // constexpr floating-point "equality" (within epsilon) 5 | template 6 | constexpr bool feq(T x, T y) 7 | { 8 | return cx::abs(x - y) <= std::numeric_limits::epsilon(); 9 | } 10 | 11 | void test_cx_math() 12 | { 13 | // All constants referenced from Wolfram Alpha :) 14 | 15 | //---------------------------------------------------------------------------- 16 | // abs 17 | static_assert(feq(1.0f, cx::abs(-1.0f)), "abs(-1.0f)"); 18 | static_assert(feq(1.0f, cx::abs(1.0f)), "abs(1.0f)"); 19 | static_assert(feq(1.0, cx::abs(-1.0)), "abs(-1.0)"); 20 | static_assert(feq(1.0, cx::abs(1.0)), "abs(1.0)"); 21 | static_assert(feq(1.0l, cx::abs(-1.0l)), "abs(-1.0l)"); 22 | static_assert(feq(1.0l, cx::abs(1.0l)), "abs(1.0l)"); 23 | 24 | //---------------------------------------------------------------------------- 25 | // fabs 26 | static_assert(feq(1.0f, cx::fabs(-1.0f)), "fabs(-1.0f)"); 27 | static_assert(feq(1.0f, cx::fabs(1.0f)), "fabs(-1.0f)"); 28 | static_assert(feq(1.0, cx::fabs(-1.0)), "fabs(-1.0)"); 29 | static_assert(feq(1.0, cx::fabs(1.0)), "fabs(1.0)"); 30 | static_assert(feq(1.0l, cx::fabs(-1.0l)), "fabs(-1.0l)"); 31 | static_assert(feq(1.0l, cx::fabs(1.0l)), "fabs(1.0l)"); 32 | static_assert(1.0 == cx::fabs(1), "fabs(1)"); 33 | static_assert(1.0 == cx::fabs(-1), "fabs(1)"); 34 | 35 | //---------------------------------------------------------------------------- 36 | // sqrt 37 | // square root of 2 = 1.414213562373095048802 38 | constexpr float rt2f = 1.4142136f; 39 | constexpr double rt2 = 1.41421356237309505; 40 | constexpr long double rt2l = 1.4142135623730950488l; 41 | 42 | static_assert(feq(1.0f, cx::sqrt(1.0f)), "sqrt(1.0f)"); 43 | static_assert(feq(rt2f, cx::sqrt(2.0f)), "sqrt(2.0f)"); 44 | static_assert(feq(1.0, cx::sqrt(1.0)), "sqrt(1.0)"); 45 | static_assert(feq(rt2, cx::sqrt(2.0)), "sqrt(2.0)"); 46 | static_assert(feq(1.0l, cx::sqrt(1.0l)), "sqrt(1.0l)"); 47 | static_assert(feq(rt2l, cx::sqrt(2.0l)), "sqrt(2.0l)"); 48 | static_assert(feq(2.0, cx::sqrt(4)), "sqrt(4)"); 49 | 50 | //---------------------------------------------------------------------------- 51 | // cbrt 52 | // cube root of 2 = 1.259921049894873164767 53 | static_assert(feq(1.0f, cx::cbrt(1.0f)), "cbrt(1.0f)"); 54 | static_assert(feq(1.259921f, cx::cbrt(2.0f)), "cbrt(2.0f)"); 55 | static_assert(feq(1.0, cx::cbrt(1.0)), "cbrt(1.0)"); 56 | static_assert(feq(1.259921049894873, cx::cbrt(2.0)), "cbrt(2.0)"); 57 | static_assert(feq(1.0l, cx::cbrt(1.0l)), "cbrt(1.0l)"); 58 | static_assert(feq(1.2599210498948731648l, cx::cbrt(2.0l)), "cbrt(2.0l)"); 59 | static_assert(feq(2.0, cx::cbrt(8)), "cbrt(8)"); 60 | 61 | //---------------------------------------------------------------------------- 62 | // hypot 63 | static_assert(feq(5.0, cx::hypot(3, 4)), "hypot(3, 4)"); 64 | static_assert(feq(13.0, cx::hypot(5, 12)), "hypot(5, 12)"); 65 | 66 | //---------------------------------------------------------------------------- 67 | // exp 68 | // e = 2.718281828459045235360 69 | static_assert(feq(2.718282f, cx::exp(1.0f)), "exp(1.0f)"); 70 | static_assert(feq(2.7182818284590454, cx::exp(1.0)), "exp(1.0)"); 71 | static_assert(feq(2.7182818284590452354l, cx::exp(1.0l)), "exp(1.0l)"); 72 | static_assert(feq(2.7182818284590454, cx::exp(1)), "exp(1)"); 73 | 74 | //---------------------------------------------------------------------------- 75 | // sin 76 | // pi = 3.141592653589793238463 77 | constexpr float PIf = 3.1415927f; 78 | constexpr double PI = 3.141592653589793; 79 | constexpr long double PIl = 3.1415926535897932385l; 80 | 81 | static_assert(feq(0.0f, cx::sin(PIf)), "sin(PIf)"); 82 | static_assert(feq(0.0, cx::sin(PI)), "sin(PI)"); 83 | static_assert(feq(0.0l, cx::sin(PIl)), "sin(PIl)"); 84 | 85 | // pi/2 = 1.570796326794896619231 86 | constexpr float PI2f = 1.57079633f; 87 | constexpr double PI2 = 1.5707963267948966; 88 | constexpr long double PI2l = 1.5707963267948966192l; 89 | 90 | static_assert(feq(1.0f, cx::sin(PI2f)), "sin(PI/2f)"); 91 | static_assert(feq(1.0, cx::sin(PI2)), "sin(PI/2)"); 92 | static_assert(feq(1.0l, cx::sin(PI2l)), "sin(PI/2l)"); 93 | 94 | // sin(1) = 0.8414709848078965066525 95 | static_assert(feq(0.8414709848078965, cx::sin(1)), "sin(1)"); 96 | 97 | //---------------------------------------------------------------------------- 98 | // cos 99 | static_assert(feq(1.0f, cx::cos(0.0f)), "cos(0f)"); 100 | static_assert(feq(1.0, cx::cos(0.0)), "cos(0)"); 101 | static_assert(feq(1.0l, cx::cos(0.0l)), "cos(0l)"); 102 | static_assert(feq(0.0f, cx::cos(PI2f)), "cos(PI/2f)"); 103 | static_assert(feq(0.0, cx::cos(PI2)), "cos(PI/2)"); 104 | static_assert(feq(0.0l, cx::cos(PI2l)), "cos(PI/2l)"); 105 | 106 | // cos(1) = 0.5403023058681397174009 107 | static_assert(feq(0.5403023058681397, cx::cos(1)), "cos(1)"); 108 | 109 | //---------------------------------------------------------------------------- 110 | // tan 111 | static_assert(feq(0.0f, cx::tan(0.0f)), "tan(0f)"); 112 | static_assert(feq(0.0, cx::tan(0.0)), "tan(0)"); 113 | static_assert(feq(0.0l, cx::tan(0.0l)), "tan(0l)"); 114 | 115 | // pi/4 = 0.785398163397448309615 116 | constexpr float PI4f = 0.78539816f; 117 | constexpr double PI4 = 0.7853981633974483; 118 | constexpr long double PI4l = 0.78539816339744830962l; 119 | 120 | static_assert(feq(1.0f, cx::tan(PI4f)), "tan(PI/4f)"); 121 | static_assert(feq(1.0, cx::tan(PI4)), "tan(PI/4)"); 122 | static_assert(feq(1.0l, cx::tan(PI4l)), "tan(PI/4l)"); 123 | 124 | // tan(1) = 1.55740772465490223050697 125 | static_assert(feq(1.5574077246549022, cx::tan(1)), "tan(1)"); 126 | 127 | //---------------------------------------------------------------------------- 128 | // atan & atan2 129 | // arctan(1) = pi/4 = 0.7853981633974483096157 130 | // (1 ulp error for long double) 131 | static_assert(feq(PI4f, cx::atan(1.0f)), "atan(1.0f)"); 132 | static_assert(feq(PI4, cx::atan(1.0)), "atan(1.0)"); 133 | static_assert(feq(0.78539816339744830966l, cx::atan(1.0l)), "atan(1.0l)"); 134 | static_assert(feq(PI4, cx::atan(1)), "atan(1)"); 135 | 136 | // arctan(pi/4) = 0.6657737500283538635905 137 | // (1 ulp error for long double) 138 | static_assert(feq(0.66577375f, cx::atan(PI4f)), "atan(PI/4f)"); 139 | static_assert(feq(0.665773750028354, cx::atan(PI4)), "atan(PI/4)"); 140 | static_assert(feq(0.6657737500283538635l, cx::atan(PI4l)), "atan(PI/4l)"); 141 | 142 | // arctan(2) = 1.1071487177940905030171 143 | // (1 ulp error for long double) 144 | static_assert(feq(1.1071487f, cx::atan(2.0f)), "atan(2.0f)"); 145 | static_assert(feq(1.10714871779409, cx::atan(2.0)), "atan(2.0)"); 146 | static_assert(feq(1.1071487177940905028l, cx::atan(2.0l)), "atan(2.0l)"); 147 | 148 | // atan(1,1) = pi/4 149 | static_assert(feq(PI4, cx::atan2(1.0, 1.0)), "atan2(1.0, 1.0)"); 150 | static_assert(feq(3.0*PI4, cx::atan2(-1.0, 1.0)), "atan2(-1.0, 1.0)"); 151 | static_assert(feq(-PI4, cx::atan2(1.0, -1.0)), "atan2(1.0, -1.0)"); 152 | static_assert(feq(-3.0*PI4, cx::atan2(-1.0, -1.0)), "atan2(-1.0, -1.0)"); 153 | static_assert(feq(PI4, cx::atan2(1, 1)), "atan2(1, 1)"); 154 | 155 | //---------------------------------------------------------------------------- 156 | // asin & acos 157 | // asin(1) = pi/2 158 | static_assert(feq(PI2f, cx::asin(1.0f)), "asin(1.0f)"); 159 | static_assert(feq(PI2, cx::asin(1.0)), "asin(1.0)"); 160 | static_assert(feq(PI2l, cx::asin(1.0l)), "asin(1.0l)"); 161 | static_assert(feq(PI2, cx::asin(1)), "asin(1)"); 162 | 163 | // asin(0.5) = pi/6 164 | // pi/6 = 0.523598775598298873077 165 | constexpr float PI6f = 0.5235988f; 166 | constexpr double PI6 = 0.523598775598299; 167 | constexpr long double PI6l = 0.523598775598298873l; 168 | 169 | static_assert(feq(PI6f, cx::asin(0.5f)), "asin(0.5f)"); 170 | static_assert(feq(PI6, cx::asin(0.5)), "asin(0.5)"); 171 | static_assert(feq(PI6l, cx::asin(0.5l)), "asin(0.5l)"); 172 | 173 | // acos(0) = pi/2 174 | static_assert(feq(PI2f, cx::acos(0.0f)), "acos(0.0f)"); 175 | static_assert(feq(PI2, cx::acos(0.0)), "acos(0.0)"); 176 | static_assert(feq(PI2l, cx::acos(0.0l)), "acos(0.0l)"); 177 | static_assert(feq(PI2, cx::acos(0)), "acos(0)"); 178 | 179 | //---------------------------------------------------------------------------- 180 | // floor and ceil 181 | static_assert(cx::floor(PIf) == 3.0f, "floor(PIf)"); 182 | static_assert(cx::ceil(PIf) == 4.0f, "ceil(PIf)"); 183 | static_assert(cx::floor(-PIf) == -4.0f, "floor(-PIf)"); 184 | static_assert(cx::ceil(-PIf) == -3.0f, "ceil(-PIf)"); 185 | 186 | static_assert(cx::floor(PI) == 3.0, "floor(PI)"); 187 | static_assert(cx::ceil(PI) == 4.0, "ceil(PI)"); 188 | static_assert(cx::floor(-PI) == -4.0, "floor(-PI)"); 189 | static_assert(cx::ceil(-PI) == -3.0, "ceil(-PI)"); 190 | 191 | #if __cplusplus == 201402L 192 | static_assert(cx::floor(PIl) == 3.0l, "floor(PIl)"); 193 | static_assert(cx::ceil(PIl) == 4.0l, "ceil(PIl)"); 194 | static_assert(cx::floor(-PIl) == -4.0l, "floor(-PIl)"); 195 | static_assert(cx::ceil(-PIl) == -3.0l, "ceil(-PIl)"); 196 | #endif 197 | 198 | static_assert(cx::floor(1) == 1.0, "floor(1)"); 199 | static_assert(cx::ceil(1) == 1.0, "ceil(1)"); 200 | 201 | //---------------------------------------------------------------------------- 202 | // trunc 203 | static_assert(cx::trunc(PIf) == 3.0f, "trunc(PIf)"); 204 | static_assert(cx::trunc(-PIf) == -3.0f, "trunc(-PIf)"); 205 | static_assert(cx::trunc(PI) == 3.0, "trunc(PI)"); 206 | static_assert(cx::trunc(-PI) == -3.0, "trunc(-PI)"); 207 | #if __cplusplus == 201402L 208 | static_assert(cx::trunc(PIl) == 3.0l, "trunc(PIl)"); 209 | static_assert(cx::trunc(-PIl) == -3.0l, "trunc(-PIl)"); 210 | #endif 211 | static_assert(cx::trunc(1) == 1.0, "trunc(1)"); 212 | 213 | //---------------------------------------------------------------------------- 214 | // round 215 | static_assert(cx::round(1.5f) == 2.0f, "round(1.5f)"); 216 | static_assert(cx::round(-1.5f) == -2.0f, "round(-1.5f)"); 217 | static_assert(cx::round(1.5) == 2.0, "round(1.5)"); 218 | static_assert(cx::round(-1.5) == -2.0, "round(-1.5)"); 219 | #if __cplusplus == 201402L 220 | static_assert(cx::round(1.5l) == 2.0l, "round(1.5l)"); 221 | static_assert(cx::round(-1.5l) == -2.0l, "round(-1.5l)"); 222 | #endif 223 | static_assert(cx::round(1) == 1.0, "round(1)"); 224 | 225 | //---------------------------------------------------------------------------- 226 | // fmod 227 | static_assert(feq(1.0f, cx::fmod(9.0f, 4.0f)), "fmod(9.0f, 4.0f)"); 228 | static_assert(feq(1.0, cx::fmod(9.0, 4.0)), "fmod(9.0, 4.0)"); 229 | #if __cplusplus == 201402L 230 | static_assert(feq(1.0l, cx::fmod(9.0l, 4.0l)), "fmod(9.0l, 4.0l)"); 231 | #endif 232 | 233 | static_assert(feq(1.0, cx::fmod(9, 4)), "fmod(9, 4)"); 234 | static_assert(feq(1.0, cx::fmod(9, 4.0)), "fmod(9, 4.0)"); 235 | #if __cplusplus == 201402L 236 | static_assert(feq(1.0l, cx::fmod(9, 4.0l)), "fmod(9, 4.0l)"); 237 | static_assert(feq(1.0l, cx::fmod(9.0l, 4)), "fmod(9.0l, 4)"); 238 | static_assert(feq(1.0l, cx::fmod(9.0l, 4.0l)), "fmod(9.0l, 4.0l)"); 239 | #endif 240 | 241 | //---------------------------------------------------------------------------- 242 | // remainder 243 | static_assert(feq(1.0f, cx::remainder(9.0f, 4.0f)), "remainder(9.0f, 4.0f)"); 244 | static_assert(feq(1.0, cx::remainder(9.0, 4.0)), "remainder(9.0, 4.0)"); 245 | #if __cplusplus == 201402L 246 | static_assert(feq(1.0l, cx::remainder(9.0l, 4.0l)), "remainder(9.0l, 4.0l)"); 247 | #endif 248 | 249 | static_assert(feq(1.0, cx::remainder(9, 4)), "remainder(9, 4)"); 250 | static_assert(feq(1.0, cx::remainder(9, 4.0)), "remainder(9, 4.0)"); 251 | #if __cplusplus == 201402L 252 | static_assert(feq(1.0l, cx::remainder(9, 4.0l)), "remainder(9, 4.0l)"); 253 | static_assert(feq(1.0l, cx::remainder(9.0l, 4)), "remainder(9.0l, 4)"); 254 | static_assert(feq(1.0l, cx::remainder(9.0l, 4.0l)), "remainder(9.0l, 4.0l)"); 255 | #endif 256 | 257 | static_assert(feq(-1.0, cx::remainder(9, 5)), "remainder(9, 5)"); 258 | static_assert(feq(1.0, cx::remainder(-9, 5)), "remainder(-9, 5)"); 259 | static_assert(feq(-1.0, cx::remainder(9, -5)), "remainder(9, -5)"); 260 | static_assert(feq(1.0, cx::remainder(-9, -5)), "remainder(-9, -5)"); 261 | 262 | //---------------------------------------------------------------------------- 263 | // fmax/fmin/fdim 264 | static_assert(feq(1.0f, cx::fmax(0.0f, 1.0f)), "fmax(0.0f, 1.0f)"); 265 | static_assert(feq(1.0, cx::fmax(0.0, 1.0)), "fmax(0.0, 1.0)"); 266 | static_assert(feq(1.0l, cx::fmax(0.0l, 1.0l)), "fmax(0.0l, 1.0l)"); 267 | 268 | static_assert(feq(0.0f, cx::fmin(0.0f, 1.0f)), "fmin(0.0f, 1.0f)"); 269 | static_assert(feq(0.0, cx::fmin(0.0, 1.0)), "fmin(0.0, 1.0)"); 270 | static_assert(feq(0.0l, cx::fmin(0.0l, 1.0l)), "fmin(0.0l, 1.0l)"); 271 | 272 | static_assert(feq(1.0f, cx::fdim(2.0f, 1.0f)), "fdim(2.0f, 1.0f)"); 273 | static_assert(feq(1.0, cx::fdim(2.0, 1.0)), "fdim(2.0, 1.0)"); 274 | static_assert(feq(1.0l, cx::fdim(2.0l, 1.0l)), "fdim(2.0l, 1.0l)"); 275 | static_assert(feq(0.0f, cx::fdim(0.0f, 1.0f)), "fdim(0.0f, 1.0f)"); 276 | static_assert(feq(0.0, cx::fdim(0.0, 1.0)), "fdim(0.0, 1.0)"); 277 | static_assert(feq(0.0l, cx::fdim(0.0l, 1.0l)), "fdim(0.0l, 1.0l)"); 278 | 279 | //---------------------------------------------------------------------------- 280 | // log 281 | static_assert(feq(0.0f, cx::log(1.0f)), "log(1.0f)"); 282 | static_assert(feq(0.0, cx::log(1.0)), "log(1.0)"); 283 | static_assert(feq(0.0l, cx::log(1.0l)), "log(1.0l)"); 284 | static_assert(feq(1.0f, cx::log(cx::exp(1.0f))), "log(ef)"); 285 | static_assert(feq(1.0, cx::log(cx::exp(1.0))), "log(e)"); 286 | static_assert(feq(1.0l, cx::log(cx::exp(1.0l))), "log(el)"); 287 | static_assert(feq(0.0, cx::log(1)), "log(1)"); 288 | 289 | // ln(2) = 0.693147180559945309417 290 | static_assert(feq(0.6931472f, cx::log(2.0f)), "log(2.0f)"); 291 | static_assert(feq(0.6931471805599454, cx::log(2.0)), "log(2.0)"); 292 | static_assert(feq(0.6931471805599453094l, cx::log(2.0l)), "log(2.0l)"); 293 | 294 | // these just exist to compile 295 | constexpr auto log_max_float = cx::log(std::numeric_limits::max()); 296 | static_assert(log_max_float > 0, "log_max_float"); 297 | constexpr auto log_min_float = cx::log(std::numeric_limits::min()); 298 | static_assert(log_min_float < 0, "log_min_float"); 299 | constexpr auto log_max_double = cx::log(std::numeric_limits::max()); 300 | static_assert(log_max_double > 0, "log_max_double"); 301 | constexpr auto log_min_double = cx::log(std::numeric_limits::min()); 302 | static_assert(log_min_double < 0, "log_min_double"); 303 | 304 | //---------------------------------------------------------------------------- 305 | // log10 306 | static_assert(feq(1.0f, cx::log10(10.0f)), "log10(10.0f)"); 307 | static_assert(feq(1.0, cx::log10(10.0)), "log10(10.0)"); 308 | static_assert(feq(1.0l, cx::log10(10.0l)), "log10(10.0l)"); 309 | static_assert(feq(0.0, cx::log10(1)), "log10(1)"); 310 | 311 | // log10(2) = 0.301029995663981195213 312 | static_assert(feq(0.301029996f, cx::log10(2.0f)), "log10(2.0f)"); 313 | static_assert(feq(0.3010299956639812, cx::log10(2.0)), "log10(2.0)"); 314 | static_assert(feq(0.3010299956639811952l, cx::log10(2.0l)), "log10(2.0l)"); 315 | 316 | // these just exist to compile 317 | constexpr auto log10_max_float = cx::log10(std::numeric_limits::max()); 318 | static_assert(log10_max_float > 0, "log10_max_float"); 319 | constexpr auto log10_min_float = cx::log10(std::numeric_limits::min()); 320 | static_assert(log10_min_float < 0, "log10_min_float"); 321 | constexpr auto log10_max_double = cx::log10(std::numeric_limits::max()); 322 | static_assert(log10_max_double > 0, "log10_max_double"); 323 | constexpr auto log10_min_double = cx::log10(std::numeric_limits::min()); 324 | static_assert(log10_min_double < 0, "log10_min_double"); 325 | 326 | //---------------------------------------------------------------------------- 327 | // log2 328 | static_assert(feq(1.0f, cx::log2(2.0f)), "log2(2.0f)"); 329 | static_assert(feq(1.0, cx::log2(2.0)), "log2(2.0)"); 330 | static_assert(feq(1.0l, cx::log2(2.0l)), "log2(2.0l)"); 331 | static_assert(feq(0.0, cx::log2(1)), "log2(1)"); 332 | 333 | // log2(10) = 3.321928094887362347870 334 | static_assert(feq(3.3219278f, cx::log2(10.0f)), "log2(10.0f)"); 335 | static_assert(feq(3.3219280948873604, cx::log2(10.0)), "log2(10.0)"); 336 | static_assert(feq(3.321928094887362348l, cx::log2(10.0l)), "log2(10.0l)"); 337 | 338 | // these just exist to compile 339 | constexpr auto log2_max_float = cx::log2(std::numeric_limits::max()); 340 | static_assert(log2_max_float > 0, "log2_max_float"); 341 | constexpr auto log2_min_float = cx::log2(std::numeric_limits::min()); 342 | static_assert(log2_min_float < 0, "log2_min_float"); 343 | constexpr auto log2_max_double = cx::log2(std::numeric_limits::max()); 344 | static_assert(log2_max_double > 0, "log2_max_double"); 345 | constexpr auto log2_min_double = cx::log2(std::numeric_limits::min()); 346 | static_assert(log2_min_double < 0, "log2_min_double"); 347 | 348 | //---------------------------------------------------------------------------- 349 | // sinh, cosh, tanh 350 | static_assert(feq(0.0f, cx::sinh(0.0f)), "sinh(0.0f)"); 351 | static_assert(feq(0.0, cx::sinh(0.0)), "sinh(0.0)"); 352 | static_assert(feq(0.0l, cx::sinh(0.0l)), "sinh(0.0l)"); 353 | static_assert(feq(0.0, cx::sinh(0)), "sinh(0)"); 354 | 355 | static_assert(feq(1.0f, cx::cosh(0.0f)), "cosh(0.0f)"); 356 | static_assert(feq(1.0, cx::cosh(0.0)), "cosh(0.0)"); 357 | static_assert(feq(1.0l, cx::cosh(0.0l)), "cosh(0.0l)"); 358 | static_assert(feq(1.0, cx::cosh(0)), "cosh(0)"); 359 | 360 | // tanh(pi/4) = 0.655794202632672435653 361 | static_assert(feq(0.6557942f, cx::tanh(PI4f)), "tanh(0.0f)"); 362 | static_assert(feq(0.6557942026326724, cx::tanh(PI4)), "tanh(0.0)"); 363 | static_assert(feq(0.65579420263267243565l, cx::tanh(PI4l)), "tanh(0.0l)"); 364 | 365 | //---------------------------------------------------------------------------- 366 | // asinh, acosh, atanh 367 | 368 | // asinh(1) = 0.8813735870195430252326 369 | static_assert(feq(0.8813736f, cx::asinh(1.0f)), "asinh(1.0f)"); 370 | static_assert(feq(0.881373587019543, cx::asinh(1.0)), "asinh(1.0)"); 371 | static_assert(feq(0.881373587019543025l, cx::asinh(1.0l)), "asinh(1.0l)"); 372 | static_assert(feq(0.0, cx::asinh(0)), "asinh(0)"); 373 | 374 | // acosh(2) = 1.3169578969248167086250 375 | static_assert(feq(1.3169579f, cx::acosh(2.0f)), "acosh(2.0f)"); 376 | static_assert(feq(1.316957896924817, cx::acosh(2.0)), "acosh(2.0)"); 377 | static_assert(feq(1.3169578969248167086l, cx::acosh(2.0l)), "acosh(2.0l)"); 378 | static_assert(feq(0.0, cx::acosh(1)), "acosh(1)"); 379 | 380 | // atanh(pi/4) = 1.05930617082324315723005 381 | static_assert(feq(1.0593062f, cx::atanh(PI4f)), "atanh(pi/4f)"); 382 | static_assert(feq(1.0593061708232432, cx::atanh(PI4)), "atanh(pi/4)"); 383 | static_assert(feq(1.0593061708232431572l, cx::atanh(PI4l)), "atanh(pi/4l)"); 384 | static_assert(feq(0.0, cx::atanh(0)), "atanh(0)"); 385 | 386 | //---------------------------------------------------------------------------- 387 | // pow 388 | static_assert(feq(9.0f, cx::pow(3.0f, 2)), "pow(3.0f, 2)"); 389 | static_assert(feq(0.111111f, cx::pow(3.0f, -2)), "pow(3.0f, -2)"); 390 | static_assert(feq(1024.0, cx::pow(2, 10)), "pow(2, 10)"); 391 | static_assert(feq(-0.125, cx::pow(-2, -3)), "pow(-2, -3)"); 392 | static_assert(feq(rt2f, cx::pow(2.0f, 0.5f)), "pow(2.0f, 0.5f)"); 393 | static_assert(feq(rt2, cx::pow(2.0, 0.5)), "pow(2.0, 0.5)"); 394 | static_assert(feq(rt2l, cx::pow(2.0l, 0.5l)), "pow(2.0l, 0.5l)"); 395 | 396 | //---------------------------------------------------------------------------- 397 | // erf 398 | // erf(1) = 0.84270079294971486934122 399 | static_assert(feq(0.8427007f, cx::erf(1.0f)), "erf(1.0f)"); 400 | static_assert(feq(0.842700792949715, cx::erf(1.0)), "erf(1.0)"); 401 | static_assert(feq(0.8427007929497148693l, cx::erf(1.0l)), "erf(1.0l)"); 402 | static_assert(feq(0.842700792949715, cx::erf(1)), "erf(1)"); 403 | } 404 | -------------------------------------------------------------------------------- /src/test/cx_numeric.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void test_cx_numeric() 5 | { 6 | // accumulate 7 | { 8 | constexpr auto a = cx::make_array(1,2,3,4,5); 9 | 10 | struct maximum 11 | { 12 | constexpr int operator()(int x, int y) const { return y > x ? y : x; } 13 | }; 14 | 15 | static_assert(cx::accumulate(a.cbegin(), a.cend(), 0) == 15, "accumulate(1)"); 16 | static_assert(cx::accumulate(a.cbegin(), a.cend(), 0, maximum{}) == 5, "accumulate(2)"); 17 | } 18 | 19 | // inner_product 20 | { 21 | constexpr auto a = cx::make_array(3,4); 22 | 23 | struct plus 24 | { 25 | constexpr int operator()(int x, int y) const { return x + y; } 26 | }; 27 | struct mult 28 | { 29 | constexpr int operator()(int x, int y) const { return x * y; } 30 | }; 31 | 32 | static_assert(cx::inner_product(a.cbegin(), a.cend(), 33 | a.cbegin(), 0) == 25, "inner_product(1)"); 34 | static_assert(cx::inner_product(a.cbegin(), a.cend(), 35 | a.cbegin(), 0, plus{}, mult{}) == 25, "inner_product(2)"); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/cx_pcg32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test_cx_pcg32() 4 | { 5 | static_assert(cx_pcg32() != cx_pcg32(), "pcg32"); 6 | } 7 | -------------------------------------------------------------------------------- /src/test/cx_strenc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | void test_cx_strenc() 9 | { 10 | constexpr auto s_enc = cx_make_encrypted_string("Hello, world!"); 11 | assert(string(s_enc) == "Hello, world!"); 12 | } 13 | -------------------------------------------------------------------------------- /src/test/cx_typeid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace a 4 | { 5 | struct A {}; 6 | constexpr static uint32_t t = cx::type_id(); 7 | } 8 | 9 | namespace b 10 | { 11 | struct A {}; 12 | constexpr static uint32_t t = cx::type_id(); 13 | } 14 | 15 | void test_cx_typeid() 16 | { 17 | static_assert(a::t != b::t, "typeid for different namespaces"); 18 | 19 | struct foo {}; 20 | struct bar {}; 21 | using baz = foo; 22 | 23 | static_assert(cx::type_id() != cx::type_id(), "typeid for different types"); 24 | static_assert(cx::type_id() == cx::type_id(), "typeid for typedef"); 25 | 26 | auto a = [](){}; 27 | auto b = [](){}; 28 | static_assert(cx::type_id() != cx::type_id(), "typeid for lambdas"); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/cx_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test_cx_utils() 4 | { 5 | // empty string, normal string 6 | static_assert(cx::strlen("") == 0, "strlen(\"\")"); 7 | static_assert(cx::strlen("hello, world") == 12, "strlen(\"hello, world\")"); 8 | 9 | // string that breaks the default max recursive depth (512) 10 | static_assert(cx::strlen("01234567890123456789012345678901234567890123456789" 11 | "01234567890123456789012345678901234567890123456789" 12 | "01234567890123456789012345678901234567890123456789" 13 | "01234567890123456789012345678901234567890123456789" 14 | "01234567890123456789012345678901234567890123456789" 15 | "01234567890123456789012345678901234567890123456789" 16 | "01234567890123456789012345678901234567890123456789" 17 | "01234567890123456789012345678901234567890123456789" 18 | "01234567890123456789012345678901234567890123456789" 19 | "01234567890123456789012345678901234567890123456789" 20 | "01234567890123456789012345678901234567890123456789" 21 | "01234567890123456789012345678901234567890123456789" 22 | "01234567890123456789012345678901234567890123456789" 23 | "01234567890123456789012345678901234567890123456789" 24 | "01234567890123456789012345678901234567890123456789" 25 | "01234567890123456789012345678901234567890123456789" 26 | "01234567890123456789012345678901234567890123456789" 27 | "01234567890123456789012345678901234567890123456789" 28 | "01234567890123456789012345678901234567890123456789" 29 | "01234567890123456789012345678901234567890123456789" 30 | ) == 1000, "strlen(\"\")"); 31 | 32 | static_assert(cx::strcmp("abc", "abc") == 0, "strcmp equal"); 33 | static_assert(cx::strcmp("abc", "abd") == -1, "strcmp a < b"); 34 | static_assert(cx::strcmp("abc", "abb") == 1, "strcmp a > b"); 35 | } 36 | -------------------------------------------------------------------------------- /src/test/main.cpp: -------------------------------------------------------------------------------- 1 | extern void test_cx_algorithm(); 2 | extern void test_cx_array(); 3 | extern void test_cx_counter(); 4 | extern void test_cx_guid(); 5 | extern void test_cx_hash(); 6 | extern void test_cx_math(); 7 | extern void test_cx_numeric(); 8 | extern void test_cx_pcg32(); 9 | extern void test_cx_strenc(); 10 | extern void test_cx_typeid(); 11 | extern void test_cx_utils(); 12 | 13 | int main(int, char* []) 14 | { 15 | test_cx_algorithm(); 16 | test_cx_array(); 17 | test_cx_counter(); 18 | test_cx_guid(); 19 | test_cx_hash(); 20 | test_cx_math(); 21 | test_cx_numeric(); 22 | test_cx_pcg32(); 23 | test_cx_strenc(); 24 | test_cx_typeid(); 25 | test_cx_utils(); 26 | 27 | return 0; 28 | } 29 | --------------------------------------------------------------------------------