#include <benchmark/benchmark.h>
#include <string.h>

#include "benchmarks/c/common/common.h"
#include "benchmarks/c/common/strings_atom.h"
#include "benchmarks/c/common/strings_date.h"
#include "benchmarks/c/common/strings_http.h"
#include "benchmarks/c/common/strings_ipv4.h"
#include "benchmarks/c/common/strings_ipv6.h"
#include "benchmarks/c/common/strings_uri.h"
#include "lib/regex.h"


int main(int argc, char** argv)
{
    static const size_t MAX_TITLE = 1024;
    char title[MAX_TITLE];

    static const size_t AAA_LEN = 1 << 13;
    char *aaa = new char[AAA_LEN + 1];
    memset(aaa, 'a', AAA_LEN);
    const char *aaa_strings[] = {aaa, NULL};

    const std::vector<bench_t> benches = {
        {"HTTP-RFC7230",   MESSAGE_HEAD,  http_strings},
        {"HTTP-simple",    MESSAGE_HEAD2, http_strings},

        {"URI-RFC3986",    URI,           uri_strings},
        {"URI-simple",     URI2,          uri_strings},

        {"IPv6",           IPV6,          ipv6_strings},
        {"IPv6-simple",    IPV62,         ipv6_strings},
        {"IPv4",           IPV4,          ipv4_strings},
        {"IPv4-simple",    IPV42,         ipv4_strings},
        {"date",           DATE,          date_strings},
        {"date-simple",    DATE2,         date_strings},
        {"package",        PACKAGE_ATOM,  atom_strings},
        {"package-simple", PACKAGE_ATOM2, atom_strings},

        {NULL, "(a{2}|a{3}|a{5})*",       aaa_strings},
        {NULL, "(a{7}|a{13}|a{19})*",     aaa_strings},
        {NULL, "(a{29}|a{41}|a{53})*",    aaa_strings},
        {NULL, "(a{67}|a{83}|a{103})*",   aaa_strings},
        {NULL, "(a{127}|a{151}|a{179})*", aaa_strings},
        {NULL, "(a{199}|a{239}|a{271})*", aaa_strings},

        {NULL, "(((a){2})|((a){3})|((a){5}))*",       aaa_strings},
        {NULL, "(((a){7})|((a){13})|((a){19}))*",     aaa_strings},
        {NULL, "(((a){29})|((a){41})|((a){53}))*",    aaa_strings},
        {NULL, "(((a){67})|((a){83})|((a){103}))*",   aaa_strings},
        {NULL, "(((a){127})|((a){151})|((a){179}))*", aaa_strings},
        {NULL, "(((a){199})|((a){239})|((a){271}))*", aaa_strings},

        {NULL, "(a{0,16})*",    aaa_strings},
        {NULL, "((a){0,16})*",  aaa_strings},
        {NULL, "(a{0,64})*",    aaa_strings},
        {NULL, "((a){0,64})*",  aaa_strings},
        {NULL, "(a{0,256})*",   aaa_strings},
        {NULL, "((a){0,256})*", aaa_strings},

        // Pathological case for constant-memory POSIX algorithms that use naive
        // (worst-case cubic in the size of TNFA) algorithm for precedence matrix
        // computation. Cubic behaviour is caused by quadratic-time computation
        // of precedence matrix on each step (the number of core items in closure
        // approaches TNFA size), multiplied by the length of compared histores
        // (which also approaches TNFA size). Trie-based algorithms are not
        // affected, but they consume memory proportional to the length of input,
        // and so are also not practical.

        {NULL, "((a?){0,16})*",  aaa_strings},
        {NULL, "((a*){0,16})*",  aaa_strings},
        {NULL, "((a?){0,64})*",  aaa_strings},
        {NULL, "((a*){0,64})*",  aaa_strings},
        {NULL, "((a?){0,256})*", aaa_strings},
        {NULL, "((a*){0,256})*", aaa_strings},
    };

    const std::vector<alg_t> algs = {
        {"LG",        ENGINE_RE2C, REG_NFA | REG_LEFTMOST},
        {"lazy-LG",   ENGINE_RE2C, REG_NFA | REG_LEFTMOST | REG_TRIE},
        {"OS",        ENGINE_RE2C, REG_NFA},
        {"simple-OS", ENGINE_RE2C, REG_NFA | REG_SLOWPREC},
        {"lazy-OS",   ENGINE_RE2C, REG_NFA | REG_TRIE},
#ifdef HAVE_RE2_RE2_H
        {"re2-LG",    ENGINE_RE2,  0},
#endif
    };

    benchmark::Initialize(&argc, argv);

    for (const bench_t &bench : benches) {
        for (const alg_t &alg : algs) {
            const char *name = bench.name ? bench.name : bench.regexp;
            snprintf(title, MAX_TITLE, "%s_%s", name, alg.name);
            benchmark::RegisterBenchmark(title, bench_regexec_t(), alg, bench)
                ->Unit(benchmark::kMicrosecond);
        }
    }

    benchmark::RunSpecifiedBenchmarks();

    delete[] aaa;
}
