Following up to a previous question I asked in Fixing parameters of a fitting function in Nonlinear Least-Square GSL (successfully answered by #zkoza), I would like to implement an algorithm that can fit data to a non-linear function, by fixing some of its parameters while leaving other parameters to change for finding the best fit to the data. The difference to my previous question is that I want to have two independent variables instead of one independent variable.
Non-linear function used to fit the data
double gaussian(double x, double b, double a, double c)
{
const double z = (x - b) / c;
return a * std::exp(-0.5 * z * z);
}
In my previous question I was considering that x was the only independent variable. Now I would like to consider two independent variables, x and b.
The original algorithm used to fit a non-linear function using only one independent variable (while fixing variable a) is a C++ wrapper of the GSL nonlinear least-squares algorithm (borrowed from https://github.com/Eleobert/gsl-curve-fit/blob/master/example.cpp):
template <typename F, size_t... Is>
auto gen_tuple_impl(F func, std::index_sequence<Is...> )
{
return std::make_tuple(func(Is)...);
}
template <size_t N, typename F>
auto gen_tuple(F func)
{
return gen_tuple_impl(func, std::make_index_sequence<N>{} );
}
template <class R, class... ARGS>
struct function_ripper {
static constexpr size_t n_args = sizeof...(ARGS);
};
template <class R, class... ARGS>
auto constexpr n_params(R (ARGS...) )
{
return function_ripper<R, ARGS...>();
}
auto internal_solve_system(gsl_vector* initial_params, gsl_multifit_nlinear_fdf *fdf,
gsl_multifit_nlinear_parameters *params) -> std::vector<double>
{
// This specifies a trust region method
const gsl_multifit_nlinear_type *T = gsl_multifit_nlinear_trust;
const size_t max_iter = 200;
const double xtol = 1.0e-8;
const double gtol = 1.0e-8;
const double ftol = 1.0e-8;
auto *work = gsl_multifit_nlinear_alloc(T, params, fdf->n, fdf->p);
int info;
// initialize solver
gsl_multifit_nlinear_init(initial_params, fdf, work);
//iterate until convergence
gsl_multifit_nlinear_driver(max_iter, xtol, gtol, ftol, nullptr, nullptr, &info, work);
// result will be stored here
gsl_vector * y = gsl_multifit_nlinear_position(work);
auto result = std::vector<double>(initial_params->size);
for(int i = 0; i < result.size(); i++)
{
result[i] = gsl_vector_get(y, i);
}
auto niter = gsl_multifit_nlinear_niter(work);
auto nfev = fdf->nevalf;
auto njev = fdf->nevaldf;
auto naev = fdf->nevalfvv;
// nfev - number of function evaluations
// njev - number of Jacobian evaluations
// naev - number of f_vv evaluations
//logger::debug("curve fitted after ", niter, " iterations {nfev = ", nfev, "} {njev = ", njev, "} {naev = ", naev, "}");
gsl_multifit_nlinear_free(work);
gsl_vector_free(initial_params);
return result;
}
template<auto n>
auto internal_make_gsl_vector_ptr(const std::array<double, n>& vec) -> gsl_vector*
{
auto* result = gsl_vector_alloc(vec.size());
int i = 0;
for(const auto e: vec)
{
gsl_vector_set(result, i, e);
i++;
}
return result;
}
template<typename C1>
struct fit_data
{
const std::vector<double>& t;
const std::vector<double>& y;
// the actual function to be fitted
C1 f;
};
template<typename FitData, int n_params>
int internal_f(const gsl_vector* x, void* params, gsl_vector *f)
{
auto* d = static_cast<FitData*>(params);
// Convert the parameter values from gsl_vector (in x) into std::tuple
auto init_args = [x](int index)
{
return gsl_vector_get(x, index);
};
auto parameters = gen_tuple<n_params>(init_args);
// Calculate the error for each...
for (size_t i = 0; i < d->t.size(); ++i)
{
double ti = d->t[i];
double yi = d->y[i];
auto func = [ti, &d](auto ...xs)
{
// call the actual function to be fitted
return d->f(ti, xs...);
};
auto y = std::apply(func, parameters);
gsl_vector_set(f, i, yi - y);
}
return GSL_SUCCESS;
}
using func_f_type = int (*) (const gsl_vector*, void*, gsl_vector*);
using func_df_type = int (*) (const gsl_vector*, void*, gsl_matrix*);
using func_fvv_type = int (*) (const gsl_vector*, const gsl_vector *, void *, gsl_vector *);
template<auto n>
auto internal_make_gsl_vector_ptr(const std::array<double, n>& vec) -> gsl_vector*;
auto internal_solve_system(gsl_vector* initial_params, gsl_multifit_nlinear_fdf *fdf,
gsl_multifit_nlinear_parameters *params) -> std::vector<double>;
template<typename C1>
auto curve_fit_impl(func_f_type f, func_df_type df, func_fvv_type fvv, gsl_vector* initial_params, fit_data<C1>& fd) -> std::vector<double>
{
assert(fd.t.size() == fd.y.size());
auto fdf = gsl_multifit_nlinear_fdf();
auto fdf_params = gsl_multifit_nlinear_default_parameters();
fdf.f = f;
fdf.df = df;
fdf.fvv = fvv;
fdf.n = fd.t.size();
fdf.p = initial_params->size;
fdf.params = &fd;
// "This selects the Levenberg-Marquardt algorithm with geodesic acceleration."
fdf_params.trs = gsl_multifit_nlinear_trs_lmaccel;
return internal_solve_system(initial_params, &fdf, &fdf_params);
}
template <typename Callable, auto n>
auto curve_fit(Callable f, const std::array<double, n>& initial_params, const std::vector<double>& x, const std::vector<double>& y) -> std::vector<double>
{
// We can't pass lambdas without convert to std::function.
//constexpr auto n = 3;//decltype(n_params(f))::n_args - 5;
//constexpr auto n = 2;
assert(initial_params.size() == n);
auto params = internal_make_gsl_vector_ptr(initial_params);
auto fd = fit_data<Callable>{x, y, f};
return curve_fit_impl(internal_f<decltype(fd), n>, nullptr, nullptr, params, fd);
}
In order to fix one of the parameters of the gaussian function, #zkoza proposed to use functors:
struct gaussian_fixed_a
{
double a;
gaussian_fixed_a(double a) : a{a} {}
double operator()(double x, double b, double c) const { return gaussian(x, b, a, c); }
};
And these last lines show how I would create a fake dataset of observed data (with some noise which is normally distributed) and test the fitting curve function with two independent variables, given by the vectors xs and bs.
int main()
{
auto device = std::random_device();
auto gen = std::mt19937(device());
auto xs = linspace<std::vector<double>>(0.0, 1.0, 300);
auto bs = linspace<std::vector<double>>(0.4, 1.4, 300);
auto ys = std::vector<double>(xs.size());
double a = 5.0, c = 0.15;
for(size_t i = 0; i < xs.size(); i++)
{
auto y = gaussian(xs[i], a, bs[i], c);
auto dist = std::normal_distribution(0.0, 0.1 * y);
ys[i] = y + dist(gen);
}
gaussian_fixed_a g(a);
auto r = curve_fit(g, std::array{0.11}, xs, bs, ys);
std::cout << "result: " << r[0] << ' ' << '\n';
std::cout << "error : " << r[0] - c << '\n';
}
Do you have any idea on how I could implement the two-independent variables non-linear fitting?
The solution, as suggested in the comments by #BenVoigt, is to replace the x and b independent variables in the gaussian function with 'one independent variable' given as a vector, whose first element is x and the second element is b.
Also the backbone of the nonlinear fitting needs to be slightly edited. The edits consist:
Replace the fit_data functor with:
struct fit_data
{
const std::vector< vector<double> > &t;
const std::vector<double>& y;
// the actual function to be fitted
C1 f;
};
Such that, the independent variable is no longer a vector but rather a vector of a vector (aka a matrix).
Replace within the function internal_f.
a) double ti = d->t[i] with std::vector<double> ti = d->t[i]
b) auto func = [ti, &d](auto ...xs) with auto func = [ti, &d](auto ...xs_matrix)
c) return d->f(ti, xs...) with return d->f(ti, xs_matrix...)
Replace within curve_fit function:
a) const std::vector<double>& x with const std::vector< vector<double> > &xs_matrix
b) auto fd = fit_data<Callable>{x, y, f} with auto fd = fit_data<Callable>{xs_matrix, y, f}
Whereas the gaussian function, gaussian_fixed_a functor and the fitting function looks like:
double gaussian(std::vector<double> x_vector, double a, double c)
{
const double z = (x_vector[0] - x_vector[1]) / c;
return a * std::exp(-0.5 * z * z);
}
struct gaussian_fixed_a
{
double a;
gaussian_fixed_a(double a) : a{a} {}
double operator()(std::vector<double> x_vector, double c) const { return gaussian(x_vector, a, c); }
};
double fittingTest(const std::vector< vector<double> > &xs_matrix, const std::vector<double> ys, const double a){
gaussian_fixed_a g(a);
auto r = curve_fit(g, std::array{3.0}, xs_matrix, ys);
return r[0]);
}
I have a window application of 1024 width and 768 height and contains a bunch of meteorites, ships and boats. The meteorites freely roam across the window driven by forces.
The forces are: random position, towards/away from boat, towards/away from ship and cohesion, seperation, alignment to other meteorites
I feel like the forces are not fully working since they sometimes move off the screen and move with inverted velocity eg: they are roaming from top right straight to top left and when reached go straight to bottom left.
Are my calculations correct or did I mess up something at the forces?
Meteorite header:
#include <chrono>
#include <cmath>
#include <array>
#include <random>
#include <algorithm>
using scalar = float;
template <typename Scalar> class basic_vector2d {
public:
constexpr basic_vector2d() noexcept = default;
constexpr basic_vector2d(Scalar x, Scalar y) noexcept : x_{ x }, y_{ y } {}
constexpr Scalar x() const noexcept { return x_; }
constexpr void x(Scalar newX) noexcept { x_ = newX; }
constexpr Scalar y() const noexcept { return y_; }
constexpr void y(Scalar newY) noexcept { y_ = newY; }
constexpr bool operator==(basic_vector2d other) const noexcept {
return x_ == other.x_ && y_ == other.y_;
}
constexpr bool operator!=(basic_vector2d other) const noexcept {
return x_ != other.x_ || y_ != other.y_;
}
constexpr basic_vector2d& operator+=(basic_vector2d other) noexcept {
x_ += other.x_;
y_ += other.y_;
return *this;
}
constexpr basic_vector2d& operator-=(basic_vector2d other) noexcept {
x_ -= other.x_;
y_ -= other.y_;
return *this;
}
constexpr basic_vector2d& operator*=(Scalar s) noexcept {
x_ *= s;
y_ *= s;
return *this;
}
constexpr basic_vector2d& operator/=(Scalar s) noexcept {
x_ /= s;
y_ /= s;
return *this;
}
private:
Scalar x_{};
Scalar y_{};
};
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator-(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() - b.x(), a.y() - b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator+(basic_vector2d<Scalar> a,
basic_vector2d<Scalar> b) {
return { a.x() + b.x(), a.y() + b.y() };
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(basic_vector2d<Scalar> v, scalar s) {
return v *= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator*(scalar s, basic_vector2d<Scalar> v) {
return operator*(v, s);
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(basic_vector2d<Scalar> v, scalar s) {
return v /= s;
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> operator/(scalar s, basic_vector2d<Scalar> v) {
return operator/(v, s);
}
template <typename Scalar>
constexpr scalar dot(basic_vector2d<Scalar> a, basic_vector2d<Scalar> b) {
return a.x() * b.x() + a.y() * b.y();
}
template <typename Scalar> constexpr auto norm(basic_vector2d<Scalar> p) {
return std::sqrt(dot(p, p));
}
template <typename Scalar>
constexpr basic_vector2d<Scalar> normalize(basic_vector2d<Scalar> p) {
auto ls = norm(p);
return { p.x() / ls, p.y() / ls };
}
using vector2d = basic_vector2d<scalar>;
template <typename T> class basic_size {
public:
constexpr basic_size() noexcept = default;
constexpr basic_size(T width, T height) noexcept
: width_{ width }, height_{ height } {}
constexpr T width() const noexcept { return width_; }
constexpr T height() const noexcept { return height_; }
constexpr void width(T new_width) noexcept { width_ = new_width; }
constexpr void height(T new_height) noexcept { height_ = new_height; }
constexpr basic_size& operator*=(T x) {
width(width() * x);
height(height() * x);
return *this;
}
private:
T width_{};
T height_{};
};
using size = basic_size<scalar>;
template <typename Scalar> class basic_rectangle {
public:
constexpr basic_rectangle(basic_vector2d<Scalar> top_left,
basic_size<Scalar> size)
: top_left_{ top_left }, size_{ size } {}
constexpr basic_vector2d<Scalar> const& top_left() const noexcept {
return top_left_;
}
constexpr basic_size<Scalar> const& size() const noexcept { return size_; }
private:
basic_vector2d<Scalar> top_left_;
basic_size<Scalar> size_;
};
using rectangle = basic_rectangle<scalar>;
inline float to_seconds(std::chrono::nanoseconds dt) {
return std::chrono::duration_cast<std::chrono::duration<float>>(dt).count();
}
class meteorite {
public:
meteorite(int id, vector2d location);
int id;
/*!
* Called every tick
* \param dt the time that has passed since the previous tick
*/
void act(std::chrono::nanoseconds dt);
vector2d location() const { return location_; }
std::vector<vector2d> random_meteorite_locations(std::size_t n);
private:
vector2d velocity;
scalar max_velocity;
vector2d location_;
vector2d acceleration;
void location(vector2d loc) { location_ = loc; }
void random_position_force();
void screen_force(std::chrono::nanoseconds dt);
void move(std::chrono::nanoseconds dt);
void add_force(vector2d force);
void island_avoidance();
};
Meteorite source:
#include "meteorite.h"
meteorite::meteorite(int id, vector2d location) : id(id), velocity{ 0, 0 }, max_velocity(0.15), acceleration{ 0, 0 }, location_(location) {}
void meteorite::act(std::chrono::nanoseconds dt) {
move(dt);
}
void meteorite::move(std::chrono::nanoseconds dt) {
this->location(this->location() + velocity);
random_position_force();
screen_force(dt);
this->velocity += this->acceleration * to_seconds(dt);
// prevent velocity from exceeding max_velocity
float velocity_length = std::sqrt((this->velocity.x() * this->velocity.x()) + (this->velocity.y() * this->velocity.y()));
if (velocity_length >= this->max_velocity) {
this->velocity = normalize(this->velocity) * this->max_velocity;
}
/*directions:
* y -1 up
* y 1 down
*
* x 1 right
* x -1 left
*/
// reset acceleration to 0 for the next set of forces to be applied
this->acceleration = vector2d(0, 0);
}
// add force propeling meteorite to a random position
void meteorite::random_position_force() {
float x = (rand() % 100 - 50);
float y = (rand() % 100 - 50);
add_force(this->velocity + vector2d((x / 5), (y / 5)));
}
void meteorite::add_force(vector2d force) {
this->acceleration += force;
}
void meteorite::screen_force(std::chrono::nanoseconds dt)
{
auto new_position = this->location() + (this->velocity + (this->acceleration * to_seconds(dt)));
auto height = 1068 - 32;
auto width = 724 - 32;
if (new_position.x() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() < 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
else if (new_position.x() >= width)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.x() > 0)
{
screen_vector = vector2d(-this->acceleration.x() * 2, 0);
}
add_force(screen_vector);
}
if (new_position.y() <= 32) {
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() < 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
else if (new_position.y() >= height)
{
vector2d screen_vector = vector2d(0, 0);
if (this->acceleration.y() > 0)
{
screen_vector = vector2d(0, -this->acceleration.y() * 2);
}
add_force(screen_vector);
}
}
std::vector<vector2d> meteorite::random_meteorite_locations(std::size_t n) {
// from 0x2 to 13x17 = 195
// from 13x0 to 28x9 = 135
// from 20x9 to 32x19 = 120
// from 6x17 to 25x24 = 133
// sum = 583
std::random_device rd{};
std::default_random_engine re{ rd() };
std::uniform_int_distribution<> id{ 0, 583 };
std::uniform_real_distribution<scalar> sd{ 0, 1 };
auto rv = [&](rectangle const& r) {
return r.top_left() + vector2d{ r.size().width() * sd(re),
r.size().height() * sd(re) };
};
std::array<rectangle, 4> rects{
rectangle{vector2d{0.1f, 2}, size{13, 15}},
rectangle{vector2d{13.f, 0.1f}, size{15, 9}},
rectangle{vector2d{20, 9}, size{12, 10}},
rectangle{vector2d{6, 17}, size{17, 6}} };
auto to_index = [](int i) -> std::size_t {
if (i < 195)
return 0;
else if (i < 330)
return 1;
else if (i < 450)
return 2;
else
return 3;
};
std::vector<vector2d> result(n);
std::generate_n(result.begin(), result.size(), [&] {
auto val = id(re);
auto index = to_index(val);
auto rect = rects[index];
return 32 * rv(rect);
});
return result;
}
Main.cpp
#include <iostream>
#include "meteorite.h"
int main()
{
meteorite m = meteorite{ 0, {} };
std::vector<meteorite*> meteorites;
std::vector<vector2d> locations = m.random_meteorite_locations(1);
int i = 1;
for (auto& loc : locations) {
meteorites.push_back(new meteorite(i, loc));
}
auto t_prev = std::chrono::high_resolution_clock::now();
while (true) {
auto t_current = std::chrono::high_resolution_clock::now();
std::chrono::nanoseconds dt = std::chrono::nanoseconds(200);
t_prev = t_current;
for (auto& m : meteorites) {
m->act(dt);
std::cout << m->location().x() << " " << m->location().y() << "\n";
}
}
for (auto& m : meteorites) {
delete m;
}
}
You're computing the new position incorrectly in both places, move() and screen_force(). You're doing s = s0 + (v + a * t), but you should be doing s = s0 + v * t + (a * t^2) / 2.
Here's a working example:
http://cpp.sh/9uu3w
My task is to modify Sergiu Dotenco's Well Equidistributed Long-period Linear (WELL) algorithm code to not use boost (not saying boost is bad, but due to some company's policy i have to remove it).
now, Sergiu's WELL is using boost's mpl library, there are quite some logic behind it. So one way is to read up all those, then naturally i would be able to finish the task. The other way is, replacing bit by bit with some best guess.
I'm on the 2nd way to hope this try-and-error approach would be faster. So far I've successfully replaced boost::mpl::if_ and if_c with std::conditional, but hit error when try to update IsPowerOfTwo and Power2Modulo etc, that's why i'm seeking help there.
Below is the code, how to rewrite it without boost, but only c++17?
/**
* Conditional expression of type (r & (r - 1)) == 0 which allows to check
* whether a number #f$r#f$ is of type #f$2^n#f$.
*/
typedef boost::mpl::equal_to<
boost::mpl::bitand_<
boost::mpl::_,
boost::mpl::minus<boost::mpl::_, boost::mpl::int_<1>
>
>,
boost::mpl::int_<0>
> IsPowerOfTwo;
template<class UIntType, UIntType r>
struct Power2Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type type;
BOOST_STATIC_ASSERT(type::value);
template<class T>
static T calc(T value)
{
return value & (r - 1);
}
};
If possible, pls give a short example on how to call it? I tried to instantiate IsPowerOfTwo or Power2Modulo in main with
Detail::IsPowerOfTwo p0;
or
Detail::Power2Modulo<int, 3> p1;
but got compilation error.
I asked a relevant question before and got some suggestion. However, not familiar to metaprogramming and boost, I don't quite get it.
So, I looked at that library, and created a no-boost fork adapting the WELL pseudo-random-number-generator to pure c++11.
See here on my github: https://github.com/sehe/well-random (the default branch is no-boost).
What is well-random?
well-random is a c++11 fork from
random, a collection of various
pseudo-random number generators and distributions that were intended
to accompany the Boost Random Number Library.
This fork currently only adopted the WELL generator and its tests.
Getting started
The no-boost branch no longer requires any boost library. Instead it
requires c++11. To compile the tests make sure first CMake 2.8 is
installed, then enter :
$ cmake . -DCMAKE_BUILD_TYPE=Release
in your terminal or command prompt on Windows inside project's
directory to generate the appropriate configuration that can be used
to compile the tests using make/nmake or inside an IDE.
What Was Refactored
BOOST_STATIC_ASSERT to STATIC_ASSERT (this becomes obsolete with c++17: http://en.cppreference.com/w/cpp/language/static_assert)
BOOST_STATIC_CONSTANT to static constexpr
BOOST_PREVENT_MACRO_SUBSTITUTION -> PREVENT_MACRO_SUBSTITUTION (trivial macro)
BOOST_THROW_EXCEPTION dropped. NOTE This implies the code cannot be compiled with exception support disabled.
All things related to Boost Test
BOOST_CHECK -> CHECK
#define MESSAGE_PREAMBLE() (std::cerr << __FILE__ << ":" << __LINE__ << " ")
#define CHECK(test) do { if (!(test)) MESSAGE_PREAMBLE() << #test << "\n"; } while (0)
BOOST_CHECK_EQUAL -> CHECK_EQUAL
#define CHECK_EQUAL(expected,actual) do { \
auto&& _e = expected; \
auto&& _a = actual; \
if (_e != _a) \
MESSAGE_PREAMBLE() << "expected:" << #expected << " = " << _e << "\n" \
<< "\tactual:" << #actual << " = " << _a << "\n"; \
} while (0)
BOOST_AUTO_TEST_CASE - dropped. The test driver is main now:
int main() {
//CHECK_EQUAL(16, Detail::shift<2>(64));
//CHECK_EQUAL(64, Detail::shift<-2>(16));
//CHECK_EQUAL(32, Detail::shift<0>(32));
//CHECK(Detail::is_powerof2(512u));
//CHECK(not Detail::is_powerof2(0u));
WellTestCase<Well512a, 0x2b3fe99e>::run();
WellTestCase<Well521a, 0xc9878363>::run();
WellTestCase<Well521b, 0xb75867f6>::run();
WellTestCase<Well607a, 0x7b5043ea>::run();
WellTestCase<Well607b, 0xaedee7da>::run();
WellTestCase<Well800a, 0x2bfe686f>::run();
WellTestCase<Well800b, 0xf009e1bd>::run();
WellTestCase<Well1024a, 0xd07f528c>::run();
WellTestCase<Well1024b, 0x867f7993>::run();
WellTestCase<Well19937a, 0xb33a2cd5>::run();
WellTestCase<Well19937b, 0x191de86a>::run();
WellTestCase<Well19937c, 0x243eaed5>::run();
WellTestCase<Well21701a, 0x7365a269>::run();
WellTestCase<Well23209a, 0x807dacb >::run();
WellTestCase<Well23209b, 0xf1a77751>::run();
WellTestCase<Well44497a, 0xfdd7c07b>::run();
WellTestCase<Well44497b, 0x9406547b>::run();
}
boost::ref -> std::ref (from <functional>)
Boost Range helpers replaced by standard c++ (boost::size, boost::end for arrays)
using ulong_long_type = unsigned long long;
Conditional operators shift and mod have been re-implemented with straight-up SFINAE based on std::enable_if instead of using MPL meta-programming:
template<class UIntType, unsigned N>
struct Left
{
static UIntType shift(UIntType a)
{
return a << N;
}
};
template<class UIntType, unsigned N>
struct Right
{
static UIntType shift(UIntType a)
{
return a >> N;
}
};
template<int N, class UIntType>
inline UIntType shift(UIntType a)
{
return boost::mpl::if_c<(N < 0),
Left<UIntType, -N>,
Right<UIntType, N>
>::type::shift(a);
}
became:
template <typename UIntType, signed N, typename Enable = void> struct Shift;
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N>=0)>::type> {
static UIntType apply(UIntType a) { return a >> N; }
};
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N<0)>::type> {
static UIntType apply(UIntType a) { return a << -N; }
};
template<int N, class UIntType>
inline UIntType shift(UIntType a) { return Shift<UIntType, N>::apply(a); }
Likewise, the Modulo switch (Power2Modulo and GenericModulo) that looked like this:
/**
* Conditional expression of type (r & (r - 1)) == 0 which allows to check
* whether a number #f$r#f$ is of type #f$2^n#f$.
*/
typedef boost::mpl::equal_to<
boost::mpl::bitand_<
boost::mpl::_,
boost::mpl::minus<boost::mpl::_, boost::mpl::int_<1>
>
>,
boost::mpl::int_<0>
> IsPowerOfTwo;
template<class UIntType, UIntType r>
struct Power2Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type type;
BOOST_STATIC_ASSERT(type::value);
template<class T>
static T calc(T value)
{
return value & (r - 1);
}
};
template<class UIntType, UIntType r>
struct GenericModulo
{
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T>
static T calc(T value)
{
BOOST_STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
template<class UIntType, UIntType r>
struct Modulo
{
typedef typename boost::mpl::apply<
IsPowerOfTwo,
boost::mpl::integral_c<UIntType, r>
>::type rIsPowerOfTwo;
static UIntType calc(UIntType value)
{
// Use the bitwise AND for power 2 modulo arithmetic, or subtraction
// otherwise. Subtraction is about two times faster than direct modulo
// calculation.
return boost::mpl::if_<
rIsPowerOfTwo,
Power2Modulo<UIntType, r>,
GenericModulo<UIntType, r>
>::type::calc(value);
}
};
became much simpler with a little bit of c++11 (constexpr!) goodness:
template <typename T, typename = typename std::enable_if<!std::is_signed<T>()>::type>
constexpr static bool is_powerof2(T v) { return v && ((v & (v - 1)) == 0); }
template<class UIntType, UIntType r>
struct Modulo {
template<class T> static T calc(T value) { return calc(value, std::integral_constant<bool, is_powerof2(r)>{}); }
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T> static T calc(T value, std::true_type) { return value & (r - 1); }
template<class T> static T calc(T value, std::false_type) {
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
<boost/cstdint.hpp> -> <cstdint> (replacing ::boost by ::std for uint_least32_t and uint32_t)
Well_quoted type function replaced by an alias template (template<...> using T = ... see http://en.cppreference.com/w/cpp/language/type_alias ad 2)
typedefs rewritten as type aliases.
Full Listing
Live On Coliru
// Copyright (c) Sergiu Dotenco 2010, 2011, 2012
// Copyright (c) Seth Heeren - made independent of BOOST using C++11 - 2017
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/**
* #brief Implementation of the Well Equidistributed Long-period Linear (WELL)
* pseudo-random number generator.
* #file well.hpp
*/
#ifndef WELL_HPP
#define WELL_HPP
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iomanip>
#include <istream>
#include <limits>
#include <ostream>
#include <functional>
#include <stdexcept>
#define STATIC_ASSERT(x) static_assert(x, #x)
#define PREVENT_MACRO_SUBSTITUTION
//! #cond hide_private
namespace Detail {
using ulong_long_type = unsigned long long;
template <typename UIntType, signed N, typename Enable = void> struct Shift;
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N>=0)>::type> {
static UIntType apply(UIntType a) { return a >> N; }
};
template <typename UIntType, signed N>
struct Shift<UIntType, N, typename std::enable_if<(N<0)>::type> {
static UIntType apply(UIntType a) { return a << -N; }
};
template<int N, class UIntType>
inline UIntType shift(UIntType a) {
return Shift<UIntType, N>::apply(a);
}
/**
* #name Transformation matrices #f$M0,\dotsc,M6#f$ from Table I
* #{
*/
struct M0
{
template<class T>
static T transform(T)
{
return T(0);
}
};
struct M1
{
template<class T>
static T transform(T x)
{
return x;
}
};
template<int N>
struct M2
{
template<class T>
static T transform(T x)
{
return shift<N>(x);
}
};
template<int N>
struct M3
{
template<class T>
static T transform(T x)
{
return x ^ shift<N>(x);
}
};
template<std::uint_least32_t a>
struct M4
{
template<class T>
static T transform(T x)
{
T result = x >> 1;
if ((x & 1) == 1)
result ^= a;
return result;
}
};
template<int N, std::uint_least32_t b>
struct M5
{
template<class T>
static T transform(T x)
{
return x ^ (shift<N>(x) & b);
}
};
template
<
std::size_t w,
std::uint_least32_t q,
std::uint_least32_t a,
std::uint_least32_t ds,
std::uint_least32_t dt
>
struct M6
{
template<class T>
static T transform(T x)
{
T result = ((x << q) ^ (x >> (w - q))) & ds;
if ((x & dt) != 0)
result ^= a;
return result;
}
};
//! #}
template <typename T, typename = typename std::enable_if<!std::is_signed<T>()>::type>
constexpr static bool is_powerof2(T v) { return v && ((v & (v - 1)) == 0); }
template<class UIntType, UIntType r>
struct Modulo {
template<class T> static T calc(T value) { return calc(value, std::integral_constant<bool, is_powerof2(r)>{}); }
/**
* #brief Determines #a value modulo #a r.
*
* #pre value >= 0 and value < 2 * r
* #post value >= 0 and value < r
*/
template<class T> static T calc(T value, std::true_type) { return value & (r - 1); }
template<class T> static T calc(T value, std::false_type) {
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
assert(value < 2 * r);
if (value >= r)
value -= r;
return value;
}
};
template<std::uint_least32_t b, std::uint_least32_t c>
struct MatsumotoKuritaTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&)[N], std::size_t)
{
x ^= (x << 7) & b;
x ^= (x << 15) & c;
return x;
}
};
template<std::uint_least32_t mask>
struct HaraseTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&s)[N], std::size_t m2)
{
return x ^ (s[Modulo<UIntType, r>::calc(m2 + 1)] & mask);
}
};
struct NoTempering
{
template<std::size_t r, class UIntType, std::size_t N>
static UIntType apply(UIntType x, UIntType (&)[N], std::size_t)
{
return x;
}
};
} // namespace Detail
//! #endcond
/**
* #brief Well Equidistributed Long-period Linear (WELL) pseudo-random number
* generator.
*
* The implementation is based on the "Improved Long-Period Generators Based on
* Linear Recurrences Modulo 2" paper by Francois Panneton, Pierre L'Ecuyer and
* Makoto Matsumoto from ACM Transactions on Mathematical Software, 32 (1,
* March) 2006, pp. 1-16.
*
* #tparam UIntType The unsigned integer type.
* #tparam w Word size.
* #tparam r State size.
*/
template
<
class UIntType,
std::size_t w,
std::size_t r,
std::size_t p,
std::size_t m1,
std::size_t m2,
std::size_t m3,
class T0,
class T1,
class T2,
class T3,
class T4,
class T5,
class T6,
class T7,
class Tempering // mpl pluggable
>
class Well
{
STATIC_ASSERT(!std::numeric_limits<UIntType>::is_signed);
STATIC_ASSERT(w <= static_cast<std::size_t>(std::numeric_limits<UIntType>::digits));
STATIC_ASSERT(r > 0 && p < w);
STATIC_ASSERT(m1 > 0 && m1 < r);
STATIC_ASSERT(m2 > 0 && m2 < r);
STATIC_ASSERT(m3 > 0 && m3 < r);
public:
//! The unsigned integer type.
typedef UIntType result_type;
//! Word size.
static constexpr std::size_t word_size = w;
//! State size.
static constexpr std::size_t state_size = r;
//! Number of mask bits.
static constexpr std::size_t mask_bits = p;
//! Default seed value.
static constexpr UIntType default_seed = 5489U;
/**
* #brief Initializes the class using the specified seed #a value.
*
* #param value The seed value to be used for state initialization.
*/
explicit Well(result_type value = default_seed)
{
seed(value);
}
template<class InputIterator>
Well(InputIterator& first, InputIterator last)
{
seed(first, last);
}
template<class Generator>
explicit Well(Generator& g)
{
seed(g);
}
template<class Generator>
void seed(Generator& g)
{
// Ensure std::generate_n doesn't copy the generator g by using
// std::reference_wrapper
std::generate_n(state_, state_size, std::ref(g));
}
void seed(result_type value = default_seed)
{
if (value == 0U)
value = default_seed;
state_[0] = value;
std::size_t i = 1;
UIntType *const s = state_;
// Same generator used to seed Mersenne twister
for ( ; i != state_size; ++i)
s[i] = (1812433253U * (s[i - 1] ^ (s[i - 1] >> (w - 2))) + i);
index_ = i;
}
template<class InputIterator>
void seed(InputIterator& first, InputIterator last)
{
index_ = 0;
std::size_t i = 0;
for ( ; i != state_size && first != last; ++i, ++first)
state_[i] = *first;
if (first == last && i != state_size)
throw std::invalid_argument("Seed sequence too short");
}
/**
* #brief Generates a random number.
*/
result_type operator()()
{
const UIntType upper_mask = ~0U << p;
const UIntType lower_mask = ~upper_mask;
// v[i,j] = state[(r-i+j) mod r]
std::size_t i = index_;
// Equivalent to r-i but allows to avoid negative values in the
// following two expressions
std::size_t j = i + r;
std::size_t k = mod(j - 1); // [i,r-1]
std::size_t l = mod(j - 2); // [i,r-2]
std::size_t im1 = i + m1;
std::size_t im2 = i + m2;
std::size_t im3 = i + m3;
UIntType z0, z1, z2, z3, z4;
z0 = (state_[k] & upper_mask) | (state_[l] & lower_mask);
z1 = T0::transform(state_[i]) ^
T1::transform(state(im1));
z2 = T2::transform(state(im2)) ^
T3::transform(state(im3));
z3 = z1 ^ z2;
z4 = T4::transform(z0) ^ T5::transform(z1) ^
T6::transform(z2) ^ T7::transform(z3);
state_[i] = z3; // v[i+1,1]
state_[k] = z4; // v[i+1,0]
index_ = k;
return Tempering::template apply<r>(z4, state_, im2);
}
result_type min PREVENT_MACRO_SUBSTITUTION () const
{
return 0U;
}
result_type max PREVENT_MACRO_SUBSTITUTION () const
{
return ~0U >> (std::numeric_limits<UIntType>::digits - w);
}
void discard(Detail::ulong_long_type z)
{
while (z-- > 0) {
operator()();
}
}
/**
* #brief Compares the state of two generators for equality.
*/
friend bool operator==(const Well& lhs, const Well& rhs)
{
for (std::size_t i = 0; i != state_size; ++i)
if (lhs.compute(i) != rhs.compute(i))
return false;
return true;
}
/**
* #brief Compares the state of two generators for inequality.
*/
friend bool operator!=(const Well& lhs, const Well& rhs)
{
return !(lhs == rhs);
}
/**
* #brief Writes the state to the specified stream.
*/
template<class E, class T>
friend std::basic_ostream<E, T>&
operator<<(std::basic_ostream<E, T>& out, const Well& well)
{
E space = out.widen(' ');
for (std::size_t i = 0; i != state_size; ++i)
out << well.compute(i) << space;
return out;
}
/**
* #brief Reads the generator state from the specified input stream.
*/
template<class E, class T>
friend std::basic_istream<E, T>&
operator>>(std::basic_istream<E, T>& in, Well& well)
{
for (std::size_t i = 0; i != state_size; ++i)
in >> well.state_[i] >> std::ws;
well.index_ = state_size;
return in;
}
private:
template<class T>
static T mod(T value)
{
return Detail::Modulo<T, r>::calc(value);
}
UIntType state(std::size_t index) const
{
return state_[mod(index)];
}
UIntType compute(std::size_t index) const
{
return state_[(index_ + index + r) % r];
}
UIntType state_[r];
std::size_t index_;
};
namespace Detail {
/**
* #name Base definitions with pluggable tempering method
* #{
*/
template <typename Tempering>
using Well512a_base = Well<
std::uint32_t, 32, 16, 0, 13, 9, 5, M3<-16>, M3<-15>, M3<11>, M0, M3<-2>, M3<-18>, M2<-28>,
M5<-5, 0xda442d24>, Tempering>;
template <typename Tempering>
using Well521a_base = Well<
std::uint32_t, 32, 17, 23, 13, 11, 10, M3<-13>, M3<-15>, M1, M2<-21>,
M3<-13>, M2<1>, M0, M3<11>, Tempering>;
template <typename Tempering>
using Well521b_base = Well<
std::uint32_t, 32, 17, 23, 11, 10, 7, M3<-21>, M3<6>, M0, M3<-13>, M3<13>,
M2<-10>, M2<-5>, M3<13>, Tempering>;
template <typename Tempering>
using Well607a_base = Well<
std::uint32_t, 32, 19, 1, 16, 15, 14, M3<19>, M3<11>, M3<-14>, M1, M3<18>,
M1, M0, M3<-5>, Tempering>;
template <typename Tempering>
using Well607b_base = Well<
std::uint32_t, 32, 19, 1, 16, 18, 13, M3<-18>, M3<-14>, M0, M3<18>,
M3<-24>, M3<5>, M3<-1>, M0, Tempering>;
template <typename Tempering>
using Well800a_base = Well<
std::uint32_t, 32, 25, 0, 14, 18, 17, M1, M3<-15>, M3<10>, M3<-11>, M3<16>,
M2<20>, M1, M3<-28>, Tempering>;
template <typename Tempering>
using Well800b_base = Well<
std::uint32_t, 32, 25, 0, 9, 4, 22, M3<-29>, M2<-14>, M1, M2<19>, M1,
M3<10>, M4<0xd3e43ffd>, M3<-25>, Tempering>;
template <typename Tempering>
using Well1024a_base = Well<
std::uint32_t, 32, 32, 0, 3, 24, 10, M1, M3<8>, M3<-19>, M3<-14>, M3<-11>,
M3<-7>, M3<-13>, M0, Tempering>;
template <typename Tempering>
using Well1024b_base = Well<
std::uint32_t, 32, 32, 0, 22, 25, 26, M3<-21>, M3<17>, M4<0x8bdcb91e>,
M3<15>, M3<-14>, M3<-21>, M1, M0, Tempering>;
template <typename Tempering>
using Well19937a_base = Well<
std::uint32_t, 32, 624, 31, 70, 179, 449, M3<-25>, M3<27>, M2<9>, M3<1>,
M1, M3<-9>, M3<-21>, M3<21>, Tempering>;
template <typename Tempering>
using Well19937b_base = Well<
std::uint32_t, 32, 624, 31, 203, 613, 123, M3<7>, M1, M3<12>, M3<-10>,
M3<-19>, M2<-11>, M3<4>, M3<-10>, Tempering>;
template <typename Tempering>
using Well21701a_base = Well<
std::uint32_t, 32, 679, 27, 151, 327, 84, M1, M3<-26>, M3<19>, M0, M3<27>,
M3<-11>, M6<32, 15, 0x86a9d87e, 0xffffffef, 0x00200000>, M3<-16>,
Tempering>;
template <typename Tempering>
using Well23209a_base = Well<
std::uint32_t, 32, 726, 23, 667, 43, 462, M3<28>, M1, M3<18>, M3<3>,
M3<21>, M3<-17>, M3<-28>, M3<-1>, Tempering>;
template <typename Tempering>
using Well23209b_base = Well<
std::uint32_t, 32, 726, 23, 610, 175, 662, M4<0xa8c296d1>, M1, M6<32, 15,
0x5d6b45cc, 0xfffeffff, 0x00000002>, M3<-24>, M3<-26>, M1, M0, M3<16>,
Tempering>;
template <typename Tempering>
using Well44497a_base = Well<
std::uint32_t, 32, 1391, 15, 23, 481, 229, M3<-24>, M3<30>, M3<-10>,
M2<-26>, M1, M3<20>, M6<32, 9, 0xb729fcec, 0xfbffffff, 0x00020000>, M1, Tempering>;
//! #}
} // namespace Detail
using Well512a = Detail::Well512a_base<Detail::NoTempering>;
using Well521a = Detail::Well521a_base<Detail::NoTempering>;
using Well521b = Detail::Well521b_base<Detail::NoTempering>;
using Well607a = Detail::Well607a_base<Detail::NoTempering>;
using Well607b = Detail::Well607b_base<Detail::NoTempering>;
using Well800a = Detail::Well800a_base<Detail::NoTempering>;
using Well800b = Detail::Well800b_base<Detail::NoTempering>;
using Well1024a = Detail::Well1024a_base<Detail::NoTempering>;
using Well1024b = Detail::Well1024b_base<Detail::NoTempering>;
using Well19937a = Detail::Well19937a_base<Detail::NoTempering>;
using Well19937b = Detail::Well19937b_base<Detail::NoTempering>;
using Well19937c = Detail::Well19937a_base<Detail::MatsumotoKuritaTempering<0xe46e1700, 0x9b868000>>;
using Well21701a = Detail::Well21701a_base<Detail::NoTempering>;
using Well23209a = Detail::Well23209a_base<Detail::NoTempering>;
using Well23209b = Detail::Well23209b_base<Detail::NoTempering>;
using Well44497a = Detail::Well44497a_base<Detail::NoTempering>;
using Well44497b = Detail::Well44497a_base<Detail::MatsumotoKuritaTempering<0x93dd1400, 0xfa118000>>;
/**
* #name Maximally equidistributed versions using Harase's tempering method
* #{
*/
using Well800a_ME = Detail::Well800a_base<Detail::HaraseTempering<0x4880>>;
using Well800b_ME = Detail::Well800b_base<Detail::HaraseTempering<0x17030806>>;
using Well19937a_ME = Detail::Well19937a_base<Detail::HaraseTempering<0x4118000>>;
using Well19937b_ME = Detail::Well19937b_base<Detail::HaraseTempering<0x30200010>>;
using Well21701a_ME = Detail::Well21701a_base<Detail::HaraseTempering<0x1002>>;
using Well23209a_ME = Detail::Well23209a_base<Detail::HaraseTempering<0x5100000>>;
using Well23209b_ME = Detail::Well23209b_base<Detail::HaraseTempering<0x34000300>>;
using Well44497a_ME = Detail::Well44497a_base<Detail::HaraseTempering<0x48000000>>;
//! #}
#endif // WELL_HPP
// Copyright (c) Sergiu Dotenco 2010
// Copyright (c) Seth Heeren - made independent of BOOST using C++11 - 2017
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/**
* #brief WELL PRNG implementation unit test.
* #file welltest.cpp
*/
#include <algorithm>
#include <memory>
#include <iostream>
// #include "well.hpp"
#define MESSAGE_PREAMBLE() (std::cerr << __FILE__ << ":" << __LINE__ << " ")
#define CHECK_EQUAL(expected,actual) do { \
auto&& _e = expected; \
auto&& _a = actual; \
if (_e != _a) \
MESSAGE_PREAMBLE() << "expected:" << #expected << " = " << _e << "\n" \
<< "\tactual:" << #actual << " = " << _a << "\n"; \
} while (0)
#define CHECK(test) do { if (!(test)) MESSAGE_PREAMBLE() << #test << "\n"; } while (0)
/**
* #brief Generic WELL test case.
*
* The test case performs the following checks:
* -# The last generated value is equal to the value generate by the reference
* implementation after #f$10^9#f$ iterations. The generator is seeded using
* an array filled with 1s.
* -# The #c min and #c max methods of the #ref Well generator return 0 and
* #f$2^{32}-1#f$ respectively.
*
* #tparam RandomNumberGenerator WELL PRNG implementation type.
* #tparam Expected The expected result after #f$10^9#f$ iterations.
*/
template
<
class RandomNumberGenerator,
typename RandomNumberGenerator::result_type Expected
>
class WellTestCase
{
RandomNumberGenerator rng;
typedef typename RandomNumberGenerator::result_type result_type;
result_type generate()
{
unsigned state[RandomNumberGenerator::state_size];
std::uninitialized_fill_n(state, RandomNumberGenerator::state_size, 1);
unsigned* p = state;
rng.seed(p, p + RandomNumberGenerator::state_size);
result_type x = 0;
int iterations = 1000000000;
while (iterations-- > 0)
x = rng();
return x;
}
public:
static void run()
{
WellTestCase c;
CHECK_EQUAL(c.generate(), Expected);
CHECK_EQUAL(c.rng.min(), 0U);
CHECK_EQUAL(c.rng.max(), ~0U);
CHECK_EQUAL(c.rng, c.rng);
CHECK(c.rng == c.rng);
}
};
/**
* #brief Defines the actual test case.
*
* #param name The name of the test case.
* #param type WELL pseudo-random generator type.
* #param expected The expected result after #f$10^9#f$ iterations.
*
* #hideinitializer
*/
int main() {
CHECK_EQUAL(16, Detail::shift<2>(64));
CHECK_EQUAL(64, Detail::shift<-2>(16));
CHECK_EQUAL(32, Detail::shift<0>(32));
CHECK(Detail::is_powerof2(512u));
CHECK(not Detail::is_powerof2(0u));
WellTestCase<Well512a, 0x2b3fe99e>::run();
#ifndef COLIRU // stay in execution time limits
WellTestCase<Well521a, 0xc9878363>::run();
WellTestCase<Well521b, 0xb75867f6>::run();
WellTestCase<Well607a, 0x7b5043ea>::run();
WellTestCase<Well607b, 0xaedee7da>::run();
WellTestCase<Well800a, 0x2bfe686f>::run();
WellTestCase<Well800b, 0xf009e1bd>::run();
WellTestCase<Well1024a, 0xd07f528c>::run();
WellTestCase<Well1024b, 0x867f7993>::run();
WellTestCase<Well19937a, 0xb33a2cd5>::run();
WellTestCase<Well19937b, 0x191de86a>::run();
WellTestCase<Well19937c, 0x243eaed5>::run();
WellTestCase<Well21701a, 0x7365a269>::run();
WellTestCase<Well23209a, 0x807dacb >::run();
WellTestCase<Well23209b, 0xf1a77751>::run();
WellTestCase<Well44497a, 0xfdd7c07b>::run();
WellTestCase<Well44497b, 0x9406547b>::run();
#endif
}
Using C++17, this code becomes way simpler and error messages are friendlier on the eye.
This is a sample implementation of Power2Modulo:
#include <type_traits>
template<class UIntType, UIntType r>
struct Power2Modulo
{
static_assert(std::is_unsigned_v<UIntType>);
static_assert((r & (r - 1)) == 0,
"The second parameter of this struct is required to be a power of 2");
template<class T>
[[nodiscard]] static constexpr T calc(T value)
{
return value & (r - 1);
}
};
You can use it like this:
int main()
{
/* This code fails to compile with friendly error message
Power2Modulo<unsigned, 12> x;
*/
// Using the static function
using Mod16 = Power2Modulo<unsigned, 16>;
static_assert(Mod16::calc(15) == 15);
static_assert(Mod16::calc(16) == 0);
static_assert(Mod16::calc(17) == 1);
// Using it like a member function
Power2Modulo<unsigned, 4> mod4;
static_assert(mod4.calc(15) == 3);
static_assert(mod4.calc(16) == 0);
static_assert(mod4.calc(17) == 1);
}
Tested with clang-6 and gcc-8 and VisualC++ (via http://webcompiler.cloudapp.net/).