<commit_before>#include <mlopen/tensor.hpp>
#include <mlopen/errors.hpp>
#include <initializer_list>
#include <array>

extern "C"
mlopenStatus_t mlopenCreateTensorDescriptor(
		mlopenTensorDescriptor_t *tensorDesc) {

	return mlopen::try_([&] {
		mlopen::deref(tensorDesc) = new mlopen::TensorDescriptor();
	});
}

extern "C"
mlopenStatus_t mlopenSet4dTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t dataType,
		int n,
		int c,
		int h,
		int w) {

	return mlopen::try_([&] {
		std::initializer_list<int> lens = {n, c, h, w};
		mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, lens.begin(), 4);
	});
}

extern "C"
mlopenStatus_t mlopenGet4dTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t *dataType,
		int *n,
		int *c,
		int *h,
		int *w,
		int *nStride,
		int *cStride,
		int *hStride,
		int *wStride) {

	return mlopen::try_([&] {
		mlopen::deref(dataType) = mlopen::deref(tensorDesc).GetType();
		mlopen::tie_deref(n, c, h, w) = mlopen::tie4(mlopen::deref(tensorDesc).GetLengths());
		mlopen::tie_deref(nStride, cStride, hStride, wStride) = mlopen::tie4(mlopen::deref(tensorDesc).GetStrides());
	});
}

// Internal API
// MD: This should not be reqired to be exported. Temporary hack
MLOPEN_EXPORT mlopenStatus_t mlopenGet4dTensorDescriptorLengths(
		mlopenTensorDescriptor_t tensorDesc,
		int *n,
		int *c,
		int *h,
		int *w) {

	return mlopen::try_([&] {
		mlopen::tie_deref(n, c, h, w) = mlopen::tie4(mlopen::deref(tensorDesc).GetLengths());
	});
}

// Internal API
MLOPEN_EXPORT mlopenStatus_t mlopenGet4dTensorDescriptorStrides(
		mlopenTensorDescriptor_t tensorDesc,
		int *nStride,
		int *cStride,
		int *hStride,
		int *wStride) {

	return mlopen::try_([&] {
		mlopen::tie_deref(nStride, cStride, hStride, wStride) = mlopen::tie4(mlopen::deref(tensorDesc).GetStrides());
	});
}

// Internal API
mlopenStatus_t mlopenSetTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t dataType,
		int nbDims,
		int *dimsA,
		int *stridesA) {

	return mlopen::try_([&] {
		if (stridesA == nullptr) {
			mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, dimsA, nbDims);
		} else {
			mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, dimsA, stridesA, nbDims);
		}
	});
}

// Internal API
int mlopenGetTensorDescriptorElementSize(mlopenTensorDescriptor_t tensorDesc) {
	return mlopen::deref(tensorDesc).GetElementSize();
}

extern "C"
mlopenStatus_t mlopenGetTensorDescriptorSize(mlopenTensorDescriptor_t tensorDesc, int* size) {
	return mlopen::try_([&] {
		mlopen::deref(size) = mlopen::deref(tensorDesc).GetSize();
	});
}

extern "C"
mlopenStatus_t mlopenGetTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t *dataType,
		int *dimsA,
		int *stridesA) {

	return mlopen::try_([&] {
		if (dataType != nullptr) {
			*dataType = mlopen::deref(tensorDesc).GetType();
		}
		if (dimsA != nullptr) {
			std::copy(mlopen::deref(tensorDesc).GetLengths().begin(), mlopen::deref(tensorDesc).GetLengths().end(), dimsA);
		}
		if (stridesA != nullptr) {
			std::copy(mlopen::deref(tensorDesc).GetStrides().begin(), mlopen::deref(tensorDesc).GetStrides().end(), stridesA);
		}
	});

}

extern "C"
mlopenStatus_t mlopenDestroyTensorDescriptor(mlopenTensorDescriptor_t tensorDesc) {
	return mlopen::try_([&] {
		mlopen_destroy_object(tensorDesc);
	});
}

extern "C"
mlopenStatus_t mlopenTransformTensor(mlopenHandle_t handle,
		const void						*alpha,
		const mlopenTensorDescriptor_t	xDesc,
		const void						*x,
		const void						*beta,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).TransformTensor(mlopen::deref(handle),
				alpha,
				mlopen::deref(xDesc),
				DataCast(x),
				beta,
				DataCast(y));
	});

}

extern "C"
mlopenStatus_t mlopenOpTensor(mlopenHandle_t handle,
		mlopenTensorOp_t				tensorOp,
		const void						*alpha1,
		const mlopenTensorDescriptor_t	aDesc,
		const void						*A,
		const void						*alpha2,
		const mlopenTensorDescriptor_t	bDesc,
		const void						*B,
		const void						*beta,
		const mlopenTensorDescriptor_t	cDesc,
		void							*C) {

	return mlopen::try_([&] {
		return mlopen::deref(cDesc).OpTensor(mlopen::deref(handle),
				tensorOp,
				alpha1,
				mlopen::deref(aDesc),
				DataCast(A),
				alpha2,
				mlopen::deref(bDesc),
				DataCast(B),
				beta,
				DataCast(C));
	});

}

extern "C"
mlopenStatus_t mlopenSetTensor(mlopenHandle_t handle,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y,
		const void						*valuePtr) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).SetTensor(mlopen::deref(handle),
				DataCast(y),
				valuePtr);
	});

}

extern "C"
mlopenStatus_t mlopenScaleTensor(mlopenHandle_t handle,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y,
		const void						*alpha) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).ScaleTensor(mlopen::deref(handle),
				DataCast(y),
				alpha);
	});

}
<commit_msg>Use extern c on api call<commit_after>#include <mlopen/tensor.hpp>
#include <mlopen/errors.hpp>
#include <initializer_list>
#include <array>

extern "C"
mlopenStatus_t mlopenCreateTensorDescriptor(
		mlopenTensorDescriptor_t *tensorDesc) {

	return mlopen::try_([&] {
		mlopen::deref(tensorDesc) = new mlopen::TensorDescriptor();
	});
}

extern "C"
mlopenStatus_t mlopenSet4dTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t dataType,
		int n,
		int c,
		int h,
		int w) {

	return mlopen::try_([&] {
		std::initializer_list<int> lens = {n, c, h, w};
		mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, lens.begin(), 4);
	});
}

extern "C"
mlopenStatus_t mlopenGet4dTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t *dataType,
		int *n,
		int *c,
		int *h,
		int *w,
		int *nStride,
		int *cStride,
		int *hStride,
		int *wStride) {

	return mlopen::try_([&] {
		mlopen::deref(dataType) = mlopen::deref(tensorDesc).GetType();
		mlopen::tie_deref(n, c, h, w) = mlopen::tie4(mlopen::deref(tensorDesc).GetLengths());
		mlopen::tie_deref(nStride, cStride, hStride, wStride) = mlopen::tie4(mlopen::deref(tensorDesc).GetStrides());
	});
}

// Internal API
// MD: This should not be reqired to be exported. Temporary hack
MLOPEN_EXPORT mlopenStatus_t mlopenGet4dTensorDescriptorLengths(
		mlopenTensorDescriptor_t tensorDesc,
		int *n,
		int *c,
		int *h,
		int *w) {

	return mlopen::try_([&] {
		mlopen::tie_deref(n, c, h, w) = mlopen::tie4(mlopen::deref(tensorDesc).GetLengths());
	});
}

// Internal API
MLOPEN_EXPORT mlopenStatus_t mlopenGet4dTensorDescriptorStrides(
		mlopenTensorDescriptor_t tensorDesc,
		int *nStride,
		int *cStride,
		int *hStride,
		int *wStride) {

	return mlopen::try_([&] {
		mlopen::tie_deref(nStride, cStride, hStride, wStride) = mlopen::tie4(mlopen::deref(tensorDesc).GetStrides());
	});
}

extern "C"
mlopenStatus_t mlopenSetTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t dataType,
		int nbDims,
		int *dimsA,
		int *stridesA) {

	return mlopen::try_([&] {
		if (stridesA == nullptr) {
			mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, dimsA, nbDims);
		} else {
			mlopen::deref(tensorDesc) = mlopen::TensorDescriptor(dataType, dimsA, stridesA, nbDims);
		}
	});
}

// Internal API
int mlopenGetTensorDescriptorElementSize(mlopenTensorDescriptor_t tensorDesc) {
	return mlopen::deref(tensorDesc).GetElementSize();
}

extern "C"
mlopenStatus_t mlopenGetTensorDescriptorSize(mlopenTensorDescriptor_t tensorDesc, int* size) {
	return mlopen::try_([&] {
		mlopen::deref(size) = mlopen::deref(tensorDesc).GetSize();
	});
}

extern "C"
mlopenStatus_t mlopenGetTensorDescriptor(
		mlopenTensorDescriptor_t tensorDesc,
		mlopenDataType_t *dataType,
		int *dimsA,
		int *stridesA) {

	return mlopen::try_([&] {
		if (dataType != nullptr) {
			*dataType = mlopen::deref(tensorDesc).GetType();
		}
		if (dimsA != nullptr) {
			std::copy(mlopen::deref(tensorDesc).GetLengths().begin(), mlopen::deref(tensorDesc).GetLengths().end(), dimsA);
		}
		if (stridesA != nullptr) {
			std::copy(mlopen::deref(tensorDesc).GetStrides().begin(), mlopen::deref(tensorDesc).GetStrides().end(), stridesA);
		}
	});

}

extern "C"
mlopenStatus_t mlopenDestroyTensorDescriptor(mlopenTensorDescriptor_t tensorDesc) {
	return mlopen::try_([&] {
		mlopen_destroy_object(tensorDesc);
	});
}

extern "C"
mlopenStatus_t mlopenTransformTensor(mlopenHandle_t handle,
		const void						*alpha,
		const mlopenTensorDescriptor_t	xDesc,
		const void						*x,
		const void						*beta,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).TransformTensor(mlopen::deref(handle),
				alpha,
				mlopen::deref(xDesc),
				DataCast(x),
				beta,
				DataCast(y));
	});

}

extern "C"
mlopenStatus_t mlopenOpTensor(mlopenHandle_t handle,
		mlopenTensorOp_t				tensorOp,
		const void						*alpha1,
		const mlopenTensorDescriptor_t	aDesc,
		const void						*A,
		const void						*alpha2,
		const mlopenTensorDescriptor_t	bDesc,
		const void						*B,
		const void						*beta,
		const mlopenTensorDescriptor_t	cDesc,
		void							*C) {

	return mlopen::try_([&] {
		return mlopen::deref(cDesc).OpTensor(mlopen::deref(handle),
				tensorOp,
				alpha1,
				mlopen::deref(aDesc),
				DataCast(A),
				alpha2,
				mlopen::deref(bDesc),
				DataCast(B),
				beta,
				DataCast(C));
	});

}

extern "C"
mlopenStatus_t mlopenSetTensor(mlopenHandle_t handle,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y,
		const void						*valuePtr) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).SetTensor(mlopen::deref(handle),
				DataCast(y),
				valuePtr);
	});

}

extern "C"
mlopenStatus_t mlopenScaleTensor(mlopenHandle_t handle,
		const mlopenTensorDescriptor_t	yDesc,
		void							*y,
		const void						*alpha) {

	return mlopen::try_([&] {
		return mlopen::deref(yDesc).ScaleTensor(mlopen::deref(handle),
				DataCast(y),
				alpha);
	});

}
<|endoftext|>


<commit_before><commit_msg>Fixed parseNumber<commit_after><|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "retirement.hpp"
#include "assets.hpp"
#include "accounts.hpp"
#include "expenses.hpp"
#include "earnings.hpp"
#include "budget_exception.hpp"
#include "config.hpp"
#include "console.hpp"
#include "writer.hpp"

using namespace budget;

namespace {

constexpr size_t running_limit = 12;

money running_expenses(){
    auto today = budget::local_day();

    budget::date end = today - budget::days(today.day() - 1);
    budget::date start = end - budget::months(running_limit);

    budget::money total;

    for(auto& expense : all_expenses()){
        if(expense.date >= start && expense.date < end){
            total += expense.amount;
        }
    }

    return total;
}

double running_savings_rate(){
    auto today = budget::local_day();

    double savings_rate = 0.0;

    for(size_t i = 1; i <= running_limit; ++i){
        auto d = today - budget::months(i);

        budget::money expenses;

        for (auto& expense : all_expenses()) {
            if (expense.date.year() == d.year() && expense.date.month() == d.month()) {
                expenses += expense.amount;
            }
        }

        budget::money earnings;

        for (auto& earning : all_earnings()) {
            if (earning.date.year() == d.year() && earning.date.month() == d.month()) {
                earnings += earning.amount;
            }
        }

        budget::money income;

        for (auto& account : all_accounts(d.year(), d.month())){
            income += account.amount;
        }

        auto balance = income + earnings - expenses;
        auto local   = balance / (income + earnings);

        if(local < 0){
            local = 0;
        }

        savings_rate += local;
    }

    return savings_rate / running_limit;
}

void retirement_set() {
    double wrate = 4.0;
    double roi = 4.0;

    if(internal_config_contains("withdrawal_rate")){
        wrate = to_number<double>(internal_config_value("withdrawal_rate"));
    }

    if(internal_config_contains("expected_roi")){
        roi = to_number<double>(internal_config_value("expected_roi"));
    }

    edit_double(wrate, "Withdrawal Rate (%)");
    edit_double(roi, "Expected Annual Return (%)");

    // Save the configuration
    internal_config_value("withdrawal_rate") = to_string(wrate);
    internal_config_value("expected_roi") = to_string(roi);
}

} // end of anonymous namespace

void budget::retirement_module::load() {
    load_accounts();
    load_assets();
    load_expenses();
    load_earnings();
}

void budget::retirement_module::handle(std::vector<std::string>& args) {
    console_writer w(std::cout);

    if (args.empty() || args.size() == 1) {
        retirement_status(w);
    } else {
        auto& subcommand = args[1];

        if (subcommand == "status") {
            retirement_status(w);
        } else if (subcommand == "set") {
            retirement_set();
            std::cout << std::endl;
            retirement_status(w);
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::retirement_status(budget::writer& w) {
    if (!w.is_web()) {
        if (!internal_config_contains("withdrawal_rate")) {
            w << "Not enough information, please configure first with retirement set" << end_of_line;
            return;
        }

        if (!internal_config_contains("expected_roi")) {
            w << "Not enough information, please configure first with retirement set" << end_of_line;
            return;
        }
    }

    auto currency       = get_default_currency();
    auto wrate          = to_number<double>(internal_config_value("withdrawal_rate"));
    auto roi            = to_number<double>(internal_config_value("expected_roi"));
    auto years          = double(int(100.0 / wrate));
    auto expenses       = running_expenses();
    auto savings_rate   = running_savings_rate();
    auto nw             = get_net_worth();
    auto missing        = years * expenses - nw;
    auto income         = 12 * get_base_income();
    auto a_savings_rate = (income - expenses) / income;

    size_t base_months   = 0;
    size_t a_base_months = 0;

    auto current_nw = nw;
    while (current_nw < years * expenses) {
        current_nw *= 1.0 + (roi / 100.0) / 12;
        current_nw += (savings_rate * income) / 12;

        ++base_months;
    }

    current_nw = nw;
    while (current_nw < years * expenses) {
        current_nw *= 1.0 + (roi / 100.0) / 12;
        current_nw += (a_savings_rate * income) / 12;

        ++a_base_months;
    }

    std::vector<std::string> columns = {};
    std::vector<std::vector<std::string>> contents;

    using namespace std::string_literals;

    contents.push_back({"Withdrawal rate"s, to_string(wrate) + "%"});
    contents.push_back({"Annual Return"s, to_string(roi) + "%"});
    contents.push_back({"Years of expense"s, to_string(years)});
    contents.push_back({"Running expenses"s, to_string(expenses) + " " + currency});
    contents.push_back({"Monthly expenses"s, to_string(expenses / 12) + " " + currency});
    contents.push_back({"Target Net Worth"s, to_string(years * expenses) + " " + currency});
    contents.push_back({"Current Net Worth"s, to_string(nw) + " " + currency});
    contents.push_back({"Missing Net Worth"s, to_string(missing) + " " + currency});
    contents.push_back({"FI Ratio"s, to_string(100 * (nw / missing)) + "%"});
    contents.push_back({"Yearly income"s, to_string(income) + " " + currency});
    contents.push_back({"Running Savings Rate"s, to_string(100 * savings_rate) + "%"});
    contents.push_back({"Yearly savings"s, to_string(savings_rate * income) + " " + currency});
    contents.push_back({"Time to FI (w/o returns)"s, to_string(missing / (savings_rate * income)) + " years"});
    contents.push_back({"Time to FI (w/ returns)"s, to_string(base_months / 12.0) + " years"});

    contents.push_back({"Adjusted Savings Rate"s, to_string(100 * a_savings_rate) + "%"});
    contents.push_back({"Adjusted Yearly savings"s, to_string(a_savings_rate * income) + " " + currency});
    contents.push_back({"Adjusted Time to FI (w/o returns)"s, to_string(missing / (a_savings_rate * income)) + " years"});
    contents.push_back({"Adjusted Time to FI (w/ returns)"s, to_string(a_base_months / 12.0) + " years"});

    w.display_table(columns, contents);

    std::array<int, 5> rate_decs{1, 2, 5, 10, 20};

    // Note: this not totally correct since we ignore the
    // correlation between the savings rate and the expenses

    for (auto dec : rate_decs) {
        auto dec_savings_rate = savings_rate + 0.01 * dec;

        auto current_nw = nw;
        size_t months   = 0;

        while (current_nw < years * expenses) {
            current_nw *= 1.0 + (roi / 100.0) / 12;
            current_nw += (dec_savings_rate * income) / 12;

            ++months;
        }

        w << p_begin << "Increasing Savings Rate by " << dec << "% would save " << (base_months - months) / 12.0 << " years (in " << months / 12.0 << " years)" << p_end;
    }

    std::array<int, 5> exp_decs{10, 50, 100, 200, 500};

    for (auto dec : exp_decs) {
        auto current_nw = nw;
        size_t months   = 0;

        auto new_savings_rate = (income - (expenses - dec * 12)) / income;;

        while (current_nw < years * (expenses - (dec * 12))) {
            current_nw *= 1.0 + (roi / 100.0) / 12;
            current_nw += (new_savings_rate * income) / 12;

            ++months;
        }

        w << p_begin << "Decreasing monthly expenses by " << dec << " " << currency << " would save " << (base_months - months) / 12.0 << " years (in " << months / 12.0 << " (adjusted) years)" << p_end;
    }
}
<commit_msg>Compute months of FI<commit_after>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "retirement.hpp"
#include "assets.hpp"
#include "accounts.hpp"
#include "expenses.hpp"
#include "earnings.hpp"
#include "budget_exception.hpp"
#include "config.hpp"
#include "console.hpp"
#include "writer.hpp"

using namespace budget;

namespace {

constexpr size_t running_limit = 12;

money running_expenses(){
    auto today = budget::local_day();

    budget::date end = today - budget::days(today.day() - 1);
    budget::date start = end - budget::months(running_limit);

    budget::money total;

    for(auto& expense : all_expenses()){
        if(expense.date >= start && expense.date < end){
            total += expense.amount;
        }
    }

    return total;
}

double running_savings_rate(){
    auto today = budget::local_day();

    double savings_rate = 0.0;

    for(size_t i = 1; i <= running_limit; ++i){
        auto d = today - budget::months(i);

        budget::money expenses;

        for (auto& expense : all_expenses()) {
            if (expense.date.year() == d.year() && expense.date.month() == d.month()) {
                expenses += expense.amount;
            }
        }

        budget::money earnings;

        for (auto& earning : all_earnings()) {
            if (earning.date.year() == d.year() && earning.date.month() == d.month()) {
                earnings += earning.amount;
            }
        }

        budget::money income;

        for (auto& account : all_accounts(d.year(), d.month())){
            income += account.amount;
        }

        auto balance = income + earnings - expenses;
        auto local   = balance / (income + earnings);

        if(local < 0){
            local = 0;
        }

        savings_rate += local;
    }

    return savings_rate / running_limit;
}

void retirement_set() {
    double wrate = 4.0;
    double roi = 4.0;

    if(internal_config_contains("withdrawal_rate")){
        wrate = to_number<double>(internal_config_value("withdrawal_rate"));
    }

    if(internal_config_contains("expected_roi")){
        roi = to_number<double>(internal_config_value("expected_roi"));
    }

    edit_double(wrate, "Withdrawal Rate (%)");
    edit_double(roi, "Expected Annual Return (%)");

    // Save the configuration
    internal_config_value("withdrawal_rate") = to_string(wrate);
    internal_config_value("expected_roi") = to_string(roi);
}

} // end of anonymous namespace

void budget::retirement_module::load() {
    load_accounts();
    load_assets();
    load_expenses();
    load_earnings();
}

void budget::retirement_module::handle(std::vector<std::string>& args) {
    console_writer w(std::cout);

    if (args.empty() || args.size() == 1) {
        retirement_status(w);
    } else {
        auto& subcommand = args[1];

        if (subcommand == "status") {
            retirement_status(w);
        } else if (subcommand == "set") {
            retirement_set();
            std::cout << std::endl;
            retirement_status(w);
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::retirement_status(budget::writer& w) {
    if (!w.is_web()) {
        if (!internal_config_contains("withdrawal_rate")) {
            w << "Not enough information, please configure first with retirement set" << end_of_line;
            return;
        }

        if (!internal_config_contains("expected_roi")) {
            w << "Not enough information, please configure first with retirement set" << end_of_line;
            return;
        }
    }

    auto currency       = get_default_currency();
    auto wrate          = to_number<double>(internal_config_value("withdrawal_rate"));
    auto roi            = to_number<double>(internal_config_value("expected_roi"));
    auto years          = double(int(100.0 / wrate));
    auto expenses       = running_expenses();
    auto savings_rate   = running_savings_rate();
    auto nw             = get_net_worth();
    auto missing        = years * expenses - nw;
    auto income         = 12 * get_base_income();
    auto a_savings_rate = (income - expenses) / income;

    size_t base_months   = 0;
    size_t a_base_months = 0;

    auto current_nw = nw;
    while (current_nw < years * expenses) {
        current_nw *= 1.0 + (roi / 100.0) / 12;
        current_nw += (savings_rate * income) / 12;

        ++base_months;
    }

    current_nw = nw;
    while (current_nw < years * expenses) {
        current_nw *= 1.0 + (roi / 100.0) / 12;
        current_nw += (a_savings_rate * income) / 12;

        ++a_base_months;
    }

    std::vector<std::string> columns = {};
    std::vector<std::vector<std::string>> contents;

    using namespace std::string_literals;

    contents.push_back({"Withdrawal rate"s, to_string(wrate) + "%"});
    contents.push_back({"Annual Return"s, to_string(roi) + "%"});
    contents.push_back({"Years of expense"s, to_string(years)});
    contents.push_back({"Running expenses"s, to_string(expenses) + " " + currency});
    contents.push_back({"Monthly expenses"s, to_string(expenses / 12) + " " + currency});
    contents.push_back({"Target Net Worth"s, to_string(years * expenses) + " " + currency});
    contents.push_back({"Current Net Worth"s, to_string(nw) + " " + currency});
    contents.push_back({"Missing Net Worth"s, to_string(missing) + " " + currency});
    contents.push_back({"FI Ratio"s, to_string(100 * (nw / missing)) + "%"});
    contents.push_back({"Months of FI"s, to_string(nw / (expenses / 12))});
    contents.push_back({"Yearly income"s, to_string(income) + " " + currency});
    contents.push_back({"Running Savings Rate"s, to_string(100 * savings_rate) + "%"});
    contents.push_back({"Yearly savings"s, to_string(savings_rate * income) + " " + currency});
    contents.push_back({"Time to FI (w/o returns)"s, to_string(missing / (savings_rate * income)) + " years"});
    contents.push_back({"Time to FI (w/ returns)"s, to_string(base_months / 12.0) + " years"});

    contents.push_back({"Adjusted Savings Rate"s, to_string(100 * a_savings_rate) + "%"});
    contents.push_back({"Adjusted Yearly savings"s, to_string(a_savings_rate * income) + " " + currency});
    contents.push_back({"Adjusted Time to FI (w/o returns)"s, to_string(missing / (a_savings_rate * income)) + " years"});
    contents.push_back({"Adjusted Time to FI (w/ returns)"s, to_string(a_base_months / 12.0) + " years"});

    w.display_table(columns, contents);

    std::array<int, 5> rate_decs{1, 2, 5, 10, 20};

    // Note: this not totally correct since we ignore the
    // correlation between the savings rate and the expenses

    for (auto dec : rate_decs) {
        auto dec_savings_rate = savings_rate + 0.01 * dec;

        auto current_nw = nw;
        size_t months   = 0;

        while (current_nw < years * expenses) {
            current_nw *= 1.0 + (roi / 100.0) / 12;
            current_nw += (dec_savings_rate * income) / 12;

            ++months;
        }

        w << p_begin << "Increasing Savings Rate by " << dec << "% would save " << (base_months - months) / 12.0 << " years (in " << months / 12.0 << " years)" << p_end;
    }

    std::array<int, 5> exp_decs{10, 50, 100, 200, 500};

    for (auto dec : exp_decs) {
        auto current_nw = nw;
        size_t months   = 0;

        auto new_savings_rate = (income - (expenses - dec * 12)) / income;;

        while (current_nw < years * (expenses - (dec * 12))) {
            current_nw *= 1.0 + (roi / 100.0) / 12;
            current_nw += (new_savings_rate * income) / 12;

            ++months;
        }

        w << p_begin << "Decreasing monthly expenses by " << dec << " " << currency << " would save " << (base_months - months) / 12.0 << " years (in " << months / 12.0 << " (adjusted) years)" << p_end;
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.clear();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }
    if (CheckSha(archivePath))
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", "Error. SHA-256 verification failed."));
        remove_all(tempDir);
        return false;
    }
    return true;
}

void RPCUpdate::Install()
{
    statusObj.clear();
    if (!Download())
    {
        return;
    }
    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }
    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to /usr/
    if (!nErr) {
        strCommand = strprintf("cp -rf %s /usr/local/bin/", (tempDir / "archive/bin/*").string());
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = 0;
    if (total != 0) {
        percent = now * 100 / total;
    }
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == total) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != total) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(total) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

bool RPCUpdate::CheckSha(const std::string& fileName) const
{
    bool result = false;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = true;
            } else {
                result = false;
            }
        } else {
            result = false;
        }
    } catch(std::runtime_error &e) {
        result = false;
    }
    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        struct stat buffer;
        if (stat("/usr/local/bin/terracoind", &buffer) != 0)
        {
            throw runtime_error("Terracoin Core not located in /usr/local/bin, can not install.");
        }

        if (::system("command -v sudo"))
        {
            throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
        }

        if (::system("command -v tar"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<commit_msg>Make sure we just clear the data it still needs to be an Object<commit_after>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rpc/update.h"

#include "init.h"
#include "clientversion.h"
#include "rpc/server.h"
#include "util.h"

#include <univalue.h>
#include <boost/thread.hpp>

#include <sys/stat.h>

using namespace std;
using namespace boost::filesystem;

bool RPCUpdate::started = false;
UniValue RPCUpdate::statusObj(UniValue::VOBJ);

std::string RPCUpdate::GetArchivePath() const
{
    std::string url = updater.GetDownloadUrl();
    return (tempDir / path(url).filename()).string();
}

bool RPCUpdate::Download()
{
    statusObj.setObject();
    // Create temporary directory
    tempDir = GetTempPath() / unique_path();
    bool result = TryCreateDirectory(tempDir);
    if (!result) {
        throw runtime_error("Failed to create directory" + tempDir.string());
    }

    // Download archive
    std::string archivePath = GetArchivePath();
    updater.DownloadFile(updater.GetDownloadUrl(), archivePath, &ProgressFunction);
    if (updater.GetStopDownload())
    {
        started = false;
        statusObj.push_back(Pair("Download", "Stopped"));
        remove_all(tempDir);
        return false;
    }
    if (CheckSha(archivePath))
    {
        statusObj.push_back(Pair("Download", "Done - " + archivePath));
    }
    else
    {
        statusObj.push_back(Pair("Download", "Error. SHA-256 verification failed."));
        remove_all(tempDir);
        return false;
    }
    return true;
}

void RPCUpdate::Install()
{
    statusObj.setObject();
    if (!Download())
    {
        return;
    }
    // Extract archive
    bool result = TryCreateDirectory(tempDir / "archive");
    if (!result) {
        throw runtime_error(strprintf("Failed to create directory %s", (tempDir / "archive").string()));
    }
    std::string strCommand = strprintf("tar xzf %s -C %s --strip-components=1", GetArchivePath(), (tempDir / "archive").string());
    int nErr = ::system(strCommand.c_str());
    if (nErr) {
        LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
        statusObj.push_back(Pair("Extract", "Error. Check debug.log"));
    } else {
        statusObj.push_back(Pair("Extract", "Done"));
    }

    // Copy files to /usr/
    if (!nErr) {
        strCommand = strprintf("cp -rf %s /usr/local/bin/", (tempDir / "archive/bin/*").string());
        nErr = ::system(strCommand.c_str());
        if (nErr) {
            LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
            statusObj.push_back(Pair("Install", "Error. Check debug.log."));
        } else {
            statusObj.push_back(Pair("Install", "Done"));
        }

        // Restart terracoind
        StartRestart();
    }

    boost::filesystem::remove_all(tempDir);
}

void RPCUpdate::ProgressFunction(curl_off_t now, curl_off_t total)
{
    int percent = 0;
    if (total != 0) {
        percent = now * 100 / total;
    }
    if (statusObj.size() == 0) {
        statusObj.push_back(Pair("Download", "In Progress"));
    }
    if ((now == total) && now != 0) {
        started = false;
        statusObj.push_back(Pair("Download", "Done"));
    } else if (now != total) {
        started = true;
        statusObj.push_back(Pair("Download", strprintf("%0.1f/%0.1fMB, %d%%",
                                        static_cast<double>(now) / 1024 / 1024,
                                        static_cast<double>(total) / 1024 / 1024,
                                        percent)));
    }
}

bool RPCUpdate::IsStarted() const
{
    return started;
}

UniValue RPCUpdate::GetStatusObject() const
{
    return statusObj;
}

bool RPCUpdate::CheckSha(const std::string& fileName) const
{
    bool result = false;
    std::string newSha = updater.GetDownloadSha256Sum();
    try
    {
        std::string sha = Sha256Sum(fileName);
        if (!sha.empty()) {
            if (newSha.compare(sha) == 0) {
                result = true;
            } else {
                result = false;
            }
        } else {
            result = false;
        }
    } catch(std::runtime_error &e) {
        result = false;
    }
    return result;
}

UniValue update(const UniValue& params, bool fHelp)
{
    RPCUpdate rpcUpdate;
    string strCommand;
    if (params.size() >= 1)
        strCommand = params[0].get_str();

    if (fHelp  || (strCommand != "check" && strCommand != "download" && strCommand != "status" &&
                   strCommand != "install" && strCommand != "stop"))
        throw runtime_error(
                "update \"command\"... ( \"passphrase\" )\n"
                "Set of commands to check and download application updates\n"
                "\nArguments:\n"
                "1. \"command\"        (string or set of strings, required) The command to execute\n"
                "2. \"passphrase\"     (string, optional) The wallet passphrase\n"
                "\nAvailable commands:\n"
                "  check        - Check for application updates\n"
                "  download     - Download a new version\n"
                "  status       - Check download status\n"
                "  install      - Install update\n"
                "  stop         - Stop update/install\n"
                );

    if (strCommand == "check")
    {
        if (params.size() > 1) {
            throw runtime_error("Too many parameters\n");
        }

        if (updater.GetStatus())
        {
            // There is an update
            UniValue obj(UniValue::VOBJ);
            obj.push_back(Pair("Current Version", FormatVersion(CLIENT_VERSION)));
            obj.push_back(Pair("Update Version", FormatVersion(updater.GetVersion())));
            obj.push_back(Pair("OS", updater.GetOsString()));
            obj.push_back(Pair("Url", updater.GetDownloadUrl()));
            obj.push_back(Pair("Sha256hash", updater.GetDownloadSha256Sum()));
            return obj;
        }
        return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
    }

    if (strCommand == "download")
    {
        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Download, rpcUpdate));
        return "Terracoin Core download started. \nRun 'terracoin-cli update status' to check download status.";
    }

    if (strCommand == "status")
    {
        return rpcUpdate.GetStatusObject();
    }

    if (strCommand == "stop")
    {
        updater.StopDownload();
        return "Terracoin Core download stopped.";
    }

    if (strCommand == "install")
    {
        if (!fServer)
        {
            throw runtime_error("Command is available only in server mode."
                "\nterracoin-qt will automatically check and notify if there is an updates\n");
        }

        if (updater.GetOS() != Updater::LINUX_32 && updater.GetOS() != Updater::LINUX_64)
        {
            throw runtime_error("Command is available only in Linux.");
        }

        struct stat buffer;
        if (stat("/usr/local/bin/terracoind", &buffer) != 0)
        {
            throw runtime_error("Terracoin Core not located in /usr/local/bin, can not install.");
        }

        if (::system("command -v sudo"))
        {
            throw runtime_error("The command 'sudo' could not be found. Please install it and try again.");
        }

        if (::system("command -v tar"))
        {
            throw runtime_error("The command 'tar' could not be found. Please install it and try again.");
        }

        if (!updater.GetStatus())
        {
            return "You are running the latest version of Terracoin Core - " + FormatVersion(CLIENT_VERSION);
        }
        if (rpcUpdate.IsStarted())
        {
            return "Download is in progress. Run 'terracoin-cli update status' to check the status.";
        }
        boost::thread t(boost::bind(&RPCUpdate::Install, rpcUpdate));
        return "Terracoin Core install started. \nRun 'terracoin-cli update status' to check download status.";
    }
    return "";
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: client_rpc_channel.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-23 16:03:07

#include "src/rpc_channel.h"
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include <iostream>
#include <sstream>
#include "sails/net/connector.h"
#include "src/saf_packet.pb.h"
#include "src/saf_const.h"
#include "src/version.h"
#include "google/protobuf/message.h"
#include "google/protobuf/descriptor.h"

using namespace google::protobuf;  // NOLINT

namespace sails {

RpcChannelImp::RpcChannelImp(string ip, int port):
    ip(ip), port(port) {
  connector = new net::Connector();
  assert(connector->connect(this->ip.c_str(), this->port, true));
  sn = 0;
  stop = false;
  recv_thread = new std::thread(recv_response, this);
  send_thread = new std::thread(send_request, this);
}

RpcChannelImp::~RpcChannelImp() {
  if (!stop) {
    stop = true;
    connector->close();
    recv_thread->join();
    send_thread->join();
  }
  if (recv_thread != NULL) {
    delete recv_thread;
  }
  if (send_thread != NULL) {
    delete send_thread;
  }
  if (connector != NULL) {
    delete connector;
    connector = NULL;
  }
  for (auto session : ticketManager) {
    if (session.second != NULL) {
      delete session.second;
    }
  }
}

void RpcChannelImp::CallMethod(const MethodDescriptor* method,
                               RpcController *controller,
                               const Message *request,
                               Message *response,
                               Closure *done) {
  if (done != NULL) {  // 异步
    this->async_all(method, controller, request, response, done);
  } else {  // 同步
    int ret = this->sync_call(method, controller, request, response);
    if (ret == 0) {
      return;
    }
    perror("sync call error");
  }
}

sails::ResponsePacket* RpcChannelImp::parser(
    net::Connector *connector) {

  if (connector->readable() < sizeof(int)) {
    return NULL;
  }
  const char* buffer = connector->peek();
  int packetLen = *(reinterpret_cast<const int*>(buffer));
  if (connector->readable() < packetLen + sizeof(int)) {
    return NULL;
  }
  // printf("parse packet len:%d\n", packetLen);

  ResponsePacket* response = new ResponsePacket();
  if (response->ParseFromArray(buffer+sizeof(int), packetLen)) {
    connector->retrieve(packetLen + sizeof(int));
    return response;
  } else {
    // 出错
    delete response;
    connector->retrieve(connector->readable());
  }

  return NULL;
}

void RpcChannelImp::async_all(const google::protobuf::MethodDescriptor *method,
                 google::protobuf::RpcController* ,
                 const google::protobuf::Message* request,
                 google::protobuf::Message* response,
               google::protobuf::Closure* done) {
  if (stop) {
    done->Run();
    return;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(10);

  if (request_list.push_back(packet)) {
    ticketManager[sn] = new TicketSession(sn, response, done);
  } else {
    perror("async call too fast");
  }
}

int RpcChannelImp::sync_call(const google::protobuf::MethodDescriptor *method,
                             google::protobuf::RpcController *,
                             const google::protobuf::Message *request,
                             google::protobuf::Message *response) {
  if (stop) {
    return -1;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(10);

  request_list.push_back(packet);
  ticketManager[sn] = new TicketSession(sn, response, NULL);

  // wait notify
  while (ticketManager[sn]->errcode == 1) {
    ticketManager[sn]->notify.wait(locker);
  }
  return ticketManager[sn]->errcode;
}

void RpcChannelImp::send_request(RpcChannelImp* channel) {
  while (!channel->stop) {
    RequestPacket *request = NULL;
    channel->request_list.pop_front(request, 100);
    if (request != NULL) {
      std::string data = request->SerializeAsString();

      int len = data.length();
      channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
      channel->connector->write(data.c_str(), data.length());
      if (channel->connector->send() > 0) {
        delete request;
      } else {
        // 0表示连接关闭，小于0表示出错
        // 这里不主动设置stop的标识，由recv_response来设置，这样可以由它来
        // 处理后续回调和通知
        return;
      }
    }
  }
}

void RpcChannelImp::recv_response(RpcChannelImp* channel) {
  while (!channel->stop) {
    // 为了结束时防止阻塞到这里不能返回，通过调用close使它返回
    int n = channel->connector->read();
    // printf("read:n:%d\n", n);
    if (n > 0) {
      bool continueParse = false;
      do {
        continueParse = false;
        if (channel->connector->readable() == 0) {
          break;  // 解析一次完成
        }
        ResponsePacket *resp = RpcChannelImp::parser(channel->connector);
        if (resp != NULL) {
          if (resp->ret() == ErrorCode::ERR_SUCCESS) {
            if (channel->ticketManager[resp->sn()] != NULL) {
              resp->detail().UnpackTo(
                  channel->ticketManager[resp->sn()]->response);
              channel->ticketManager[resp->sn()]->errcode = 0;
              if (channel->ticketManager[resp->sn()]->done != NULL) {
                // 异步，那么就在这里调用
                channel->ticketManager[resp->sn()]->done->Run();
              } else {
                // 同步，通知调用线程
                std::unique_lock<std::mutex> locker(channel->request_mutex);
                channel->ticketManager[resp->sn()]->notify.notify_all();
              }
              continueParse = true;
            }
          } else {
            char msg[50];
            snprintf(msg, sizeof(msg), "get a response for error_code %d",
                     resp->ret());
            perror(msg);
          }
          delete(resp);
        } else {
          // 接收的数据不够，等待再次接收后再解析
          continueParse = false;
        }
      } while (continueParse);
    } else {  // 0表示连接被关闭, 小于0，出错
      // perror("recv");
      // 由于response是一个自定义的结构
      // 所以没有errcode之类的标识，这里就只能由用户自己通过response没有
      // 设置来得知是出错了
      channel->stop = true;
      for (auto ticket : channel->ticketManager) {
        if (ticket.second != NULL) {
          ticket.second->errcode = -1;
          if (ticket.second->done == NULL) {  // 是同步，通知主线程返回
            std::unique_lock<std::mutex> locker(channel->request_mutex);
            channel->ticketManager[ticket.first]->notify.notify_all();
          } else {  // 异步
            ticket.second->done->Run();
          }
        }
      }
      return;
    }
  }
}

}  // namespace sails
<commit_msg>update<commit_after>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: client_rpc_channel.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-23 16:03:07

#include "src/rpc_channel.h"
#include <stdio.h>
#include <limits.h>
#include <assert.h>
#include <iostream>
#include <sstream>
#include "sails/net/connector.h"
#include "src/saf_packet.pb.h"
#include "src/saf_const.h"
#include "src/version.h"
#include "google/protobuf/message.h"
#include "google/protobuf/descriptor.h"

using namespace google::protobuf;  // NOLINT

namespace sails {

RpcChannelImp::RpcChannelImp(string ip, int port):
    ip(ip), port(port) {
  connector = new net::Connector();
  assert(connector->connect(this->ip.c_str(), this->port, true));
  sn = 0;
  stop = false;
  recv_thread = new std::thread(recv_response, this);
  send_thread = new std::thread(send_request, this);
}

RpcChannelImp::~RpcChannelImp() {
  if (!stop) {
    stop = true;
    connector->close();
    recv_thread->join();
    send_thread->join();
  }
  if (recv_thread != NULL) {
    delete recv_thread;
  }
  if (send_thread != NULL) {
    delete send_thread;
  }
  if (connector != NULL) {
    delete connector;
    connector = NULL;
  }
  for (auto session : ticketManager) {
    if (session.second != NULL) {
      delete session.second;
    }
  }
}

void RpcChannelImp::CallMethod(const MethodDescriptor* method,
                               RpcController *controller,
                               const Message *request,
                               Message *response,
                               Closure *done) {
  if (done != NULL) {  // 异步
    this->async_all(method, controller, request, response, done);
  } else {  // 同步
    if (this->sync_call(method, controller, request, response) != 0) {
      printf("sync call error\n");
    }
  }
}

sails::ResponsePacket* RpcChannelImp::parser(
    net::Connector *connector) {

  if (connector->readable() < sizeof(int)) {
    return NULL;
  }
  const char* buffer = connector->peek();
  int packetLen = *(reinterpret_cast<const int*>(buffer));
  if (connector->readable() < packetLen + sizeof(int)) {
    return NULL;
  }
  // printf("parse packet len:%d\n", packetLen);

  ResponsePacket* response = new ResponsePacket();
  if (response->ParseFromArray(buffer+sizeof(int), packetLen)) {
    connector->retrieve(packetLen + sizeof(int));
    return response;
  } else {
    // 出错
    delete response;
    connector->retrieve(connector->readable());
  }

  return NULL;
}

void RpcChannelImp::async_all(const google::protobuf::MethodDescriptor *method,
                 google::protobuf::RpcController* ,
                 const google::protobuf::Message* request,
                 google::protobuf::Message* response,
               google::protobuf::Closure* done) {
  if (stop) {
    done->Run();
    return;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(10);

  if (request_list.push_back(packet)) {
    ticketManager[sn] = new TicketSession(sn, response, done);
  } else {
    perror("async call too fast");
  }
}

int RpcChannelImp::sync_call(const google::protobuf::MethodDescriptor *method,
                             google::protobuf::RpcController *,
                             const google::protobuf::Message *request,
                             google::protobuf::Message *response) {
  if (stop) {
    return -1;
  }
  std::unique_lock<std::mutex> locker(request_mutex);
  sn++;
  RequestPacket *packet = new RequestPacket();
  packet->set_version(VERSION_MAJOR*1000+VERSION_MINOR*100+VERSION_PATCH);
  packet->set_type(MessageType::RPC_REQUEST);
  packet->set_sn(sn);
  packet->set_servicename(method->service()->name());
  packet->set_funcname(method->name());
  packet->mutable_detail()->PackFrom(*request);
  packet->set_timeout(10);

  request_list.push_back(packet);
  ticketManager[sn] = new TicketSession(sn, response, NULL);

  // wait notify
  while (ticketManager[sn]->errcode == 1) {
    ticketManager[sn]->notify.wait(locker);
  }
  return ticketManager[sn]->errcode;
}

void RpcChannelImp::send_request(RpcChannelImp* channel) {
  while (!channel->stop) {
    RequestPacket *request = NULL;
    channel->request_list.pop_front(request, 100);
    if (request != NULL) {
      std::string data = request->SerializeAsString();

      int len = data.length();
      channel->connector->write(reinterpret_cast<char*>(&len), sizeof(int));
      channel->connector->write(data.c_str(), data.length());
      if (channel->connector->send() > 0) {
        delete request;
      } else {
        // 0表示连接关闭，小于0表示出错
        // 这里不主动设置stop的标识，由recv_response来设置，这样可以由它来
        // 处理后续回调和通知
        return;
      }
    }
  }
}

void RpcChannelImp::recv_response(RpcChannelImp* channel) {
  while (!channel->stop) {
    // 为了结束时防止阻塞到这里不能返回，通过调用close使它返回
    int n = channel->connector->read();
    // printf("read:n:%d\n", n);
    if (n > 0) {
      bool continueParse = false;
      do {
        continueParse = false;
        if (channel->connector->readable() == 0) {
          break;  // 解析一次完成
        }
        ResponsePacket *resp = RpcChannelImp::parser(channel->connector);
        if (resp != NULL) {
          if (resp->ret() == ErrorCode::ERR_SUCCESS) {
            if (channel->ticketManager[resp->sn()] != NULL) {
              resp->detail().UnpackTo(
                  channel->ticketManager[resp->sn()]->response);
              channel->ticketManager[resp->sn()]->errcode = 0;
              if (channel->ticketManager[resp->sn()]->done != NULL) {
                // 异步，那么就在这里调用
                channel->ticketManager[resp->sn()]->done->Run();
              } else {
                // 同步，通知调用线程
                std::unique_lock<std::mutex> locker(channel->request_mutex);
                channel->ticketManager[resp->sn()]->notify.notify_all();
              }
              continueParse = true;
            }
          } else {
            char msg[50];
            snprintf(msg, sizeof(msg), "get a response for error_code %d",
                     resp->ret());
            perror(msg);
          }
          delete(resp);
        } else {
          // 接收的数据不够，等待再次接收后再解析
          continueParse = false;
        }
      } while (continueParse);
    } else {  // 0表示连接被关闭, 小于0，出错
      // perror("recv");
      // 由于response是一个自定义的结构
      // 所以没有errcode之类的标识，这里就只能由用户自己通过response没有
      // 设置来得知是出错了
      channel->stop = true;
      for (auto ticket : channel->ticketManager) {
        if (ticket.second != NULL) {
          ticket.second->errcode = -1;
          if (ticket.second->done == NULL) {  // 是同步，通知主线程返回
            std::unique_lock<std::mutex> locker(channel->request_mutex);
            channel->ticketManager[ticket.first]->notify.notify_all();
          } else {  // 异步
            ticket.second->done->Run();
          }
        }
      }
      return;
    }
  }
}

}  // namespace sails
<|endoftext|>


<commit_before>/* This file is part of qjson
  *
  * Copyright (C) 2009 Till Adam <adam@kde.org>
  * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library 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
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public License
  * along with this library; see the file COPYING.LIB.  If not, write to
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */

#include "serializer.h"

#include <QtCore/QDataStream>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

#include <cmath>

using namespace QJson;

class Serializer::SerializerPrivate {
  public:
    SerializerPrivate() : specialNumbersAllowed(false) {}
    bool specialNumbersAllowed;
    QString sanitizeString( QString str );
};

QString Serializer::SerializerPrivate::sanitizeString( QString str )
{
  str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );

  // escape unicode chars
  QString result;
  const ushort* unicode = str.utf16();
  unsigned int i = 0;

  while ( unicode[ i ] ) {
    if ( unicode[ i ] < 128 ) {
      result.append( QChar( unicode[ i ] ) );
    }
    else {
      QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
                                                           QLatin1Char('0') );

      result.append( QLatin1String ("\\u") ).append( hexCode );
    }
    ++i;
  }
  str = result;

  str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
  str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
  str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
  str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
  str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
  str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );

  return QString( QLatin1String( "\"%1\"" ) ).arg( str );
}

Serializer::Serializer()
  : d( new SerializerPrivate )
{
}

Serializer::~Serializer() {
  delete d;
}

void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok )
{
  Q_ASSERT( io );
  if (!io->isOpen()) {
    if (!io->open(QIODevice::WriteOnly)) {
      if ( ok != 0 )
        *ok = false;
      qCritical ("Error opening device");
      return;
    }
  }

  if (!io->isWritable()) {
    if (ok != 0)
      *ok = false;
    qCritical ("Device is not readable");
    io->close();
    return;
  }

  const QByteArray str = serialize( v );
  if ( !str.isNull() ) {
    QDataStream stream( io );
    stream << str;
  } else {
    if ( ok )
      *ok = false;
  }
}

static QByteArray join( const QList<QByteArray>& list, const QByteArray& sep ) {
  QByteArray res;
  Q_FOREACH( const QByteArray& i, list ) {
    if ( !res.isEmpty() )
      res += sep;
    res += i;
  }
  return res;
}

QByteArray Serializer::serialize( const QVariant &v )
{
  QByteArray str;
  bool error = false;

  if ( ! v.isValid() ) { // invalid or null?
    str = "null";
  } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
    const QVariantList list = v.toList();
    QList<QByteArray> values;
    Q_FOREACH( const QVariant& v, list )
    {
      QByteArray serializedValue = serialize( v );
      if ( serializedValue.isNull() ) {
        error = true;
        break;
      }
      values << serializedValue;
    }
    str = "[ " + join( values, ", " ) + " ]";
  } else if ( v.type() == QVariant::Map ) { // variant is a map?
    const QVariantMap vmap = v.toMap();
    QMapIterator<QString, QVariant> it( vmap );
    str = "{ ";
    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      QByteArray serializedValue = serialize( it.value() );
      if ( serializedValue.isNull() ) {
        error = true;
        break;
      }
      pairs << d->sanitizeString( it.key() ).toUtf8() + " : " + serializedValue;
    }
    str += join( pairs, ", " );
    str += " }";
  } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
    str = d->sanitizeString( v.toString() ).toUtf8();
  } else if (( v.type() == QVariant::Double) || (v.type() == QMetaType::Float)) { // a double or a float?
    const double value = v.toDouble();
#ifdef _WIN32
    const bool special = _isnan(value) || !_finite(value);
#elif defined(Q_OS_SYMBIAN)
    const bool special = isnan(value) || isinf(value);
#else
    const bool special = std::isnan(value) || std::isinf(value);
#endif
    if (special) {
      if (specialNumbersAllowed()) {
#ifdef _WIN32
        if (_isnan(value)) {
#elif defined(Q_OS_SYMBIAN)
        if (isnan(value)) {
#else
        if (std::isnan(value)) {
#endif
          str += "NaN";
        } else {
          if (value<0) {
            str += "-";
          }
          str += "Infinity";
        }
      } else {
        qCritical("Attempt to write NaN or infinity, which is not supported by json");
        error = true;
    }
    } else {
      str = QByteArray::number( value );
      if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
        str += ".0";
      }
    }
  } else if ( v.type() == QVariant::Bool ) { // boolean value?
    str = ( v.toBool() ? "true" : "false" );
  } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
    str = QByteArray::number( v.value<qulonglong>() );
  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
    str = QByteArray::number( v.value<qlonglong>() );
  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
    // this will catch QDate, QDateTime, QUrl, ...
    str = d->sanitizeString( v.toString() ).toUtf8();
    //TODO: catch other values like QImage, QRect, ...
  } else {
    error = true;
  }
  if ( !error )
    return str;
  else
    return QByteArray();
}

void QJson::Serializer::allowSpecialNumbers(bool allow) {
  d->specialNumbersAllowed = allow;
}

bool QJson::Serializer::specialNumbersAllowed() const {
  return d->specialNumbersAllowed;
}
<commit_msg>Fix symbian building.<commit_after>/* This file is part of qjson
  *
  * Copyright (C) 2009 Till Adam <adam@kde.org>
  * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library 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
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public License
  * along with this library; see the file COPYING.LIB.  If not, write to
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */

#include "serializer.h"

#include <QtCore/QDataStream>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

#include <cmath>

using namespace QJson;

class Serializer::SerializerPrivate {
  public:
    SerializerPrivate() : specialNumbersAllowed(false) {}
    bool specialNumbersAllowed;
    QString sanitizeString( QString str );
};

QString Serializer::SerializerPrivate::sanitizeString( QString str )
{
  str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );

  // escape unicode chars
  QString result;
  const ushort* unicode = str.utf16();
  unsigned int i = 0;

  while ( unicode[ i ] ) {
    if ( unicode[ i ] < 128 ) {
      result.append( QChar( unicode[ i ] ) );
    }
    else {
      QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
                                                           QLatin1Char('0') );

      result.append( QLatin1String ("\\u") ).append( hexCode );
    }
    ++i;
  }
  str = result;

  str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
  str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
  str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
  str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
  str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
  str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );

  return QString( QLatin1String( "\"%1\"" ) ).arg( str );
}

Serializer::Serializer()
  : d( new SerializerPrivate )
{
}

Serializer::~Serializer() {
  delete d;
}

void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok )
{
  Q_ASSERT( io );
  if (!io->isOpen()) {
    if (!io->open(QIODevice::WriteOnly)) {
      if ( ok != 0 )
        *ok = false;
      qCritical ("Error opening device");
      return;
    }
  }

  if (!io->isWritable()) {
    if (ok != 0)
      *ok = false;
    qCritical ("Device is not readable");
    io->close();
    return;
  }

  const QByteArray str = serialize( v );
  if ( !str.isNull() ) {
    QDataStream stream( io );
    stream << str;
  } else {
    if ( ok )
      *ok = false;
  }
}

static QByteArray join( const QList<QByteArray>& list, const QByteArray& sep ) {
  QByteArray res;
  Q_FOREACH( const QByteArray& i, list ) {
    if ( !res.isEmpty() )
      res += sep;
    res += i;
  }
  return res;
}

QByteArray Serializer::serialize( const QVariant &v )
{
  QByteArray str;
  bool error = false;

  if ( ! v.isValid() ) { // invalid or null?
    str = "null";
  } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
    const QVariantList list = v.toList();
    QList<QByteArray> values;
    Q_FOREACH( const QVariant& v, list )
    {
      QByteArray serializedValue = serialize( v );
      if ( serializedValue.isNull() ) {
        error = true;
        break;
      }
      values << serializedValue;
    }
    str = "[ " + join( values, ", " ) + " ]";
  } else if ( v.type() == QVariant::Map ) { // variant is a map?
    const QVariantMap vmap = v.toMap();
    QMapIterator<QString, QVariant> it( vmap );
    str = "{ ";
    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      QByteArray serializedValue = serialize( it.value() );
      if ( serializedValue.isNull() ) {
        error = true;
        break;
      }
      pairs << d->sanitizeString( it.key() ).toUtf8() + " : " + serializedValue;
    }
    str += join( pairs, ", " );
    str += " }";
  } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
    str = d->sanitizeString( v.toString() ).toUtf8();
  } else if (( v.type() == QVariant::Double) || (v.type() == QMetaType::Float)) { // a double or a float?
    const double value = v.toDouble();
#ifdef _WIN32 && !defined(Q_OS_SYMBIAN)
    const bool special = _isnan(value) || !_finite(value);
#elif defined(Q_OS_SYMBIAN)
    const bool special = isnan(value) || isinf(value);
#else
    const bool special = std::isnan(value) || std::isinf(value);
#endif
    if (special) {
      if (specialNumbersAllowed()) {
#ifdef _WIN32 && !defined(Q_OS_SYMBIAN)
        if (_isnan(value)) {
#elif defined(Q_OS_SYMBIAN)
        if (isnan(value)) {
#else
        if (std::isnan(value)) {
#endif
          str += "NaN";
        } else {
          if (value<0) {
            str += "-";
          }
          str += "Infinity";
        }
      } else {
        qCritical("Attempt to write NaN or infinity, which is not supported by json");
        error = true;
    }
    } else {
      str = QByteArray::number( value );
      if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
        str += ".0";
      }
    }
  } else if ( v.type() == QVariant::Bool ) { // boolean value?
    str = ( v.toBool() ? "true" : "false" );
  } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
    str = QByteArray::number( v.value<qulonglong>() );
  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
    str = QByteArray::number( v.value<qlonglong>() );
  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
    // this will catch QDate, QDateTime, QUrl, ...
    str = d->sanitizeString( v.toString() ).toUtf8();
    //TODO: catch other values like QImage, QRect, ...
  } else {
    error = true;
  }
  if ( !error )
    return str;
  else
    return QByteArray();
}

void QJson::Serializer::allowSpecialNumbers(bool allow) {
  d->specialNumbersAllowed = allow;
}

bool QJson::Serializer::specialNumbersAllowed() const {
  return d->specialNumbersAllowed;
}
<|endoftext|>


<commit_before>//
// created by : Timothée Feuillet
// date: 2022-1-2
//
//
// Copyright (c) 2022 Timothée Feuillet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#pragma once

#include <set>
#include <vector>
#include <map>

#include "types.hpp"
#include "../id/id.hpp"

namespace neam::threading
{
  struct ir_opcode
  {
    enum opcode_type : uint16_t
    {
      declare_chain_index = 6,

      // NOTE: only one chain can have an execute_task_group for a given group
      execute_task_group = 16,
      // NOTE: unlike execute_task_group, as many chains as necessary can have a wait_task_group
      wait_task_group = 17,

      end_chain = 18,
    };

    opcode_type opcode;

    // execution chain is a set of linear dependencies
    // something like:
    //    exec group[1]
    //    exec group[2]
    //    wait group[1]
    //    exec group[3]
    //    wait group[3]
    //    wait group[4]
    //    exec group[5]
    //    exec group[6]
    //    ...
    //    end_chain chain[1]
    //    wait group[2]
    //    exec group[4]
    //    end_chain chain[2]
    // will produce the following:
    //    chain 1: group[1],group[2] -> group[3] + -> group[5],group[6]  ...
    //    chain 2:                  \->  group[4] /
    // where group[1] and group[3] can execute parallely to group[2] and group[4]
    // (so as to avoid contention on a specific group).
    // and is the result of the following calls:
    //  add_group(1);
    //  add_group(2);
    //  add_group(3);
    //  add_group(4);
    //  add_group(5);
    //  add_group(6);
    //  add_dependency(5, 3);
    //  add_dependency(5, 4);
    //  add_dependency(6, 3);
    //  add_dependency(6, 4);
    //  add_dependency(4, 2);
    //  add_dependency(3, 1);

    // a value of 0 is the invalid task_group/chain
    uint16_t arg = 0;

    static_assert(sizeof(arg) >= sizeof(group_t));
  };

  struct resolved_graph
  {
    std::map<id_t, group_t> groups;
    uint32_t chain_count;
    std::vector<ir_opcode> opcodes;
    std::map<group_t, std::string> debug_names;

    void print_debug() const;
  };

  /// \brief class that takes the task-group dependency tree and outputs IR
  /// \note Must be a tree, not a graph
  class task_group_dependency_tree
  {
    public:
      /// \brief Add a task group. Cannot be task::k_non_transient_task_group.
      group_t add_task_group(id_t id, std::string group_debug_name);

      /// \brief makes \p group dependent on \p dependency
      /// (\p group will not execute before \p dependency has completed all its tasks)
      void add_dependency(group_t group, group_t dependency);

      void add_dependency(id_t group, id_t dependency);

      /// \brief output the IR
      resolved_graph compile_tree();

    private:
      /// \brief Only keep the longest dependency chains
      bool canonicalize();

    private:
      std::map<id_t, group_t> group_names;

      struct links
      {
        // from -> tg -> to
        std::set<group_t> from;
        std::set<group_t> to;
      };
      std::set<group_t> roots;
      std::map<group_t, links> dependencies;
      std::map<group_t, std::string> debug_names;

      group_t task_group_id = 1;
  };

}

// So we can serialize the IR (and store it as an asset)
#include "../struct_metadata/struct_metadata.hpp"

N_METADATA_STRUCT(neam::threading::ir_opcode)
{
  using member_list = neam::ct::type_list
  <
    N_MEMBER_DEF(opcode),
    N_MEMBER_DEF(arg)
  >;
};

N_METADATA_STRUCT(neam::threading::resolved_graph)
{
  using member_list = neam::ct::type_list
  <
    N_MEMBER_DEF(groups),
    N_MEMBER_DEF(chain_count),
    N_MEMBER_DEF(opcodes)
  >;
};

<commit_msg>Add debug info to the serialized compiled graph<commit_after>//
// created by : Timothée Feuillet
// date: 2022-1-2
//
//
// Copyright (c) 2022 Timothée Feuillet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#pragma once

#include <set>
#include <vector>
#include <map>

#include "types.hpp"
#include "../id/id.hpp"

namespace neam::threading
{
  struct ir_opcode
  {
    enum opcode_type : uint16_t
    {
      declare_chain_index = 6,

      // NOTE: only one chain can have an execute_task_group for a given group
      execute_task_group = 16,
      // NOTE: unlike execute_task_group, as many chains as necessary can have a wait_task_group
      wait_task_group = 17,

      end_chain = 18,
    };

    opcode_type opcode;

    // execution chain is a set of linear dependencies
    // something like:
    //    exec group[1]
    //    exec group[2]
    //    wait group[1]
    //    exec group[3]
    //    wait group[3]
    //    wait group[4]
    //    exec group[5]
    //    exec group[6]
    //    ...
    //    end_chain chain[1]
    //    wait group[2]
    //    exec group[4]
    //    end_chain chain[2]
    // will produce the following:
    //    chain 1: group[1],group[2] -> group[3] + -> group[5],group[6]  ...
    //    chain 2:                  \->  group[4] /
    // where group[1] and group[3] can execute parallely to group[2] and group[4]
    // (so as to avoid contention on a specific group).
    // and is the result of the following calls:
    //  add_group(1);
    //  add_group(2);
    //  add_group(3);
    //  add_group(4);
    //  add_group(5);
    //  add_group(6);
    //  add_dependency(5, 3);
    //  add_dependency(5, 4);
    //  add_dependency(6, 3);
    //  add_dependency(6, 4);
    //  add_dependency(4, 2);
    //  add_dependency(3, 1);

    // a value of 0 is the invalid task_group/chain
    uint16_t arg = 0;

    static_assert(sizeof(arg) >= sizeof(group_t));
  };

  struct resolved_graph
  {
    std::map<id_t, group_t> groups;
    uint32_t chain_count;
    std::vector<ir_opcode> opcodes;
    std::map<group_t, std::string> debug_names;

    void print_debug() const;
  };

  /// \brief class that takes the task-group dependency tree and outputs IR
  /// \note Must be a tree, not a graph
  class task_group_dependency_tree
  {
    public:
      /// \brief Add a task group. Cannot be task::k_non_transient_task_group.
      group_t add_task_group(id_t id, std::string group_debug_name);

      /// \brief makes \p group dependent on \p dependency
      /// (\p group will not execute before \p dependency has completed all its tasks)
      void add_dependency(group_t group, group_t dependency);

      void add_dependency(id_t group, id_t dependency);

      /// \brief output the IR
      resolved_graph compile_tree();

    private:
      /// \brief Only keep the longest dependency chains
      bool canonicalize();

    private:
      std::map<id_t, group_t> group_names;

      struct links
      {
        // from -> tg -> to
        std::set<group_t> from;
        std::set<group_t> to;
      };
      std::set<group_t> roots;
      std::map<group_t, links> dependencies;
      std::map<group_t, std::string> debug_names;

      group_t task_group_id = 1;
  };

}

// So we can serialize the IR (and store it as an asset)
#include "../struct_metadata/struct_metadata.hpp"

N_METADATA_STRUCT(neam::threading::ir_opcode)
{
  using member_list = neam::ct::type_list
  <
    N_MEMBER_DEF(opcode),
    N_MEMBER_DEF(arg)
  >;
};

N_METADATA_STRUCT(neam::threading::resolved_graph)
{
  using member_list = neam::ct::type_list
  <
    N_MEMBER_DEF(groups),
    N_MEMBER_DEF(chain_count),
    N_MEMBER_DEF(opcodes),
    N_MEMBER_DEF(debug_names)
  >;
};

<|endoftext|>


<commit_before>// simulation.cpp

#include <random>
#include <iostream>
#include <set>
#include <algorithm>

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>

#include "utility.h"
#include "random_gens.h"
#include "movetypes.h"
#include "simulation.h"

using std::cout;
using std::min;

namespace mpi = boost::mpi;

using namespace Movetypes;
using namespace Simulation;
using namespace Utility;
using namespace RandomGen;

GCMCSimulation::GCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        m_origami_system {origami_system} {

    m_logging_freq = params.m_logging_freq;
    m_centering_freq = params.m_centering_freq;
    m_movetype_constructors = params.m_movetype_constructors;

    // Create cumulative probability array
    double cum_prob {0};
    for (size_t i {0}; i != m_movetype_constructors.size(); i++) {
        cum_prob += params.m_movetype_probs[i];
        m_cumulative_probs.push_back(cum_prob);
    }
}

void GCMCSimulation::simulate(int steps, int start_step) {

    for (int step {start_step + 1}; step != (steps + start_step + 1); step ++) {
        unique_ptr<MCMovetype> movetype {select_movetype()};
        bool accepted;
        accepted = movetype->attempt_move();

        if (not accepted) {
            movetype->reset_origami();
        }

        if (m_centering_freq != 0 and step % m_centering_freq == 0) {
            m_origami_system.centre();
            m_origami_system.check_all_constraints();
        }

        // Write log entry to standard out
        if (m_logging_freq !=0 and step % m_logging_freq == 0) {
            write_log_entry(step, *movetype, accepted);
        }

        // Write system properties to file
        for (auto output_file: m_output_files) {
            if (output_file->m_write_freq != 0 and step % output_file->m_write_freq == 0) {
                output_file->write(step);
            }
        }
    }
}

unique_ptr<MCMovetype> GCMCSimulation::select_movetype() {
    unique_ptr<MCMovetype> movetype;
    double prob {m_random_gens.uniform_real()};
    for (size_t i {0}; i != m_cumulative_probs.size(); i++) {
        if (prob < m_cumulative_probs[i]) {
            movetype = m_movetype_constructors[i](m_origami_system,
                    m_random_gens, m_ideal_random_walks);
            break;
        }
    }
    return movetype;
}

void GCMCSimulation::write_log_entry(int step, MCMovetype& movetype,
        bool accepted) {

    *m_logging_stream << "Step: " << step << " ";
    *m_logging_stream << "Movetype: " << movetype.m_label() << " ";
    *m_logging_stream << "Staples: " << m_origami_system.num_staples() << " ";
    *m_logging_stream << "Accepted: " << accepted << " ";
    *m_logging_stream << "Temp: " << m_origami_system.m_temp << " ";
    *m_logging_stream << "Energy: " << m_origami_system.energy() << " ";
    *m_logging_stream << "\n";
}

ConstantTGCMCSimulation::ConstantTGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_steps {params.m_steps} {

    m_logging_stream = &cout;
    setup_output_files(params, params.m_output_filebase, m_origami_system);
}

AnnealingGCMCSimulation::AnnealingGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_max_temp {params.m_max_temp},
        m_min_temp {params.m_min_temp},
        m_temp_interval {params.m_temp_interval},
        m_steps_per_temp {params.m_steps_per_temp} {

    if (fmod(m_max_temp - m_min_temp, m_temp_interval) != 0) {
        cout << "Bad temperature interval";
    }
    m_logging_stream = &cout;
    setup_output_files(params, params.m_output_filebase, m_origami_system);
}

void AnnealingGCMCSimulation::run() {
    double temp {m_max_temp};
    int step {0};
    while (temp >= m_min_temp) {
        m_origami_system.update_temp(temp);
        simulate(m_steps_per_temp, step);
        temp -= m_temp_interval;
        step += m_steps_per_temp;
    }
}

PTGCMCSimulation::PTGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_num_reps {params.m_num_reps},
        m_exchange_interval {params.m_exchange_interval} {

    m_swaps = params.m_steps / m_exchange_interval;
    string string_rank {std::to_string(m_rank)};
    string output_filebase {params.m_output_filebase + "-" + string_rank};
    setup_output_files(params, output_filebase, m_origami_system);
    m_logging_stream = new ofstream {output_filebase + ".out"};

    // Initialize temp of each replica
    for (int i {0}; i != m_num_reps; i++) {
        if (m_rank == i) {
            m_temp = params.m_temps[i];
        }
    }

    // Initialize temperature and temperature index to replica index vectors
    if (m_rank == m_master_rep) {
        m_temps = params.m_temps;
        for (int i {0}; i != m_num_reps; i++) {
            m_tempi_to_repi.push_back(i);
        }
    }

    // Initalize swap file
    if (m_rank == m_master_rep) {
        m_swapfile.open(params.m_output_filebase + ".swp");
        for (auto temp: m_temps) {
            m_swapfile <<  temp << " ";
        }
        m_swapfile << "\n";
    }
}

void PTGCMCSimulation::run() {
    int step {0};
    vector<int> attempt_count(m_num_reps - 1, 0);
    vector<int> swap_count(m_num_reps - 1, 0);
    for (int swap_i {1}; swap_i != m_swaps + 1; swap_i++) {
        m_origami_system.update_temp(m_temp);
        simulate(m_exchange_interval, step);
        step += m_exchange_interval;
        if (m_rank != m_master_rep) {
            send_and_recieve_exchange_info(swap_i);
        }
        else {
            attempt_exchange(swap_i, attempt_count, swap_count);
            m_temp = m_temps[m_master_rep];
            write_swap_entry();
        }
    }
    if (m_world.rank() == m_master_rep) {
        write_acceptance_freqs(attempt_count, swap_count);
    }
}

void PTGCMCSimulation::send_and_recieve_exchange_info(int swap_i) {

    double energy {m_origami_system.energy()};
    m_world.send(m_master_rep, swap_i, energy);
    m_world.recv(m_master_rep, swap_i, m_temp);
}

void PTGCMCSimulation::attempt_exchange(int swap_i,
        vector<int>& attempt_count, vector<int>& swap_count) {

    // Collect results and add own
    vector<double> energies {m_origami_system.energy()};
    for (int rep_i {1}; rep_i != m_num_reps; rep_i++) {
        pair<double, int> msg;
        m_world.recv(rep_i, swap_i, msg);
        energies.push_back(msg.first);
    }

    // Iterate through pairs in current set and attempt swap
    int swap_set {swap_i % 2};
    for (int i {swap_set}; i < (m_num_reps - swap_set); i += 2) {
        attempt_count[i]++;

        // Collect values
        double temp1 {m_temps[i]};
        double temp2 {m_temps[i + 1]};
        int repi1 {m_tempi_to_repi[i]};
        int repi2 {m_tempi_to_repi[i + 1]};

        // Energies are actually E/B, so multiply by T
        double energy1 {energies[repi1] * temp1};
        double energy2 {energies[repi2] * temp2};
        bool accept {test_acceptance(temp1, temp2, energy1, energy2)};
        if (accept) {
            swap_count[i]++;
            int repi1_old {repi1};
            repi1 = repi2;
            repi2 = repi1_old;
            m_tempi_to_repi[i] = repi1;
            m_tempi_to_repi[i + 1] = repi2;
        }
    }

    // Send temps
    for (int rep_i {1}; rep_i != m_num_reps; rep_i++) {
        m_world.send(rep_i, swap_i, m_temps[rep_i]);
    }
}

bool PTGCMCSimulation::test_acceptance(double temp1, double temp2, double energy1,
        double energy2) {

    double DB {1/temp2 - 1/temp1};
    double DE {energy2 - energy1};
    double p_accept {min({1.0, exp(-DB*DE)})};
    bool accept;
    if (p_accept == 1) {
        accept = true;
    }
    else {
        double prob {m_random_gens.uniform_real()};
        if (p_accept > prob) {
            accept = true;
        }
        else {
            accept = false;
        }
    }

    return accept;
}

void PTGCMCSimulation::write_swap_entry() {
    for (auto repi: m_tempi_to_repi) {
        m_swapfile << repi << " ";
    }
    m_swapfile << "\n";
}

void PTGCMCSimulation::write_acceptance_freqs(vector<int> attempt_count,
        vector<int> swap_count) {

    for (size_t i {0}; i != attempt_count.size(); i++) {
        cout << m_temps[i] << " ";
        cout << m_temps[i + 1] << " ";
        cout << swap_count[i] << " ";
        cout << attempt_count[i] << " ";
        cout << (static_cast<double>(swap_count[i]) / attempt_count[i]) << " ";
        cout << "\n";
    }
    cout << "\n";
}

vector<OrigamiOutputFile*> Simulation::setup_output_files(
        InputParameters params, string output_filebase,
        OrigamiSystem& origami) {

    vector<OrigamiOutputFile*> outs {};
    if (params.m_configs_output_freq != 0) {
        OrigamiOutputFile* config_out = new OrigamiTrajOutputFile {
                output_filebase + ".trj", params.m_configs_output_freq,
                origami};
        outs.push_back(config_out);
        OrigamiOutputFile* counts_out = new OrigamiCountsOutputFile {
                output_filebase + ".counts", params.m_counts_output_freq,
                origami};
        outs.push_back(counts_out);
    }

    return outs;
}
<commit_msg>Changed to attempt fix for cluster<commit_after>// simulation.cpp

#include <random>
#include <iostream>
#include <set>
#include <algorithm>

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>

#include "utility.h"
#include "random_gens.h"
#include "movetypes.h"
#include "simulation.h"

using std::cout;
using std::min;

namespace mpi = boost::mpi;

using namespace Movetypes;
using namespace Simulation;
using namespace Utility;
using namespace RandomGen;

GCMCSimulation::GCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        m_origami_system {origami_system} {

    m_logging_freq = params.m_logging_freq;
    m_centering_freq = params.m_centering_freq;
    m_movetype_constructors = params.m_movetype_constructors;

    // Create cumulative probability array
    double cum_prob {0};
    for (size_t i {0}; i != m_movetype_constructors.size(); i++) {
        cum_prob += params.m_movetype_probs[i];
        m_cumulative_probs.push_back(cum_prob);
    }
}

void GCMCSimulation::simulate(int steps, int start_step) {

    for (int step {start_step + 1}; step != (steps + start_step + 1); step ++) {
        unique_ptr<MCMovetype> movetype {select_movetype()};
        bool accepted;
        accepted = movetype->attempt_move();

        if (not accepted) {
            movetype->reset_origami();
        }

        if (m_centering_freq != 0 and step % m_centering_freq == 0) {
            m_origami_system.centre();
            m_origami_system.check_all_constraints();
        }

        // Write log entry to standard out
        if (m_logging_freq !=0 and step % m_logging_freq == 0) {
            write_log_entry(step, *movetype, accepted);
        }

        // Write system properties to file
        for (auto output_file: m_output_files) {
            if (output_file->m_write_freq != 0 and step % output_file->m_write_freq == 0) {
                output_file->write(step);
            }
        }
    }
}

unique_ptr<MCMovetype> GCMCSimulation::select_movetype() {
    unique_ptr<MCMovetype> movetype;
    double prob {m_random_gens.uniform_real()};
    for (size_t i {0}; i != m_cumulative_probs.size(); i++) {
        if (prob < m_cumulative_probs[i]) {
            movetype = m_movetype_constructors[i](m_origami_system,
                    m_random_gens, m_ideal_random_walks);
            break;
        }
    }
    return movetype;
}

void GCMCSimulation::write_log_entry(int step, MCMovetype& movetype,
        bool accepted) {

    *m_logging_stream << "Step: " << step << " ";
    *m_logging_stream << "Movetype: " << movetype.m_label() << " ";
    *m_logging_stream << "Staples: " << m_origami_system.num_staples() << " ";
    *m_logging_stream << "Accepted: " << accepted << " ";
    *m_logging_stream << "Temp: " << m_origami_system.m_temp << " ";
    *m_logging_stream << "Energy: " << m_origami_system.energy() << " ";
    *m_logging_stream << "\n";
}

ConstantTGCMCSimulation::ConstantTGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_steps {params.m_steps} {

    m_logging_stream = &cout;
    setup_output_files(params, params.m_output_filebase, m_origami_system);
}

AnnealingGCMCSimulation::AnnealingGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_max_temp {params.m_max_temp},
        m_min_temp {params.m_min_temp},
        m_temp_interval {params.m_temp_interval},
        m_steps_per_temp {params.m_steps_per_temp} {

    if (fmod(m_max_temp - m_min_temp, m_temp_interval) != 0) {
        cout << "Bad temperature interval";
    }
    m_logging_stream = &cout;
    setup_output_files(params, params.m_output_filebase, m_origami_system);
}

void AnnealingGCMCSimulation::run() {
    double temp {m_max_temp};
    int step {0};
    while (temp >= m_min_temp) {
        m_origami_system.update_temp(temp);
        simulate(m_steps_per_temp, step);
        temp -= m_temp_interval;
        step += m_steps_per_temp;
    }
}

PTGCMCSimulation::PTGCMCSimulation(OrigamiSystem& origami_system,
        InputParameters params) :
        GCMCSimulation(origami_system, params),
        m_num_reps {params.m_num_reps},
        m_exchange_interval {params.m_exchange_interval} {

    m_swaps = params.m_steps / m_exchange_interval;
    string string_rank {std::to_string(m_rank)};
    string output_filebase {params.m_output_filebase + "-" + string_rank};
    setup_output_files(params, output_filebase, m_origami_system);
    m_logging_stream = new ofstream {output_filebase + ".out"};

    // Initialize temp of each replica
    for (int i {0}; i != m_num_reps; i++) {
        if (m_rank == i) {
            m_temp = params.m_temps[i];
        }
    }

    // Initialize temperature and temperature index to replica index vectors
    if (m_rank == m_master_rep) {
        m_temps = params.m_temps;
        for (int i {0}; i != m_num_reps; i++) {
            m_tempi_to_repi.push_back(i);
        }
    }

    // Initalize swap file
    if (m_rank == m_master_rep) {
        m_swapfile.open(params.m_output_filebase + ".swp");
        for (auto temp: m_temps) {
            m_swapfile <<  temp << " ";
        }
        m_swapfile << "\n";
    }
}

void PTGCMCSimulation::run() {
    int step {0};
    vector<int> attempt_count(m_num_reps - 1, 0);
    vector<int> swap_count(m_num_reps - 1, 0);
    for (int swap_i {1}; swap_i != m_swaps + 1; swap_i++) {
        m_origami_system.update_temp(m_temp);
        simulate(m_exchange_interval, step);
        step += m_exchange_interval;
        if (m_rank != m_master_rep) {
            send_and_recieve_exchange_info(swap_i);
        }
        else {
            attempt_exchange(swap_i, attempt_count, swap_count);
            m_temp = m_temps[m_master_rep];
            write_swap_entry();
        }
    }
    if (m_rank == m_master_rep) {
        write_acceptance_freqs(attempt_count, swap_count);
    }
}

void PTGCMCSimulation::send_and_recieve_exchange_info(int swap_i) {

    double energy {m_origami_system.energy()};
    m_world.send(m_master_rep, swap_i, energy);
    m_world.recv(m_master_rep, swap_i, m_temp);
}

void PTGCMCSimulation::attempt_exchange(int swap_i,
        vector<int>& attempt_count, vector<int>& swap_count) {

    // Collect results and add own
    vector<double> energies {m_origami_system.energy()};
    for (int rep_i {1}; rep_i != m_num_reps; rep_i++) {
        pair<double, int> msg;
        m_world.recv(rep_i, swap_i, msg);
        energies.push_back(msg.first);
    }

    // Iterate through pairs in current set and attempt swap
    int swap_set {swap_i % 2};
    for (int i {swap_set}; i < (m_num_reps - swap_set); i += 2) {
        attempt_count[i]++;

        // Collect values
        double temp1 {m_temps[i]};
        double temp2 {m_temps[i + 1]};
        int repi1 {m_tempi_to_repi[i]};
        int repi2 {m_tempi_to_repi[i + 1]};

        // Energies are actually E/B, so multiply by T
        double energy1 {energies[repi1] * temp1};
        double energy2 {energies[repi2] * temp2};
        bool accept {test_acceptance(temp1, temp2, energy1, energy2)};
        if (accept) {
            swap_count[i]++;
            int repi1_old {repi1};
            repi1 = repi2;
            repi2 = repi1_old;
            m_tempi_to_repi[i] = repi1;
            m_tempi_to_repi[i + 1] = repi2;
        }
    }

    // Send temps
    for (int rep_i {1}; rep_i != m_num_reps; rep_i++) {
        m_world.send(rep_i, swap_i, m_temps[rep_i]);
    }
}

bool PTGCMCSimulation::test_acceptance(double temp1, double temp2, double energy1,
        double energy2) {

    double DB {1/temp2 - 1/temp1};
    double DE {energy2 - energy1};
    double p_accept {min({1.0, exp(-DB*DE)})};
    bool accept;
    if (p_accept == 1) {
        accept = true;
    }
    else {
        double prob {m_random_gens.uniform_real()};
        if (p_accept > prob) {
            accept = true;
        }
        else {
            accept = false;
        }
    }

    return accept;
}

void PTGCMCSimulation::write_swap_entry() {
    for (auto repi: m_tempi_to_repi) {
        m_swapfile << repi << " ";
    }
    m_swapfile << "\n";
}

void PTGCMCSimulation::write_acceptance_freqs(vector<int> attempt_count,
        vector<int> swap_count) {

    for (size_t i {0}; i != attempt_count.size(); i++) {
        cout << m_temps[i] << " ";
        cout << m_temps[i + 1] << " ";
        cout << swap_count[i] << " ";
        cout << attempt_count[i] << " ";
        cout << (static_cast<double>(swap_count[i]) / attempt_count[i]) << " ";
        cout << "\n";
    }
    cout << "\n";
}

vector<OrigamiOutputFile*> Simulation::setup_output_files(
        InputParameters params, string output_filebase,
        OrigamiSystem& origami) {

    vector<OrigamiOutputFile*> outs {};
    if (params.m_configs_output_freq != 0) {
        OrigamiOutputFile* config_out = new OrigamiTrajOutputFile {
                output_filebase + ".trj", params.m_configs_output_freq,
                origami};
        outs.push_back(config_out);
        OrigamiOutputFile* counts_out = new OrigamiCountsOutputFile {
                output_filebase + ".counts", params.m_counts_output_freq,
                origami};
        outs.push_back(counts_out);
    }

    return outs;
}
<|endoftext|>