#include "Debug.h"
#include "Command.h"
#include "CommandDeclarations.h"
#include "Util.h"
#include "Parameters.h"

const char* binary_name = "mmseqs";
const char* tool_name = "MMseqs2";
const char* tool_introduction = "MMseqs2 (Many against Many sequence searching) is an open-source software suite for very fast, \nparallelized protein sequence searches and clustering of huge protein sequence data sets.\n\nPlease cite: M. Steinegger and J. Soding. MMseqs2 enables sensitive protein sequence searching for the analysis of massive data sets. Nature Biotechnology, doi:10.1038/nbt.3988 (2017).";
const char* main_author = "Martin Steinegger (martin.steinegger@mpibpc.mpg.de)";
const char* show_extended_help = "1";
const char* show_bash_info = "1";

Parameters& par = Parameters::getInstance();

std::vector<struct Command> commands = {
// Main tools (for non-experts)
        {"createdb",             createdb,             &par.createdb,             COMMAND_MAIN,
                "Convert protein sequence set in a FASTA file to MMseqs sequence DB format",
                "converts a protein sequence flat/gzipped FASTA or FASTQ file to the MMseqs sequence DB format. This format is needed as input to mmseqs search, cluster and many other tools.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:fastaFile1[.gz]> ... <i:fastaFileN[.gz]> <o:sequenceDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"easy-search",          easysearch,           &par.easysearchworkflow,   COMMAND_EASY,
                "Search with a query fasta against target fasta (or database) and return a BLAST-compatible result in a single step",
                "Searches with a sequence FASTA file through the target sequence FASTA file or DB by in a single step. This combines createdb, search, summarizeresults, convert and convertalis modules into a single workflow.",
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryFastaFile[.gz]> <i:targetFastaFile[.gz]>|<i:targetDB> <o:alignmentFile> <tmpDir>",
                CITATION_SERVER | CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"easy-linsearch",       easylinsearch,        &par.easylinsearchworkflow,COMMAND_EASY,
                "Linear time search with a query fasta against target fasta (or database) and return a BLAST-compatible result in a single step",
                "Searches with a sequence FASTA file through the target sequence FASTA file or DB by in a single step. This combines createdb, linsearch, summarizeresults, convert and convertalis modules into a single workflow.",
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryFastaFile[.gz]> <i:targetFastaFile[.gz]>|<i:targetDB> <o:alignmentFile> <tmpDir>",
                CITATION_SERVER | CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"easy-linclust",        easylinclust,         &par.easylinclustworkflow, COMMAND_EASY,
                "Compute clustering of a fasta database in linear time. The workflow outputs the representative sequences, a cluster tsv and a fasta-like format containing all sequences.",
                "Clusters sequences by similarity in linear time. It groups similar sequences together based on user-specified criteria (max. E-value, seq. id., min. coverage,...).",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryFastaFile[.gz]> <o:clusterPrefix> <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"easy-cluster",         easycluster,          &par.easyclusterworkflow,  COMMAND_EASY,
                "Compute clustering of a fasta database. The workflow outputs the representative sequences, a cluster tsv and a fasta-like format containing all sequences.",
                "Clusters sequences by similarity. It compares all sequences in the sequence DB with each other using mmseqs search, filters alignments according to user-specified criteria (max. E-value, min. coverage,...),   and runs mmseqs clust to group similar sequences together into clusters.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryFastaFile[.gz]> <o:clusterPrefix> <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"search",               search,               &par.searchworkflow,       COMMAND_MAIN,
                "Search with query sequence or profile DB (iteratively) through target sequence DB",
                "Searches with the sequences or profiles query DB through the target sequence DB by running the prefilter tool and the align tool for Smith-Waterman alignment. For each query a results file with sequence matches is written as entry into a database of search results (alignmentDB).\nIn iterative profile search mode, the detected sequences satisfying user-specified criteria are aligned to the query MSA, and the resulting query profile is used for the next search iteration. Iterative profile searches are usually much more sensitive than (and at least as sensitive as) searches with single query sequences.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <o:alignmentDB> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"alignmentDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb  },
                                  {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::directory }}},
        {"linsearch",               linsearch,               &par.linsearchworkflow,       COMMAND_MAIN,
                "Search with query sequence  DB through target sequence DB",
                "Searches with the sequences query DB through the target sequence DB by running the kmersearch tool and the align tool for Smith-Waterman alignment. For each query a results file with sequence matches is written as entry into a database of search results (alignmentDB).\n",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <o:alignmentDB> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"alignmentDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb },
                                  {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::directory }}},
        {"map",                  map,                  &par.mapworkflow,          COMMAND_MAIN,
                "Fast ungapped mapping of query sequences to target sequences.",
                "Finds very similar sequence matches in a sequence database. First calls the prefilter module (with a low sensitivity setting) to detect high scoring diagonals and then computes an ungapped alignment with the rescorediagonal module. In contrast to the normal search, for maximum speed no gapped alignment is computed, query sequences are not masked for low complexity regions and no compositional bias correction is applied.",
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <o:alignmentDB> <tmpDir>",
                CITATION_PLASS|CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                  {"alignmentDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb },
                                                  {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::directory }}},
        {"cluster",              clusteringworkflow,   &par.clusterworkflow,      COMMAND_MAIN,
                "Compute clustering of a sequence DB (quadratic time)",
                "Clusters sequences by similarity. It compares all sequences in the sequence DB with each other using mmseqs search, filters alignments according to user-specified criteria (max. E-value, min. coverage,...),   and runs mmseqs clust to group similar sequences together into clusters.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> & Lars von den Driesch",
                "<i:sequenceDB> <o:clusterDB> <tmpDir>",
                CITATION_LINCLUST|CITATION_MMSEQS1|CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                                      {"clusterDB", DbType::ACCESS_MODE_OUTPUT, NULL },
                                                                      {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb }}},
        {"linclust",          linclust,          &par.linclustworkflow,           COMMAND_MAIN,
                "Cluster sequences of >30% sequence identity *in linear time*",
                "Detects redundant sequences based on reduced alphabet and k-mer sorting.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:sequenceDB> <o:clusterDB> <tmpDir>",
                CITATION_MMSEQS2|CITATION_LINCLUST, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                     {"clusterDB", DbType::ACCESS_MODE_OUTPUT, NULL },
                                                     {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb }}},
        {"indexdb",          indexdb,          &par.indexdb,                      COMMAND_HIDDEN,
                "Precompute index table of sequence DB for faster searches",
                "Precomputes an index table for the sequence DB. Handing over the precomputed index table as input to mmseqs search or mmseqs prefilter eliminates the computational overhead of building the index table on the fly.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:sequenceIndexDB>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceIndexDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::indexDb }}},
        {"createindex",          createindex,          &par.createindex,          COMMAND_MAIN,
                "Precompute index table of sequence DB for faster searches",
                "Precomputes an index table for the sequence DB. Handing over the precomputed index table as input to mmseqs search or mmseqs prefilter eliminates the computational overhead of building the index table on the fly.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <tmpDir>",
                CITATION_SERVER | CITATION_MMSEQS2,{{"sequenceDB",  DbType::ACCESS_MODE_INPUT, &DbValidator::sequenceDb },
                                                    {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb }}},
        {"createlinindex",          createlinindex,          &par.createlinindex,          COMMAND_MAIN,
                "Precompute index for linsearch",
                "Precomputes a sorted kmer list for the sequence DB.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <tmpDir>",
                CITATION_SERVER | CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT, &DbValidator::sequenceDb },
                                                     {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb }}},
        {"enrich",                enrich,              &par.enrichworkflow,       COMMAND_MAIN,
                "Enrich a query set by searching iteratively through a profile sequence set.",
                "",
                "Milot Mirdita <milot@mirdita.de>",
                "<i:queryDB> <i:targetDB> <o:alignmentDB> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb  },
                                   {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::directory }}},
        {"rbh",                  rbh,                  &par.searchworkflow,       COMMAND_MAIN,
                "Find reciprocal best hits between query and target",
                "",
                "Eli Levy Karin",
                "<i:queryDB> <i:targetDB> <o:alignmentDB> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb  },
                                   {"tmpDir", DbType::ACCESS_MODE_INPUT, &DbValidator::directory }}},
// Utility tools for format conversions
        {"createtsv",            createtsv,            &par.createtsv,            COMMAND_FORMAT_CONVERSION,
                "Create tab-separated flat file from prefilter DB, alignment DB, cluster DB, or taxa DB",
                "Create tab-separated flat file from prefilter DB, alignment DB, cluster DB, or taxa DB. The target database is optional. This is useful for taxa DB, since it does not have a target key.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> [<i:targetDB>] <i:resultDB> <o:tsvFile>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"convertalis",          convertalignments,    &par.convertalignments,    COMMAND_FORMAT_CONVERSION,
                "Convert alignment DB to BLAST-tab format or specified custom-column output format",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDb> <i:targetDb> <i:alignmentDB> <o:alignmentFile>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                   {"tmpDir", DbType::ACCESS_MODE_OUTPUT, &DbValidator::alignmentDb}}},
        {"convertprofiledb",     convertprofiledb,     &par.convertprofiledb,     COMMAND_FORMAT_CONVERSION,
                "Convert ffindex DB of HMM files to profile DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:ffindexProfileDB> <o:profileDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"convert2fasta",        convert2fasta,        &par.convert2fasta,        COMMAND_FORMAT_CONVERSION,
                "Convert sequence DB to FASTA format",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:sequenceDB> <o:fastaFile>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::nuclAaDb },
                                   {"fastaFile", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::flatfile }}},
        {"result2flat",          result2flat,          &par.result2flat,          COMMAND_FORMAT_CONVERSION,
                "Create a FASTA-like flat file from prefilter DB, alignment DB, or cluster DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:fastaDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"fastaDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::flatfile}}},
// Taxonomy
        {"taxonomy",             taxonomy,             &par.taxonomy,             COMMAND_TAXONOMY,
                "Compute taxonomy and lowest common ancestor for each sequence.",
                NULL,
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <o:taxaDB> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::taxSequenceDb },
                                   {"taxaDB",   DbType::ACCESS_MODE_OUTPUT,  &DbValidator::taxResult },
                                   {"tmpDir", DbType::ACCESS_MODE_INPUT,    &DbValidator::directory }}},
        {"easy-taxonomy",             easytaxonomy,             &par.easytaxonomy,             COMMAND_EASY,
                "Compute taxonomy and lowest common ancestor for each sequence. The workflow outputs a taxonomic classification for sequences and a hierarchical summery report.",
                "Classifies sequences by alignment followed by LCA (optional). It searches all sequences in the query DB against the target DB using mmseqs search, filters alignments according to the LCA mode: 2bLCA, approx. 2bLCA, whole result or top hit. An LCA is computed from the filtered alignment result.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryFastaFile[.gz]> <i:targetDB> <o:taxReports> <tmpDir>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::flatfile },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::taxSequenceDb },
                                   {"taxReports",   DbType::ACCESS_MODE_OUTPUT,  &DbValidator::flatfile },
                                   {"tmpDir", DbType::ACCESS_MODE_INPUT,    &DbValidator::directory }}},
        {"createtaxdb",          createtaxdb,          &par.onlyverbosity,         COMMAND_TAXONOMY,
                "Annotates a sequence database with NCBI taxonomy information",
                "Annotates a sequence database with NCBI taxonomy information. The program will download the Uniprot taxMappingFile and ncbi-taxdump-folder and assign taxonmical identifier to the sequence database."
                "An custom mapping from sequence to taxonomic identifier can be provided by the taxMappingFile.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> [<i:taxMappingFile> <i:ncbi-taxdump-folder>]  <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"addtaxonomy",          addtaxonomy,          &par.addtaxonomy, COMMAND_TAXONOMY,
                "Add taxonomy information to result database.",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                " <i:targetDB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"targetDB", DbType::ACCESS_MODE_INPUT, &DbValidator::taxSequenceDb },
                                   {"resultDB",   DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb },
                                   {"resultDB",   DbType::ACCESS_MODE_OUTPUT, &DbValidator::resultDb }}},
        {"lca",                  lca,                  &par.lca,                  COMMAND_TAXONOMY,
                "Compute the lowest common ancestor from a set of taxa.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:targetDB> <i:resultDB> <o:taxaDB>",
                CITATION_MMSEQS2, {{"targetDB", DbType::ACCESS_MODE_INPUT, &DbValidator::taxSequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb },
                                   {"taxDB",    DbType::ACCESS_MODE_OUTPUT, &DbValidator::taxResult }}},
        {"taxonomyreport",        taxonomyreport,  &par.threadsandcompression,   COMMAND_TAXONOMY,
                "Create Kraken-style taxonomy report.",
                NULL,
                "Florian Breitwieser <florian.bw@gmail.com>",
                "<i:targetDB> <i:taxDB> <o:taxonomyReport>",
                CITATION_MMSEQS2, {{"targetDB", DbType::ACCESS_MODE_INPUT, &DbValidator::taxSequenceDb },
                                          {"resultDB", DbType::ACCESS_MODE_INPUT, &DbValidator::taxResult },
                                          {"taxonomyReport",    DbType::ACCESS_MODE_OUTPUT, &DbValidator::flatfile }}},
        {"filtertaxdb",          filtertaxdb,          &par.filtertaxdb,          COMMAND_TAXONOMY,
                "Filter taxonomy database.",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:targetDB> <i:taxaDB> <o:taxaDB>",
                CITATION_MMSEQS2, {{"targetDB", DbType::ACCESS_MODE_INPUT, &DbValidator::taxSequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT, &DbValidator::resultDb },
                                   {"taxDB",   DbType::ACCESS_MODE_OUTPUT, &DbValidator::taxResult }}},
// multi hit search
        {"multihitdb",           multihitdb,           &par.multihitdb,           COMMAND_MULTIHIT,
                "Create sequence database and associated metadata for multi hit searches",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:fastaFile1[.gz]> ... <i:fastaFileN[.gz]> <o:setDB> <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"multihitsearch",       multihitsearch,       &par.multihitsearch,       COMMAND_MULTIHIT,
                "Search with a grouped set of sequences against another grouped set",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:querySetDB> <i:targetSetDB> <o:resultDB> <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"besthitperset",        besthitperset,        &par.besthitbyset,         COMMAND_MULTIHIT,
                "For each set of sequences compute the best element and updates the p-value",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                " <i:targetSetDB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"combinepvalperset",    combinepvalperset,    &par.combinepvalbyset,     COMMAND_MULTIHIT,
                "For each set compute the combined p-value",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:querySetDB> <i:targetSetDB> <i:resultDB> <o:pvalDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"summerizeresultsbyset",summerizeresultsbyset,&par.summerizeresultsbyset,COMMAND_MULTIHIT,
                "For each set compute summary statistics, such as spread-pvalue etc.",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:querySetDB> <i:targetSetDB> <i:resultDB> <o:setSummaryDB> <tmpDir>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"resultsbyset",         resultsbyset,         &par.summerizeresultsbyset,COMMAND_MULTIHIT,
                "For each set compute the combined p-value",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:querySetDB> <i:targetSetDB> <i:resultDB> <o:setSummaryDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"mergeresultsbyset",    mergeresultsbyset,    &par.threadsandcompression,COMMAND_MULTIHIT,
                "Merge results from multiple orfs back to their respective contig",
                NULL,
                "Clovis Norroy & Milot Mirdita <milot@mirdita.de>",
                "<i:setDB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
// Utility tools for clustering
        {"clusterupdate",        clusterupdate,        &par.clusterUpdate,        COMMAND_MAIN,
                "Update clustering of old sequence DB to clustering of new sequence DB",
                NULL,
                "Clovis Galiez & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:oldSequenceDB> <i:newSequenceDB> <i:oldClustResultDB> <o:newMappedSequenceDB> <o:newClustResultDB> <tmpDir>",
                CITATION_MMSEQS2|CITATION_MMSEQS1,{{"oldSequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                   {"newSequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                   {"oldClustResultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::clusterDb },
                                                   {"newMappedSequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb},
                                                   {"newClustResultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::clusterDb},
                                                   {"tmpDir", DbType::ACCESS_MODE_INPUT,  &DbValidator::directory}}},
        {"createseqfiledb",      createseqfiledb,      &par.createseqfiledb,      COMMAND_FORMAT_CONVERSION,
                "Create DB of unaligned FASTA files (1 per cluster) from sequence DB and cluster DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:sequenceDB> <i:clusterDB> <o:fastaDB>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"clusterDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::clusterDb },
                                   {"fastaDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::genericDb }}},
        {"mergeclusters",        mergeclusters,        &par.threadsandcompression,COMMAND_CLUSTER,
                "Merge multiple cluster DBs into single cluster DB",
                NULL,
                "Maria Hauser & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:clusterDB> <i:clusterDB1> ... <i:clusterDBn>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
// Expert tools (for advanced users)
        {"prefilter",            prefilter,            &par.prefilter,            COMMAND_EXPERT,
                "Search with query sequence / profile DB through target DB (k-mer matching + ungapped alignment)",
                "Searches with the sequences or profiles in query DB through the target sequence DB in two consecutive stages: a very fast k-mer matching stage (double matches on same diagonal) and a subsequent ungapped alignment stage. For each query a results file with sequence matches is written as entry into the prefilter DB.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> & Maria Hauser",
                "<i:queryDB> <i:targetDB> <o:prefilterDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"prefilterDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::prefilterDb }}},

        {"ungappedprefilter",    ungappedprefilter,    &par.ungappedprefilter,    COMMAND_EXPERT,
                "Search with query sequence / profile DB through target DB and compute optimal ungapped alignment score",
                "Searches with the sequences or profiles in query DB through the target sequence DB. We compute ungapped alignment score for each diagonal. For each query a results file with sequence matches is written as entry into the prefilter DB.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <o:prefilterDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"prefilterDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::prefilterDb }}},
        {"align",                align,                &par.align,                COMMAND_EXPERT,
                "Compute Smith-Waterman alignments for previous results (e.g. prefilter DB, cluster DB)",
                "Calculates Smith-Waterman alignment scores between all sequences in the query database and the sequences of the target database which passed the prefiltering.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> & Maria Hauser",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:alignmentDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"alignall",             alignall,             &par.align,                COMMAND_EXPERT,
                "Compute all against all Smith-Waterman alignments for a results (e.g. prefilter DB, cluster DB)",
                "Calculates an all against all Smith-Waterman alignment scores between all sequences in a result. It reports all hits which passed the alignment criteria.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <i:resultDB> <o:alignmentDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"transitivealign",             transitivealign,             &par.align,                COMMAND_EXPERT,
                "Transfers alignments by transitivity via a center star alignment",
                "It infers the alignment from sequence A->C via B, where B is the center sequence and A,C are aligned against B.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <i:alignmentDB> <o:alignmentDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},

        {"clust",                clust,                &par.clust,                COMMAND_EXPERT,
                "Cluster sequence DB from alignment DB (e.g. created by searching DB against itself)",
                "Computes a clustering of a sequence DB based on the alignment DB containing each query sequence or profile Smith-Waterman alignments generated by mmseqs align. (When given a prefilter DB as input, the tool will use the ungapped alignment scores for the clustering.) The tool reads the search results DB, constructs a similarity graph based on the matched sequences in alignment DB, and applies one of several clustering algorithms. The first, representative sequence of each cluster is connected by an edge to each cluster member. Its names are used as ID in the resulting cluster DB, the entries contain the names of all member sequences.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> & Lars von den Driesch & Maria Hauser",
                "<i:sequenceDB> <i:resultDB> <o:clusterDB>",
                CITATION_MMSEQS2|CITATION_MMSEQS1,{{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                                   {"clusterDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::clusterDb }}},
        {"kmermatcher",          kmermatcher,          &par.kmermatcher,          COMMAND_EXPERT,
                "Finds exact $k$-mers matches between sequences",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:sequenceDB> <o:prefilterDB>",
                CITATION_MMSEQS2,{{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"prefilterDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::prefilterDb }}},
        {"kmersearch",          kmersearch,          &par.kmersearch,          COMMAND_EXPERT,
                "Search with query sequence through target DB.  (k-mer matching) ",
                "Searches with the query sequence DB through the target sequence DB. Results are target centric target -> query.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:queryDB> <i:kmerIndexDB> <o:prefilterDB>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::indexDb },
                                   {"prefilterDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::prefilterDb }}},
        {"kmerindexdb",          kmerindexdb,          &par.kmerindexdb,          COMMAND_EXPERT,
                "Finds exact $k$-mers matches between sequences and stores them as index",
                "Precomputes an index table for the sequence DB. Handing over the precomputed index table as input to mmseqs linsearch.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:sequenceDB> <o:kmerIndexDB>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"kmerIndexDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::indexDb }}},
        {"clusthash",            clusthash,            &par.clusthash,            COMMAND_EXPERT,
                "Cluster sequences of same length and >90% sequence identity *in linear time*",
                "Detects redundant sequences based on reduced alphabet hashing and hamming distance.",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:sequenceDB> <o:alignmentDB>",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
// Utility tools to manipulate DBs
        {"compress",             compress,             &par.onlythreads,          COMMAND_DB,
                "Compresses a database.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:DB> <o:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::allDb }}},
        {"decompress",           decompress,           &par.onlythreads,          COMMAND_DB,
                "Decompresses a database.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:DB> <o:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::allDb }}},
        {"apply",                apply,                &par.threadsandcompression,COMMAND_DB,
                "Passes each input database entry to stdin of the specified program, executes it and writes its stdout to the output database.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:DB> <o:DB> -- program [args...]",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::allDb }}},
        {"extractorfs",          extractorfs,          &par.extractorfs,          COMMAND_DB,
                "Extract open reading frames from all six frames from nucleotide sequence DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2,  {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                    {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"extractframes",        extractframes,        &par.extractframes,        COMMAND_DB,
                "Extract frames reading frames from a nucleotide sequence DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"orftocontig",          orftocontig,          &par.orftocontig,          COMMAND_DB,
                "Obtain location information of extracted orfs with respect to their contigs in alignment format",
                "Parses extracted orfs headers to compute their coordinates on the contig and writes the results in alignment format",
                "Eli Levy Karin <eli.levy.karin@gmail.com> ",
                "<i:contigsSequenceDB> <i:extractedOrfsHeadersDB> <o:orfsAlignedToContigDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"reverseseq",          reverseseq,          &par.reverseseq,          COMMAND_DB,
                "Reverse each sequence in a DB",
                "Reversed sequences can be used for the assessment of summary statistics computed for the input sequences",
                "Eli Levy Karin <eli.levy.karin@gmail.com> ",
                "<i:sequenceDB> <o:revSequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"touchdb",              touchdb,              &par.onlythreads,          COMMAND_DB,
                "Memory map database",
                "Touch every system page of a database",
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb }}},
        {"translatenucs",        translatenucs,        &par.translatenucs,        COMMAND_DB,
                "Translate nucleotide sequence DB into protein sequence DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::nuclDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::aaDb }}},
        {"translateaa",          translateaa,          &par.threadsandcompression,COMMAND_DB,
                "Translate protein sequence into nucleotide sequence DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::aaDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::nuclDb }}},
        {"swapresults",          swapresults,          &par.swapresult,           COMMAND_DB,
                "Reformat prefilter or alignment DB as if target DB had been searched through query DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>, Clovis Galiez & Eli Levy Karin",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::prefAlnResDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::prefAlnResDb }}},
        {"swapdb",               swapdb,               &par.swapdb,               COMMAND_DB,
                "Create a DB where the key is from the first column of the input result DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>, Clovis Galiez & Eli Levy Karin",
                "<i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"resultDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"mergedbs",             mergedbs,             &par.mergedbs,             COMMAND_DB,
                "Merge multiple DBs into a single DB, based on IDs (names) of entries",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:resultDB> <i:resultDB1> ... <i:resultDBn>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb }}},
        {"splitdb",              splitdb,              &par.splitdb,              COMMAND_DB,
                "Split a mmseqs DB into multiple DBs",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:sequenceDB> <o:sequenceDB_1..N>",
                CITATION_MMSEQS2,{{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"splitsequence",       splitsequence,         &par.splitsequence,        COMMAND_DB,
                "Split sequences by length",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"subtractdbs",          subtractdbs,          &par.subtractdbs,          COMMAND_DB,
                "Generate a DB with entries of first DB not occurring in second DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:resultDBLeft> <i:resultDBRight> <o:resultDB>",
                CITATION_MMSEQS2, {{"resultDBLeft", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDBRight", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"filterdb",             filterdb,             &par.filterDb,             COMMAND_DB,
                "Filter a DB by conditioning (regex, numerical, ...) on one of its whitespace-separated columns",
                NULL,
                "Clovis Galiez & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::resultDb }}},
        {"createsubdb",          createsubdb,          &par.onlyverbosity,        COMMAND_DB,
                "Create a subset of a DB from a file of IDs of entries",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:subsetFile or DB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"subsetFile", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDbAndFlat },
                                    {"resultDB", DbType::ACCESS_MODE_INPUT, &DbValidator::allDb },
                                    {"resultDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::allDb }}},
        {"view",          view,          &par.view,        COMMAND_DB,
                "Prints entries to console",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT, &DbValidator::allDb }}},
        {"rmdb",          rmdb,          &par.onlyverbosity,        COMMAND_DB,
                "Removes the database",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT, &DbValidator::allDb }}},
        {"mvdb",          mvdb,          &par.onlyverbosity,        COMMAND_DB,
                "Move the database",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:srcDB> <o:dstDB>",
                    CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT, &DbValidator::allDb },
                                       {"DB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::allDb }}},
        {"result2profile",       result2profile,       &par.result2profile,       COMMAND_DB,
                "Compute profile and consensus DB from a prefilter, alignment or cluster DB",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:profileDB>",
                CITATION_MMSEQS2,{{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                  {"profileDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::profileDb }}},
        {"result2pp",            result2pp,            &par.result2pp,            COMMAND_DB,
                "Merge the query profiles with target profiles according to search results and outputs an enriched profile DB",
                NULL,
                "Clovis Galiez & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:profileDB>",
                CITATION_MMSEQS2,{{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::profileDb },
                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::profileDb },
                                  {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                  {"profileDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::profileDb }}},
        {"result2rbh",           result2rbh,           &par.threadsandcompression,COMMAND_DB,
                "Filter a merged result DB to retain only reciprocal best hits",
                NULL,
                "Eli Levy Karin",
                "<i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"result2msa",           result2msa,           &par.result2msa,           COMMAND_DB,
                "Generate MSAs for queries by locally aligning their matched targets in prefilter/alignment/cluster DB",
                NULL,
                "Martin Steinegger (martin.steinegger@mpibpc.mpg.de) & Milot Mirdita <milot@mirdita.de> & Clovis Galiez",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:msaDB>",
                CITATION_MMSEQS2,{{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                  {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                  {"msaDB",    DbType::ACCESS_MODE_OUTPUT, &DbValidator::msaDb }}},
        {"convertmsa",           convertmsa,           &par.convertmsa,           COMMAND_DB,
                "Turns an MSA file into an MSA database.",
                "Builds an MSA database out of an MSA file in either Stockholm or PFAM format.",
                "Milot Mirdita <milot@mirdita.de>",
                "<i:msaFile[.gz]> <o:msaDB>",
                CITATION_SERVER |CITATION_MMSEQS2, {{"msaFile[.gz]", DbType::ACCESS_MODE_INPUT, &DbValidator::flatfile },
                                                    {"msaDB",DbType::ACCESS_MODE_OUTPUT, &DbValidator::msaDb }}},
        {"msa2profile",          msa2profile,          &par.msa2profile,          COMMAND_DB,
                "Turns an MSA database into a MMseqs profile database.",
                "Builds a profile database from a database containing MSAs. The first sequence in the MSA is chosen as the query sequence. Gap columns (insertions) are discarded.",
                "Milot Mirdita <milot@mirdita.de>",
                "<i:msaDB> <o:profileDB>",
                CITATION_SERVER |CITATION_MMSEQS2, {{"msaDB",DbType::ACCESS_MODE_INPUT, &DbValidator::msaDb },
                                                    {"profileDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::profileDb }}},
        {"profile2pssm",         profile2pssm,         &par.profile2pssm,         COMMAND_DB,
                "Converts a profile database into a human readable tab-separated PSSM file.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:profileDB> <o:pssmFile>",
                CITATION_MMSEQS2, {{"profileDB",DbType::ACCESS_MODE_INPUT, &DbValidator::profileDb },
                                   {"pssmFile", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::genericDb }}},
        {"profile2cs",         profile2cs,             &par.profile2cs,           COMMAND_DB,
                "Converts a profile database into a column state sequence.",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:profileDB> <o:csDB>",
                CITATION_MMSEQS2, {{"profileDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::profileDb },
                                   {"csDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::csDb }}},
        {"result2stats",         result2stats,         &par.result2stats,         COMMAND_DB,
                "Compute statistics for each entry in a sequence, prefilter, alignment or cluster DB",
                NULL,
                "Clovis Galiez & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:statsDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::prefAlnResDb },
                                   {"statsDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::genericDb }}},
        {"offsetalignment",      offsetalignment,      &par.offsetalignment,      COMMAND_HIDDEN,
                "Offset alignment by orf start position.",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:queryOrfDB> <i:targetDB> <i:targetOrfDB> <i:alnDB> <o:alnDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"queryOrfDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetOrfDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alnDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                   {"alnDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"proteinaln2nucl",      proteinaln2nucl,      &par.threadsandcompression,COMMAND_DB,
                "Map protein alignment to nucleotide alignment",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de> ",
                "<i:queryDB> <i:targetDB> <i:alnDB> <o:alnDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alnDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                   {"alnDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"tsv2db",               tsv2db,               &par.tsv2db,               COMMAND_DB,
                "Turns a TSV file into a MMseqs database",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:tsvFile> <o:resultDB>",
                CITATION_MMSEQS2,  {{"tsvFile",  DbType::ACCESS_MODE_INPUT,  &DbValidator::flatfile },
                                    {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"result2repseq",       result2repseq,         &par.threadsandcompression,COMMAND_DB,
                "Get representative sequences for a result database",
                NULL,
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <i:resultDB> <o:sequenceDb>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"sequenceDb", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
// Special-purpose utilities
        {"rescorediagonal",     rescorediagonal,       &par.rescorediagonal,      COMMAND_SPECIAL,
                "Compute sequence identity for diagonal",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:prefilterDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"alignbykmer",         alignbykmer,           &par.alignbykmer,          COMMAND_SPECIAL,
                "Predict sequence identity, score, alignment start and end by kmer alignment",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"diffseqdbs",           diffseqdbs,           &par.diff,                 COMMAND_SPECIAL,
                "Find IDs of sequences kept, added and removed between two versions of sequence DB",
                "It creates 3 filtering files, that can be used in conjunction with \"createsubdb\" tool.\nThe first file contains the keys that has been removed from DBold to DBnew.\nThe second file maps the keys of the kept sequences from DBold to DBnew.\nThe third file contains the keys of the sequences that have been added in DBnew.",
                "Clovis Galiez & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:oldSequenceDB> <i:newSequenceDB> <o:rmSeqKeysFile> <o:keptSeqKeysFile> <o:newSeqKeysFile>",
                CITATION_MMSEQS2, {{"oldSequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"newSequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"rmSeqKeysFile", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::flatfile },
                                   {"keptSeqKeysFile", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::flatfile },
                                   {"newSeqKeysFile", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::flatfile }}},
        {"concatdbs",            concatdbs,            &par.concatdbs,            COMMAND_SPECIAL,
                "Concatenate two DBs, giving new IDs to entries from second input DB",
                NULL,
                "Clovis Galiez & Martin Steinegger (martin.steinegger@mpibpc.mpg.de)",
                "<i:DB> <i:DB> <o:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::allDb }}},
        {"sortresult",           sortresult,           &par.sortresult,           COMMAND_SPECIAL,
                "Sort a result database in the same order as prefilter or align would.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:resultbDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"summarizealis",      summarizealis,      &par.threadsandcompression,      COMMAND_SPECIAL,
                "Summarize alignment results into a single show uniq. coverage, coverage and avg. sequence identity",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:alignmentDB> <o:summerizedDB>",
                CITATION_MMSEQS2|CITATION_UNICLUST, {{"alignmentDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                                            {"summerizedDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::genericDb }}},
        {"summarizeresult",      summarizeresult,      &par.summarizeresult,      COMMAND_SPECIAL,
                "Extract annotations from alignment DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:alignmentDB> <o:alignmentDB>",
                CITATION_MMSEQS2|CITATION_UNICLUST, {{"alignmentDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                                     {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"summarizetabs",        summarizetabs,        &par.summarizetabs,        COMMAND_SPECIAL,
                "Extract annotations from HHblits BAST-tab-formatted results",
                NULL,
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:blastTabDB> <i:lengthFile> <o:summarizedBlastTabDB>",
                CITATION_MMSEQS2|CITATION_UNICLUST,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"gff2db",               gff2db,               &par.gff2ffindex,          COMMAND_SPECIAL,
                "Turn a gff3 (generic feature format) file into a gff3 DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:gff3File> <i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"gff3File", DbType::ACCESS_MODE_INPUT,  &DbValidator::flatfile },
                                   {"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"masksequence",            masksequence,            &par.threadsandcompression,          COMMAND_SPECIAL,
                "Soft mask sequences using tantan, low. complex regions in lower case the rest upper ",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"maskbygff",            maskbygff,            &par.gff2ffindex,          COMMAND_SPECIAL,
                "X out sequence regions in a sequence DB by features in a gff3 file",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:gff3File> <i:sequenceDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"gff3File", DbType::ACCESS_MODE_INPUT,  &DbValidator::flatfile },
                                   {"sequenceDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"sequenceDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::sequenceDb }}},
        {"prefixid",             prefixid,             &par.prefixid,             COMMAND_SPECIAL,
                "For each entry in a DB prepend the entry ID to the entry itself",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:DB> <o:DB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::allDb }}},
        {"suffixid",             suffixid,             &par.prefixid,             COMMAND_SPECIAL,
                "For each entry in a DB append the entry ID to the entry itself",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:resultDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"DB", DbType::ACCESS_MODE_INPUT,  &DbValidator::allDb },
                                   {"DB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::allDb }}},
        {"convertkb",            convertkb,            &par.convertkb,            COMMAND_SPECIAL,
                "Convert UniProt knowledge base files into MMseqs2 database format for the selected column types",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:uniprotkb.dat[.gz]> ... <i:uniprotkb.dat[.gz]> <o:uniprotkbDB>",
                CITATION_MMSEQS2,{{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"summarizeheaders",     summarizeheaders,     &par.summarizeheaders,     COMMAND_SPECIAL,
                "Return a new summarized header DB from the UniProt headers of a cluster DB",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:queryDB> <i:targetDB> <i:clusterDB> <o:headerDB>",
                CITATION_MMSEQS2|CITATION_UNICLUST, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                     {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                                     {"clusterDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                                     {"headerDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::genericDb}}},
        {"extractalignedregion", extractalignedregion, &par.extractalignedregion, COMMAND_SPECIAL,
                "Extract aligned sequence region from query",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <o:sequenceDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                   {"queryDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::genericDb}}},
        {"extractdomains",       extractdomains,       &par.extractdomains,       COMMAND_SPECIAL,
                "Extract highest scoring alignment region for each sequence from BLAST-tab file",
                NULL,
                "Milot Mirdita <milot@mirdita.de> & Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:alignmentDb> <i:msaDB> <o:domainDB>",
                CITATION_MMSEQS2|CITATION_UNICLUST, {{"alignmentDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::alignmentDb },
                                                     {"msaDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::msaDb },
                                                     {"domainDB", DbType::ACCESS_MODE_OUTPUT, &DbValidator::genericDb}}},
        {"convertca3m",          convertca3m,          &par.threadsandcompression,COMMAND_SPECIAL,
                "Converts a cA3M database into a MMseqs2 result database.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:ca3mDB> <o:resultDB>",
                CITATION_MMSEQS2, {{"ca3mDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::ca3mDb },
                                   {"resultDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::resultDb }}},
        {"expandaln",           expandaln,             &par.expandaln,            COMMAND_SPECIAL,
                "Expands an alignment result based on another.",
                NULL,
                "Milot Mirdita <milot@mirdita.de>",
                "<i:queryDB> <i:targetDB> <i:resultDB> <i:resultDB|ca3mDB> <o:alignmentDB>",
                CITATION_MMSEQS2, {{"queryDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"targetDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"resultDB", DbType::ACCESS_MODE_INPUT,  &DbValidator::resultDb },
                                   {"alignmentDB", DbType::ACCESS_MODE_OUTPUT,  &DbValidator::alignmentDb }}},
        {"countkmer",           countkmer,             &par.countkmer,            COMMAND_SPECIAL,
                "Simple kmer counter, it prints the numeric, alphanumeric representation and kmercount",
                NULL,
                "Martin Steinegger <martin.steinegger@mpibpc.mpg.de>",
                "<i:sequenceDB> ",
                CITATION_MMSEQS2, {{"sequenceDB",  DbType::ACCESS_MODE_INPUT,  &DbValidator::sequenceDb }}},
        {"dbtype",              dbtype,                &par.empty,                COMMAND_HIDDEN,
                "",
                NULL,
                "",
                "",
                CITATION_MMSEQS2, {{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"version",              versionstring,        &par.empty,                COMMAND_HIDDEN,
                "",
                NULL,
                "",
                "",
                CITATION_MMSEQS2, {{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"diskspaceavail",       diskspaceavail,       &par.empty,                COMMAND_HIDDEN,
                "",
                NULL,
                "",
                "",
                CITATION_MMSEQS2, {{"",DbType::ACCESS_MODE_INPUT, NULL}}},
        {"shellcompletion",      shellcompletion,      &par.empty,                COMMAND_HIDDEN,
                "",
                NULL,
                "",
                "",
                CITATION_MMSEQS2, {{"",DbType::ACCESS_MODE_INPUT, NULL}}}
};
