lumiera_/tests/library/random-draw-test.cpp
Ichthyostega bdb2f12b80 Library: RandomDraw - use dynamic quantiser
For sake of simplicity, since this whole exercise is a byproduct,
the mapping calculations are done in doubles. To get even distribution
of values and a good randomisation, it is thus necessary to break
down the size_t hash value in a first step (size_t can be 64bit
and random numbers would be subject to rounding errors otherwise)

The choice of this quantiser is tricky; it must be a power of two
to guarantee even distribution, and if chosen to close to the grid
of the result values, with lower probabilities we'd fail to cover
some of the possible result values.  If chosen to large, then
of course we'd run danger of producing correlated numbers on
consecutive picks.

Attempting to use 4 bits of headroom above the log-2 of the
required value range. For example, 10-step values would use
a quantiser of 128, which looks like a good compromise.
The following tests will show how good this choice holds up.
2023-11-21 19:50:22 +01:00

243 lines
6.5 KiB
C++

/*
RandomDraw(Test) - verify the component builder for random selected values
Copyright (C) Lumiera.org
2023, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file random-draw-test.cpp
** unit test \ref RandomDraw_test
*/
#include "lib/test/run.hpp"
//#include "lib/test/test-helper.hpp"
#include "lib/random-draw.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/test/diagnostic-output.hpp"////////////////////TODO
//#include "lib/util.hpp"
//#include <cstdlib>
namespace lib {
namespace test{
// using util::isSameObject;
// using std::rand;
using lib::time::FSecs;
using lib::time::TimeVar;
// namespace error = lumiera::error;
// using error::LUMIERA_ERROR_FATAL;
// using error::LUMIERA_ERROR_STATE;
namespace {
// const Literal THE_END = "all dead and hero got the girl";
struct SymmetricFive
: function<Limited<int, 5,-5,0>(size_t)>
{
static size_t defaultSrc (size_t hash) { return hash; }
/**
* @internal helper to expose the signature `size_t(size_t)`
* by wrapping a given lambda or functor.
*/
template<class SIG>
struct Adaptor
{
static_assert (not sizeof(SIG), "Unable to adapt given functor.");
};
template<typename RES>
struct Adaptor<RES(size_t)>
{
template<typename FUN>
static decltype(auto)
build (FUN&& fun)
{
return std::forward<FUN>(fun);
}
};
template<typename RES>
struct Adaptor<RES(void)>
{
template<typename FUN>
static auto
build (FUN&& fun)
{
return [functor=std::forward<FUN>(fun)]
(size_t)
{
return functor();
};
}
};
};
}
using Draw = RandomDraw<SymmetricFive>;
/***********************************************************************************//**
* @test Verify a flexible builder for random-value generators; using a config template,
* these can be outfitted to use a suitable source of randomness and to produce
* values from a desired target type and limited range.
* - TODO
* @see result.hpp
* @see lib::ThreadJoinable usage example
*/
class RandomDraw_test
: public Test
{
void
run (Arg)
{
simpleUse();
verify_policy();
verify_numerics();
verify_buildProfile();
verify_dynamicChange();
}
/** @test TODO demonstrate a basic usage scenario
* @todo WIP 11/23 ✔ define ⟶ 🔁 implement
*/
void
simpleUse()
{
auto draw = Draw().probability(0.5);
SHOW_EXPR (int(draw(0) ));
SHOW_EXPR (int(draw(63 )));
SHOW_EXPR (int(draw(64 )));
SHOW_EXPR (int(draw(70 )));
SHOW_EXPR (int(draw(72 )));
SHOW_EXPR (int(draw(82 )));
SHOW_EXPR (int(draw(83 )));
SHOW_EXPR (int(draw(84 )));
SHOW_EXPR (int(draw(85 )));
SHOW_EXPR (int(draw(86 )));
SHOW_EXPR (int(draw(87 )));
SHOW_EXPR (int(draw(88 )));
SHOW_EXPR (int(draw(89 )));
SHOW_EXPR (int(draw(90 )));
SHOW_EXPR (int(draw(91 )));
SHOW_EXPR (int(draw(92 )));
SHOW_EXPR (int(draw(93 )));
SHOW_EXPR (int(draw(94 )));
SHOW_EXPR (int(draw(95 )));
SHOW_EXPR (int(draw(96 )));
SHOW_EXPR (int(draw(97 )));
SHOW_EXPR (int(draw(102)));
SHOW_EXPR (int(draw(103)));
SHOW_EXPR (int(draw(108)));
SHOW_EXPR (int(draw(109)));
SHOW_EXPR (int(draw(121)));
SHOW_EXPR (int(draw(122)));
SHOW_EXPR (int(draw(127)));
SHOW_EXPR (int(draw(128)));
SHOW_EXPR (int(draw(129)));
SHOW_EXPR (int(draw(192)));
SHOW_EXPR (int(draw(255)));
SHOW_EXPR (int(draw(256)));
// CHECK (draw(0) == 0);
// CHECK (draw(127) == 0);
// CHECK (draw(128) == 1);
// CHECK (draw(141) == 2);
// CHECK (draw(255) ==10);
// CHECK (draw(256) == 0);
}
/** @test TODO verify configuration through policy template
* @todo WIP 11/23 🔁 define ⟶ 🔁 implement
*/
void
verify_policy()
{
auto d1 = RandomDraw<random_draw::LimitedRandomGenerate<5>>().probability(1.0);
uint v1 = d1();
CHECK (0 < v1 and v1 <=5);
struct SpecialPolicy
: function<Limited<char, 'Z','A'>(char,uint)>
{
static double defaultSrc (char b, uint off) { return fmod ((b-'A'+off)/double('Z'-'A'), 1.0); }
};
auto d2 = RandomDraw<SpecialPolicy>().probability(1.0);
CHECK (d2('A', 2) == 'D');
CHECK (d2('M',10) == 'X');
CHECK (d2('Y', 0) == 'Z');
CHECK (d2('Y',15) == 'P');
}
/** @test TODO verify random number transformations
* @todo WIP 11/23 🔁 define ⟶ implement
*/
void
verify_numerics()
{
UNIMPLEMENTED ("verify random number transformations");
}
/** @test TODO verify the Builder-API to define the profile of result values.
* @todo WIP 11/23 🔁 define ⟶ implement
*/
void
verify_buildProfile()
{
UNIMPLEMENTED ("verify random number profile configuration");
}
/** @test TODO change the generation profile dynamically
* @todo WIP 11/23 🔁 define ⟶ implement
*/
void
verify_dynamicChange()
{
UNIMPLEMENTED ("change the generation profile dynamically");
}
};
/** Register this test class... */
LAUNCHER (RandomDraw_test, "unit common");
}} // namespace lib::test