This is intended to be used in the new exact mesh boolean algorithm by @howardt. The new `BLI_fixed_width_int.hh` header provides types like `Int256` and `UInt256` which are like e.g. `uint64_t` but with higher precision. The code supports many different integer sizes. The following operations are supported: * Addition * Subtraction * Multiplication * Comparisons * Negation * Conversion to and from other number types * Conversion to and from string (based on `GMP`) Division is not implemented. It could be implemented, but it's more complex and is not required for the new mesh boolean algorithm. Some alternatives to having a custom implementation have been discussed in https://devtalk.blender.org/t/fixed-length-multiprecision-arithmetic/29189/. Generally, the implementation is fairly straight forward. The main complexity is the addition/multiplication algorithm which isn't too complicated. It's nice to have control over this part as it allows us to optimize the code more if necessary. Also, from what I understand, we might be able to benefit from some special cases like multiplying a large integer with a smaller one. I tried some different ways to optimize this already, but so far the normal compiler optimization turned out to work best. Not sure if the same is true on windows though, as it doesn't have native support for an `int128` which helps the compiler understand what I'm doing. Alternatives I tried so far are using intrinsics directly (mainly `_addcarry_u64` and similar), writing inline assembly manually and copying the assembly output from the compiler. I assume the assembly implementation didn't help for me because it prohibited other compiler optimizations. Pull Request: https://projects.blender.org/blender/blender/pulls/119528
155 lines
3.0 KiB
C++
155 lines
3.0 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_span.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
namespace blender {
|
|
|
|
class RandomNumberGenerator {
|
|
private:
|
|
uint64_t x_;
|
|
|
|
public:
|
|
RandomNumberGenerator(uint32_t seed = 0)
|
|
{
|
|
this->seed(seed);
|
|
}
|
|
|
|
/**
|
|
* Creates a random number generator with a somewhat random seed. This can be used when
|
|
* determinism is not necessary or not desired.
|
|
*/
|
|
static RandomNumberGenerator from_random_seed();
|
|
|
|
/**
|
|
* Set the seed for future random numbers.
|
|
*/
|
|
void seed(uint32_t seed)
|
|
{
|
|
constexpr uint64_t lowseed = 0x330E;
|
|
x_ = (uint64_t(seed) << 16) | lowseed;
|
|
}
|
|
|
|
/**
|
|
* Set a randomized hash of the value as seed.
|
|
*/
|
|
void seed_random(uint32_t seed);
|
|
|
|
uint32_t get_uint32()
|
|
{
|
|
this->step();
|
|
return uint32_t(x_ >> 17);
|
|
}
|
|
|
|
int32_t get_int32()
|
|
{
|
|
this->step();
|
|
return int32_t(x_ >> 17);
|
|
}
|
|
|
|
uint64_t get_uint64()
|
|
{
|
|
return (uint64_t(this->get_uint32()) << 32) | this->get_uint32();
|
|
}
|
|
|
|
/**
|
|
* \return Random value (0..N), but never N.
|
|
*/
|
|
int32_t get_int32(int32_t max_exclusive)
|
|
{
|
|
BLI_assert(max_exclusive > 0);
|
|
return this->get_int32() % max_exclusive;
|
|
}
|
|
|
|
/**
|
|
* \return Random value (0..1), but never 1.0.
|
|
*/
|
|
double get_double()
|
|
{
|
|
return double(this->get_int32()) / 0x80000000;
|
|
}
|
|
|
|
/**
|
|
* \return Random value (0..1), but never 1.0.
|
|
*/
|
|
float get_float()
|
|
{
|
|
return (float)this->get_int32() / 0x80000000;
|
|
}
|
|
|
|
template<typename T> void shuffle(MutableSpan<T> values)
|
|
{
|
|
/* Cannot shuffle arrays of this size yet. */
|
|
BLI_assert(values.size() <= INT32_MAX);
|
|
|
|
for (int i = values.size() - 1; i >= 2; i--) {
|
|
int j = this->get_int32(i);
|
|
if (i != j) {
|
|
std::swap(values[i], values[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute uniformly distributed barycentric coordinates.
|
|
*/
|
|
float3 get_barycentric_coordinates()
|
|
{
|
|
float rand1 = this->get_float();
|
|
float rand2 = this->get_float();
|
|
|
|
if (rand1 + rand2 > 1.0f) {
|
|
rand1 = 1.0f - rand1;
|
|
rand2 = 1.0f - rand2;
|
|
}
|
|
|
|
return float3(rand1, rand2, 1.0f - rand1 - rand2);
|
|
}
|
|
|
|
/**
|
|
* Round value to the next integer randomly.
|
|
* 4.9f is more likely to round to 5 than 4.6f.
|
|
*/
|
|
int round_probabilistic(float x);
|
|
|
|
float2 get_unit_float2();
|
|
float3 get_unit_float3();
|
|
/**
|
|
* Generate a random point inside the given triangle.
|
|
*/
|
|
float2 get_triangle_sample(float2 v1, float2 v2, float2 v3);
|
|
float3 get_triangle_sample_3d(float3 v1, float3 v2, float3 v3);
|
|
void get_bytes(MutableSpan<char> r_bytes);
|
|
|
|
/**
|
|
* Simulate getting \a n random values.
|
|
*/
|
|
void skip(int64_t n)
|
|
{
|
|
while (n--) {
|
|
this->step();
|
|
}
|
|
}
|
|
|
|
private:
|
|
void step()
|
|
{
|
|
constexpr uint64_t multiplier = 0x5DEECE66Dll;
|
|
constexpr uint64_t addend = 0xB;
|
|
constexpr uint64_t mask = 0x0000FFFFFFFFFFFFll;
|
|
|
|
x_ = (multiplier * x_ + addend) & mask;
|
|
}
|
|
};
|
|
|
|
} // namespace blender
|