summaryrefslogtreecommitdiff
path: root/options.h
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2025-09-03 23:31:40 +0200
committerTom Smeding <tom@tomsmeding.com>2025-09-03 23:37:53 +0200
commit7e60437d3c064bca402d486be967c43bf4326067 (patch)
tree8b4307b90c942861176d9c705cd760b9fe7b6c6b /options.h
Initial (with old code)HEADmaster
This includes old code too, perhaps from 2021-05-05, judging from the birth timestamp on the directory. The old code included option parsing and equation solving for fancy mutually-dependent options, but no actual rendering (imagine that).
Diffstat (limited to 'options.h')
-rw-r--r--options.h137
1 files changed, 137 insertions, 0 deletions
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..831af3e
--- /dev/null
+++ b/options.h
@@ -0,0 +1,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: