19#include <Common/assertion.h>
20#include <Common/primitive_type.h>
21#include <Common/compiler.h>
22#include <Common/math_basics.h>
23#include <Common/utility.h>
38#if PH_COMPILER_IS_MSVC
40#pragma intrinsic(_BitScanReverse)
41#pragma intrinsic(_umul128)
48inline auto matrix2x2(
const T e00,
const T e01,
const T e10,
const T e11)
49 -> std::array<std::array<T, 2>, 2>
77inline T
clamp(
const T value,
const T lowerBound,
const T upperBound)
81 if constexpr(std::is_floating_point_v<T>)
83 PH_ASSERT(!std::isnan(value));
84 PH_ASSERT(!std::isnan(lowerBound));
85 PH_ASSERT(!std::isnan(upperBound));
88 PH_ASSERT_LE(lowerBound, upperBound);
90 return std::clamp(value, lowerBound, upperBound);
98inline T
safe_clamp(
const T value,
const T lowerBound,
const T upperBound)
102 if constexpr(std::is_floating_point_v<T>)
104 PH_ASSERT(!std::isnan(lowerBound));
105 PH_ASSERT(!std::isnan(upperBound));
108 PH_ASSERT_LE(lowerBound, upperBound);
110 return std::isfinite(value) ? std::clamp(value, lowerBound, upperBound) : lowerBound;
119 if constexpr(std::is_floating_point_v<T>)
121 return std::isfinite(value) && value != 0 ?
static_cast<T
>(1) / value :
static_cast<T
>(0);
125 return std::abs(value) == 1 ? value :
static_cast<T
>(0);
156 return (
static_cast<T
>(0) < value) - (
static_cast<T
>(0) > value);
169 PH_ASSERT(value <= (1UL << 31));
177 value |= value >> 16;
189 if constexpr(std::is_floating_point_v<T>)
191 PH_ASSERT_GT(value, T(0));
193 return static_cast<T
>(std::floor(std::log2(value)));
197 constexpr T NUM_BITS =
sizeof(T) * CHAR_BIT;
199 static_assert(std::is_integral_v<T>);
201 PH_ASSERT_GT(value, T(0));
203#if PH_COMPILER_IS_MSVC
205 unsigned long first1BitFromLeftIndex = std::numeric_limits<unsigned long>::max();
206 if constexpr(
sizeof(T) <=
sizeof(
unsigned long))
208 const auto isIndexSet = _BitScanReverse(
209 &first1BitFromLeftIndex,
210 static_cast<unsigned long>(value));
211 PH_ASSERT_GT(isIndexSet, 0);
215 static_assert(
sizeof(T) <=
sizeof(
unsigned __int64),
216 "size of T is too large");
218 const auto isIndexSet = _BitScanReverse64(
219 &first1BitFromLeftIndex,
220 static_cast<unsigned __int64
>(value));
221 PH_ASSERT_GT(isIndexSet, 0);
223 PH_ASSERT_IN_RANGE_INCLUSIVE(first1BitFromLeftIndex, T(0), NUM_BITS - 1);
225 return static_cast<T
>(first1BitFromLeftIndex);
227#elif PH_COMPILER_IS_CLANG || PH_COMPILER_IS_GNU
229 if constexpr(
sizeof(T) <=
sizeof(
unsigned int))
231 const int numLeftZeros = __builtin_clz(
static_cast<unsigned int>(value));
232 return static_cast<T
>(
sizeof(
unsigned int) * CHAR_BIT - 1 - numLeftZeros);
234 else if constexpr(
sizeof(T) <=
sizeof(
unsigned long))
236 const int numLeftZeros = __builtin_clzl(
static_cast<unsigned long>(value));
237 return static_cast<T
>(
sizeof(
unsigned long) * CHAR_BIT - 1 - numLeftZeros);
241 static_assert(
sizeof(T) <=
sizeof(
unsigned long long),
242 "size of T is too large");
244 const int numLeftZeros = __builtin_clzll(
static_cast<unsigned long long>(value));
245 return static_cast<T
>(
sizeof(
unsigned long long) * CHAR_BIT - 1 - numLeftZeros);
257template<
typename T, std::enable_if_t<std::is_
floating_po
int_v<T>,
int> = 0>
260 long double integralPart;
261 return static_cast<T
>(std::modf(
static_cast<long double>(value), &integralPart));
267template<
typename T, std::enable_if_t<std::is_
integral_v<T>,
int> = 0>
268inline T
wrap(T value,
const T lowerBound,
const T upperBound)
270 PH_ASSERT_GE(upperBound, lowerBound);
272 const T rangeSize = upperBound - lowerBound +
static_cast<T
>(1);
274 if(value < lowerBound)
276 value += rangeSize * ((lowerBound - value) / rangeSize +
static_cast<T
>(1));
279 PH_ASSERT_GE(value, lowerBound);
282 return lowerBound + (value - lowerBound) % rangeSize;
291 static_assert(std::is_integral_v<T>,
292 "T must be an integer type.");
294 return value % 2 == 0;
321template<
typename T, std::
size_t N>
322T
summation(
const std::array<T, N>& values, T initialValue = 0);
325T
summation(
const std::vector<T>& values, T initialValue = 0);
327template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
334template<
typename T, std::
size_t N>
335T
product(
const std::array<T, N>& values, T initialValue = 1);
338T
product(
const std::vector<T>& values, T initialValue = 1);
340template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
344template<
typename NumberType>
347 return static_cast<NumberType
>(numBytes) /
static_cast<NumberType
>(
constant::KiB);
350template<
typename NumberType>
353 return static_cast<NumberType
>(numBytes) /
static_cast<NumberType
>(
constant::MiB);
356template<
typename NumberType>
359 return static_cast<NumberType
>(numBytes) /
static_cast<NumberType
>(
constant::GiB);
362template<
typename NumberType>
365 return static_cast<NumberType
>(numBytes) /
static_cast<NumberType
>(
constant::TiB);
368template<
typename NumberType>
371 return static_cast<NumberType
>(numBytes) /
static_cast<NumberType
>(
constant::PiB);
380 const std::size_t rangeIndex,
381 const std::size_t totalSize,
382 const std::size_t numDivisions)
388 PH_ASSERT_GT(numDivisions, 0);
389 PH_ASSERT_LT(rangeIndex, numDivisions);
393 rangeIndex * totalSize / numDivisions,
394 (rangeIndex + 1) * totalSize / numDivisions
414 PH_ASSERT_GT(x, 0.0f);
416 const float halvedInput = 0.5f * x;
420 bits = 0x5F375A86 - (bits >> 1);
426 x = x * (1.5f - halvedInput * x * x);
447template<
typename T,
typename = std::enable_if_t<std::is_
integral_v<T>>>
450 using namespace detail;
452 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
453 "Unsupported type detected");
455 constexpr std::size_t NUM_BITS =
sizeof(T) * CHAR_BIT;
458 static_assert(NUM_BITS % 8 == 0,
459 "Non-multiple-of-8 integer bits are not supported");
461 if constexpr(NUM_BITS == 8)
465 else if constexpr(NUM_BITS == 16)
470 else if constexpr(NUM_BITS == 32)
479 static_assert(NUM_BITS == 64);
496template<std::
unsigned_
integral UIntType, std::
integral RangeType>
497inline UIntType
set_bits_in_range(
const UIntType bits,
const RangeType beginBitIdx,
const RangeType endBitIdx)
500 PH_ASSERT_IN_RANGE_INCLUSIVE(beginBitIdx, 0, endBitIdx);
501 PH_ASSERT_IN_RANGE_INCLUSIVE(endBitIdx, beginBitIdx, sizeof_in_bits<UIntType>());
508 const auto bitMask =
static_cast<UIntType
>(((UIntType(1) << (endBitIdx - beginBitIdx)) - 1) << beginBitIdx);
510 return bits | bitMask;
517template<std::
unsigned_
integral UIntType, std::
integral RangeType>
518inline UIntType
clear_bits_in_range(
const UIntType bits,
const RangeType beginBitIdx,
const RangeType endBitIdx)
521 return bits & (~bitMask);
528template<std::
unsigned_
integral UIntType>
531 PH_ASSERT_IN_RANGE(bitIdx, 0, sizeof_in_bits<UIntType>());
533 return static_cast<UIntType
>(1) << bitIdx;
536template<std::
unsigned_
integral UIntType, UIntType BIT_IDX>
539 static_assert(0 <= BIT_IDX && BIT_IDX < sizeof_in_bits<UIntType>(),
540 "BIT_IDX must be within [0, # bits in UIntType)");
542 return static_cast<UIntType
>(1) << BIT_IDX;
546template<
typename T, T MIN, T MAX, std::
size_t N>
550 static_assert(N >= 2);
552 constexpr T TOTAL_SIZE =
MAX -
MIN;
554 std::array<T, N> arr;
556 for(std::size_t i = 1; i < N - 1; ++i)
560 arr[i] =
MIN +
static_cast<T
>(i * TOTAL_SIZE / (N - 1));
562 PH_ASSERT_IN_RANGE_INCLUSIVE(arr[i],
MIN,
MAX);
572 PH_ASSERT_GT(max, min);
575 const T totalSize = max - min;
577 std::vector<T> vec(n, 0);
579 for(std::size_t i = 0; i < n; ++i)
583 vec[i] = min +
static_cast<T
>(i * totalSize / (n - 1));
585 PH_ASSERT_IN_RANGE_INCLUSIVE(vec[i], min, max);
605 static_assert(std::numeric_limits<float32>::is_iec559);
607 if(std::abs(value) < 0.0000610352f)
609 return std::signbit(value) ? 0x8000 : 0x0000;
615 fp32Bits = std::bit_cast<uint32>(value);
618 fp16Bits =
static_cast<uint16
>((((fp32Bits & 0x7FFFFFFF) >> 13) - (0x38000000 >> 13)));
619 fp16Bits |= ((fp32Bits & 0x80000000) >> 16);
631 static_assert(std::numeric_limits<float32>::is_iec559);
634 if((fp16Bits & 0x7FFF) == 0)
636 return (fp16Bits & 0x8000) == 0 ? 0.0f : -0.0f;
639 uint32 fp32Bits = ((fp16Bits & 0x8000) << 16);
640 fp32Bits |= (((fp16Bits & 0x7FFF) << 13) + 0x38000000);
642 return std::bit_cast<float32>(fp32Bits);
653template<std::
floating_po
int FloatType, std::
integral IntType>
656 if constexpr(std::is_unsigned_v<IntType>)
658 return static_cast<FloatType
>(
659 static_cast<long double>(intVal) / std::numeric_limits<IntType>::max());
663 return static_cast<FloatType
>(intVal >= 0
664 ?
static_cast<long double>(intVal) / std::numeric_limits<IntType>::max()
665 : -
static_cast<long double>(intVal) / std::numeric_limits<IntType>::min());
671template<std::
integral IntType, std::
floating_po
int FloatType>
674 if constexpr(std::is_unsigned_v<IntType>)
676 PH_ASSERT_IN_RANGE_INCLUSIVE(floatVal, 0.0f, 1.0f);
678 return static_cast<IntType
>(
679 std::round(
static_cast<long double>(floatVal) * std::numeric_limits<IntType>::max()));
683 PH_ASSERT_IN_RANGE_INCLUSIVE(floatVal, -1.0f, 1.0f);
685 return static_cast<IntType
>(floatVal >= 0
686 ? std::round(
static_cast<long double>(floatVal) * std::numeric_limits<IntType>::max())
687 : std::round(-
static_cast<long double>(floatVal) * std::numeric_limits<IntType>::min()));
695inline void uint64_mul(
const uint64 lhs,
const uint64 rhs, uint64& out_high64, uint64& out_low64)
697#if defined(__SIZEOF_INT128__)
698 const auto result128 = __uint128_t(lhs) * __uint128_t(rhs);
699 out_high64 =
static_cast<uint64
>(result128 >> 64);
700 out_low64 =
static_cast<uint64
>(result128);
701#elif PH_COMPILER_IS_MSVC
702 out_low64 = _umul128(lhs, rhs, &out_high64);
727 const uint64 a = lhs >> 32, b = lhs & 0xFFFFFFFF;
728 const uint64 c = rhs >> 32, d = rhs & 0xFFFFFFFF;
730 const uint64 ac = a * c;
731 const uint64 bc = b * c;
732 const uint64 ad = a * d;
733 const uint64 bd = b * d;
737 const uint64 mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
739 out_high64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
740 out_low64 = (mid34 << 32) | (bd & 0xFFFFFFFF);
747template<
typename T, std::
size_t N>
748T
length(
const std::array<T, N>& vec);
751T
length(
const std::vector<T>& vec);
753template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
760template<
typename T, std::
size_t N>
766template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
773template<
typename T, std::
size_t N>
774T
p_norm(
const std::array<T, N>& vec);
777T
p_norm(
const std::vector<T>& vec);
779template<std::
size_t P,
typename T, std::
size_t EXTENT = std::dynamic_extent>
788template<
typename T, std::
size_t N>
794template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
801template<
typename T, std::
size_t N>
802T
dot_product(
const std::array<T, N>& vecA,
const std::array<T, N>& vecB);
805T
dot_product(
const std::vector<T>& vecA,
const std::vector<T>& vecB);
807template<
typename T, std::
size_t EXTENT = std::dynamic_extent>
constexpr T rcp_pi
Value of .
Definition constant.h:21
constexpr std::size_t MiB
Definition constant.h:80
constexpr std::size_t GiB
Definition constant.h:81
constexpr std::size_t PiB
Definition constant.h:83
constexpr std::size_t TiB
Definition constant.h:82
constexpr T pi
Value of .
Definition constant.h:15
constexpr std::size_t KiB
Definition constant.h:79
constexpr std::array< unsigned char, 256 > BITS8_REVERSE
Definition math_table.h:353
Math functions and utilities.
Definition TransformInfo.h:10
T safe_clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound], fallback to lowerBound. If a floating-point value is non-...
Definition math.h:98
consteval UIntType flag_bit()
Definition math.h:537
bool is_odd(const T value)
Checks if a number is odd.
Definition math.h:301
T dot_product(const std::array< T, N > &vecA, const std::array< T, N > &vecB)
Treating input values as vectors and calculates their dot product.
Definition math.ipp:206
float32 fp16_bits_to_fp32(const uint16 fp16Bits)
Convert a 16-bit representation back to 32-bit float.
Definition math.h:626
void form_orthonormal_basis_frisvad(const Vector3R &unitYaxis, Vector3R *const out_unitXaxis, Vector3R *const out_unitZaxis)
Definition math.cpp:13
T safe_rcp(const T value)
Calculates the reciprocal of a value, fallback to 0. If the resulting reciprocal is non-finite (e....
Definition math.h:117
T length_squared(const std::array< T, N > &vec)
Treating input values as a vector and calculates its squared length.
Definition math.ipp:68
T reverse_bits(const T value)
Get an integral value with reversed bits.
Definition math.h:448
constexpr NumberType bytes_to_GiB(const std::size_t numBytes)
Definition math.h:357
uint32 next_power_of_2(uint32 value)
Gets the minimum power of 2 value that is >= value.
Definition math.h:167
constexpr NumberType bytes_to_TiB(const std::size_t numBytes)
Definition math.h:363
T to_degrees(const T radians)
Convert radians to degrees.
Definition math.h:132
bool is_even(const T value)
Checks if a number is even.
Definition math.h:289
T squared(const T value)
Definition math.h:59
IntType quantize_normalized_float(const FloatType floatVal)
Definition math.h:672
T summation(const std::array< T, N > &values, T initialValue=0)
Sum all values within a container together.
Definition math.ipp:9
FloatType normalize_integer(const IntType intVal)
Transform an integer to the range [0, 1] ([-1, 1] for signed integer). When transforming an unsigned ...
Definition math.h:654
T wrap(T value, const T lowerBound, const T upperBound)
Wraps an integer around [lower-bound, upper-bound]. For example, given a bound [-1,...
Definition math.h:268
void normalize(std::array< T, N > &vec)
Treating input values as a vector and normalize it. Notice that normalizing a integer typed vector wi...
Definition math.ipp:142
T p_norm(const std::array< T, N > &vec)
Treating input values as a vector and calculates its p-norm.
Definition math.ipp:91
constexpr NumberType bytes_to_MiB(const std::size_t numBytes)
Definition math.h:351
T length(const std::array< T, N > &vec)
Treating input values as a vector and calculates its length.
Definition math.ipp:50
std::array< T, N > evenly_spaced_array()
Definition math.h:547
T product(const std::array< T, N > &values, T initialValue=1)
Multiplies all values within a container together.
Definition math.ipp:27
int sign(const T value)
Extract the sign of value.
Definition math.h:154
std::pair< std::size_t, std::size_t > ith_evenly_divided_range(const std::size_t rangeIndex, const std::size_t totalSize, const std::size_t numDivisions)
Gets the i-th evenly divided range.
Definition math.h:379
bool is_same_hemisphere(const Vector3R &vector, const Vector3R &N)
Checks whether the specified vector is within the hemisphere defined by the normal vector N....
Definition math.cpp:42
T to_radians(const T degrees)
Convert degrees to radians.
Definition math.h:140
uint16 fp32_to_fp16_bits(const float32 value)
Convert a 32-bit float to 16-bit representation.
Definition math.h:600
T fractional_part(const T value)
Retrieve the fractional part of value (with the sign unchanged). The result is not guaranteed to be t...
Definition math.h:258
std::vector< T > evenly_spaced_vector(const T min, const T max, const std::size_t n)
Definition math.h:570
void uint64_mul(const uint64 lhs, const uint64 rhs, uint64 &out_high64, uint64 &out_low64)
Multiply two unsigined 64-bit numbers and get a 128-bit result.
Definition math.h:695
constexpr NumberType bytes_to_PiB(const std::size_t numBytes)
Definition math.h:369
UIntType set_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
Set bits in the range to 1. The bits in [beginBitIdx, endBitIdx) will be set to 1,...
Definition math.h:497
float fast_rcp_sqrt(float x)
Computes 1/sqrt(x) in a fast but approximative way.
Definition math.h:407
constexpr NumberType bytes_to_KiB(const std::size_t numBytes)
Definition math.h:345
float fast_sqrt(const float x)
Computes sqrt(x) in a fast but approximative way.
Definition math.h:436
bool is_opposite_hemisphere(const Vector3R &vector, const Vector3R &N)
Checks whether the specified vector is within the hemisphere defined by the normal vector -N....
Definition math.cpp:49
T log2_floor(const T value)
Calculate a positive number's base 2 logarithm (floored).
Definition math.h:187
auto matrix2x2(const T e00, const T e01, const T e10, const T e11) -> std::array< std::array< T, 2 >, 2 >
Definition math.h:48
UIntType clear_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
Set bits in the range to 0. The bits in [beginBitIdx, endBitIdx) will be set to 0,...
Definition math.h:518
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12
Target bitwise_cast(const Source &source)
Definition utility.h:126