<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in Section and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <ctime>

#include "TestDimension.hpp"
#include <nix/util/util.hpp>

using namespace std;
using namespace nix;

void TestDimension::setUp() {
    file = File::open("test_dimension.h5", FileMode::Overwrite);
    block = file.createBlock("dimensionTest","test");
    data_array = block.createDataArray("dimensionTest", "Test");
}

void TestDimension::tearDown() {
    file.deleteBlock(block.id());
    file.close();
}

void TestDimension::testId() {
    Dimension sd = data_array.appendSetDimension();
    CPPUNIT_ASSERT(data_array.dimensionCount() == 1 && sd.id() == 1);
    data_array.deleteDimension(sd.id());
    CPPUNIT_ASSERT(data_array.dimensionCount() == 0);
}

void TestDimension::testSampledDimLabel() {
    std::string label = "aLabel";
    std::string other_label = "anotherLabel";
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    sd.label(label);
    CPPUNIT_ASSERT(*(sd.label()) == label);
    sd.label(other_label);
    CPPUNIT_ASSERT(*(sd.label()) == other_label);
    CPPUNIT_ASSERT_NO_THROW(sd.label(nix::none));
    CPPUNIT_ASSERT(sd.label() == nix::none);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimUnit() {
    std::string invalidUnit = "invalidunit";
    std::string validUnit = "mV^2";
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT_THROW(sd.unit(invalidUnit), nix::InvalidUnit);
    CPPUNIT_ASSERT_NO_THROW(sd.unit(validUnit));
    CPPUNIT_ASSERT(*(sd.unit()) == validUnit);
    CPPUNIT_ASSERT_NO_THROW(sd.unit(boost::none));
    CPPUNIT_ASSERT(sd.unit() == boost::none);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimSamplingInterval() {
    double impossible_sampling_interval = -1.0;
    double invalid_sampling_interval = 0.0;
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT(sd.samplingInterval() == boost::math::constants::pi<double>());
    CPPUNIT_ASSERT_THROW(sd.samplingInterval(impossible_sampling_interval), std::runtime_error);
    CPPUNIT_ASSERT_THROW(sd.samplingInterval(invalid_sampling_interval), std::runtime_error);
    CPPUNIT_ASSERT_NO_THROW(sd.samplingInterval(samplingInterval));
    CPPUNIT_ASSERT(sd.samplingInterval() == samplingInterval);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimOffset() {
    double offset = 1.0;
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT_NO_THROW(sd.offset(offset));
    CPPUNIT_ASSERT(*(sd.offset()) == offset);
    CPPUNIT_ASSERT_NO_THROW(sd.offset(boost::none));
    CPPUNIT_ASSERT(sd.offset() == 0);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimOperators() {
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    Dimension d2 = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);
    CPPUNIT_ASSERT(d2.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd1, sd2, sd3;
    sd1 = d;
    sd2 = d2;
    sd3 = data_array.getDimension(d.id());
    CPPUNIT_ASSERT(sd1.id() == d.id() && sd2.id() == d2.id());
    CPPUNIT_ASSERT(sd1 != sd2);
    CPPUNIT_ASSERT(sd1 != sd3);

    data_array.deleteDimension(d.id());
    data_array.deleteDimension(d2.id());
}

void TestDimension::testSetDimLabels() {
    std::vector<std::string> labels = {"label_a", "label_b","label_c","label_d","label_e"};
    std::vector<std::string> new_labels = {"new label_a", "new label_b","new label_c"};

    Dimension d = data_array.appendSetDimension();
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Set);

    SetDimension sd;
    sd = d;
    CPPUNIT_ASSERT(sd);
    sd.labels(labels);
    std::vector<std::string> retrieved_labels = sd.labels();
    CPPUNIT_ASSERT(retrieved_labels.size() == labels.size());
    for (size_t i = 0; i < labels.size(); i++){
        CPPUNIT_ASSERT(labels[i] == retrieved_labels[i]);
    }

    sd.labels(new_labels);
    retrieved_labels = sd.labels();
    CPPUNIT_ASSERT(retrieved_labels.size() == new_labels.size());
    for (size_t i = 0; i < new_labels.size(); i++){
        CPPUNIT_ASSERT(new_labels[i] == retrieved_labels[i]);
    }

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeDimLabel() {
    std::string label = "aLabel";
    std::string other_label = "anotherLabel";
    std::vector<double> ticks;
    for (size_t i = 0; i < 5; i++) {
        ticks.push_back(i * boost::math::constants::pi<double>());
    }

    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    rd.label(label);
    CPPUNIT_ASSERT(*(rd.label()) == label);
    rd.label(other_label);
    CPPUNIT_ASSERT(*(rd.label()) == other_label);

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeDimUnit() {
    std::string invalidUnit = "invalidunit";
    std::string validUnit = "ms";

    std::vector<double> ticks;
    for (size_t i = 0; i < 5; i++) {
        ticks.push_back(i * boost::math::constants::pi<double>());
    }
    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    CPPUNIT_ASSERT_THROW(rd.unit(invalidUnit), nix::InvalidUnit);
    CPPUNIT_ASSERT_NO_THROW(rd.unit(validUnit));
    CPPUNIT_ASSERT(*(rd.unit()) == validUnit);
    CPPUNIT_ASSERT_NO_THROW(rd.unit(boost::none));
    CPPUNIT_ASSERT(*(rd.unit()) == "");

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeTicks() {
    std::vector<double> ticks = {1.0, 2.0, 3.4, 42.0};
    std::vector<double> new_ticks = {-100.0, -10.0, 0.0, 10.0, 100.0};
    std::vector<double> unordered_ticks = {-20.0, -100.0, 10.0, -10.0, 0.0};
    std::vector<double> double_ticks = {-20.0, -10.0, 10.0, -10.0, -20.0};

    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    CPPUNIT_ASSERT(rd.ticks().size() == ticks.size());
    std::vector<double> retrieved_ticks = rd.ticks();
    CPPUNIT_ASSERT(retrieved_ticks.size() == ticks.size());
    for (size_t i = 0; i < ticks.size(); i++){
        CPPUNIT_ASSERT(ticks[i] == retrieved_ticks[i]);
    }
    CPPUNIT_ASSERT_THROW(rd.ticks(unordered_ticks), nix::UnsortedTicks);
    CPPUNIT_ASSERT_THROW(rd.ticks(double_ticks), nix::UnsortedTicks);
    rd.ticks(new_ticks);
    retrieved_ticks = rd.ticks();
    CPPUNIT_ASSERT(retrieved_ticks.size() == new_ticks.size());
    for (size_t i = 0; i < new_ticks.size(); i++){
        CPPUNIT_ASSERT(new_ticks[i] == retrieved_ticks[i]);
    }

    data_array.deleteDimension(d.id());
}
<commit_msg>[test] RangeDimension::unit: fix test for none return case<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in Section and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <ctime>

#include "TestDimension.hpp"
#include <nix/util/util.hpp>

using namespace std;
using namespace nix;

void TestDimension::setUp() {
    file = File::open("test_dimension.h5", FileMode::Overwrite);
    block = file.createBlock("dimensionTest","test");
    data_array = block.createDataArray("dimensionTest", "Test");
}

void TestDimension::tearDown() {
    file.deleteBlock(block.id());
    file.close();
}

void TestDimension::testId() {
    Dimension sd = data_array.appendSetDimension();
    CPPUNIT_ASSERT(data_array.dimensionCount() == 1 && sd.id() == 1);
    data_array.deleteDimension(sd.id());
    CPPUNIT_ASSERT(data_array.dimensionCount() == 0);
}

void TestDimension::testSampledDimLabel() {
    std::string label = "aLabel";
    std::string other_label = "anotherLabel";
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    sd.label(label);
    CPPUNIT_ASSERT(*(sd.label()) == label);
    sd.label(other_label);
    CPPUNIT_ASSERT(*(sd.label()) == other_label);
    CPPUNIT_ASSERT_NO_THROW(sd.label(nix::none));
    CPPUNIT_ASSERT(sd.label() == nix::none);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimUnit() {
    std::string invalidUnit = "invalidunit";
    std::string validUnit = "mV^2";
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT_THROW(sd.unit(invalidUnit), nix::InvalidUnit);
    CPPUNIT_ASSERT_NO_THROW(sd.unit(validUnit));
    CPPUNIT_ASSERT(*(sd.unit()) == validUnit);
    CPPUNIT_ASSERT_NO_THROW(sd.unit(boost::none));
    CPPUNIT_ASSERT(sd.unit() == boost::none);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimSamplingInterval() {
    double impossible_sampling_interval = -1.0;
    double invalid_sampling_interval = 0.0;
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT(sd.samplingInterval() == boost::math::constants::pi<double>());
    CPPUNIT_ASSERT_THROW(sd.samplingInterval(impossible_sampling_interval), std::runtime_error);
    CPPUNIT_ASSERT_THROW(sd.samplingInterval(invalid_sampling_interval), std::runtime_error);
    CPPUNIT_ASSERT_NO_THROW(sd.samplingInterval(samplingInterval));
    CPPUNIT_ASSERT(sd.samplingInterval() == samplingInterval);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimOffset() {
    double offset = 1.0;
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd;
    sd = d;
    CPPUNIT_ASSERT_NO_THROW(sd.offset(offset));
    CPPUNIT_ASSERT(*(sd.offset()) == offset);
    CPPUNIT_ASSERT_NO_THROW(sd.offset(boost::none));
    CPPUNIT_ASSERT(sd.offset() == 0);

    data_array.deleteDimension(d.id());
}

void TestDimension::testSampledDimOperators() {
    double samplingInterval = boost::math::constants::pi<double>();

    Dimension d = data_array.appendSampledDimension(samplingInterval);
    Dimension d2 = data_array.appendSampledDimension(samplingInterval);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Sample);
    CPPUNIT_ASSERT(d2.dimensionType() == nix::DimensionType::Sample);

    SampledDimension sd1, sd2, sd3;
    sd1 = d;
    sd2 = d2;
    sd3 = data_array.getDimension(d.id());
    CPPUNIT_ASSERT(sd1.id() == d.id() && sd2.id() == d2.id());
    CPPUNIT_ASSERT(sd1 != sd2);
    CPPUNIT_ASSERT(sd1 != sd3);

    data_array.deleteDimension(d.id());
    data_array.deleteDimension(d2.id());
}

void TestDimension::testSetDimLabels() {
    std::vector<std::string> labels = {"label_a", "label_b","label_c","label_d","label_e"};
    std::vector<std::string> new_labels = {"new label_a", "new label_b","new label_c"};

    Dimension d = data_array.appendSetDimension();
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Set);

    SetDimension sd;
    sd = d;
    CPPUNIT_ASSERT(sd);
    sd.labels(labels);
    std::vector<std::string> retrieved_labels = sd.labels();
    CPPUNIT_ASSERT(retrieved_labels.size() == labels.size());
    for (size_t i = 0; i < labels.size(); i++){
        CPPUNIT_ASSERT(labels[i] == retrieved_labels[i]);
    }

    sd.labels(new_labels);
    retrieved_labels = sd.labels();
    CPPUNIT_ASSERT(retrieved_labels.size() == new_labels.size());
    for (size_t i = 0; i < new_labels.size(); i++){
        CPPUNIT_ASSERT(new_labels[i] == retrieved_labels[i]);
    }

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeDimLabel() {
    std::string label = "aLabel";
    std::string other_label = "anotherLabel";
    std::vector<double> ticks;
    for (size_t i = 0; i < 5; i++) {
        ticks.push_back(i * boost::math::constants::pi<double>());
    }

    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    rd.label(label);
    CPPUNIT_ASSERT(*(rd.label()) == label);
    rd.label(other_label);
    CPPUNIT_ASSERT(*(rd.label()) == other_label);

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeDimUnit() {
    std::string invalidUnit = "invalidunit";
    std::string validUnit = "ms";

    std::vector<double> ticks;
    for (size_t i = 0; i < 5; i++) {
        ticks.push_back(i * boost::math::constants::pi<double>());
    }
    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    CPPUNIT_ASSERT_THROW(rd.unit(invalidUnit), nix::InvalidUnit);
    CPPUNIT_ASSERT_NO_THROW(rd.unit(validUnit));
    CPPUNIT_ASSERT(*(rd.unit()) == validUnit);
    CPPUNIT_ASSERT_NO_THROW(rd.unit(nix::none));
    CPPUNIT_ASSERT(rd.unit() == nix::none);

    data_array.deleteDimension(d.id());
}

void TestDimension::testRangeTicks() {
    std::vector<double> ticks = {1.0, 2.0, 3.4, 42.0};
    std::vector<double> new_ticks = {-100.0, -10.0, 0.0, 10.0, 100.0};
    std::vector<double> unordered_ticks = {-20.0, -100.0, 10.0, -10.0, 0.0};
    std::vector<double> double_ticks = {-20.0, -10.0, 10.0, -10.0, -20.0};

    Dimension d = data_array.appendRangeDimension(ticks);
    CPPUNIT_ASSERT(d.dimensionType() == nix::DimensionType::Range);

    RangeDimension rd;
    rd = d;
    CPPUNIT_ASSERT(rd.ticks().size() == ticks.size());
    std::vector<double> retrieved_ticks = rd.ticks();
    CPPUNIT_ASSERT(retrieved_ticks.size() == ticks.size());
    for (size_t i = 0; i < ticks.size(); i++){
        CPPUNIT_ASSERT(ticks[i] == retrieved_ticks[i]);
    }
    CPPUNIT_ASSERT_THROW(rd.ticks(unordered_ticks), nix::UnsortedTicks);
    CPPUNIT_ASSERT_THROW(rd.ticks(double_ticks), nix::UnsortedTicks);
    rd.ticks(new_ticks);
    retrieved_ticks = rd.ticks();
    CPPUNIT_ASSERT(retrieved_ticks.size() == new_ticks.size());
    for (size_t i = 0; i < new_ticks.size(); i++){
        CPPUNIT_ASSERT(new_ticks[i] == retrieved_ticks[i]);
    }

    data_array.deleteDimension(d.id());
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// black_scholes.cpp
// -----------------
//
// Computes a numerical solution to the Black-Scholes equation with constant
// coefficients.
//
// Author: Parsiad Azimzadeh
////////////////////////////////////////////////////////////////////////////////

#include <QuantPDE/Core>
#include <QuantPDE/Modules/Lambdas>
#include <QuantPDE/Modules/Operators>

using namespace QuantPDE;
using namespace QuantPDE::Modules;

///////////////////////////////////////////////////////////////////////////////

#include <iostream> // cout, cerr
#include <unistd.h> // getopt

using namespace std;

///////////////////////////////////////////////////////////////////////////////

/**
 * Prints help to stderr.
 */
void help() {
	cerr <<
"unequal_borrowing_lending_rates [OPTIONS]" << endl << endl <<
"Prices a long/short position straddle under the Black-Scholes model assuming" << endl <<
"unequal borrowing/lending rates." << endl <<
endl <<
"-d REAL" << endl <<
endl <<
"    Sets the dividend rate (default is 0.)." << endl <<
endl <<
"-K REAL" << endl <<
endl <<
"    Sets the strike price (default is 100.)." << endl <<
endl <<
"-r REAL" << endl <<
endl <<
"    Sets the interest rate (default is 0.04)." << endl <<
endl <<
"-R NONNEGATIVE_INTEGER" << endl <<
endl <<
"    Controls the coarseness of the grid, with 0 being coarsest (default is 0)." << endl <<
endl <<
"-t POSITIVE_REAL" << endl <<
endl <<
"    Sets the timestep size (default is 0.01)." << endl <<
endl <<
"-T POSITIVE_REAL" << endl <<
endl <<
"    Sets the expiry time (default is 1.)." << endl <<
endl <<
"-v REAL" << endl <<
endl <<
"    Sets the volatility (default is 0.2)." << endl << endl;
}

int main(int argc, char **argv) {

	Real K  = 100.;
	Real T  = 1.;
	Real r  = .04;
	Real v  = .2;
	Real q  = 0.;
	Real dt = .01;

	int R = 0;

	// Setting options with getopt
	{ char c;
	while((c = getopt(argc, argv, "d:hK:r:R:t:T:v:")) != -1) {
		switch(c) {
			case 'd':
				q = atof(optarg);
				break;
			case 'h':
				help();
				return 0;
			case 'K':
				K = atof(optarg);
				break;
			case 'r':
				r = atof(optarg);
				break;
			case 'R':
				R = atoi(optarg);
				if(R < 0) {
					cerr <<
"error: the maximum level of refinement must be nonnegative" << endl;
					return 1;
				}
				break;
			case 't':
				dt = atof(optarg);
				if(dt <= 0.) {
					cerr <<
"error: the timestep size must be positive" << endl;
					return 1;
				}
				break;
			case 'T':
				if((T = atof(optarg)) <= 0.) {
					cerr <<
"error: expiry time must be positive" << endl;
					return 1;
				}
				break;
			case 'v':
				v = atof(optarg);
				break;
			case ':':
			case '?':
				cerr << endl;
				help();
				return 1;
		}
	} }

	////////////////////////////////////////////////////////////////////////
	// Spatial grid
	////////////////////////////////////////////////////////////////////////

	RectilinearGrid1 grid(
		Axis {
			0., 10., 20., 30., 40., 50., 60., 70.,
			75., 80.,
			84., 88., 92.,
			94., 96., 98., 100., 102., 104., 106., 108., 110.,
			114., 118.,
			123.,
			130., 140., 150.,
			175.,
			225.,
			300.,
			750.,
			2000.,
			10000.
		}
	);

	// Refine grid
	for(int i = 0; i < R; ++i) {
		grid = grid.refined();
	}

	////////////////////////////////////////////////////////////////////////
	// Payoff
	// ------
	//
	// Payoffs are lambda functions. The following is equivalent to
	//
	// auto payoff = [K] (Real S) {
	// 	return S < K ? K - S : 0.;
	// };
	////////////////////////////////////////////////////////////////////////

	auto payoff = putPayoff(K);

	////////////////////////////////////////////////////////////////////////
	// Iteration tree
	// --------------
	//
	// Sets up the loop structure:
	// for(int n = 0; n < N; ++n) {
	// 	// Solve a linear system
	// }
	////////////////////////////////////////////////////////////////////////

	ReverseConstantStepper stepper(
		0., // Initial time
		T,  // Expiry time
		dt  // Timestep size
	);

	////////////////////////////////////////////////////////////////////////
	// Linear system tree
	// ------------------
	//
	// Makes the linear system to solve at each iteration
	////////////////////////////////////////////////////////////////////////

	BlackScholes1 bs(
		grid,

		r, // Interest
		v, // Volatility
		q  // Dividend rate
	);

	ReverseBDFTwo1 bdf2(grid, bs);
	bdf2.setIteration(stepper);

	////////////////////////////////////////////////////////////////////////
	// Running
	// -------
	//
	// Everything prior to this was setup. Now we run the method.
	////////////////////////////////////////////////////////////////////////

	BiCGSTABSolver solver;

	auto V = stepper.solve(
		grid,   // Domain
		payoff, // Initial condition
		bdf2,   // Root of linear system tree
		solver  // Linear system solver
	);

	////////////////////////////////////////////////////////////////////////
	// Print solution
	////////////////////////////////////////////////////////////////////////

	RectilinearGrid1 printGrid( Axis::range(0., 10., 200.) );
	cout << accessor( printGrid, V );

	return 0;

}
<commit_msg>Fixed typo in black_scholes.cpp.<commit_after>////////////////////////////////////////////////////////////////////////////////
// black_scholes.cpp
// -----------------
//
// Computes a numerical solution to the Black-Scholes equation with constant
// coefficients.
//
// Author: Parsiad Azimzadeh
////////////////////////////////////////////////////////////////////////////////

#include <QuantPDE/Core>
#include <QuantPDE/Modules/Lambdas>
#include <QuantPDE/Modules/Operators>

using namespace QuantPDE;
using namespace QuantPDE::Modules;

///////////////////////////////////////////////////////////////////////////////

#include <iostream> // cout, cerr
#include <unistd.h> // getopt

using namespace std;

///////////////////////////////////////////////////////////////////////////////

/**
 * Prints help to stderr.
 */
void help() {
	cerr <<
"unequal_borrowing_lending_rates [OPTIONS]" << endl << endl <<
"Prices a long/short position put under the Black-Scholes model assuming" << endl <<
"unequal borrowing/lending rates." << endl <<
endl <<
"-d REAL" << endl <<
endl <<
"    Sets the dividend rate (default is 0.)." << endl <<
endl <<
"-K REAL" << endl <<
endl <<
"    Sets the strike price (default is 100.)." << endl <<
endl <<
"-r REAL" << endl <<
endl <<
"    Sets the interest rate (default is 0.04)." << endl <<
endl <<
"-R NONNEGATIVE_INTEGER" << endl <<
endl <<
"    Controls the coarseness of the grid, with 0 being coarsest (default is 0)." << endl <<
endl <<
"-t POSITIVE_REAL" << endl <<
endl <<
"    Sets the timestep size (default is 0.01)." << endl <<
endl <<
"-T POSITIVE_REAL" << endl <<
endl <<
"    Sets the expiry time (default is 1.)." << endl <<
endl <<
"-v REAL" << endl <<
endl <<
"    Sets the volatility (default is 0.2)." << endl << endl;
}

int main(int argc, char **argv) {

	Real K  = 100.;
	Real T  = 1.;
	Real r  = .04;
	Real v  = .2;
	Real q  = 0.;
	Real dt = .01;

	int R = 0;

	// Setting options with getopt
	{ char c;
	while((c = getopt(argc, argv, "d:hK:r:R:t:T:v:")) != -1) {
		switch(c) {
			case 'd':
				q = atof(optarg);
				break;
			case 'h':
				help();
				return 0;
			case 'K':
				K = atof(optarg);
				break;
			case 'r':
				r = atof(optarg);
				break;
			case 'R':
				R = atoi(optarg);
				if(R < 0) {
					cerr <<
"error: the maximum level of refinement must be nonnegative" << endl;
					return 1;
				}
				break;
			case 't':
				dt = atof(optarg);
				if(dt <= 0.) {
					cerr <<
"error: the timestep size must be positive" << endl;
					return 1;
				}
				break;
			case 'T':
				if((T = atof(optarg)) <= 0.) {
					cerr <<
"error: expiry time must be positive" << endl;
					return 1;
				}
				break;
			case 'v':
				v = atof(optarg);
				break;
			case ':':
			case '?':
				cerr << endl;
				help();
				return 1;
		}
	} }

	////////////////////////////////////////////////////////////////////////
	// Spatial grid
	////////////////////////////////////////////////////////////////////////

	RectilinearGrid1 grid(
		Axis {
			0., 10., 20., 30., 40., 50., 60., 70.,
			75., 80.,
			84., 88., 92.,
			94., 96., 98., 100., 102., 104., 106., 108., 110.,
			114., 118.,
			123.,
			130., 140., 150.,
			175.,
			225.,
			300.,
			750.,
			2000.,
			10000.
		}
	);

	// Refine grid
	for(int i = 0; i < R; ++i) {
		grid = grid.refined();
	}

	////////////////////////////////////////////////////////////////////////
	// Payoff
	// ------
	//
	// Payoffs are lambda functions. The following is equivalent to
	//
	// auto payoff = [K] (Real S) {
	// 	return S < K ? K - S : 0.;
	// };
	////////////////////////////////////////////////////////////////////////

	auto payoff = putPayoff(K);

	////////////////////////////////////////////////////////////////////////
	// Iteration tree
	// --------------
	//
	// Sets up the loop structure:
	// for(int n = 0; n < N; ++n) {
	// 	// Solve a linear system
	// }
	////////////////////////////////////////////////////////////////////////

	ReverseConstantStepper stepper(
		0., // Initial time
		T,  // Expiry time
		dt  // Timestep size
	);

	////////////////////////////////////////////////////////////////////////
	// Linear system tree
	// ------------------
	//
	// Makes the linear system to solve at each iteration
	////////////////////////////////////////////////////////////////////////

	BlackScholes1 bs(
		grid,

		r, // Interest
		v, // Volatility
		q  // Dividend rate
	);

	ReverseBDFTwo1 bdf2(grid, bs);
	bdf2.setIteration(stepper);

	////////////////////////////////////////////////////////////////////////
	// Running
	// -------
	//
	// Everything prior to this was setup. Now we run the method.
	////////////////////////////////////////////////////////////////////////

	BiCGSTABSolver solver;

	auto V = stepper.solve(
		grid,   // Domain
		payoff, // Initial condition
		bdf2,   // Root of linear system tree
		solver  // Linear system solver
	);

	////////////////////////////////////////////////////////////////////////
	// Print solution
	////////////////////////////////////////////////////////////////////////

	RectilinearGrid1 printGrid( Axis::range(0., 10., 200.) );
	cout << accessor( printGrid, V );

	return 0;

}
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2010-2012 The PECOS Development Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the Version 2 GNU General
// Public License as published by the Free Software Foundation.
//
// 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 library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA
//
//-----------------------------------------------------------------------el-
//
// $Id$
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

// libMesh
#include "getpot.h"

// GRINS
#include "cea_thermo.h"

double cp( double T, double a0, double a1, double a2,
	   double a3, double a4, double a5, double a6 );

int test_cp( const std::string& species_name, unsigned int species, double cp_exact, double T,
	     const GRINS::CEAThermodynamics& thermo );

int main()
{
  std::vector<std::string> species_str_list;
  const unsigned int n_species = 5;
  species_str_list.reserve(n_species);
  species_str_list.push_back( "N2" );
  species_str_list.push_back( "O2" );
  species_str_list.push_back( "N" );
  species_str_list.push_back( "O" );
  species_str_list.push_back( "NO" );

  GRINS::ChemicalMixture chem_mixture( species_str_list );

  GRINS::CEAThermodynamics thermo( GetPot(), chem_mixture );

  const double P = 100000.0;
  const std::vector<double> mass_fractions( 5, 0.2 );
  const double T1 = 190.0;
  const double T2 = 1500.0;
  const double T3 = 10000.0;

  const double R_N2 = GRINS::Constants::R_universal/28.016;
  const double R_exact = GRINS::Constants::R_universal*( 0.2/28.016 + 0.2/32.0 + 0.2/14.008 + 0.2/16.0 + 0.2/30.008 );

  int return_flag = 0;

  // Test N2 cp
  {
    const double cp_N2_1 = R_N2*cp( T1, 2.21037122e+04, -3.81846145e+02, 6.08273815e+00,
				    -8.53091381e-03,  1.38464610e-05, -9.62579293e-09,  2.51970560e-12);

    const double cp_N2_2 = R_N2*cp( T2, 5.87709908e+05, -2.23924255e+03,  6.06694267e+00,
				    -6.13965296e-04, 1.49179819e-07, -1.92309442e-11, 1.06194871e-15 );

    const double cp_N2_3 = R_N2*cp( T3, 8.30971200e+08, -6.42048187e+05,  2.02020507e+02, -3.06501961e-02,
				    2.48685558e-06, -9.70579208e-11, 1.43751673e-15);

    const GRINS::Species species = chem_mixture.species_list()[0];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, 0, cp_N2_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, 0, cp_N2_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, 0, cp_N2_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  return return_flag;
}

int test_cp( const std::string& species_name, unsigned int species, double cp_exact, double T,
	     const GRINS::CEAThermodynamics& thermo )
{
  int return_flag = 0;

  const double tol = 1.0e-15;

  const double cp = thermo.cp(T, species);

  if( std::fabs( (cp_exact - cp)/cp_exact ) > tol )
    {
      std::cerr << "Error: Mismatch in species specific heat." << std::endl
		<< "species = " << species_name << std::endl
		<< "cp       = " << cp << std::endl
		<< "cp_exact = " << cp_exact << std::endl
		<< "T = " << T << std::endl;
      return_flag = 1;
    }

  return return_flag;
}

double cp( double T, double a0, double a1, double a2,
	   double a3, double a4, double a5, double a6 )
{
  if( T < 200.1)
    T = 200.1;

  return a0/(T*T) + a1/T + a2 + a3*T + a4*(T*T) + a5*(T*T*T) + a6*(T*T*T*T);
}
<commit_msg>[GRINS]: Finished up the CEA thermo test (#2600).<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2010-2012 The PECOS Development Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the Version 2 GNU General
// Public License as published by the Free Software Foundation.
//
// 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 library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA
//
//-----------------------------------------------------------------------el-
//
// $Id$
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

// libMesh
#include "getpot.h"

// GRINS
#include "cea_thermo.h"

double cp( double T, double a0, double a1, double a2,
	   double a3, double a4, double a5, double a6 );

int test_cp( const std::string& species_name, unsigned int species, double cp_exact, double T,
	     const GRINS::CEAThermodynamics& thermo );

int main()
{
  std::vector<std::string> species_str_list;
  const unsigned int n_species = 5;
  species_str_list.reserve(n_species);
  species_str_list.push_back( "N2" );
  species_str_list.push_back( "O2" );
  species_str_list.push_back( "N" );
  species_str_list.push_back( "O" );
  species_str_list.push_back( "NO" );

  GRINS::ChemicalMixture chem_mixture( species_str_list );

  GRINS::CEAThermodynamics thermo( GetPot(), chem_mixture );

  const double P = 100000.0;
  const std::vector<double> mass_fractions( 5, 0.2 );
  const double T1 = 190.0;
  const double T2 = 1500.0;
  const double T3 = 10000.0;

  const double R_N2 = GRINS::Constants::R_universal/28.016;
  const double R_O2 = GRINS::Constants::R_universal/32.0;
  const double R_N = GRINS::Constants::R_universal/14.008;
  const double R_O = GRINS::Constants::R_universal/16.0;
  const double R_NO = GRINS::Constants::R_universal/30.008;
  const double R_exact = mass_fractions[0]*R_N2 + mass_fractions[1]*R_O2 + mass_fractions[2]*R_N +
    mass_fractions[3]*R_O + mass_fractions[4]*R_NO;

  int return_flag = 0;

  // Test N2 cp
  {
    unsigned int index = 0;
    const double cp_N2_1 = R_N2*cp( T1, 2.21037122e+04, -3.81846145e+02, 6.08273815e+00,
				    -8.53091381e-03,  1.38464610e-05, -9.62579293e-09,  2.51970560e-12);

    const double cp_N2_2 = R_N2*cp( T2, 5.87709908e+05, -2.23924255e+03,  6.06694267e+00,
				    -6.13965296e-04, 1.49179819e-07, -1.92309442e-11, 1.06194871e-15 );

    const double cp_N2_3 = R_N2*cp( T3, 8.30971200e+08, -6.42048187e+05,  2.02020507e+02, -3.06501961e-02,
				    2.48685558e-06, -9.70579208e-11, 1.43751673e-15);

    const GRINS::Species species = chem_mixture.species_list()[index];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, index, cp_N2_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_N2_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_N2_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  // Test O2 cp
  {
    unsigned int index = 1;
    const double cp_1 = R_O2*cp( T1, -3.42556269e+04, 4.84699986e+02, 1.11901159e+00,
				 4.29388743e-03, -6.83627313e-07, -2.02337478e-09 , 1.03904064e-12 );

    const double cp_2 = R_O2*cp( T2, -1.03793994e+06, 2.34483275e+03, 1.81972949e+00, 1.26784887e-03,
				 -2.18807142e-07, 2.05372411e-11, -8.19349062e-16 );

    const double cp_3 = R_O2*cp( T3, 4.97515261e+08, -2.86602339e+05, 6.69015464e+01, -6.16971869e-03,
				 3.01623757e-07, -7.42087888e-12, 7.27744063e-17);

    const GRINS::Species species = chem_mixture.species_list()[index];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, index, cp_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  // Test N cp
  {
    unsigned int index = 2;
    const double cp_1 = R_N*cp( T1, 0.00000000e+00, 0.00000000e+00, 2.50000000e+00, 0.00000000e+00,
				0.00000000e+00, 0.00000000e+00, 0.00000000e+00);

    const double cp_2 = R_N*cp( T2, 8.87650138e+04, -1.07123150e+02, 2.36218829e+00, 2.91672008e-04,
				-1.72951510e-07, 4.01265788e-11, -2.67722757e-15 );

    const double cp_3 = R_N*cp( T3, 5.47518105e+08, -3.10757498e+05, 6.91678274e+01, -6.84798813e-03,
				3.82757240e-07, -1.09836771e-11, 1.27798602e-16);

    const GRINS::Species species = chem_mixture.species_list()[index];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, index, cp_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  // Test O cp
  {
    unsigned int index = 3;
    const double cp_1 = R_O*cp( T1, -7.95361130e+03 , 1.60717779e+02, 1.96622644e+00, 1.01367031e-03,
				-1.11041542e-06, 6.51750750e-10, -1.58477925e-13 );

    const double cp_2 = R_O*cp( T2, 2.61902026e+05, -7.29872203e+02, 3.31717727e+00, -4.28133436e-04,
				1.03610459e-07, -9.43830433e-12, 2.72503830e-16 );

    const double cp_3 = R_O*cp( T3, 1.77900426e+08, -1.08232826e+05, 2.81077837e+01, -2.97523226e-03,
				1.85499753e-07, -5.79623154e-12, 7.19172016e-17 );

    const GRINS::Species species = chem_mixture.species_list()[index];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, index, cp_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  // Test NO cp
  {
    unsigned int index = 4;
    const double cp_1 = R_NO*cp( T1, -1.14391658e+04, 1.53646774e+02, 3.43146865e+00, -2.66859213e-03,
				 8.48139877e-06, -7.68511079e-09, 2.38679758e-12 );

    const double cp_2 = R_NO*cp( T2, 2.23903708e+05, -1.28965624e+03, 5.43394039e+00, -3.65605546e-04,
				 9.88101763e-08, -1.41608327e-11, 9.38021642e-16 );

    const double cp_3 = R_NO*cp( T3, -9.57530764e+08, 5.91243671e+05, -1.38456733e+02, 1.69433998e-02,
				 -1.00735146e-06, 2.91258526e-11, -3.29511091e-16 );

    const GRINS::Species species = chem_mixture.species_list()[index];
    const std::string species_name = chem_mixture.species_inverse_name_map().find(species)->second;

    int return_flag_temp = 0;
    return_flag_temp = test_cp( species_name, index, cp_1, T1, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_2, T2, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;

    return_flag = test_cp( species_name, index, cp_3, T3, thermo );
    if( return_flag_temp != 0 ) return_flag = 1;
  }

  return return_flag;
}

int test_cp( const std::string& species_name, unsigned int species, double cp_exact, double T,
	     const GRINS::CEAThermodynamics& thermo )
{
  int return_flag = 0;

  const double tol = 1.0e-15;

  const double cp = thermo.cp(T, species);

  if( std::fabs( (cp_exact - cp)/cp_exact ) > tol )
    {
      std::cerr << "Error: Mismatch in species specific heat." << std::endl
		<< "species = " << species_name << std::endl
		<< "cp       = " << cp << std::endl
		<< "cp_exact = " << cp_exact << std::endl
		<< "T = " << T << std::endl;
      return_flag = 1;
    }

  return return_flag;
}

double cp( double T, double a0, double a1, double a2,
	   double a3, double a4, double a5, double a6 )
{
  if( T < 200.1)
    T = 200.1;

  return a0/(T*T) + a1/T + a2 + a3*T + a4*(T*T) + a5*(T*T*T) + a6*(T*T*T*T);
}
<|endoftext|>


<commit_before>#include <fastdds/rtps/writer/LivelinessManager.h>
#include <fastrtps/log/Log.h>

#include <algorithm>

using namespace std::chrono;

namespace eprosima {
namespace fastrtps {
namespace rtps {

using LivelinessDataIterator = ResourceLimitedVector<LivelinessData>::iterator;

LivelinessManager::LivelinessManager(
        const LivelinessCallback& callback,
        ResourceEvent& service,
        bool manage_automatic)
    : callback_(callback)
    , manage_automatic_(manage_automatic)
    , writers_()
    , mutex_()
    , timer_owner_(nullptr)
    , timer_(
        service,
        [this](TimedEvent::EventCode code) -> bool
            {
                if (TimedEvent::EVENT_SUCCESS == code)
                {
                    return timer_expired();
                }

                return false;
            },
        0)
{
}

LivelinessManager::~LivelinessManager()
{
    std::unique_lock<std::mutex> lock(mutex_);
    timer_owner_ = nullptr;
    timer_.cancel_timer();
}

bool LivelinessManager::add_writer(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (!manage_automatic_ && kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        logWarning(RTPS_WRITER, "Liveliness manager not managing automatic writers, writer not added");
        return false;
    }

    for (LivelinessData& writer : writers_)
    {
        if (writer.guid == guid &&
                writer.kind == kind &&
                writer.lease_duration == lease_duration)
        {
            writer.count++;
            return true;
        }
    }
    writers_.emplace_back(guid, kind, lease_duration);
    return true;
}

bool LivelinessManager::remove_writer(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    for (LivelinessData& writer: writers_)
    {
        if (writer.guid == guid &&
                writer.kind == kind &&
                writer.lease_duration == lease_duration)
        {
            if (--writer.count == 0)
            {
                writers_.remove(writer);

                if (callback_ != nullptr)
                {
                    if (writer.status == LivelinessData::WriterStatus::ALIVE)
                    {
                        callback_(writer.guid,
                                writer.kind,
                                writer.lease_duration,
                                -1,
                                0);
                    }
                    else if (writer.status == LivelinessData::WriterStatus::NOT_ALIVE)
                    {
                        callback_(writer.guid,
                                writer.kind,
                                writer.lease_duration,
                                0,
                                -1);
                    }
                }

                if (timer_owner_ != nullptr && timer_owner_->guid == guid)
                {
                    timer_owner_ = nullptr;
                    if (!calculate_next())
                    {
                        timer_.cancel_timer();
                        return true;
                    }

                    // Some times the interval could be negative if a writer expired during the call to this function
                    // Once in this situation there is not much we can do but let asio timers expire inmediately
                    auto interval = timer_owner_->time - steady_clock::now();
                    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
                    timer_.restart_timer();
                }
                return true;
            }
        }
    }

    return false;
}

bool LivelinessManager::assert_liveliness(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    ResourceLimitedVector<LivelinessData>::iterator wit;
    if (!find_writer(
                guid,
                kind,
                lease_duration,
                &wit))
    {
        return false;
    }

    timer_.cancel_timer();

    if (wit->kind == LivelinessQosPolicyKind::MANUAL_BY_PARTICIPANT_LIVELINESS_QOS ||
            wit->kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        for (LivelinessData& w: writers_)
        {
            if (w.kind == wit->kind)
            {
                assert_writer_liveliness(w);
            }
        }
    }
    else if (wit->kind == LivelinessQosPolicyKind::MANUAL_BY_TOPIC_LIVELINESS_QOS)
    {
        assert_writer_liveliness(*wit);
    }

    // Updates the timer owner
    if (!calculate_next())
    {
        logError(RTPS_WRITER, "Error when restarting liveliness timer");
        return false;
    }

    // Some times the interval could be negative if a writer expired during the call to this function
    // Once in this situation there is not much we can do but let asio timers expire inmediately
    auto interval = timer_owner_->time - steady_clock::now();
    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
    timer_.restart_timer();

    return true;
}

bool LivelinessManager::assert_liveliness(
        LivelinessQosPolicyKind kind)
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (!manage_automatic_ && kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        logWarning(RTPS_WRITER, "Liveliness manager not managing automatic writers, writer not added");
        return false;
    }

    if (writers_.empty())
    {
        return true;
    }

    timer_.cancel_timer();

    for (LivelinessData& writer: writers_)
    {
        if (writer.kind == kind)
        {
            assert_writer_liveliness(writer);
        }
    }

    // Updates the timer owner
    if (!calculate_next())
    {
        logError(RTPS_WRITER,
                "Error when restarting liveliness timer: " << writers_.size() << " writers, liveliness " <<
                kind);
        return false;
    }

    // Some times the interval could be negative if a writer expired during the call to this function
    // Once in this situation there is not much we can do but let asio timers expire inmediately
    auto interval = timer_owner_->time - steady_clock::now();
    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
    timer_.restart_timer();

    return true;
}

bool LivelinessManager::calculate_next()
{

    timer_owner_ = nullptr;

    steady_clock::time_point min_time = steady_clock::now() + nanoseconds(c_TimeInfinite.to_ns());

    bool any_alive = false;

    for (LivelinessDataIterator it=writers_.begin(); it!=writers_.end(); ++it)
    {
        if (it->status == LivelinessData::WriterStatus::ALIVE)
        {
            if (it->time < min_time)
            {
                min_time = it->time;
                timer_owner_ = &*it;
            }
            any_alive = true;
        }
    }
    return any_alive;
}

bool LivelinessManager::timer_expired()
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (timer_owner_ == nullptr)
    {
        logError(RTPS_WRITER, "Liveliness timer expired but there is no writer");
        return false;
    }

    if (callback_ != nullptr)
    {
        callback_(timer_owner_->guid,
                timer_owner_->kind,
                timer_owner_->lease_duration,
                -1,
                1);
    }
    timer_owner_->status = LivelinessData::WriterStatus::NOT_ALIVE;

    if (calculate_next())
    {
        // Some times the interval could be negative if a writer expired during the call to this function
        // Once in this situation there is not much we can do but let asio timers expire inmediately
        auto interval = timer_owner_->time - steady_clock::now();
        timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
        return true;
    }

    return false;
}

bool LivelinessManager::find_writer(
        const GUID_t& guid,
        const LivelinessQosPolicyKind& kind,
        const Duration_t& lease_duration,
        ResourceLimitedVector<LivelinessData>::iterator* wit_out)
{
    for (LivelinessDataIterator it=writers_.begin(); it!=writers_.end(); ++it)
    {
        if (it->guid == guid &&
                it->kind == kind &&
                it->lease_duration == lease_duration)
        {
            *wit_out = it;
            return true;
        }
    }
    return false;
}

bool LivelinessManager::is_any_alive(
        LivelinessQosPolicyKind kind)
{
    std::unique_lock<std::mutex> lock(mutex_);

    for (const auto& writer : writers_)
    {
        if (writer.kind == kind && writer.status == LivelinessData::WriterStatus::ALIVE)
        {
            return true;
        }
    }
    return false;
}

void LivelinessManager::assert_writer_liveliness(
        LivelinessData& writer)
{
    if (callback_ != nullptr)
    {
        if (writer.status == LivelinessData::WriterStatus::NOT_ASSERTED)
        {
            callback_(writer.guid,
                    writer.kind,
                    writer.lease_duration,
                    1,
                    0);
        }
        else if (writer.status == LivelinessData::WriterStatus::NOT_ALIVE)
        {
            callback_(writer.guid,
                    writer.kind,
                    writer.lease_duration,
                    1,
                    -1);
        }
    }

    writer.status = LivelinessData::WriterStatus::ALIVE;
    writer.time = steady_clock::now() + nanoseconds(writer.lease_duration.to_ns());
}

const ResourceLimitedVector<LivelinessData>& LivelinessManager::get_liveliness_data() const
{
    return writers_;
}

}
}
}
<commit_msg>Removing a logError in favor of a logInfo. (#876)<commit_after>#include <fastdds/rtps/writer/LivelinessManager.h>
#include <fastrtps/log/Log.h>

#include <algorithm>

using namespace std::chrono;

namespace eprosima {
namespace fastrtps {
namespace rtps {

using LivelinessDataIterator = ResourceLimitedVector<LivelinessData>::iterator;

LivelinessManager::LivelinessManager(
        const LivelinessCallback& callback,
        ResourceEvent& service,
        bool manage_automatic)
    : callback_(callback)
    , manage_automatic_(manage_automatic)
    , writers_()
    , mutex_()
    , timer_owner_(nullptr)
    , timer_(
        service,
        [this](TimedEvent::EventCode code) -> bool
            {
                if (TimedEvent::EVENT_SUCCESS == code)
                {
                    return timer_expired();
                }

                return false;
            },
        0)
{
}

LivelinessManager::~LivelinessManager()
{
    std::unique_lock<std::mutex> lock(mutex_);
    timer_owner_ = nullptr;
    timer_.cancel_timer();
}

bool LivelinessManager::add_writer(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (!manage_automatic_ && kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        logWarning(RTPS_WRITER, "Liveliness manager not managing automatic writers, writer not added");
        return false;
    }

    for (LivelinessData& writer : writers_)
    {
        if (writer.guid == guid &&
                writer.kind == kind &&
                writer.lease_duration == lease_duration)
        {
            writer.count++;
            return true;
        }
    }
    writers_.emplace_back(guid, kind, lease_duration);
    return true;
}

bool LivelinessManager::remove_writer(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    for (LivelinessData& writer: writers_)
    {
        if (writer.guid == guid &&
                writer.kind == kind &&
                writer.lease_duration == lease_duration)
        {
            if (--writer.count == 0)
            {
                writers_.remove(writer);

                if (callback_ != nullptr)
                {
                    if (writer.status == LivelinessData::WriterStatus::ALIVE)
                    {
                        callback_(writer.guid,
                                writer.kind,
                                writer.lease_duration,
                                -1,
                                0);
                    }
                    else if (writer.status == LivelinessData::WriterStatus::NOT_ALIVE)
                    {
                        callback_(writer.guid,
                                writer.kind,
                                writer.lease_duration,
                                0,
                                -1);
                    }
                }

                if (timer_owner_ != nullptr && timer_owner_->guid == guid)
                {
                    timer_owner_ = nullptr;
                    if (!calculate_next())
                    {
                        timer_.cancel_timer();
                        return true;
                    }

                    // Some times the interval could be negative if a writer expired during the call to this function
                    // Once in this situation there is not much we can do but let asio timers expire inmediately
                    auto interval = timer_owner_->time - steady_clock::now();
                    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
                    timer_.restart_timer();
                }
                return true;
            }
        }
    }

    return false;
}

bool LivelinessManager::assert_liveliness(
        GUID_t guid,
        LivelinessQosPolicyKind kind,
        Duration_t lease_duration)
{
    std::unique_lock<std::mutex> lock(mutex_);

    ResourceLimitedVector<LivelinessData>::iterator wit;
    if (!find_writer(
                guid,
                kind,
                lease_duration,
                &wit))
    {
        return false;
    }

    timer_.cancel_timer();

    if (wit->kind == LivelinessQosPolicyKind::MANUAL_BY_PARTICIPANT_LIVELINESS_QOS ||
            wit->kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        for (LivelinessData& w: writers_)
        {
            if (w.kind == wit->kind)
            {
                assert_writer_liveliness(w);
            }
        }
    }
    else if (wit->kind == LivelinessQosPolicyKind::MANUAL_BY_TOPIC_LIVELINESS_QOS)
    {
        assert_writer_liveliness(*wit);
    }

    // Updates the timer owner
    if (!calculate_next())
    {
        logError(RTPS_WRITER, "Error when restarting liveliness timer");
        return false;
    }

    // Some times the interval could be negative if a writer expired during the call to this function
    // Once in this situation there is not much we can do but let asio timers expire inmediately
    auto interval = timer_owner_->time - steady_clock::now();
    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
    timer_.restart_timer();

    return true;
}

bool LivelinessManager::assert_liveliness(
        LivelinessQosPolicyKind kind)
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (!manage_automatic_ && kind == LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS)
    {
        logWarning(RTPS_WRITER, "Liveliness manager not managing automatic writers, writer not added");
        return false;
    }

    if (writers_.empty())
    {
        return true;
    }

    timer_.cancel_timer();

    for (LivelinessData& writer: writers_)
    {
        if (writer.kind == kind)
        {
            assert_writer_liveliness(writer);
        }
    }

    // Updates the timer owner
    if (!calculate_next())
    {
        logInfo(RTPS_WRITER,
                "Error when restarting liveliness timer: " << writers_.size() << " writers, liveliness " <<
                kind);
        return false;
    }

    // Some times the interval could be negative if a writer expired during the call to this function
    // Once in this situation there is not much we can do but let asio timers expire inmediately
    auto interval = timer_owner_->time - steady_clock::now();
    timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
    timer_.restart_timer();

    return true;
}

bool LivelinessManager::calculate_next()
{

    timer_owner_ = nullptr;

    steady_clock::time_point min_time = steady_clock::now() + nanoseconds(c_TimeInfinite.to_ns());

    bool any_alive = false;

    for (LivelinessDataIterator it=writers_.begin(); it!=writers_.end(); ++it)
    {
        if (it->status == LivelinessData::WriterStatus::ALIVE)
        {
            if (it->time < min_time)
            {
                min_time = it->time;
                timer_owner_ = &*it;
            }
            any_alive = true;
        }
    }
    return any_alive;
}

bool LivelinessManager::timer_expired()
{
    std::unique_lock<std::mutex> lock(mutex_);

    if (timer_owner_ == nullptr)
    {
        logError(RTPS_WRITER, "Liveliness timer expired but there is no writer");
        return false;
    }

    if (callback_ != nullptr)
    {
        callback_(timer_owner_->guid,
                timer_owner_->kind,
                timer_owner_->lease_duration,
                -1,
                1);
    }
    timer_owner_->status = LivelinessData::WriterStatus::NOT_ALIVE;

    if (calculate_next())
    {
        // Some times the interval could be negative if a writer expired during the call to this function
        // Once in this situation there is not much we can do but let asio timers expire inmediately
        auto interval = timer_owner_->time - steady_clock::now();
        timer_.update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
        return true;
    }

    return false;
}

bool LivelinessManager::find_writer(
        const GUID_t& guid,
        const LivelinessQosPolicyKind& kind,
        const Duration_t& lease_duration,
        ResourceLimitedVector<LivelinessData>::iterator* wit_out)
{
    for (LivelinessDataIterator it=writers_.begin(); it!=writers_.end(); ++it)
    {
        if (it->guid == guid &&
                it->kind == kind &&
                it->lease_duration == lease_duration)
        {
            *wit_out = it;
            return true;
        }
    }
    return false;
}

bool LivelinessManager::is_any_alive(
        LivelinessQosPolicyKind kind)
{
    std::unique_lock<std::mutex> lock(mutex_);

    for (const auto& writer : writers_)
    {
        if (writer.kind == kind && writer.status == LivelinessData::WriterStatus::ALIVE)
        {
            return true;
        }
    }
    return false;
}

void LivelinessManager::assert_writer_liveliness(
        LivelinessData& writer)
{
    if (callback_ != nullptr)
    {
        if (writer.status == LivelinessData::WriterStatus::NOT_ASSERTED)
        {
            callback_(writer.guid,
                    writer.kind,
                    writer.lease_duration,
                    1,
                    0);
        }
        else if (writer.status == LivelinessData::WriterStatus::NOT_ALIVE)
        {
            callback_(writer.guid,
                    writer.kind,
                    writer.lease_duration,
                    1,
                    -1);
        }
    }

    writer.status = LivelinessData::WriterStatus::ALIVE;
    writer.time = steady_clock::now() + nanoseconds(writer.lease_duration.to_ns());
}

const ResourceLimitedVector<LivelinessData>& LivelinessManager::get_liveliness_data() const
{
    return writers_;
}

}
}
}
<|endoftext|>


<commit_before>#include <bulk/bulk.hpp>
#include "bulk_test_common.hpp"
#include "set_backend.hpp"

extern environment env;

void test_communication() {
    env.spawn(env.available_processors(), [](auto& world) {
        int s = world.processor_id();
        int p = world.active_processors();

        BULK_SECTION("Put") {
            // test `put` to single variable
            bulk::var<int> a(world, 3);

            BULK_CHECK_ONCE(a.value() == 3, "correct initial value for variable");

            bulk::put(world.next_processor(), s, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == ((s + p - 1) % p),
                            "receive correct value after putting");
        }

        BULK_SECTION("Sugarized put") {
            // test `put` to single variable
            bulk::var<int> a(world);

            a(world.next_processor()) = s;
            world.sync();

            BULK_CHECK_ONCE(a.value() == ((s + p - 1) % p),
                            "receive correct value after sugarized putting");
        }

        BULK_SECTION("Put to self") {
            bulk::var<int> a(world);

            bulk::put(s, s, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == s,
                            "receive correct value after putting to self");
        }

        BULK_SECTION("Get from self") {
            bulk::var<int> a(world);
            a = s;
            auto b = bulk::get(s, a);
            world.sync();

            BULK_CHECK_ONCE(b.value() == s,
                            "receive correct value after getting from self");
        }

        BULK_SECTION("Put non-int") {
            // test `put` float to single variable
            bulk::var<float> a(world);

            bulk::put(world.next_processor(), 1.0f, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == 1.0f,
                            "receive correct value after putting float");
        }

        BULK_SECTION("Put multiple") {
            int size = 5;

            // test `put` to multiple variables
            std::vector<bulk::var<int>> xs;
            for (int i = 0; i < size; ++i) xs.emplace_back(world);

            for (int i = 0; i < size; ++i) {
                bulk::put(world.next_processor(), s + i, xs[i]);
            }

            world.sync();

            for (int i = 0; i < size; ++i) {
                BULK_CHECK_ONCE(xs[i].value() == ((s + p - 1) % p) + i,
                                "receive correct value after multiple puts to "
                                "array of variables");
            }
        }

        BULK_SECTION("Put unequal") {
            int size = 5;

            // test `put` to multiple variables
            std::vector<bulk::var<int>> xs;
            for (int i = 0; i < size; ++i) xs.emplace_back(world);

            if (s == 0)
                for (int i = 1; i < p; ++i) {
                    for (int j = 0; j < size; ++j) {
                        bulk::put(i, i, xs[j]);
                    }
                }

            world.sync();

            bulk::future<int> a(world);
            if (s == 0) a = bulk::get(p - 1, xs[size - 1]);

            world.sync();

            BULK_CHECK_ONCE(
                a.value() == p - 1,
                "receive correct value after heterogeneous puts and getting");
        }

        BULK_SECTION("Get") {
            bulk::var<int> b(world);
            b.value() = s;
            world.sync();

            auto c = bulk::get(world.next_processor(), b);
            world.sync();

            BULK_CHECK_ONCE(c.value() == world.next_processor(),
                            "receive correct value after getting");
        }

        BULK_SECTION("Sugarized get") {
            bulk::var<int> b(world);
            b.value() = s;
            world.sync();

            auto c = b(world.next_processor()).get();
            world.sync();

            BULK_CHECK_ONCE(c.value() == world.next_processor(),
                            "receive correct value after sugarized getting");
        }

        BULK_SECTION("Get multiple") {
            int size = 5;
            bulk::var<int> x(world);
            x.value() = s;

            world.sync();

            std::vector<bulk::future<int>> ys;
            for (int i = 0; i < size; ++i) {
                ys.push_back(bulk::get(world.next_processor(), x));
            }

            world.sync();

            for (auto& y : ys) {
                BULK_CHECK_ONCE(y.value() == world.next_processor(),
                                "receive correct value after getting multiple");
            }
        }

        BULK_SECTION("Put array") {
            std::vector<int> test(10);
            std::iota(test.begin(), test.end(), 1);

            bulk::coarray<int> xs(world, 10, 5);
            xs.put(world.next_processor(), test.begin(), test.end());
            world.sync();

            BULK_CHECK_ONCE(xs[5] == 6, "can put iterator range");
        }

        BULK_SECTION("Coarray") {
            bulk::coarray<int> zs(world, 10);

            BULK_CHECK_ONCE(zs.size() == 10,
                            "can obtain the size of a coarray");
            BULK_CHECK_ONCE(zs.empty() == false, "can check for emptyness");

            zs(world.next_processor())[1] = s;

            world.sync();

            BULK_CHECK_ONCE(
                zs[1] == world.prev_processor(),
                "putting to remote coarray image gives correct result");

            zs[3] = 2;

            BULK_CHECK_ONCE(zs[3] == 2,
                            "writing to local coarray gives correct result");

            auto a = zs(2)[1].get();
            world.sync();

            BULK_CHECK_ONCE(a.value() == 1,
                            "getting from coarray gives correct result");
        }

        BULK_SECTION("Coarray iteration") {
            auto xs = bulk::gather_all(world, s);
            int t = 0;
            for (auto x : xs) {
                BULK_CHECK_ONCE(x == t++, "gather operation succeeded");
            }
        }

        BULK_SECTION("Single message passing") {
            bulk::queue<int, int> q(world);
            q(world.next_processor()).send(123, 1337);
            world.sync();
            for (auto& msg : q) {
                BULK_CHECK_ONCE(msg.tag == 123 && msg.content == 1337,
                                "message passed succesfully");
            }
        }

        BULK_SECTION("Message to self") {
            bulk::queue<int, int> q(world);

            q(world.processor_id()).send(123, 1337);
            world.sync();
            int tag = 0;
            int content = 0;
            for (auto msg : q) {
                tag = msg.tag;
                content = msg.content;
            }
            BULK_CHECK_ONCE(tag == 123 && content == 1337,
                            "message passed succesfully");
        }

        BULK_SECTION("Multiple messages to self") {
            auto q = bulk::queue<int, int>(world);

            q(world.processor_id()).send(123, 1337);
            world.sync();
            int tag = 0;
            int content = 0;
            for (auto msg : q) {
                tag = msg.tag;
                content = msg.content;
            }
            BULK_CHECK_ONCE(tag == 123 && content == 1337,
                            "message passed succesfully");
        }

        BULK_SECTION("Multiple message passing") {
            std::vector<int> contents = {1337, 12345, 1230519, 5, 8};

            bulk::queue<int, int> q(world);
            for (size_t i = 0; i < contents.size(); ++i) {
                q(world.next_processor()).send(s, contents[i]);
            }

            world.sync();

            int k = 0;
            BULK_CHECK_ONCE(!q.empty(), "multiple messages arrived");
            for (auto msg : q) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents[k++],
                                "multiple messages passed succesfully");
            }
        }

        BULK_SECTION("Multiple queue and types message passing") {
            std::vector<int> contents = {1337, 12345, 1230519, 5, 8};
            std::vector<float> contents2 = {1.0f, 2.0f, 3.0f, 4.0f};

            bulk::queue<int, int> q(world);
            bulk::queue<int, float> q2(world);

            for (size_t i = 0; i < contents.size(); ++i) {
                q(world.next_processor()).send(s, contents[i]);
            }
            for (size_t i = 0; i < contents2.size(); ++i) {
                q2(world.next_processor()).send(s, contents2[i]);
            }

            world.sync();

            int k = 0;
            BULK_CHECK_ONCE(!q.empty() && !q2.empty(), "queues are non-empty");
            for (auto& msg : q) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents[k++],
                                "received correct result on q");
            }

            int l = 0;
            BULK_CHECK_ONCE(q.size() == contents.size(),
                            "first queue correct number of messages");

            BULK_CHECK_ONCE(q2.size() == contents2.size(),
                            "second queue correct number of messages");

            for (auto& msg : q2) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents2[l++],
                                "received correct result on q2");
            }

            world.sync();

            BULK_CHECK_ONCE(q.empty(), "first queue gets emptied");
            BULK_CHECK_ONCE(q2.empty(), "second queue gets emptied");
        }
    });
}
<commit_msg>Add unit test for delayed puts<commit_after>#include <chrono>

#include "bulk_test_common.hpp"
#include "set_backend.hpp"
#include <bulk/bulk.hpp>

extern environment env;

void test_communication() {
    env.spawn(env.available_processors(), [](auto& world) {
        int s = world.processor_id();
        int p = world.active_processors();

        BULK_SECTION("Put") {
            // test `put` to single variable
            bulk::var<int> a(world, 3);

            BULK_CHECK_ONCE(a.value() == 3,
                            "correct initial value for variable");

            bulk::put(world.next_processor(), s, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == ((s + p - 1) % p),
                            "receive correct value after putting");
        }

        BULK_SECTION("Put is delayed") {
            using namespace std::chrono_literals;

            // test `put` to single variable
            bulk::var<int> a(world, s);

            bulk::put(world.next_processor(), s, a);

            // sleep
            std::this_thread::sleep_for(20ms);

            BULK_CHECK_ONCE(a.value() == s, "value not put during superstep");

            world.sync();
        }

        BULK_SECTION("Sugarized put") {
            // test `put` to single variable
            bulk::var<int> a(world);

            a(world.next_processor()) = s;
            world.sync();

            BULK_CHECK_ONCE(a.value() == ((s + p - 1) % p),
                            "receive correct value after sugarized putting");
        }

        BULK_SECTION("Put to self") {
            bulk::var<int> a(world);

            bulk::put(s, s, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == s,
                            "receive correct value after putting to self");
        }

        BULK_SECTION("Get from self") {
            bulk::var<int> a(world);
            a = s;
            auto b = bulk::get(s, a);
            world.sync();

            BULK_CHECK_ONCE(b.value() == s,
                            "receive correct value after getting from self");
        }

        BULK_SECTION("Put non-int") {
            // test `put` float to single variable
            bulk::var<float> a(world);

            bulk::put(world.next_processor(), 1.0f, a);
            world.sync();

            BULK_CHECK_ONCE(a.value() == 1.0f,
                            "receive correct value after putting float");
        }

        BULK_SECTION("Put multiple") {
            int size = 5;

            // test `put` to multiple variables
            std::vector<bulk::var<int>> xs;
            for (int i = 0; i < size; ++i)
                xs.emplace_back(world);

            for (int i = 0; i < size; ++i) {
                bulk::put(world.next_processor(), s + i, xs[i]);
            }

            world.sync();

            for (int i = 0; i < size; ++i) {
                BULK_CHECK_ONCE(xs[i].value() == ((s + p - 1) % p) + i,
                                "receive correct value after multiple puts to "
                                "array of variables");
            }
        }

        BULK_SECTION("Put unequal") {
            int size = 5;

            // test `put` to multiple variables
            std::vector<bulk::var<int>> xs;
            for (int i = 0; i < size; ++i)
                xs.emplace_back(world);

            if (s == 0)
                for (int i = 1; i < p; ++i) {
                    for (int j = 0; j < size; ++j) {
                        bulk::put(i, i, xs[j]);
                    }
                }

            world.sync();

            bulk::future<int> a(world);
            if (s == 0)
                a = bulk::get(p - 1, xs[size - 1]);

            world.sync();

            BULK_CHECK_ONCE(
                a.value() == p - 1,
                "receive correct value after heterogeneous puts and getting");
        }

        BULK_SECTION("Get") {
            bulk::var<int> b(world);
            b.value() = s;
            world.sync();

            auto c = bulk::get(world.next_processor(), b);
            world.sync();

            BULK_CHECK_ONCE(c.value() == world.next_processor(),
                            "receive correct value after getting");
        }

        BULK_SECTION("Sugarized get") {
            bulk::var<int> b(world);
            b.value() = s;
            world.sync();

            auto c = b(world.next_processor()).get();
            world.sync();

            BULK_CHECK_ONCE(c.value() == world.next_processor(),
                            "receive correct value after sugarized getting");
        }

        BULK_SECTION("Get multiple") {
            int size = 5;
            bulk::var<int> x(world);
            x.value() = s;

            world.sync();

            std::vector<bulk::future<int>> ys;
            for (int i = 0; i < size; ++i) {
                ys.push_back(bulk::get(world.next_processor(), x));
            }

            world.sync();

            for (auto& y : ys) {
                BULK_CHECK_ONCE(y.value() == world.next_processor(),
                                "receive correct value after getting multiple");
            }
        }

        BULK_SECTION("Put array") {
            std::vector<int> test(10);
            std::iota(test.begin(), test.end(), 1);

            bulk::coarray<int> xs(world, 10, 5);
            xs.put(world.next_processor(), test.begin(), test.end());
            world.sync();

            BULK_CHECK_ONCE(xs[5] == 6, "can put iterator range");
        }

        BULK_SECTION("Coarray") {
            bulk::coarray<int> zs(world, 10);

            BULK_CHECK_ONCE(zs.size() == 10,
                            "can obtain the size of a coarray");
            BULK_CHECK_ONCE(zs.empty() == false, "can check for emptyness");

            zs(world.next_processor())[1] = s;

            world.sync();

            BULK_CHECK_ONCE(
                zs[1] == world.prev_processor(),
                "putting to remote coarray image gives correct result");

            zs[3] = 2;

            BULK_CHECK_ONCE(zs[3] == 2,
                            "writing to local coarray gives correct result");

            auto a = zs(2)[1].get();
            world.sync();

            BULK_CHECK_ONCE(a.value() == 1,
                            "getting from coarray gives correct result");
        }

        BULK_SECTION("Coarray iteration") {
            auto xs = bulk::gather_all(world, s);
            int t = 0;
            for (auto x : xs) {
                BULK_CHECK_ONCE(x == t++, "gather operation succeeded");
            }
        }

        BULK_SECTION("Single message passing") {
            bulk::queue<int, int> q(world);
            q(world.next_processor()).send(123, 1337);
            world.sync();
            for (auto& msg : q) {
                BULK_CHECK_ONCE(msg.tag == 123 && msg.content == 1337,
                                "message passed succesfully");
            }
        }

        BULK_SECTION("Message to self") {
            bulk::queue<int, int> q(world);

            q(world.processor_id()).send(123, 1337);
            world.sync();
            int tag = 0;
            int content = 0;
            for (auto msg : q) {
                tag = msg.tag;
                content = msg.content;
            }
            BULK_CHECK_ONCE(tag == 123 && content == 1337,
                            "message passed succesfully");
        }

        BULK_SECTION("Multiple messages to self") {
            auto q = bulk::queue<int, int>(world);

            q(world.processor_id()).send(123, 1337);
            world.sync();
            int tag = 0;
            int content = 0;
            for (auto msg : q) {
                tag = msg.tag;
                content = msg.content;
            }
            BULK_CHECK_ONCE(tag == 123 && content == 1337,
                            "message passed succesfully");
        }

        BULK_SECTION("Multiple message passing") {
            std::vector<int> contents = {1337, 12345, 1230519, 5, 8};

            bulk::queue<int, int> q(world);
            for (size_t i = 0; i < contents.size(); ++i) {
                q(world.next_processor()).send(s, contents[i]);
            }

            world.sync();

            int k = 0;
            BULK_CHECK_ONCE(!q.empty(), "multiple messages arrived");
            for (auto msg : q) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents[k++],
                                "multiple messages passed succesfully");
            }
        }

        BULK_SECTION("Multiple queue and types message passing") {
            std::vector<int> contents = {1337, 12345, 1230519, 5, 8};
            std::vector<float> contents2 = {1.0f, 2.0f, 3.0f, 4.0f};

            bulk::queue<int, int> q(world);
            bulk::queue<int, float> q2(world);

            for (size_t i = 0; i < contents.size(); ++i) {
                q(world.next_processor()).send(s, contents[i]);
            }
            for (size_t i = 0; i < contents2.size(); ++i) {
                q2(world.next_processor()).send(s, contents2[i]);
            }

            world.sync();

            int k = 0;
            BULK_CHECK_ONCE(!q.empty() && !q2.empty(), "queues are non-empty");
            for (auto& msg : q) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents[k++],
                                "received correct result on q");
            }

            int l = 0;
            BULK_CHECK_ONCE(q.size() == contents.size(),
                            "first queue correct number of messages");

            BULK_CHECK_ONCE(q2.size() == contents2.size(),
                            "second queue correct number of messages");

            for (auto& msg : q2) {
                BULK_CHECK_ONCE(msg.tag == world.prev_processor() &&
                                    msg.content == contents2[l++],
                                "received correct result on q2");
            }

            world.sync();

            BULK_CHECK_ONCE(q.empty(), "first queue gets emptied");
            BULK_CHECK_ONCE(q2.empty(), "second queue gets emptied");
        }
    });
}
<|endoftext|>