feat(composition-hash): added robust hashed for compositions
also added ability to use Compositions as a std::hash in unordered_* types. Further added a constructor to build a Composition from a const CompositionAbstract&
This commit is contained in:
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = fourdst::libcomposition
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v2.0.6
|
||||
PROJECT_NUMBER = v2.1.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewers a
|
||||
|
||||
@@ -2,4 +2,5 @@ cmake = import('cmake')
|
||||
|
||||
subdir('fourdst')
|
||||
subdir('cppad')
|
||||
subdir('xxHash')
|
||||
|
||||
|
||||
33
build-config/xxHash/LICENSE
Normal file
33
build-config/xxHash/LICENSE
Normal file
@@ -0,0 +1,33 @@
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
Header File
|
||||
Copyright (C) 2012-2021 Yann Collet
|
||||
|
||||
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at:
|
||||
- xxHash homepage: https://www.xxhash.com
|
||||
- xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
|
||||
355
build-config/xxHash/include/constexpr-xxh3.h
Normal file
355
build-config/xxHash/include/constexpr-xxh3.h
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
constexpr-xxh3 - C++20 constexpr implementation of the XXH3 64-bit variant of xxHash
|
||||
Copyright (c) 2021-2023, chys <admin@chys.info> <chys87@github>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file uses code from Yann Collet's xxHash implementation.
|
||||
Original xxHash copyright notice:
|
||||
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
Header File
|
||||
Copyright (C) 2012-2020 Yann Collet
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator> // for std::data, std::size
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace constexpr_xxh3 {
|
||||
|
||||
template <typename T>
|
||||
concept ByteType = (std::is_integral_v<T> && sizeof(T) == 1)
|
||||
#if defined __cpp_lib_byte && __cpp_lib_byte >= 201603
|
||||
|| std::is_same_v<T, std::byte>
|
||||
#endif
|
||||
;
|
||||
|
||||
template <typename T>
|
||||
concept BytePtrType = requires (T ptr) {
|
||||
requires std::is_pointer_v<T>;
|
||||
requires ByteType<std::remove_cvref_t<decltype(*ptr)>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept BytesType = requires (const T& bytes) {
|
||||
{ std::data(bytes) };
|
||||
requires BytePtrType<decltype(std::data(bytes))>;
|
||||
// -> std::convertible_to is not supported widely enough
|
||||
{ static_cast<size_t>(std::size(bytes)) };
|
||||
};
|
||||
|
||||
inline constexpr uint32_t swap32(uint32_t x) noexcept {
|
||||
return ((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) |
|
||||
((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr uint32_t readLE32(const T* ptr) noexcept {
|
||||
return uint8_t(ptr[0]) | uint32_t(uint8_t(ptr[1])) << 8 |
|
||||
uint32_t(uint8_t(ptr[2])) << 16 | uint32_t(uint8_t(ptr[3])) << 24;
|
||||
}
|
||||
|
||||
inline constexpr uint64_t swap64(uint64_t x) noexcept {
|
||||
return ((x << 56) & 0xff00000000000000ULL) |
|
||||
((x << 40) & 0x00ff000000000000ULL) |
|
||||
((x << 24) & 0x0000ff0000000000ULL) |
|
||||
((x << 8) & 0x000000ff00000000ULL) |
|
||||
((x >> 8) & 0x00000000ff000000ULL) |
|
||||
((x >> 24) & 0x0000000000ff0000ULL) |
|
||||
((x >> 40) & 0x000000000000ff00ULL) |
|
||||
((x >> 56) & 0x00000000000000ffULL);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr uint64_t readLE64(const T* ptr) noexcept {
|
||||
return readLE32(ptr) | uint64_t(readLE32(ptr + 4)) << 32;
|
||||
}
|
||||
|
||||
inline constexpr void writeLE64(uint8_t* dst, uint64_t v) noexcept {
|
||||
for (int i = 0; i < 8; ++i) dst[i] = uint8_t(v >> (i * 8));
|
||||
}
|
||||
|
||||
inline constexpr uint32_t PRIME32_1 = 0x9E3779B1U;
|
||||
inline constexpr uint32_t PRIME32_2 = 0x85EBCA77U;
|
||||
inline constexpr uint32_t PRIME32_3 = 0xC2B2AE3DU;
|
||||
|
||||
inline constexpr uint64_t PRIME64_1 = 0x9E3779B185EBCA87ULL;
|
||||
inline constexpr uint64_t PRIME64_2 = 0xC2B2AE3D27D4EB4FULL;
|
||||
inline constexpr uint64_t PRIME64_3 = 0x165667B19E3779F9ULL;
|
||||
inline constexpr uint64_t PRIME64_4 = 0x85EBCA77C2B2AE63ULL;
|
||||
inline constexpr uint64_t PRIME64_5 = 0x27D4EB2F165667C5ULL;
|
||||
|
||||
inline constexpr size_t SECRET_DEFAULT_SIZE = 192;
|
||||
inline constexpr size_t SECRET_SIZE_MIN = 136;
|
||||
|
||||
inline constexpr uint8_t kSecret[SECRET_DEFAULT_SIZE]{
|
||||
0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c,
|
||||
0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb,
|
||||
0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e,
|
||||
0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
|
||||
0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6,
|
||||
0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb,
|
||||
0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97,
|
||||
0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
|
||||
0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7,
|
||||
0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31,
|
||||
0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83,
|
||||
0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
|
||||
0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26,
|
||||
0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc,
|
||||
0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f,
|
||||
0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
|
||||
};
|
||||
|
||||
inline constexpr std::pair<uint64_t, uint64_t> mult64to128(
|
||||
uint64_t lhs, uint64_t rhs) noexcept {
|
||||
uint64_t lo_lo = uint64_t(uint32_t(lhs)) * uint32_t(rhs);
|
||||
uint64_t hi_lo = (lhs >> 32) * uint32_t(rhs);
|
||||
uint64_t lo_hi = uint32_t(lhs) * (rhs >> 32);
|
||||
uint64_t hi_hi = (lhs >> 32) * (rhs >> 32);
|
||||
uint64_t cross = (lo_lo >> 32) + uint32_t(hi_lo) + lo_hi;
|
||||
uint64_t upper = (hi_lo >> 32) + (cross >> 32) + hi_hi;
|
||||
uint64_t lower = (cross << 32) | uint32_t(lo_lo);
|
||||
return {lower, upper};
|
||||
}
|
||||
|
||||
inline constexpr uint64_t mul128_fold64(uint64_t lhs, uint64_t rhs) noexcept {
|
||||
#if defined __GNUC__ && __WORDSIZE >= 64
|
||||
// It appears both GCC and Clang support evaluating __int128 as constexpr
|
||||
auto product = static_cast<unsigned __int128>(lhs) * rhs;
|
||||
return uint64_t(product >> 64) ^ uint64_t(product);
|
||||
#else
|
||||
auto product = mult64to128(lhs, rhs);
|
||||
return product.first ^ product.second;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline constexpr uint64_t XXH64_avalanche(uint64_t h) noexcept {
|
||||
h = (h ^ (h >> 33)) * PRIME64_2;
|
||||
h = (h ^ (h >> 29)) * PRIME64_3;
|
||||
return h ^ (h >> 32);
|
||||
}
|
||||
|
||||
inline constexpr uint64_t XXH3_avalanche(uint64_t h) noexcept {
|
||||
h = (h ^ (h >> 37)) * 0x165667919E3779F9ULL;
|
||||
return h ^ (h >> 32);
|
||||
}
|
||||
|
||||
inline constexpr uint64_t rrmxmx(uint64_t h, uint64_t len) noexcept {
|
||||
h ^= ((h << 49) | (h >> 15)) ^ ((h << 24) | (h >> 40));
|
||||
h *= 0x9FB21C651E98DF25ULL;
|
||||
h ^= (h >> 35) + len;
|
||||
h *= 0x9FB21C651E98DF25ULL;
|
||||
return h ^ (h >> 28);
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
constexpr uint64_t mix16B(const T* input, const S* secret,
|
||||
uint64_t seed) noexcept {
|
||||
return mul128_fold64(readLE64(input) ^ (readLE64(secret) + seed),
|
||||
readLE64(input + 8) ^ (readLE64(secret + 8) - seed));
|
||||
}
|
||||
|
||||
inline constexpr size_t STRIPE_LEN = 64;
|
||||
inline constexpr size_t SECRET_CONSUME_RATE = 8;
|
||||
inline constexpr size_t ACC_NB = STRIPE_LEN / sizeof(uint64_t);
|
||||
|
||||
template <typename T, typename S>
|
||||
constexpr void accumulate_512(uint64_t* acc, const T* input,
|
||||
const S* secret) noexcept {
|
||||
for (size_t i = 0; i < ACC_NB; i++) {
|
||||
uint64_t data_val = readLE64(input + 8 * i);
|
||||
uint64_t data_key = data_val ^ readLE64(secret + i * 8);
|
||||
acc[i ^ 1] += data_val;
|
||||
acc[i] += uint32_t(data_key) * (data_key >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
constexpr uint64_t hashLong_64b_internal(const T* input, size_t len,
|
||||
const S* secret,
|
||||
size_t secretSize) noexcept {
|
||||
uint64_t acc[ACC_NB]{PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3,
|
||||
PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1};
|
||||
size_t nbStripesPerBlock = (secretSize - STRIPE_LEN) / SECRET_CONSUME_RATE;
|
||||
size_t block_len = STRIPE_LEN * nbStripesPerBlock;
|
||||
size_t nb_blocks = (len - 1) / block_len;
|
||||
|
||||
for (size_t n = 0; n < nb_blocks; n++) {
|
||||
for (size_t i = 0; i < nbStripesPerBlock; i++)
|
||||
accumulate_512(acc, input + n * block_len + i * STRIPE_LEN,
|
||||
secret + i * SECRET_CONSUME_RATE);
|
||||
for (size_t i = 0; i < ACC_NB; i++)
|
||||
acc[i] = (acc[i] ^ (acc[i] >> 47) ^
|
||||
readLE64(secret + secretSize - STRIPE_LEN + 8 * i)) *
|
||||
PRIME32_1;
|
||||
}
|
||||
|
||||
size_t nbStripes = ((len - 1) - (block_len * nb_blocks)) / STRIPE_LEN;
|
||||
for (size_t i = 0; i < nbStripes; i++)
|
||||
accumulate_512(acc, input + nb_blocks * block_len + i * STRIPE_LEN,
|
||||
secret + i * SECRET_CONSUME_RATE);
|
||||
accumulate_512(acc, input + len - STRIPE_LEN,
|
||||
secret + secretSize - STRIPE_LEN - 7);
|
||||
uint64_t result = len * PRIME64_1;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
result +=
|
||||
mul128_fold64(acc[2 * i] ^ readLE64(secret + 11 + 16 * i),
|
||||
acc[2 * i + 1] ^ readLE64(secret + 11 + 16 * i + 8));
|
||||
return XXH3_avalanche(result);
|
||||
}
|
||||
|
||||
template <typename T, typename S, typename HashLong>
|
||||
constexpr uint64_t XXH3_64bits_internal(const T* input, size_t len,
|
||||
uint64_t seed, const S* secret,
|
||||
size_t secretLen,
|
||||
HashLong f_hashLong) noexcept {
|
||||
if (len == 0) {
|
||||
return XXH64_avalanche(seed ^
|
||||
(readLE64(secret + 56) ^ readLE64(secret + 64)));
|
||||
} else if (len < 4) {
|
||||
uint64_t keyed = ((uint32_t(uint8_t(input[0])) << 16) |
|
||||
(uint32_t(uint8_t(input[len >> 1])) << 24) |
|
||||
uint8_t(input[len - 1]) | (uint32_t(len) << 8)) ^
|
||||
((readLE32(secret) ^ readLE32(secret + 4)) + seed);
|
||||
return XXH64_avalanche(keyed);
|
||||
} else if (len <= 8) {
|
||||
uint64_t keyed =
|
||||
(readLE32(input + len - 4) + (uint64_t(readLE32(input)) << 32)) ^
|
||||
((readLE64(secret + 8) ^ readLE64(secret + 16)) -
|
||||
(seed ^ (uint64_t(swap32(uint32_t(seed))) << 32)));
|
||||
return rrmxmx(keyed, len);
|
||||
} else if (len <= 16) {
|
||||
uint64_t input_lo =
|
||||
readLE64(input) ^
|
||||
((readLE64(secret + 24) ^ readLE64(secret + 32)) + seed);
|
||||
uint64_t input_hi =
|
||||
readLE64(input + len - 8) ^
|
||||
((readLE64(secret + 40) ^ readLE64(secret + 48)) - seed);
|
||||
uint64_t acc =
|
||||
len + swap64(input_lo) + input_hi + mul128_fold64(input_lo, input_hi);
|
||||
return XXH3_avalanche(acc);
|
||||
} else if (len <= 128) {
|
||||
uint64_t acc = len * PRIME64_1;
|
||||
size_t secret_off = 0;
|
||||
for (size_t i = 0, j = len; j > i; i += 16, j -= 16) {
|
||||
acc += mix16B(input + i, secret + secret_off, seed);
|
||||
acc += mix16B(input + j - 16, secret + secret_off + 16, seed);
|
||||
secret_off += 32;
|
||||
}
|
||||
return XXH3_avalanche(acc);
|
||||
} else if (len <= 240) {
|
||||
uint64_t acc = len * PRIME64_1;
|
||||
for (size_t i = 0; i < 128; i += 16)
|
||||
acc += mix16B(input + i, secret + i, seed);
|
||||
acc = XXH3_avalanche(acc);
|
||||
for (size_t i = 128; i < len / 16 * 16; i += 16)
|
||||
acc += mix16B(input + i, secret + (i - 128) + 3, seed);
|
||||
acc += mix16B(input + len - 16, secret + SECRET_SIZE_MIN - 17, seed);
|
||||
return XXH3_avalanche(acc);
|
||||
} else {
|
||||
return f_hashLong(input, len, seed, secret, secretLen);
|
||||
}
|
||||
}
|
||||
|
||||
template <BytesType Bytes>
|
||||
constexpr size_t bytes_size(const Bytes& bytes) noexcept {
|
||||
return std::size(bytes);
|
||||
}
|
||||
|
||||
template <ByteType T, size_t N>
|
||||
constexpr size_t bytes_size(T (&)[N]) noexcept {
|
||||
return (N ? N - 1 : 0);
|
||||
}
|
||||
|
||||
/// Basic interfaces
|
||||
|
||||
template <ByteType T>
|
||||
consteval uint64_t XXH3_64bits_const(const T* input, size_t len) noexcept {
|
||||
return XXH3_64bits_internal(
|
||||
input, len, 0, kSecret, sizeof(kSecret),
|
||||
[](const T* input, size_t len, uint64_t, const void*,
|
||||
size_t) constexpr noexcept {
|
||||
return hashLong_64b_internal(input, len, kSecret, sizeof(kSecret));
|
||||
});
|
||||
}
|
||||
|
||||
template <ByteType T, ByteType S>
|
||||
consteval uint64_t XXH3_64bits_withSecret_const(const T* input, size_t len,
|
||||
const S* secret,
|
||||
size_t secretSize) noexcept {
|
||||
return XXH3_64bits_internal(
|
||||
input, len, 0, secret, secretSize,
|
||||
[](const T* input, size_t len, uint64_t, const S* secret,
|
||||
size_t secretLen) constexpr noexcept {
|
||||
return hashLong_64b_internal(input, len, secret, secretLen);
|
||||
});
|
||||
}
|
||||
|
||||
template <ByteType T>
|
||||
consteval uint64_t XXH3_64bits_withSeed_const(const T* input, size_t len,
|
||||
uint64_t seed) noexcept {
|
||||
if (seed == 0) return XXH3_64bits_const(input, len);
|
||||
return XXH3_64bits_internal(
|
||||
input, len, seed, kSecret, sizeof(kSecret),
|
||||
[](const T* input, size_t len, uint64_t seed, const void*,
|
||||
size_t) constexpr noexcept {
|
||||
uint8_t secret[SECRET_DEFAULT_SIZE];
|
||||
for (size_t i = 0; i < SECRET_DEFAULT_SIZE; i += 16) {
|
||||
writeLE64(secret + i, readLE64(kSecret + i) + seed);
|
||||
writeLE64(secret + i + 8, readLE64(kSecret + i + 8) - seed);
|
||||
}
|
||||
return hashLong_64b_internal(input, len, secret, sizeof(secret));
|
||||
});
|
||||
}
|
||||
|
||||
/// Convenient interfaces
|
||||
|
||||
template <BytesType Bytes>
|
||||
consteval uint64_t XXH3_64bits_const(const Bytes& input) noexcept {
|
||||
return XXH3_64bits_const(std::data(input), bytes_size(input));
|
||||
}
|
||||
|
||||
template <BytesType Bytes, BytesType Secret>
|
||||
consteval uint64_t XXH3_64bits_withSecret_const(const Bytes& input,
|
||||
const Secret& secret) noexcept {
|
||||
return XXH3_64bits_withSecret_const(std::data(input), bytes_size(input),
|
||||
std::data(secret), bytes_size(secret));
|
||||
}
|
||||
|
||||
template <BytesType Bytes>
|
||||
consteval uint64_t XXH3_64bits_withSeed_const(const Bytes& input,
|
||||
uint64_t seed) noexcept {
|
||||
return XXH3_64bits_withSeed_const(std::data(input), bytes_size(input), seed);
|
||||
}
|
||||
|
||||
} // namespace constexpr_xxh3
|
||||
202
build-config/xxHash/include/xxhash64.h
Normal file
202
build-config/xxHash/include/xxhash64.h
Normal file
@@ -0,0 +1,202 @@
|
||||
// //////////////////////////////////////////////////////////
|
||||
// xxhash64.h
|
||||
// Copyright (c) 2016 Stephan Brumme. All rights reserved.
|
||||
// see http://create.stephan-brumme.com/disclaimer.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h> // for uint32_t and uint64_t
|
||||
|
||||
/// XXHash (64 bit), based on Yann Collet's descriptions, see http://cyan4973.github.io/xxHash/
|
||||
/** How to use:
|
||||
uint64_t myseed = 0;
|
||||
XXHash64 myhash(myseed);
|
||||
myhash.add(pointerToSomeBytes, numberOfBytes);
|
||||
myhash.add(pointerToSomeMoreBytes, numberOfMoreBytes); // call add() as often as you like to ...
|
||||
// and compute hash:
|
||||
uint64_t result = myhash.hash();
|
||||
|
||||
// or all of the above in one single line:
|
||||
uint64_t result2 = XXHash64::hash(mypointer, numBytes, myseed);
|
||||
|
||||
Note: my code is NOT endian-aware !
|
||||
**/
|
||||
class XXHash64
|
||||
{
|
||||
public:
|
||||
/// create new XXHash (64 bit)
|
||||
/** @param seed your seed value, even zero is a valid seed **/
|
||||
explicit XXHash64(uint64_t seed)
|
||||
{
|
||||
state[0] = seed + Prime1 + Prime2;
|
||||
state[1] = seed + Prime2;
|
||||
state[2] = seed;
|
||||
state[3] = seed - Prime1;
|
||||
bufferSize = 0;
|
||||
totalLength = 0;
|
||||
}
|
||||
|
||||
/// add a chunk of bytes
|
||||
/** @param input pointer to a continuous block of data
|
||||
@param length number of bytes
|
||||
@return false if parameters are invalid / zero **/
|
||||
bool add(const void* input, uint64_t length)
|
||||
{
|
||||
// no data ?
|
||||
if (!input || length == 0)
|
||||
return false;
|
||||
|
||||
totalLength += length;
|
||||
// byte-wise access
|
||||
const unsigned char* data = (const unsigned char*)input;
|
||||
|
||||
// unprocessed old data plus new data still fit in temporary buffer ?
|
||||
if (bufferSize + length < MaxBufferSize)
|
||||
{
|
||||
// just add new data
|
||||
while (length-- > 0)
|
||||
buffer[bufferSize++] = *data++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + length;
|
||||
const unsigned char* stopBlock = stop - MaxBufferSize;
|
||||
|
||||
// some data left from previous update ?
|
||||
if (bufferSize > 0)
|
||||
{
|
||||
// make sure temporary buffer is full (16 bytes)
|
||||
while (bufferSize < MaxBufferSize)
|
||||
buffer[bufferSize++] = *data++;
|
||||
|
||||
// process these 32 bytes (4x8)
|
||||
process(buffer, state[0], state[1], state[2], state[3]);
|
||||
}
|
||||
|
||||
// copying state to local variables helps optimizer A LOT
|
||||
uint64_t s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3];
|
||||
// 32 bytes at once
|
||||
while (data <= stopBlock)
|
||||
{
|
||||
// local variables s0..s3 instead of state[0]..state[3] are much faster
|
||||
process(data, s0, s1, s2, s3);
|
||||
data += 32;
|
||||
}
|
||||
// copy back
|
||||
state[0] = s0; state[1] = s1; state[2] = s2; state[3] = s3;
|
||||
|
||||
// copy remainder to temporary buffer
|
||||
bufferSize = stop - data;
|
||||
for (uint64_t i = 0; i < bufferSize; i++)
|
||||
buffer[i] = data[i];
|
||||
|
||||
// done
|
||||
return true;
|
||||
}
|
||||
|
||||
/// get current hash
|
||||
/** @return 64 bit XXHash **/
|
||||
uint64_t hash() const
|
||||
{
|
||||
// fold 256 bit state into one single 64 bit value
|
||||
uint64_t result;
|
||||
if (totalLength >= MaxBufferSize)
|
||||
{
|
||||
result = rotateLeft(state[0], 1) +
|
||||
rotateLeft(state[1], 7) +
|
||||
rotateLeft(state[2], 12) +
|
||||
rotateLeft(state[3], 18);
|
||||
result = (result ^ processSingle(0, state[0])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, state[1])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, state[2])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, state[3])) * Prime1 + Prime4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// internal state wasn't set in add(), therefore original seed is still stored in state2
|
||||
result = state[2] + Prime5;
|
||||
}
|
||||
|
||||
result += totalLength;
|
||||
|
||||
// process remaining bytes in temporary buffer
|
||||
const unsigned char* data = buffer;
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + bufferSize;
|
||||
|
||||
// at least 8 bytes left ? => eat 8 bytes per step
|
||||
for (; data + 8 <= stop; data += 8)
|
||||
result = rotateLeft(result ^ processSingle(0, *(uint64_t*)data), 27) * Prime1 + Prime4;
|
||||
|
||||
// 4 bytes left ? => eat those
|
||||
if (data + 4 <= stop)
|
||||
{
|
||||
result = rotateLeft(result ^ (*(uint32_t*)data) * Prime1, 23) * Prime2 + Prime3;
|
||||
data += 4;
|
||||
}
|
||||
|
||||
// take care of remaining 0..3 bytes, eat 1 byte per step
|
||||
while (data != stop)
|
||||
result = rotateLeft(result ^ (*data++) * Prime5, 11) * Prime1;
|
||||
|
||||
// mix bits
|
||||
result ^= result >> 33;
|
||||
result *= Prime2;
|
||||
result ^= result >> 29;
|
||||
result *= Prime3;
|
||||
result ^= result >> 32;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// combine constructor, add() and hash() in one static function (C style)
|
||||
/** @param input pointer to a continuous block of data
|
||||
@param length number of bytes
|
||||
@param seed your seed value, e.g. zero is a valid seed
|
||||
@return 64 bit XXHash **/
|
||||
static uint64_t hash(const void* input, uint64_t length, uint64_t seed)
|
||||
{
|
||||
XXHash64 hasher(seed);
|
||||
hasher.add(input, length);
|
||||
return hasher.hash();
|
||||
}
|
||||
|
||||
private:
|
||||
/// magic constants :-)
|
||||
static const uint64_t Prime1 = 11400714785074694791ULL;
|
||||
static const uint64_t Prime2 = 14029467366897019727ULL;
|
||||
static const uint64_t Prime3 = 1609587929392839161ULL;
|
||||
static const uint64_t Prime4 = 9650029242287828579ULL;
|
||||
static const uint64_t Prime5 = 2870177450012600261ULL;
|
||||
|
||||
/// temporarily store up to 31 bytes between multiple add() calls
|
||||
static const uint64_t MaxBufferSize = 31+1;
|
||||
|
||||
uint64_t state[4];
|
||||
unsigned char buffer[MaxBufferSize];
|
||||
uint64_t bufferSize;
|
||||
uint64_t totalLength;
|
||||
|
||||
/// rotate bits, should compile to a single CPU instruction (ROL)
|
||||
static inline uint64_t rotateLeft(uint64_t x, unsigned char bits)
|
||||
{
|
||||
return (x << bits) | (x >> (64 - bits));
|
||||
}
|
||||
|
||||
/// process a single 64 bit value
|
||||
static inline uint64_t processSingle(uint64_t previous, uint64_t input)
|
||||
{
|
||||
return rotateLeft(previous + input * Prime2, 31) * Prime1;
|
||||
}
|
||||
|
||||
/// process a block of 4x4 bytes, this is the main part of the XXHash32 algorithm
|
||||
static inline void process(const void* data, uint64_t& state0, uint64_t& state1, uint64_t& state2, uint64_t& state3)
|
||||
{
|
||||
const uint64_t* block = (const uint64_t*) data;
|
||||
state0 = processSingle(state0, block[0]);
|
||||
state1 = processSingle(state1, block[1]);
|
||||
state2 = processSingle(state2, block[2]);
|
||||
state3 = processSingle(state3, block[3]);
|
||||
}
|
||||
};
|
||||
4
build-config/xxHash/meson.build
Normal file
4
build-config/xxHash/meson.build
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
xxhash_dep = declare_dependency(
|
||||
include_directories: include_directories('include')
|
||||
)
|
||||
@@ -18,7 +18,7 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# *********************************************************************** #
|
||||
project('libcomposition', 'cpp', version: 'v2.0.6', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0')
|
||||
project('libcomposition', 'cpp', version: 'v2.1.0', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0')
|
||||
|
||||
# Add default visibility for all C++ targets
|
||||
add_project_arguments('-fvisibility=default', language: 'cpp')
|
||||
|
||||
@@ -274,6 +274,8 @@ namespace fourdst::composition {
|
||||
*/
|
||||
Composition(const Composition& composition);
|
||||
|
||||
explicit Composition(const CompositionAbstract& composition);
|
||||
|
||||
/**
|
||||
* @brief Assignment operator.
|
||||
* @param other The Composition to assign from.
|
||||
@@ -820,4 +822,22 @@ namespace fourdst::composition {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline bool operator==(const Composition& a, const Composition& b) noexcept {
|
||||
if (a.size() != b.size()) return false;
|
||||
|
||||
// Compare species sets quickly
|
||||
if (a.getRegisteredSpecies() != b.getRegisteredSpecies())
|
||||
return false;
|
||||
|
||||
// Compare all abundances
|
||||
for (auto itA = a.begin(), itB = b.begin();
|
||||
itA != a.end() && itB != b.end(); ++itA, ++itB) {
|
||||
if (itA->first != itB->first)
|
||||
return false;
|
||||
if (itA->second != itB->second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}; // namespace fourdst::composition
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <bit>
|
||||
|
||||
#include "xxhash64.h"
|
||||
|
||||
namespace fourdst::composition::utils {
|
||||
struct CompositionHash {
|
||||
static constexpr uint64_t kSeed = 0xC04D5EEDBEEFull;
|
||||
static constexpr char kTag[] = "4DSTAR:Composition";
|
||||
|
||||
template <typename CompositionT>
|
||||
static uint64_t hash_exact(const CompositionT& comp) {
|
||||
std::vector<std::uint8_t> buf;
|
||||
reserve_bytes(comp, buf);
|
||||
write_header(comp, buf);
|
||||
|
||||
for (auto it = comp.begin(); it != comp.end(); ++it) {
|
||||
const auto& species = it->first;
|
||||
const double abundance = it->second;
|
||||
|
||||
const std::uint32_t spWord = pack_species(species);
|
||||
push_le32(buf, spWord);
|
||||
|
||||
const std::uint64_t bits = normalize_double_bits(abundance);
|
||||
push_le64(buf, bits);
|
||||
}
|
||||
|
||||
return XXHash64::hash(buf.data(), buf.size(), kSeed);
|
||||
}
|
||||
|
||||
static inline bool is_finite(double v) noexcept {
|
||||
return std::isfinite(v);
|
||||
}
|
||||
|
||||
static inline std::int64_t quantize_index(double v, double eps) noexcept {
|
||||
const auto ld_v = static_cast<long double>(v);
|
||||
const auto ld_eps = static_cast<long double>(eps);
|
||||
|
||||
const long double scaled = ld_v / ld_eps;
|
||||
const long long idx = std::llroundl(scaled);
|
||||
return static_cast<std::int64_t>(idx);
|
||||
}
|
||||
|
||||
template <typename CompositionT>
|
||||
static uint64_t hash_quantized(const CompositionT& comp, double eps) noexcept {
|
||||
std::vector<std::uint8_t> buf;
|
||||
reserve_bytes(comp, buf);
|
||||
write_header(comp, buf);
|
||||
push_bytes(buf, reinterpret_cast<const std::uint8_t*>("quantized"), 9);
|
||||
push_le64(buf, encode_fp64(eps));
|
||||
|
||||
for (auto it = comp.begin(); it != comp.end(); ++it) {
|
||||
const auto& species = it->first;
|
||||
const double abundance = it->second;
|
||||
|
||||
const std::uint32_t spWord = pack_species(species);
|
||||
push_le32(buf, spWord);
|
||||
|
||||
if (!is_finite(abundance) || eps <= 0.0) {
|
||||
const std::uint64_t bits = normalize_double_bits(abundance);
|
||||
push_le64(buf, bits);
|
||||
} else {
|
||||
const std::int64_t idx = quantize_index(abundance, eps);
|
||||
push_le64(buf, static_cast<std::uint64_t>(idx));
|
||||
}
|
||||
}
|
||||
|
||||
return XXHash64::hash(buf.data(), buf.size(), kSeed ^ 0x7319'BEEF'1234ull);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template <typename SpeciesT>
|
||||
static std::uint32_t pack_species(const SpeciesT& s) noexcept {
|
||||
// Adjust accessors if your Species API differs.
|
||||
const auto z = static_cast<std::uint16_t>(s.z());
|
||||
const auto a = static_cast<std::uint16_t>(s.a());
|
||||
return (static_cast<std::uint32_t>(z) << 16) | static_cast<std::uint32_t>(a);
|
||||
}
|
||||
|
||||
static inline std::uint64_t normalize_double_bits(double v) noexcept {
|
||||
if (v == 0.0) v = 0.0; // fold -0.0 -> +0.0
|
||||
if (std::isnan(v)) {
|
||||
return 0x7ff8000000000000ULL; // canonical quiet NaN
|
||||
}
|
||||
return std::bit_cast<std::uint64_t>(v);
|
||||
}
|
||||
|
||||
static inline double quantize(double v, double eps) noexcept {
|
||||
if (!std::isfinite(v) || eps <= 0.0) return v;
|
||||
const double q = std::nearbyint(v / eps) * eps;
|
||||
return (q == 0.0) ? 0.0 : q;
|
||||
}
|
||||
|
||||
static inline std::uint64_t encode_fp64(double v) noexcept {
|
||||
return std::bit_cast<std::uint64_t>(v);
|
||||
}
|
||||
|
||||
// ---------- byte helpers (explicit little-endian) ----------
|
||||
static inline void push_le32(std::vector<std::uint8_t>& b, std::uint32_t x) {
|
||||
b.push_back(static_cast<std::uint8_t>( x & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 8 ) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 16) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 24) & 0xFF));
|
||||
}
|
||||
|
||||
static inline void push_le64(std::vector<std::uint8_t>& b, std::uint64_t x) noexcept {
|
||||
b.push_back(static_cast<std::uint8_t>( x & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 8 ) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 16) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 24) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 32) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 40) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 48) & 0xFF));
|
||||
b.push_back(static_cast<std::uint8_t>((x >> 56) & 0xFF));
|
||||
}
|
||||
|
||||
static inline void push_bytes(std::vector<std::uint8_t>& b, const std::uint8_t* p, std::size_t n) noexcept{
|
||||
b.insert(b.end(), p, p + n);
|
||||
}
|
||||
|
||||
template <typename CompositionT>
|
||||
static void write_header(const CompositionT& comp, std::vector<std::uint8_t>& buf) noexcept {
|
||||
push_bytes(buf, reinterpret_cast<const std::uint8_t*>(kTag), sizeof(kTag) - 1);
|
||||
|
||||
const std::size_t nRegistered = comp.getRegisteredSpecies().size();
|
||||
std::size_t nMolar = 0;
|
||||
for (auto it = comp.begin(); it != comp.end(); ++it) { ++nMolar; }
|
||||
|
||||
push_le64(buf, static_cast<std::uint64_t>(nRegistered));
|
||||
push_le64(buf, static_cast<std::uint64_t>(nMolar));
|
||||
}
|
||||
|
||||
template <typename CompositionT>
|
||||
static void reserve_bytes(const CompositionT& comp, std::vector<std::uint8_t>& buf) noexcept {
|
||||
std::size_t nMolar = 0;
|
||||
for (auto it = comp.begin(); it != comp.end(); ++it) { ++nMolar; }
|
||||
const std::size_t approx = (sizeof(kTag) - 1) + 16 + nMolar * (4 + 8 + 0 /*quantized flag optional*/);
|
||||
buf.reserve(approx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<fourdst::composition::Composition> {
|
||||
std::size_t operator()(const fourdst::composition::Composition& c) const noexcept {
|
||||
return static_cast<std::size_t>(
|
||||
fourdst::composition::utils::CompositionHash::hash_exact(c)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
5
src/composition/include/fourdst/config.h.in
Normal file
5
src/composition/include/fourdst/config.h.in
Normal file
@@ -0,0 +1,5 @@
|
||||
// Define the project name
|
||||
#define PROJECT_NAME @PROJECT_NAME@
|
||||
|
||||
// Define the project version
|
||||
#define PROJECT_VERSION @PROJECT_VERSION@
|
||||
@@ -165,6 +165,13 @@ namespace fourdst::composition {
|
||||
m_molarAbundances = composition.m_molarAbundances;
|
||||
}
|
||||
|
||||
Composition::Composition(const CompositionAbstract &composition) {
|
||||
for (const auto& species : composition.getRegisteredSpecies()) {
|
||||
registerSpecies(species);
|
||||
setMolarAbundance(species, composition.getMolarAbundance(species));
|
||||
}
|
||||
}
|
||||
|
||||
Composition& Composition::operator=(
|
||||
const Composition &other
|
||||
) {
|
||||
|
||||
@@ -30,7 +30,8 @@ dependencies = [
|
||||
species_weight_dep,
|
||||
const_dep,
|
||||
config_dep,
|
||||
log_dep
|
||||
log_dep,
|
||||
xxhash_dep
|
||||
]
|
||||
|
||||
# Define the libcomposition library so it can be linked against by other parts of the build system
|
||||
@@ -69,3 +70,15 @@ composition_exception_headers = files(
|
||||
'include/fourdst/composition/exceptions/exceptions_composition.h',
|
||||
)
|
||||
install_headers(composition_exception_headers, subdir : 'fourdst/fourdst/composition/exceptions')
|
||||
|
||||
v = meson.project_version()
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set_quoted('PROJECT_VERSION', v)
|
||||
conf_data.set_quoted('PROJECT_NAME', meson.project_name())
|
||||
|
||||
configure_file(
|
||||
input : 'include/fourdst/config.h.in',
|
||||
output : 'config.h',
|
||||
configuration : conf_data
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "fourdst/composition/exceptions/exceptions_composition.h"
|
||||
#include "fourdst/composition/utils.h"
|
||||
#include "fourdst/composition/decorators/composition_masked.h"
|
||||
#include "fourdst/composition/utils/composition_hash.h"
|
||||
|
||||
#include "fourdst/config/config.h"
|
||||
|
||||
@@ -248,6 +249,102 @@ TEST_F(compositionTest, decorators) {
|
||||
|
||||
comp.setMolarAbundance("H-1", 1.0);
|
||||
ASSERT_NE(mComp.getMolarAbundance(fourdst::atomic::H_1), 1.0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, orderInvariance) {
|
||||
fourdst::composition::Composition a;
|
||||
a.registerSymbol("He-4"); a.registerSymbol("H-1"); a.registerSymbol("O-16");
|
||||
a.setMolarAbundance("H-1", 0.6); a.setMolarAbundance("He-4", 0.6);
|
||||
|
||||
fourdst::composition::Composition b;
|
||||
b.registerSymbol("O-16"); b.registerSymbol("H-1"); b.registerSymbol("He-4");
|
||||
b.setMolarAbundance("He-4", 0.6); b.setMolarAbundance("H-1", 0.6);
|
||||
|
||||
const std::uint64_t ha = fourdst::composition::utils::CompositionHash::hash_exact(a);
|
||||
const std::uint64_t hb = fourdst::composition::utils::CompositionHash::hash_exact(b);
|
||||
ASSERT_EQ(ha, hb);
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, negativeZeroEqualsPositiveZero) {
|
||||
fourdst::composition::Composition a, b;
|
||||
a.registerSymbol("H-1"); b.registerSymbol("H-1");
|
||||
a.setMolarAbundance("H-1", 0.0);
|
||||
b.setMolarAbundance("H-1", -0.0);
|
||||
|
||||
ASSERT_EQ(fourdst::composition::utils::CompositionHash::hash_exact(a),
|
||||
fourdst::composition::utils::CompositionHash::hash_exact(b));
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, quantizedIgnoresTinyJitter) {
|
||||
fourdst::composition::Composition a, b;
|
||||
a.registerSymbol("H-1"); b.registerSymbol("H-1");
|
||||
a.setMolarAbundance("H-1", 1.0);
|
||||
b.setMolarAbundance("H-1", 1.0 + 5e-14); // smaller than eps
|
||||
|
||||
const double eps = 1e-12;
|
||||
const auto hqa = fourdst::composition::utils::CompositionHash::hash_quantized(a, eps);
|
||||
const auto hqb = fourdst::composition::utils::CompositionHash::hash_quantized(b, eps);
|
||||
ASSERT_EQ(hqa, hqb);
|
||||
|
||||
// But exact should differ if the bit pattern changes
|
||||
const auto hea = fourdst::composition::utils::CompositionHash::hash_exact(a);
|
||||
const auto heb = fourdst::composition::utils::CompositionHash::hash_exact(b);
|
||||
ASSERT_NE(hea, heb);
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, quantizedDetectsAboveEps) {
|
||||
fourdst::composition::Composition a, b;
|
||||
a.registerSymbol("H-1"); b.registerSymbol("H-1");
|
||||
a.setMolarAbundance("H-1", 1.0);
|
||||
b.setMolarAbundance("H-1", 1.0 + 2e-12); // larger than eps
|
||||
|
||||
const double eps = 1e-12;
|
||||
ASSERT_NE(fourdst::composition::utils::CompositionHash::hash_quantized(a, eps),
|
||||
fourdst::composition::utils::CompositionHash::hash_quantized(b, eps));
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, exactVsQuantizedDifferentSeeds) {
|
||||
fourdst::composition::Composition a;
|
||||
a.registerSymbol("H-1"); a.setMolarAbundance("H-1", 0.5);
|
||||
|
||||
const auto he = fourdst::composition::utils::CompositionHash::hash_exact(a);
|
||||
const auto hq = fourdst::composition::utils::CompositionHash::hash_quantized(a, 1e-12);
|
||||
ASSERT_NE(he, hq);
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, cloneAndCopyStable) {
|
||||
std::vector<std::string> symbols = {"H-1", "He-4"};
|
||||
std::vector<double> abundances = {0.6, 0.4};
|
||||
fourdst::composition::Composition a(symbols, abundances);
|
||||
fourdst::composition::Composition b(a);
|
||||
|
||||
const auto ha = fourdst::composition::utils::CompositionHash::hash_exact(a);
|
||||
const auto hb = fourdst::composition::utils::CompositionHash::hash_exact(b);
|
||||
ASSERT_EQ(ha, hb);
|
||||
|
||||
std::unique_ptr<fourdst::composition::CompositionAbstract> cptr = a.clone();
|
||||
const auto hc = fourdst::composition::utils::CompositionHash::hash_exact(
|
||||
*static_cast<fourdst::composition::Composition*>(cptr.get()));
|
||||
ASSERT_EQ(ha, hc);
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, bothSidesRegisterSameZeroSpeciesEquality) {
|
||||
fourdst::composition::Composition a, b;
|
||||
a.registerSymbol("H-1"); b.registerSymbol("H-1");
|
||||
a.setMolarAbundance("H-1", 0.6); b.setMolarAbundance("H-1", 0.6);
|
||||
|
||||
a.registerSymbol("He-4"); b.registerSymbol("He-4");
|
||||
ASSERT_EQ(fourdst::composition::utils::CompositionHash::hash_exact(a),
|
||||
fourdst::composition::utils::CompositionHash::hash_exact(b));
|
||||
}
|
||||
|
||||
TEST_F(compositionTest, canonicalizeNaNIfAllowed) {
|
||||
fourdst::composition::Composition a, b;
|
||||
a.registerSymbol("H-1"); b.registerSymbol("H-1");
|
||||
double qnan1 = std::numeric_limits<double>::quiet_NaN();
|
||||
double qnan2 = std::bit_cast<double>(std::uint64_t{0x7ff80000'00000042ULL});
|
||||
a.setMolarAbundance("H-1", qnan1);
|
||||
b.setMolarAbundance("H-1", qnan2);
|
||||
ASSERT_EQ(fourdst::composition::utils::CompositionHash::hash_exact(a),
|
||||
fourdst::composition::utils::CompositionHash::hash_exact(b));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user