thesis version

This commit is contained in:
2021-05-10 18:14:13 +02:00
commit b688da651b
191 changed files with 35833 additions and 0 deletions

5
tests/common.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "common.h"
std::size_t _copy{};
std::size_t _move{};

138
tests/common.h Normal file
View File

@ -0,0 +1,138 @@
#ifndef ALSK_TESTS_COMMON_H
#define ALSK_TESTS_COMMON_H
#include <numeric>
#include <initializer_list>
#include <tuple>
#include <utility>
extern std::size_t _copy;
extern std::size_t _move;
template<typename T>
struct Measure {
using value_type = T;
T v;
Measure() = default;
explicit Measure(int n): v(n) {}
Measure(T const& v): v{v} { ++_copy; }
Measure(T&& v): v{std::move(v)} { ++_move; }
Measure(Measure const& o): v{o.v} { ++_copy; }
Measure(Measure&& o): v{std::move(o.v)} { ++_move; }
Measure& operator=(T const& v) {
this->v = v;
++_copy;
return *this;
}
Measure& operator=(T&& v) {
this->v = std::move(v);
++_move;
return *this;
}
Measure& operator=(Measure const& o) {
if(this == &o) return *this;
this->v = o.v;
++_copy;
return *this;
}
Measure& operator=(Measure&& o) {
if(this == &o) return *this;
this->v = std::move(o.v);
++_move;
return *this;
}
decltype(auto) begin() const { return v.begin(); }
decltype(auto) end() const { return v.end(); }
decltype(auto) size() const { return v.size(); }
decltype(auto) operator[](std::size_t i) const { return v[i]; }
decltype(auto) operator[](std::size_t i) { return v[i]; }
};
struct Add {
template<typename T>
T operator()(T const& lhs, T const& rhs) { return lhs+rhs; }
};
struct Mult {
template<typename T>
T operator()(T const& lhs, T const& rhs) { return lhs*rhs; }
};
struct Div {
template<typename T>
T operator()(T const& lhs, T const& rhs) { return lhs/rhs; }
};
struct Sqrt {
template<typename T>
T operator()(T const& lhs) { return sqrt(lhs); }
};
template<typename T>
struct MultC {
T m;
template<typename U>
auto operator()(U const& lhs) { return lhs*m; }
};
struct Tupler {
template<typename... Ts>
auto operator()(Ts const&... values) { return std::make_tuple(values...); }
};
/* for copy/move count test */
template<typename Vector>
struct MultiplyBy {
Vector coeffs;
MultiplyBy() = default;
MultiplyBy(Vector const& coeffs): coeffs{coeffs} {}
MultiplyBy(Vector&& coeffs): coeffs{std::move(coeffs)} {}
int operator()(int v) const {
return std::accumulate(std::begin(coeffs), std::end(coeffs), v,
[](int a, int b) { return a*b; });
}
};
template<typename Vector>
struct Process {
Vector operator()(int n, int a) const {
Vector v(n);
for(int i = 0; i < n; ++i)
v[i] = a+i;
return v;
}
};
template<typename Vector>
struct ProdVect {
void operator()(int& r, Vector const& a, Vector const& b) const {
r = 0;
for(std::size_t i = 0; i < a.size(); ++i)
r += a[i] * b[i];
}
};
template<typename Vector>
struct Sum {
Vector operator()(Vector const& a, Vector const& b) const {
Vector r(a.size());
for(std::size_t i = 0; i < a.size(); ++i)
r[i] = a[i] * b[i];
return r;
}
};
#endif

75
tests/copy.cpp Normal file
View File

@ -0,0 +1,75 @@
#include <catch.hpp>
#include <alsk/alsk.h>
#include <numeric>
#include "common.h"
using namespace alsk;
namespace {
using Data = Measure<int>;
struct Opv {
template<typename... Ts>
void operator()(Ts&&...) {}
};
struct Op {
template<typename... Ts>
Data operator()(Ts&&...) { return {}; }
};
/* NOTE:
* `Data const&` causes a copy when then stored in Data
* to avoid: simply return `Data` instead or store in `Data&`
* hw uses one `auto` to enables this
*/
struct Opr {
template<typename... Ts>
Data const& operator()(Data const& a, Ts&&...) { return a; }
};
using Struct = S<Serial, Opv, Op, Opr, Op>;
using Links =
L<Serial, arg::R<3>(Data const&),
void(arg::P<0>),
Data(arg::P<0>),
Data(arg::R<1> const&),
Data(arg::P<0>, arg::R<1> const&, arg::R<2> const&)
>;
using Test = BuildSkeletonT<Struct, Links>;
void hw(Data const& p0) {
Opv{}(p0);
decltype(auto) r1 = Op{}(p0);
auto r2 = Opr{}(r1);
decltype(auto) r3 = Op{}(p0, r1, r2);
static_cast<void>(r3);
}
}
TEST_CASE("Copy") {
std::size_t bCopy, bMove, sCopy, sMove;
_copy = _move = 0;
hw({});
bCopy = _copy;
bMove = _move;
auto test = implement<exec::Sequential, Test>();
_copy = _move = 0;
test({});
sCopy = _copy;
sMove = _move;
CAPTURE(bCopy);
CAPTURE(sCopy);
REQUIRE(bCopy >= sCopy);
REQUIRE(bMove <= sMove);
}

127
tests/edsl/edsl.cpp Normal file
View File

@ -0,0 +1,127 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::arg;
using namespace alsk::edsl;
static int counterDo, counterThen, counterDone;
struct Do { void operator()() { ++counterDo; } };
struct Then { void operator()() { ++counterThen; } };
struct Done { void operator()() { ++counterDone; } };
struct Produce0 { int operator()(int x) { return x+1; } };
struct Produce1 { int operator()(int x) { return x-2; } };
struct Consume { int operator()(int x, int y) { return x*y; } };
// TODO? avoid copies so it is truly stateful
struct Generate { int value; int operator()(int b) { return ++value+b; } };
struct Transform0 { int operator()(int x) { return x+1; } };
struct Transform1 { int operator()(int x) { return x-2; } };
struct Produce { int operator()(int x, int y) { return x*y; } };
struct Select {
int mod;
int operator()(int a, int b) { if(a%mod == b%mod) return std::min(a, b); return (a%mod > b%mod)? a : b; }
};
TEST_CASE("edsl") {
auto aDo = makeOperand<Do>();
auto aThen = makeOperand<Then>();
auto aDone = makeOperand<Done>();
auto aProduce0 = makeOperand<Produce0>();
auto aProduce1 = makeOperand<Produce1>();
auto aConsume = makeOperand<Consume>();
auto e0 = aDo & (5*aThen) & aDone;
using E0 = decltype(e0);
using S0 = alsk::S<alsk::Serial, Do, alsk::S<alsk::Farm, Then>, Done>;
using L0 = alsk::L<alsk::Serial, void(), void(), alsk::L<alsk::Farm, void(), void()>, void()>;
auto e1 = 5*(aDo, 2*aThen, aDone);
using E1 = decltype(e1);
using S1 = alsk::S<alsk::Farm, alsk::S<alsk::Serial, Do, alsk::S<alsk::Farm, Then>, Done>>;
using L1 = alsk::L<alsk::Farm, void(), alsk::L<alsk::Serial, void(), void(), alsk::L<alsk::Farm, void(), void()>, void()>>;
auto e2 = link<R<2>(int)>(
link<int(P<0>)>(aProduce0),
link<int(P<0>)>(aProduce1),
link<int(R<1>, R<0>)>(aConsume)
);
SECTION("Construction") {
REQUIRE(std::is_same<E0::Signature, void()>{});
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E1::Signature, void()>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E1::Links, L1>{});
}
SECTION("Linking") {
{
auto f2 = implement<alsk::exec::Sequential>(e2);
REQUIRE(f2(0) == -2);
REQUIRE(f2(2) == 0);
REQUIRE(f2(5) == 18);
}
}
SECTION("Setup") {
auto f0 = implement<alsk::exec::Sequential>(e0);
REQUIRE(f0.skeleton.task<1>().n == 5);
}
SECTION("Check run") {
{
auto f0 = implement<alsk::exec::Sequential>(e0);
counterDo = counterThen = counterDone = 0;
f0();
REQUIRE(counterDo == 1);
REQUIRE(counterThen == 5);
REQUIRE(counterDone == 1);
}
{
auto f1 = implement<alsk::exec::Sequential>(e1);
counterDo = counterThen = counterDone = 0;
f1();
REQUIRE(counterDo == 5);
REQUIRE(counterThen == 10);
REQUIRE(counterDone == 5);
}
}
SECTION("Complex case") {
auto generate = makeOperand<Generate>();
auto transform0 = makeOperand<Transform0>();
auto transform1 = makeOperand<Transform1>();
auto produce = makeOperand<Produce>();
auto select = makeOperand<int(int, int), Select>();
auto innerTask = link<R<3>(int)>(
link<int(P<0>)>(generate),
link<int(R<0>)>(transform0),
link<int(R<0>)>(transform1),
link<int(R<2>, R<1>)>(produce)
);
auto task = link<int(int)>(10 * link<R<3>(P<0>)>(innerTask)) ->* select;
auto f = implement<alsk::exec::Sequential>(task);
f.skeleton.select.mod = 5;
REQUIRE(f(4) == 18);
REQUIRE(f(5) == 28);
REQUIRE(f(6) == 40);
REQUIRE(f(7) == 54);
REQUIRE(f(8) == 70);
}
}

62
tests/edsl/op/farm.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Do { void operator()() { std::puts("Do"); } };
TEST_CASE("edsl::Farm") {
auto aDo = makeOperand<Do>();
auto e0 = *aDo;
using E0 = decltype(e0);
using S0 = alsk::S<alsk::Farm, Do>;
using L0 = alsk::L<alsk::Farm, void(), void()>;
auto e1 = 5*aDo;
using E1 = decltype(e1);
using S1 = alsk::S<alsk::Farm, Do>;
using L1 = alsk::L<alsk::Farm, void(), void()>;
auto e2 = aDo*3;
using E2 = decltype(e2);
using S2 = alsk::S<alsk::Farm, Do>;
using L2 = alsk::L<alsk::Farm, void(), void()>;
auto e3 = farm(aDo, 3);
using E3 = decltype(e3);
using S3 = alsk::S<alsk::Farm, Do>;
using L3 = alsk::L<alsk::Farm, void(), void()>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E0::Signature, void()>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E1::Links, L1>{});
REQUIRE(std::is_same<E1::Signature, void()>{});
REQUIRE(std::is_same<E2::Struct, S2>{});
REQUIRE(std::is_same<E2::Links, L2>{});
REQUIRE(std::is_same<E2::Signature, void()>{});
REQUIRE(std::is_same<E3::Struct, S3>{});
REQUIRE(std::is_same<E3::Links, L3>{});
REQUIRE(std::is_same<E3::Signature, void()>{});
}
SECTION("Setup") {
auto f0 = implement<alsk::exec::Sequential>(e0);
REQUIRE(f0.skeleton.n == 0);
auto f1 = implement<alsk::exec::Sequential>(e1);
REQUIRE(f1.skeleton.n == 5);
auto f2 = implement<alsk::exec::Sequential>(e2);
REQUIRE(f2.skeleton.n == 3);
auto f3 = implement<alsk::exec::Sequential>(e3);
REQUIRE(f3.skeleton.n == 3);
}
}

52
tests/edsl/op/farmsel.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Generate { int operator()() { return 0; } };
struct Select { int operator()(int a, int b) { return std::max(a, b); } };
TEST_CASE("edsl::FarmSel") {
auto generate = makeOperand<int(), Generate>();
auto select = makeOperand<int(int, int), Select>();
auto e0 = (10 * generate) ->* select;
using E0 = decltype(e0);
using S0 = alsk::S<alsk::FarmSel, Generate, Select>;
using L0 = alsk::L<alsk::FarmSel, int(), int(), int(int, int)>;
auto e1 = link<void(int)>(*generate) ->* select;
using E1 = decltype(e1);
using S1 = alsk::S<alsk::FarmSel, Generate, Select>;
using L1 = alsk::L<alsk::FarmSel, int(int), int(), int(int, int)>;
auto e2 = farm(generate).select(select);
using E2 = decltype(e2);
using S2 = alsk::S<alsk::FarmSel, Generate, Select>;
using L2 = alsk::L<alsk::FarmSel, int(), int(), int(int, int)>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E0::Signature, int()>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E1::Links, L1>{});
REQUIRE(std::is_same<E1::Signature, int(int)>{});
REQUIRE(std::is_same<E2::Struct, S2>{});
REQUIRE(std::is_same<E2::Links, L2>{});
REQUIRE(std::is_same<E2::Signature, int()>{});
}
SECTION("Setup") {
auto f0 = implement<alsk::exec::DynamicPool>(e0);
REQUIRE(f0.skeleton.n == 10);
auto f1 = implement<alsk::exec::DynamicPool>(e1);
REQUIRE(f1.skeleton.n == 0);
auto f2 = implement<alsk::exec::DynamicPool>(e2);
REQUIRE(f2.skeleton.n == 0);
}
}

52
tests/edsl/op/itersel.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Generate { int operator()() { return 0; } };
struct Select { int operator()(int a, int b) { return std::max(a, b); } };
TEST_CASE("edsl::IterSel") {
auto generate = makeOperand<int(), Generate>();
auto select = makeOperand<int(int, int), Select>();
auto e0 = iter(generate, 10).select(select);
using E0 = decltype(e0);
using S0 = alsk::S<alsk::IterSel, Generate, Select>;
using L0 = alsk::L<alsk::IterSel, int(), int(), int(int, int)>;
auto e1 = &(link<void(int)>(*generate) ->* select);
using E1 = decltype(e1);
using S1 = alsk::S<alsk::IterSel, Generate, Select>;
using L1 = alsk::L<alsk::IterSel, int(int), int(), int(int, int)>;
auto e2 = seq(link<void(int)>(*generate) ->* select);
using E2 = decltype(e2);
using S2 = alsk::S<alsk::IterSel, Generate, Select>;
using L2 = alsk::L<alsk::IterSel, int(int), int(), int(int, int)>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E0::Signature, int()>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E1::Links, L1>{});
REQUIRE(std::is_same<E1::Signature, int(int)>{});
REQUIRE(std::is_same<E2::Struct, S2>{});
REQUIRE(std::is_same<E2::Links, L2>{});
REQUIRE(std::is_same<E2::Signature, int(int)>{});
}
SECTION("Setup") {
auto f0 = implement<alsk::exec::DynamicPool>(e0);
REQUIRE(f0.skeleton.n == 10);
auto f1 = implement<alsk::exec::DynamicPool>(e1);
REQUIRE(f1.skeleton.n == 0);
auto f2 = implement<alsk::exec::DynamicPool>(e2);
REQUIRE(f2.skeleton.n == 0);
}
}

50
tests/edsl/op/loop.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Do { void operator()() { std::puts("Do"); } };
TEST_CASE("edsl::Loop") {
auto aDo = makeOperand<Do>();
auto e0 = loop(aDo);
using E0 = decltype(e0);
using S0 = alsk::S<alsk::Loop, Do>;
using L0 = alsk::L<alsk::Loop, void(), void()>;
auto e1 = seq(5*aDo);
using E1 = decltype(e1);
using S1 = alsk::S<alsk::Loop, Do>;
using L1 = alsk::L<alsk::Loop, void(), void()>;
auto e2 = &(aDo*3);
using E2 = decltype(e2);
using S2 = alsk::S<alsk::Loop, Do>;
using L2 = alsk::L<alsk::Loop, void(), void()>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E0::Signature, void()>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E1::Links, L1>{});
REQUIRE(std::is_same<E1::Signature, void()>{});
REQUIRE(std::is_same<E2::Struct, S2>{});
REQUIRE(std::is_same<E2::Links, L2>{});
REQUIRE(std::is_same<E2::Signature, void()>{});
}
SECTION("Setup") {
auto f0 = implement<alsk::exec::Sequential>(e0);
REQUIRE(f0.skeleton.n == 0);
auto f1 = implement<alsk::exec::Sequential>(e1);
REQUIRE(f1.skeleton.n == 5);
auto f2 = implement<alsk::exec::Sequential>(e2);
REQUIRE(f2.skeleton.n == 3);
}
}

46
tests/edsl/op/operand.cpp Normal file
View File

@ -0,0 +1,46 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct A { void operator()() {} };
struct B { int operator()(float) { return {}; } };
void f() {}
TEST_CASE("edsl::Operand") {
SECTION("from function-object") {
{
auto x = makeOperand<A>();
using X = decltype(x);
REQUIRE(std::is_same<X::Struct, A>{});
REQUIRE(std::is_same<X::Links, void()>{});
}
{
auto x = makeOperand<int(alsk::arg::P<0>), B>();
using X = decltype(x);
REQUIRE(std::is_same<X::Struct, B>{});
REQUIRE(std::is_same<X::Links, int(alsk::arg::P<0>)>{});
}
}
SECTION("from function") {
{
auto x = alskMakeOperand(f);
using X = decltype(x);
REQUIRE(std::is_same<X::Struct, Fn<void(&)(), f>>{});
REQUIRE(std::is_same<X::Links, void()>{});
}
{
using Signature = int const&(&)(int const&, int const&);
auto x = makeOperand<Signature, std::min<int>>();
using X = decltype(x);
REQUIRE(std::is_same<X::Struct, Fn<Signature, std::min<int>>>{});
REQUIRE(std::is_same<X::Links, int const&(int const&, int const&)>{});
}
}
}

37
tests/edsl/op/serial.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Do { void operator()() { std::puts("Do"); } };
struct Then { void operator()() { std::puts("Then"); } };
struct Done { void operator()() { std::puts("Done"); } };
TEST_CASE("edsl::Serial") {
auto aDo = makeOperand<Do>();
auto aThen = makeOperand<Then>();
auto aDone = makeOperand<Done>();
using E0 = decltype(aDo & aThen);
using E1 = decltype(aDo, aThen, aDone);
using S0 = alsk::S<alsk::Serial, Do, Then>;
using S1 = alsk::S<alsk::Serial, Do, Then, Done>;
using L0 = alsk::L<alsk::Serial, void(), void(), void()>;
using L1 = alsk::L<alsk::Serial, void(), void(), void(), void()>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Signature, void()>{});
REQUIRE(std::is_same<E1::Signature, void()>{});
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E1::Struct, S1>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E1::Links, L1>{});
}
SECTION("Setup") {
}
}

23
tests/edsl/op/while.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <catch.hpp>
#include <alsk/alsk.h>
using namespace alsk::edsl;
struct Pred { int n; bool operator()() { return --n; } };
struct Do { void operator()() { std::puts("Do"); } };
TEST_CASE("edsl::While") {
auto aPred = makeOperand<bool(), Pred>();
auto aDo = makeOperand<Do>();
auto e0 = while_(aPred).do_(aDo);
using E0 = decltype(e0);
using S0 = alsk::S<alsk::While, Pred, Do>;
using L0 = alsk::L<alsk::While, void(), bool(), void()>;
SECTION("Construction") {
REQUIRE(std::is_same<E0::Struct, S0>{});
REQUIRE(std::is_same<E0::Links, L0>{});
REQUIRE(std::is_same<E0::Signature, void()>{});
}
}

43
tests/executor/base.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <catch.hpp>
#include <alsk/alsk.h>
TEST_CASE("ExecutorBase") {
alsk::exec::ExecutorBase e;
SECTION("Repeatability") {
auto& r = e.repeatability;
auto const& cl = r.coresList;
SECTION("upTo") {
SECTION("disabled") {
r.upTo(0);
REQUIRE(cl.empty());
r.upTo(1);
REQUIRE(cl.empty());
}
SECTION("enabled") {
for(std::size_t k = 10; k < 20; ++k) {
r.upTo(k);
REQUIRE(cl.size() == k-1);
for(std::size_t i = 0; i < cl.size(); ++i)
REQUIRE(cl[i] == 2+i);
for(std::size_t m = 3; m < 10; ++m) {
r.upTo(k, m);
REQUIRE(cl.size() == k-m+1);
for(std::size_t i = 0; i < cl.size(); ++i)
REQUIRE(cl[i] == m+i);
for(std::size_t s = 2; s < 5; ++s) {
r.upTo(k, m, s);
REQUIRE(cl.size() == (k-m+1+s-1)/s);
for(std::size_t i = 0; i < cl.size(); ++i)
REQUIRE(cl[i] == m+i*s);
}
}
}
}
}
}
}

161
tests/executor/common.cpp Normal file
View File

@ -0,0 +1,161 @@
#include <catch.hpp>
#include <tmp/debug.h>
#include "common.h"
std::atomic_ullong CallCounterV::count{};
std::atomic_ullong CallCounterII::count{};
namespace expar {
auto buildTestFunction() {
auto callCounterv = alsk::edsl::makeOperand<int(), CallCounterV>();
auto callCounterii = alsk::edsl::makeOperand<int(int, int), CallCounterII>();
auto loop = seq(3*callCounterv);
auto serial = link<R<0>()>(callCounterv, loop);
auto itersel = (callCounterv, &link<int(R<0>)>(5*link<R<0>(int)>(serial)) ->* callCounterii);
auto farmsel = link<int()>(7*link<R<1>()>(itersel, callCounterv)) ->* callCounterii;
auto farm = 11*farmsel;
return farm;
}
constexpr auto countV() { return 11 * 7 * (1 + 5 * (1 + 3) + 1); }
constexpr auto countII() { return 11 * ((7 - 1) + 7 * 5); }
constexpr auto maxCores = 11*7 * 64;
constexpr std::array<decltype(maxCores), 8> coresList{1, 2, 3, 7, 11*7-1, 11*7, 11*7+1, maxCores};
}
namespace exacc {
auto buildTestFunction() {
auto callCounterv = alsk::edsl::makeOperand<int(), CallCounterV>();
auto callCounterii = alsk::edsl::makeOperand<int(int, int), CallCounterII>();
auto loop = seq(3*callCounterv);
auto serial = link<R<0>()>(callCounterv, loop);
auto itersel = (callCounterv, &link<int(R<0>)>(5*link<R<0>(int)>(serial)) ->* callCounterii);
auto farm = link<R<1>()>(7*itersel, callCounterv);
auto farmsel = link<int()>(11*farm) ->* callCounterii;
return farmsel;
}
constexpr auto countV() { return 11 * (7 * (1 + 5 * (1 + 3)) + 1); }
constexpr auto countII() { return (11-1) + 11 * 7 * 5; }
constexpr auto maxCores = 11*7 * 64;
constexpr std::array<decltype(maxCores), 8> coresList{1, 2, 3, 7, 11*7-1, 11*7, 11*7+1, maxCores};
}
namespace repeatability {
using PRNG = std::mt19937;
int task(PRNG& rng, std::size_t) {
std::uniform_int_distribution<int> dist(-100, 100);
int a = dist(rng);
std::this_thread::sleep_for(std::chrono::milliseconds(0));
int b = dist(rng);
return a - b;
}
int sel(int a, int b) { return a + b; }
auto buildTestFunction() {
auto etask = alsk::edsl::makeOperand<int(RNG, CtxId), FN(task)>();
auto esel = alsk::edsl::makeOperand<int(int, int), FN(sel)>();
auto loop = seq(3*etask);
auto serial = link<R<0>()>(etask, (2*loop));
auto farmsel = link<int()>(5*serial) ->* esel;
return link<R<1>()>(farmsel, farmsel);
}
}
using namespace alsk;
using Executors = std::tuple<
exec::DynamicPool<void>,
exec::FirstLevelEqui<void>,
exec::FirstLevelGreedy<void>,
exec::FirstLevelNoOpti<void>,
exec::Sequential<void>,
exec::StaticPool<void>,
exec::StaticPoolId<void>,
exec::StaticThread<void>
>;
TEMPLATE_LIST_TEST_CASE("Executors", "[executors]", Executors) {
CAPTURE(tmp::typeName<TestType>());
SECTION("executeParallel") {
using namespace expar;
auto e = buildTestFunction();
for(auto cores: coresList) {
auto f = edsl::implement<GetExecutor<TestType>::template Template>(e);
f.executor.cores = cores;
CallCounterV::count = 0;
CallCounterII::count = 0;
f();
REQUIRE(CallCounterV::count == countV());
REQUIRE(CallCounterII::count == countII());
}
}
SECTION("executeParallelAccumulate") {
using namespace exacc;
auto e = buildTestFunction();
for(auto cores: coresList) {
auto f = edsl::implement<GetExecutor<TestType>::template Template>(e);
f.executor.cores = cores;
CallCounterV::count = 0;
CallCounterII::count = 0;
f();
REQUIRE(CallCounterV::count == countV());
REQUIRE(CallCounterII::count == countII());
}
}
}
TEMPLATE_LIST_TEST_CASE("Repeatability", "[executors][repeatability]", Executors) {
using namespace repeatability;
CAPTURE(tmp::typeName<TestType>());
auto e = buildTestFunction();
for(std::size_t upTo: {2, 3, 4, 5, 6, 7, 8}) {
CAPTURE(upTo);
auto f = edsl::implement<GetExecutor<TestType>::template Template>(e);
f.executor.repeatability.upTo(upTo);
f.executor.cores = 1;
auto expected = f();
f.state.context.reset();
for(std::size_t cores = 2; cores <= upTo; ++cores) {
CAPTURE(cores);
f.executor.cores = cores;
REQUIRE(f() == expected);
f.state.context.reset();
}
}
}

21
tests/executor/common.h Normal file
View File

@ -0,0 +1,21 @@
#include <alsk/alsk.h>
using namespace alsk::arg;
using alsk::edsl::link;
template<typename> struct GetExecutor;
template<template<typename> class TT, typename Arg>
struct GetExecutor<TT<Arg>> {
template<typename T>
using Template = TT<T>;
};
struct CallCounterV {
static std::atomic_ullong count;
int operator()() { ++count; return {}; }
};
struct CallCounterII {
static std::atomic_ullong count;
int operator()(int, int) { ++count; return {}; }
};

View File

@ -0,0 +1,5 @@
#include <catch.hpp>
#include "common.h"
TEST_CASE("DynamicPool") {
}

View File

@ -0,0 +1,5 @@
#include <catch.hpp>
#include "../common.h"
TEST_CASE("FirstLevelEqui") {
}

View File

@ -0,0 +1,5 @@
#include <catch.hpp>
#include "../common.h"
TEST_CASE("FirstLevelGreedy") {
}

View File

@ -0,0 +1,5 @@
#include <catch.hpp>
#include "../common.h"
TEST_CASE("FirstLevelNoOpti") {
}

View File

@ -0,0 +1,5 @@
#include <catch.hpp>
#include "common.h"
TEST_CASE("Sequential") {
}

106
tests/farmsel.cpp Normal file
View File

@ -0,0 +1,106 @@
#include <catch.hpp>
#include <atomic>
#include <alsk/alsk.h>
#include <random>
#include "common.h"
using namespace alsk;
TEST_CASE("FarmSel") {
constexpr std::size_t n = 1024;
SECTION("basic test") {
using Data = int;
using Task = std::function<Data()>;
using Accu = std::function<Data(Data const&, Data const&)>;
std::atomic_size_t countTask, countAccu;
Task task = [&]{
++countTask;
return 42;
};
Accu accu = [&](Data const& a, Data const& b) {
++countAccu;
return std::min(a, b);
};
using Skel = decltype(getSkeleton((*edsl::makeOperand<Data(), Task>()) ->* edsl::makeOperand<Data(Data, Data), Accu>()));
auto f = implement<exec::FirstLevelEqui, Skel>();
f.skeleton.n = n;
f.skeleton.task = task;
f.skeleton.select = accu;
countTask = countAccu = 0;
SECTION("cores = 1") {
f.executor.cores = 1;
Data d = f();
REQUIRE(d == 42);
REQUIRE(countTask == f.skeleton.n);
REQUIRE(countAccu == f.skeleton.n-1);
}
SECTION("cores = 2") {
f.executor.cores = 2;
Data d = f();
REQUIRE(d == 42);
REQUIRE(countTask == f.skeleton.n);
REQUIRE(countAccu == f.skeleton.n-1);
}
}
SECTION("repeatability") {
using Data = std::tuple<int, std::string>;
using RNG = std::mt19937;
using Task = std::function<Data(RNG&)>;
using Accu = std::function<Data(Data const&, Data const&)>;
std::atomic_size_t countTask, countAccu;
Task task = [&](RNG& rng) {
std::uniform_int_distribution<int> dist{0, 32};
std::uniform_int_distribution<int> text{0, n*n};
++countTask;
return Data{dist(rng), std::to_string(text(rng))};
};
Accu accu = [&](Data const& a, Data const& b) {
++countAccu;
return std::get<0>(a) < std::get<0>(b)? a:b;
};
using Struct = S<FarmSel, Task, Accu>;
using Links = L<FarmSel, Data(), Data(arg::RNG), Data(Data, Data)>;
using Skel = BuildSkeletonT<Struct, Links>;
auto f = implement<exec::FirstLevelEqui, Skel>();
f.skeleton.n = n;
f.skeleton.task = task;
f.skeleton.select = accu;
f.executor.cores = 1;
Data expected = f();
countTask = countAccu = 0;
for(std::size_t k = 1; k <= 2; k *= 2) {
SECTION("cores = "+std::to_string(k)) {
f.executor.cores = k;
f.state.context.reset();
Data d = f();
REQUIRE(std::get<0>(d) == std::get<0>(expected));
REQUIRE(std::get<1>(d) == std::get<1>(expected));
REQUIRE(countTask == f.skeleton.n);
REQUIRE(countAccu == f.skeleton.n-1);
}
}
}
}

2
tests/main.cpp Normal file
View File

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>

152
tests/serial.cpp Normal file
View File

@ -0,0 +1,152 @@
#include <catch.hpp>
#include <alsk/alsk.h>
#include "common.h"
using namespace alsk;
namespace {
using Vector = Measure<std::vector<int>>;
using Struct =
S<Serial,
S<Serial, Mult, Mult, MultC<double>, Add>,
MultC<double>,
Sqrt,
S<Serial, Add, Div>,
S<Serial, MultC<double>, Add, Div>,
Tupler
>;
using Links =
L<Serial, arg::R<5>(double, double, double),
L<Serial, arg::R<3>(arg::P<0>, arg::P<1>, arg::P<2>), // a, b, c
double(arg::P<1>, arg::P<1>), // b*b
double(arg::P<0>, arg::P<2>), // a*c
double(arg::R<1>), // a*c*#
double(arg::R<0>, arg::R<2>) // b*b + a*c*# = D
>,
double(arg::P<0>), // a*#
double(arg::R<0>), // sqrt(D)
L<Serial, arg::R<1>(arg::P<1>, arg::R<2>, arg::R<1>), // b, sqrt(D), a*#
double(arg::P<0>, arg::P<1>), // +
double(arg::R<0>, arg::P<2>) // ^/(a*#)
>,
L<Serial, arg::R<2>(arg::P<1>, arg::R<2>, arg::R<1>), // b, sqrt(D), a*#
double(arg::P<1>), // sqrt(D)*#
double(arg::R<0>, arg::P<0>), // ^+b
double(arg::R<1>, arg::P<2>) // ^/(a*#)
>,
std::tuple<double, double>(arg::R<3>, arg::R<4>)
>;
using Solver = BuildSkeletonT<Struct, Links>;
constexpr std::initializer_list<int> benchVec1{5, 2, 3, 7, 8, 9, 1, 2, 7, 6, 3, 2, 7};
constexpr std::initializer_list<int> benchVec2{2, 2, 3, 5, 2, 1, 1, 4, 7, 9, 2, 3, 5, 6, 1};
using MultiplyByV = MultiplyBy<Vector>;
using ProcessV = Process<Vector>;
using SumV = Sum<Vector>;
using ProdVectV = ProdVect<Vector>;
using CMTestStruct = S<Serial, MultiplyByV, MultiplyByV, ProcessV, ProcessV, SumV, ProdVectV>;
using CMTestLinks =
L<Serial, arg::R<4>(int, int, int, int&),
int(arg::P<0>), int(arg::P<1>),
Vector(arg::P<2>, arg::R<0>), Vector(arg::P<2>, arg::R<1>),
Vector(arg::R<2> const&, arg::R<3> const&),
void(arg::P<3>, arg::R<2> const&, arg::R<3> const&)
>;
using CMTest = BuildSkeletonT<CMTestStruct, CMTestLinks>;
decltype(auto) cmTest(int a, int b, int n, int& r) {
decltype(auto) r0 = MultiplyByV{Vector::value_type{benchVec1}}(a);
decltype(auto) r1 = MultiplyByV{Vector::value_type{benchVec2}}(b);
decltype(auto) r2 = ProcessV{}(n, r0);
decltype(auto) r3 = ProcessV{}(n, r1);
decltype(auto) r4 = SumV{}(r2, r3);
ProdVectV{}(r, r2, r3);
return r4;
}
}
TEST_CASE("Serial") {
SECTION("basic test") {
using F0 = std::function<int()>;
using FN = std::function<int(int)>;
using FX = std::function<int(int, int)>;
std::size_t count;
F0 f0 = [&]{ ++count; return 0; };
FN f1 = [&](int v) { ++count; return v+1; };
FN f2 = [&](int v) { ++count; return v*2; };
FX f3 = [&](int v, int x) { ++count; return v<<x; };
using Struct = S<Serial, F0, S<Serial, FN, FN>, FX>;
using Links = L<Serial, arg::R<2>(int), int(), L<Serial, arg::R<1>(arg::R<0>), int(arg::P<0>), int(arg::R<0>)>, int(arg::R<1>, arg::P<0>)>;
using Skel = BuildSkeletonT<Struct, Links>;
auto f = implement<exec::FirstLevelEqui, Skel>();
f.skeleton.task<0>() = f0;
f.skeleton.task<1>().task<0>() = f1;
f.skeleton.task<1>().task<1>() = f2;
f.skeleton.task<2>() = f3;
count = 0;
REQUIRE(f(3) == 16);
REQUIRE(count == 4);
}
SECTION("serial") {
auto solver = implement<exec::StaticPool, Solver>();
solver.skeleton.task<0>().task<2>().m = -4;
solver.skeleton.task<1>().m = -2;
solver.skeleton.task<4>().task<0>().m = -1;
double x0 = -2.63;
double x1 = 3.77;
double a = 3.7;
double b = -a*(x0+x1);
double c = a*x0*x1;
auto sol = solver(a, b, c);
REQUIRE(std::get<0>(sol) == Approx{x0});
REQUIRE(std::get<1>(sol) == Approx{x1});
}
SECTION("copy/moves") {
int a{500}, b{100}, n{1'000}, r;
int cpBase, mvBase, cpSkel, mvSkel;
_copy = _move = 0;
cmTest(a, b, n, r);
cpBase = _copy;
mvBase = _move;
std::printf("[base] copy: %zu, move: %zu\n", _copy, _move);
_copy = _move = 0;
auto cmTestSkel = implement<exec::Sequential, CMTest>();
// cmTestSkel(a, b);
cmTestSkel.skeleton.task<0>() = MultiplyByV{Vector::value_type{benchVec1}};
cmTestSkel.skeleton.task<1>() = MultiplyByV{Vector::value_type{benchVec2}};
cmTestSkel(a, b, n, r);
cpSkel = _copy;
mvSkel = _move;
std::printf("[skel] copy: %zu, move: %zu\n", _copy, _move);
REQUIRE(cpBase >= cpSkel);
REQUIRE(mvBase <= mvSkel);
}
}

264
tests/skeleton.cpp Normal file
View File

@ -0,0 +1,264 @@
#include <catch.hpp>
#include <alsk/skeleton/skeleton.h>
using namespace alsk;
using tmp::Pack;
struct Int {
int v;
Int(int v): v{v} {}
operator int() const { return v; }
};
struct A: Int {};
struct B: Int {};
struct C: Int {};
struct D: Int {};
struct E: Int {};
struct F: Int {};
struct G: Int {};
struct H: Int {};
struct I: Int {};
template<typename R>
struct Unary {
template<typename T>
R operator()(T a) { return{a+1}; }
};
template<typename R>
struct Binary {
template<typename T, typename U>
R operator()(T a, U b) { return{a+b}; }
};
template<>
struct Binary<void> {
template<typename T, typename U>
void operator()(T, U) {}
};
using S_ =
S<Serial,
S<Serial, Unary<D>, Binary<E>>,
Unary<F>,
S<Serial,
Unary<G>,
S<Serial, Unary<H>, Binary<void>>,
Unary<I>
>
>;
using L1 =
L<Serial, arg::R<1>(arg::P<1>, arg::P<2>),
D(arg::P<1>),
E(arg::R<0>, arg::P<0>)
>;
using L2 = F(arg::P<2>);
using L31 = G(arg::P<0>, arg::P<2>);
using L32 =
L<Serial, arg::R<0>(arg::R<0>, arg::P<1>),
H(arg::P<0>),
void(arg::P<0>, arg::P<1>)
>;
using L33 = I(arg::R<0>);
using L3 =
L<Serial, arg::R<2>(arg::R<1>, arg::P<0>, arg::R<0>),
L31, L32, L33
>;
using L_ =
L<Serial, arg::R<2>(A, B, C),
L1, L2, L3
>;
TEST_CASE("LinksToReturnTypes") {
REQUIRE(std::is_same<Pack<E>, impl::LinksToReturnTypes<L1>::type>{});
REQUIRE(std::is_same<Pack<F>, impl::LinksToReturnTypes<L2>::type>{});
REQUIRE(std::is_same<Pack<G>, impl::LinksToReturnTypes<L31>::type>{});
REQUIRE(std::is_same<Pack<H>, impl::LinksToReturnTypes<L32>::type>{});
REQUIRE(std::is_same<Pack<I>, impl::LinksToReturnTypes<L33>::type>{});
REQUIRE(std::is_same<Pack<I>, impl::LinksToReturnTypes<L3>::type>{});
REQUIRE(std::is_same<Pack<I>, impl::LinksToReturnTypes<L_>::type>{});
REQUIRE(std::is_same<Pack<E, F, I>, impl::LinksToReturnTypes<L1, L2, L3>::type>{});
}
using T_ =
Serial<arg::R<2>(A, B, C),
// 1
Fun<Serial<arg::R<1>(B, C),
Fun<Unary<D>, D(arg::P<1>)>,
Fun<Binary<E>, E(arg::R<0>, arg::P<0>)>
>, E(arg::P<1>, arg::P<2>)>,
// 2
Fun<Unary<F>, F(arg::P<2>)>,
// 3
Fun<Serial<arg::R<2>(F, A, E),
Fun<Unary<G>, G(arg::P<0>, arg::P<2>)>,
Fun<Serial<arg::R<0>(G, A),
Fun<Unary<H>, H(arg::P<0>)>,
Fun<Binary<void>, void(arg::P<0>, arg::P<1>)>
>, H(arg::R<0>, arg::P<1>)>,
Fun<Unary<I>, I(arg::R<0>)>
>, I(arg::R<1>, arg::P<0>, arg::R<0>)>
>;
template<typename...>
using TS_ = S_;
template<typename...>
using TL_ = L_;
TEST_CASE("BuildSkeleton") {
using T1 = BuildSkeleton<TS_, TL_>::skeleton<Pack<>, Pack<>>;
using T2 = BuildSkeletonT<S_, L_>;
REQUIRE(std::is_same<T_, T1>{});
REQUIRE(std::is_same<T_, T2>{});
}
TEST_CASE("TreeFromSkeleton") {
SECTION("simple") {
struct F {};
using Struct = S<Serial, F>;
using Links = L<Serial, void(), void()>;
using Skel = BuildSkeletonT<Struct, Links>;
using Expected =
tmp::Tree<
Branch<Serial, void ()>,
tmp::Tree<Leaf<F, void ()>>
>;
using Result = TreeFromSkeleton<Skel>;
REQUIRE(std::is_same<Expected, Result>{});
}
SECTION("with links") {
struct F {}; struct G {};
using Struct = S<Serial, S<Serial, F, G>>;
using Links = L<Serial, arg::R<0>(int&, int), L<Serial, arg::R<1>(arg::P<1>, arg::P<0>), long(arg::P<1>, arg::P<0>), int(arg::R<0>)>>;
using Skel = BuildSkeletonT<Struct, Links>;
using Expected =
tmp::Tree<
Branch<Serial, arg::R<0>(int&, int)>,
tmp::Tree<
Branch<Serial, arg::R<1>(int, int&), int(arg::P<1>, arg::P<0>)>,
tmp::Tree<Leaf<F, long(arg::P<1>, arg::P<0>)>>,
tmp::Tree<Leaf<G, int(arg::R<0>)>>
>
>;
using Result = TreeFromSkeleton<Skel>;
REQUIRE(std::is_same<Expected, Result>{});
}
SECTION("complex") {
struct A0 {}; struct A1 {}; struct A2 {}; struct A3 {}; struct R {};
using Struct = S<Serial, A0, S<Farm, S<Serial, A1, A2>>, A0, S<Serial, A3, A3>>;
using Links = L<Serial, void(), R(), L<Farm, bool(), L<Serial, char(), short(), int()>>, R(), L<Serial, long(), float(), double()>>;
using Skel = BuildSkeletonT<Struct, Links>;
using Expected =
tmp::Tree<
Branch<Serial, void()>,
tmp::Tree<Leaf<A0, R()>>,
tmp::Tree<
Branch<Farm, bool()>,
tmp::Tree<
Branch<Serial, char()>,
tmp::Tree<Leaf<A1, short()>>,
tmp::Tree<Leaf<A2, int()>>
>
>,
tmp::Tree<Leaf<A0, R()>>,
tmp::Tree<
Branch<Serial, long()>,
tmp::Tree<Leaf<A3, float()>>,
tmp::Tree<Leaf<A3, double()>>
>
>;
using Result = TreeFromSkeleton<Skel>;
REQUIRE(std::is_same<Expected, Result>{});
}
}
TEST_CASE("ToSkeleton") {
struct A0 {}; struct A1 {}; struct A2 {}; struct A3 {}; struct R {};
using Struct = S<Serial, A0, S<Farm, S<Serial, A1, A2>>, A0, S<Serial, A3, A3>>;
using Links = L<Serial, void(), R(), L<Farm, bool(), L<Serial, char(), short(), int()>>, R(), L<Serial, long(), float(), double()>>;
using Expected = BuildSkeletonT<Struct, Links>;
using Input =
tmp::Tree<
Branch<Serial, void()>,
tmp::Tree<Leaf<A0, R()>>,
tmp::Tree<
Branch<Farm, bool()>,
tmp::Tree<
Branch<Serial, char()>,
tmp::Tree<Leaf<A1, short()>>,
tmp::Tree<Leaf<A2, int()>>
>
>,
tmp::Tree<Leaf<A0, R()>>,
tmp::Tree<
Branch<Serial, long()>,
tmp::Tree<Leaf<A3, float()>>,
tmp::Tree<Leaf<A3, double()>>
>
>;
using Result = SkeletonFromTree<Input>;
REQUIRE(std::is_same<Expected, Result>{});
REQUIRE(std::is_same<Expected, SkeletonFromTree<TreeFromSkeleton<Expected>>>{});
}
TEST_CASE("SkeletonParallelHeight") {
SECTION("serial") {
struct F {}; struct G {};
using Struct = S<Serial, S<Serial, F, G>>;
using Links = L<Serial, arg::R<0>(int&, int), L<Serial, arg::R<1>(arg::P<1>, arg::P<0>), long(arg::P<1>, arg::P<0>), int(arg::R<0>)>>;
using Skel = BuildSkeletonT<Struct, Links>;
REQUIRE(skeletonParallelHeight<Skel> == 0);
}
SECTION("par2") {
struct A0 {}; struct A1 {}; struct A2 {};
using Struct = S<Serial, A0, S<Farm, S<Farm, A1>>, S<Farm, A2>>;
using Links = L<Serial, int(), int(), L<Farm, int(), L<Farm, int(), int()>>, L<Farm, int(), int()>>;
using Skel = BuildSkeletonT<Struct, Links>;
REQUIRE(skeletonParallelHeight<Skel> == 2);
}
SECTION("par4") {
struct A0 {};
using Struct = S<Farm, S<Farm, S<Farm, S<Farm, A0>>>>;
using Links = L<Farm, int(), L<Farm, int(), L<Farm, int(), L<Farm, int(), int()>>>>;
using Skel = BuildSkeletonT<Struct, Links>;
REQUIRE(skeletonParallelHeight<Skel> == 4);
}
SECTION("complex") {
struct A0 {};
using Struct = S<Farm, S<Serial, S<Farm, S<Farm, A0>>, S<Farm, S<Serial, S<Farm, A0>>>>>;
using Links = L<Farm, int(), L<Serial, int(), L<Farm, int(), L<Farm, int(), int()>>, L<Farm, int(), L<Serial, int(), L<Farm, int(), int()>>>>>;
using Skel = BuildSkeletonT<Struct, Links>;
REQUIRE(skeletonParallelHeight<Skel> == 3);
}
}

View File

@ -0,0 +1,71 @@
#include <catch.hpp>
#include <alsk/skeleton/link/args.h>
#include <alsk/context/context.h>
using namespace alsk;
using tmp::Pack;
TEST_CASE("ArgType") {
using P = Pack<struct A, struct B, struct C>;
using R = Pack<struct D, struct E, struct F>;
using X = Pack<struct G, struct H>;
using Placeholders = arg::Placeholders<P, R, X>;
REQUIRE(std::is_same<struct A, ArgType<arg::P<0>, Placeholders>>{});
REQUIRE(std::is_same<struct B, ArgType<arg::P<1>, Placeholders>>{});
REQUIRE(std::is_same<struct C, ArgType<arg::P<2>, Placeholders>>{});
REQUIRE(std::is_same<struct D, ArgType<arg::R<0>, Placeholders>>{});
REQUIRE(std::is_same<struct E, ArgType<arg::R<1>, Placeholders>>{});
REQUIRE(std::is_same<struct F, ArgType<arg::R<2>, Placeholders>>{});
REQUIRE(std::is_same<struct H, ArgType<arg::RNG, Placeholders>>{});
}
TEST_CASE("ArgGet") {
std::tuple<int, int, int> p{0, 1, 2};
std::tuple<int, int, int> r{3, 4, 5};
std::tuple<int, int> x{6, 7};
REQUIRE(std::get<0>(p) == ArgGet<arg::P<0>>::get(p, r, x));
REQUIRE(std::get<1>(p) == ArgGet<arg::P<1>>::get(p, r, x));
REQUIRE(std::get<2>(p) == ArgGet<arg::P<2>>::get(p, r, x));
REQUIRE(std::get<0>(r) == ArgGet<arg::R<0>>::get(p, r, x));
REQUIRE(std::get<1>(r) == ArgGet<arg::R<1>>::get(p, r, x));
REQUIRE(std::get<2>(r) == ArgGet<arg::R<2>>::get(p, r, x));
REQUIRE(std::get<1>(x) == ArgGet<arg::RNG>::get(p, r, x));
}
TEST_CASE("PRFilter") {}
TEST_CASE("RealType") {
using P = Pack<struct A, struct B, struct C>;
using R = Pack<struct D, struct E, struct F>;
using X = Pack<struct G, struct H>;
using Placeholders = arg::Placeholders<P, R, X>;
REQUIRE(std::is_same<struct A, RealType<arg::P<0>, Placeholders>>{});
REQUIRE(std::is_same<struct B, RealType<arg::P<1>, Placeholders>>{});
REQUIRE(std::is_same<struct C, RealType<arg::P<2>, Placeholders>>{});
REQUIRE(std::is_same<struct D, RealType<arg::R<0>, Placeholders>>{});
REQUIRE(std::is_same<struct E, RealType<arg::R<1>, Placeholders>>{});
REQUIRE(std::is_same<struct F, RealType<arg::R<2>, Placeholders>>{});
REQUIRE(std::is_same<struct H, RealType<arg::RNG, Placeholders>>{});
REQUIRE(std::is_same<struct _, RealType<struct _, Placeholders>>{});
}
TEST_CASE("RealSignature") {
using P = Pack<struct A, struct B, struct C>;
using R = Pack<struct D, struct E, struct F>;
using X = Pack<struct G, struct H>;
using Placeholders = arg::Placeholders<P, R, X>;
using S = int&(arg::P<0>, arg::R<1>, arg::P<2>, arg::RNG, struct _);
using Q = std::function<int&(struct A, struct E, struct C, struct H, struct _)>;
REQUIRE(std::is_same<Q, RealSignature<S, Placeholders>>{});
}
TEST_CASE("Returns") {}
TEST_CASE("Execute") {}