summaryrefslogtreecommitdiff
path: root/options.h
blob: 831af3e521ea2b90320801776972ff966f3e3b7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#pragma once

#include "eqsystem_solve.h"
#include <iostream>
#include <optional>
#include <thread>
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <unistd.h>


template <typename T>
struct Param {
	bool given = false, computed = false;
	T value;  // valid if given || computed
	std::string name;

	Param(std::string name) : name{name} {}
	Param(std::string name, T value) : computed{true}, value{std::move(value)}, name{name} {}

	Product toProduct() const;
	void from_computed(const std::vector<std::pair<std::string, double>> &assign);
	void parse(const char *str, const char *option);
	void set(T value);
	bool valid() const;
};

template <typename T>
Product Param<T>::toProduct() const {
	static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>);
	if (given || computed) return Product{static_cast<double>(value), {}};
	else return Product{1.0, {name}};
}

template <typename T>
void Param<T>::from_computed(const std::vector<std::pair<std::string, double>> &assign) {
	if (given || computed) return;
	for (const auto &p : assign) {
		if (p.first == name) {
			computed = true;
			if constexpr (std::is_integral_v<T>) {
				value = p.second;
				if (p.second != value) {
					std::cerr << "Non-integral value computed for integral option " << name << ": " << p.second << std::endl;
					exit(1);
				}
			} else {
				static_assert(std::is_same_v<T, double>);
				value = p.second;
			}
			value = p.second;
			return;
		}
	}

	if (!computed) {
		std::cerr << "Value for option " << name << " ambiguous! Was a dimension zero?" << std::endl;
		exit(1);
	}
}

std::optional<const char*> parse_int_param(int64_t *dest, const char *str);

template <typename T>
void Param<T>::parse(const char *str, const char *option) {
	if (str == nullptr) {
		std::cerr << "Missing argument for option '" << option << "'" << std::endl;
		exit(1);
	}

	if constexpr (std::is_same_v<T, double>) {
		char *endp;
		value = strtod(str, &endp);
		if (!*str || *endp) {
			std::cerr << "Could not parse floating-point value from argument '" << str << "'" << std::endl;
			exit(1);
		}
		given = true;
	} else if constexpr (std::is_same_v<T, std::string>) {
		value = str;
		given = true;
	} else {
		static_assert(std::is_same_v<T, int> || std::is_same_v<T, int64_t>);
		int64_t res;
		if (std::optional<const char*> err = parse_int_param(&res, str)) {
			std::cerr << "Could not parse integer from argument '" << str << "': " << *err << std::endl;
			exit(1);
		} else {
			value = (T)res;
			given = true;
		}
	}
}

template <typename T>
void Param<T>::set(T value) {
	computed = true;
	this->value = std::move(value);
}

template <typename T>
bool Param<T>::valid() const {
	return given || computed;
}

struct Options {
	// Mutually-inferrable parameters
	Param<double> xmin{"xmin"}, xmax{"xmax"}, ymin{"ymin"}, ymax{"ymax"};
	Param<double> cplxwidth{"cplxwidth"}, cplxheight{"cplxheight"};
	Param<double> cx{"cx"}, cy{"cy"};
	Param<int> imgwidth{"imgwidth"}, imgheight{"imgheight"};

	// Normal parameters
	Param<int> maxiter{"maxiter", 1024};
	Param<int64_t> numorbits{"numorbits", 1000000};
	Param<int> num_threads{"num_threads", (int)std::thread::hardware_concurrency()};
	Param<int> orbit_burnin{"orbit_burnin", 4};
	bool debug_paramsolver = false;
	Param<std::string> outfname{"outfname"};
	Param<std::string> algorithm{"algorithm", "vectorised"};

	// Infer the mutually-inferrable parameters
	void fill_dimensions();

	// Check presence of normal parameters and consistency of all parameters
	void validate();
};

std::ostream& operator<<(std::ostream &os, const Options &opts);

void usage(const char *argv0);

Options parse_options(int argc, char **argv);

// vim: set sw=4 ts=4 noet: