Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CFLAGS += -O3 -flto=auto -march=native
CXXFLAGS = $(CFLAGS)
LDFLAGS = $(CXXFLAGS)
LDLIBS = -lm
OBJS = partdiff.o askparams.o
OBJS = partdiff.o argument_parser.o calculation_arguments.o tensor.o

default: all

Expand Down
194 changes: 194 additions & 0 deletions argument_parser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include "argument_parser.hpp"
#include "calculation_options.hpp"
#include <format>
#include <print>
#include <sstream>

namespace partdiff {

template <typename T, typename U = std::underlying_type_t<T>>
U to_underlying(T v) {
return static_cast<U>(v);
}

static constexpr uint64_t max_interlines = 10240;
static constexpr uint64_t max_iteration = 200000;
static constexpr uint64_t max_threads = 1024;
static constexpr double min_accuracy = 1e-4;
static constexpr double max_accuracy = 1e-20;

using calculation_method = calculation_options::calculation_method;
using perturbation_function = calculation_options::perturbation_function;
using termination_condition = calculation_options::termination_condition;

argument_parser::argument_parser(const int argc, char const *argv[])
: app_name(argv[0]),
args(argv + 1, argv + argc) {
this->fill_argument_descriptions();
this->ask_params();
}

calculation_options argument_parser::get_options() {
return this->options;
}

argument_parser::argument_description argument_parser::get_description(std::size_t index) const {
return this->argument_descriptions[index];
}

argument_parser::argument_description argument_parser::get_description(argument_index index) const {
return this->get_description(to_underlying(index));
}

void argument_parser::usage() const {
const auto get_name = [](const std::string &input) { return std::format(" - {:11}", input + ":"); };

std::print("Usage: {}", this->app_name);
for (std::size_t i = 0; i <= to_underlying(argument_index::term_dummy); i++) {
std::print(" [{}]", get_description(i).name);
}
std::println("");
std::println("");
for (std::size_t i = 0; i <= to_underlying(argument_index::term_dummy); i++) {
const std::string description = this->get_description(i).description_for_usage.value_or("<invalid>");
std::println("{}{}", get_name(this->get_description(i).name), description);
}
std::println("Example: {} 1 2 100 1 2 100", app_name);
}

void argument_parser::ask_params() {
if (this->args.size() < 6) {
usage();
exit(EXIT_SUCCESS);
}
for (std::size_t i = 0; i <= to_underlying(argument_index::termination); i++) {
parse_param(i, args[i]);
}
if (this->options.termination == termination_condition::accuracy) {
parse_param(argument_index::term_accuracy, args[5]);
this->options.term_iteration = partdiff::max_iteration;
} else {
parse_param(argument_index::term_iteration, args[5]);
this->options.term_accuracy = 0.0;
}
}

void argument_parser::parse_param(argument_index index, std::string &input) {
this->parse_param(to_underlying(index), input);
}

void argument_parser::parse_param(std::size_t index, std::string &input) {
if (!this->get_description(index).read_from_string(input)) {
this->usage();
exit(EXIT_FAILURE);
}
}

void argument_parser::fill_argument_descriptions() {

auto scientific_double = [](double val) {
auto temp = std::format("{:.0e}", val);
int epos = temp.find("e");
std::string mantissa_str = temp.substr(0, epos);
std::string exponent_str = temp.substr(epos + 1, temp.length() - epos - 1);
int exponent = stoi(exponent_str);
return mantissa_str + "e" + std::to_string(exponent);
};

constexpr int indent_width = 17;
const std::string indent = std::format("{:{}s}", "", indent_width);

auto number = &(this->options.number);
this->add_argument_description("num", number, std::format("number of threads (1 .. {:d})", partdiff::max_threads),
[number] { return (*number >= 1 && *number <= partdiff::max_threads); });

auto method = &(this->options.method);
this->add_argument_description(
"method", method,
std::format("calculation method (1 .. 2)\n"
"{0}{1:d}: Gauß-Seidel\n"
"{0}{2:d}: Jacobi",
indent, to_underlying(calculation_method::gauss_seidel), to_underlying(calculation_method::jacobi)),
[method] { return (*method == calculation_method::gauss_seidel || *method == calculation_method::jacobi); });

auto interlines = &(this->options.interlines);
this->add_argument_description("lines", interlines,
std::format("number of interlines (0 .. {1:d})\n"
"{0}matrixsize = (interlines * 8) + 9",
indent, partdiff::max_interlines),
[interlines] { return (*interlines <= partdiff::max_interlines); });

auto pert_func = &(this->options.pert_func);
this->add_argument_description(
"func", pert_func,
std::format("perturbation function (1 .. 2)\n"
"{0}{1:d}: f(x,y) = 0\n"
"{0}{2:d}: f(x,y) = 2 * pi^2 * sin(pi * x) * sin(pi * y)",
indent, to_underlying(perturbation_function::f0), to_underlying(perturbation_function::fpisin)),
[pert_func] {
return (*pert_func == perturbation_function::f0 || *pert_func == perturbation_function::fpisin);
});

auto termination = &(this->options.termination);
this->add_argument_description("term", termination,
std::format("termination condition ( 1.. 2)\n"
"{0}{1:d}: sufficient accuracy\n"
"{0}{2:d}: number of iterations",
indent, to_underlying(termination_condition::accuracy),
to_underlying(termination_condition::iterations)),
[termination] {
return (*termination == termination_condition::accuracy ||
*termination == termination_condition::iterations);
});

this->add_argument_description("acc/iter",
std::format("depending on term:\n"
"{0}accuracy: {1:s} .. {2:s}\n"
"{0}iterations: 1 .. {3:d}\n",
indent, scientific_double(partdiff::min_accuracy),
scientific_double(partdiff::max_accuracy), partdiff::max_iteration));

auto term_accuracy = &(this->options.term_accuracy);
this->add_argument_description("acc", term_accuracy, std::nullopt, [term_accuracy] {
return (*term_accuracy >= partdiff::max_accuracy && *term_accuracy <= partdiff::min_accuracy);
});

auto term_iteration = &(this->options.term_iteration);
this->add_argument_description("iter", term_iteration, std::nullopt, [term_iteration] {
return (*term_iteration >= 1 && *term_iteration <= partdiff::max_iteration);
});
}

void argument_parser::add_argument_description(std::string name, std::optional<std::string> description_for_usage) {
argument_description arg_desc;
arg_desc.name = name;
arg_desc.description_for_usage = description_for_usage;
this->argument_descriptions.push_back(arg_desc);
}

template <class T>
void argument_parser::add_argument_description(std::string name, T *target,
std::optional<std::string> description_for_usage,
std::function<bool()> check) {
argument_description arg_desc;
arg_desc.name = name;
arg_desc.target = target;
arg_desc.read_from_string = [target = arg_desc.target, check](const std::string &input) {
T *casted_ptr = std::any_cast<T *>(target);
bool valid_input = false;
std::istringstream iss(input);
if constexpr (std::is_enum_v<T>) {
std::underlying_type_t<T> temp;
valid_input = static_cast<bool>(iss >> temp);
*casted_ptr = static_cast<T>(temp);
} else {
valid_input = static_cast<bool>(iss >> *casted_ptr);
}
valid_input &= check();
return valid_input;
};
arg_desc.description_for_usage = description_for_usage;
this->argument_descriptions.push_back(arg_desc);
}

} // namespace partdiff
53 changes: 53 additions & 0 deletions argument_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "calculation_options.hpp"
#include <any>
#include <functional>
#include <optional>
#include <string>
#include <vector>

namespace partdiff {

class argument_parser {
public:
argument_parser(const int argc, char const *argv[]);
calculation_options get_options();

private:
struct argument_description {
std::any target;
std::string name;
std::optional<std::string> description_for_usage;
std::function<bool(const std::string &input)> read_from_string = [](auto) { return false; };
};

enum class argument_index : std::size_t {
number = 0,
method = 1,
interlines = 2,
pert_func = 3,
termination = 4,
term_dummy = 5,
term_accuracy = 6,
term_iteration = 7
};

calculation_options options;
std::string app_name;
std::vector<std::string> args;
std::vector<argument_description> argument_descriptions;
argument_description get_description(std::size_t index) const;
argument_description get_description(argument_index index) const;
void usage() const;
void ask_params();
void parse_param(std::size_t index, std::string &input);
void parse_param(argument_index index, std::string &input);
void ask_param(std::size_t index);
void ask_param(argument_index index);
void fill_argument_descriptions();
template <class T>
void add_argument_description(std::string name, T *target, std::optional<std::string> description_for_usage,
std::function<bool()> check);
void add_argument_description(std::string name, std::optional<std::string> description_for_usage);
};

} // namespace partdiff
Loading
Loading