💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
6.9 KiB

  1. /*
  2. * Copyright (C) 2023 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef ANTKEEPER_MATH_HASH_PCG_HPP
  20. #define ANTKEEPER_MATH_HASH_PCG_HPP
  21. #include "math/vector.hpp"
  22. #include "math/hash/make-uint.hpp"
  23. #include <cstdint>
  24. #include <type_traits>
  25. namespace math {
  26. namespace hash {
  27. /// @private
  28. template <class T>
  29. constexpr T pcg_multiplier = 0;
  30. template <>
  31. constexpr std::uint8_t pcg_multiplier<std::uint8_t> = 141U;
  32. template <>
  33. constexpr std::uint16_t pcg_multiplier<std::uint16_t> = 12829U;
  34. template <>
  35. constexpr std::uint32_t pcg_multiplier<std::uint32_t> = 747796405UL;
  36. template <>
  37. constexpr std::uint64_t pcg_multiplier<std::uint64_t> = 6364136223846793005ULL;
  38. /// @private
  39. template <class T>
  40. constexpr T pcg_increment = 0;
  41. template <>
  42. constexpr std::uint8_t pcg_increment<std::uint8_t> = 77U;
  43. template <>
  44. constexpr std::uint16_t pcg_increment<std::uint16_t> = 47989U;
  45. template <>
  46. constexpr std::uint32_t pcg_increment<std::uint32_t> = 2891336453UL;
  47. template <>
  48. constexpr std::uint64_t pcg_increment<std::uint64_t> = 1442695040888963407ULL;
  49. /// @private
  50. template <class T>
  51. constexpr T mcg_multiplier = 0;
  52. template <>
  53. constexpr std::uint8_t mcg_multiplier<std::uint8_t> = 217U;
  54. template <>
  55. constexpr std::uint16_t mcg_multiplier<std::uint16_t> = 62169U;
  56. template <>
  57. constexpr std::uint32_t mcg_multiplier<std::uint32_t> = 277803737UL;
  58. template <>
  59. constexpr std::uint64_t mcg_multiplier<std::uint64_t> = 12605985483714917081ULL;
  60. /// @private
  61. template <class T>
  62. [[nodiscard]] constexpr T pcg_uint(T x) noexcept
  63. {
  64. static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
  65. static_assert(sizeof(T) <= 8);
  66. x = x * pcg_multiplier<T> + pcg_increment<T>;
  67. x = (x ^ (x >> ((x >> ((sizeof(T) * 8) - (sizeof(T) + 1))) + (sizeof(T) + 1)))) * mcg_multiplier<T>;
  68. return x ^ (x >> ((sizeof(T) * 16 + 2) / 3));
  69. }
  70. /// @private
  71. template <class T>
  72. [[nodiscard]] inline constexpr vector<T, 1> pcg_uvec1(vector<T, 1> x) noexcept
  73. {
  74. static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
  75. static_assert(sizeof(T) <= 8);
  76. x[0] = T(x[0]);
  77. return x;
  78. }
  79. /// @private
  80. template <class T>
  81. [[nodiscard]] constexpr vector<T, 2> pcg_uvec2(vector<T, 2> x) noexcept
  82. {
  83. static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
  84. static_assert(sizeof(T) <= 8);
  85. x = x * pcg_multiplier<T> + pcg_increment<T>;
  86. x[0] += x[1] * pcg_multiplier<T>;
  87. x[1] += x[0] * pcg_multiplier<T>;
  88. x[0] ^= x[0] >> (sizeof(T) * 4);
  89. x[1] ^= x[1] >> (sizeof(T) * 4);
  90. x[0] += x[1] * pcg_multiplier<T>;
  91. x[1] += x[0] * pcg_multiplier<T>;
  92. x[0] ^= x[0] >> (sizeof(T) * 4);
  93. x[1] ^= x[1] >> (sizeof(T) * 4);
  94. return x;
  95. }
  96. /// @private
  97. template <class T>
  98. [[nodiscard]] constexpr vector<T, 3> pcg_uvec3(vector<T, 3> x) noexcept
  99. {
  100. static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
  101. static_assert(sizeof(T) <= 8);
  102. x = x * pcg_multiplier<T> + pcg_increment<T>;
  103. x[0] += x[1] * x[2];
  104. x[1] += x[2] * x[0];
  105. x[2] += x[0] * x[1];
  106. x[0] ^= x[0] >> (sizeof(T) * 4);
  107. x[1] ^= x[1] >> (sizeof(T) * 4);
  108. x[2] ^= x[2] >> (sizeof(T) * 4);
  109. x[0] += x[1] * x[2];
  110. x[1] += x[2] * x[0];
  111. x[2] += x[0] * x[1];
  112. return x;
  113. }
  114. /// @private
  115. template <class T>
  116. [[nodiscard]] constexpr vector<T, 4> pcg_uvec4(vector<T, 4> x) noexcept
  117. {
  118. static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
  119. static_assert(sizeof(T) <= 8);
  120. x = x * pcg_multiplier<T> + pcg_increment<T>;
  121. x[0] += x[1] * x[3];
  122. x[1] += x[2] * x[0];
  123. x[2] += x[0] * x[1];
  124. x[3] += x[1] * x[2];
  125. x[0] ^= x[0] >> (sizeof(T) * 4);
  126. x[1] ^= x[1] >> (sizeof(T) * 4);
  127. x[2] ^= x[2] >> (sizeof(T) * 4);
  128. x[3] ^= x[3] >> (sizeof(T) * 4);
  129. x[0] += x[1] * x[3];
  130. x[1] += x[2] * x[0];
  131. x[2] += x[0] * x[1];
  132. x[3] += x[1] * x[2];
  133. return x;
  134. }
  135. /**
  136. * PCG hash function.
  137. *
  138. * @param x Input value.
  139. *
  140. * @return Unsigned pseudorandom output value.
  141. *
  142. * @warning Floating point and signed input values will be converted to unsigned integers via `static_cast`.
  143. * @warning Vectors with more than 4 elements are not supported.
  144. *
  145. * @see https://en.wikipedia.org/wiki/Permuted_congruential_generator
  146. * @see O'Neill, M.E. (2014). PCG : A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation.
  147. * @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020.
  148. */
  149. /// @{
  150. [[nodiscard]] inline constexpr std::uint8_t pcg(std::uint8_t x) noexcept
  151. {
  152. return pcg_uint<std::uint8_t>(x);
  153. }
  154. [[nodiscard]] inline constexpr std::uint16_t pcg(std::uint16_t x) noexcept
  155. {
  156. return pcg_uint<std::uint16_t>(x);
  157. }
  158. [[nodiscard]] inline constexpr std::uint32_t pcg(std::uint32_t x) noexcept
  159. {
  160. return pcg_uint<std::uint32_t>(x);
  161. }
  162. [[nodiscard]] inline constexpr std::uint64_t pcg(std::uint64_t x) noexcept
  163. {
  164. return pcg_uint<std::uint64_t>(x);
  165. }
  166. [[nodiscard]] inline constexpr std::uint8_t pcg(std::int8_t x) noexcept
  167. {
  168. return pcg_uint<std::uint8_t>(static_cast<std::uint8_t>(x));
  169. }
  170. [[nodiscard]] inline constexpr std::uint16_t pcg(std::int16_t x) noexcept
  171. {
  172. return pcg_uint<std::uint16_t>(static_cast<std::uint16_t>(x));
  173. }
  174. [[nodiscard]] inline constexpr std::uint32_t pcg(std::int32_t x) noexcept
  175. {
  176. return pcg_uint<std::uint32_t>(static_cast<std::uint32_t>(x));
  177. }
  178. [[nodiscard]] inline constexpr std::uint64_t pcg(std::int64_t x) noexcept
  179. {
  180. return pcg_uint<std::uint64_t>(static_cast<std::uint64_t>(x));
  181. }
  182. [[nodiscard]] inline constexpr std::uint32_t pcg(float x) noexcept
  183. {
  184. return pcg_uint<std::uint32_t>(static_cast<std::uint32_t>(x));
  185. }
  186. [[nodiscard]] inline constexpr std::uint64_t pcg(double x) noexcept
  187. {
  188. return pcg_uint<std::uint64_t>(static_cast<std::uint64_t>(x));
  189. }
  190. template <class T, std::size_t N>
  191. [[nodiscard]] inline constexpr vector<make_uint_t<T>, N> pcg(const vector<T, N>& x) noexcept
  192. {
  193. static_assert(N > 0 && N < 5, "PCG hash only supports vectors with 1-4 elements.");
  194. if constexpr (N == 1)
  195. return pcg_uvec1<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
  196. else if constexpr (N == 2)
  197. return pcg_uvec2<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
  198. else if constexpr (N == 3)
  199. return pcg_uvec3<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
  200. else
  201. return pcg_uvec4<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
  202. }
  203. /// @}
  204. } // namespace hash
  205. } // namespace math
  206. #endif // ANTKEEPER_MATH_HASH_PCG_HPP