diff --git a/README.md b/README.md index b1dd33e..e8f0f69 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,11 @@ See below for a list of programs available. ### Requirements -The `tup` build system and the `yaggo` argument parsing library are required. +The `tup` build system is required. On Ubuntu, install with: ``` shell -sudo apt install tup yaggo build-essential libxxhash-dev +sudo apt install tup fuse3 build-essential libxxhash-dev pkg-config ``` ### Alphabet and K-mer size diff --git a/argparse.hpp b/argparse.hpp new file mode 100755 index 0000000..df52e01 --- /dev/null +++ b/argparse.hpp @@ -0,0 +1,592 @@ +#pragma once +// +// @author : Morris Franken +// https://github.com/morrisfranken/argparse +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#include // for isdigit, tolower +#include +#include // for size_t, exit +#include // for max, transform, copy, min +#include // for operator<<, setw +#include // for operator<<, basic_ostream, endl, ostream +#include // for ostream_iterator +#include // for operator!=, map, _Rb_tree_iterator +#include // for allocator, shared_ptr, __shared_ptr_ac... +#include // for optional, nullopt +#include // for runtime_error, invalid_argument +#include // for getting program_name from path +#include // for string, operator+, basic_string, char_... +#include // for declval, false_type, true_type, is_enum +#include // for move, pair +#include // for vector + +#if __has_include() +#include // for enum_entries +#define HAS_MAGIC_ENUM +#endif + +#define ARGPARSE_VERSION 4 + +namespace argparse { + class Args; + using std::cout, std::cerr, std::endl, std::setw, std::size_t; + + template struct is_vector : public std::false_type {}; + template struct is_vector> : public std::true_type {}; + + template struct is_optional : public std::false_type {}; + template struct is_optional> : public std::true_type {}; + + template struct is_shared_ptr : public std::false_type {}; + template struct is_shared_ptr> : public std::true_type {}; + + template struct has_ostream_operator : std::false_type {}; + template struct has_ostream_operator() << std::declval()))> : std::true_type {}; + + inline std::string bold(const std::string& input_str) { +#ifdef _WIN32 + return input_str; // no bold for windows +#else + return "\033[1m" + input_str + "\033[0m"; +#endif + } + + template std::string toString(const T &v) { + if constexpr (has_ostream_operator::value) { + return static_cast((std::ostringstream() << std::boolalpha << v)).str(); // https://github.com/stan-dev/math/issues/590#issuecomment-550122627 + } else { + return "unknown"; + } + } + + std::vector inline split(const std::string &str) { + std::vector splits; + std::stringstream ss(str); + std::string key; + while (std::getline(ss, key, ',')) { + if (!key.empty() && key.back() == '\0') + key.pop_back(); // last variables contain a '\0', which is unexpected when comparing to raw string, e.g. value == "test" will fail when the last character is '\0'. Therefore we can remove it + splits.emplace_back(std::move(key)); + } + return splits; + } + + template std::string to_lower(const T &str_) { // both std::string and std::basic_string_view (for magic_enum) are using to_lower + std::string str(str_.size(), '\0'); + std::transform(str_.begin(), str_.end(), str.begin(), ::tolower); + return str; + } + + template inline T get(const std::string &v); + template<> inline std::string get(const std::string &v) { return v; } + template<> inline char get(const std::string &v) { return v.empty()? throw std::invalid_argument("empty string") : v.size() > 1? v.substr(0,2) == "0x"? (char)std::stoul(v, nullptr, 16) : (char)std::stoi(v) : v[0]; } + template<> inline int get(const std::string &v) { return std::stoi(v); } + template<> inline short get(const std::string &v) { return std::stoi(v); } + template<> inline long get(const std::string &v) { return std::stol(v); } + template<> inline long long get(const std::string &v) { return std::stol(v); } + template<> inline bool get(const std::string &v) { return to_lower(v) == "true" || v == "1"; } + template<> inline float get(const std::string &v) { return std::stof(v); } + template<> inline double get(const std::string &v) { return std::stod(v); } + template<> inline unsigned char get(const std::string &v) { return get(v); } + template<> inline unsigned int get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned short get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned long get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned long long get(const std::string &v) { return std::stoul(v); } + template<> inline const char* get(const std::string& v) { return v.c_str(); } + + template inline T get(const std::string &v) { // remaining types + if constexpr (is_vector::value) { + const std::vector splitted = split(v); + T res(splitted.size()); + if (!v.empty()) + std::transform (splitted.begin(), splitted.end(), res.begin(), get); + return res; + } else if constexpr (std::is_pointer::value) { + return new typename std::remove_pointer::type(get::type>(v)); + } else if constexpr (is_shared_ptr::value) { + return std::make_shared(get(v)); + } else if constexpr (is_optional::value) { + return get(v); + } else if constexpr (std::is_enum::value) { // case-insensitive enum conversion +#ifdef HAS_MAGIC_ENUM + constexpr auto& enum_entries = magic_enum::enum_entries(); + const std::string lower_str = to_lower(v); + for (const auto &[value, name] : enum_entries) { + if (to_lower(name) == lower_str) + return value; + } + std::string error = "enum is only accepting ["; + for (size_t i = 0; i < enum_entries.size(); i++) + error += (i==0? "" : ", ") + to_lower(enum_entries[i].second); + error += "]"; + throw std::runtime_error(error); +#else + throw std::runtime_error("Enum not supported, please install magic_enum (https://github.com/Neargye/magic_enum)"); +#endif + } else { + return T(v); + } + } + + struct ConvertBase { + virtual ~ConvertBase() = default; + virtual void convert(const std::string &v) = 0; + virtual void set_default(const std::unique_ptr &default_value, const std::string &default_string) = 0; + [[nodiscard]] virtual size_t get_type_id() const = 0; + [[nodiscard]] virtual std::string get_allowed_entries() const = 0; + }; + + template struct ConvertType : public ConvertBase { + T data; + ~ConvertType() override = default; + ConvertType() : ConvertBase() {}; + explicit ConvertType(const T &value) : ConvertBase(), data(value) {}; + + void convert(const std::string &v) override { + data = get(v); + } + + void set_default(const std::unique_ptr &default_value, const std::string &default_string) override { + if (this->get_type_id() == default_value->get_type_id()) // When the types do not match exactly. resort to string conversion + data = ((ConvertType*)(default_value.get()))->data; + else + data = get(default_string); + } + + [[nodiscard]] size_t get_type_id() const override { + return typeid(T).hash_code(); + } + + [[nodiscard]] std::string get_allowed_entries() const override { + std::stringstream ss; + +#ifdef HAS_MAGIC_ENUM + if constexpr (std::is_enum::value) { + for (const auto &[value, name] : magic_enum::enum_entries()) { + ss << to_lower(name) << ", "; + } + } +#endif + + return ss.str(); + } + }; + + struct Entry { + enum ARG_TYPE {ARG, KWARG, FLAG} type; + + Entry(ARG_TYPE type, const std::string& key, std::string help, std::optional implicit_value=std::nullopt) : + type(type), + keys_(split(key)), + help(std::move(help)), + implicit_value_(std::move(implicit_value)) { + } + + // Allow both string inputs and direct-type inputs. Where a string-input will be converted like it would when using the commandline, and the direct approach is to simply use the value provided. + template Entry &set_default(const T &default_value) { + this->default_str_ = toString(default_value); + if constexpr (!(std::is_array::value || std::is_same::type, char>::value)) { + data_default = std::make_unique>(default_value); + } + return *this; + } + + Entry &multi_argument() { + _is_multi_argument = true; + return *this; + } + + // Magically convert the value string to the requested type + template operator T&() { + // Automatically set the default to nullptr for pointer types and empty for optional types + if constexpr (is_optional::value || std::is_pointer::value || is_shared_ptr::value) { + if (!default_str_.has_value()) { + default_str_ = "none"; + if constexpr(is_optional::value) { + data_default = std::make_unique> (T{std::nullopt}); + } else { + data_default = std::make_unique> ((T) nullptr); + } + } + } + + datap = std::make_unique>(); + return ((ConvertType*)(datap.get()))->data; + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + + private: + std::vector keys_; + std::string help; + std::optional value_; + std::optional implicit_value_; + std::optional default_str_; + std::string error; + std::unique_ptr datap; + std::unique_ptr data_default; + bool _is_multi_argument = false; + bool is_set_by_user = true; + + [[nodiscard]] std::string _get_keys() const { + std::stringstream ss; + for (size_t i = 0; i < keys_.size(); i++) + ss << (i? "," : "") << (type == ARG? "" : (keys_[i].size() > 1 ? "--" : "-")) + keys_[i]; + return ss.str(); + } + + void _convert(const std::string &value) { + try { + this->value_ = value; + datap->convert(value); + } catch (const std::invalid_argument &e) { + error = "Invalid argument, could not convert \"" + value + "\" for " + _get_keys() + " (" + help + ")"; + } catch (const std::runtime_error &e) { + error = "Invalid argument \"" + value + "\" for " + _get_keys() + " (" + help + "). Error: " + e.what(); + } + } + + void _apply_default() { + is_set_by_user = false; + if (data_default != nullptr) { + value_ = *default_str_; // for printing + datap->set_default(data_default, *default_str_); + } else if (default_str_.has_value()) { // in cases where a string is provided to the `set_default` function + _convert(default_str_.value()); + } else { + error = "Argument missing: " + _get_keys() + " (" + help + ")"; + } + } + + [[nodiscard]] std::string info() const { + const std::string allowed_entries = datap->get_allowed_entries(); + const std::string default_value = default_str_.has_value() ? "default: " + *default_str_ : "required"; + const std::string implicit_value = implicit_value_.has_value() ? "implicit: \"" + *implicit_value_ + "\", ": ""; + const std::string allowed_value = !allowed_entries.empty()? "allowed: <" + allowed_entries.substr(0, allowed_entries.size()-2) + ">, ": ""; + return " [" + allowed_value + implicit_value + default_value + "]"; + } + + friend class Args; + }; + + struct SubcommandEntry { + std::shared_ptr subargs; + std::string subcommand_name; + + explicit SubcommandEntry(std::string subcommand_name) : subcommand_name(std::move(subcommand_name)) {} + + template operator T &() { + static_assert(std::is_base_of_v, "Subcommand type must be a derivative of argparse::Args"); + + std::shared_ptr res = std::make_shared(); + res->program_name = subcommand_name; + subargs = res; + return *(T*)(subargs.get()); + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + }; + + class Args { + private: + size_t _arg_idx = 0; + std::vector params; + std::vector> all_entries; + std::map> kwarg_entries; + std::vector> arg_entries; + std::map> subcommand_entries; + bool has_options() { + return std::find_if(all_entries.begin(), all_entries.end(), [](auto e) { return e->type != Entry::ARG; }) != all_entries.end(); + }; + + public: + std::string program_name; + bool is_valid = false; + + virtual ~Args() = default; + + /* Add a positional argument, the order in which it is defined equals the order in which they are being read. + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string &help) { + return arg("arg_" + std::to_string(_arg_idx), help); + } + + /* Add a *named* positional argument, the order in which it is defined equals the order in which they are being read. + * key : The name of the argument, otherwise arg_ will be used + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string& key, const std::string &help) { + std::shared_ptr entry = std::make_shared(Entry::ARG, key, help); + // Increasing _arg_idx, so that arg2 will be arg_2, irregardless of whether it is preceded by other positional arguments + _arg_idx++; + arg_entries.emplace_back(entry); + all_entries.emplace_back(entry); + return *entry; + } + + /* Add a Key-Worded argument that takes a variable. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * implicit_value : Implicit values are used when no value is provided. + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &kwarg(const std::string &key, const std::string &help, const std::optional& implicit_value=std::nullopt) { + std::shared_ptr entry = std::make_shared(Entry::KWARG, key, help, implicit_value); + all_entries.emplace_back(entry); + for (const std::string &k : entry->keys_) { + kwarg_entries[k] = entry; + } + return *entry; + } + + /* Add a flag which will be false by default. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * + * Returns reference to Entry like kwarg + */ + Entry &flag(const std::string &key, const std::string &help) { + return kwarg(key, help, "true").set_default(false); + } + + /* Add a a subcommand + * command : name of the subcommand, e.g. 'commit', if you wish to implement a function like 'git commit' + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + * Expected type *Must* be an std::shared_ptr of derivative of the argparse::Args class + */ + SubcommandEntry &subcommand(const std::string &command) { + std::shared_ptr entry = std::make_shared(command); + subcommand_entries[command] = entry; + return *entry; + } + + virtual void welcome() {} // Allow to overwrite the `welcome` function to add a welcome-message to the help output + virtual void help() { + welcome(); + cout << "Usage: " << program_name << " "; + for (const auto &entry : arg_entries) + cout << entry->keys_[0] << ' '; + if (has_options()) cout << " [options...]"; + if (!subcommand_entries.empty()) { + cout << " [SUBCOMMAND: "; + for (const auto &[subcommand, subentry]: subcommand_entries) { + cout << subcommand << ", "; + } + cout << "]"; + } + cout << endl; + for (const auto &entry : arg_entries) { + cout << setw(17) << entry->keys_[0] << " : " << entry->help << entry->info() << endl; + } + + if (has_options()) cout << endl << "Options:" << endl; + for (const auto &entry : all_entries) { + if (entry->type != Entry::ARG) { + cout << setw(17) << entry->_get_keys() << " : " << entry->help << entry->info() << endl; + } + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + cout << endl << endl << bold("Subcommand: ") << bold(subcommand) << endl; + subentry->subargs->help(); + } + } + + void validate(const bool &raise_on_error) { + for (const auto &entry : all_entries) { + if (!entry->error.empty()) { + if (raise_on_error) { + throw std::runtime_error(entry->error); + } else { + std::cerr << entry->error << std::endl; + exit(-1); + } + } + } + } + + /* parse all parameters and also check for the help_flag which was set in this constructor + * Upon error, it will print the error and exit immediately if validation_action is ValidationAction::EXIT_ON_ERROR + */ + void parse(int argc, const char* const *argv, const bool &raise_on_error) { + auto parse_subcommands = [&]() -> int { + for (int i = 1; i < argc; i++) { + for (auto &[subcommand, subentry] : subcommand_entries) { + if (subcommand == argv[i]) { + subentry->subargs->parse(argc - i, argv + i, raise_on_error); + return i; + } + } + } + return argc; + }; + argc = parse_subcommands(); // argc_ is the number of arguments that should be parsed after the subcommand has finished parsing + + program_name = std::filesystem::path(argv[0]).stem().string(); + params = std::vector(argv + 1, argv + argc); + + bool& _help = flag("?,help", "print help"); + + auto is_value = [&](const size_t &i) -> bool { + return params.size() > i && (params[i][0] != '-' || (params[i].size() > 1 && std::isdigit(params[i][1]))); // check for number to not accidentally mark negative numbers as non-parameter + }; + auto parse_param = [&](size_t &i, const std::string &key, const bool is_short, const std::optional &equal_value=std::nullopt) { + auto itt = kwarg_entries.find(key); + if (itt != kwarg_entries.end()) { + auto &entry = itt->second; + if (equal_value.has_value()) { + entry->_convert(equal_value.value()); + } else if (entry->implicit_value_.has_value()) { + entry->_convert(*entry->implicit_value_); + } else if (!is_short) { // short values are not allowed to look ahead for the next parameter + if (is_value(i + 1)) { + std::string value = params[++i]; + if (entry->_is_multi_argument) { + while (is_value(i + 1)) + value += "," + params[++i]; + } + entry->_convert(value); + } else if (entry->_is_multi_argument) { + entry->_convert(""); // for multiargument parameters, return an empty vector when not passing any more values + } else { + entry->error = "No value provided for: " + key; + } + } else { + entry->error = "No value provided for: " + key; + } + } else { + cerr << "unrecognised commandline argument: " << key << endl; + } + }; + auto add_param = [&](size_t &i, const size_t &start) { + size_t eq_idx = params[i].find('='); // check if value was passed using the '=' sign + if (eq_idx != std::string::npos) { // key/value from = notation + std::string key = params[i].substr(start, eq_idx - start); + std::string value = params[i].substr(eq_idx + 1); + parse_param(i, key, false, value); + } else { + std::string key = std::string(params[i].substr(start)); + parse_param(i, key, false); + } + }; + + std::vector arguments_flat; + for (size_t i = 0; i < params.size(); i++) { + if (!is_value(i)) { + if (params[i].size() > 1 && params[i][1] == '-') { // long -- + add_param(i, 2); + } else { // short - + const size_t j_end = std::min(params[i].size(), params[i].find('=')) - 1; + for (size_t j = 1; j < j_end; j++) { // add possible other flags + const std::string key = std::string(1, params[i][j]); + parse_param(i, key, true); + } + add_param(i, j_end); + } + } else { + arguments_flat.emplace_back(params[i]); + } + } + + // Parse all the positional arguments, making sure multi_argument positional arguments are processed last to enable arguments afterwards + size_t arg_i = 0; + for (; arg_i < arg_entries.size() && !arg_entries[arg_i]->_is_multi_argument; arg_i++) { // iterate over positional arguments until a multi-argument is found + if (arg_i < arguments_flat.size()) + arg_entries[arg_i]->_convert(arguments_flat[arg_i]); + } + size_t arg_j = 1; + for (size_t j_end = arg_entries.size() - arg_i; arg_j <= j_end; arg_j++) { // iterate from back to front, to ensure non-multi-arguments in the front and back are given preference + size_t flat_idx = arguments_flat.size() - arg_j; + if (flat_idx < arguments_flat.size() && flat_idx >= arg_i) { + if (arg_entries[arg_entries.size() - arg_j]->_is_multi_argument) { + std::stringstream s; // Combine multiple arguments into 1 comma-separated string for parsing + copy(&arguments_flat[arg_i],&arguments_flat[flat_idx] + 1, std::ostream_iterator(s,",")); + std::string value = s.str(); + value.back() = '\0'; // remove trailing ',' + arg_entries[arg_i]->_convert(value); + } else { + arg_entries[arg_entries.size() - arg_j]->_convert(arguments_flat[flat_idx]); + } + } + } + + // try to apply default values for arguments which have not been set + for (const auto &entry : all_entries) { + if (!entry->value_.has_value()) { + entry->_apply_default(); + } + } + + if (_help) { + help(); + exit(0); + } + + validate(raise_on_error); + is_valid = true; + } + + void print() const { + for (const auto &entry : all_entries) { + std::string snip = entry->type == Entry::ARG ? "(" + (entry->help.size() > 10 ? entry->help.substr(0, 7) + "..." : entry->help) + ")" : ""; + cout << setw(21) << entry->_get_keys() + snip << " : " << (entry->is_set_by_user? bold(entry->value_.value_or("null")) : entry->value_.value_or("null")) << endl; + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + cout << endl << "--- Subcommand: " << subcommand << endl; + subentry->subargs->print(); + } + } + } + + virtual int run() {return 0;} // For automatically running subcommands + int run_subcommands() { + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + return subentry->subargs->run(); + } + } + + std::cerr << "No subcommand provided" << std::endl; + help(); + return -1; + } + }; + + template T parse(int argc, const char* const *argv, const bool &raise_on_error=false) { + T args = T(); + args.parse(argc, argv, raise_on_error); + return args; + } +} diff --git a/champarnaud_set.cc b/champarnaud_set.cc index 80e5969..9699205 100644 --- a/champarnaud_set.cc +++ b/champarnaud_set.cc @@ -1,13 +1,9 @@ #include #include -#include -#include "divisor.hpp" -#include "misc.hpp" #include "common.hpp" -#include "dbg.hpp" #include "champarnaud.hpp" -#include "champarnaud_set.hpp" +#include "argparse.hpp" #ifndef K #error Must define k-mer length K @@ -22,6 +18,21 @@ typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; +struct ChamparnaudArgs : argparse::Args { + bool& brute_flag = flag("brute", "Brute force"); + bool& notsorted_flag = flag("notsorted", "Do not sort output"); + + void welcome() override { + std::cout << + "For each PCR, find the minimal k-mer m, do a principal division:\n" + "\n" + "m = l^n u, with l a Lyndon word, the smallest one, and |l|<|u|\n" + "\n" + "Output u l^n for each PCR." + << std::endl; + } +}; + template void enumerate_champarnaud(Fn fn) { index_t index(K); @@ -47,7 +58,7 @@ void brute_champarnaud(Fn fn) { } int main(int argc, char* argv[]) { - champarnaud_set args(argc, argv); + auto args = argparse::parse(argc, argv); if(args.notsorted_flag) { bool first = true; diff --git a/champarnaud_set.yaggo b/champarnaud_set.yaggo deleted file mode 100644 index 64013b6..0000000 --- a/champarnaud_set.yaggo +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: false - -purpose ' Generate the Champarnaud decycling set' -description <<~HELP - For each PCR, find the minimal k-mer m, do a principal division: - - m = l^n u, with l a Lyndon word, the smallest one, and |l|<|u| - - Output u l^n for each PCR. -HELP - -option('brute') { - description 'Brute force' - flag; off -} - -option("notsorted") { - description "Do not sort output" - flag; off -} diff --git a/comp2rankdot.cc b/comp2rankdot.cc index cbc52fc..1d6d129 100644 --- a/comp2rankdot.cc +++ b/comp2rankdot.cc @@ -5,12 +5,26 @@ #include #include +#include "argparse.hpp" #include "mer_op.hpp" #include "mds_op.hpp" #include "longest_path.hpp" #include "misc.hpp" #include "common.hpp" -#include "comp2rankdot.hpp" + +struct Comp2RankdotArgs : argparse::Args { + bool& longest_flag = flag("l,longest", "Annotate with longest remaining path"); + std::string& output_arg = kwarg("o,output", "Dot file output").set_default("/dev/stdout"); + bool& progress_flag = flag("p,progress", "Show progress"); + std::vector& mds_arg = arg("MDS"); + + void welcome() override { + std::cout << + "Generate dot file for 1 component, with rank for proper plotting\n\n" + "From 1 MDS, do a BFS and layout each layer with same rank" + << std::endl; + } +}; struct mds_info { static size_t total; // Total number of MDSs found. Used to set index @@ -85,7 +99,7 @@ void update_ranges(std::vector>& fm_ranges, mer_t fmi) { int main(int argc, char* argv[]) { std::ios::sync_with_stdio(false); - comp2rankdot args(argc, argv); + const auto args = argparse::parse(argc, argv); std::vector first_bmds; std::vector first_fms, mds; @@ -100,7 +114,7 @@ int main(int argc, char* argv[]) { std::ofstream dot_fd; - if(strlen(args.output_arg) > 0) { + if(args.output_arg.size() > 0) { dot_fd.open(args.output_arg); if(!dot_fd.good()) { std::cerr << "Failed to open dot output file '" << args.output_arg << '\'' << std::endl; diff --git a/comp2rankdot.yaggo b/comp2rankdot.yaggo deleted file mode 100644 index ad4f19a..0000000 --- a/comp2rankdot.yaggo +++ /dev/null @@ -1,22 +0,0 @@ -purpose 'Generate dot file for 1 component, with rank for proper plotting' -description 'From 1 MDS, do a BFS and layout each layer with same rank' - -option('l', 'longest') { - description 'Annotate with longest remaining path' - flag; off -} - -option('o', 'output') { - description 'Dot file output' - c_string; default '/dev/stdout'; typestr 'path' -} - -option('p', 'progress') { - description 'Show progress' - flag; off -} - -arg('mds') { - description 'MDS' - c_string; multiple; typestr 'MDS' -} diff --git a/configure.sh b/configure.sh index a2ae362..cea5546 100755 --- a/configure.sh +++ b/configure.sh @@ -19,7 +19,6 @@ variables: CXXFLAGS Compilation flags LDFLAGS Linker flags LDLIBS Extra libraries flags - YAGGO Path to yaggo PKG_CONFIG_PATH Used by pkg-config Enable testing the -r and -f flags. -r gives the number of repeats for an @@ -68,10 +67,9 @@ fi NAME="A${ALPHA}K${K}${SUFFIX}" -[ -z "$YAGGO" ] && YAGGO=$(which yaggo || true) [ -z "$TUP" ] && TUP=$(which tup || true) -[[ -z "$TUP" || -z "$YAGGO" ]] && { echo >&2 "Missing required dependencies: tup and/or yaggo"; false; } +[[ -z "$TUP" ]] && { echo >&2 "Missing required dependencies: tup"; false; } detect_compiledb() { tup compiledb >& /dev/null && echo yes || true @@ -115,7 +113,6 @@ CONFIG_CXX=$GCXX CONFIG_CXXFLAGS=$XXHASH_CFLAGS $OPTFLAGS $CXXFLAGS CONFIG_LDFLAGS=$XXHASH_LDFLAGS $LDFLAGS CONFIG_LDLIBS=$XXHASH_LDLIBS $LDLIBS -CONFIG_YAGGO=$YAGGO CONFIG_COMPILEDB=$COMPILEDB EOF diff --git a/create_seed.cc b/create_seed.cc index 3a8f7a6..73ae333 100644 --- a/create_seed.cc +++ b/create_seed.cc @@ -1,8 +1,16 @@ -#include "create_seed.hpp" +#include "argparse.hpp" #include "random_seed.hpp" +struct CreateSeedArgs : argparse::Args { + std::string& seed_arg = arg("Output seed file"); + + void welcome() { + std::cout << "Create a random seed file" << std::endl; + } +}; + int main(int argc, char* argv[]) { - create_seed args(argc, argv); - seeded_prg(args.seed_arg, nullptr); + const auto args = argparse::parse(argc, argv); + seeded_prg(args.seed_arg.c_str(), nullptr); return EXIT_SUCCESS; } diff --git a/create_seed.yaggo b/create_seed.yaggo deleted file mode 100644 index a28836b..0000000 --- a/create_seed.yaggo +++ /dev/null @@ -1,7 +0,0 @@ -purpose 'Create a random seed file' - -arg('seed') { - description 'Output seed file' - c_string - typestr 'path' -} diff --git a/find_longest_path.cc b/find_longest_path.cc index d2aad0b..8be32bf 100644 --- a/find_longest_path.cc +++ b/find_longest_path.cc @@ -1,9 +1,5 @@ +#include "argparse.hpp" #include -#include -#include -#include - -#include "find_longest_path.hpp" #ifndef K #error Must define k-mer length K @@ -14,7 +10,6 @@ #endif #include "mer_op.hpp" -#include "mds_op.hpp" #include "misc.hpp" #include "longest_path.hpp" @@ -22,11 +17,20 @@ typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; typedef longest_path_type longest_path; +struct LongestPathArgs : argparse::Args { + std::optional& mds_arg = kwarg("f,mds", "File with MDS"); + std::vector& comp_arg = arg("component"); + + void welcome() override { + std::cout << "Find longest remaining path" << std::endl; + } +}; + int main(int argc, char* argv[]) { - find_longest_path args(argc, argv); + const auto args = argparse::parse(argc, argv); longest_path lp; - const auto mds = args.mds_given ? mds_from_file(args.mds_arg) : mds_from_arg(args.comp_arg); + const auto mds = args.mds_arg ? mds_from_file(*args.mds_arg) : mds_from_arg(args.comp_arg); std::cout << (size_t)lp.longest_path(mds) << '\n'; return EXIT_SUCCESS; diff --git a/find_longest_path.yaggo b/find_longest_path.yaggo deleted file mode 100644 index 0f55552..0000000 --- a/find_longest_path.yaggo +++ /dev/null @@ -1,11 +0,0 @@ -description 'Find longest remaining path' - -option('f', 'mds') { - description 'File with MDS' - c_string; typestr 'path' -} - -arg('comp') { - description 'component' - c_string; multiple -} diff --git a/fms2mds.cc b/fms2mds.cc index d479bb9..dc093f7 100644 --- a/fms2mds.cc +++ b/fms2mds.cc @@ -2,18 +2,25 @@ #include #include +#include "argparse.hpp" #include "mer_op.hpp" #include "mds_op.hpp" -#include "fms2mds.hpp" #include "misc.hpp" #include "common.hpp" typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; +struct FMS2MDSArgs : argparse::Args { + + void welcome() { + std::cout << "Transform list of FMs to an mers in an MDS" << std::endl; + } +}; + int main(int argc, char* argv[]) { std::ios::sync_with_stdio(false); - fms2mds args(argc, argv); + const auto args = argparse::parse(argc, argv); mds_op_type mds_op; std::string line; diff --git a/fms2mds.yaggo b/fms2mds.yaggo deleted file mode 100644 index 0d4257b..0000000 --- a/fms2mds.yaggo +++ /dev/null @@ -1,9 +0,0 @@ -purpose 'Transform list of FMs to an mers in an MDS' -description 'See usage :)' - -# Work around a bug with yaggo < 1.5.11: with no options, it generates -# an invalid class. Define a bogus option. -option("noop") { - description "Does not do anything. Ignore this option" - flag; off -} diff --git a/frac_set.cc b/frac_set.cc index fc73011..8f500ff 100644 --- a/frac_set.cc +++ b/frac_set.cc @@ -1,10 +1,9 @@ #include #include -#include +#include "argparse.hpp" #include "random_seed.hpp" #include "common.hpp" -#include "frac_set.hpp" #ifndef K #error Must define k-mer length K @@ -16,33 +15,41 @@ #include "mer_op.hpp" +struct FracSetArgs : argparse::Args { + std::optional& fraction_arg = kwarg("f,fraction", "Fraction of k-mers to keep"); + std::optional& size_arg = kwarg("s,size", "Size of fractional set"); + std::optional& iseed_arg = kwarg("i,iseed", "Input seed file"); + std::optional& oseed_arg = kwarg("o,ioeed", "Output seed file"); + +}; + typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; int main(int argc, char* argv[]) { - frac_set args(argc, argv); + const auto args = argparse::parse(argc, argv); size_t set_size; - if(args.fraction_given) { - if(args.fraction_arg < 0.0 || args.fraction_arg > 1.0) { + if(args.fraction_arg) { + if(*args.fraction_arg < 0.0 || *args.fraction_arg > 1.0) { std::cerr << "Frace must be in [0, 1]" << std::endl; return EXIT_FAILURE; } - set_size = std::min((size_t)mer_ops::nb_mers, (size_t)std::round(args.fraction_arg * mer_ops::nb_mers)); - } else if(args.size_given) { - if(args.size_arg > mer_ops::nb_mers) { + set_size = std::min((size_t)mer_ops::nb_mers, (size_t)std::round(*args.fraction_arg * mer_ops::nb_mers)); + } else if(args.size_arg) { + if(*args.size_arg > mer_ops::nb_mers) { std::cerr << "Size must be in [0, " << (size_t)mer_ops::nb_mers << ']' << std::endl; return EXIT_FAILURE; } - set_size = args.size_arg; + set_size = *args.size_arg; } else { set_size = mer_ops::nb_necklaces; } std::cerr << "size " << set_size << '\n'; // The properly seeded PRG - auto prg = seeded_prg(args.oseed_given ? args.oseed_arg : nullptr, - args.iseed_given ? args.iseed_arg : nullptr); + auto prg = seeded_prg(args.oseed_arg ? *args.oseed_arg : nullptr, + args.iseed_arg ? *args.iseed_arg : nullptr); std::vector all_mers(mer_ops::nb_mers); for(mer_t m = 0; m < all_mers.size(); ++m) diff --git a/frac_set.yaggo b/frac_set.yaggo deleted file mode 100644 index dafc838..0000000 --- a/frac_set.yaggo +++ /dev/null @@ -1,24 +0,0 @@ -purpose "Fractional set" - -option("f", "fraction") { - description "Fraction of k-mers to keep" - double - conflict "size" -} - -option("s", "size") { - description "Size of fractional set" - uint64 -} - -option('i', 'iseed') { - description 'Input seed file' - typestr 'path' - c_string -} - -option('o', 'oseed') { - description 'Output seed file' - typestr 'path' - c_string -} diff --git a/magic_enum.hpp b/magic_enum.hpp new file mode 100644 index 0000000..6d6f1e7 --- /dev/null +++ b/magic_enum.hpp @@ -0,0 +1,1508 @@ +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.9.6 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2024 Daniil Goncharov . +// +// 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. + +#ifndef NEARGYE_MAGIC_ENUM_HPP +#define NEARGYE_MAGIC_ENUM_HPP + +#define MAGIC_ENUM_VERSION_MAJOR 0 +#define MAGIC_ENUM_VERSION_MINOR 9 +#define MAGIC_ENUM_VERSION_PATCH 6 + +#ifndef MAGIC_ENUM_USE_STD_MODULE +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(MAGIC_ENUM_CONFIG_FILE) +# include MAGIC_ENUM_CONFIG_FILE +#endif + +#ifndef MAGIC_ENUM_USE_STD_MODULE +#if !defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +# include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING) +# include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +# include +#endif +#endif + +#if defined(MAGIC_ENUM_NO_ASSERT) +# define MAGIC_ENUM_ASSERT(...) static_cast(0) +#elif !defined(MAGIC_ENUM_ASSERT) +# include +# define MAGIC_ENUM_ASSERT(...) assert((__VA_ARGS__)) +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +# pragma clang diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast('\0')' for char_type = char (common on Linux). +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'. +# pragma GCC diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast('\0')' for char_type = char (common on Linux). +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 26495) // Variable 'static_str::chars_' is uninitialized. +# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. +# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. +# pragma warning(disable : 4514) // Unreferenced inline function has been removed. +#endif + +// Checks magic_enum compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 || defined(__RESHARPER__) +# undef MAGIC_ENUM_SUPPORTED +# define MAGIC_ENUM_SUPPORTED 1 +#endif + +// Checks magic_enum compiler aliases compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 +# undef MAGIC_ENUM_SUPPORTED_ALIASES +# define MAGIC_ENUM_SUPPORTED_ALIASES 1 +#endif + +// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default MAGIC_ENUM_RANGE_MIN = -128. +// If need another min range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN. +#if !defined(MAGIC_ENUM_RANGE_MIN) +# define MAGIC_ENUM_RANGE_MIN -128 +#endif + +// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default MAGIC_ENUM_RANGE_MAX = 127. +// If need another max range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MAX. +#if !defined(MAGIC_ENUM_RANGE_MAX) +# define MAGIC_ENUM_RANGE_MAX 127 +#endif + +// Improve ReSharper C++ intellisense performance with builtins, avoiding unnecessary template instantiations. +#if defined(__RESHARPER__) +# undef MAGIC_ENUM_GET_ENUM_NAME_BUILTIN +# undef MAGIC_ENUM_GET_TYPE_NAME_BUILTIN +# if __RESHARPER__ >= 20230100 +# define MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V) __rscpp_enumerator_name(V) +# define MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(T) __rscpp_type_name() +# else +# define MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V) nullptr +# define MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(T) nullptr +# endif +#endif + +namespace magic_enum { + +// If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL. +#if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +MAGIC_ENUM_USING_ALIAS_OPTIONAL +#else +using std::optional; +#endif + +// If need another string_view type, define the macro MAGIC_ENUM_USING_ALIAS_STRING_VIEW. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +MAGIC_ENUM_USING_ALIAS_STRING_VIEW +#else +using std::string_view; +#endif + +// If need another string type, define the macro MAGIC_ENUM_USING_ALIAS_STRING. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING) +MAGIC_ENUM_USING_ALIAS_STRING +#else +using std::string; +#endif + +using char_type = string_view::value_type; +static_assert(std::is_same_v, "magic_enum::customize requires same string_view::value_type and string::value_type"); +static_assert([] { + if constexpr (std::is_same_v) { + constexpr const char c[] = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789|"; + constexpr const wchar_t wc[] = L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789|"; + static_assert(std::size(c) == std::size(wc), "magic_enum::customize identifier characters are multichars in wchar_t."); + + for (std::size_t i = 0; i < std::size(c); ++i) { + if (c[i] != wc[i]) { + return false; + } + } + } + return true; +} (), "magic_enum::customize wchar_t is not compatible with ASCII."); + +namespace customize { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 127. +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. +// If need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static constexpr int min = MAGIC_ENUM_RANGE_MIN; + static constexpr int max = MAGIC_ENUM_RANGE_MAX; +}; + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); + +namespace detail { + +enum class customize_tag { + default_tag, + invalid_tag, + custom_tag +}; + +} // namespace magic_enum::customize::detail + +class customize_t : public std::pair { + public: + constexpr customize_t(string_view srt) : std::pair{detail::customize_tag::custom_tag, srt} {} + constexpr customize_t(const char_type* srt) : customize_t{string_view{srt}} {} + constexpr customize_t(detail::customize_tag tag) : std::pair{tag, string_view{}} { + MAGIC_ENUM_ASSERT(tag != detail::customize_tag::custom_tag); + } +}; + +// Default customize. +inline constexpr auto default_tag = customize_t{detail::customize_tag::default_tag}; +// Invalid customize. +inline constexpr auto invalid_tag = customize_t{detail::customize_tag::invalid_tag}; + +// If need custom names for enum, add specialization enum_name for necessary enum type. +template +constexpr customize_t enum_name(E) noexcept { + return default_tag; +} + +// If need custom type name for enum, add specialization enum_type_name for necessary enum type. +template +constexpr customize_t enum_type_name() noexcept { + return default_tag; +} + +} // namespace magic_enum::customize + +namespace detail { + +template +struct supported +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template , std::enable_if_t, int> = 0> +using enum_constant = std::integral_constant; + +template +inline constexpr bool always_false_v = false; + +template +struct has_is_flags : std::false_type {}; + +template +struct has_is_flags::is_flags)>> : std::bool_constant::is_flags)>>> {}; + +template +struct range_min : std::integral_constant {}; + +template +struct range_min::min)>> : std::integral_constant::min), customize::enum_range::min> {}; + +template +struct range_max : std::integral_constant {}; + +template +struct range_max::max)>> : std::integral_constant::max), customize::enum_range::max> {}; + +struct str_view { + const char* str_ = nullptr; + std::size_t size_ = 0; +}; + +template +class static_str { + public: + constexpr explicit static_str(str_view str) noexcept : static_str{str.str_, std::make_integer_sequence{}} { + MAGIC_ENUM_ASSERT(str.size_ == N); + } + + constexpr explicit static_str(string_view str) noexcept : static_str{str.data(), std::make_integer_sequence{}} { + MAGIC_ENUM_ASSERT(str.size() == N); + } + + constexpr const char_type* data() const noexcept { return chars_; } + + constexpr std::uint16_t size() const noexcept { return N; } + + constexpr operator string_view() const noexcept { return {data(), size()}; } + + private: + template + constexpr static_str(const char* str, std::integer_sequence) noexcept : chars_{static_cast(str[I])..., static_cast('\0')} {} + + template + constexpr static_str(string_view str, std::integer_sequence) noexcept : chars_{str[I]..., static_cast('\0')} {} + + char_type chars_[static_cast(N) + 1]; +}; + +template <> +class static_str<0> { + public: + constexpr explicit static_str() = default; + + constexpr explicit static_str(str_view) noexcept {} + + constexpr explicit static_str(string_view) noexcept {} + + constexpr const char_type* data() const noexcept { return nullptr; } + + constexpr std::uint16_t size() const noexcept { return 0; } + + constexpr operator string_view() const noexcept { return {}; } +}; + +template > +class case_insensitive { + static constexpr char_type to_lower(char_type c) noexcept { + return (c >= static_cast('A') && c <= static_cast('Z')) ? static_cast(c + (static_cast('a') - static_cast('A'))) : c; + } + + public: + template + constexpr auto operator()(L lhs, R rhs) const noexcept -> std::enable_if_t, char_type> && std::is_same_v, char_type>, bool> { + return Op{}(to_lower(lhs), to_lower(rhs)); + } +}; + +constexpr std::size_t find(string_view str, char_type c) noexcept { +#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) +// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc +// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + + if constexpr (workaround) { + for (std::size_t i = 0; i < str.size(); ++i) { + if (str[i] == c) { + return i; + } + } + + return string_view::npos; + } else { + return str.find(c); + } +} + +template +constexpr bool is_default_predicate() noexcept { + return std::is_same_v, std::equal_to> || + std::is_same_v, std::equal_to<>>; +} + +template +constexpr bool is_nothrow_invocable() { + return is_default_predicate() || + std::is_nothrow_invocable_r_v; +} + +template +constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable()) { +#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) + // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + + if constexpr (!is_default_predicate() || workaround) { + if (lhs.size() != rhs.size()) { + return false; + } + + const auto size = lhs.size(); + for (std::size_t i = 0; i < size; ++i) { + if (!p(lhs[i], rhs[i])) { + return false; + } + } + + return true; + } else { + return lhs == rhs; + } +} + +template +constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "magic_enum::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return static_cast(lhs) < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return lhs < static_cast(rhs); + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template +constexpr I log2(I value) noexcept { + static_assert(std::is_integral_v, "magic_enum::detail::log2 requires integral type."); + + if constexpr (std::is_same_v) { // bool special case + return MAGIC_ENUM_ASSERT(false), value; + } else { + auto ret = I{0}; + for (; value > I{1}; value >>= I{1}, ++ret) {} + + return ret; + } +} + +#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L +# define MAGIC_ENUM_ARRAY_CONSTEXPR 1 +#else +template +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { + return {{a[I]...}}; +} +#endif + +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + + if constexpr (supported::value) { +#if defined(MAGIC_ENUM_GET_TYPE_NAME_BUILTIN) + constexpr auto name_ptr = MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(E); + constexpr auto name = name_ptr ? str_view{name_ptr, std::char_traits::length(name_ptr)} : str_view{}; +#elif defined(__clang__) + str_view name; + if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) { + static_assert(always_false_v, "magic_enum::detail::n requires __PRETTY_FUNCTION__."); + return str_view{}; + } else { + name.size_ = sizeof(__PRETTY_FUNCTION__) - 36; + name.str_ = __PRETTY_FUNCTION__ + 34; + } +#elif defined(__GNUC__) + auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1}; + if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) { + static_assert(always_false_v, "magic_enum::detail::n requires __PRETTY_FUNCTION__."); + return str_view{}; + } else if (name.str_[name.size_ - 1] == ']') { + name.size_ -= 50; + name.str_ += 49; + } else { + name.size_ -= 40; + name.str_ += 37; + } +#elif defined(_MSC_VER) + // CLI/C++ workaround (see https://github.com/Neargye/magic_enum/issues/284). + str_view name; + name.str_ = __FUNCSIG__; + name.str_ += 40; + name.size_ += sizeof(__FUNCSIG__) - 57; +#else + auto name = str_view{}; +#endif + std::size_t p = 0; + for (std::size_t i = name.size_; i > 0; --i) { + if (name.str_[i] == ':') { + p = i + 1; + break; + } + } + if (p > 0) { + name.size_ -= p; + name.str_ += p; + } + return name; + } else { + return str_view{}; // Unsupported compiler or Invalid customize. + } +} + +template +constexpr auto type_name() noexcept { + [[maybe_unused]] constexpr auto custom = customize::enum_type_name(); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_str{name}; + } else if constexpr (custom.first == customize::detail::customize_tag::invalid_tag) { + return static_str<0>{}; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { + constexpr auto name = n(); + return static_str{name}; + } else { + static_assert(always_false_v, "magic_enum::customize invalid."); + } +} + +template +inline constexpr auto type_name_v = type_name(); + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + + if constexpr (supported::value) { +#if defined(MAGIC_ENUM_GET_ENUM_NAME_BUILTIN) + constexpr auto name_ptr = MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V); + auto name = name_ptr ? str_view{name_ptr, std::char_traits::length(name_ptr)} : str_view{}; +#elif defined(__clang__) + str_view name; + if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) { + static_assert(always_false_v, "magic_enum::detail::n requires __PRETTY_FUNCTION__."); + return str_view{}; + } else { + name.size_ = sizeof(__PRETTY_FUNCTION__) - 36; + name.str_ = __PRETTY_FUNCTION__ + 34; + } + if (name.size_ > 22 && name.str_[0] == '(' && name.str_[1] == 'a' && name.str_[10] == ' ' && name.str_[22] == ':') { + name.size_ -= 23; + name.str_ += 23; + } + if (name.str_[0] == '(' || name.str_[0] == '-' || (name.str_[0] >= '0' && name.str_[0] <= '9')) { + name = str_view{}; + } +#elif defined(__GNUC__) + auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1}; + if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) { + static_assert(always_false_v, "magic_enum::detail::n requires __PRETTY_FUNCTION__."); + return str_view{}; + } else if (name.str_[name.size_ - 1] == ']') { + name.size_ -= 55; + name.str_ += 54; + } else { + name.size_ -= 40; + name.str_ += 37; + } + if (name.str_[0] == '(') { + name = str_view{}; + } +#elif defined(_MSC_VER) + str_view name; + if ((__FUNCSIG__[5] == '_' && __FUNCSIG__[35] != '(') || (__FUNCSIG__[5] == 'c' && __FUNCSIG__[41] != '(')) { + // CLI/C++ workaround (see https://github.com/Neargye/magic_enum/issues/284). + name.str_ = __FUNCSIG__; + name.str_ += 35; + name.size_ = sizeof(__FUNCSIG__) - 52; + } +#else + auto name = str_view{}; +#endif + std::size_t p = 0; + for (std::size_t i = name.size_; i > 0; --i) { + if (name.str_[i] == ':') { + p = i + 1; + break; + } + } + if (p > 0) { + name.size_ -= p; + name.str_ += p; + } + return name; + } else { + return str_view{}; // Unsupported compiler or Invalid customize. + } +} + +#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920 +# define MAGIC_ENUM_VS_2017_WORKAROUND 1 +#endif + +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + +# if defined(MAGIC_ENUM_GET_ENUM_NAME_BUILTIN) + constexpr auto name_ptr = MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V); + auto name = name_ptr ? str_view{name_ptr, std::char_traits::length(name_ptr)} : str_view{}; +# else + // CLI/C++ workaround (see https://github.com/Neargye/magic_enum/issues/284). + str_view name; + name.str_ = __FUNCSIG__; + name.size_ = sizeof(__FUNCSIG__) - 17; + std::size_t p = 0; + for (std::size_t i = name.size_; i > 0; --i) { + if (name.str_[i] == ',' || name.str_[i] == ':') { + p = i + 1; + break; + } + } + if (p > 0) { + name.size_ -= p; + name.str_ += p; + } + if (name.str_[0] == '(' || name.str_[0] == '-' || (name.str_[0] >= '0' && name.str_[0] <= '9')) { + name = str_view{}; + } + return name; +# endif +} +#endif + +template +constexpr auto enum_name() noexcept { + [[maybe_unused]] constexpr auto custom = customize::enum_name(V); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_str{name}; + } else if constexpr (custom.first == customize::detail::customize_tag::invalid_tag) { + return static_str<0>{}; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) + constexpr auto name = n(); +#else + constexpr auto name = n(); +#endif + return static_str{name}; + } else { + static_assert(always_false_v, "magic_enum::customize invalid."); + } +} + +template +inline constexpr auto enum_name_v = enum_name(); + +template +constexpr bool is_valid() noexcept { +#if defined(__clang__) && __clang_major__ >= 16 + // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 + constexpr E v = __builtin_bit_cast(E, V); +#else + constexpr E v = static_cast(V); +#endif + [[maybe_unused]] constexpr auto custom = customize::enum_name(v); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return name.size() != 0; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) + return n().size_ != 0; +#else + return n().size_ != 0; +#endif + } else { + return false; + } +} + +enum class enum_subtype { + common, + flags +}; + +template > +constexpr U ualue(std::size_t i) noexcept { + if constexpr (std::is_same_v) { // bool special case + static_assert(O == 0, "magic_enum::detail::ualue requires valid offset."); + + return static_cast(i); + } else if constexpr (S == enum_subtype::flags) { + return static_cast(U{1} << static_cast(static_cast(i) + O)); + } else { + return static_cast(static_cast(i) + O); + } +} + +template > +constexpr E value(std::size_t i) noexcept { + return static_cast(ualue(i)); +} + +template > +constexpr int reflected_min() noexcept { + if constexpr (S == enum_subtype::flags) { + return 0; + } else { + constexpr auto lhs = range_min::value; + constexpr auto rhs = (std::numeric_limits::min)(); + + if constexpr (cmp_less(rhs, lhs)) { + return lhs; + } else { + return rhs; + } + } +} + +template > +constexpr int reflected_max() noexcept { + if constexpr (S == enum_subtype::flags) { + return std::numeric_limits::digits - 1; + } else { + constexpr auto lhs = range_max::value; + constexpr auto rhs = (std::numeric_limits::max)(); + + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return rhs; + } + } +} + +#define MAGIC_ENUM_FOR_EACH_256(T) \ + T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ + T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ + T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ + T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ + T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ + T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ + T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ + T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) + +template +constexpr void valid_count(bool* valid, std::size_t& count) noexcept { +#define MAGIC_ENUM_V(O) \ + if constexpr ((I + O) < Size) { \ + if constexpr (is_valid(I + O)>()) { \ + valid[I + O] = true; \ + ++count; \ + } \ + } + + MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V) + + if constexpr ((I + 256) < Size) { + valid_count(valid, count); + } +#undef MAGIC_ENUM_V +} + +template +struct valid_count_t { + std::size_t count = 0; + bool valid[N] = {}; +}; + +template +constexpr auto valid_count() noexcept { + valid_count_t vc; + valid_count(vc.valid, vc.count); + return vc; +} + +template +constexpr auto values() noexcept { + constexpr auto vc = valid_count(); + + if constexpr (vc.count > 0) { +#if defined(MAGIC_ENUM_ARRAY_CONSTEXPR) + std::array values = {}; +#else + E values[vc.count] = {}; +#endif + for (std::size_t i = 0, v = 0; v < vc.count; ++i) { + if (vc.valid[i]) { + values[v++] = value(i); + } + } +#if defined(MAGIC_ENUM_ARRAY_CONSTEXPR) + return values; +#else + return to_array(values, std::make_index_sequence{}); +#endif + } else { + return std::array{}; + } +} + +template > +constexpr auto values() noexcept { + constexpr auto min = reflected_min(); + constexpr auto max = reflected_max(); + constexpr auto range_size = max - min + 1; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + + return values(); +} + +template > +constexpr enum_subtype subtype(std::true_type) noexcept { + if constexpr (std::is_same_v) { // bool special case + return enum_subtype::common; + } else if constexpr (has_is_flags::value) { + return customize::enum_range::is_flags ? enum_subtype::flags : enum_subtype::common; + } else { +#if defined(MAGIC_ENUM_AUTO_IS_FLAGS) + constexpr auto flags_values = values(); + constexpr auto default_values = values(); + if (flags_values.size() == 0 || default_values.size() > flags_values.size()) { + return enum_subtype::common; + } + for (std::size_t i = 0; i < default_values.size(); ++i) { + const auto v = static_cast(default_values[i]); + if (v != 0 && (v & (v - 1)) != 0) { + return enum_subtype::common; + } + } + return enum_subtype::flags; +#else + return enum_subtype::common; +#endif + } +} + +template +constexpr enum_subtype subtype(std::false_type) noexcept { + // For non-enum type return default common subtype. + return enum_subtype::common; +} + +template > +inline constexpr auto subtype_v = subtype(std::is_enum{}); + +template +inline constexpr auto values_v = values(); + +template > +using values_t = decltype((values_v)); + +template +inline constexpr auto count_v = values_v.size(); + +template > +inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; + +template > +inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; + +template +constexpr auto names(std::index_sequence) noexcept { + constexpr auto names = std::array{{enum_name_v[I]>...}}; + return names; +} + +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); + +template > +using names_t = decltype((names_v)); + +template +constexpr auto entries(std::index_sequence) noexcept { + constexpr auto entries = std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; + return entries; +} + +template +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); + +template > +using entries_t = decltype((entries_v)); + +template > +constexpr bool is_sparse() noexcept { + if constexpr (count_v == 0) { + return false; + } else if constexpr (std::is_same_v) { // bool special case + return false; + } else { + constexpr auto max = (S == enum_subtype::flags) ? log2(max_v) : max_v; + constexpr auto min = (S == enum_subtype::flags) ? log2(min_v) : min_v; + constexpr auto range_size = max - min + 1; + + return range_size != count_v; + } +} + +template > +inline constexpr bool is_sparse_v = is_sparse(); + +template +struct is_reflected +#if defined(MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM) + : std::true_type {}; +#else + : std::bool_constant && (count_v != 0)> {}; +#endif + +template +inline constexpr bool is_reflected_v = is_reflected, S>{}; + +template +struct enable_if_enum {}; + +template +struct enable_if_enum { + using type = R; + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); +}; + +template , typename D = std::decay_t> +using enable_if_t = typename enable_if_enum && std::is_invocable_r_v, R>::type; + +template >, int> = 0> +using enum_concept = T; + +template > +struct is_scoped_enum : std::false_type {}; + +template +struct is_scoped_enum : std::bool_constant>> {}; + +template > +struct is_unscoped_enum : std::false_type {}; + +template +struct is_unscoped_enum : std::bool_constant>> {}; + +template >> +struct underlying_type {}; + +template +struct underlying_type : std::underlying_type> {}; + +#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH) + +template +struct constexpr_hash_t; + +template +struct constexpr_hash_t>> { + constexpr auto operator()(Value value) const noexcept { + using U = typename underlying_type::type; + if constexpr (std::is_same_v) { // bool special case + return static_cast(value); + } else { + return static_cast(value); + } + } + using secondary_hash = constexpr_hash_t; +}; + +template +struct constexpr_hash_t>> { + static constexpr std::uint32_t crc_table[256] { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL + }; + constexpr std::uint32_t operator()(string_view value) const noexcept { + auto crc = static_cast(0xffffffffL); + for (const auto c : value) { + crc = (crc >> 8) ^ crc_table[(crc ^ static_cast(c)) & 0xff]; + } + return crc ^ 0xffffffffL; + } + + struct secondary_hash { + constexpr std::uint32_t operator()(string_view value) const noexcept { + auto acc = static_cast(2166136261ULL); + for (const auto c : value) { + acc = ((acc ^ static_cast(c)) * static_cast(16777619ULL)) & (std::numeric_limits::max)(); + } + return static_cast(acc); + } + }; +}; + +template +inline constexpr Hash hash_v{}; + +template +constexpr auto calculate_cases(std::size_t Page) noexcept { + constexpr std::array values = *GlobValues; + constexpr std::size_t size = values.size(); + + using switch_t = std::invoke_result_t; + static_assert(std::is_integral_v && !std::is_same_v); + const std::size_t values_to = (std::min)(static_cast(256), size - Page); + + std::array result{}; + auto fill = result.begin(); + { + auto first = values.begin() + static_cast(Page); + auto last = values.begin() + static_cast(Page + values_to); + while (first != last) { + *fill++ = hash_v(*first++); + } + } + + // dead cases, try to avoid case collisions + for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits::max)(); *fill++ = ++last_value) { + } + + { + auto it = result.begin(); + auto last_value = (std::numeric_limits::min)(); + for (; fill != result.end(); *fill++ = last_value++) { + while (last_value == *it) { + ++last_value, ++it; + } + } + } + + return result; +} + +template +constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v) { + if constexpr (std::is_void_v) { + std::forward(f)(std::forward(args)...); + } else { + return static_cast(std::forward(f)(std::forward(args)...)); + } +} + +enum class case_call_t { + index, + value +}; + +template +inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v) { return T{}; }; + +template <> +inline constexpr auto default_result_type_lambda = []() noexcept {}; + +template +constexpr bool has_duplicate() noexcept { + using value_t = std::decay_t; + using hash_value_t = std::invoke_result_t; + std::arraysize()> hashes{}; + std::size_t size = 0; + for (auto elem : *Arr) { + hashes[size] = hash_v(elem); + for (auto i = size++; i > 0; --i) { + if (hashes[i] < hashes[i - 1]) { + auto tmp = hashes[i]; + hashes[i] = hashes[i - 1]; + hashes[i - 1] = tmp; + } else if (hashes[i] == hashes[i - 1]) { + return false; + } else { + break; + } + } + } + return true; +} + +#define MAGIC_ENUM_CASE(val) \ + case cases[val]: \ + if constexpr ((val) + Page < size) { \ + if (!pred(values[val + Page], searched)) { \ + break; \ + } \ + if constexpr (CallValue == case_call_t::index) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ + } else if constexpr (std::is_invocable_v>) { \ + MAGIC_ENUM_ASSERT(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } else if constexpr (CallValue == case_call_t::value) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), enum_constant{}); \ + } else if constexpr (std::is_invocable_r_v>) { \ + MAGIC_ENUM_ASSERT(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } \ + break; \ + } else [[fallthrough]]; + +template ::value_type>, + typename BinaryPredicate = std::equal_to<>, + typename Lambda, + typename ResultGetterType> +constexpr decltype(auto) constexpr_switch( + Lambda&& lambda, + typename std::decay_t::value_type searched, + ResultGetterType&& def, + BinaryPredicate&& pred = {}) { + using result_t = std::invoke_result_t; + using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; + static_assert(has_duplicate(), "magic_enum::detail::constexpr_switch duplicated hash found, please report it: https://github.com/Neargye/magic_enum/issues."); + constexpr std::array values = *GlobValues; + constexpr std::size_t size = values.size(); + constexpr std::array cases = calculate_cases(Page); + + switch (hash_v(searched)) { + MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE) + default: + if constexpr (size > 256 + Page) { + return constexpr_switch(std::forward(lambda), searched, std::forward(def)); + } + break; + } + return def(); +} + +#undef MAGIC_ENUM_CASE + +#endif + +} // namespace magic_enum::detail + +// Checks is magic_enum supported compiler. +inline constexpr bool is_magic_enum_supported = detail::supported::value; + +template +using Enum = detail::enum_concept; + +// Checks whether T is an Unscoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type. Otherwise, value is equal to false. +template +struct is_unscoped_enum : detail::is_unscoped_enum {}; + +template +inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; + +// Checks whether T is an Scoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. Otherwise, value is equal to false. +template +struct is_scoped_enum : detail::is_scoped_enum {}; + +template +inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; + +// If T is a complete enumeration type, provides a member typedef type that names the underlying type of T. +// Otherwise, if T is not an enumeration type, there is no member type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. +template +struct underlying_type : detail::underlying_type {}; + +template +using underlying_type_t = typename underlying_type::type; + +template +using enum_constant = detail::enum_constant; + +// Returns type name of enum. +template +[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t { + constexpr string_view name = detail::type_name_v>; + static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name."); + + return name; +} + +// Returns number of enum values. +template > +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t { + return detail::count_v, S>; +} + +// Returns enum value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +template > +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v) { + return MAGIC_ENUM_ASSERT(index < detail::count_v), detail::values_v[index]; + } else { + constexpr auto min = (S == detail::enum_subtype::flags) ? detail::log2(detail::min_v) : detail::min_v; + + return MAGIC_ENUM_ASSERT(index < detail::count_v), detail::value(index); + } +} + +// Returns enum value at specified index. +template > +[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + static_assert(I < detail::count_v, "magic_enum::enum_value out of range."); + + return enum_value(I); +} + +// Returns std::array with enum values, sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + return detail::values_v; +} + +// Returns integer value from enum value. +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t> { + return static_cast>(value); +} + +// Returns underlying value from enum value. +template +[[nodiscard]] constexpr auto enum_underlying(E value) noexcept -> detail::enable_if_t> { + return static_cast>(value); +} + +// Obtains index in enum values from enum value. +// Returns optional with index. +template > +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + using U = underlying_type_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v || (S == detail::enum_subtype::flags)) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{i}; }, + value, + detail::default_result_type_lambda>); +#else + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + return {}; // Invalid value or out of range. +#endif + } else { + const auto v = static_cast(value); + if (v >= detail::min_v && v <= detail::max_v) { + return static_cast(v - detail::min_v); + } + return {}; // Invalid value or out of range. + } +} + +// Obtains index in enum values from enum value. +// Returns optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + return enum_index(value); +} + +// Obtains index in enum values from static storage enum variable. +template >> +[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t {\ + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + constexpr auto index = enum_index(V); + static_assert(index, "magic_enum::enum_index enum value does not have a index."); + + return *index; +} + +// Returns name from static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the enum_range limitation. +template +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t { + constexpr string_view name = detail::enum_name_v, V>; + static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name."); + + return name; +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template > +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + if (const auto i = enum_index(value)) { + return detail::names_v[*i]; + } + return {}; +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + return enum_name(value); +} + +// Returns std::array with names, sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + return detail::names_v; +} + +// Returns std::array with pairs (value, name), sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + return detail::entries_v; +} + +// Allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); +inline constexpr auto case_insensitive = detail::case_insensitive<>{}; + +// Obtains enum value from integer value. +// Returns optional with enum value. +template > +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v || (S == detail::enum_subtype::flags)) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + [](D v) { return optional{v}; }, + static_cast(value), + detail::default_result_type_lambda>); +#else + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == static_cast>(enum_value(i))) { + return static_cast(value); + } + } + return {}; // Invalid value or out of range. +#endif + } else { + if (value >= detail::min_v && value <= detail::max_v) { + return static_cast(value); + } + return {}; // Invalid value or out of range. + } +} + +// Obtains enum value from name. +// Returns optional with enum value. +template , typename BinaryPredicate = std::equal_to<>> +[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { + using D = std::decay_t; + static_assert(detail::is_reflected_v, "magic_enum requires enum implementation and valid max and min."); + +#if defined(MAGIC_ENUM_ENABLE_HASH) + if constexpr (detail::is_default_predicate()) { + return detail::constexpr_switch<&detail::names_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{detail::values_v[i]}; }, + value, + detail::default_result_type_lambda>, + [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); + } +#endif + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); + } + } + return {}; // Invalid value or out of range. +} + +// Checks whether enum contains value with such value. +template > +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + using U = underlying_type_t; + + return static_cast(enum_cast(static_cast(value))); +} + +// Checks whether enum contains value with such value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + using U = underlying_type_t; + + return static_cast(enum_cast(static_cast(value))); +} + +// Checks whether enum contains value with such integer value. +template > +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value)); +} + +// Checks whether enum contains enumerator with such name. +template , typename BinaryPredicate = std::equal_to<>> +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value, std::move(p))); +} + +// Returns true if the enum integer value is in the range of values that can be reflected. +template > +[[nodiscard]] constexpr auto enum_reflected(underlying_type_t value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + if constexpr (detail::is_reflected_v) { + constexpr auto min = detail::reflected_min(); + constexpr auto max = detail::reflected_max(); + return value >= min && value <= max; + } else { + return false; + } +} + +// Returns true if the enum value is in the range of values that can be reflected. +template > +[[nodiscard]] constexpr auto enum_reflected(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + return enum_reflected(static_cast>(value)); +} + +// Returns true if the enum value is in the range of values that can be reflected. +template +[[nodiscard]] constexpr auto enum_reflected(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + return enum_reflected(value); +} + +template +inline constexpr auto as_flags = AsFlags ? detail::enum_subtype::flags : detail::enum_subtype::common; + +template +inline constexpr auto as_common = AsFlags ? detail::enum_subtype::common : detail::enum_subtype::flags; + +namespace bitwise_operators { + +template = 0> +constexpr E operator~(E rhs) noexcept { + return static_cast(~static_cast>(rhs)); +} + +template = 0> +constexpr E operator|(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +template = 0> +constexpr E operator&(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +template = 0> +constexpr E operator^(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +template = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { + return lhs = (lhs | rhs); +} + +template = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { + return lhs = (lhs & rhs); +} + +template = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { + return lhs = (lhs ^ rhs); +} + +} // namespace magic_enum::bitwise_operators + +} // namespace magic_enum + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +#undef MAGIC_ENUM_GET_ENUM_NAME_BUILTIN +#undef MAGIC_ENUM_GET_TYPE_NAME_BUILTIN +#undef MAGIC_ENUM_VS_2017_WORKAROUND +#undef MAGIC_ENUM_ARRAY_CONSTEXPR +#undef MAGIC_ENUM_FOR_EACH_256 + +#endif // NEARGYE_MAGIC_ENUM_HPP diff --git a/mdss2dot.cc b/mdss2dot.cc index 6b9edf3..243e92b 100644 --- a/mdss2dot.cc +++ b/mdss2dot.cc @@ -2,17 +2,24 @@ #include #include #include -#include -#include "mdss2dot.hpp" +#include "argparse.hpp" #include "mer_op.hpp" #include "common.hpp" typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; +struct MDSs2DOTArgs : argparse::Args { + std::string& mds_arg = arg("DS list file"); + + void welcome() override { + std::cout << "Generate dot file from MDS list" << std::endl; + } +}; + int main(int argc, char* argv[]) { - mdss2dot args(argc, argv); + const auto args = argparse::parse(argc, argv); std::ifstream is(args.mds_arg); if(!is.good()) { diff --git a/mdss2dot.yaggo b/mdss2dot.yaggo deleted file mode 100644 index f7500c2..0000000 --- a/mdss2dot.yaggo +++ /dev/null @@ -1,7 +0,0 @@ -purpose 'Generate dot file from MDS list' -description '' - -arg('mds') { - description 'MDS list file' - c_string; typestr 'path' -} diff --git a/mdsscope.def b/mdsscope.def index b0a2a23..6d4a6bc 100644 --- a/mdsscope.def +++ b/mdsscope.def @@ -15,7 +15,7 @@ Stage: devel %post # System installation apt update - apt install -y --no-install-recommends tup fuse3 yaggo build-essential libxxhash-dev pkg-config + apt install -y --no-install-recommends tup fuse3 build-essential libxxhash-dev pkg-config # Build mdsscope for each of the parameters in params. ( diff --git a/mykkeltveit_set.cc b/mykkeltveit_set.cc index 617cbc3..386848a 100644 --- a/mykkeltveit_set.cc +++ b/mykkeltveit_set.cc @@ -1,12 +1,11 @@ #include #include -#include #include #include #include +#include "argparse.hpp" #include "mykkeltveit.hpp" -#include "mykkeltveit_set.hpp" #ifndef K #error Must define k-mer length K @@ -22,6 +21,21 @@ #include "dbg.hpp" #include "longest_path.hpp" +struct MykkeltveiSetArgs : argparse::Args { + std::optional& offset_arg = kwarg("o,offset", "Offset in root of unity power").set_default(1); + bool& all_flag = flag("a,all", "Output all set (both offsets, all combination of repeat PCR)"); + bool& range_flag = flag("r,range", "Find range (smallest/largest longest path)"); + bool& longest_path_flag = flag("l,longest-path", "Compute longest path"); + bool& brute_flag = flag("brute", "Use brute force to create set"); + + void welcome() override { + std::cout << + "Create Mykkeltveit\'s set\n\n" + "Almost identical to Mykkeltveit, but not quite" + << std::endl; + } +}; + typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; typedef pcr_info_type pcr_info_t; @@ -87,12 +101,12 @@ struct repeat { }; int main(int argc, char* argv[]) { - mykkeltveit_set args(argc, argv); + const auto args = argparse::parse(argc, argv); const pcr_info_t pcr_info; root_unity_type root_unity; - mer_t offset = args.offset_arg; + mer_t offset = *args.offset_arg; auto mds = !args.brute_flag ? find_mds(pcr_info, offset, root_unity) : brute_force_mds(pcr_info, offset, root_unity); std::vector repeat_pcrs; @@ -156,7 +170,7 @@ int main(int argc, char* argv[]) { mds[r.index] = r.pcr[r.offset]; } if(i < 0) { - if(!args.offset_given && args.all_flag && offset == 1) { + if(!args.offset_arg && args.all_flag && offset == 1) { offset = mer_ops::k / 2 + 1; mds = find_mds(pcr_info, offset, root_unity); } else { diff --git a/mykkeltveit_set.yaggo b/mykkeltveit_set.yaggo deleted file mode 100644 index 2a0474f..0000000 --- a/mykkeltveit_set.yaggo +++ /dev/null @@ -1,27 +0,0 @@ -purpose 'Create Mykkeltveit\'s set' -description 'Almost identical to Mykkeltveit, but not quite' - -option('o', 'offset') { - description 'Offset in root of unity power' - uint32; default 1 -} - -option('a', 'all') { - description 'Output all set (both offsets, all combination of repeat PCR)' - flag; off -} - -option('r', 'range') { - description 'Find range (smallest/largest longest path)' - flag; off -} - -option('l', 'longest-path') { - description 'Compute longest path' - flag; off -} - -option('brute') { - description 'Use brute force to create set' - flag; off -} diff --git a/old_champarnaud_set.cc b/old_champarnaud_set.cc index 9b6a7ad..a2ff177 100644 --- a/old_champarnaud_set.cc +++ b/old_champarnaud_set.cc @@ -1,11 +1,10 @@ #include #include #include -#include #include #include -#include "old_champarnaud_set.hpp" +#include "argparse.hpp" #include "common.hpp" #include "dbg.hpp" @@ -18,6 +17,18 @@ #error Must define alphabet length ALPHA #endif +struct OldChamparnaudArgs : argparse::Args { + + void welcome() override { + std::cout << + "Generate the Champarnaud decycling set\n\n" + "For each PCR, find the minimal k-mer m, do a principal division:\n\n" + "m = l^n u, with l a Lyndon word, the smallest one, and |l|<|u|\n\n" + "Output u l^n for each PCR." + << std::endl; + } +}; + #include "mer_op.hpp" typedef mer_op_type mer_ops; @@ -114,7 +125,7 @@ mer_t champarnaud_mer(mer_t m) { template struct amain { - int operator()(const old_champarnaud_set& args) { + int operator()(const OldChamparnaudArgs& args) { std::cerr << "Problem size too big" << std::endl; return EXIT_FAILURE; } @@ -122,7 +133,7 @@ struct amain { template struct amain { - int operator()(const old_champarnaud_set& args) { + int operator()(const OldChamparnaudArgs& args) { std::bitset done; std::vector res; @@ -151,7 +162,7 @@ struct amain { int main(int argc, char* argv[]) { - const old_champarnaud_set args(argc, argv); + const auto args = argparse::parse(argc, argv); return amain()(args); diff --git a/old_champarnaud_set.yaggo b/old_champarnaud_set.yaggo deleted file mode 100644 index b61ec2c..0000000 --- a/old_champarnaud_set.yaggo +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: false - -purpose ' Generate the Champarnaud decycling set' -description <<~HELP - For each PCR, find the minimal k-mer m, do a principal division: - - m = l^n u, with l a Lyndon word, the smallest one, and |l|<|u| - - Output u l^n for each PCR. -HELP - -# Work around a bug with yaggo < 1.5.11: with no options, it generates -# an invalid class. Define a bogus option. -option("noop") { - description "Does not do anything. Ignore this option" - flag; off -} diff --git a/opt_canon.cc b/opt_canon.cc index 8c1e070..94b4ffa 100644 --- a/opt_canon.cc +++ b/opt_canon.cc @@ -7,12 +7,10 @@ #include #include #include -#include #include -#include "opt_canon.hpp" +#include "argparse.hpp" #include "misc.hpp" -#include "common.hpp" #include "mt_queue.hpp" #include "random_seed.hpp" #include "simple_thread_pool.hpp" @@ -28,6 +26,20 @@ #endif #include "mer_op.hpp" + +struct OptCanonArgs : argparse::Args { + std::optional& sketch_file_arg = kwarg("f,sketch-file", "File with sketch mer set"); + bool& can_flag = flag("c,can", "Must be a super set of the canonicalized set"); + uint32_t& threads_arg = kwarg("t,threads", "Number of threads"); + std::optional& output_arg = kwarg("o,output", "Output optimized set"); + bool& longest_flag = flag("l,longest", "Compute and print longest path"); + bool& progress_flag = flag("p,progress", "Show progress"); + std::optional& iseed_arg = kwarg("i,iseed", "Input seed file"); + std::optional& oseed_arg = kwarg("o,ioeed", "Output seed file"); + + std::vector& sketch_arg = arg("sketch"); +}; + typedef amer_type amer_t; typedef amer_t::mer_ops mer_ops; typedef amer_t::mer_t mer_t; @@ -211,7 +223,7 @@ void signal_handler(int signal) template struct amain { - int operator()(const opt_canon& args) { + int operator()(const OptCanonArgs& args) { std::cerr << "Problem size too big" << std::endl; return EXIT_FAILURE; } @@ -219,9 +231,9 @@ struct amain { template struct amain { - int operator()(const opt_canon& args) { - auto prg = seeded_prg(args.oseed_given ? args.oseed_arg : nullptr, - args.iseed_given ? args.iseed_arg : nullptr); + int operator()(const OptCanonArgs& args) { + auto prg = seeded_prg(args.oseed_arg ? *args.oseed_arg : nullptr, + args.iseed_arg ? *args.iseed_arg : nullptr); // Install a signal handler so the computation can be stopped at any time std::signal(SIGINT, signal_handler); @@ -229,7 +241,7 @@ struct amain { - auto orig_set = get_mds>(args.sketch_file_arg, args.sketch_arg); + auto orig_set = get_mds>(args.sketch_file_arg ? *args.sketch_file_arg : nullptr, args.sketch_arg); //auto order = get_mds>(args.sketch_file_arg, args.sketch_arg); std::vector order(orig_set.cbegin(), orig_set.cend()); std::shuffle(order.begin(), order.end(), prg); @@ -287,8 +299,8 @@ struct amain { if(progress) std::cout << '\n'; std::cout << "Removed " << removed << '/' << progress << "\nopt set: " << union_size(order, mer_set) << '\n'; - if(args.output_given) { - std::ofstream out(args.output_arg); + if(args.output_arg) { + std::ofstream out(*args.output_arg); bool first = true; for(mer_t i = 0; i < mer_ops::nb_mers; ++i) { if(!mer_set._data->test(i)) continue; @@ -325,7 +337,7 @@ struct amain { int main(int argc, char* argv[]) { - opt_canon args(argc, argv); + const auto args = argparse::parse(argc, argv); -return amain()(args); + return amain()(args); } diff --git a/opt_canon.yaggo b/opt_canon.yaggo deleted file mode 100644 index 2052616..0000000 --- a/opt_canon.yaggo +++ /dev/null @@ -1,48 +0,0 @@ -description "test" - -option('f', 'sketch-file') { - description 'File with sketch mer set' - c_string; typestr 'path' -} - -option('c', 'can') { - description 'Must be a super set of the canonicalized set' - flag; off -} - -option('t', 'threads') { - description 'Number of threads' - uint32; typestr 'N'; default 1 -} - -option('o', 'output') { - description 'Output optimized set' - c_string; typestr 'path' -} - -option('l', 'longest') { - description 'Compute and print longest path' - flag; off -} - -option('s', 'iseed') { - description 'Input seed file' - typestr 'path' - c_string -} - -option('S', 'oseed') { - description 'Output seed file' - typestr 'path' - c_string -} - -option('p', 'progress') { - description 'Show progress' - flag; off -} - -arg('sketch') { - description 'k-mers' - c_string; multiple -} diff --git a/optimize_rem_path_len.cc b/optimize_rem_path_len.cc index b48dc8e..b559e49 100644 --- a/optimize_rem_path_len.cc +++ b/optimize_rem_path_len.cc @@ -11,15 +11,23 @@ #error Must define alphabet length ALPHA #endif +#include "argparse.hpp" #include "mer_op.hpp" #include "mds_op.hpp" #include "imoves.hpp" #include "imove_signature.hpp" #include "longest_path.hpp" -#include "file_queue.hpp" -#include "misc.hpp" -#include "optimize_rem_path_len.hpp" +enum ArgsOperation { min, max }; +struct OptimizeRemPathLenArgs : argparse::Args { + uint64_t& iteration_arg = kwarg("iteration", "Maximum number of iterations").set_default(1000); + double& lambda_arg = kwarg("lambda", "Lower temperature factor").set_default(0.99); + ArgsOperation& op_arg = kwarg("op", "Operation to optimize for"); + bool& progress_flag = flag("p,progress", "Show progress"); + std::optional& mds_arg = kwarg("f,mds", "File with MDS"); + + std::vector& comp_arg = arg("comp"); +}; typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; @@ -84,15 +92,15 @@ int main(int argc, char* argv[]) { std::mt19937_64 rand_gen((std::random_device())()); std::uniform_real_distribution rand_unit(0.0, 1.0); - optimize_rem_path_len args(argc, argv); + const auto args = argparse::parse(argc, argv); // Comparator for operation bool (*comp)(mer_t, mer_t); switch(args.op_arg) { - case optimize_rem_path_len::op::min: + case ArgsOperation::min: comp = less; break; - case optimize_rem_path_len::op::max: + case ArgsOperation::max: comp = greater; break; default: @@ -103,7 +111,7 @@ int main(int argc, char* argv[]) { element current, best; std::vector fms; - const auto start(args.mds_given ? mds_from_file(args.mds_arg) : mds_from_arg(args.comp_arg)); + const auto start(args.mds_arg ? mds_from_file(*args.mds_arg) : mds_from_arg(args.comp_arg)); mds_op.from_mds_fms(start, current.bmds, current.fms); mds_op.mds2fmoves(start); current.fmoves = mds_op.fmoves; diff --git a/optimize_rem_path_len.yaggo b/optimize_rem_path_len.yaggo deleted file mode 100644 index 3d7d0fc..0000000 --- a/optimize_rem_path_len.yaggo +++ /dev/null @@ -1,31 +0,0 @@ -purpose 'Traverse MDS graph to optimize remaining path length' -description '' - -option('iteration') { - description 'Maximum number of iterations' - uint64; default 1000 -} -option('lambda') { - description 'Lower temperature factor' - double; default 0.99 -} - -option('op') { - description 'Operation to optimize for' - enum 'min', 'max' -} - -option('p', 'progress') { - description 'Show progress' - flag; off -} - -option('f', 'mds') { - description 'File with MDS' - c_string; typestr 'path' -} - -arg('comp') { - description 'component' - c_string; multiple -} diff --git a/rules.sh b/rules.sh index 074e11c..e7e3145 100755 --- a/rules.sh +++ b/rules.sh @@ -1,7 +1,6 @@ #! /bin/bash for n in "${@}"; do - echo ": ${n}.yaggo |> !yaggo |>" - echo ": ${n}.cc | ${n}.hpp |> !cxx |> {${n}_objs}" + echo ": ${n}.cc |> !cxx |> {${n}_objs}" echo ": {${n}_objs} common.ar |> !lxxd |> ${n}" done diff --git a/sketch_components.cc b/sketch_components.cc index fb929ff..aecdbc4 100644 --- a/sketch_components.cc +++ b/sketch_components.cc @@ -2,14 +2,12 @@ // strongly connected components of the de Bruijn graph minus the set of // selected k-mers. +#include "argparse.hpp" #include #include #include -#include #include #include -#include -#include #include #ifndef K @@ -20,16 +18,27 @@ #error Must define alphabet length ALPHA #endif -#include "typename.hpp" #include "misc.hpp" -#include "common.hpp" -#include "sketch_components.hpp" #include "tarjan_scc.hpp" #include "mer_op.hpp" typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; +struct SketchComponentsArgs : argparse::Args { + std::optional& sketch_file_arg = kwarg("f,sketch-file", "File with sketch mer set"); + bool& straight_flag = flag("s,straight", "Use set directly (default)"); + bool& canonical_flag = flag("c,canonical", "Use canonical k-mers"); + bool& union_flag = flag("u,union", "Use union of set and reverse complemented set"); + bool& progress_flag = flag("p,progress", "Display progress"); + + std::vector& sketch_arg = arg("k-mers"); + + void welcome() override { + std::cout << "Find sketching methods strongly connected components" << std::endl; + } +}; + // Function checking if a mer is in the set of selecting mers. // typedef bool (*in_set_fn)(mer_t); @@ -52,13 +61,18 @@ struct is_in_union { }; int main(int argc, char* argv[]) { - sketch_components args(argc, argv); + const auto args = argparse::parse(argc, argv); + + if(args.straight_flag + args.canonical_flag + args.union_flag > 1) { + std::cerr << "--straight, --canonical, --union conflict" << std::endl; + return EXIT_FAILURE; + } if constexpr(mer_ops::ak_bits > mer_ops::max_bits) { std::cerr << "Problem size too big" << std::endl; return EXIT_FAILURE; } else { - const auto mer_set = get_mds>(args.sketch_file_arg, args.sketch_arg); + const auto mer_set = get_mds>(args.sketch_file_arg ? *args.sketch_file_arg : nullptr, args.sketch_arg); mer_t components = 0, in_components = 0, visited = 0; size_t updates = 0; diff --git a/sketch_components.yaggo b/sketch_components.yaggo deleted file mode 100644 index 5a91d67..0000000 --- a/sketch_components.yaggo +++ /dev/null @@ -1,37 +0,0 @@ -purpose 'Find sketching methods strongly connected components' -description < #include +#include "argparse.hpp" #include "misc.hpp" // #include "common.hpp" #include "permutations.hpp" @@ -15,7 +16,6 @@ #include "mykkeltveit.hpp" #include "champarnaud.hpp" #include "syncmer.hpp" -#include "sketch_histo.hpp" #ifndef K #error Must define k-mer length K @@ -27,6 +27,34 @@ #include "mer_op.hpp" +struct SketchHistoArgs : argparse::Args { + std::optional& alphabet_arg = kwarg("a,alphabet", "Alphabet translation"); + std::optional& sketch_file_arg = kwarg("f,sketch-file", "File with sketch mer set"); + std::optional& iseed_arg = kwarg("i,iseed", "Input seed file"); + std::optional& oseed_arg = kwarg("o,ioeed", "Output seed file"); + bool& mykkeltveit_flag = flag("mykkeltveit", "Stream Mykkeltveit set"); + std::optional& syncmer_arg = kwarg("syncmer", "Stream syncmer set"); + std::optional& syncmer_s_arg = kwarg("syncmer-s", "Syncmer s parameter (default k/2 - 1)"); + std::optional& frac_arg = kwarg("frac", "Stream Frac set"); + bool& champarnaud_flag = flag("champarnaud", "Stream Champarnaud set"); + bool& straight_flag = flag("s,straight", "Use set directly (default)"); + bool& canonical_flag = flag("c,canonical", "Use canonical k-mers"); + bool& union_flag = flag("u,union", "Use union of set and reverse complement set"); + bool& sum_flag = flag("sum", "Output weighted sum of the histo"); + uint32_t& hmin_arg = flag("hmin", "Ignore length of histo less than hmin").set_default(0); + + + std::vector& sketch_arg = arg("sketch"); + + void welcome() override { + std::cout << + "Sketch a sequence given a context free / set scheme\n\n" + "Boils down to the intersection of k-mers in the set and in the sequence.\n" + "Sequence is read from stdin, set from -f or command line arguments." + << std::endl; + } +}; + typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; @@ -206,13 +234,13 @@ double fill_in_histo(translated_stream& ts, std::vector& histo, std::fun } int main(int argc, char* argv[]) { - sketch_histo args(argc, argv); + const auto args = argparse::parse(argc, argv); std::vector> lookups; // Stack of lookup function. Composed with top of stack lookups.reserve(10); - auto prg = seeded_prg(args.oseed_given ? args.oseed_arg : nullptr, - args.iseed_given ? args.iseed_arg : nullptr); + auto prg = seeded_prg(args.oseed_arg ? *args.oseed_arg : nullptr, + args.iseed_arg ? *args.iseed_arg : nullptr); // Bottom layer std::unique_ptr> mer_set; @@ -223,27 +251,27 @@ int main(int argc, char* argv[]) { std::unique_ptr frac_data; std::unique_ptr champarnaud_data; - if(args.sketch_file_given || !args.sketch_arg.empty()) { + if(args.sketch_file_arg || !args.sketch_arg.empty()) { mer_set.reset(new std::unordered_set); - *mer_set = get_mds>(args.sketch_file_arg, args.sketch_arg); + *mer_set = get_mds>(*args.sketch_file_arg, args.sketch_arg); // std::cout << "mer_set size " << mer_set.size() << ": " << joinT(mer_set, ',') << '\n'; lookups.emplace_back(std::bind_front(straight_set, mer_set.get())); // Bottom layer is querying the set } else if(args.mykkeltveit_flag) { root_unity.reset(new root_unity_type); lookups.emplace_back(std::bind_front(mykkeltveit, root_unity.get())); - } else if(args.syncmer_given) { - const unsigned s = args.syncmer_s_given ? args.syncmer_s_arg : K / 2 - 1; + } else if(args.syncmer_arg) { + const unsigned s = args.syncmer_s_arg ? *args.syncmer_s_arg : K / 2 - 1; if(std::pow(mer_ops::alpha, s) < 1e9) { - std::cerr << "syncmer " << s << ' ' << args.syncmer_arg << std::endl; - syncmer_data.reset(new syncmer_data_type(s, args.syncmer_arg, &prg)); + std::cerr << "syncmer " << s << ' ' << *args.syncmer_arg << std::endl; + syncmer_data.reset(new syncmer_data_type(s, *args.syncmer_arg, &prg)); lookups.emplace_back(std::bind_front(syncmer, syncmer_data.get())); } else { - std::cerr << "syncmer large" << s << ' ' << args.syncmer_arg << std::endl; - syncmer_large_data.reset(new syncmer_large_data_type(s, args.syncmer_arg, &prg)); + std::cerr << "syncmer large" << s << ' ' << *args.syncmer_arg << std::endl; + syncmer_large_data.reset(new syncmer_large_data_type(s, *args.syncmer_arg, &prg)); lookups.emplace_back(std::bind_front(syncmer_large, syncmer_large_data.get())); } - } else if(args.frac_given) { - frac_data.reset(new frac_data_type(args.frac_arg, prg)); + } else if(args.frac_arg) { + frac_data.reset(new frac_data_type(*args.frac_arg, prg)); lookups.emplace_back(std::bind_front(frac, frac_data.get())); } else if(args.champarnaud_flag) { champarnaud_data.reset(new champarnaud_data_type()); @@ -263,7 +291,7 @@ int main(int argc, char* argv[]) { const auto& lookup = lookups.back(); std::vector histo; - translated_stream ts(args.alphabet_arg, mer_ops::alpha, std::cin); + translated_stream ts(args.alphabet_arg ? *args.alphabet_arg : nullptr, mer_ops::alpha, std::cin); while(ts) { // std::cout << "loop" << std::endl; diff --git a/sketch_histo.yaggo b/sketch_histo.yaggo deleted file mode 100644 index 96ab542..0000000 --- a/sketch_histo.yaggo +++ /dev/null @@ -1,93 +0,0 @@ -purpose 'Sketch a sequence given a context free / set scheme' -description < -#include "syncmer_set.hpp" +#include "argparse.hpp" #include "random_seed.hpp" #include "divisor.hpp" #include "syncmer.hpp" @@ -17,16 +17,26 @@ typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; +struct SyncmerArgs : argparse::Args { + uint32_t& s_arg = kwarg("s", "s-mer size"); + uint32_t& t_arg = kwarg("t", "Position of minimum s-mer"); + std::optional& iseed_arg = kwarg("i,iseed", "Input seed file"); + std::optional& oseed_arg = kwarg("o,ioeed", "Output seed file"); + void welcome() override { + std::cout << "Generate a syncmer set" << std::endl; + } +}; int main(int argc, char* argv[]) { - syncmer_set args(argc, argv); + const auto args = argparse::parse(argc, argv); + const auto nb_smers = ipow(mer_ops::alpha, args.s_arg); jflib::divisor64 div_s(nb_smers); // Fast division / modulo by nb_smers // The properly seeded PRG - auto prg = seeded_prg(args.oseed_given ? args.oseed_arg : nullptr, - args.iseed_given ? args.iseed_arg : nullptr); + auto prg = seeded_prg(args.oseed_arg ? *args.oseed_arg : nullptr, + args.iseed_arg ? *args.iseed_arg : nullptr); // Random order of s-mers std::vector smer_order(nb_smers); diff --git a/syncmer_set.yaggo b/syncmer_set.yaggo deleted file mode 100644 index 0132252..0000000 --- a/syncmer_set.yaggo +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: false - -purpose 'Generate a syncmer set' - -option('s') { - description 's-mer size' - uint32 - required -} -option('t') { - description 'Position of minimum s-mer' - uint32 - required -} - -option('i', 'iseed') { - description 'Input seed file' - typestr 'path' - c_string -} - -option('o', 'oseed') { - description 'Output seed file' - typestr 'path' - c_string -} diff --git a/syncmer_sketch.cc b/syncmer_sketch.cc index 17f86ab..e550036 100644 --- a/syncmer_sketch.cc +++ b/syncmer_sketch.cc @@ -1,13 +1,12 @@ #include #include #include -#include #include #include #include +#include "argparse.hpp" #include "random_seed.hpp" -#include "syncmer_sketch.hpp" #include "divisor.hpp" #include "sequence.hpp" #include "syncmer.hpp" @@ -22,12 +21,23 @@ #include "mer_op.hpp" +struct SyncmerSketchArgs : argparse::Args { + uint32_t& s_arg = kwarg("s", "s-mer size"); + uint32_t& t_arg = kwarg("t", "Position of minimum s-mer"); + std::optional& alphabet_arg = kwarg("a,alphabet", "Alphabet translation"); + bool& canonical_flag = flag("c,canonical", "Canonical space"); + bool& lex_flag = flag("lex", "Use lexicographic order"); + std::optional& iseed_arg = kwarg("i,iseed", "Input seed file"); + std::optional& oseed_arg = kwarg("o,ioeed", "Output seed file"); +}; + typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; int main(int argc, char* argv[]) { - syncmer_sketch args(argc, argv); + const auto args = argparse::parse(argc, argv); + if(args.s_arg > mer_ops::k) { std::cerr << "Error: s must be less than k" << std::endl; return EXIT_FAILURE; @@ -37,8 +47,8 @@ int main(int argc, char* argv[]) { jflib::divisor64 div_s(nb_smers); // Fast division / modulo by nb_smers // The properly seeded PRG - auto prg = seeded_prg(args.oseed_given ? args.oseed_arg : nullptr, - args.iseed_given ? args.iseed_arg : nullptr); + auto prg = seeded_prg(args.oseed_arg ? *args.oseed_arg : nullptr, + args.iseed_arg ? *args.iseed_arg : nullptr); // Random order of s-mers std::vector smer_order(nb_smers); for(mer_t s = 0; s < nb_smers; ++s) @@ -49,7 +59,7 @@ int main(int argc, char* argv[]) { char inchar = '0'; mer_t mer = 0; size_t offset = 0; - translated_stream ts(args.alphabet_arg, mer_ops::k, std::cin); + translated_stream ts(*args.alphabet_arg, mer_ops::k, std::cin); // Read first k-1 bases while(offset + 1 < mer_ops::k && ts >> inchar) { mer = mer_ops::nmer(mer, inchar); diff --git a/syncmer_sketch.yaggo b/syncmer_sketch.yaggo deleted file mode 100644 index 1de8f34..0000000 --- a/syncmer_sketch.yaggo +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: false - -purpose 'Generate a syncmer sketch' - -option('s') { - description 's-mer size' - uint32 - required -} -option('t') { - description 'Position of minimum s-mer' - uint32 - required -} - -option('a', 'alphabet') { - description 'Alphabet translation' - c_string -} - -option('c', 'canonical') { - description 'Canonical space' - flag; off -} - -option('i', 'iseed') { - description 'Input seed file' - typestr 'path' - c_string -} - -option('o', 'oseed') { - description 'Output seed file' - typestr 'path' - c_string -} - -option('lex') { - description 'Use lexicographic order' - flag; off -} diff --git a/traverse_comp.cc b/traverse_comp.cc index bf207c6..8a0ec99 100644 --- a/traverse_comp.cc +++ b/traverse_comp.cc @@ -1,11 +1,10 @@ +#include "argparse.hpp" #include #include #include #include #include -#include "traverse_comp.hpp" - #ifndef K #error Must define k-mer length K #endif @@ -31,6 +30,21 @@ typedef mer_op_type mer_ops; typedef mer_ops::mer_t mer_t; typedef std::lock_guard guard_t; +struct TraverseCompArgs : argparse::Args { + std::string& comps_arg = kwarg("c,comps", "Output file for component"); + std::string& dot_arg = kwarg("d,dot", "Output file for the component graph"); + bool& progress_flag = flag("p,progress", "Display progress"); + uint32_t& threads_arg = kwarg("t,threads", "Thread target (all)").set_default(0); + std::vector& comp_arg = arg("component"); + + void welcome() { + std::cout << + "Traverse the component graph\n" + "Start from one MDS, output all the components in the graph." + << std::endl; + } +}; + void thread_work(comp_queue& queue, std::mutex& qlock, signatures_type& signatures, std::mutex& sig_lock, std::ostream& dot_fd, std::mutex& dot_lock) { @@ -121,7 +135,7 @@ void progress_thread(const size_t th_target, std::atomic& joined, int main(int argc, char* argv[]) { std::ios::sync_with_stdio(false); - traverse_comp args(argc, argv); + const auto args = argparse::parse(argc, argv); std::ofstream dot_fd(args.dot_arg); if(!dot_fd.good()) { @@ -129,7 +143,7 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } dot_fd << "digraph {\n"; - comp_queue queue(args.comps_arg); + comp_queue queue(args.comps_arg.c_str()); signatures_type signatures; { // Initialize signature set and queue diff --git a/traverse_comp.yaggo b/traverse_comp.yaggo deleted file mode 100644 index 4d5ddc0..0000000 --- a/traverse_comp.yaggo +++ /dev/null @@ -1,22 +0,0 @@ -purpose 'Traverse the component graph' -description 'Start from one MDS, output all the components in the graph.' - -option('comps', 'c') { - description 'Output file for component' - required; c_string; typestr "path" -} -option('dot', 'd') { - description 'Output file for the component graph' - required; c_string; typestr "path" -} -option('progress', 'p') { - description 'Display progress' - flag; off -} -option('threads', 't') { - description 'Thread target (all)' - uint32; default 0 -} -arg('comp') { - description 'component' - c_string; multiple}