commit f84f6f5e88ab839ceec70c493fe6f404a307633f
parent 8bd80d61285b842845ed2fc658ef4edbaf510421
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 2 Dec 2020 15:41:51 +0100
Merge branch 'release_0.9'
Diffstat:
37 files changed, 1058 insertions(+), 268 deletions(-)
diff --git a/README.md b/README.md
@@ -10,7 +10,7 @@ The Star-Sampling library relies on the [CMake](http://www.cmake.org) and the
[RCMake](https://gitlab.com/vaplv/rcmake/) packages to build. It also depends
on the [RSys](https://gitlab.com/vaplv/rsys/) library and on random number
generators from C++11 and the
-[Random123](https://github.com/DEShawResearch/Random123-Boost/) library.
+[Random123](https://www.deshawresearch.com/resources_random123.html) library.
First ensure that CMake and a C++11 compiler are installed on your system. Then
install the RCMake package as well as the RSys and Random123 libraries. Finally
@@ -34,6 +34,21 @@ variable to the directory that contains the `boost` include directory.
## Release notes
+### Version 0.9
+
+- Rewrite the caching mechanism used to register the RNG states provided by the
+ proxy to its managed generators. These states are no more saved in files that
+ continuously grow. The streams have now a limited size, and the states are
+ structured into them as in a FIFO circular queue. When a queue is full, the
+ RNG states are no more stored into it but will be generated by the associated
+ managed RNG.
+- Update the `ssp_rng_proxy_create2` function: input arguments are now provided
+ through a structured variable. Furthermore, its initial state can be
+ setup from an optionnal RNG.
+- Add the `ssp_ran_tetrahedron_uniform[_float]` random variates that uniformly
+ distributes a point into a tetrahedron.
+- Fix the read function of the KISS RNG: de-serialised data could be wrong.
+
### Version 0.8.1
- Fix a possible invalid memory read on proxy allocator clear.
@@ -86,11 +101,10 @@ variable to the directory that contains the `boost` include directory.
- Add the `ssp_rng_proxy_write` and `ssp_rng_proxy_read` functions that
serializes and deserializes the data of the proxy RNG, respectively.
-## Licenses
+## License
-Star-Sampling is Copyright (C) |Meso|Star> 2015-2018
-(<contact@meso-star.com>). It is a free software released under the
-[OSI](http://opensource.org)-approved CeCILL license. You are welcome to
+Copyright (C) 2015-2020 |Meso|Star> (<contact@meso-star.com>). Star-Sampling is
+free software released under the CeCILLv2.1 license. You are welcome to
redistribute it under certain conditions; refer to the COPYING files for
details.
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+# Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
#
# This software is a computer program whose purpose is to generate files
# used to build the Star-SP library.
@@ -52,6 +52,9 @@ include(rcmake)
include(rcmake_runtime)
if(MSVC)
+ # CMake 3.11 is required to handle the naming rule of the boost 1.67 libs
+ cmake_minimum_required(VERSION 3.11)
+
# Currently, the C++11 random library on MSVC is bugged and consequently we
# use the Boost library instead
find_package(Boost 1.67 REQUIRED COMPONENTS random)
@@ -71,8 +74,8 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys ${Boost_LIBRARY_DIRS})
# Configure and define targets
################################################################################
set(VERSION_MAJOR 0)
-set(VERSION_MINOR 8)
-set(VERSION_PATCH 1)
+set(VERSION_MINOR 9)
+set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
set(SSP_FILES_SRC
@@ -177,6 +180,7 @@ if(NOT NO_TEST)
new_test(test_ssp_ran_sphere ${MATH_LIB})
new_test(test_ssp_ran_sphere_cap ${MATH_LIB})
new_test(test_ssp_ran_triangle ${MATH_LIB})
+ new_test(test_ssp_ran_tetrahedron ${MATH_LIB})
new_test(test_ssp_ran_uniform_disk)
rcmake_copy_runtime_libraries(test_ssp_rng)
endif()
diff --git a/src/ssp.h b/src/ssp.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
@@ -76,7 +76,9 @@ struct ssp_rng_type {
uint64_t (*get)(void* state);
res_T (*discard)(void* state, uint64_t n);
res_T (*read)(void* state, FILE* file);
+ res_T (*read_cstr)(void* state, const char* cstr);
res_T (*write)(const void* state, FILE* file);
+ res_T (*write_cstr)(const void* state, char* buf, const size_t sz, size_t* len);
double (*entropy)(const void* state);
uint64_t min;
@@ -95,13 +97,29 @@ ssp_rng_type_eq(const struct ssp_rng_type* t0, const struct ssp_rng_type* t1)
&& t0->get == t1->get
&& t0->discard == t1->discard
&& t0->read == t1->read
+ && t0->read_cstr == t1->read_cstr
&& t0->write == t1->write
+ && t0->write_cstr == t1->write_cstr
&& t0->entropy == t1->entropy
&& t0->min == t1->min
&& t0->max == t1->max
- && t0->sizeof_state == t1->sizeof_state;
+ && t0->sizeof_state == t1->sizeof_state
+ && t0->alignof_state == t1->alignof_state;
}
+/* Arguments of the ssp_rng_proxy_create2 function */
+struct ssp_rng_proxy_create2_args {
+ const struct ssp_rng* rng; /* Original RNG state. May be NULL*/
+ const struct ssp_rng_type* type; /* Type of the RN if 'rng' is NULL */
+ size_t sequence_offset; /* #RNs before the 1st valid sequence */
+ size_t sequence_size; /* #RNs in a sequence */
+ size_t sequence_pitch; /* #RNs between sequences. Must be >= sequence_size */
+ size_t nbuckets; /* #buckets of continuous RNs in a sequence */
+};
+#define SSP_RNG_PROXY_CREATE2_ARGS_NULL__ {NULL, NULL, 0, 0, 0, 0}
+static const struct ssp_rng_proxy_create2_args SSP_RNG_PROXY_CREATE2_ARGS_NULL =
+ SSP_RNG_PROXY_CREATE2_ARGS_NULL__;
+
BEGIN_DECLS
/* David Jones's Keep It Simple Stupid builtin PRNG type. Suitable for fast
@@ -202,15 +220,27 @@ ssp_rng_max
(struct ssp_rng* rng);
SSP_API res_T
-ssp_rng_write
- (const struct ssp_rng* rng,
+ssp_rng_read
+ (struct ssp_rng* rng,
FILE* stream);
SSP_API res_T
-ssp_rng_read
+ssp_rng_read_cstr
(struct ssp_rng* rng,
+ const char* cstr); /* Null terminated string */
+
+SSP_API res_T
+ssp_rng_write
+ (const struct ssp_rng* rng,
FILE* stream);
+SSP_API res_T
+ssp_rng_write_cstr
+ (const struct ssp_rng* rng,
+ char* buf, /* May be NULL */
+ const size_t bufsz, /* buf capacity */
+ size_t* len); /* May be NULL. #chars to write into buf without null char */
+
SSP_API double
ssp_rng_entropy
(const struct ssp_rng* rng);
@@ -238,11 +268,7 @@ ssp_rng_proxy_create_from_rng
SSP_API res_T
ssp_rng_proxy_create2
(struct mem_allocator* mem_allocator,
- const struct ssp_rng_type* type,
- const size_t sequence_offset, /* #RNs before the 1st valid sequence */
- const size_t sequence_size, /* #RNs in a sequence */
- const size_t sequence_pitch, /* #RNs between 2 consecutive sequence */
- const size_t nbuckets, /* #buckets in sequence */
+ const struct ssp_rng_proxy_create2_args* args,
struct ssp_rng_proxy** out_proxy);
SSP_API res_T
@@ -280,7 +306,7 @@ ssp_rng_proxy_get_type
* Miscellaneous distributions
******************************************************************************/
/* Random variate from the exponential distribution with mean `mu':
- * P(x) dx = mu * exp(-x * mu) dx */
+ * P(x) dx = 1/mu * exp(-x / mu) dx */
SSP_API double
ssp_ran_exp
(struct ssp_rng* rng,
@@ -465,6 +491,63 @@ ssp_ran_triangle_uniform_float_pdf
}
/*******************************************************************************
+ * Tetrahedron distribution
+ ******************************************************************************/
+/* Uniform sampling of a tetrahedron */
+SSP_API double*
+ssp_ran_tetrahedron_uniform
+ (struct ssp_rng* rng,
+ const double v0[3], /* Position of the 1st vertex */
+ const double v1[3], /* Position of the 2nd vertex */
+ const double v2[3], /* Position of the 3rd vertex */
+ const double v3[3], /* Position of the 4th vertex */
+ double sample[3], /* Sampled position */
+ double* pdf); /* Can be NULL => pdf not computed */
+
+SSP_API float*
+ssp_ran_tetrahedron_uniform_float
+ (struct ssp_rng* rng,
+ const float v0[3], /* Position of the 1st vertex */
+ const float v1[3], /* Position of the 2nd vertex */
+ const float v2[3], /* Position of the 3rd vertex */
+ const float v3[3], /* Position of the 4th vertex */
+ float sample[3], /* Sampled position */
+ float* pdf); /* Can be NULL => pdf not computed */
+
+/* Return the probability distribution for a uniform point sampling in a tetrahedron */
+static INLINE double
+ssp_ran_tetrahedron_uniform_pdf
+ (const double v0[3],
+ const double v1[3],
+ const double v2[3],
+ const double v3[3])
+{
+ double vec0[3], vec1[3], vec2[3], tmp[3];
+ ASSERT(v0 && v1 && v2 && v3);
+
+ d3_sub(vec0, v1, v2);
+ d3_sub(vec1, v3, v2);
+ d3_sub(vec2, v0, v2);
+ return 6 / fabs(d3_dot(d3_cross(tmp, vec0, vec1), vec2));
+}
+
+static INLINE float
+ssp_ran_tetrahedron_uniform_float_pdf
+ (const float v0[3],
+ const float v1[3],
+ const float v2[3],
+ const float v3[3])
+{
+ float vec0[3], vec1[3], vec2[3], tmp[3];
+ ASSERT(v0 && v1 && v2 && v3);
+
+ f3_sub(vec0, v1, v2);
+ f3_sub(vec1, v3, v2);
+ f3_sub(vec2, v0, v2);
+ return 6.f / absf(f3_dot(f3_cross(tmp, vec0, vec1), vec2));
+}
+
+/*******************************************************************************
* Hemisphere distribution
******************************************************************************/
/* Uniform sampling of an unit hemisphere whose up direction is implicitly the
@@ -674,8 +757,14 @@ ssp_ran_sphere_hg
double sample_local[3];
double basis[9];
ASSERT(-1 <= g && g <= +1 && rng && up && sample && d3_is_normalized(up));
- ssp_ran_sphere_hg_local(rng, g, sample_local, pdf);
- return d33_muld3(sample, d33_basis(basis, up), sample_local);
+ if (fabs(g) == 1) {
+ d3_muld(sample, up, g);
+ if(pdf) *pdf=INF;
+ } else {
+ ssp_ran_sphere_hg_local(rng, g, sample_local, pdf);
+ d33_muld3(sample, d33_basis(basis, up), sample_local);
+ }
+ return sample;
}
static INLINE float*
@@ -689,8 +778,14 @@ ssp_ran_sphere_hg_float
float sample_local[3];
float basis[9];
ASSERT(-1 <= g && g <= +1 && rng && up && sample && f3_is_normalized(up));
- ssp_ran_sphere_hg_float_local(rng, g, sample_local, pdf);
- return f33_mulf3(sample, f33_basis(basis, up), sample_local);
+ if(absf(g) == 1) {
+ f3_mulf(sample, up, g);
+ if(pdf) *pdf = (float)INF;
+ } else {
+ ssp_ran_sphere_hg_float_local(rng, g, sample_local, pdf);
+ f33_mulf3(sample, f33_basis(basis, up), sample_local);
+ }
+ return sample;
}
/* Return the probability distribution for a Henyey-Greenstein random
diff --git a/src/ssp_ran.c b/src/ssp_ran.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
@@ -291,6 +291,94 @@ ssp_ran_triangle_uniform_float
}
double*
+ssp_ran_tetrahedron_uniform
+ (struct ssp_rng* rng,
+ const double v0[3],
+ const double v1[3],
+ const double v2[3],
+ const double v3[3],
+ double sample[3],
+ double* pdf)
+{
+ double a, s, t, u; /* Barycentric coordinates of the sampled point. */
+ double tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3];
+ ASSERT(rng && v0 && v1 && v2 && v3 && sample);
+
+ s = ssp_rng_canonical(rng);
+ t = ssp_rng_canonical(rng);
+ u = ssp_rng_canonical(rng);
+
+ if (s + t > 1) { /* Cut and fold the cube into a prism */
+ s = 1 - s;
+ t = 1 - t;
+ }
+ if (t + u > 1) { /* Cut and fold the prism into a tetrahedron */
+ double swp = u;
+ u = 1 - s - t;
+ t = 1 - swp;
+ }
+ else if (s + t + u > 1) {
+ double swp = u;
+ u = s + t + u - 1;
+ s = 1 - t - swp;
+ }
+ a = 1 - s - t - u;
+
+ d3_add(sample,
+ d3_add(tmp4, d3_muld(tmp0, v0, a), d3_muld(tmp1, v1, s)),
+ d3_add(tmp5, d3_muld(tmp2, v2, t), d3_muld(tmp3, v3, u)));
+
+ if (pdf)
+ *pdf = ssp_ran_tetrahedron_uniform_pdf(v0, v1, v2, v3);
+
+ return sample;
+}
+
+float*
+ssp_ran_tetrahedron_uniform_float
+ (struct ssp_rng* rng,
+ const float v0[3],
+ const float v1[3],
+ const float v2[3],
+ const float v3[3],
+ float sample[3],
+ float* pdf)
+{
+ float a, s, t, u; /* Barycentric coordinates of the sampled point. */
+ float tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3];
+ ASSERT(rng && v0 && v1 && v2 && v3 && sample);
+
+ s = ssp_rng_canonical_float(rng);
+ t = ssp_rng_canonical_float(rng);
+ u = ssp_rng_canonical_float(rng);
+
+ if (s + t > 1) { /* Cut and fold the cube into a prism */
+ s = 1 - s;
+ t = 1 - t;
+ }
+ if (t + u > 1) { /* Cut and fold the prism into a tetrahedron */
+ float swp = u;
+ u = 1 - s - t;
+ t = 1 - swp;
+ }
+ else if (s + t + u > 1) {
+ float swp = u;
+ u = s + t + u - 1;
+ s = 1 - t - swp;
+ }
+ a = 1 - s - t - u;
+
+ f3_add(sample,
+ f3_add(tmp4, f3_mulf(tmp0, v0, a), f3_mulf(tmp1, v1, s)),
+ f3_add(tmp5, f3_mulf(tmp2, v2, t), f3_mulf(tmp3, v3, u)));
+
+ if (pdf)
+ *pdf = ssp_ran_tetrahedron_uniform_float_pdf(v0, v1, v2, v3);
+
+ return sample;
+}
+
+double*
ssp_ran_hemisphere_uniform_local
(struct ssp_rng* rng,
double sample[3],
@@ -411,15 +499,20 @@ ssp_ran_sphere_hg_local
{
double phi, R, cos_theta, sin_theta;
ASSERT(fabs(g) <= 1 && rng && sample);
- phi = ssp_rng_uniform_double(rng, 0, 2 * PI);
- R = ssp_rng_canonical(rng);
- cos_theta = 2 * R*(1 + g)*(1 + g)*(g*(R - 1) + 1)
- / ((1 - g*(1 - 2 * R))*(1 - g*(1 - 2 * R))) - 1;
- sin_theta = cos2sin(cos_theta);
- sample[0] = cos(phi) * sin_theta;
- sample[1] = sin(phi) * sin_theta;
- sample[2] = cos_theta;
- if (pdf) *pdf = ssp_ran_sphere_hg_local_pdf(g, sample);
+ if(fabs(g) == 1) {
+ d3(sample, 0, 0, g);
+ if(pdf) *pdf = INF;
+ } else {
+ phi = ssp_rng_uniform_double(rng, 0, 2 * PI);
+ R = ssp_rng_canonical(rng);
+ cos_theta = 2 * R*(1 + g)*(1 + g)*(g*(R - 1) + 1)
+ / ((1 - g * (1 - 2 * R))*(1 - g * (1 - 2 * R))) - 1;
+ sin_theta = cos2sin(cos_theta);
+ sample[0] = cos(phi) * sin_theta;
+ sample[1] = sin(phi) * sin_theta;
+ sample[2] = cos_theta;
+ if(pdf) *pdf = ssp_ran_sphere_hg_local_pdf(g, sample);
+ }
return sample;
}
@@ -432,15 +525,20 @@ ssp_ran_sphere_hg_float_local
{
float phi, R, cos_theta, sin_theta;
ASSERT(fabsf(g) <= 1 && rng && sample);
- phi = ssp_rng_uniform_float(rng, 0, 2 * (float)PI);
- R = ssp_rng_canonical_float(rng);
- cos_theta = 2 * R*(1 + g)*(1 + g)*(g*(R - 1) + 1)
- / ((1 - g*(1 - 2 * R))*(1 - g*(1 - 2 * R))) - 1;
- sin_theta = cosf2sinf(cos_theta);
- sample[0] = cosf(phi) * sin_theta;
- sample[1] = sinf(phi) * sin_theta;
- sample[2] = cos_theta;
- if (pdf) *pdf = ssp_ran_sphere_hg_float_local_pdf(g, sample);
+ if(fabsf(g) == 1) {
+ f3(sample, 0, 0, g);
+ if(pdf) *pdf = (float)INF;
+ } else {
+ phi = ssp_rng_uniform_float(rng, 0, 2 * (float)PI);
+ R = ssp_rng_canonical_float(rng);
+ cos_theta = 2 * R*(1 + g)*(1 + g)*(g*(R - 1) + 1)
+ / ((1 - g * (1 - 2 * R))*(1 - g * (1 - 2 * R))) - 1;
+ sin_theta = cosf2sinf(cos_theta);
+ sample[0] = cosf(phi) * sin_theta;
+ sample[1] = sinf(phi) * sin_theta;
+ sample[2] = cos_theta;
+ if(pdf) *pdf = ssp_ran_sphere_hg_float_local_pdf(g, sample);
+ }
return sample;
}
@@ -453,6 +551,7 @@ ssp_ran_sphere_hg_pdf
double epsilon_g, epsilon_mu;
ASSERT(-1 <= g && g <= +1 &&
up && sample && d3_is_normalized(up) && d3_is_normalized(sample));
+ if(fabs(g) == 1) return INF;
if(g>0) {
epsilon_g = 1 - g;
epsilon_mu = 1 - d3_dot(sample, up);
@@ -474,6 +573,7 @@ ssp_ran_sphere_hg_float_pdf
float epsilon_g, epsilon_mu;
ASSERT(-1 <= g && g <= +1 &&
up && sample && f3_is_normalized(up) && f3_is_normalized(sample));
+ if(fabsf(g) == 1) return (float)INF;
if(g>0) {
epsilon_g = 1 - g;
epsilon_mu = 1 - f3_dot(sample, up);
diff --git a/src/ssp_ranst_discrete.c b/src/ssp_ranst_discrete.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/ssp_ranst_gaussian.c b/src/ssp_ranst_gaussian.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/ssp_ranst_piecewise_linear.c b/src/ssp_ranst_piecewise_linear.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/ssp_rng.c b/src/ssp_rng.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
@@ -106,31 +106,20 @@ static res_T
rng_kiss_read(void* data, FILE* file)
{
struct rng_kiss* rng = (struct rng_kiss*)data;
- struct darray_char buf;
- res_T res = RES_OK;
- int err;
+ int n;
ASSERT(data && file);
+ n = fscanf(file, "%u %u %u %u\n", &rng->x, &rng->y, &rng->z, &rng->c);
+ return (n == EOF || n < 4) ? RES_IO_ERR : RES_OK;
+}
- darray_char_init(&mem_default_allocator, &buf);
- res = darray_char_resize(&buf, 64);
- if(res != RES_OK) goto error;
-
- while(fgets(darray_char_data_get(&buf), (int)darray_char_size_get(&buf), file)) {
- /* Ensure that the whole line is read */
- if(strrchr(darray_char_cdata_get(&buf), '\n')) break;
- res = darray_char_resize(&buf, darray_char_size_get(&buf) * 2);
- if(res != RES_OK) goto error;
- }
-
- err = sscanf(darray_char_cdata_get(&buf), "%u %u %u %u",
- &rng->x, &rng->y, &rng->z, &rng->c);
- res = err == EOF || !err ? RES_IO_ERR : RES_OK;
-
-exit:
- darray_char_release(&buf);
- return res;
-error:
- goto exit;
+static res_T
+rng_kiss_read_cstr(void* data, const char* cstr)
+{
+ struct rng_kiss* rng = (struct rng_kiss*)data;
+ int n;
+ ASSERT(data && cstr);
+ n = sscanf(cstr, "%u %u %u %u\n", &rng->x, &rng->y, &rng->z, &rng->c);
+ return (n == EOF || n < 4) ? RES_IO_ERR : RES_OK;
}
static res_T
@@ -144,6 +133,26 @@ rng_kiss_write(const void* data, FILE* file)
}
static res_T
+rng_kiss_write_cstr
+ (const void* data,
+ char* buf,
+ const size_t bufsz,
+ size_t* out_len)
+{
+ const struct rng_kiss* rng = (const struct rng_kiss*)data;
+ int len = 0;
+ ASSERT(data);
+
+ len = snprintf(buf, bufsz, "%u %u %u %u\n", rng->x, rng->y, rng->z, rng->c);
+ CHK(len > 0);
+ if((size_t)len >= (bufsz - 1/*null char*/)) {
+ buf[bufsz-1] = '\0';
+ }
+ if(out_len) *out_len = (size_t)len;
+ return RES_OK;
+}
+
+static res_T
rng_kiss_init(struct mem_allocator* allocator, void* data)
{
(void)allocator;
@@ -181,7 +190,9 @@ const struct ssp_rng_type ssp_rng_kiss = {
rng_kiss_get,
rng_kiss_discard,
rng_kiss_read,
+ rng_kiss_read_cstr,
rng_kiss_write,
+ rng_kiss_write_cstr,
rng_kiss_entropy,
0,
UINT32_MAX,
@@ -242,6 +253,37 @@ rng_cxx_write<RAN_NAMESPACE::random_device>(const void* data, FILE* file)
template<typename RNG>
static res_T
+rng_cxx_write_cstr
+ (const void* data,
+ char* buf,
+ const size_t bufsz,
+ size_t* out_len)
+{
+ int len = 0;
+ std::stringstream stream;
+ RNG* rng = (RNG*)data;
+ ASSERT(rng);
+ stream << *rng << std::endl;
+ len = snprintf(buf, bufsz, "%s", stream.str().c_str());
+ CHK(len > 0);
+ if((size_t)len >= (bufsz - 1/*null char*/)) {
+ buf[bufsz-1] = '\0';
+ }
+ if(out_len) *out_len = (size_t)len;
+ return RES_OK;
+}
+
+template<>
+res_T
+rng_cxx_write_cstr<RAN_NAMESPACE::random_device>
+ (const void* data, char* buf, const size_t bufsz, size_t* out_len)
+{
+ (void)data; (void)buf, (void)bufsz, (void)out_len;
+ return RES_BAD_OP;
+}
+
+template<typename RNG>
+static res_T
rng_cxx_read(void* data, FILE* file)
{
std::stringstream stream;
@@ -261,7 +303,27 @@ template<>
res_T
rng_cxx_read<RAN_NAMESPACE::random_device>(void* data, FILE* file)
{
- (void) data; (void) file;
+ (void) data; (void)file;
+ return RES_BAD_OP;
+}
+
+template<typename RNG>
+static res_T
+rng_cxx_read_cstr(void* data, const char* cstr)
+{
+ std::stringstream stream;
+ RNG* rng = (RNG*)data;
+ ASSERT(rng && cstr && cstr[strlen(cstr)-1] == '\n');
+ stream << std::string(cstr);
+ stream >> *rng;
+ return stream.fail() ? RES_IO_ERR : RES_OK;
+}
+
+template<>
+res_T
+rng_cxx_read_cstr<RAN_NAMESPACE::random_device>(void* data, const char* cstr)
+{
+ (void)data, (void)cstr;
return RES_BAD_OP;
}
@@ -329,7 +391,9 @@ const struct ssp_rng_type ssp_rng_mt19937_64 = {
rng_cxx_get<RAN_NAMESPACE::mt19937_64>,
rng_cxx_discard<RAN_NAMESPACE::mt19937_64>,
rng_cxx_read<RAN_NAMESPACE::mt19937_64>,
+ rng_cxx_read_cstr<RAN_NAMESPACE::mt19937_64>,
rng_cxx_write<RAN_NAMESPACE::mt19937_64>,
+ rng_cxx_write_cstr<RAN_NAMESPACE::mt19937_64>,
rng_cxx_entropy<RAN_NAMESPACE::mt19937_64>,
RAN_NAMESPACE::mt19937_64::min(),
RAN_NAMESPACE::mt19937_64::max(),
@@ -345,7 +409,9 @@ const struct ssp_rng_type ssp_rng_ranlux48 = {
rng_cxx_get<RAN_NAMESPACE::ranlux48>,
rng_cxx_discard<RAN_NAMESPACE::ranlux48>,
rng_cxx_read<RAN_NAMESPACE::ranlux48>,
+ rng_cxx_read_cstr<RAN_NAMESPACE::ranlux48>,
rng_cxx_write<RAN_NAMESPACE::ranlux48>,
+ rng_cxx_write_cstr<RAN_NAMESPACE::ranlux48>,
rng_cxx_entropy<RAN_NAMESPACE::ranlux48>,
RAN_NAMESPACE::ranlux48::min(),
RAN_NAMESPACE::ranlux48::max(),
@@ -361,7 +427,9 @@ const struct ssp_rng_type ssp_rng_random_device = {
rng_cxx_get<RAN_NAMESPACE::random_device>,
rng_cxx_discard<RAN_NAMESPACE::random_device>,
rng_cxx_read<RAN_NAMESPACE::random_device>,
+ rng_cxx_read_cstr<RAN_NAMESPACE::random_device>,
rng_cxx_write<RAN_NAMESPACE::random_device>,
+ rng_cxx_write_cstr<RAN_NAMESPACE::random_device>,
rng_cxx_entropy<RAN_NAMESPACE::random_device>,
RAN_NAMESPACE::random_device::min(),
RAN_NAMESPACE::random_device::max(),
@@ -401,7 +469,9 @@ const struct ssp_rng_type ssp_rng_threefry = {
rng_cxx_get<threefry_T>,
rng_cxx_discard<threefry_T>,
rng_cxx_read<threefry_T>,
+ rng_cxx_read_cstr<threefry_T>,
rng_cxx_write<threefry_T>,
+ rng_cxx_write_cstr<threefry_T>,
rng_cxx_entropy<threefry_T>,
threefry_T::min(),
threefry_T::max(),
@@ -418,7 +488,9 @@ const struct ssp_rng_type ssp_rng_aes = {
rng_cxx_get<aes_T>,
rng_cxx_discard<aes_T>,
rng_cxx_read<aes_T>,
+ rng_cxx_read_cstr<aes_T>,
rng_cxx_write<aes_T>,
+ rng_cxx_write_cstr<aes_T>,
rng_cxx_entropy<aes_T>,
aes_T::min(),
aes_T::max(),
@@ -654,12 +726,30 @@ ssp_rng_read(struct ssp_rng* rng, FILE* stream)
}
res_T
+ssp_rng_read_cstr(struct ssp_rng* rng, const char* cstr)
+{
+ if(!rng || !cstr) return RES_BAD_ARG;
+ return rng->type.read_cstr(rng->state, cstr);
+}
+
+res_T
ssp_rng_write(const struct ssp_rng* rng, FILE* stream)
{
if(!rng || !stream) return RES_BAD_ARG;
return rng->type.write(rng->state, stream);
}
+res_T
+ssp_rng_write_cstr
+ (const struct ssp_rng* rng,
+ char* buf,
+ const size_t bufsz,
+ size_t* len)
+{
+ if(!rng) return RES_BAD_ARG;
+ return rng->type.write_cstr(rng->state, buf, bufsz, len);
+}
+
double
ssp_rng_entropy(const struct ssp_rng* rng)
{
diff --git a/src/ssp_rng_c.h b/src/ssp_rng_c.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/ssp_rng_proxy.c b/src/ssp_rng_proxy.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
@@ -31,6 +31,7 @@
#include "ssp_rng_c.h"
+#include <rsys/dynamic_array_char.h>
#include <rsys/mutex.h>
#include <rsys/ref_count.h>
#include <rsys/signal.h>
@@ -38,12 +39,19 @@
#include <limits.h>
-#define BUCKET_SIZE_DEFAULT 1000000
+#define BUCKET_SIZE_DEFAULT 1000000 /* #RNs per bucket */
+#define STATE_CACHE_HINT_MAX_SIZE (32*(1024*1024)) /* 32 MB */
-/* FIFO list of RNG states */
+/* Cache of RNG states */
struct rng_state_cache {
- FILE* stream; /* Stream in which the RNG states are stored */
- long head, tail; /* Position onto the head/tail `states' stream */
+ struct darray_char state; /* Save the next RNG state with 'no_wstream' */
+ struct darray_char state_scratch; /* Scracth state buffer */
+ FILE* stream; /* Stream into which the RNG states are stored */
+ size_t state_pitch; /* #RNs between 2 cached states */
+ size_t nstates; /* #cached states */
+ long read, write; /* Offset into the stream where to read/write RNG states */
+ int no_wstream; /* Define if the RNG states are no more written to a stream */
+ int no_rstream; /* Define if the RNG states are no more read from a stream */
};
CLBK(rng_proxy_cb_T, ARG1(const struct ssp_rng_proxy*));
@@ -88,7 +96,7 @@ struct ssp_rng_proxy {
};
/* Return a RNG with a pool of `bucket_size' indenpendant random numbers. Each
- * pool are ensure to be independant per `bucket_id' in [0, N) and per function
+ * pool are ensured to be independant per `bucket_id' in [0, N) and per function
* call, i.e. calling this function X times with the same bucket_id will
* provide X different random pools.
*
@@ -107,16 +115,58 @@ rng_proxy_next_ran_pool
(struct ssp_rng_proxy* proxy,
const size_t bucket_id);
+/* Write the RNG state into buf. State data are terminated by a null char */
+static res_T
+rng_write_cstr
+ (const struct ssp_rng* rng,
+ struct darray_char* buf,
+ size_t* out_len) /* May be NULL. String length without the null char */
+{
+ size_t len;
+ res_T res = RES_OK;
+ ASSERT(rng && buf);
+
+ /* Write the RNG state into a temporary buffer */
+ res = ssp_rng_write_cstr
+ (rng, darray_char_data_get(buf), darray_char_size_get(buf), &len);
+ if(res != RES_OK) goto error;
+
+ /* Not sufficient space to store the state */
+ if(len >= darray_char_size_get(buf)) {
+ res = darray_char_resize(buf, len + 1/*null char*/);
+ if(res != RES_OK) goto error;
+
+ res = ssp_rng_write_cstr
+ (rng, darray_char_data_get(buf), darray_char_size_get(buf), &len);
+ if(res != RES_OK) goto error;
+ ASSERT(len + 1/*null char*/ == darray_char_size_get(buf));
+ }
+
+ if(out_len) *out_len = len;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
/*******************************************************************************
* Cache of RNG states
******************************************************************************/
static res_T
-rng_state_cache_init(struct rng_state_cache* cache)
+rng_state_cache_init
+ (struct mem_allocator* allocator,
+ const size_t state_pitch, /* #RNs between cached states */
+ struct rng_state_cache* cache)
{
ASSERT(cache);
+ memset(cache, 0, sizeof(*cache));
+ darray_char_init(allocator, &cache->state);
+ darray_char_init(allocator, &cache->state_scratch);
cache->stream = tmpfile();
if(!cache->stream) return RES_IO_ERR;
- cache->head = cache->tail = ftell(cache->stream);
+ cache->read = cache->write = ftell(cache->stream);
+ cache->state_pitch = state_pitch;
return RES_OK;
}
@@ -125,48 +175,126 @@ rng_state_cache_release(struct rng_state_cache* cache)
{
ASSERT(cache);
if(cache->stream) fclose(cache->stream);
+ darray_char_release(&cache->state);
+ darray_char_release(&cache->state_scratch);
}
static char
rng_state_cache_is_empty(struct rng_state_cache* cache)
{
ASSERT(cache);
- return cache->head == cache->tail;
+ return cache->nstates == 0;
}
static res_T
rng_state_cache_read(struct rng_state_cache* cache, struct ssp_rng* rng)
{
- res_T res;
+ res_T res = RES_OK;
ASSERT(cache && rng && !rng_state_cache_is_empty(cache));
- /* Read the rng state from the stream */
- fseek(cache->stream, cache->head, SEEK_SET);
- res = ssp_rng_read(rng, cache->stream);
- if(res != RES_OK) return res;
- cache->head = ftell(cache->stream);
-
- /* Flush the state cache stream if there is no more RNG state */
- if(rng_state_cache_is_empty(cache)) {
+ if(!cache->no_rstream
+ && cache->read == cache->write
+ && cache->nstates == 1/* A state is saved in 'cache->state' */) {
+ /* There is not more data cached into the stream. Close the stream and do
+ * not rely anymore on the proxy RNG to generate the RNG states */
fclose(cache->stream);
- cache->stream = tmpfile();
- if(!cache->stream) return RES_IO_ERR;
- cache->head = cache->tail = ftell(cache->stream);
+ cache->stream = NULL;
+ cache->no_rstream = 1;
}
- return RES_OK;
+ /* Read the cached RNG state from the stream */
+ if(!cache->no_rstream) {
+ fseek(cache->stream, cache->read, SEEK_SET);
+ res = ssp_rng_read(rng, cache->stream);
+ if(res != RES_OK) goto error;
+ cache->read = ftell(cache->stream);
+
+ /* The fp reaches the end of the cached data */
+ if(cache->read >= STATE_CACHE_HINT_MAX_SIZE) {
+ cache->read = 0;
+ }
+
+ /* Remove one cached states */
+ cache->nstates -= 1;
+
+ /* Generate the next RNG state and load the cached one */
+ } else {
+ /* Copy the cached RNG state */
+ res = darray_char_copy(&cache->state_scratch, &cache->state);
+ if(res != RES_OK) goto error;
+
+ /* Load the cached RNG state */
+ res = ssp_rng_read_cstr(rng, darray_char_cdata_get(&cache->state));
+ if(res != RES_OK) goto error;
+
+ /* Setup the next RNG state */
+ res = ssp_rng_discard(rng, cache->state_pitch);
+ if(res != RES_OK) goto error;
+
+ /* Save the next RNG state */
+ res = rng_write_cstr(rng, &cache->state, NULL);
+ if(res != RES_OK) goto error;
+
+ /* Setup the current RNG state */
+ res = ssp_rng_read_cstr(rng, darray_char_cdata_get(&cache->state_scratch));
+ if(res != RES_OK) goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
}
static res_T
rng_state_cache_write(struct rng_state_cache* cache, struct ssp_rng* rng)
{
- res_T res;
+ res_T res = RES_OK;
ASSERT(cache && rng);
- fseek(cache->stream, cache->tail, SEEK_SET);
- res = ssp_rng_write(rng, cache->stream);
- if(res != RES_OK) return res;
- cache->tail = ftell(cache->stream);
- return RES_OK;
+
+ if(cache->no_wstream) goto exit; /* Do not cache the submitted state */
+
+ fseek(cache->stream, cache->write, SEEK_SET);
+ if(rng_state_cache_is_empty(cache) || cache->write > cache->read) {
+ /* Directly write the RNG state into the cache stream */
+ res = ssp_rng_write(rng, cache->stream);
+ if(res != RES_OK) goto error;
+ cache->write = ftell(cache->stream);
+
+ /* The fp exceed the amount of cached data */
+ if(cache->write >= STATE_CACHE_HINT_MAX_SIZE) {
+ cache->write = 0;
+ }
+
+ } else {
+ size_t len;
+ res = rng_write_cstr(rng, &cache->state, &len);
+ if(res != RES_OK) goto error;
+
+ if(len > (size_t)(cache->read - cache->write)) {
+ /* No sufficient space into the cache stream to save the RNG state */
+ cache->no_wstream = 1;
+ } else {
+ /* Write the RNG state into the cached stream */
+ size_t sz;
+ sz = fwrite(darray_char_cdata_get(&cache->state), 1, len, cache->stream);
+ if(sz != len) { res = RES_IO_ERR; goto error; }
+ cache->write = ftell(cache->stream);
+
+ /* The fp exceed the amount of cached data */
+ if(cache->write >= STATE_CACHE_HINT_MAX_SIZE) {
+ cache->write = 0;
+ }
+ }
+ }
+
+ /* Update the number of cached states */
+ cache->nstates += 1;
+
+exit:
+ return res;
+error:
+ goto exit;
}
/*******************************************************************************
@@ -224,6 +352,13 @@ rng_bucket_read(void* data, FILE* file)
}
static res_T
+rng_bucket_read_cstr(void* data, const char* cstr)
+{
+ (void)data, (void)cstr;
+ return RES_BAD_OP;
+}
+
+static res_T
rng_bucket_write(const void* data, FILE* file)
{
(void)data, (void)file;
@@ -231,6 +366,17 @@ rng_bucket_write(const void* data, FILE* file)
}
static res_T
+rng_bucket_write_cstr
+ (const void* data,
+ char* buf,
+ const size_t bufsz,
+ size_t* len)
+{
+ (void)data, (void)buf, (void)bufsz, (void)len;
+ return RES_BAD_OP;
+}
+
+static res_T
rng_bucket_init(struct mem_allocator* allocator, void* data)
{
struct rng_bucket* rng = (struct rng_bucket*)data;
@@ -280,7 +426,9 @@ static const struct ssp_rng_type RNG_BUCKET_NULL = {
rng_bucket_get,
rng_bucket_discard,
rng_bucket_read,
+ rng_bucket_read_cstr,
rng_bucket_write,
+ rng_bucket_write_cstr,
rng_bucket_entropy,
INT_MAX, /* Min dummy value */
0, /* Max dummy value */
@@ -341,12 +489,15 @@ rng_proxy_clear(struct ssp_rng_proxy* proxy)
}
static res_T
-rng_proxy_setup(struct ssp_rng_proxy* proxy, const size_t nbuckets)
+rng_proxy_setup
+ (struct ssp_rng_proxy* proxy,
+ const size_t sequence_pitch, /* #RNs between 2 consecutive sequences */
+ const size_t nbuckets)
{
size_t ibucket;
res_T res = RES_OK;
- ASSERT(proxy && nbuckets);
+ ASSERT(proxy && sequence_pitch && nbuckets);
rng_proxy_clear(proxy);
sa_add(proxy->states, nbuckets);
@@ -354,7 +505,8 @@ rng_proxy_setup(struct ssp_rng_proxy* proxy, const size_t nbuckets)
sa_add(proxy->buckets, nbuckets);
FOR_EACH(ibucket, 0, nbuckets) {
- res = rng_state_cache_init(proxy->states+ibucket);
+ res = rng_state_cache_init
+ (proxy->allocator, sequence_pitch, proxy->states+ibucket);
if(res != RES_OK) goto error;
res = ssp_rng_create(proxy->allocator, &proxy->type, proxy->pools+ibucket);
if(res != RES_OK) goto error;
@@ -378,7 +530,7 @@ rng_proxy_release(ref_T* ref)
sa_release(proxy->states);
sa_release(proxy->pools);
sa_release(proxy->buckets);
- SSP(rng_ref_put(proxy->rng));
+ if(proxy->rng) SSP(rng_ref_put(proxy->rng));
if(proxy->mutex) mutex_destroy(proxy->mutex);
MEM_RM(proxy->allocator, proxy);
}
@@ -393,124 +545,106 @@ ssp_rng_proxy_create
const size_t nbuckets,
struct ssp_rng_proxy** out_proxy)
{
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
const size_t sz = BUCKET_SIZE_DEFAULT * nbuckets;
- return ssp_rng_proxy_create2
- (mem_allocator, type, 0, sz, sz, nbuckets, out_proxy);
+ args.type = type;
+ args.sequence_offset = 0;
+ args.sequence_size = sz;
+ args.sequence_pitch = sz;
+ args.nbuckets = nbuckets;
+ return ssp_rng_proxy_create2(mem_allocator, &args, out_proxy);
}
res_T
-ssp_rng_proxy_create2
+ssp_rng_proxy_create_from_rng
(struct mem_allocator* mem_allocator,
- const struct ssp_rng_type* type,
- const size_t sequence_offset, /* #RNs before the 1st valid sequence */
- const size_t sequence_size, /* #RNs in a sequence */
- const size_t sequence_pitch, /* #RNs between 2 consecutive sequences */
- const size_t nbuckets, /* #buckets in a sequence */
+ const struct ssp_rng* rng,
+ const size_t nbuckets,
struct ssp_rng_proxy** out_proxy)
{
- struct mem_allocator* allocator = NULL;
- struct ssp_rng_proxy* proxy = NULL;
- size_t i;
- res_T res = RES_OK;
-
- if(!type || !out_proxy || !sequence_size || sequence_pitch < sequence_size
- || !nbuckets || sequence_size < nbuckets) {
- res = RES_BAD_ARG;
- goto error;
- }
-
- allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
- proxy = (struct ssp_rng_proxy*)
- MEM_CALLOC(allocator, 1, sizeof(struct ssp_rng_proxy));
- if(!proxy) {
- res = RES_MEM_ERR;
- goto error;
- }
- proxy->allocator = allocator;
- ref_init(&proxy->ref);
- proxy->bucket_size = sequence_size / nbuckets;
- proxy->sequence_bias = sequence_pitch - (proxy->bucket_size * nbuckets);
-
- res = ssp_rng_create(allocator, type, &proxy->rng);
- if(res != RES_OK) goto error;
- proxy->type = *type;
-
- res = ssp_rng_discard(proxy->rng, sequence_offset);
- if(res != RES_OK) goto error;
-
- proxy->mutex = mutex_create();
- if(!proxy->mutex) {
- res = RES_MEM_ERR;
- goto error;
- }
-
- FOR_EACH(i, 0, RNG_PROXY_SIGS_COUNT__) {
- SIG_INIT(proxy->signals + i);
- }
-
- res = rng_proxy_setup(proxy, nbuckets);
- if(res != RES_OK) goto error;
-
-exit:
- if(out_proxy) *out_proxy = proxy;
- return res;
-error:
- if(proxy) {
- SSP(rng_proxy_ref_put(proxy));
- proxy = NULL;
- }
- goto exit;
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
+ const size_t sz = BUCKET_SIZE_DEFAULT * nbuckets;
+ args.rng = rng;
+ args.sequence_offset = 0;
+ args.sequence_size = sz;
+ args.sequence_pitch = sz;
+ args.nbuckets = nbuckets;
+ return ssp_rng_proxy_create2(mem_allocator, &args, out_proxy);
}
res_T
-ssp_rng_proxy_create_from_rng
+ssp_rng_proxy_create2
(struct mem_allocator* mem_allocator,
- const struct ssp_rng* rng,
- const size_t nbuckets,
+ const struct ssp_rng_proxy_create2_args* args,
struct ssp_rng_proxy** out_proxy)
{
+ struct darray_char buf;
struct mem_allocator* allocator = NULL;
struct ssp_rng_proxy* proxy = NULL;
- struct ssp_rng_type type;
- FILE* stream = NULL;
size_t i;
res_T res = RES_OK;
- if(!rng || !out_proxy || !nbuckets) {
+ darray_char_init(mem_allocator, &buf);
+
+ if(!args
+ || !out_proxy
+ || !args->sequence_size
+ || !args->nbuckets
+ || (!args->type && !args->rng)
+ || args->sequence_pitch < args->sequence_size
+ || args->sequence_size < args->nbuckets) {
res = RES_BAD_ARG;
goto error;
}
allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
- proxy = (struct ssp_rng_proxy*)
- MEM_CALLOC(allocator, 1, sizeof(struct ssp_rng_proxy));
+ proxy = (struct ssp_rng_proxy*)MEM_CALLOC(allocator, 1, sizeof(*proxy));
if(!proxy) {
res = RES_MEM_ERR;
goto error;
}
proxy->allocator = allocator;
ref_init(&proxy->ref);
- proxy->bucket_size = BUCKET_SIZE_DEFAULT;
- proxy->sequence_bias = 1;
+ proxy->bucket_size = args->sequence_size / args->nbuckets;
+ proxy->sequence_bias =
+ args->sequence_pitch - (proxy->bucket_size * args->nbuckets);
- res = ssp_rng_get_type(rng, &type);
- if(res != RES_OK) goto error;
+ /* Create the proxy RNG in its default state */
+ if(!args->rng) {
+ res = ssp_rng_create(allocator, args->type, &proxy->rng);
+ if(res != RES_OK) goto error;
+ proxy->type = *args->type;
- res = ssp_rng_create(allocator, &type, &proxy->rng);
- if(res != RES_OK) goto error;
- proxy->type = type;
+ /* Create the proxy RNG from a submitted RNG state */
+ } else {
+ size_t len;
- stream = tmpfile();
- if(!stream) {
- res = RES_IO_ERR;
- goto error;
- }
+ /* Create the RNG proxy of the type of the submitted RNG. Simply Ignore the
+ * submitted RNG type if any */
+ res = ssp_rng_get_type(args->rng, &proxy->type);
+ if(res != RES_OK) goto error;
- res = ssp_rng_write(rng, stream);
- if(res != RES_OK) goto error;
+ /* Bucket RNG is not allowed to be a proxy RNG */
+ if(proxy->type.init == rng_bucket_init) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
- rewind(stream);
- res = ssp_rng_read(proxy->rng, stream);
+ res = ssp_rng_create(allocator, &proxy->type, &proxy->rng);
+ if(res != RES_OK) goto error;
+
+ /* Initialise the RNG proxy state from the state of the submitted RNG */
+ res = ssp_rng_write_cstr(args->rng, NULL, 0, &len);
+ if(res != RES_OK) goto error;
+ res = darray_char_resize(&buf, len+1/*Null char*/);
+ if(res != RES_OK) goto error;
+ res = ssp_rng_write_cstr(args->rng, darray_char_data_get(&buf), len+1, &len);
+ if(res != RES_OK) goto error;
+ res = ssp_rng_read_cstr(proxy->rng, darray_char_cdata_get(&buf));
+ if(res != RES_OK) goto error;
+ }
+
+ res = ssp_rng_discard(proxy->rng, args->sequence_offset);
if(res != RES_OK) goto error;
proxy->mutex = mutex_create();
@@ -523,11 +657,11 @@ ssp_rng_proxy_create_from_rng
SIG_INIT(proxy->signals + i);
}
- res = rng_proxy_setup(proxy, nbuckets);
+ res = rng_proxy_setup(proxy, args->sequence_pitch, args->nbuckets);
if(res != RES_OK) goto error;
exit:
- if(stream) fclose(stream);
+ darray_char_release(&buf);
if(out_proxy) *out_proxy = proxy;
return res;
error:
diff --git a/src/test_ssp_ran_circle.c b/src/test_ssp_ran_circle.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_circle.h b/src/test_ssp_ran_circle.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_discrete.c b/src/test_ssp_ran_discrete.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/test_ssp_ran_discrete.h b/src/test_ssp_ran_discrete.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_ran_gaussian.c b/src/test_ssp_ran_gaussian.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_ran_gaussian.h b/src/test_ssp_ran_gaussian.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a library whose purpose is to generate [pseudo] random
* numbers and random variates.
diff --git a/src/test_ssp_ran_hemisphere.c b/src/test_ssp_ran_hemisphere.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_hemisphere.h b/src/test_ssp_ran_hemisphere.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_hg.c b/src/test_ssp_ran_hg.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_hg.h b/src/test_ssp_ran_hg.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_piecewise_linear.c b/src/test_ssp_ran_piecewise_linear.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_ran_piecewise_linear.h b/src/test_ssp_ran_piecewise_linear.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_ran_sphere.c b/src/test_ssp_ran_sphere.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_sphere.h b/src/test_ssp_ran_sphere.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_sphere_cap.c b/src/test_ssp_ran_sphere_cap.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_sphere_cap.h b/src/test_ssp_ran_sphere_cap.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_tetrahedron.c b/src/test_ssp_ran_tetrahedron.c
@@ -0,0 +1,42 @@
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#define TYPE_FLOAT 1
+#include "test_ssp_ran_tetrahedron.h"
+
+#define TYPE_FLOAT 0
+#include "test_ssp_ran_tetrahedron.h"
+
+int
+main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+ test_float();
+ test_double();
+ return 0;
+}
diff --git a/src/test_ssp_ran_tetrahedron.h b/src/test_ssp_ran_tetrahedron.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#ifndef TEST_SSP_RAN_TETRA_H
+#define TEST_SSP_RAN_TETRA_H
+
+#include "ssp.h"
+#include "test_ssp_utils.h"
+
+#include <rsys/float4.h>
+
+#define NSAMPS 128
+
+#endif /* TEST_SSP_RAN_TETRA_H */
+
+#if TYPE_FLOAT==0
+ #define REAL double
+ #define TEST test_double
+ #define RNG_UNIFORM_R ssp_rng_uniform_double
+ #define RAN_TETRA_UNIFORM ssp_ran_tetrahedron_uniform
+ #define RAN_TETRA_UNIFORM_PDF ssp_ran_tetrahedron_uniform_pdf
+ #define EQ_EPS_R eq_eps
+ #define FABS_R fabs
+ #define R3 d3
+ #define R3_DOT d3_dot
+ #define R3_SUB d3_sub
+ #define R3_MINUS d3_minus
+ #define R3_CROSS d3_cross
+ #define R3_LEN d3_len
+
+#elif TYPE_FLOAT==1
+ #define REAL float
+ #define TEST test_float
+ #define RNG_UNIFORM_R ssp_rng_uniform_float
+ #define RAN_TETRA_UNIFORM ssp_ran_tetrahedron_uniform_float
+ #define RAN_TETRA_UNIFORM_PDF ssp_ran_tetrahedron_uniform_float_pdf
+ #define EQ_EPS_R eq_epsf
+ #define FABS_R absf
+ #define R3 f3
+ #define R3_DOT f3_dot
+ #define R3_SUB f3_sub
+ #define R3_MINUS f3_minus
+ #define R3_CROSS f3_cross
+ #define R3_LEN f3_len
+
+#else
+ #error "TYPE_FLOAT must be defined either 0 or 1"
+#endif
+
+static void
+TEST()
+{
+ struct ssp_rng* rng;
+ struct mem_allocator allocator;
+ REAL samps[NSAMPS][3];
+ REAL A[3], B[3], C[3], D[3];
+ REAL v0[3], v1[3], v2[3], v3[3], v4[3];
+ REAL in0[3], in1[3], in2[3], in3[3];
+ REAL pdf[NSAMPS];
+ size_t i, j;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ CHK(ssp_rng_create(&allocator, &ssp_rng_mt19937_64, &rng) == RES_OK);
+
+ FOR_EACH(j, 0, 100) {
+ FOR_EACH(i, 0, 3) {
+ A[i] = RNG_UNIFORM_R(rng, 0, 1);
+ B[i] = RNG_UNIFORM_R(rng, 0, 1);
+ C[i] = RNG_UNIFORM_R(rng, 0, 1);
+ D[i] = RNG_UNIFORM_R(rng, 0, 1);
+ }
+
+ R3_SUB(v0, B, A);
+ R3_SUB(v1, C, A);
+ R3_SUB(v2, C, B);
+ R3_SUB(v3, D, A);
+ R3_SUB(v4, D, C);
+ /* Inward vectors perpandicular to faces */
+ R3_CROSS(in0, v0, v1);
+ if (R3_DOT(in0, v3) < 0) R3_MINUS(in0, in0);
+ R3_CROSS(in1, v1, v3);
+ if (R3_DOT(in1, v0) < 0) R3_MINUS(in1, in1);
+ R3_CROSS(in2, v3, v0);
+ if (R3_DOT(in2, v1) < 0) R3_MINUS(in2, in2);
+ R3_CROSS(in3, v2, v4);
+ if (R3_DOT(in3, v3) > 0) R3_MINUS(in3, in3);
+
+ FOR_EACH(i, 0, NSAMPS) {
+ REAL X[3], tmp[3];
+ REAL dot = 0;
+ REAL vol = 0;
+
+ CHK(RAN_TETRA_UNIFORM(rng, A, B, C, D, samps[i], &pdf[i]) == samps[i]);
+
+ /* Check sample is in the tetrahedron */
+ R3_SUB(X, samps[i], A);
+ dot = R3_DOT(X, in0);
+ CHK(sign(dot) == 1);
+ dot = R3_DOT(X, in1);
+ CHK(sign(dot) == 1);
+ dot = R3_DOT(X, in2);
+ CHK(sign(dot) == 1);
+ dot = R3_DOT(X, in3);
+ CHK(sign(dot) == -1);
+
+ /* Check pdf */
+ vol = FABS_R(R3_DOT(R3_CROSS(tmp, v0, v1), v3)) / 6;
+ CHK(EQ_EPS_R(pdf[i], RAN_TETRA_UNIFORM_PDF(A, B, C, D), (REAL)1.e-8) == 1);
+ /* Pdf is 1/vol
+ * But numerical value depends on the vector used to compute it */
+ CHK(EQ_EPS_R(vol * pdf[i], 1, (REAL)1.e-5) == 1);
+ }
+ }
+
+ ssp_rng_ref_put(rng);
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+}
+
+#undef REAL
+#undef TEST
+#undef RNG_UNIFORM_R
+#undef RAN_TETRA_UNIFORM
+#undef RAN_TETRA_UNIFORM_PDF
+#undef EQ_EPS_R
+#undef FABS_R
+#undef R3
+#undef R3_DOT
+#undef R3_SUB
+#undef R3_MINUS
+#undef R3_CROSS
+#undef R3_LEN
+#undef TYPE_FLOAT
diff --git a/src/test_ssp_ran_triangle.c b/src/test_ssp_ran_triangle.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
diff --git a/src/test_ssp_ran_triangle.h b/src/test_ssp_ran_triangle.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
@@ -77,7 +77,7 @@ TEST()
struct mem_allocator allocator;
REAL samps[NSAMPS][3];
REAL A[3], B[3], C[3];
- REAL v0[3], v1[3], v2[3], v3[3], v4[3], v5[3];
+ REAL v0[3], v1[3], v2[3], m0[3], m1[3], m2[3];
REAL plane[4];
REAL pdf;
size_t counter[2];
@@ -97,9 +97,9 @@ TEST()
R3_SUB(v0, B, A);
R3_SUB(v1, C, A);
R3_SUB(v2, C, B);
- R3_MINUS(v3, v0);
- R3_MINUS(v4, v1);
- R3_MINUS(v5, v2);
+ R3_MINUS(m0, v0);
+ R3_MINUS(m1, v1);
+ R3_MINUS(m2, v2);
R3_CROSS(plane, v0, v1);
plane[3] = -R3_DOT(plane, C);
@@ -115,14 +115,15 @@ TEST()
dot = R3_DOT(R3_CROSS(tmp0, tmp0, v0), R3_CROSS(tmp1, v1, v0));
CHK(sign(dot) == 1);
R3_SUB(tmp0, samps[i], B);
- dot = R3_DOT(R3_CROSS(tmp0, tmp0, v2), R3_CROSS(tmp1, v3, v2));
+ dot = R3_DOT(R3_CROSS(tmp0, tmp0, v2), R3_CROSS(tmp1, m0, v2));
CHK(sign(dot) == 1);
R3_SUB(tmp0, samps[i], C);
- dot = R3_DOT(R3_CROSS(tmp0, tmp0, v4), R3_CROSS(tmp1, v5, v4));
+ dot = R3_DOT(R3_CROSS(tmp0, tmp0, m1), R3_CROSS(tmp1, m2, m1));
CHK(sign(dot) == 1);
area = R3_LEN(tmp1) * 0.5f;
- CHK(EQ_EPS_R(1 / area, RAN_TRIANGLE_UNIFORM_PDF(A, B, C), (REAL)1.e-6) == 1);
+ CHK(EQ_EPS_R(pdf, RAN_TRIANGLE_UNIFORM_PDF(A, B, C), (REAL)1.e-8) == 1);
+ CHK(EQ_EPS_R(1 / area, pdf, (REAL)1.e-6) == 1);
}
nsteps = 10000;
diff --git a/src/test_ssp_ran_uniform_disk.c b/src/test_ssp_ran_uniform_disk.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_ran_uniform_disk.h b/src/test_ssp_ran_uniform_disk.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_rng.c b/src/test_ssp_rng.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
@@ -51,7 +51,9 @@ test_rng(const struct ssp_rng_type* type)
uint64_t datai1[NRAND];
double datad[NRAND];
float dataf[NRAND];
+ size_t len;
char buf[512];
+ char* cstr = NULL;
int i, j;
res_T r;
const char can_set = (type != &ssp_rng_random_device);
@@ -180,7 +182,7 @@ test_rng(const struct ssp_rng_type* type)
time_dump(&t0, TIME_SEC|TIME_MSEC|TIME_USEC, NULL, buf, sizeof(buf));
printf("1,000,000 random numbers in %s\n", buf);
- if (can_have_entropy) {
+ if(can_have_entropy) {
printf("Entropy for this implementation and system: %f\n",
ssp_rng_entropy(rng));
}
@@ -192,6 +194,15 @@ test_rng(const struct ssp_rng_type* type)
CHK(ssp_rng_write(NULL, stream) == RES_BAD_ARG);
CHK(ssp_rng_write(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP));
+ CHK(ssp_rng_write_cstr(NULL, NULL, 0, &len) == RES_BAD_ARG);
+ CHK(ssp_rng_write_cstr(rng, NULL, 0, NULL) == (can_rw ? RES_OK : RES_BAD_OP));
+ CHK(ssp_rng_write_cstr(rng, NULL, 0, &len) == (can_rw ? RES_OK : RES_BAD_OP));
+ cstr = mem_calloc(len+1, 1);
+ CHK(ssp_rng_write_cstr(rng, cstr, len+1, NULL) == (can_rw ? RES_OK : RES_BAD_OP));
+
+ /* Reserialize the RNG state */
+ CHK(ssp_rng_write(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP));
+
FOR_EACH(i, 0, NRAND)
datai0[i] = ssp_rng_get(rng);
@@ -200,14 +211,37 @@ test_rng(const struct ssp_rng_type* type)
CHK(ssp_rng_read(NULL, stream) == RES_BAD_ARG);
CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_IO_ERR : RES_BAD_OP));
rewind(stream);
+
+ /* Read the first state of the stream */
CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP));
+ if(can_rw) {
+ FOR_EACH(i, 0, NRAND) {
+ uint64_t rn = ssp_rng_get(rng);
+ CHK(rn == datai0[i]);
+ }
+ }
+
+ /* Read the second state of the stream */
+ CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP));
+ if(can_rw) {
+ FOR_EACH(i, 0, NRAND) {
+ uint64_t rn = ssp_rng_get(rng);
+ CHK(rn == datai0[i]);
+ }
+ }
- if (can_rw) {
+ /* Read the RNG state from an in memory buffer */
+ CHK(ssp_rng_read_cstr(NULL, cstr) == RES_BAD_ARG);
+ CHK(ssp_rng_read_cstr(rng, NULL) == RES_BAD_ARG);
+ CHK(ssp_rng_read_cstr(rng, cstr) == (can_rw ? RES_OK : RES_BAD_OP));
+ if(can_rw) {
FOR_EACH(i, 0, NRAND) {
uint64_t rn = ssp_rng_get(rng);
CHK(rn == datai0[i]);
}
}
+
+ mem_rm(cstr);
fclose(stream);
CHK(ssp_rng_ref_put(rng) == RES_OK);
diff --git a/src/test_ssp_rng.h b/src/test_ssp_rng.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_rng_proxy.c b/src/test_ssp_rng_proxy.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
@@ -73,52 +73,52 @@ test_creation(void)
static void
test_creation2(void)
{
- const struct ssp_rng_type* type = &ssp_rng_mt19937_64;
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
struct ssp_rng_proxy* proxy;
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 0, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 0, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 0, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 0, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 32, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 32, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 32, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 0, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 0, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 0, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 0, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 0, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 32, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 32, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 32, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 1, NULL) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 0, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 0, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 0, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 0, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 32, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 32, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 32, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 0, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 0, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 0, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 0, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 0, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 0, 32, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 0, 32, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, NULL, 0, 32, 32, 1, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 1, &proxy) == RES_OK);
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+
+ args.type = &ssp_rng_mt19937_64;
+ args.sequence_offset = 0;
+ args.sequence_size = 32;
+ args.sequence_pitch = 32;
+ args.nbuckets = 1;
+ CHK(ssp_rng_proxy_create2(NULL, &args, NULL) == RES_BAD_ARG);
+
+ args.type = NULL;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.type = &ssp_rng_mt19937_64;
+ args.sequence_size= 0;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.sequence_size = 32;
+ args.sequence_pitch = 0;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.sequence_pitch = 31;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.sequence_pitch = 32;
+ args.nbuckets = 0;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.nbuckets = 33;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ args.nbuckets = 1;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 4, &proxy) == RES_OK);
+ args.nbuckets = 4;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 32, &proxy) == RES_OK);
+ args.nbuckets = 5;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
- CHK(ssp_rng_proxy_create2(NULL, type, 0, 32, 32, 33, &proxy) == RES_BAD_ARG);
- CHK(ssp_rng_proxy_create2
- (&mem_default_allocator, type, 1024, 32, 32, 4, &proxy) == RES_OK);
+ args.nbuckets = 32;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
+ CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
+
+ args.sequence_offset = 1024;
+ args.nbuckets = 4;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
}
@@ -165,6 +165,7 @@ test_rng_from_proxy(void)
static void
test_rng_from_proxy2(void)
{
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
struct ssp_rng_proxy* proxy[2];
struct ssp_rng* rng[4];
const size_t block_sz = 99;
@@ -177,8 +178,11 @@ test_rng_from_proxy2(void)
CHK((r[2] = mem_alloc(sizeof(uint64_t)*nrands)) != NULL);
CHK((r[3] = mem_alloc(sizeof(uint64_t)*nrands)) != NULL);
- CHK(ssp_rng_proxy_create2
- (NULL, &ssp_rng_mt19937_64, 0, block_sz, block_sz, 4, &proxy[0]) == RES_OK);
+ args.type = &ssp_rng_mt19937_64;
+ args.sequence_size = block_sz;
+ args.sequence_pitch = block_sz;
+ args.nbuckets = 4;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy[0]) == RES_OK);
CHK(ssp_rng_proxy_create_rng(proxy[0], 0, &rng[0]) == RES_OK);
CHK(ssp_rng_proxy_create_rng(proxy[0], 1, &rng[1]) == RES_OK);
@@ -210,11 +214,16 @@ test_rng_from_proxy2(void)
CHK(ssp_rng_ref_put(rng[1]) == RES_OK);
CHK(ssp_rng_ref_put(rng[2]) == RES_OK);
CHK(ssp_rng_ref_put(rng[3]) == RES_OK);
-
- CHK(ssp_rng_proxy_create2(NULL, &ssp_rng_mt19937_64,
- 0, block_sz, 2*block_sz, 2, &proxy[0]) == RES_OK);
- CHK(ssp_rng_proxy_create2(NULL, &ssp_rng_mt19937_64,
- block_sz, block_sz, 2*block_sz, 2, &proxy[1]) == RES_OK);
+
+ args.type = &ssp_rng_mt19937_64;
+ args.sequence_size = block_sz;
+ args.sequence_pitch = 2*block_sz;
+ args.nbuckets = 2;
+
+ args.sequence_offset = 0;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy[0]) == RES_OK);
+ args.sequence_offset = block_sz;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy[1]) == RES_OK);
CHK(ssp_rng_proxy_create_rng(proxy[0], 0, &rng[0]) == RES_OK);
CHK(ssp_rng_proxy_create_rng(proxy[0], 1, &rng[1]) == RES_OK);
@@ -289,6 +298,7 @@ test_multi_proxies(void)
static void
test_proxy_from_rng(void)
{
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
struct ssp_rng_proxy* proxy;
struct ssp_rng* rng[4];
struct ssp_rng_type type;
@@ -333,6 +343,35 @@ test_proxy_from_rng(void)
FOR_EACH(k, 0, j) CHK(r[k] != r[j]);
}
}
+
+ FOR_EACH(i, 1, 4) CHK(ssp_rng_ref_put(rng[i]) == RES_OK);
+
+ args.rng = rng[0];
+ args.sequence_offset = 0;
+ args.sequence_size = 4000;
+ args.sequence_pitch = 4000;
+ args.nbuckets = 4;
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_BAD_ARG);
+ CHK(ssp_rng_ref_put(rng[0]) == RES_OK);
+
+ CHK(ssp_rng_create(NULL, &ssp_rng_threefry, &rng[0]) == RES_OK);
+ CHK(ssp_rng_discard(rng[0], 100) == RES_OK);
+
+ args.rng = rng[0];
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
+ CHK(ssp_rng_ref_put(rng[0]) == RES_OK);
+ FOR_EACH(i, 0, 4) {
+ CHK(ssp_rng_proxy_create_rng(proxy, i, &rng[i]) == RES_OK);
+ }
+
+ FOR_EACH(i, 0, NRANDS) {
+ FOR_EACH(j, 0, 4) {
+ r[j] = ssp_rng_get(rng[j]);
+ FOR_EACH(k, 0, j) CHK(r[k] != r[j]);
+ }
+ }
+
+ CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
FOR_EACH(i, 0, 4) CHK(ssp_rng_ref_put(rng[i]) == RES_OK);
}
@@ -417,6 +456,82 @@ test_write(void)
fclose(stream);
}
+static void
+test_cache(void)
+{
+ struct ssp_rng_proxy_create2_args args = SSP_RNG_PROXY_CREATE2_ARGS_NULL;
+ struct ssp_rng_proxy* proxy;
+ struct ssp_rng* rng0;
+ struct ssp_rng* rng1;
+ struct ssp_rng* rng;
+ const size_t nrands = 20000;
+ size_t i;
+
+ /* Create a proxy with a very small sequence size of 2 RNs per bucket in
+ * order to maximize the number of cached states. Furthermore, use the
+ * Mersenne Twister RNG type since its internal state is the greatest one of
+ * the proposed builtin type and is thus the one that will fill quickly the
+ * cache stream. */
+ args.type = &ssp_rng_mt19937_64;
+ args.sequence_size = 4;
+ args.sequence_pitch = 4;
+ args.nbuckets = 2;
+
+ /* Simply test that the RNs generated by the proxy are the same thant the
+ * ones generated by a regular RNG. Since each RNG invocation are
+ * interleaved, the cache pressure is very low, at most 1 RNG state is
+ * cached. */
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
+ CHK(ssp_rng_proxy_create_rng(proxy, 0, &rng0) == RES_OK);
+ CHK(ssp_rng_proxy_create_rng(proxy, 1, &rng1) == RES_OK);
+ CHK(ssp_rng_create(NULL, &ssp_rng_mt19937_64, &rng) == RES_OK);
+ FOR_EACH(i, 0, nrands*2) {
+ if((i / 2) % 2) {
+ CHK(ssp_rng_get(rng1) == ssp_rng_get(rng));
+ } else {
+ CHK(ssp_rng_get(rng0) == ssp_rng_get(rng));
+ }
+ }
+ CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
+ CHK(ssp_rng_ref_put(rng0) == RES_OK);
+ CHK(ssp_rng_ref_put(rng1) == RES_OK);
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ CHK(ssp_rng_proxy_create2(NULL, &args, &proxy) == RES_OK);
+ CHK(ssp_rng_proxy_create_rng(proxy, 0, &rng0) == RES_OK);
+ CHK(ssp_rng_proxy_create_rng(proxy, 1, &rng1) == RES_OK);
+ CHK(ssp_rng_create(NULL, &ssp_rng_mt19937_64, &rng) == RES_OK);
+
+ /* Generate several RNs for the first RNG only to make under pressure the
+ * cache stream of 'rng1'. The cache stream limit is set to 32 MB and the
+ * size of a Mersenne Twister RNG state is greater than 6 KB. Consquently,
+ * ~5500 RNG states will exceed the cache stream, i.e. 5500*2 = 11000
+ * random generations (since there is 2 RNs per bucket). Above this limit,
+ * 'rng1' will not rely anymore on the proxy RNG to manage its state.
+ *
+ * For both RNGs, ensure that the generated RNs are the expected ones by
+ * comparing them to the ones generated by a regular RNG */
+ FOR_EACH(i, 0, nrands) {
+ CHK(ssp_rng_get(rng0) == ssp_rng_get(rng));
+ if(i % 2) CHK(ssp_rng_discard(rng, 2) == RES_OK);
+ }
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ /* Check the cache mechanisme of rng1 */
+ CHK(ssp_rng_create(NULL, &ssp_rng_mt19937_64, &rng) == RES_OK);
+ CHK(ssp_rng_discard(rng, 2) == RES_OK);
+ FOR_EACH(i, 0, nrands) {
+ CHK(ssp_rng_get(rng1) == ssp_rng_get(rng));
+ if(i % 2) CHK(ssp_rng_discard(rng, 2) == RES_OK);
+ }
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ CHK(ssp_rng_proxy_ref_put(proxy) == RES_OK);
+ CHK(ssp_rng_ref_put(rng0) == RES_OK);
+ CHK(ssp_rng_ref_put(rng1) == RES_OK);
+
+}
+
int
main(int argc, char** argv)
{
@@ -429,6 +544,7 @@ main(int argc, char** argv)
test_proxy_from_rng();
test_read();
test_write();
+ test_cache();
CHK(mem_allocated_size() == 0);
return 0;
}
diff --git a/src/test_ssp_rng_proxy.h b/src/test_ssp_rng_proxy.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is a program whose purpose is to test the spp library.
*
diff --git a/src/test_ssp_utils.h b/src/test_ssp_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2015-2018 (contact@meso-star.com)
+/* Copyright (C) 2015-2020 |Meso|Star> (contact@meso-star.com)
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,