Azur/libnum/test/unit_sample.h

175 lines
5.3 KiB
C++

//---------------------------------------------------------------------------//
// ," /\ ", Azur: A game engine for CASIO fx-CG and PC //
// | _/__\_ | Designed by Lephe' and the Planète Casio community. //
// "._`\/'_." License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
// unit_sample.h: Utility for generating sample values for testing
//
// This file mainly provides the runOnSampleInputs() function which runs a
// boolean function f() on a series of generated inputs. It achieves this by
// reading a storage of pre-computed inputs for each of the arguments of f.
// This requires a storage to exist for each argument's type, which is provided
// by SampleBase and subclassed by Sample.
//
// The meat of the generation is the generateIntSample() function, which
// generates sparse sets of integers of varied size that omit needlessly
// redundant input while densely covering special cases.
//---
#pragma once
#include <vector>
#include <type_traits>
#include <functional>
#include <cassert>
#include <stdint.h>
#include <num/num.h>
using namespace libnum;
//---
// Integer sampling
//---
template<typename T, typename Uint>
void generateIntSample(std::vector<T> &v, std::function<T(Uint)> ctor,
Uint start, Uint length, int count)
{
/* When we get to a set of size 16, do all values. */
if(count <= 16) {
/* If we have an even-only range, force in some odd numbers too. */
bool switch_odd = (length >= (Uint)(2*count));
Uint step = length / count;
while(count-- > 0) {
v.push_back(ctor(start));
start += switch_odd ? step ^ (count & 1) : step;
}
return;
}
/* Otherwise, fractally divide into 16 segments and assign a portion of the
available points to each section. We divide the count of points in 16ths
so that there is at least one point in each segment. */
Uint sublength = length / 16;
int subcount = count / 16;
/* This array must add up to 16. */
int props[16] = { 4, 1, 1, 0, 0, 2, 0, 0, 1, 0, 2, 0, 0, 0, 1, 4 };
for(int i = 0; i < 16; i++) {
if(props[i])
generateIntSample(v, ctor, (Uint)(start + i * sublength),
sublength, subcount * props[i]);
}
}
//---
// Input sampling
//---
template<typename T>
struct SampleBase {
static std::vector<T> v;
};
template<typename T>
std::vector<T> SampleBase<T>::v;
template<typename T>
struct Sample {};
template<typename T>
concept has_sample = requires { Sample<T>::get; };
template<>
struct Sample<num8>: SampleBase<num8>
{
static std::vector<num8> const &get() {
if(v.size() > 0)
return v;
for(int i = 0; i <= 0xff; i++) {
num8 x;
x.v = i;
v.push_back(x);
}
return v;
}
};
template<>
struct Sample<num16>: SampleBase<num16>
{
static std::vector<num16> const &get() {
if(v.size() > 0)
return v;
auto f = [](uint16_t i) { num16 x; x.v = i; return x; };
generateIntSample<num16, uint16_t>(v, f, 0, 1 << 15, 512);
generateIntSample<num16, uint16_t>(v, f, -(1 << 15), 1 << 15, 512);
return v;
}
};
template<>
struct Sample<num32>: SampleBase<num32>
{
static std::vector<num32> const &get() {
if(v.size() > 0)
return v;
auto f = [](uint32_t i) { num32 x; x.v = i; return x; };
generateIntSample<num32, uint32_t>(v, f, 0, 1 << 15, 512);
generateIntSample<num32, uint32_t>(v, f, -(1 << 15), 1 << 15, 512);
generateIntSample<num32, uint32_t>(v, f, 0, 1ul << 31, 512);
generateIntSample<num32, uint32_t>(v, f, 1ul << 31, 1ul << 31, 512);
return v;
}
};
template<>
struct Sample<int>: SampleBase<int>
{
static std::vector<int> const &get() {
if(v.size() > 0)
return v;
auto f = [](uint32_t i) { return i; };
generateIntSample<int, uint32_t>(v, f, 0, 1 << 15, 512);
generateIntSample<int, uint32_t>(v, f, -(1 << 15), 1 << 15, 512);
generateIntSample<int, uint32_t>(v, f, 0, 1ul << 31, 512);
generateIntSample<int, uint32_t>(v, f, 1ul << 31, 1ul << 31, 512);
return v;
}
};
template<>
struct Sample<num64>: SampleBase<num64>
{
static std::vector<num64> const &get() {
if(v.size() > 0)
return v;
auto f = [](uint64_t i) { num64 x; x.v = i; return x; };
generateIntSample<num64, uint64_t>(v, f, 0, 1 << 15, 512);
generateIntSample<num64, uint64_t>(v, f, -(1 << 15), 1 << 15, 512);
generateIntSample<num64, uint64_t>(v, f, 0, 1ul << 31, 512);
generateIntSample<num64, uint64_t>(v, f, -(1ul << 31), 1ul << 31, 512);
generateIntSample<num64, uint64_t>(v, f, 0, 1ull << 63, 512);
generateIntSample<num64, uint64_t>(v, f, 1ull << 63, 1ull << 63, 512);
return v;
}
};
//---
// Automatic test functions
//---
template<typename T> requires(has_sample<T>)
void runOnSampleInputs(std::function<void(T)> f)
{
for(auto t: Sample<T>::get())
f(t);
}
template<typename T, typename U> requires(has_sample<T> && has_sample<U>)
void runOnSampleInputs(std::function<void(T, U)> f)
{
for(auto t: Sample<T>::get())
for(auto u: Sample<U>::get())
f(t, u);
}