star-sp

Random number generators and distributions
git clone git://git.meso-star.fr/star-sp.git
Log | Files | Refs | README | LICENSE

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:
MREADME.md | 24+++++++++++++++++++-----
Mcmake/CMakeLists.txt | 10+++++++---
Msrc/ssp.h | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/ssp_ran.c | 138++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/ssp_ranst_discrete.c | 2+-
Msrc/ssp_ranst_gaussian.c | 2+-
Msrc/ssp_ranst_piecewise_linear.c | 2+-
Msrc/ssp_rng.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/ssp_rng_c.h | 2+-
Msrc/ssp_rng_proxy.c | 370++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/test_ssp_ran_circle.c | 2+-
Msrc/test_ssp_ran_circle.h | 2+-
Msrc/test_ssp_ran_discrete.c | 2+-
Msrc/test_ssp_ran_discrete.h | 2+-
Msrc/test_ssp_ran_gaussian.c | 2+-
Msrc/test_ssp_ran_gaussian.h | 2+-
Msrc/test_ssp_ran_hemisphere.c | 2+-
Msrc/test_ssp_ran_hemisphere.h | 2+-
Msrc/test_ssp_ran_hg.c | 2+-
Msrc/test_ssp_ran_hg.h | 2+-
Msrc/test_ssp_ran_piecewise_linear.c | 2+-
Msrc/test_ssp_ran_piecewise_linear.h | 2+-
Msrc/test_ssp_ran_sphere.c | 2+-
Msrc/test_ssp_ran_sphere.h | 2+-
Msrc/test_ssp_ran_sphere_cap.c | 2+-
Msrc/test_ssp_ran_sphere_cap.h | 2+-
Asrc/test_ssp_ran_tetrahedron.c | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/test_ssp_ran_tetrahedron.h | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssp_ran_triangle.c | 2+-
Msrc/test_ssp_ran_triangle.h | 17+++++++++--------
Msrc/test_ssp_ran_uniform_disk.c | 2+-
Msrc/test_ssp_ran_uniform_disk.h | 2+-
Msrc/test_ssp_rng.c | 40+++++++++++++++++++++++++++++++++++++---
Msrc/test_ssp_rng.h | 2+-
Msrc/test_ssp_rng_proxy.c | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/test_ssp_rng_proxy.h | 2+-
Msrc/test_ssp_utils.h | 2+-
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,