// ======================================================================
// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==
// ======================================================================
#pragma once

#define ZEROERR_VERSION_MAJOR 0
#define ZEROERR_VERSION_MINOR 3
#define ZEROERR_VERSION_PATCH 0
#define ZEROERR_VERSION \
    (ZEROERR_VERSION_MAJOR * 10000 + ZEROERR_VERSION_MINOR * 100 + ZEROERR_VERSION_PATCH)

#define ZEROERR_STR(x) #x

#define ZEROERR_VERSION_STR_BUILDER(a, b, c) ZEROERR_STR(a) "." ZEROERR_STR(b) "." ZEROERR_STR(c)
#define ZEROERR_VERSION_STR \
    ZEROERR_VERSION_STR_BUILDER(ZEROERR_VERSION_MAJOR, ZEROERR_VERSION_MINOR, ZEROERR_VERSION_PATCH)

// If you just wish to use the color without dynamic
// enable or disable it, you can uncomment the following line
// #define ZEROERR_ALWAYS_COLORFUL
// #define ZEROERR_DISABLE_COLORFUL

// If you wish to use the whole library without thread safety, uncomment the following line
// #define ZEROERR_NO_THREAD_SAFE

// If you wish to disable auto initialization of the system
// #define ZEROERR_DISABLE_AUTO_INIT

// If you didn't wish override operator<< for ostream, we can disable it
// #define ZEROERR_DISABLE_OSTREAM_OVERRIDE

// If you wish to disable AND, OR macro
// #define ZEROERR_DISABLE_COMPLEX_AND_OR

// If you wish ot disable BDD style macros
// #define ZEROERR_DISABLE_BDD

// Detect C++ standard with a cross-platform way

#ifdef _MSC_VER
#define ZEROERR_CPLUSPLUS _MSVC_LANG
#else
#define ZEROERR_CPLUSPLUS __cplusplus
#endif

#if ZEROERR_CPLUSPLUS >= 202300L
#define ZEROERR_CXX_STANDARD 23
#elif ZEROERR_CPLUSPLUS >= 202002L
#define ZEROERR_CXX_STANDARD 20
#elif ZEROERR_CPLUSPLUS >= 201703L
#define ZEROERR_CXX_STANDARD 17
#elif ZEROERR_CPLUSPLUS >= 201402L
#define ZEROERR_CXX_STANDARD 14
#elif ZEROERR_CPLUSPLUS >= 201103L
#define ZEROERR_CXX_STANDARD 11
#else
#error "Unsupported C++ standard detected. ZeroErr requires C++11 or later."
#endif

#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#define ZEROERR_OS_UNIX
#if defined(__linux__)
#define ZEROERR_OS_LINUX
#endif
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
#define ZEROERR_OS_WINDOWS
#else
#define ZEROERR_OS_UNKNOWN
#endif


#if defined(NDEBUG) && !defined(ZEROERR_ALWAYS_ASSERT)
// FIXME: we should safely remove the assert in IF statement
// #define ZEROERR_NO_ASSERT
#endif

// This is used for generating a unique name based on the file name and line number
#define ZEROERR_CAT_IMPL(s1, s2) s1##s2
#define ZEROERR_CAT(x, s)        ZEROERR_CAT_IMPL(x, s)

// The following macros are used to check the arguments is empty or not
// from: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define ZEROERR_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define ZEROERR_HAS_COMMA(...) \
    ZEROERR_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define ZEROERR_TRIGGER_PARENTHESIS_(...) ,

#define ZEROERR_ISEMPTY(...)                                                                     \
    _ZEROERR_ISEMPTY(/* test if there is just one argument, eventually an empty                  \
                one */                                                                           \
                     ZEROERR_HAS_COMMA(__VA_ARGS__), /* test if ZEROERR_TRIGGER_PARENTHESIS_     \
                                                together with the argument adds a comma */       \
                     ZEROERR_HAS_COMMA(ZEROERR_TRIGGER_PARENTHESIS_                              \
                                           __VA_ARGS__), /* test if the argument together with   \
                                             a parenthesis adds a comma */                       \
                     ZEROERR_HAS_COMMA(__VA_ARGS__(                                              \
                         /*empty*/)), /* test if placing it between ZEROERR_TRIGGER_PARENTHESIS_ \
                                         and the parenthesis adds a comma */                     \
                     ZEROERR_HAS_COMMA(ZEROERR_TRIGGER_PARENTHESIS_ __VA_ARGS__(/*empty*/)))

#define ZEROERR_PASTE5(_0, _1, _2, _3, _4) _0##_1##_2##_3##_4
#define _ZEROERR_ISEMPTY(_0, _1, _2, _3) \
    ZEROERR_HAS_COMMA(ZEROERR_PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,


// The counter is used to generate a unique name
#ifdef __COUNTER__
#define ZEROERR_NAMEGEN(x) ZEROERR_CAT(x, __COUNTER__)
#else  // __COUNTER__
#define ZEROERR_NAMEGEN(x) ZEROERR_CAT(x, __LINE__)
#endif  // __COUNTER__

#ifdef ZEROERR_OS_LINUX
#define ZEROERR_PERF
#endif

#ifdef ZEROERR_DISABLE_ASSERTS_RETURN_VALUES
#define ZEROERR_FUNC_SCOPE_BEGIN  do
#define ZEROERR_FUNC_SCOPE_END    while (0)
#define ZEROERR_FUNC_SCOPE_RET(v) (void)0
#else
#define ZEROERR_FUNC_SCOPE_BEGIN  [&]
#define ZEROERR_FUNC_SCOPE_END    ()
#define ZEROERR_FUNC_SCOPE_RET(v) return v
#endif

#ifndef ZEROERR_NO_SHORT_LOG_MACRO
#define ZEROERR_USE_SHORT_LOG_MACRO
#endif

#define ZEROERR_EXPAND(x) x


// =================================================================================================
// == COMPILER Detector ============================================================================
// =================================================================================================

#define ZEROERR_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR) * 10000000 + (MINOR) * 100000 + (PATCH))

// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
#if defined(_MSC_VER) && defined(_MSC_FULL_VER)
#if _MSC_VER == _MSC_FULL_VER / 10000
#define ZEROERR_MSVC ZEROERR_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000)
#else  // MSVC
#define ZEROERR_MSVC \
    ZEROERR_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
#endif  // MSVC
#endif  // MSVC
#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
#define ZEROERR_CLANG ZEROERR_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
    !defined(__INTEL_COMPILER)
#define ZEROERR_GCC ZEROERR_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif  // GCC
#if defined(__INTEL_COMPILER)
#define ZEROERR_ICC ZEROERR_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
#endif  // ICC

#ifndef ZEROERR_MSVC
#define ZEROERR_MSVC 0
#endif  // ZEROERR_MSVC
#ifndef ZEROERR_CLANG
#define ZEROERR_CLANG 0
#endif  // ZEROERR_CLANG
#ifndef ZEROERR_GCC
#define ZEROERR_GCC 0
#endif  // ZEROERR_GCC
#ifndef ZEROERR_ICC
#define ZEROERR_ICC 0
#endif  // ZEROERR_ICC


// =================================================================================================
// == COMPILER WARNINGS HELPERS ====================================================================
// =================================================================================================

#if ZEROERR_CLANG && !ZEROERR_ICC
#define ZEROERR_PRAGMA_TO_STR(x)            _Pragma(#x)
#define ZEROERR_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
#define ZEROERR_CLANG_SUPPRESS_WARNING(w)   ZEROERR_PRAGMA_TO_STR(clang diagnostic ignored w)
#define ZEROERR_CLANG_SUPPRESS_WARNING_POP  _Pragma("clang diagnostic pop")
#define ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \
    ZEROERR_CLANG_SUPPRESS_WARNING_PUSH ZEROERR_CLANG_SUPPRESS_WARNING(w)
#else  // ZEROERR_CLANG
#define ZEROERR_CLANG_SUPPRESS_WARNING_PUSH
#define ZEROERR_CLANG_SUPPRESS_WARNING(w)
#define ZEROERR_CLANG_SUPPRESS_WARNING_POP
#define ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH(w)
#endif  // ZEROERR_CLANG

#if ZEROERR_GCC
#define ZEROERR_PRAGMA_TO_STR(x)          _Pragma(#x)
#define ZEROERR_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push")
#define ZEROERR_GCC_SUPPRESS_WARNING(w)   ZEROERR_PRAGMA_TO_STR(GCC diagnostic ignored w)
#define ZEROERR_GCC_SUPPRESS_WARNING_POP  _Pragma("GCC diagnostic pop")
#define ZEROERR_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \
    ZEROERR_GCC_SUPPRESS_WARNING_PUSH ZEROERR_GCC_SUPPRESS_WARNING(w)
#else  // ZEROERR_GCC
#define ZEROERR_GCC_SUPPRESS_WARNING_PUSH
#define ZEROERR_GCC_SUPPRESS_WARNING(w)
#define ZEROERR_GCC_SUPPRESS_WARNING_POP
#define ZEROERR_GCC_SUPPRESS_WARNING_WITH_PUSH(w)
#endif  // ZEROERR_GCC

#if ZEROERR_MSVC
#define ZEROERR_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push))
#define ZEROERR_MSVC_SUPPRESS_WARNING(w)   __pragma(warning(disable : w))
#define ZEROERR_MSVC_SUPPRESS_WARNING_POP  __pragma(warning(pop))
#define ZEROERR_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \
    ZEROERR_MSVC_SUPPRESS_WARNING_PUSH ZEROERR_MSVC_SUPPRESS_WARNING(w)
#else  // ZEROERR_MSVC
#define ZEROERR_MSVC_SUPPRESS_WARNING_PUSH
#define ZEROERR_MSVC_SUPPRESS_WARNING(w)
#define ZEROERR_MSVC_SUPPRESS_WARNING_POP
#define ZEROERR_MSVC_SUPPRESS_WARNING_WITH_PUSH(w)
#endif  // ZEROERR_MSVC

// =================================================================================================
// == COMPILER WARNINGS ============================================================================
// =================================================================================================

// both the header and the implementation suppress all of these,
// so it only makes sense to aggregate them like so
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH                                                      \
    ZEROERR_CLANG_SUPPRESS_WARNING_PUSH                                                            \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")                                            \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wweak-vtables")                                               \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wpadded")                                                     \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")                                         \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wc++98-compat")                                               \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")                                      \
                                                                                                   \
    ZEROERR_GCC_SUPPRESS_WARNING_PUSH                                                              \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")                                              \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wpragmas")                                                      \
    ZEROERR_GCC_SUPPRESS_WARNING("-Weffc++")                                                       \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wstrict-overflow")                                              \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")                                              \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wmissing-declarations")                                         \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wuseless-cast")                                                 \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wnoexcept")                                                     \
                                                                                                   \
    ZEROERR_MSVC_SUPPRESS_WARNING_PUSH                                                             \
    /* these 4 also disabled globally via cmake: */                                                \
    ZEROERR_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */        \
    ZEROERR_MSVC_SUPPRESS_WARNING(4571) /* SEH related */                                          \
    ZEROERR_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */                                 \
    ZEROERR_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/                \
    /* common ones */                                                                              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */                             \
    ZEROERR_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */                             \
    ZEROERR_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */    \
    ZEROERR_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */             \
    ZEROERR_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
    ZEROERR_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(4820) /* padding */                                              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */           \
    ZEROERR_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */          \
    ZEROERR_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */  \
    ZEROERR_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */        \
    /* static analysis */                                                                          \
    ZEROERR_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */       \
    ZEROERR_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */                 \
    ZEROERR_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */                             \
    ZEROERR_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */  \
    ZEROERR_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */

#define ZEROERR_SUPPRESS_COMMON_WARNINGS_POP \
    ZEROERR_CLANG_SUPPRESS_WARNING_POP       \
    ZEROERR_GCC_SUPPRESS_WARNING_POP         \
    ZEROERR_MSVC_SUPPRESS_WARNING_POP


#define ZEROERR_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN                                 \
    ZEROERR_MSVC_SUPPRESS_WARNING_PUSH                                                             \
    ZEROERR_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */       \
    ZEROERR_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */     \
    ZEROERR_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */      \
    ZEROERR_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */                \
    ZEROERR_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */                  \
    ZEROERR_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */                             \
    ZEROERR_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(4820) /* padding */                                              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */           \
    ZEROERR_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */          \
    ZEROERR_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */              \
    ZEROERR_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */           \
    ZEROERR_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
    ZEROERR_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */     \
    ZEROERR_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */  \
    ZEROERR_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */

#define ZEROERR_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END ZEROERR_MSVC_SUPPRESS_WARNING_POP

#define ZEROERR_SUPPRESS_VARIADIC_MACRO \
    ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wgnu-zero-variadic-macro-arguments")

#define ZEROERR_SUPPRESS_VARIADIC_MACRO_POP ZEROERR_CLANG_SUPPRESS_WARNING_POP

#define ZEROERR_SUPPRESS_COMPARE                                          \
    ZEROERR_CLANG_SUPPRESS_WARNING_PUSH                                   \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wsign-conversion")                   \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wsign-compare")                      \
    ZEROERR_CLANG_SUPPRESS_WARNING("-Wgnu-zero-variadic-macro-arguments") \
    ZEROERR_GCC_SUPPRESS_WARNING_PUSH                                     \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wsign-conversion")                     \
    ZEROERR_GCC_SUPPRESS_WARNING("-Wsign-compare")                        \
    ZEROERR_MSVC_SUPPRESS_WARNING_PUSH                                    \
    ZEROERR_MSVC_SUPPRESS_WARNING(4388)                                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(4389)                                   \
    ZEROERR_MSVC_SUPPRESS_WARNING(4018)

#define ZEROERR_SUPPRESS_COMPARE_POP                                    \
    ZEROERR_CLANG_SUPPRESS_WARNING_POP ZEROERR_GCC_SUPPRESS_WARNING_POP \
        ZEROERR_MSVC_SUPPRESS_WARNING_POP

/**
 * Macro to suppress unused variable/parameter warnings
 *
 * This macro can be used to mark variables or parameters as intentionally unused
 * while maintaining cross-compiler compatibility. It handles different compiler-specific
 * attributes and warning suppressions:
 *
 * - For Clang/GCC: Uses __attribute__((unused))
 * - For LCLINT: Uses @unused@ comment annotation
 * - For MSVC: Suppresses warning C4100 (unreferenced formal parameter)
 * - For other compilers: No special handling
 *
 * Usage example:
 *   void foo(ZEROERR_UNUSED(int x)) {
 *     // x is marked as intentionally unused
 *   }
 */
#if ZEROERR_CLANG || ZEROERR_GCC
#define ZEROERR_UNUSED(x) x __attribute__((unused))
#elif defined(__LCLINT__)
#define ZEROERR_UNUSED(x) /*@unused@*/ x
#elif ZEROERR_MSVC
#define ZEROERR_UNUSED(x) \
    ZEROERR_MSVC_SUPPRESS_WARNING_WITH_PUSH(4100) x ZEROERR_MSVC_SUPPRESS_WARNING_POP
#else
#define ZEROERR_UNUSED(x) x
#endif

#pragma once



namespace zeroerr {

#ifdef ZEROERR_ALWAYS_COLORFUL
constexpr const char* Reset      = "\x1b[0m";
constexpr const char* Bright     = "\x1b[1m";
constexpr const char* Dim        = "\x1b[2m";
constexpr const char* Underscore = "\x1b[4m";
constexpr const char* Blink      = "\x1b[5m";
constexpr const char* Reverse    = "\x1b[7m";
constexpr const char* Hidden     = "\x1b[8m";

constexpr const char* FgBlack   = "\x1b[30m";
constexpr const char* FgRed     = "\x1b[31m";
constexpr const char* FgGreen   = "\x1b[32m";
constexpr const char* FgYellow  = "\x1b[33m";
constexpr const char* FgBlue    = "\x1b[34m";
constexpr const char* FgMagenta = "\x1b[35m";
constexpr const char* FgCyan    = "\x1b[36m";
constexpr const char* FgWhite   = "\x1b[37m";

constexpr const char* BgBlack   = "\x1b[40m";
constexpr const char* BgRed     = "\x1b[41m";
constexpr const char* BgGreen   = "\x1b[42m";
constexpr const char* BgYellow  = "\x1b[43m";
constexpr const char* BgBlue    = "\x1b[44m";
constexpr const char* BgMagenta = "\x1b[45m";
constexpr const char* BgCyan    = "\x1b[46m";
constexpr const char* BgWhite   = "\x1b[47m";

#elif defined(ZEROERR_DISABLE_COLORFUL)
constexpr const char* Reset      = "";
constexpr const char* Bright     = "";
constexpr const char* Dim        = "";
constexpr const char* Underscore = "";
constexpr const char* Blink      = "";
constexpr const char* Reverse    = "";
constexpr const char* Hidden     = "";

constexpr const char* FgBlack   = "";
constexpr const char* FgRed     = "";
constexpr const char* FgGreen   = "";
constexpr const char* FgYellow  = "";
constexpr const char* FgBlue    = "";
constexpr const char* FgMagenta = "";
constexpr const char* FgCyan    = "";
constexpr const char* FgWhite   = "";

constexpr const char* BgBlack   = "";
constexpr const char* BgRed     = "";
constexpr const char* BgGreen   = "";
constexpr const char* BgYellow  = "";
constexpr const char* BgBlue    = "";
constexpr const char* BgMagenta = "";
constexpr const char* BgCyan    = "";
constexpr const char* BgWhite   = "";
#else
extern const char* Reset;
extern const char* Bright;
extern const char* Dim;
extern const char* Underscore;
extern const char* Blink;
extern const char* Reverse;
extern const char* Hidden;

extern const char* FgBlack;
extern const char* FgRed;
extern const char* FgGreen;
extern const char* FgYellow;
extern const char* FgBlue;
extern const char* FgMagenta;
extern const char* FgCyan;
extern const char* FgWhite;

extern const char* BgBlack;
extern const char* BgRed;
extern const char* BgGreen;
extern const char* BgYellow;
extern const char* BgBlue;
extern const char* BgMagenta;
extern const char* BgCyan;
extern const char* BgWhite;

/**
 * @brief Global function to disable colorful output.
 */
extern void disableColorOutput();

/**
 * @brief Global function to enable colorful output.
 */
extern void enableColorOutput();

#endif

}  // namespace zeroerr

#pragma once



namespace zeroerr {

enum OutputStream { STDOUT, STDERR };
extern bool isTerminalOutput(OutputStream stream);


struct TerminalSize {
    int width;
    int height;
};
TerminalSize getTerminalSize();

}  // namespace zeroerr

/* Copyright (c) 2011-2021, Scott Tsai
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef DEBUG_BREAK_H
#define DEBUG_BREAK_H

#ifdef _MSC_VER

#define debug_break __debugbreak

#else

#ifdef __cplusplus
extern "C" {
#endif

#define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1
#define DEBUG_BREAK_USE_BULTIN_TRAP      2
#define DEBUG_BREAK_USE_SIGTRAP          3

#if defined(__i386__) || defined(__x86_64__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__inline__ static void trap_instruction(void) { __asm__ volatile("int $0x03"); }
#elif defined(__thumb__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
/* FIXME: handle __THUMB_INTERWORK__ */
__attribute__((always_inline)) __inline__ static void trap_instruction(void) {
    /* See 'arm-linux-tdep.c' in GDB source.
     * Both instruction sequences below work. */
#if 1
    /* 'eabi_linux_thumb_le_breakpoint' */
    __asm__ volatile(".inst 0xde01");
#else
    /* 'eabi_linux_thumb2_le_breakpoint' */
    __asm__ volatile(".inst.w 0xf7f0a000");
#endif

    /* Known problem:
     * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
     * 'step' would keep getting stuck on the same instruction.
     *
     * Workaround: use the new GDB commands 'debugbreak-step' and
     * 'debugbreak-continue' that become available
     * after you source the script from GDB:
     *
     * $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...>
     *
     * 'debugbreak-step' would jump over the breakpoint instruction with
     * roughly equivalent of:
     * (gdb) set $instruction_len = 2
     * (gdb) tbreak *($pc + $instruction_len)
     * (gdb) jump   *($pc + $instruction_len)
     */
}
#elif defined(__arm__) && !defined(__thumb__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline)) __inline__ static void trap_instruction(void) {
    /* See 'arm-linux-tdep.c' in GDB source,
     * 'eabi_linux_arm_le_breakpoint' */
    __asm__ volatile(".inst 0xe7f001f0");
    /* Known problem:
     * Same problem and workaround as Thumb mode */
}
#elif defined(__aarch64__) && defined(__APPLE__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
#elif defined(__aarch64__)
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline)) __inline__ static void trap_instruction(void) {
    /* See 'aarch64-tdep.c' in GDB source,
     * 'aarch64_default_breakpoint' */
    __asm__ volatile(".inst 0xd4200000");
}
#elif defined(__powerpc__)
/* PPC 32 or 64-bit, big or little endian */
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline)) __inline__ static void trap_instruction(void) {
    /* See 'rs6000-tdep.c' in GDB source,
     * 'rs6000_breakpoint' */
    __asm__ volatile(".4byte 0x7d821008");

    /* Known problem:
     * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
     * 'step' stuck on the same instruction ("twge r2,r2").
     *
     * The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py
     * or manually jump over the instruction. */
}
#elif defined(__riscv)
/* RISC-V 32 or 64-bit, whether the "C" extension
 * for compressed, 16-bit instructions are supported or not */
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline)) __inline__ static void trap_instruction(void) {
    /* See 'riscv-tdep.c' in GDB source,
     * 'riscv_sw_breakpoint_from_kind' */
    __asm__ volatile(".4byte 0x00100073");
}
#else
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP
#endif


#ifndef DEBUG_BREAK_IMPL
#error "debugbreak.h is not supported on this target"
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION
__attribute__((always_inline)) __inline__ static void debug_break(void) { trap_instruction(); }
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
__attribute__((always_inline)) __inline__ static void debug_break(void) { __builtin_debugtrap(); }
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP
__attribute__((always_inline)) __inline__ static void debug_break(void) { __builtin_trap(); }
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP
#include <signal.h>
__attribute__((always_inline)) __inline__ static void debug_break(void) { raise(SIGTRAP); }
#else
#error "invalid DEBUG_BREAK_IMPL value"
#endif

#ifdef __cplusplus
}
#endif

#endif /* ifdef _MSC_VER */


// Here is for checking the debugger is running

#include <fstream>

#ifdef IS_DEBUGGER_ACTIVE
__attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
    return IS_DEBUGGER_ACTIVE();
}
#else  // IS_DEBUGGER_ACTIVE
#ifdef __linux__
class ErrnoGuard {
public:
    ErrnoGuard() : m_oldErrno(errno) {}
    ~ErrnoGuard() { errno = m_oldErrno; }

private:
    int m_oldErrno;
};
// See the comments in Catch2 for the reasoning behind this implementation:
// https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102
__attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
    ErrnoGuard    guard;
    std::ifstream in("/proc/self/status");
    for (std::string line; std::getline(in, line);) {
        static const int PREFIX_LEN = 11;
        if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) {
            return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
        }
    }
    return false;
}
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#include <unistd.h>
#include <iostream>
// The following function is taken directly from the following technical note:
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
__attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
    int        mib[4];
    kinfo_proc info;
    size_t     size;
    // Initialize the flags so that, if sysctl fails for some bizarre
    // reason, we get a predictable result.
    info.kp_proc.p_flag = 0;
    // Initialize mib, which tells sysctl the info we want, in this case
    // we're looking for information about a specific process ID.
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();
    // Call sysctl.
    size = sizeof(info);
    if (sysctl(mib, (sizeof(mib) / sizeof(*mib)), &info, &size, 0, 0) != 0) {
        std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n";
        return false;
    }
    // We're being debugged if the P_TRACED flag is set.
    return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
#elif defined(__MINGW32__) || defined(__MINGW64__)
extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
__attribute__((always_inline)) __inline__  static bool isDebuggerActive() {
    return ::IsDebuggerPresent() != 0;
}
#elif defined(_MSC_VER)
extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
inline __forceinline static bool isDebuggerActive() {
    return ::IsDebuggerPresent() != 0;
}
#else

__attribute__((always_inline)) __inline__ static bool isDebuggerActive() { return false; }
#endif
#endif  // IS_DEBUGGER_ACTIVE


#endif /* ifndef DEBUG_BREAK_H */
#pragma once


/**
 * @brief Thread safety support
 * This header provides thread-safe support for zeroerr.
 * 
 * It defines macros for mutexes, locks, and atomic operations.
 * The macros are conditionally defined based on the ZEROERR_NO_THREAD_SAFE flag.
 */
#ifdef ZEROERR_NO_THREAD_SAFE

#define ZEROERR_MUTEX(x)
#define ZEROERR_LOCK(x)
#define ZEROERR_ATOMIC(x) x
#define ZEROERR_LOAD(x)   x

#else

#define ZEROERR_MUTEX(x)  static std::mutex x;
#define ZEROERR_LOCK(x)   std::lock_guard<std::mutex> lock(x);
#define ZEROERR_ATOMIC(x) std::atomic<x>
#define ZEROERR_LOAD(x)   x.load()

#include <atomic>
#include <mutex>

#endif

#pragma once


#include <ostream>
#include <sstream>
#include <string>
#include <tuple>  // this should be removed
#include <type_traits>
#include <complex>
#include <memory>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH


namespace zeroerr {


/**
 * @brief rank is a helper class for Printer to define the priority of overloaded functions.
 * @tparam N the priority of the rule. 0 is the lowest priority. The maximum priority is max_rank.
 *
 * You can define a rule by adding it as a function parameter with rank<N> where N is the priority.
 * For example:
 * template<typename T>
 * void Foo(T v, rank<0>); // lowest priority
 * void Foo(int v, rank<1>); // higher priority
 *
 * Even though in the first rule, type T can be an int, the second function will still be called due
 * to the priority.
 */
template <unsigned N>
struct rank : rank<N - 1> {};
template <>
struct rank<0> {};


namespace detail {

// C++11 void_t
template <typename... Ts>
using void_t = void;

// Type dependent "true"/"false".
// Useful for SFINAE and static_asserts where we need a type dependent
// expression that happens to be constant.
template <typename T>
struct always_false {
    static std::false_type value;
};

template <typename T>
struct always_true {
    static std::true_type value;
};


// Generate sequence of integers from 0 to N-1
// Usage: detail::gen_seq<N>  then use <size_t... I> to match it
template <unsigned...>
struct seq {};

template <unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};

template <unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...> {};


// Some utility structs to check template specialization
template <typename Test, template <typename...> class Ref>
struct is_specialization : std::false_type {};

template <template <typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};


// Check if a type is stream writable, i.e., std::cout << foo;
// Usage: is_streamable<std::ostream, int>::value
template <typename S, typename T>
using has_stream_operator = void_t<decltype(std::declval<S&>() << std::declval<T>())>;

template <typename S, typename T, typename = void>
struct is_streamable : std::false_type {};

template <typename S, typename T>
struct is_streamable<S, T, has_stream_operator<S, T>> : std::true_type {};


// Check if a type is a container type
// Usage: is_container<std::vector<int>>::value
template <typename T>
using has_begin_end =
    void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>;

template <typename T, typename = void>
struct is_container : std::false_type {};

template <typename T>
struct is_container<T, has_begin_end<T>> : std::true_type {};


template <typename T>
using has_begin_end_find_insert =
    void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end()),
           decltype(std::declval<T>().find(std::declval<typename T::key_type>())),
           decltype(std::declval<T>().insert(std::declval<typename T::value_type>()))>;

template <typename T, typename = void>
struct is_associative_container : std::false_type {};

template <typename T>
struct is_associative_container<T, has_begin_end_find_insert<T>> : std::true_type {};


#if ZEROERR_CXX_STANDARD >= 17
#define ZEROERR_STRING_VIEW std::is_same<T, std::string_view>::value
#else
#define ZEROERR_STRING_VIEW 0
#endif

// Check if a type is a string type
template <class T>
struct is_string
    : std::integral_constant<bool, std::is_same<T, std::string>::value ||
                                       std::is_same<T, const char*>::value || ZEROERR_STRING_VIEW> {
};


// Check if a type can use arr[0] like an array
template <typename T, typename = void>
struct is_array : std::false_type {};

template <typename T>
struct is_array<T, void_t<decltype(std::declval<T>()[0])>> : std::true_type {};


template <typename T, typename = void>
struct is_modifiable : std::false_type {};

template <typename T>
struct is_modifiable<T, void_t<decltype(
                                // Iterable
                                T().begin(), T().end(), T().size(),
                                // Values are mutable
                                // This rejects associative containers, for example
                                // *T().begin() = std::declval<value_type_t<T>>(),
                                // Can insert and erase elements
                                T().insert(T().end(), std::declval<typename T::value_type>()),
                                T().erase(T().begin()), (void)0)>> : std::true_type {};



// Check if a type has the element type as std::pair
template <typename T>
using has_pair_type =
    void_t<typename T::value_type, decltype(std::declval<typename T::value_type>().first),
           decltype(std::declval<typename T::value_type>().second)>;
template <typename T, typename = void>
struct ele_type_is_pair : std::false_type {};

template <typename T>
struct ele_type_is_pair<T, has_pair_type<T>> : std::true_type {};

template <typename T, typename V = void>
struct to_store_type {
    using type = T;
};

template <>
struct to_store_type<const char*> {
    using type = std::string;
};

template <>
struct to_store_type<const char (&)[]> {
    using type = std::string;
};

template <typename T>
using is_not_array = typename std::enable_if<!std::is_array<T>::value>::type;
template <typename T>
struct to_store_type<T&, is_not_array<T>> {
    using type = T;
};

template <typename T>
struct to_store_type<T&&> {
    using type = T;
};

template <typename T>
using to_store_type_t = typename to_store_type<T>::type;


template <size_t I>
struct visit_impl {
    template <typename T, typename F>
    static void visit(T& tup, size_t idx, F&& fun) {
        if (idx == I - 1)
            fun(std::get<I - 1>(tup));
        else
            visit_impl<I - 1>::visit(tup, idx, std::forward<F>(fun));
    }
};

template <>
struct visit_impl<0> {
    template <typename T, typename F>
    static void visit(T&, size_t, F&&) {}
};

template <typename F, typename... Ts>
void visit_at(const std::tuple<Ts...>& tup, size_t idx, F&& fun) {
    visit_impl<sizeof...(Ts)>::visit(tup, idx, std::forward<F>(fun));
}

template <typename F, typename... Ts>
void visit_at(std::tuple<Ts...>& tup, size_t idx, F&& fun) {
    visit_impl<sizeof...(Ts)>::visit(tup, idx, std::forward<F>(fun));
}


template <size_t I>
struct visit2_impl {
    template <typename T1, typename T2, typename F>
    static void visit(T1 tup1, T2 tup2, size_t idx, F&& fun) {
        if (idx == I - 1)
            fun(std::get<I - 1>(tup1), std::get<I - 1>(tup2));
        else
            visit2_impl<I - 1>::visit(tup1, tup2, idx, std::forward<F>(fun));
    }
};

template <>
struct visit2_impl<0> {
    template <typename T1, typename T2, typename F>
    static void visit(T1&, T2&, size_t, F&&) {}
};

template <typename F, typename... Ts, typename... T2s>
void visit2_at(const std::tuple<Ts...>& tup1, const std::tuple<T2s...>& tup2, size_t idx, F&& fun) {
    visit2_impl<sizeof...(Ts)>::visit(tup1, tup2, idx, std::forward<F>(fun));
}

template <typename F, typename... Ts, typename... T2s>
void visit2_at(std::tuple<Ts...>& tup1, std::tuple<T2s...>& tup2, size_t idx, F&& fun) {
    visit2_impl<sizeof...(Ts)>::visit(tup1, tup2, idx, std::forward<F>(fun));
}

template <typename F, typename... Ts, typename... T2s>
void visit2_at(const std::tuple<Ts...>& tup1, std::tuple<T2s...>& tup2, size_t idx, F&& fun) {
    visit2_impl<sizeof...(Ts)>::visit(tup1, tup2, idx, std::forward<F>(fun));
}

template <typename F, typename... Ts, typename... T2s>
void visit2_at(std::tuple<Ts...>& tup1, const std::tuple<T2s...>& tup2, size_t idx, F&& fun) {
    visit2_impl<sizeof...(Ts)>::visit(tup1, tup2, idx, std::forward<F>(fun));
}

#define ZEROERR_ENABLE_IF(x) \
    template <typename T>    \
    typename std::enable_if<x, void>::type
#define ZEROERR_IS_INT        std::is_integral<T>::value
#define ZEROERR_IS_FLOAT      std::is_floating_point<T>::value
#define ZEROERR_IS_CONTAINER  detail::is_container<T>::value
#define ZEROERR_IS_STRING     detail::is_string<T>::value
#define ZEROERR_IS_POINTER    std::is_pointer<T>::value
#define ZEROERR_IS_CHAR       std::is_same<T, char>::value
#define ZEROERR_IS_WCHAR      std::is_same<T, wchar_t>::value
#define ZEROERR_IS_CLASS      std::is_class<T>::value
#define ZEROERR_IS_STREAMABLE detail::is_streamable<std::ostream, T>::value
#define ZEROERR_IS_ARRAY      detail::is_array<T>::value
#define ZEROERR_IS_COMPLEX    detail::is_specialization<T, std::complex>::value
#define ZEROERR_IS_BOOL       std::is_same<T, bool>::value
#define ZEROERR_IS_AUTOPTR                                   \
    (detail::is_specialization<T, std::unique_ptr>::value || \
     detail::is_specialization<T, std::shared_ptr>::value || \
     detail::is_specialization<T, std::weak_ptr>::value)
#define ZEROERR_IS_MAP detail::ele_type_is_pair<T>::value
#define ZEROERR_IS_POD std::is_standard_layout<T>::value
#define ZEROERR_IS_EXT detail::has_extension<T>::value

}  // namespace detail

}  // namespace zeroerr


ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once




#include <cstdint>
#include <cstring>
#include <string>
#include <type_traits>
#include <vector>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {

/**
 * @brief Interface for serializable objects
 * 
 * IRObject (Intermediate Representation Object) provides a low-level interface for serializing 
 * data types into a common format. It uses a union to store different data types and provides
 * type-safe access through templated getter methods.
 *
 * The object can store:
 * - Integers (int64_t)
 * - Floating point numbers (double) 
 * - Strings (char* for long strings, char[8] for short strings)
 * - Nested objects (IRObject*)
 *
 * Memory management:
 * - The object takes ownership of allocated strings and nested objects
 * - Copy/move operations perform deep copies/moves
 * - The destructor frees any owned memory
 *
 * Usage example:
 * @code
 * IRObject obj;
 * obj.type = IRObject::Int;
 * obj.i = 42;
 * int value = obj.GetScalar<int>(); // value = 42
 * @endcode
 */

struct IRObject {
    IRObject() { std::memset(this, 0, sizeof(IRObject)); }
    ~IRObject() {}
    IRObject(const IRObject& other) { *this = other; }
    IRObject(IRObject&& other) { *this = std::move(other); }
    IRObject& operator=(const IRObject& other) {
        std::memcpy(this, &other, sizeof(IRObject));
        return *this;
    }
    IRObject& operator=(IRObject&& other) {
        std::memcpy(this, &other, sizeof(IRObject));
        std::memset(&other, 0, sizeof(IRObject));
        return *this;
    }

    enum Type { Undefined, Int, Float, String, ShortString, Object };

    union {
        int64_t   i;
        double    f;
        char*     s;
        char      ss[8];
        IRObject* o;  // first must be the number of elements
    };
    char     others[7];
    unsigned type;

    template <typename T>
    typename std::enable_if<std::is_integral<T>::value, T>::type GetScalar() {
        return static_cast<T>(i);
    }

    template <typename T>
    typename std::enable_if<std::is_floating_point<T>::value, T>::type GetScalar() {
        return static_cast<T>(f);
    }

    template <typename T>
    typename std::enable_if<std::is_enum<T>::value, T>::type GetScalar() {
        return GetScalar<typename std::underlying_type<T>::type>();
    }

    template <typename T>
    typename std::enable_if<std::is_same<T, std::string>::value, T>::type GetScalar() {
        if (type == Type::String)
            return std::string(s);
        else if (type == Type::ShortString)
            return std::string(ss);
        return "";
    }

    template <typename T>
    typename std::enable_if<std::is_integral<T>::value, void>::type SetScalar(T val) {
        i    = static_cast<int64_t>(val);
        type = Type::Int;
    }

    template <typename T>
    typename std::enable_if<std::is_floating_point<T>::value, void>::type SetScalar(T val) {
        f    = static_cast<double>(val);
        type = Type::Float;
    }

    template <typename T>
    typename std::enable_if<std::is_enum<T>::value, void>::type SetScalar(T val) {
        SetScalar<typename std::underlying_type<T>::type>(val);
    }

    template <typename T>
    typename std::enable_if<std::is_same<T, std::string>::value, void>::type SetScalar(T val) {
        size_t size = val.size();
        if (size > 14) {
            s = alloc_str(size);
            strcpy(s, val.c_str());
            type = Type::String;
        } else {
            strcpy(ss, val.c_str());
            ss[size] = 0;
            type     = Type::ShortString;
        }
    }

    void SetScalar(const IRObject& obj) { *this = obj; }

    struct Childrens {
        int64_t   size;
        IRObject* children;
    };
    Childrens GetChildren() { return {o->i, o + 1}; }

    void SetChildren(IRObject* children) {
        o    = children - 1;
        type = Type::Object;
    }

    // ================================================================

    template <typename T>
    static
        typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value ||
                                    std::is_same<T, std::string>::value || std::is_enum<T>::value,
                                IRObject>::type
        FromCorpus(T val) {
        IRObject obj;
        obj.SetScalar(val);
        return obj;
    }

    template <typename T>
    static typename std::enable_if<
        detail::is_container<T>::value && !std::is_same<T, std::string>::value, IRObject>::type
    FromCorpus(const T& val) {
        IRObject* children = alloc(val.size());
        IRObject  obj;
        obj.SetChildren(children);

        for (const auto& elem : val) {
            *children++ = IRObject::FromCorpus(elem);
        }
        return obj;
    }


    template <class TupType, unsigned... I>
    inline static void handle_tuple(const TupType& _tup, IRObject* children, detail::seq<I...>) {
        int _[] = {((children + I)->SetScalar(FromCorpus(std::get<I>(_tup))), 0)...};
        (void)_;
    }

    template <typename... Args>
    static IRObject FromCorpus(const std::tuple<Args...>& val) {
        unsigned  size = sizeof...(Args);
        IRObject  obj;
        IRObject* children = alloc(size);
        obj.SetChildren(children);

        handle_tuple(val, children, detail::gen_seq<sizeof...(Args)>{});
        return obj;
    }

    template <typename T1, typename T2>
    static IRObject FromCorpus(const std::pair<T1, T2>& val) {
        IRObject  obj;
        IRObject* children = alloc(2);
        obj.SetChildren(children);
        children[0] = IRObject::FromCorpus(val.first);
        children[1] = IRObject::FromCorpus(val.second);
        return obj;
    }

    template <typename T>
    static typename std::enable_if<std::is_integral<T>::value || std::is_enum<T>::value, T>::type
    ToCorpus(IRObject obj) {
        if (obj.type != IRObject::Int) {
            throw std::runtime_error("Invalid type conversion.");
        }
        return obj.GetScalar<T>();
    }

    template <typename T>
    static typename std::enable_if<std::is_floating_point<T>::value, T>::type ToCorpus(
        IRObject obj) {
        if (obj.type != IRObject::Float) {
            throw std::runtime_error("Invalid type conversion.");
        }
        return obj.GetScalar<T>();
    }

    template <typename T>
    static typename std::enable_if<std::is_same<T, std::string>::value, T>::type ToCorpus(
        IRObject obj) {
        if (obj.type != IRObject::String && obj.type != IRObject::ShortString) {
            throw std::runtime_error("Invalid type conversion.");
        }
        return obj.GetScalar<T>();
    }


    template <typename TupType, unsigned... I>
    static TupType parse_tuple(IRObject* children, detail::seq<I...>) {
        return std::make_tuple(
            ToCorpus<typename std::tuple_element<I, TupType>::type>(*(children + I))...);
    }

    template <typename T>
    static typename std::enable_if<
        detail::is_container<T>::value && !std::is_same<T, std::string>::value, T>::type
    ToCorpus(IRObject obj) {
        if (obj.type != IRObject::Object) {
            throw std::runtime_error("Invalid type conversion.");
        }

        auto c = obj.GetChildren();

        T val;
        for (int i = 0; i < c.size; i++) {
            val.insert(val.end(), ToCorpus<typename T::value_type>(c.children[i]));
        }
        return val;
    }


    template <typename T>
    static typename std::enable_if<detail::is_specialization<T, std::tuple>::value, T>::type
    ToCorpus(IRObject obj) {
        if (obj.type != IRObject::Object || obj.GetChildren().size != std::tuple_size<T>::value) {
            throw std::runtime_error("Invalid type conversion.");
        }
        return parse_tuple<T>(obj.o + 1, detail::gen_seq<std::tuple_size<T>::value>{});
    }

    template <typename T>
    static typename std::enable_if<detail::is_specialization<T, std::pair>::value, T>::type
    ToCorpus(IRObject obj) {
        if (obj.type != IRObject::Object || obj.GetChildren().size != 2) {
            throw std::runtime_error("Invalid type conversion.");
        }
        return std::make_pair(ToCorpus<typename T::first_type>(obj.GetChildren().children[0]),
                              ToCorpus<typename T::second_type>(obj.GetChildren().children[1]));
    }


    static char*     alloc_str(size_t size);
    static IRObject* alloc(size_t size);

    // Serialize the object as a string.
    static std::string ToString(IRObject obj);
    static IRObject    FromString(std::string str);

    static std::vector<uint8_t> ToBinary(IRObject obj);
    static IRObject             FromBinary(std::vector<uint8_t> bin);
};


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once





#ifdef __GNUG__
#include <cxxabi.h>
#endif

#if defined(ZEROERR_ENABLE_PFR) && (ZEROERR_CXX_STANDARD >= 14)
#include "pfr.hpp"
#endif

#if defined(ZEROERR_ENABLE_MAGIC_ENUM) && (ZEROERR_CXX_STANDARD >= 17)
#include "magic_enum.hpp"
#endif

#if defined(ZEROERR_ENABLE_DSVIZ)
#include "dsviz.h"
#endif

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH


namespace zeroerr {


constexpr unsigned max_rank = 5;


struct Printer;
template <typename T>
void PrinterExt(Printer&, T, unsigned, const char*, rank<0>);

namespace detail {

/**
 * @brief has_extension is a type trait to check if user defined PrinterExt for a type
 */
template <typename T, typename = void>
struct has_extension : std::false_type {};

template <typename T>
using has_printer_ext =
    void_t<decltype(zeroerr::PrinterExt(std::declval<zeroerr::Printer&>(), std::declval<T&>(), 0,
                                        nullptr, zeroerr::rank<zeroerr::max_rank>()))>;

template <typename T>
struct has_extension<T, has_printer_ext<T>> : std::true_type {};

}  // namespace detail


/**
 * @brief A functor class Printer for printing a value of any type.
 *
 * This class can print values with all basic types, pointers, STL containers, tuple, optional, and
 * variant values. Any class that is streamable can be printed. POD structs can be supported using
 * third-party library Boost.PFR and enum can be supported using magic_enum.
 */
struct Printer {
    template <typename... Args>
    Printer& operator()(Args&&... args) {
        check_stream();
        call(std::forward<Args>(args)...);
        return *this;
    }
    template <typename T>
    Printer& operator()(std::initializer_list<T>&& value) {
        check_stream();
        call(std::forward<decltype(value)>(value));
        return *this;
    }

    void check_stream() {
        if (use_stringstream && clear_stream_before_printing) {
            auto& ss = static_cast<std::stringstream&>(os);
            ss.str(std::string());
            ss.clear();
        }
    }

    template <typename T, typename... V>
    void call(T value, V... others) {
        PrinterExt(*this, std::forward<T>(value), 0, " ", rank<max_rank>{});
        call(std::forward<V>(others)...);
    }

    template <typename T>
    void call(T value) {
        PrinterExt(*this, std::forward<T>(value), 0, "", rank<max_rank>{});
        os << line_break;
        os.flush();
    }

    Printer(std::ostream& os) : os(os) {}
    Printer() : os(*new std::stringstream()) { use_stringstream = true; }
    ~Printer() {
        if (use_stringstream) delete &os;
    }

    bool          isColorful = true;   // colorful output
    bool          isCompact  = false;  // compact mode
    bool          isQuoted   = true;   // string is quoted
    int           indent     = 2;
    const char*   line_break = "\n";
    std::ostream& os;
    bool          use_stringstream             = false;
    bool          clear_stream_before_printing = true;

    template <class T>
    static std::string type(const T& t) {
        return demangle(typeid(t).name());
    }


    ZEROERR_ENABLE_IF(ZEROERR_IS_INT || ZEROERR_IS_FLOAT)
    print(T value, unsigned level, const char* lb, rank<0>) { os << tab(level) << value << lb; }

    ZEROERR_ENABLE_IF(ZEROERR_IS_POINTER)
    print(T value, unsigned level, const char* lb, rank<0>) {
        if (value == nullptr)
            os << tab(level) << "nullptr" << lb;
        else
            os << tab(level) << "<" << type(value) << " at " << value << ">" << lb;
    }


    ZEROERR_ENABLE_IF(ZEROERR_IS_CLASS)
    print(T value, unsigned level, const char* lb, rank<0>) {
        os << tab(level) << type(value) << lb;
    }


    ZEROERR_ENABLE_IF(ZEROERR_IS_CHAR || ZEROERR_IS_WCHAR)
    print(T value, unsigned level, const char* lb, rank<1>) {
        os << tab(level) << '\'' << value << '\'' << lb;
    }

#if defined(ZEROERR_ENABLE_PFR) && (ZEROERR_CXX_STANDARD >= 14)
    template <class StructType, unsigned... I>
    void print_struct(const StructType& s, unsigned, const char*, detail::seq<I...>) {
        int _[] = {(os << (I == 0 ? "" : ", ") << pfr::get<I>(s), 0)...};
        (void)_;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_CLASS && ZEROERR_IS_POD)
    print(const T& value, unsigned level, const char* lb, rank<1>) {
        os << tab(level) << "{";
        print_struct(value, level, isCompact ? " " : line_break,
                     detail::gen_seq<pfr::tuple_size<T>::value>{});
        os << tab(level) << "}" << lb;
    }
#endif

    ZEROERR_ENABLE_IF(ZEROERR_IS_BOOL)
    print(T value, unsigned level, const char* lb, rank<2>) {
        os << tab(level) << (value ? "true" : "false") << lb;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_CLASS && ZEROERR_IS_STREAMABLE)
    print(T value, unsigned level, const char* lb, rank<2>) { os << tab(level) << value << lb; }


    ZEROERR_ENABLE_IF(ZEROERR_IS_CONTAINER)
    print(const T& value, unsigned level, const char* lb, rank<2>) {
        os << tab(level) << "{" << (isCompact ? "" : line_break);
        bool last = false;
        for (auto iter = value.begin(); iter != value.end(); ++iter) {
            if (std::next(iter) == value.end()) last = true;
            print(*iter, level + 1, isCompact ? (last ? "" : ", ") : line_break, rank<max_rank>{});
        }
        os << tab(level) << "}" << lb;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_CONTAINER && ZEROERR_IS_ARRAY)
    print(const T& value, unsigned level, const char* lb, rank<3>) {
        os << tab(level) << "[";
        bool last = false;
        for (auto iter = value.begin(); iter != value.end(); ++iter) {
            if (std::next(iter) == value.end()) last = true;
            print(*iter, 0, last ? "" : ", ", rank<max_rank>{});
        }
        os << tab(level) << "]" << lb;
    }


    ZEROERR_ENABLE_IF(ZEROERR_IS_AUTOPTR)
    print(T value, unsigned level, const char* lb, rank<3>) {
        if (value.get() == nullptr)
            os << tab(level) << "nullptr" << lb;
        else
            os << tab(level) << "<" << type(value) << " at " << value.get() << ">" << lb;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_CONTAINER && ZEROERR_IS_MAP)
    print(const T& value, unsigned level, const char* lb, rank<4>) {
        os << tab(level) << "{" << (isCompact ? "" : line_break);
        bool last = false;
        for (auto iter = value.begin(); iter != value.end(); ++iter) {
            if (std::next(iter) == value.end()) last = true;
            print(iter->first, level + 1, " : ", rank<max_rank>{});
            print(iter->second, level + 1, isCompact ? (last ? "" : ", ") : line_break,
                  rank<max_rank>{});
        }
        os << tab(level) << "}" << lb;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_COMPLEX)
    print(T value, unsigned level, const char* lb, rank<4>) {
        os << tab(level) << "(" << value.real() << "+" << value.imag() << "i)" << lb;
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_STRING)
    print(T value, unsigned level, const char* lb, rank<4>) {
        os << tab(level) << quote() << value << quote() << lb;
    }

    template <class TupType>
    inline void print_tuple(const TupType&, unsigned, const char*, detail::seq<>) {}

    template <class TupType, unsigned... I>
    inline void print_tuple(const TupType& _tup, unsigned level, const char*, detail::seq<I...>) {
        int _[] = {(os << (I == 0 ? "" : ", "),
                    print(std::get<I>(_tup), level + 1, "", rank<max_rank>{}), 0)...};
        (void)_;
    }

    template <class... Args>
    void print(const std::tuple<Args...>& value, unsigned level, const char* lb, rank<3>) {
        os << tab(level) << "(";
        print_tuple(value, level, isCompact ? " " : line_break, detail::gen_seq<sizeof...(Args)>{});
        os << ")" << lb;
    }

    std::string tab(unsigned level) { return std::string((isCompact ? 0 : level * indent), ' '); }
    const char* quote() { return isQuoted ? "\"" : ""; }

    static std::string demangle(const char* name) {
#ifdef __GNUG__
        int         status = -4;
        char*       res    = abi::__cxa_demangle(name, NULL, NULL, &status);
        std::string ret    = (status == 0) ? res : name;
        std::free(res);
        return ret;
#else
        return name;
#endif
    }

    std::string str() const {
        if (use_stringstream == false)
            throw std::runtime_error("Printer is not using stringstream");
        return static_cast<std::stringstream&>(os).str();
    }
    operator std::string() const { return str(); }

    friend std::ostream& operator<<(std::ostream& os, const Printer& P) {
        if (P.use_stringstream) os << P.str();
        return os;
    }
};

/**
 * @brief PrinterExt is an extension of Printer that allows user to write custom rules for printing.
 * @details
 * User can use SFINAE to extend PrinterExt, e.g.:
 * ```
 * template<typename T>
 * typename std::enable_if<std::is_base_of<llvm::Function, T>::value, void>::type
 * PrinterExt(Printer& P, T* s, unsigned level, const char* lb, rank<3>);
 * ```
 * @tparam T the type of the object to be printed.
 * @param P Printer class
 * @param v the object to be printed.
 * @param level the indentation level.
 * @param lb  the line break.
 * @param r  the rank of the rule. 0 is lowest priority.
 */
template <class T>
void PrinterExt(Printer& P, T v, unsigned level, const char* lb, rank<0>) {
    P.print(std::forward<T>(v), level, lb, rank<max_rank>{});
}

extern Printer& getStdoutPrinter();
extern Printer& getStderrPrinter();


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP

#pragma once






ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH
ZEROERR_SUPPRESS_COMPARE

#ifndef ZEROERR_DISABLE_COMPLEX_AND_OR
/**
 * AND and OR are used to combine expressions in a more readable way.
 * For example:
 *   CHECK(1 == 1 AND 2 == 2);
 *
 * In its implementation, the expression is decomposed to
 *   ExpressionDecomposer() << 1 == 1 && ExpressionDecomposer() << 2 == 2
 */
#define AND &&zeroerr::ExpressionDecomposer() <<
#define OR  || zeroerr::ExpressionDecomposer() <<
#endif

namespace zeroerr {


// SFINAE helper used to check L op R is supported, but the result type is `ret`
#define ZEROERR_SFINAE_OP(ret, op) \
    typename std::decay<decltype(std::declval<L>() op std::declval<R>(), std::declval<ret>())>::type

template <typename T>
struct deferred_false {
    static const bool value = false;
};

#define ZEROERR_EXPRESSION_COMPARISON(op, op_name)                                                 \
    template <typename R>                                                                          \
    ZEROERR_SFINAE_OP(Expression<R>, op)                                                           \
    operator op(R && rhs) {                                                                        \
        std::stringstream ss;                                                                      \
        Printer           print(ss);                                                               \
        print.isCompact  = true;                                                                   \
        print.line_break = "";                                                                     \
        if (decomp.empty()) {                                                                      \
            print(lhs);                                                                            \
            res = true;                                                                            \
        } else                                                                                     \
            ss << decomp;                                                                          \
        ss << " " #op " ";                                                                         \
        print(rhs);                                                                                \
        return Expression<R>(static_cast<R&&>(rhs), res && (lhs op rhs), ss.str());                \
    }                                                                                              \
    template <typename R,                                                                          \
              typename std::enable_if<!std::is_rvalue_reference<R>::value, void>::type* = nullptr> \
    ZEROERR_SFINAE_OP(Expression<const R&>, op)                                                    \
    operator op(const R & rhs) {                                                                   \
        std::stringstream ss;                                                                      \
        Printer           print(ss);                                                               \
        print.isCompact  = true;                                                                   \
        print.line_break = "";                                                                     \
        if (decomp.empty()) {                                                                      \
            print(lhs);                                                                            \
            res = true;                                                                            \
        } else                                                                                     \
            ss << decomp;                                                                          \
        ss << " " #op " ";                                                                         \
        print(rhs);                                                                                \
        return Expression<const R&>(rhs, res && (lhs op rhs), ss.str());                           \
    }

#define ZEROERR_EXPRESSION_ANDOR(op, op_name)        \
    ExprResult operator op(ExprResult rhs) {         \
        std::stringstream ss;                        \
        ss << decomp << " " #op " " << rhs.decomp;   \
        return ExprResult(res op rhs.res, ss.str()); \
    }


#define ZEROERR_FORBIT_EXPRESSION(rt, op)                                 \
    template <typename R>                                                 \
    rt& operator op(const R&) {                                           \
        static_assert(deferred_false<R>::value,                           \
                      "Please Rewrite Expression As Binary Comparison!"); \
        return *this;                                                     \
    }

struct ExprResult {
    bool        res;
    std::string decomp;

    ExprResult(bool res, std::string decomposition = "") : res(res), decomp(decomposition) {}

    ZEROERR_EXPRESSION_ANDOR(&&, and)
    ZEROERR_EXPRESSION_ANDOR(||, or)

    ZEROERR_FORBIT_EXPRESSION(ExprResult, &)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, ^)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, |)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, ==)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, !=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, <)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, >)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, <=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, >=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, +=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, -=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, *=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, /=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, %=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, <<=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, >>=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, &=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, ^=)
    ZEROERR_FORBIT_EXPRESSION(ExprResult, |=)
};

namespace details {
template <typename T>
typename std::enable_if<std::is_convertible<T, bool>::value, bool>::type getBool(T&& lhs) {
    return static_cast<bool>(lhs);
}

template <typename T>
typename std::enable_if<!std::is_convertible<T, bool>::value, bool>::type getBool(T&&) {
    return true;
}
}  // namespace details

template <typename L>
struct Expression {
    L           lhs;
    bool        res = true;
    std::string decomp;

    explicit Expression(L&& in) : lhs(static_cast<L&&>(in)) { res = details::getBool(lhs); }
    explicit Expression(L&& in, bool res, std::string&& decomp)
        : lhs(static_cast<L&&>(in)), res(res), decomp(static_cast<std::string&&>(decomp)) {}

    operator ExprResult() {
        if (decomp.empty()) {
            Printer print;
            print.isCompact  = true;
            print.line_break = "";
            decomp           = print(lhs).str();
        }
        return ExprResult(res, decomp);
    }

    operator L() const { return lhs; }

    ZEROERR_EXPRESSION_COMPARISON(==, eq)
    ZEROERR_EXPRESSION_COMPARISON(!=, ne)
    ZEROERR_EXPRESSION_COMPARISON(>, gt)
    ZEROERR_EXPRESSION_COMPARISON(<, lt)
    ZEROERR_EXPRESSION_COMPARISON(>=, ge)
    ZEROERR_EXPRESSION_COMPARISON(<=, le)

    ZEROERR_EXPRESSION_ANDOR(&&, and)
    ZEROERR_EXPRESSION_ANDOR(||, or)

    ZEROERR_FORBIT_EXPRESSION(Expression, &)
    ZEROERR_FORBIT_EXPRESSION(Expression, ^)
    ZEROERR_FORBIT_EXPRESSION(Expression, |)
    ZEROERR_FORBIT_EXPRESSION(Expression, =)
    ZEROERR_FORBIT_EXPRESSION(Expression, +=)
    ZEROERR_FORBIT_EXPRESSION(Expression, -=)
    ZEROERR_FORBIT_EXPRESSION(Expression, *=)
    ZEROERR_FORBIT_EXPRESSION(Expression, /=)
    ZEROERR_FORBIT_EXPRESSION(Expression, %=)
    ZEROERR_FORBIT_EXPRESSION(Expression, <<=)
    ZEROERR_FORBIT_EXPRESSION(Expression, >>=)
    ZEROERR_FORBIT_EXPRESSION(Expression, &=)
    ZEROERR_FORBIT_EXPRESSION(Expression, ^=)
    ZEROERR_FORBIT_EXPRESSION(Expression, |=)
    ZEROERR_FORBIT_EXPRESSION(Expression, <<)
    ZEROERR_FORBIT_EXPRESSION(Expression, >>)
};

#undef ZEROERR_EXPRESSION_COMPARISON
#undef ZEROERR_EXPRESSION_ANDOR
#undef ZEROERR_FORBIT_EXPRESSION

struct ExpressionDecomposer {
    // The right operator for capturing expressions is "<=" instead of "<<" (based on the
    // operator precedence table) but then there will be warnings from GCC about "-Wparentheses"
    // and since "_Pragma()" is problematic this will stay for now...
    // https://github.com/catchorg/Catch2/issues/870
    // https://github.com/catchorg/Catch2/issues/565

    // For temporary objects, we need to use rvalue reference to avoid copy
    template <typename L>
    Expression<L> operator<<(L&& operand) {
        return Expression<L>(static_cast<L&&>(operand));
    }

    // For other objects, we will store the reference
    template <typename L,
              typename std::enable_if<!std::is_rvalue_reference<L>::value, void>::type* = nullptr>
    Expression<const L&> operator<<(const L& operand) {
        return Expression<const L&>(operand);
    }
};


template <typename T>
class IMatcher {
public:
    virtual ~IMatcher() = default;


    virtual bool match(const T&) const = 0;
};

template <typename T>
class IMatcherRef {
public:
    IMatcherRef(const IMatcher<T>* ptr) : p(ptr) {}
    IMatcherRef(const IMatcherRef&) = delete;

    IMatcherRef(IMatcherRef&& other) {
        p       = std::move(other.p);
        other.p = nullptr;
    }
    void operator=(IMatcherRef&& other) {
        p       = std::move(other.p);
        other.p = nullptr;
    }
    IMatcherRef& operator=(const IMatcherRef&) = delete;
    ~IMatcherRef() {
        if (p) delete p;
    }

    IMatcherRef operator&&(IMatcherRef&& other);
    IMatcherRef operator||(IMatcherRef&& other);
    IMatcherRef operator!();

    const IMatcher<T>* operator->() const { return p; }

protected:
    const IMatcher<T>* p = nullptr;
};


template <typename T>
class CombinedMatcher : public IMatcher<T> {
public:
    CombinedMatcher(IMatcherRef<T>&& lhs, IMatcherRef<T>&& rhs, bool is_and)
        : lhs(std::move(lhs)), rhs(std::move(rhs)), is_and(is_and) {}

    IMatcherRef<T> lhs;
    IMatcherRef<T> rhs;
    bool           is_and;

    virtual bool match(const T& t) const override {
        if (is_and) {
            return lhs->match(t) && rhs->match(t);
        } else {
            return lhs->match(t) || rhs->match(t);
        }
    }
};

template <typename T>
class NotMatcher : public IMatcher<T> {
public:
    NotMatcher(IMatcherRef<T>&& matcher) : matcher(std::move(matcher)) {}
    IMatcherRef<T> matcher;

    virtual bool match(const T& t) const override { return !matcher->match(t); }
};

template <typename T>
inline IMatcherRef<T> IMatcherRef<T>::operator&&(IMatcherRef<T>&& other) {
    return new CombinedMatcher<T>(std::move(*this), std::move(other), true);
}

template <typename T>
inline IMatcherRef<T> IMatcherRef<T>::operator||(IMatcherRef<T>&& other) {
    return new CombinedMatcher<T>(std::move(*this), std::move(other), false);
}

template <typename T>
inline IMatcherRef<T> IMatcherRef<T>::operator!() {
    return new NotMatcher<T>(std::move(*this));
}


template <typename T>
struct StartWithMatcher : public IMatcher<T> {
    StartWithMatcher(const T& s) : start(s) {}
    T start;

    virtual bool match(const T& t) const override {
        bool result = true;
        for (auto i = start.begin(), j = t.begin(); i != start.end(); ++i, ++j) {
            if (j == t.end() || *i != *j) {
                result = false;
                break;
            }
        }
        return result;
    }
};

template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value,
                        IMatcherRef<std::string>>::type
start_with(T&& s) {
    return new StartWithMatcher<std::string>(std::string(s));
}

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMPARE_POP
ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once



#include <cstdint>
#include <cstring>
#include <vector>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {

/**
 * An extremely fast random generator. Currently, this implements *RomuDuoJr*, developed by Mark
 * Overton. Source: http://www.romu-random.org/
 *
 * RomuDuoJr is extremely fast and provides reasonable good randomness. Not enough for large
 * jobs, but definitely good enough for a benchmarking framework.
 *
 *  * Estimated capacity: @f$ 2^{51} @f$ bytes
 *  * Register pressure: 4
 *  * State size: 128 bits
 *
 * This random generator is a drop-in replacement for the generators supplied by ``<random>``.
 * It is not cryptographically secure. It's intended purpose is to be very fast so that
 * benchmarks that make use of randomness are not distorted too much by the random generator.
 *
 * Rng also provides a few non-standard helpers, optimized for speed.
 */
class Rng final {
public:
    /**
     * @brief This RNG provides 64bit randomness.
     */
    using result_type = uint64_t;

    static uint64_t min();
    static uint64_t max();

    /**
     * As a safety precausion, we don't allow copying. Copying a PRNG would mean you would have
     * two random generators that produce the same sequence, which is generally not what one
     * wants. Instead create a new rng with the default constructor Rng(), which is
     * automatically seeded from `std::random_device`. If you really need a copy, use copy().
     */
    Rng(Rng const&) = delete;

    /**
     * Same as Rng(Rng const&), we don't allow assignment. If you need a new Rng create one with
     * the default constructor Rng().
     */
    Rng& operator=(Rng const&) = delete;

    // moving is ok
    Rng(Rng&&) noexcept            = default;
    Rng& operator=(Rng&&) noexcept = default;
    ~Rng() noexcept                = default;

    /**
     * @brief Creates a new Random generator with random seed.
     *
     * Instead of a default seed (as the random generators from the STD), this properly seeds
     * the random generator from `std::random_device`. It guarantees correct seeding. Note that
     * seeding can be relatively slow, depending on the source of randomness used. So it is best
     * to create a Rng once and use it for all your randomness purposes.
     */
    Rng();

    /*!
      Creates a new Rng that is seeded with a specific seed. Each Rng created from the same seed
      will produce the same randomness sequence. This can be useful for deterministic behavior.
      @verbatim embed:rst
      .. note::
         The random algorithm might change between nanobench releases. Whenever a faster and/or
      better random generator becomes available, I will switch the implementation.
      @endverbatim
      As per the Romu paper, this seeds the Rng with splitMix64 algorithm and performs 10
      initial rounds for further mixing up of the internal state.
      @param seed  The 64bit seed. All values are allowed, even 0.
     */
    explicit Rng(uint64_t seed) noexcept;
    Rng(uint64_t x, uint64_t y) noexcept;
    Rng(std::vector<uint64_t> const& data);

    /**
     * Creates a copy of the Rng, thus the copy provides exactly the same random sequence as the
     * original.
     */
    Rng copy() const noexcept;

    /**
     * @brief Produces a 64bit random value. This should be very fast, thus it is marked as
     * inline. In my benchmark, this is ~46 times faster than `std::default_random_engine` for
     * producing 64bit random values. It seems that the fastest std contender is
     * `std::mt19937_64`. Still, this RNG is 2-3 times as fast.
     *
     * @return uint64_t The next 64 bit random value.
     */
    inline uint64_t operator()() noexcept {
        auto x = mX;

        mX = UINT64_C(15241094284759029579) * mY;
        mY = rotl(mY - x, 27);

        return x;
    }

    // This is slightly biased. See

    /**
     * Generates a random number between 0 and range (excluding range).
     *
     * The algorithm only produces 32bit numbers, and is slightly biased. The effect is quite
     * small unless your range is close to the maximum value of an integer. It is possible to
     * correct the bias with rejection sampling (see
     * [here](https://lemire.me/blog/2016/06/30/fast-random-shuffling/), but this is most likely
     * irrelevant in practices for the purposes of this Rng.
     *
     * See Daniel Lemire's blog post [A fast alternative to the modulo
     * reduction](https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/)
     *
     * @param range Upper exclusive range. E.g a value of 3 will generate random numbers 0,
     * 1, 2.
     * @return uint32_t Generated random values in range [0, range).
     */
    inline uint32_t bounded(uint32_t range) noexcept {
        uint64_t r32         = static_cast<uint32_t>(operator()());
        auto     multiresult = r32 * range;
        return static_cast<uint32_t>(multiresult >> 32U);
    }

    // random double in range [0, 1]
    // see http://prng.di.unimi.it/

    /**
     * Provides a random uniform double value between 0 and 1. This uses the method described in
     * [Generating uniform doubles in the unit interval](http://prng.di.unimi.it/), and is
     * extremely fast.
     *
     * @return double Uniformly distributed double value in range [0,1], excluding 1.
     */
    inline double uniform01() noexcept {
        auto i = (UINT64_C(0x3ff) << 52U) | (operator()() >> 12U);
        // can't use union in c++ here for type puning, it's undefined behavior.
        // std::memcpy is optimized anyways.
        double d;
        std::memcpy(&d, &i, sizeof(double));
        return d - 1.0;
    }

    /**
     * Shuffles all entries in the given container. Although this has a slight bias due to the
     * implementation of bounded(), this is preferable to `std::shuffle` because it is over 5
     * times faster. See Daniel Lemire's blog post [Fast random
     * shuffling](https://lemire.me/blog/2016/06/30/fast-random-shuffling/).
     *
     * @param container The whole container will be shuffled.
     */
    template <typename Container>
    void shuffle(Container& container) noexcept {
        auto size = static_cast<uint32_t>(container.size());
        for (auto i = size; i > 1U; --i) {
            using std::swap;
            auto p = bounded(i);  // number in [0, i)
            swap(container[i - 1], container[p]);
        }
    }

    /**
     * Extracts the full state of the generator, e.g. for serialization. For this RNG this is
     * just 2 values, but to stay API compatible with future implementations that potentially
     * use more state, we use a vector.
     *
     * @return Vector containing the full state:
     */
    std::vector<uint64_t> state() const;

private:
    static uint64_t rotl(uint64_t x, unsigned k) noexcept;

    uint64_t mX;
    uint64_t mY;
};

} // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once





ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {


/**
 * @brief Domain class for generating random values of a specific type.
 * @tparam ValueType The type of the value to generate.
 * @tparam CorpusType The type of the corpus stored in the domain.
 * Here is an example. If you want to generate an list of intergers, 
 * but will store the list in a vector, then ValueType will be 
 * std::list<int> and CorpusType will be std::vector<int>.
 */
template <typename ValueType, typename CorpusType = ValueType>
class Domain {
public:
    virtual ~Domain() = default;

    virtual CorpusType GetRandomCorpus(Rng& rng) const = 0;
    virtual ValueType  GetRandomValue(Rng& rng) const { return GetValue(GetRandomCorpus(rng)); };

    virtual CorpusType FromValue(const ValueType& v) const = 0;
    virtual ValueType  GetValue(const CorpusType& v) const = 0;

    virtual CorpusType ParseCorpus(IRObject v) const { return IRObject::ToCorpus<CorpusType>(v); }
    virtual IRObject SerializeCorpus(const CorpusType& v) const { return IRObject::FromCorpus(v); }

    virtual void     Mutate(Rng& rng, CorpusType& v, bool only_shrink = false) const = 0;
    // virtual void     MutateSelectedField(Rng& rng, CorpusType& v, unsigned field,
    //                                      bool only_shrink = false) const {}
    // virtual unsigned CountNumberOfFields(CorpusType v) const { return 0; }
};


/**
 * @brief DomainConvertable is a base class for domains that can be converted to and from a ValueType
 * 
 * This class provides default implementations for the GetValue and FromValue methods.
 * It is used to convert between the corpus types and the value types.
 */
template <typename ValueType, typename CorpusType = ValueType>
class DomainConvertable : public Domain<ValueType, CorpusType> {
public:
    virtual ValueType  GetValue(const CorpusType& v) const { return v; }
    virtual CorpusType FromValue(const ValueType& v) const { return v; }
};


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP

#pragma once




ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {

/**
 * @brief InRange is a domain that generates random values within a specified range
 * 
 * @tparam T The numeric type to generate values for (e.g. int, float)
 * 
 * This domain generates random values between a minimum and maximum value (inclusive).
 * It supports any numeric type that can be used with arithmetic operations.
 * 
 * Example:
 * ```cpp
 * // Generate integers between 1 and 100
 * auto domain = InRange(1, 100);
 * 
 * // Generate floating point numbers between 0.0 and 1.0
 * auto domain = InRange(0.0, 1.0);
 * ```
 */
template <typename T>
class InRange : public DomainConvertable<T> {
public:
    using ValueType  = T;
    using CorpusType = T;

    ValueType min, max;

    InRange(T min, T max) : min(min), max(max) {}

    CorpusType GetRandomCorpus(Rng& rng) const override {
        ValueType offsize = max - min + 1;
        ValueType v       = rng.bounded(offsize);
        v                 = min + v;
        return v;
    }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        CorpusType offsize = max - min + 1;
        v                  = rng.bounded(offsize);
        v                  = min + v;
    }
};

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once




ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {

/**
 * @brief ElementOf is a domain that generates random values from a fixed set of elements
 * 
 * @tparam T The type of elements to generate
 * 
 * This domain allows generating random values by selecting from a predefined set of elements.
 * The elements are provided as a vector during construction.
 * 
 * Example:
 * ```cpp
 * // Generate random values from a set of strings
 * auto domain = ElementOf<std::string>({"red", "green", "blue"});
 * 
 * // Generate random values from a set of integers
 * auto domain = ElementOf<int>({1, 2, 3, 4, 5});
 * ```
 */
template <typename T>
class ElementOf : public Domain<T, uint64_t> {
public:
    using ValueType  = T;
    using CorpusType = uint64_t;

    std::vector<T> elements;

    ElementOf(std::vector<T> elements) : elements(elements) {}

    CorpusType GetRandomCorpus(Rng& rng) override { return rng.bounded(elements.size()); }

    ValueType GetValue(const CorpusType& v) const override { return elements[v]; }

    CorpusType FromValue(const ValueType& v) const override {
        for (size_t i = 0; i < elements.size(); i++) {
            if (elements[i] == v) return i;
        }
        return 0;
    }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        if (elements.size() <= 1) return;
        if (only_shrink) {
            v = rng.bounded(v);
        } else {
            v = rng.bounded(elements.size());
        }
    }
};

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once





ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {

/**
 * @brief ContainerOf is a domain that generates random containers filled with elements from an inner domain
 * 
 * @tparam T The container type to generate (e.g. vector, list, set)
 * @tparam InnerDomain The domain type used to generate the container elements
 * 
 * This domain allows generating containers where each element is generated by an inner domain.
 * It supports configuring the size constraints of the generated containers.
 * 
 * Example:
 * ```cpp
 * // Generate vectors of ints between 0-100
 * auto domain = ContainerOf<std::vector<int>>(InRange(0, 100));
 * 
 * // Generate sets of strings
 * auto domain = ContainerOf<std::set<std::string>>(Arbitrary<std::string>());
 * 
 * // Configure size constraints
 * domain.WithMinSize(5);  // At least 5 elements
 * domain.WithMaxSize(10); // At most 10 elements
 * domain.WithSize(7);     // Exactly 7 elements
 * ```
 */
struct ContainerOfBase {
    int min_size = 0, max_size = 100, size = -1;

    void WithMaxSize(unsigned _max_size) { this->max_size = _max_size; }
    void WithMinSize(unsigned _min_size) { this->min_size = _min_size; }
    void WithSize(unsigned _size) { this->size = _size; }
};


template <typename T, typename InnerDomain>
class AssociativeContainerOf : public Domain<T, std::vector<typename InnerDomain::CorpusType>>,
                               public ContainerOfBase {
    InnerDomain inner_domain;

public:
    using ValueType  = T;
    using CorpusType = std::vector<typename InnerDomain::CorpusType>;

    AssociativeContainerOf(InnerDomain&& inner_domain) : inner_domain(std::move(inner_domain)) {}

    virtual ValueType GetValue(const CorpusType& v) const override {
        ValueType result;
        for (const auto& elem : v) {
            result.insert(inner_domain.GetValue(elem));
        }
        return result;
    }

    virtual CorpusType FromValue(const ValueType& v) const override {
        CorpusType result;
        for (const auto& elem : v) {
            result.push_back(inner_domain.FromValue(elem));
        }
        return result;
    }

    CorpusType GetRandomCorpus(Rng& rng) const override {
        unsigned   E = rng.bounded(max_size - min_size + 1) + min_size;
        CorpusType result;
        for (unsigned i = 0; i < E; i++) {
            result.push_back(inner_domain.GetRandomCorpus(rng));
        }
        return result;
    }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        int action = rng.bounded(5);
        if (size == -1 && action == 0) {
            if (static_cast<int>(v.size()) > min_size)
                v.erase(v.begin() + rng.bounded(static_cast<uint32_t>(v.size())));

        } else if (size == -1 && action == 1) {
            if (static_cast<int>(v.size()) < max_size)
                v.push_back(inner_domain.GetRandomCorpus(rng));

        } else {
            inner_domain.Mutate(rng, v[rng.bounded(static_cast<uint32_t>(v.size()))], only_shrink);
        }
    }
};

template <typename T, typename InnerDomain>
class SequenceContainerOf : public Domain<T, std::vector<typename InnerDomain::CorpusType>>,
                            public ContainerOfBase {
    InnerDomain inner_domain;

public:
    using ValueType  = T;
    using CorpusType = std::vector<typename InnerDomain::CorpusType>;

    SequenceContainerOf(InnerDomain&& inner_domain) : inner_domain(std::move(inner_domain)) {}

    virtual ValueType GetValue(const CorpusType& v) const override {
        return ValueType(v.begin(), v.end());
    }
    virtual CorpusType FromValue(const ValueType& v) const override {
        return CorpusType(v.begin(), v.end());
    }

    CorpusType GetRandomCorpus(Rng& rng) const override {
        unsigned   E = rng.bounded(max_size - min_size + 1) + min_size;
        CorpusType result;
        for (unsigned i = 0; i < E; i++) {
            result.push_back(inner_domain.GetRandomCorpus(rng));
        }
        return result;
    }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        int action = rng.bounded(5);
        if (size == -1 && action == 0) {
            if (static_cast<int>(v.size()) > min_size) v.erase(v.begin() + rng.bounded(static_cast<uint32_t>(v.size())));

        } else if (size == -1 && action == 1) {
            if (static_cast<int>(v.size()) < max_size)
                v.push_back(inner_domain.GetRandomCorpus(rng));
        } else {
            inner_domain.Mutate(rng, v[rng.bounded(static_cast<uint32_t>(v.size()))], only_shrink);
        }
    }
};


// ContainerOf<T>(inner) combinator creates a domain for a container T (eg, a
// std::vector, std::set, etc) where elements are created from `inner`.
//
// Example usage:
//
//   ContainerOf<std::vector<int>>(InRange(1, 2021))
//
// The domain also supports customizing the minimum and maximum size via the
// `WithSize`, `WithMinSize` and `WithMaxSize` functions. Eg:
//
//   ContainerOf<std::vector<int>>(Arbitrary<int>()).WithMaxSize(5)
//
template <typename T, typename Inner>
typename std::enable_if<detail::is_associative_container<T>::value,
                        AssociativeContainerOf<T, Inner>>::type
ContainerOf(Inner&& inner) {
    return AssociativeContainerOf<T, Inner>(std::move(inner));
}

template <typename T, typename Inner>
typename std::enable_if<!detail::is_associative_container<T>::value &&
                            detail::is_container<T>::value,
                        SequenceContainerOf<T, Inner>>::type
ContainerOf(Inner&& inner) {
    return SequenceContainerOf<T, Inner>(std::move(inner));
}

// We can also support with a template template parameter, so that we can use
// the name of the container instead of the complete type. In such case,
// the ValueType of the inner domain should be passed into the container
//
//   ContainerOf<std::vector>(Positive<int>()).WithSize(3);
//
template <template <typename, typename...> class T, typename... Inner,
          typename C = T<typename Inner::ValueType...>>
auto ContainerOf(Inner... inner) -> decltype(ContainerOf<C>(std::move(inner)...)) {
    return ContainerOf<C>(std::move(inner)...);
}


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once




#include <tuple>

#if defined(ZEROERR_ENABLE_PFR) && (ZEROERR_CXX_STANDARD >= 14)
#include "pfr.hpp"
#endif

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {


/**
 * @brief AggregateOf is a domain that combines multiple inner domains into a tuple or aggregate type
 * 
 * @tparam T The aggregate type to generate (e.g. struct or tuple)
 * @tparam Inner The inner domain types that will generate each field
 * 
 * This domain allows generating structured data by composing multiple inner domains.
 * Each inner domain generates one field of the aggregate type.
 * 
 * Example:
 * ```cpp
 * struct Point {
 *   int x;
 *   int y; 
 * };
 * 
 * auto domain = AggregateOf<Point>(
 *   InRange(0, 100),  // Domain for x
 *   InRange(0, 100)   // Domain for y
 * );
 * ```
 */

template <typename T, typename... Inner>
class AggregateOf : public Domain<T, std::tuple<typename Inner::CorpusType...>> {
public:
    using ValueType  = T;
    using CorpusType = std::tuple<typename Inner::CorpusType...>;

private:
    template <unsigned... I>
    inline CorpusType get_random(Rng& rng, detail::seq<I...>) const {
        return CorpusType{std::get<I>(inner_domains).GetRandomCorpus(rng)...};
    }

    template <unsigned... I>
    inline ValueType get_tuple(const CorpusType& v, detail::seq<I...>) const {
        return ValueType{std::get<I>(inner_domains).GetValue(std::get<I>(v))...};
    }

    template <unsigned... I>
    inline CorpusType from_tuple(const ValueType& v, detail::seq<I...>) const {
        return CorpusType{std::get<I>(inner_domains).FromValue(std::get<I>(v))...};
    }

    std::tuple<Inner...> inner_domains;

public:
    AggregateOf(Inner&&... inner) : inner_domains(std::make_tuple(std::move(inner)...)) {}

    CorpusType GetRandomCorpus(Rng& rng) const override {
        return get_random(rng, detail::gen_seq<sizeof...(Inner)>{});
    }

    ValueType GetValue(const CorpusType& v) const override {
        return get_tuple(v, detail::gen_seq<sizeof...(Inner)>{});
    }

    CorpusType FromValue(const ValueType& v) const override {
        return from_tuple(v, detail::gen_seq<sizeof...(Inner)>{});
    }

    struct GetTupleDomainMapValue {
        Rng& rng;
        bool only_shrink;

        template <typename D, typename H>
        void operator()(const D& domain, H& value) {
            domain.Mutate(rng, value, only_shrink);
        }
    };

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        unsigned               index = rng.bounded(sizeof...(Inner));
        GetTupleDomainMapValue visitor{rng, only_shrink};
        detail::visit2_at(inner_domains, v, index, visitor);
    }
};


#ifdef ZEROERR_ENABLE_PFR

template <typename T, typename... Inner>
AggregateOfImpl<T, Inner...> StructOf(Inner&&... inner) {
    return AggregateOfImpl<T, Inner...>(std::move(inner)...);
}

#endif

template <typename... Inner>
AggregateOf<std::tuple<typename Inner::ValueType...>, Inner...> TupleOf(Inner&&... inner) {
    return AggregateOf<std::tuple<typename Inner::ValueType...>, Inner...>(std::move(inner)...);
}

template <typename K, typename V>
AggregateOf<std::pair<typename K::ValueType, typename V::ValueType>, K, V> PairOf(K&& k, V&& v) {
    return AggregateOf<std::pair<typename K::ValueType, typename V::ValueType>, K, V>(std::move(k),
                                                                                      std::move(v));
}


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once








#include <limits>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

namespace zeroerr {


/**
 * @brief Arbitrary is a domain that generates random values of a given type
 * 
 * @tparam T The type to generate values for
 * @tparam N Template parameter for SFINAE-based specialization selection
 * 
 * This domain provides default random value generation for common types.
 * It uses template specialization to handle different types appropriately.
 * 
 * The base template is empty and specializations are provided for:
 * - bool
 * - unsigned integers 
 * - signed integers
 * - floating point numbers
 * - strings
 * - containers
 * 
 * Example:
 * ```cpp
 * auto domain = Arbitrary<int>(); // Generates random integers
 * auto domain = Arbitrary<std::string>(); // Generates random strings
 * ```
 */

template <typename T, unsigned N = 2, typename = void>
class Arbitrary : public Arbitrary<T, N-1> {};

template <typename T>
struct Arbitrary <T, 0> {
    static_assert(detail::always_false<T>::value, "No Arbitrary specialization for this type");
};

template <>
class Arbitrary<bool> : public DomainConvertable<bool> {
public:
    using ValueType  = bool;
    using CorpusType = bool;

    CorpusType GetRandomCorpus(Rng& rng) const override { return rng.bounded(2); }

    void Mutate(Rng&, CorpusType& v, bool) const override { v = !v; }
};


template <typename T>
using is_unsigned_int =
    typename std::enable_if<std::is_integral<T>::value && !std::numeric_limits<T>::is_signed,
                            void>::type;
template <typename T>
class Arbitrary<T, 2, is_unsigned_int<T>> : public DomainConvertable<T> {
public:
    using ValueType  = T;
    using CorpusType = T;

    CorpusType GetRandomCorpus(Rng& rng) const override { return static_cast<T>(rng.bounded(100)); }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        v = static_cast<T>(rng.bounded(100));
    }
};

template <typename T>
using is_signed_int =
    typename std::enable_if<std::is_integral<T>::value && std::numeric_limits<T>::is_signed,
                            void>::type;

template <typename T>
class Arbitrary<T, 2, is_signed_int<T>> : public DomainConvertable<T> {
public:
    using ValueType  = T;
    using CorpusType = T;

    CorpusType GetRandomCorpus(Rng& rng) const override { return static_cast<T>(rng.bounded(100)); }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        v = static_cast<T>(rng.bounded(100));
        v -= 50;
    }
};

template <typename T>
using is_float_point = typename std::enable_if<std::is_floating_point<T>::value, void>::type;
template <typename T>
class Arbitrary<T, 2, is_float_point<T>> : public DomainConvertable<T> {
public:
    using ValueType  = T;
    using CorpusType = T;

    CorpusType GetRandomCorpus(Rng& rng) const override {
        return static_cast<T>(rng.bounded(1000));
    }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        v = static_cast<T>(rng.bounded(1000));
    }
};


template <typename T>
using is_string =
    typename std::enable_if<detail::is_specialization<T, std::basic_string>::value>::type;

template <typename T>
class Arbitrary<T, 2, is_string<T>> : public Domain<T, std::vector<typename T::value_type>> {
    Arbitrary<std::vector<typename T::value_type>> impl;

public:
    using ValueType  = T;
    using CorpusType = std::vector<typename T::value_type>;

    ValueType GetValue(const CorpusType& v) const override { return ValueType(v.begin(), v.end()); }
    CorpusType FromValue(const ValueType& v) const override {
        return CorpusType(v.begin(), v.end());
    }

    CorpusType GetRandomCorpus(Rng& rng) const override { return impl.GetRandomCorpus(rng); }

    void Mutate(Rng& rng, CorpusType& v, bool only_shrink) const override {
        impl.Mutate(rng, v, only_shrink);
    }
};

template <typename T>
using is_modifiable = typename std::enable_if<detail::is_modifiable<T>::value>::type;

template <typename T>
class Arbitrary<T, 1, is_modifiable<T>>
    : public SequenceContainerOf<T, Arbitrary<typename T::value_type>> {
public:
    Arbitrary()
        : SequenceContainerOf<T, Arbitrary<typename T::value_type>>(
              Arbitrary<typename T::value_type>{}) {}
};

template <typename T, typename U>
class Arbitrary<std::pair<T, U>, 1>
    : public AggregateOf<
          std::pair<typename std::remove_const<T>::type, typename std::remove_const<U>::type>> {};


template <typename... T>
class Arbitrary<std::tuple<T...>, 1>
    : public AggregateOf<std::tuple<typename std::remove_const<T>::type...>> {};

template <typename T>
class Arbitrary<const T, 2> : public Arbitrary<T> {};

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
/*
 * This benchmark component is modified from nanobench by Martin Ankerl
 * https://github.com/martinus/nanobench
 */

#pragma once


#include <chrono>
#include <cstdint>
#include <string>
#include <vector>


ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

#define ZEROERR_CREATE_BENCHMARK_FUNC(function, name, ...)                              \
    static void                     function(zeroerr::TestContext*);                    \
    static zeroerr::detail::regTest ZEROERR_NAMEGEN(_zeroerr_reg)(                      \
        {name, __FILE__, __LINE__, function, {__VA_ARGS__}}, zeroerr::TestType::bench); \
    static void function(ZEROERR_UNUSED(zeroerr::TestContext* _ZEROERR_TEST_CONTEXT))

#define BENCHMARK(name, ...) \
    ZEROERR_CREATE_BENCHMARK_FUNC(ZEROERR_NAMEGEN(_zeroerr_benchmark), name, __VA_ARGS__)


namespace zeroerr {

/**
 * @brief PerfCountSet is a set of performance counters.
 */
template <typename T>
struct PerfCountSet {
    T iterations{};
    T data[7]{};

    T& timeElapsed() { return data[0]; }
    T& pageFaults() { return data[1]; }
    T& cpuCycles() { return data[2]; }
    T& contextSwitches() { return data[3]; }
    T& instructions() { return data[4]; }
    T& branchInstructions() { return data[5]; }
    T& branchMisses() { return data[6]; }
};

using Clock = std::conditional<std::chrono::high_resolution_clock::is_steady,
                               std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;

namespace detail {
struct LinuxPerformanceCounter;
struct WindowsPerformanceCounter;
}  // namespace detail

/**
 * @brief PerformanceCounter is a class to measure the performance of a function.
 */
struct PerformanceCounter {
    PerformanceCounter();
    ~PerformanceCounter();

    void beginMeasure();
    void endMeasure();
    void updateResults(uint64_t numIters);

    const PerfCountSet<uint64_t>& val() const noexcept { return _val; }
    PerfCountSet<bool>            has() const noexcept { return _has; }

    static PerformanceCounter& inst();

    Clock::duration elapsed;

protected:
    Clock::time_point      _start;
    PerfCountSet<uint64_t> _val;
    PerfCountSet<bool>     _has;

    detail::LinuxPerformanceCounter*   _perf    = nullptr;
    detail::WindowsPerformanceCounter* win_perf = nullptr;
};

/**
 * @brief BenchResult is a result of running the benchmark.
 */
struct BenchResult {
    enum Measure {
        time_elapsed        = 1 << 0,
        iterations          = 1 << 1,
        page_faults         = 1 << 2,
        cpu_cycles          = 1 << 3,
        context_switches    = 1 << 4,
        instructions        = 1 << 5,
        branch_instructions = 1 << 6,
        branch_misses       = 1 << 7,
        all                 = (1 << 8) - 1,
    };
    std::string                       name;
    std::vector<PerfCountSet<double>> epoch_details;
    PerfCountSet<bool>                has;

    PerfCountSet<double> average() const;
    PerfCountSet<double> min() const;
    PerfCountSet<double> max() const;
    PerfCountSet<double> mean() const;
};

struct Benchmark;
struct BenchState;
BenchState* createBenchState(Benchmark& benchmark);
void        destroyBenchState(BenchState* state);

size_t getNumIter(BenchState* state);
void   runIteration(BenchState* state);
void   moveResult(BenchState* state, std::string name);


/**
 * @brief Benchmark create a core object for configuration of a benchmark.
 * This class is a driver to run multiple times of a benchmark. Each time of a run will generate a
 * row of data. Report will print the data in console.
 */
struct Benchmark {
    std::string title          = "benchmark";
    const char* op_unit        = "op";
    const char* time_unit      = "ns";
    uint64_t    epochs         = 10;
    uint64_t    warmup         = 0;
    uint64_t    iter_per_epoch = 0;

    using ns   = std::chrono::nanoseconds;
    using ms   = std::chrono::milliseconds;
    using time = ns;

    time mMaxEpochTime = ms(100);
    time mMinEpochTime = ms(1);

    uint64_t minimalResolutionMutipler = 1000;

    Benchmark(std::string title) { this->title = title; }


    template <typename Op>
    Benchmark& run(std::string name, Op&& op) {
        auto* s  = createBenchState(*this);
        auto& pc = PerformanceCounter::inst();
        while (auto n = getNumIter(s)) {
            pc.beginMeasure();
            while (n-- > 0) op();
            pc.endMeasure();
            runIteration(s);
        }
        moveResult(s, name);
        return *this;
    }

    template <typename Op>
    Benchmark& run(Op&& op) {
        return run("", std::forward<Op>(op));
    }

    std::vector<BenchResult> result;
    void                     report();
};


namespace detail {

#if defined(_MSC_VER)
void doNotOptimizeAwaySink(const void*);

template <typename T>
void doNotOptimizeAway(const T& val) {
    doNotOptimizeAwaySink(&val);
}

#else

// These assembly magic is directly from what Google Benchmark is doing. I have previously used
// what facebook's folly was doing, but this seemed to have compilation problems in some cases.
// Google Benchmark seemed to be the most well tested anyways. see
// https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307
template <typename T>
void doNotOptimizeAway(const T& val) {
    // NOLINTNEXTLINE(hicpp-no-assembler)
    asm volatile("" : : "r,m"(val) : "memory");
}

template <typename T>
void doNotOptimizeAway(T& val) {
#if defined(__clang__)
    // NOLINTNEXTLINE(hicpp-no-assembler)
    asm volatile("" : "+r,m"(val) : : "memory");
#else
    // NOLINTNEXTLINE(hicpp-no-assembler)
    asm volatile("" : "+m,r"(val) : : "memory");
#endif
}
#endif

}  // namespace detail


/**
 * @brief Makes sure none of the given arguments are optimized away by the compiler.
 *
 * @tparam Arg Type of the argument that shouldn't be optimized away.
 * @param arg The input that we mark as being used, even though we don't do anything with it.
 */
template <typename Arg>
void doNotOptimizeAway(Arg&& arg) {
    detail::doNotOptimizeAway(std::forward<Arg>(arg));
}

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once








#include <cstdint>
#include <exception>
#include <iostream>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH


// This macro will be redefined in the log.h header, so the global context variable could be
// envolved only when we use log.h at the same time. If you didn't use log.h, this is still a
// header-only library.
#ifndef ZEROERR_G_CONTEXT_SCOPE
#define ZEROERR_G_CONTEXT_SCOPE(x)
#endif


/**
 * @brief Default printer for assertion messages
 * 
 * This macro defines the default printer for assertion messages.
 * It prints the assertion message in different colors based on the assertion level.
 * 
 * The macro can be overridden by defining ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER before
 * including this header. (Or undefine it and implement your own printer.)
 */
#ifndef ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER
#define ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, ...)                    \
    do {                                                                          \
        if (cond) {                                                               \
            switch (zeroerr::assert_level::ZEROERR_CAT(level, _l)) {              \
                case zeroerr::assert_level::ZEROERR_WARN_l:                       \
                    std::cerr << zeroerr::FgYellow << "WARN" << zeroerr::Reset;   \
                    break;                                                        \
                case zeroerr::assert_level::ZEROERR_ERROR_l:                      \
                    std::cerr << zeroerr::FgRed << "ERROR" << zeroerr::Reset;     \
                    break;                                                        \
                case zeroerr::assert_level::ZEROERR_FATAL_l:                      \
                    std::cerr << zeroerr::FgMagenta << "FATAL" << zeroerr::Reset; \
                    break;                                                        \
            }                                                                     \
            std::cerr << zeroerr::format(__VA_ARGS__) << std::endl;               \
        }                                                                         \
    } while (0)
#endif

#ifdef ZEROERR_OS_WINDOWS
#define ZEROERR_PRINT_ASSERT(cond, level, pattern, ...)                                    \
    ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, " Assertion Failed:\n{msg}" pattern, \
                                         assertion_data.log(), __VA_ARGS__)
#else
ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wgnu-zero-variadic-macro-arguments")
#define ZEROERR_PRINT_ASSERT(cond, level, pattern, ...)                                    \
    ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, " Assertion Failed:\n{msg}" pattern, \
                                         assertion_data.log(), ##__VA_ARGS__)
ZEROERR_CLANG_SUPPRESS_WARNING_POP
#endif

#define ZEROERR_ASSERT_EXP(cond, level, expect_throw, is_false, ...)                             \
    ZEROERR_FUNC_SCOPE_BEGIN {                                                                   \
        zeroerr::assert_info info{zeroerr::assert_level::ZEROERR_CAT(level, _l),                 \
                                  zeroerr::assert_throw::expect_throw, is_false};                \
                                                                                                 \
        zeroerr::AssertionData assertion_data(__FILE__, __LINE__, #cond, info);                  \
        try {                                                                                    \
            assertion_data.setResult(zeroerr::ExpressionDecomposer() << cond);                   \
        } catch (const std::exception& e) {                                                      \
            assertion_data.setException(e);                                                      \
        }                                                                                        \
        zeroerr::detail::context_helper<                                                         \
            decltype(_ZEROERR_TEST_CONTEXT),                                                     \
            std::is_same<decltype(_ZEROERR_TEST_CONTEXT),                                        \
                         const bool>::value>::setContext(assertion_data, _ZEROERR_TEST_CONTEXT); \
        ZEROERR_PRINT_ASSERT(assertion_data.passed == false, level, __VA_ARGS__);                \
        if (false) debug_break();                                                                \
        assertion_data();                                                                        \
        ZEROERR_FUNC_SCOPE_RET(assertion_data.passed);                                           \
    }                                                                                            \
    ZEROERR_FUNC_SCOPE_END


#define ZEROERR_ASSERT_CMP(lhs, op, rhs, level, expect_throw, is_false, ...)                     \
    ZEROERR_FUNC_SCOPE_BEGIN {                                                                   \
        zeroerr::assert_info info{zeroerr::assert_level::ZEROERR_CAT(level, _l),                 \
                                  zeroerr::assert_throw::expect_throw, is_false};                \
                                                                                                 \
        zeroerr::Printer print;                                                                  \
        print.isQuoted = false;                                                                  \
        zeroerr::AssertionData assertion_data(__FILE__, __LINE__, #lhs " " #op " " #rhs, info);  \
        try {                                                                                    \
            assertion_data.setResult({(lhs)op(rhs), print(lhs, #op, rhs)});                      \
        } catch (const std::exception& e) {                                                      \
            assertion_data.setException(e);                                                      \
        }                                                                                        \
        zeroerr::detail::context_helper<                                                         \
            decltype(_ZEROERR_TEST_CONTEXT),                                                     \
            std::is_same<decltype(_ZEROERR_TEST_CONTEXT),                                        \
                         const bool>::value>::setContext(assertion_data, _ZEROERR_TEST_CONTEXT); \
        ZEROERR_PRINT_ASSERT(assertion_data.passed == false, level, __VA_ARGS__);                \
        if (false) debug_break();                                                                \
        assertion_data();                                                                        \
        ZEROERR_FUNC_SCOPE_RET(assertion_data.passed);                                           \
    }                                                                                            \
    ZEROERR_FUNC_SCOPE_END


#ifdef ZEROERR_NO_ASSERT

#define CHECK(...)
#define CHECK_NOT(...)
#define CHECK_THROWS(...)
#define REQUIRE(...)
#define REQUIRE_NOT(...)
#define REQUIRE_THROWS(...)
#define ASSERT(...)
#define ASSERT_NOT(...)
#define ASSERT_THROWS(...)

#define CHECK_EQ(...)
#define CHECK_NE(...)
#define CHECK_LT(...)
#define CHECK_LE(...)
#define CHECK_GT(...)
#define CHECK_GE(...)

#define REQUIRE_EQ(...)
#define REQUIRE_NE(...)
#define REQUIRE_LT(...)
#define REQUIRE_LE(...)
#define REQUIRE_GT(...)
#define REQUIRE_GE(...)

#define ASSERT_EQ(...)
#define ASSERT_NE(...)
#define ASSERT_LT(...)
#define ASSERT_LE(...)
#define ASSERT_GT(...)
#define ASSERT_GE(...)

#else
// clang-format off
ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wgnu-zero-variadic-macro-arguments")

#define ZEROERR_CHECK(cond, ...)          ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, no_throw, false, __VA_ARGS__))
#define ZEROERR_CHECK_NOT(cond, ...)      ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, no_throw, true, __VA_ARGS__))
#define ZEROERR_CHECK_THROWS(cond, ...)   ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, throws, false, __VA_ARGS__))
#define ZEROERR_REQUIRE(cond, ...)        ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, no_throw, false, __VA_ARGS__))
#define ZEROERR_REQUIRE_NOT(cond, ...)    ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, no_throw, true, __VA_ARGS__))
#define ZEROERR_REQUIRE_THROWS(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, throws, false, __VA_ARGS__))
#define ZEROERR_ASSERT(cond, ...)         ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, no_throw, false, __VA_ARGS__))
#define ZEROERR_ASSERT_NOT(cond, ...)     ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, no_throw, true, __VA_ARGS__))
#define ZEROERR_ASSERT_THROWS(cond, ...)  ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, throws, false, __VA_ARGS__))

#define CHECK(...)          ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_NOT(...)      ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_THROWS(...)   ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE(...)        ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_NOT(...)    ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_THROWS(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT(...)         ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_NOT(...)     ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_THROWS(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP

#define ZEROERR_CHECK_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
#define ZEROERR_CHECK_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
#define ZEROERR_CHECK_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
#define ZEROERR_CHECK_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
#define ZEROERR_CHECK_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
#define ZEROERR_CHECK_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)

#define ZEROERR_REQUIRE_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
#define ZEROERR_REQUIRE_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
#define ZEROERR_REQUIRE_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
#define ZEROERR_REQUIRE_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
#define ZEROERR_REQUIRE_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
#define ZEROERR_REQUIRE_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)

#define ZEROERR_ASSERT_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
#define ZEROERR_ASSERT_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
#define ZEROERR_ASSERT_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
#define ZEROERR_ASSERT_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
#define ZEROERR_ASSERT_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
#define ZEROERR_ASSERT_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)

#define CHECK_EQ(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_NE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_LT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_LE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_GT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define CHECK_GE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_EQ(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_NE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_LT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_LE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_GT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define REQUIRE_GE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_EQ(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_NE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_LT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_LE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_GT(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ASSERT_GE(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP

ZEROERR_CLANG_SUPPRESS_WARNING_POP
// clang-format on
#endif


// This symbol must be in the global namespace or anonymous namespace
// used for checking the assert is inside testing or not
namespace {
constexpr bool _ZEROERR_TEST_CONTEXT = false;
}  // namespace


namespace zeroerr {

enum class assert_level : uint8_t { ZEROERR_WARN_l, ZEROERR_ERROR_l, ZEROERR_FATAL_l };
enum class assert_throw : uint8_t { no_throw, throws, throws_as };
enum class assert_cmp : uint8_t { eq, ne, gt, ge, lt, le };

/**
 * @brief This is a one-byte assert info struct, which is used to collect the meta info of an
 * assertion
 */
struct assert_info {
    assert_level level      : 2;
    assert_throw throw_type : 2;
    bool         is_false   : 1;
};


/**
 * @brief AssertionData is a struct that contains all the information of an assertion.
 *       It will be thrown as an exception when the assertion failed.
 */
struct AssertionData : std::exception {
    const char* file;     // file name
    unsigned    line;     // line number
    assert_info info;     // assert info
    bool        passed;   // if the assertion passed
    std::string message;  // the message of the assertion
    std::string cond;     // the condition of the assertion

    AssertionData(const char* file, unsigned line, const char* cond, assert_info info)
        : file(file), line(line), info(info) {
        static std::string pattern = "zeroerr::ExpressionDecomposer() << ";
        std::string        cond_str(cond);
        size_t             pos = cond_str.find(pattern);
        if (pos != std::string::npos) cond_str.replace(pos, pos + pattern.size(), "");
        this->cond = cond_str;
    }

    void setResult(ExprResult&& result) {
        ExprResult r(std::move(result));
        if (info.is_false)
            passed = !r.res;
        else
            passed = r.res;
        message = r.decomp;
    }

    void setException(const std::exception& e) {
        passed  = info.throw_type == assert_throw::throws;
        message = e.what();
    }

    std::string log() {
        std::stringstream ss;
        ss << "    " << cond << "  expands to  " << message << std::endl;
        ss << Dim << "(" << file << ":" << line << ")" << Reset << std::endl;
        return ss.str();
    }

    // throw the exception
    void operator()() {
        if (passed) return;
        if (shouldThrow()) throw *this;
    }

    bool shouldThrow() { return info.level != assert_level::ZEROERR_WARN_l; }
};

namespace detail {
// This struct is used for handle constexpr if in C++11
// https://stackoverflow.com/questions/43587405/constexpr-if-alternative
template <typename T, bool>
struct context_helper;

template <typename T>
struct context_helper<T, true> {
    static void setContext(AssertionData& data, T) {
        if (data.passed) return;
    }
};

template <typename T>
struct context_helper<T, false> {
    static void setContext(AssertionData& data, T ctx) {
        if (data.passed) {
            ctx->passed_as++;
            return;
        }
        switch (data.info.level) {
            case assert_level::ZEROERR_FATAL_l:
            case assert_level::ZEROERR_ERROR_l: ctx->failed_as++; break;
            case assert_level::ZEROERR_WARN_l:  ctx->warning_as++; break;
        }
    }
};
}  // namespace detail


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once





#include <iostream>
#include <tuple>  // for std::get and std::tie

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

#ifndef ZEROERR_DISABLE_DBG_MARCO
#define dbg(...) zeroerr::DebugExpr(__FILE__, __LINE__, __func__, #__VA_ARGS__, __VA_ARGS__)
#else
#define dbg(...) (__VA_ARGS__)
#endif

namespace zeroerr {

template <class T1, class... T>
struct last {
    using type = typename last<T...>::type;
};

template <class T1>
struct last<T1> {
    using type = T1;
};

/**
 * @brief get_last is a function to get the last argument of a variadic template.
 *        It is used by DebugExpr.
 */
template <typename... Args>
auto get_last(Args&&... args) -> typename last<Args...>::type {
    return std::get<sizeof...(Args) - 1>(std::tie(args...));
}


/**
 * @brief DebugExpr is a function to print any type of variable with its type name.
 *        It is used by dbg macro.
 */
template <typename... T>
auto DebugExpr(const char* file, unsigned line, const char* func, const char* exprs, T... t) ->
    typename last<T...>::type {
    std::string fileName(file);
    auto        p = fileName.find_last_of('/');
    if (p != std::string::npos) fileName = fileName.substr(p + 1);

    std::cerr << Dim << "[" << fileName << ":" << line << " " << func << "] " << Reset;
    std::cerr << FgCyan << exprs << Reset << " = ";
    Printer print(std::cerr);
    print.line_break = "";
    print(t...);
    std::cerr << " (" << FgGreen;
    std::string typenames[] = {print.type(t)...};
    for (unsigned i = 0; i < sizeof...(T); ++i) {
        if (i != 0) std::cerr << ", ";
        std::cerr << typenames[i];
    }
    std::cerr << Reset << ")" << std::endl;
    return get_last(t...);
}


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once


#include <sstream>
#include <string>

namespace zeroerr {


/**
 * @brief Format a string with arguments
 * @param fmt The format string
 * @param args The arguments
 * @return std::string The formatted string
 * 
 * This function is used to format a string with arguments. The format string
 * is a string with placeholders in the form of `{}`. You can pass any type of
 * arguments to this function and it will format the string accordingly.
 * 
 * Example:
 *    format("Hello, {name}!", "John") -> "Hello, John!"
 * 
 */
template <typename... T>
std::string format(const char* fmt, T... args) {
    std::stringstream ss;
    bool              parse_name = false;
    Printer           print;

    print.isQuoted         = false;
    print.isCompact        = true;
    print.line_break       = "";
    std::string str_args[] = {print(args)...};

    int j = 0;
    for (const char* i = fmt; *i != '\0'; i++) {
        switch (*i) {
            case '{': parse_name = true; break;
            case '}':
                parse_name = false;
                ss << str_args[j++];
                break;
            default:
                if (!parse_name) ss << *i;
                break;
        }
    }
    return ss.str();
}

}  // namespace zeroerr

#pragma once









#include <chrono>
#include <iosfwd>
#include <map>
#include <string>
#include <vector>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

extern const char* ZEROERR_LOG_CATEGORY;

namespace zeroerr {

// clang-format off
#define ZEROERR_INFO(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_INFO_(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ZEROERR_LOG(...)   ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_LOG_(LOG_l, __VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ZEROERR_WARN(...)  ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_LOG_(WARN_l, __VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ZEROERR_ERROR(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_LOG_(ERROR_l, __VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
#define ZEROERR_FATAL(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_LOG_(FATAL_l, __VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
// clang-format on

#ifdef ZEROERR_USE_SHORT_LOG_MACRO

#ifdef INFO
#undef INFO
#endif

#ifdef LOG
#undef LOG
#endif

#ifdef WARN
#undef WARN
#endif

#ifdef ERR
#undef ERR
#endif

#ifdef FATAL
#undef FATAL
#endif

#ifdef VERBOSE
#undef VERBOSE
#endif

#define INFO(...)  ZEROERR_INFO(__VA_ARGS__)
#define LOG(...)   ZEROERR_LOG(__VA_ARGS__)
#define WARN(...)  ZEROERR_WARN(__VA_ARGS__)
#define ERR(...)   ZEROERR_ERROR(__VA_ARGS__)
#define FATAL(...) ZEROERR_FATAL(__VA_ARGS__)
#define VERBOSE(v) ZEROERR_VERBOSE(v)

#define LOG_GET(func, id, name, type) ZEROERR_LOG_GET(func, id, name, type)

#endif  // ZEROERR_USE_SHORT_LOG_MACRO

#define ZEROERR_LOG_IF(condition, ACTION, ...) \
    do {                                       \
        if (condition) ACTION(__VA_ARGS__);    \
    } while (0)


#define INFO_IF(cond, ...)  ZEROERR_LOG_IF(cond, ZEROERR_INFO, __VA_ARGS__)
#define LOG_IF(cond, ...)   ZEROERR_LOG_IF(cond, ZEROERR_LOG, __VA_ARGS__)
#define WARN_IF(cond, ...)  ZEROERR_LOG_IF(cond, ZEROERR_WARN, __VA_ARGS__)
#define ERR_IF(cond, ...)   ZEROERR_LOG_IF(cond, ZEROERR_ERROR, __VA_ARGS__)
#define FATAL_IF(cond, ...) ZEROERR_LOG_IF(cond, ZEROERR_FATAL, __VA_ARGS__)


#define ZEROERR_LOG_EVERY_(n, ACTION, ...) \
    do {                                   \
        static unsigned counter = 0;       \
        if (counter == 0) {                \
            counter = n;                   \
            ACTION(__VA_ARGS__);           \
        }                                  \
        --counter;                         \
    } while (0)


#define INFO_EVERY_(n, ...)  ZEROERR_LOG_EVERY_(n, ZEROERR_INFO, __VA_ARGS__)
#define LOG_EVERY_(n, ...)   ZEROERR_LOG_EVERY_(n, ZEROERR_LOG, __VA_ARGS__)
#define WARN_EVERY_(n, ...)  ZEROERR_LOG_EVERY_(n, ZEROERR_WARN, __VA_ARGS__)
#define ERR_EVERY_(n, ...)   ZEROERR_LOG_EVERY_(n, ZEROERR_ERROR, __VA_ARGS__)
#define FATAL_EVERY_(n, ...) ZEROERR_LOG_EVERY_(n, ZEROERR_FATAL, __VA_ARGS__)


#define ZEROERR_LOG_IF_EVERY_(n, cond, ACTION, ...) \
    do {                                            \
        if (cond) {                                 \
            static unsigned counter = 0;            \
            if (counter == 0) {                     \
                counter = n;                        \
                ACTION(__VA_ARGS__);                \
            }                                       \
            --counter;                              \
        }                                           \
    } while (0)

#define INFO_IF_EVERY_(n, cond, ...)  ZEROERR_LOG_IF_EVERY_(n, cond, ZEROERR_INFO, __VA_ARGS__)
#define LOG_IF_EVERY_(n, cond, ...)   ZEROERR_LOG_IF_EVERY_(n, cond, ZEROERR_LOG, __VA_ARGS__)
#define WARN_IF_EVERY_(n, cond, ...)  ZEROERR_LOG_IF_EVERY_(n, cond, ZEROERR_WARN, __VA_ARGS__)
#define ERR_IF_EVERY_(n, cond, ...)   ZEROERR_LOG_IF_EVERY_(n, cond, ZEROERR_ERROR, __VA_ARGS__)
#define FATAL_IF_EVERY_(n, cond, ...) ZEROERR_LOG_IF_EVERY_(n, cond, ZEROERR_FATAL, __VA_ARGS__)

#define ZEROERR_LOG_FIRST(cond, ACTION, ...) \
    do {                                     \
        static bool first = true;            \
        if (first && (cond)) {               \
            first = false;                   \
            ACTION(__VA_ARGS__);             \
        }                                    \
    } while (0)

#define INFO_FIRST(cond, ...)  ZEROERR_LOG_FIRST(cond, ZEROERR_INFO, __VA_ARGS__)
#define LOG_FIRST(cond, ...)   ZEROERR_LOG_FIRST(cond, ZEROERR_LOG, __VA_ARGS__)
#define WARN_FIRST(cond, ...)  ZEROERR_LOG_FIRST(cond, ZEROERR_WARN, __VA_ARGS__)
#define ERR_FIRST(cond, ...)   ZEROERR_LOG_FIRST(cond, ZEROERR_ERROR, __VA_ARGS__)
#define FATAL_FIRST(cond, ...) ZEROERR_LOG_FIRST(cond, ZEROERR_FATAL, __VA_ARGS__)

#define ZEROERR_LOG_FIRST_(n, cond, ACTION, ...) \
    do {                                         \
        static unsigned counter = n;             \
        if (counter && (cond)) {                 \
            counter--;                           \
            ACTION(__VA_ARGS__);                 \
        }                                        \
    } while (0)

#define INFO_FIRST_(n, cond, ...)  ZEROERR_LOG_FIRST_(n, cond, ZEROERR_INFO, __VA_ARGS__)
#define LOG_FIRST_(n, cond, ...)   ZEROERR_LOG_FIRST_(n, cond, ZEROERR_LOG, __VA_ARGS__)
#define WARN_FIRST_(n, cond, ...)  ZEROERR_LOG_FIRST_(n, cond, ZEROERR_WARN, __VA_ARGS__)
#define ERR_FIRST_(n, cond, ...)   ZEROERR_LOG_FIRST_(n, cond, ZEROERR_ERROR, __VA_ARGS__)
#define FATAL_FIRST_(n, cond, ...) ZEROERR_LOG_FIRST_(n, cond, ZEROERR_FATAL, __VA_ARGS__)

#ifdef _DEBUG
#define DLOG(ACTION, ...) ZEROERR_EXPAND(ACTION(__VA_ARGS__))
#else
#define DLOG(ACTION, ...)
#endif

extern int _ZEROERR_G_VERBOSE;

#define ZEROERR_VERBOSE(v) if (zeroerr::_ZEROERR_G_VERBOSE >= (v))

#define ZEROERR_LOG_(severity, message, ...)                                           \
    do {                                                                               \
        ZEROERR_G_CONTEXT_SCOPE(true);                                                 \
        auto msg = zeroerr::log(__VA_ARGS__);                                          \
                                                                                       \
        static zeroerr::LogInfo log_info{__FILE__,                                     \
                                         __func__,                                     \
                                         message,                                      \
                                         ZEROERR_LOG_CATEGORY,                         \
                                         __LINE__,                                     \
                                         msg.size,                                     \
                                         zeroerr::LogSeverity::severity};              \
        msg.log->info = &log_info;                                                     \
        if (msg.stream.getFlushMode() == zeroerr::LogStream::FlushMode::FLUSH_AT_ONCE) \
            msg.stream.flush();                                                        \
    } while (0)

#define ZEROERR_INFO_(...) \
    ZEROERR_INFO_IMPL(ZEROERR_NAMEGEN(_capture_), ZEROERR_NAMEGEN(_capture_), __VA_ARGS__)

#define ZEROERR_INFO_IMPL(mb_name, v_name, ...)                                \
    auto v_name = zeroerr::MakeContextScope([&](std::ostream& _capture_name) { \
        Printer print(_capture_name);                                          \
        print.isQuoted = false;                                                \
        print(__VA_ARGS__);                                                    \
    })

#ifdef ZEROERR_G_CONTEXT_SCOPE
#undef ZEROERR_G_CONTEXT_SCOPE
#endif

#define ZEROERR_G_CONTEXT_SCOPE(x)                                 \
    if (x) {                                                       \
        for (auto* i : zeroerr::_ZEROERR_G_CONTEXT_SCOPE_VECTOR) { \
            i->str(std::cerr);                                     \
        }                                                          \
    }

#ifdef ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER
#undef ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER
#endif

#define ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, ...) \
    ZEROERR_LOG_IF(cond, level, __VA_ARGS__)


// This macro can access the log in memory
#define ZEROERR_LOG_GET(func, id, name, type) \
    zeroerr::LogStream::getDefault().getLog<type>(#func, id, #name)


namespace detail {

template <typename T, unsigned... I>
std::string gen_str(const char* msg, const T& args, seq<I...>) {
    return format(msg, std::get<I>(args)...);
}

template <typename T>
std::string gen_str(const char* msg, const T&, seq<>) {
    return msg;
}

}  // namespace detail


enum LogSeverity {
    INFO_l,  // it will not write to file if no other log related
    LOG_l,
    WARN_l,
    ERROR_l,
    FATAL_l,  // it will contain a stack trace
};

/**
 * @brief LogInfo is a struct to store the meta data of the log message.
 * @details LogInfo is a struct to store the meta data of the log message.
 * It contains filename, function, message, category, line number, size, and severity.
 * Those data is initialized when the first log message is created using a static
 * local variable in the function where the log message is put.
 *
 * For example:
 *   void foo() {
 *     log("Hello, {name}!", "John");
 *   }
 *
 * The inner implementation could be considered as (not exactly
 * since message is allocated from a pool):
 *   void foo() {
 *      static LogInfo log_info{
 *          __FILE__, __func__, "Hello, {name}!",
 *          ZEROERR_LOG_CATEGORY,
 *          __LINE__,
 *          sizeof("Hello, world!"),
 *          LogSeverity::INFO_l);
 *      LogMessage* logdata = new LogMessageImpl<std::string>("John");
 *      logdata->info = &log_info;
 *    }
 */
struct LogInfo {
    const char*                filename;
    const char*                function;
    const char*                message;
    const char*                category;
    unsigned                   line;
    unsigned                   size;
    LogSeverity                severity;
    std::map<std::string, int> names;

    LogInfo(const char* filename, const char* function, const char* message, const char* category,
            unsigned line, unsigned size, LogSeverity severity);
};

struct LogMessage;
typedef std::string (*LogCustomCallback)(const LogMessage&, bool colorful);

/**
 * @brief set the log level
 */
extern void setLogLevel(LogSeverity level);

/**
 * @brief set the log category
 */
extern void setLogCategory(const char* categories);

/**
 * @brief set the log custom callback, this can support custom format of the log message
 */
extern void setLogCustomCallback(LogCustomCallback callback);

/**
 * @brief suspend the log to flush to the file
 */
extern void suspendLog();

/**
 * @brief resume the log to flush to the file
 */
extern void resumeLog();

/**
 * @brief LogMessage is a class to store the log message.
 * @details LogMessage is a class to store the log message and a base class
 * for all the messages implementation. You can create a log message with any
 * type of arguments and it will store the arguments in a tuple.
 * The log message can be converted to a string with the str() function.
 * You can also get the raw pointer of the arguments with the getRawLog() function.
 */
struct LogMessage {
    // time is assigned when the log message is created
    LogMessage() { time = std::chrono::system_clock::now(); }

    // convert the log message to a string
    virtual std::string str() const = 0;

    // get the raw data pointer of the field with the name
    virtual void* getRawLog(std::string name) const = 0;

    // a map of the data indexing by the field name
    // for example: log("print {i}", 1);
    // a map of {"i": "1"} will be returned
    virtual std::map<std::string, std::string> getData() const = 0;

    // meta data of this log message
    const LogInfo* info;

    // recorded wall time
    std::chrono::system_clock::time_point time;
};


/**
 * @brief LogMessageImpl is the implementation of the LogMessage.
 * @details LogMessageImpl is the implementation of the LogMessage. It stores
 * the arguments in a tuple and provides the str() function to convert the log
 * message to a string. All fields could be accessed by getRawLog() or getData().
 */
template <typename... T>
struct LogMessageImpl final : LogMessage {
    std::tuple<T...> args;
    LogMessageImpl(T... args) : LogMessage(), args(args...) {}

    std::string str() const override {
        return gen_str(info->message, args, detail::gen_seq<sizeof...(T)>{});
    }

    // This is a helper class to get the raw pointer of the tuple
    struct GetTuplePtr {
        void* ptr = nullptr;
        template <typename H>
        void operator()(H& v) {
            ptr = (void*)&v;
        }
    };

    void* getRawLog(std::string name) const override {
        GetTuplePtr f;
        detail::visit_at(args, info->names.at(name), f);
        return f.ptr;
    }

    struct PrintTupleData {
        std::map<std::string, std::string> data;
        Printer                            print;
        std::string                        name;

        PrintTupleData() : print() {
            print.isCompact  = true;
            print.line_break = "";
        }

        template <typename H>
        void operator()(H& v) {
            data[name] = print(v);
        }
    };

    std::map<std::string, std::string> getData() const override {
        PrintTupleData printer;
        for (auto it = info->names.begin(); it != info->names.end(); ++it) {
            printer.name = it->first;
            detail::visit_at(args, it->second, printer);
        }
        return printer.data;
    }
};

struct DataBlock;
class LogStream;

class Logger {
public:
    virtual ~Logger()              = default;
    virtual void flush(DataBlock*) = 0;
};

struct PushResult {
    LogMessage* log;
    unsigned    size;
    LogStream&  stream;
};

/**
 * @brief LogIterator is a class to iterate the log messages.
 * @details LogIterator is a class to iterate the log messages. You can also filter
 * the log messages by message, function name, and line number.
 *
 * An example of using LogIterator:
 *    for (int i = 0; i < 10; ++i)
 *      log("i = {i}", i);
 *
 *    LogIterator it = LogStream::getDefault().begin("Hello, world!");
 *    LogIterator end = LogStream::getDefault().end();
 *
 *    for (; it != end; ++it) {
 *      LogMessage& msg = *it;
 *      std::cout << msg.str() << std::endl;   // "i = {i}"
 *      std::cout << it.get<int>("i") << std::endl; // "0", "1", "2", ...
 *    }
 */
class LogIterator {
public:
    LogIterator() : p(nullptr), q(nullptr) {}
    LogIterator(LogStream& stream, std::string message = "", std::string function_name = "",
                int line = -1);
    LogIterator(const LogIterator& rhs) : p(rhs.p), q(rhs.q) {}
    LogIterator& operator=(const LogIterator& rhs) {
        p = rhs.p;
        q = rhs.q;
        return *this;
    }

    LogIterator& operator++();
    LogIterator  operator++(int) {
        LogIterator tmp = *this;
        ++*this;
        return tmp;
    }

    template <typename T>
    T get(std::string name) {
        void* data = q->getRawLog(name);
        if (data) return *(T*)(data);
        return T{};
    }

    bool operator==(const LogIterator& rhs) const { return p == rhs.p && q == rhs.q; }
    bool operator!=(const LogIterator& rhs) const { return !(*this == rhs); }

    LogMessage& get() const { return *q; }
    LogMessage& operator*() const { return *q; }
    LogMessage* operator->() const { return q; }

    void check_at_safe_pos();

    friend class LogStream;

protected:
    bool check_filter();
    void next();

    DataBlock*  p;
    LogMessage* q;

    std::string function_name_filter;
    std::string message_filter;
    int         line_filter = -1;
};

/**
 * @brief LogStream is a class to manage the log messages.
 * @details LogStream is a class to manage the log messages. It can be used to
 * create log messages and push them to the logger. A default LogStream is
 * created when the first time you call getDefault() function (or first log happens).
 * You can also adjust the way to flush the messages and how the log messages are
 * written to the log file.
 */
class LogStream {
public:
    LogStream();
    virtual ~LogStream();

    enum FlushMode { FLUSH_AT_ONCE, FLUSH_WHEN_FULL, FLUSH_MANUALLY };
    enum LogMode { ASYNC, SYNC };
    enum DirMode {
        SINGLE_FILE       = 0,
        DAILY_FILE        = 1,
        SPLIT_BY_SEVERITY = 1 << 1,
        SPLIT_BY_CATEGORY = 1 << 2
    };


    /**
     * @brief push a log message to the stream
     * @tparam T The types of the arguments
     * @param args The arguments
     * @return PushResult The result of the push
     *
     * This function is used to push a log message to the stream. You can pass
     * any type of arguments to this function and it will create a log message
     * with the arguments. The log message is not written to the log file until
     * the stream is flushed.
     *
     * The log message is structured as a tuple of the arguments in the inner
     * implementation class LogMessageImpl. After the log message is created, it
     * used type erasure to return a LogMessage pointer to the caller.
     *
     * The stored data type is determined by the to_store_type_t<T> template.
     * For all the string type in raw pointer like const char* or char[],
     * it will be converted to std::string.
     * All reference type (including right value reference) will be converted
     * to the original type.
     */
    template <typename... T>
    PushResult push(T&&... args) {
        // unsigned size = sizeof(LogMessageImpl<T...>);
        unsigned size = sizeof(LogMessageImpl<detail::to_store_type_t<T>...>);
        void*    p;
        if (use_lock_free)
            p = alloc_block_lockfree(size);
        else
            p = alloc_block(size);
        // LogMessage* msg = new (p) LogMessageImpl<T...>(std::forward<T>(args)...);
        LogMessage* msg = new (p) LogMessageImpl<detail::to_store_type_t<T>...>(args...);
        return {msg, size, *this};
    }


    /**
     * @brief get a log message from the stream
     * @tparam T The type of the log message
     * @param func The function name of the log message
     * @param line The line number of the log message
     * @param name The name of field you want to get
     *
     * This function is used to get a log message from the stream and extract
     * the field with the name. The function will return the field with the type
     * T. However, this type must be specified by the caller.
     */
    template <typename T>
    T getLog(std::string func, unsigned line, std::string name) {
        void* data = getRawLog(func, line, name);
        if (data) return *(T*)(data);
        return T{};
    }

    /**
     * @brief get a log message from the stream
     * @tparam T The type of the log message
     * @param func The function name of the log message
     * @param msg The message of the log message
     * @param name The name of field you want to get
     *
     * This function is used to get a log message from the stream and extract
     * the field with the name. The function will return the field with the type
     * T. However, this type must be specified by the caller.
     */
    template <typename T>
    T getLog(std::string func, std::string msg, std::string name) {
        void* data = getRawLog(func, msg, name);
        if (data) return *(T*)(data);
        return T{};
    }

    LogIterator begin(std::string message = "", std::string function_name = "", int line = -1) {
        return LogIterator(*this, message, function_name, line);
    }
    LogIterator end() { return LogIterator(); }
    LogIterator current(std::string message = "", std::string function_name = "", int line = -1);

    void flush();
    void setFileLogger(std::string name, DirMode mode1 = SINGLE_FILE, DirMode mode2 = SINGLE_FILE,
                       DirMode mode3 = SINGLE_FILE);
    void setStdoutLogger();
    void setStderrLogger();

    static LogStream& getDefault();

    void setFlushAtOnce() { flush_mode = FLUSH_AT_ONCE; }
    void setFlushWhenFull() { flush_mode = FLUSH_WHEN_FULL; }
    void setFlushManually() { flush_mode = FLUSH_MANUALLY; }
    void setAsyncLog() { log_mode = ASYNC; }
    void setSyncLog() { log_mode = SYNC; }

    FlushMode getFlushMode() const { return flush_mode; }
    void      setFlushMode(FlushMode mode) { flush_mode = mode; }
    LogMode   getLogMode() const { return log_mode; }
    void      setLogMode(LogMode mode) { log_mode = mode; }

    bool use_lock_free = true;

    friend class LogIterator;

private:
    DataBlock *first, *prepare;
    ZEROERR_ATOMIC(DataBlock*) m_last;
    Logger*   logger     = nullptr;
    FlushMode flush_mode = FLUSH_AT_ONCE;
    LogMode   log_mode   = SYNC;
#ifndef ZEROERR_NO_THREAD_SAFE
    std::mutex* mutex;
#endif

    // The implementation of alloc objects by giving a size
    void* alloc_block(unsigned size);
    void* alloc_block_lockfree(unsigned size);

    // The implementation of getLog which returns a raw pointer
    // This way can reduce the overhead of code generation by template
    void* getRawLog(std::string func, unsigned line, std::string name);
    void* getRawLog(std::string func, std::string msg, std::string name);
};


template <typename... T>
PushResult log(T&&... args) {
    return LogStream::getDefault().push(std::forward<T>(args)...);
}

template <typename... T>
PushResult log(LogStream& stream, T&&... args) {
    return stream.push(std::forward<T>(args)...);
}


/**
 * @brief ContextScope is a helper class created in each basic block where you use INFO().
 * The context scope can has lazy evaluated function F(std::ostream&) that is called when the
 * assertation is failed
 */
class IContextScope {
public:
    virtual void str(std::ostream& os) const = 0;
};

extern thread_local std::vector<IContextScope*> _ZEROERR_G_CONTEXT_SCOPE_VECTOR;

template <typename F>
class ContextScope : public IContextScope {
public:
    ContextScope(F f) : f_(f) { _ZEROERR_G_CONTEXT_SCOPE_VECTOR.push_back(this); }
    ~ContextScope() { _ZEROERR_G_CONTEXT_SCOPE_VECTOR.pop_back(); }

    virtual void str(std::ostream& os) const override { return f_(os); }

protected:
    F f_;
};

template <typename F>
ContextScope<F> MakeContextScope(const F& f) {
    return ContextScope<F>(f);
}


}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once





#include <ostream>
#include <string>
#include <vector>

namespace zeroerr {

/**
 * @brief Card defines a display range in the console.
 */
struct Card {
    Card() : title(), width(0), height(0) {}
    Card(std::string title) : title(title), width(0), height(0) {}
    Card(unsigned width, unsigned height) : title(), width(width), height(height) {}
    Card(std::string title, unsigned width, unsigned height)
        : title(title), width(width), height(height) {}
    std::string title;
    unsigned    width, height;

    void show(std::ostream& os, std::string str);
};

/**
 * @brief Table is used to generate a table with configurable style.
 */
class Table : public Card {
public:
    Table() : Card() {}
    Table(std::string title) : Card(title) {}
    Table(unsigned width, unsigned height) : Card(width, height) {}
    Table(std::string title, unsigned width, unsigned height) : Card(title, width, height) {}
    ~Table() {}

    /**
     * @brief Style is used to define the border style of the table.
     */
    struct Style {
        Style() {}
        Style(std::initializer_list<std::string> args) : m_args(args) {}
        std::vector<std::string> m_args;

        operator bool() const { return !m_args.empty(); }
    };

    static void registerStyle(std::string name, Style style);

    /**
     * @brief getStyle can load predefined style from the StyleManager.
     * @param name is the name of the style.
     * Available styles:
     *  "ascii"
     *  "ascii2"
     *  "ascii_double_head"
     *  "square"
     *  "square_double_head"
     *  "simple"
     *  "simple_head"
     *  "simple_heavy"
     *  "horizontal"
     *  "rounded"
     *  "heavy"
     *  "heavy_edge"
     *  "heavy_head"
     *  "double"
     *  "double_edge"
     *  "minimal"
     *  "minimal_heavy_head"
     *  "minimal_double_hea
     */
    static Style getStyle(std::string name);

    /**
     * @brief Config is used to configure the table style how it is displayed.
     */
    struct Config {
        bool show_tb_border;     // show top and bottom border
        bool show_lr_border;     // show left and right border
        bool show_header_split;  // show header split
        bool show_col_split;     // show column split
        bool show_row_split;     // show row split
        bool show_footer_split;  // show footer split
        Config()
            : show_tb_border(true),
              show_lr_border(true),
              show_header_split(true),
              show_col_split(true),
              show_row_split(true),
              show_footer_split(true) {}
    };

    /**
     * @brief str is used to generate the table string.
     * @param config decides how the table is displayed.
     * @param style decides the border style of the table.
     */
    std::string str(Config config = Config(), Style style = Table::getStyle("square_double_head"));

    void set_header(std::vector<std::string> _header) { header = _header; }
    void add_row(std::initializer_list<std::string> _row) { cells.push_back(_row); }

    template <typename T, typename... Args>
    void push_back(std::vector<std::string>& row, T&& t, Args&&... args) {
        _push_back(rank<2>{}, row, std::forward<T>(t));
        push_back(row, std::forward<Args>(args)...);
    }

    template <typename T>
    void push_back(std::vector<std::string>& row, T&& t) {
        _push_back(rank<2>{}, row, std::forward<T>(t));
    }


    template <typename... Args>
    void add_row(Args&&... args) {
        std::vector<std::string> row;
        push_back(row, std::forward<Args>(args)...);
        cells.push_back(row);
    }

    template <typename T, typename... Args>
    void add_rows(T&& t, Args&&... args) {
        add_row(std::forward<T>(t));
        add_rows(std::forward<Args>(args)...);
    }

    template <typename T>
    void add_rows(T&& t) {
        add_row(std::forward<T>(t));
    }

protected:
    ZEROERR_ENABLE_IF(!ZEROERR_IS_CONTAINER)
    _push_back(rank<0>, std::vector<std::string>& row, T&& t) {
        Printer print;
        print.isCompact  = true;
        print.isQuoted   = false;
        print.line_break = "";
        print(std::forward<T>(t));
        row.push_back(print.str());
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_STRING)
    _push_back(rank<2>, std::vector<std::string>& row, T t) {
        row.push_back(std::forward<std::string>(t));
    }

    ZEROERR_ENABLE_IF(ZEROERR_IS_CONTAINER)
    _push_back(rank<1>, std::vector<std::string>& row, const T& t) {
        for (auto& ele : t) {
            Printer print;
            print.isCompact  = true;
            print.isQuoted   = false;
            print.line_break = "";
            print(ele);
            row.push_back(print.str());
        }
    }

    std::vector<unsigned>                 col_width;
    std::vector<std::string>              header, footer;
    std::vector<std::vector<std::string>> cells;
};


}  // namespace  zeroerr


#pragma once



#include <chrono>
#include <functional>
#include <string>
#include <vector>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

#define ZEROERR_CREATE_TEST_FUNC(function, name, ...)                \
    static void                     function(zeroerr::TestContext*); \
    static zeroerr::detail::regTest ZEROERR_NAMEGEN(_zeroerr_reg)(   \
        {name, __FILE__, __LINE__, function, {__VA_ARGS__}});        \
    static void function(ZEROERR_UNUSED(zeroerr::TestContext* _ZEROERR_TEST_CONTEXT))

#define TEST_CASE(name, ...) \
    ZEROERR_CREATE_TEST_FUNC(ZEROERR_NAMEGEN(_zeroerr_testcase), name, __VA_ARGS__)

#define SUB_CASE(name, ...)                                                          \
    zeroerr::SubCase(name, __FILE__, __LINE__, _ZEROERR_TEST_CONTEXT, {__VA_ARGS__}) \
        << [=](ZEROERR_UNUSED(zeroerr::TestContext * _ZEROERR_TEST_CONTEXT)) mutable

#define ZEROERR_CREATE_TEST_CLASS(fixture, classname, funcname, name, ...)                   \
    class classname : public fixture {                                                       \
    public:                                                                                  \
        void funcname(zeroerr::TestContext*);                                                \
    };                                                                                       \
    static void ZEROERR_CAT(call_, funcname)(zeroerr::TestContext * _ZEROERR_TEST_CONTEXT) { \
        classname instance;                                                                  \
        instance.funcname(_ZEROERR_TEST_CONTEXT);                                            \
    }                                                                                        \
    static zeroerr::detail::regTest ZEROERR_NAMEGEN(_zeroerr_reg)(                           \
        {name, __FILE__, __LINE__, ZEROERR_CAT(call_, funcname), {__VA_ARGS__}});            \
    inline void classname::funcname(ZEROERR_UNUSED(zeroerr::TestContext* _ZEROERR_TEST_CONTEXT))

#define TEST_CASE_FIXTURE(fixture, name, ...)                           \
    ZEROERR_CREATE_TEST_CLASS(fixture, ZEROERR_NAMEGEN(_zeroerr_class), \
                              ZEROERR_NAMEGEN(_zeroerr_test_method), name, __VA_ARGS__)


#define ZEROERR_HAVE_SAME_OUTPUT _ZEROERR_TEST_CONTEXT->save_output();

#ifndef ZEROERR_DISABLE_BDD
#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__)
#define GIVEN(...)    SUB_CASE("given: " __VA_ARGS__)
#define WHEN(...)     SUB_CASE("when: " __VA_ARGS__)
#define THEN(...)     SUB_CASE("then: " __VA_ARGS__)
#endif

namespace zeroerr {

class IReporter;
struct TestCase;
class Decorator;

/**
 * @brief TestContext is a class that holds the test results and reporter context.
 * There are 8 different matrices that are used to store the test results.
 * * passed    : Number of passed tests
 * * warning   : Number of tests that passed with warning
 * * failed    : Number of failed tests
 * * skipped   : Number of skipped tests
 * * passed_as : Number of passed tests in assertion
 * * warning_as: Number of tests that passed with warning in assertion
 * * failed_as : Number of failed tests in assertion
 * * skipped_as: Number of skipped tests in assertion
 */
class TestContext {
public:
    unsigned passed     = 0;
    unsigned warning    = 0;
    unsigned failed     = 0;
    unsigned skipped    = 0;
    unsigned passed_as  = 0;
    unsigned warning_as = 0;
    unsigned failed_as  = 0;
    unsigned skipped_as = 0;

    std::chrono::duration<double> duration = std::chrono::duration<double>::zero();

    IReporter& reporter;

    /**
     * @brief Add the subtest results to the matrices.
     * @param local The local test context that will be added to the global context.
     * @return int  0 if the test passed, 1 if the test passed with warning, 2 if the test failed.
     */
    int add(TestContext& local);

    /**
     * @brief Reset the matrices to 0.
     */
    void reset();

    /**
     * @brief Save the output of the test to the correct_output_path as a golden file.
     */
    void save_output();

    /**
     * @brief Construct a new Test Context object
     * @param reporter The reporter object that will be used to report the test results.
     */
    TestContext(IReporter& reporter) : reporter(reporter) {}
    ~TestContext() = default;
};

/**
 * @brief UnitTest is a class that holds the test configuration.
 * There are several options that can be set to configure the test.
 * * silent          : If true, the test will not print the test results.
 * * run_bench       : If true, the test will run the benchmark tests.
 * * run_fuzz        : If true, the test will run the fuzz tests.
 * * list_test_cases : If true, the test will list the test cases.
 * * no_color        : If true, the test will not print the test results with color.
 * * log_to_report   : If true, the test will log the test results to the report.
 * * correct_output_path : The path that the golden files will be saved.
 * * reporter_name   : The name of the reporter that will be used to report the test results.
 * * binary          : The binary name that will be used to run the test.
 * * filters         : The filters that will be used to filter the test cases.
 */
struct UnitTest {
    /**
     * @brief Parse the arguments to configure the test.
     * @param argc The number of arguments.
     * @param argv The arguments.
     * @return UnitTest& The test configuration.
     */
    UnitTest& parseArgs(int argc, const char** argv);

    /**
     * @brief Run the test.
     * @return int 0 if the test passed.
     */
    int run();

    /**
     * @brief Run the test with the given filter.
     * @param tc The test case that will be run.
     * @return true If the test passed.
     * @return false If the test failed.
     */
    bool run_filter(const TestCase& tc);

    bool            silent          = false;
    bool            run_bench       = false;
    bool            run_fuzz        = false;
    bool            list_test_cases = false;
    bool            no_color        = false;
    bool            log_to_report   = false;
    std::string     correct_output_path;
    std::string     reporter_name = "console";
    std::string     binary;
    struct Filters* filters;
};

/**
 * @brief TestCase is a class that holds the test case information.
 * There are several fields that are used to store the test case information.
 * * name : The name of the test case.
 * * file : The file that the test case is defined.
 * * line : The line that the test case is defined.
 * * func : The function that will be run to test the test case.
 * * subcases : The subcases that are defined in the test case.
 */
struct TestCase {
    std::string                       name;
    std::string                       file;
    unsigned                          line;
    std::function<void(TestContext*)> func;
    std::vector<TestCase*>            subcases;
    std::vector<Decorator*>           decorators;
    /**
     * @brief Compare the test cases.
     * @param rhs The test case that will be compared.
     * @return true If the test case is less than the rhs, otherwise false.
     */
    bool operator<(const TestCase& rhs) const;

    /**
     * @brief Construct a new Test Case object
     * @param name The name of the test case.
     * @param file The file that the test case is defined.
     * @param line The line that the test case is defined.
     */
    TestCase(std::string name, std::string file, unsigned line, std::vector<Decorator*> decorators)
        : name(name), file(file), line(line), decorators(decorators) {}

    /**
     * @brief Construct a new Test Case object
     * @param name The name of the test case.
     * @param file The file that the test case is defined.
     * @param line The line that the test case is defined.
     * @param func The function that will be run to test the test case.
     * @param decorators The decorators that will be used to decorate the test case.
     */
    TestCase(std::string name, std::string file, unsigned line,
             std::function<void(TestContext*)> func, std::vector<Decorator*> decorators)
        : name(name), file(file), line(line), func(func), decorators(decorators) {}
};


/**
 * @brief SubCase is a class that holds the subcase information.
 */
struct SubCase : TestCase {
    SubCase(std::string name, std::string file, unsigned line, TestContext* context,
            std::vector<Decorator*> decorators);
    ~SubCase() = default;
    TestContext* context;
    void         operator<<(std::function<void(TestContext*)> op);
};


template <typename T>
struct TestedObjects {
    void           add(T&& obj) { objects.push_back(std::forward<T>(obj)); }
    std::vector<T> objects;
};


/**
 * @brief IReporter is an interface that is used to report the test results.
 * You can create a new reporter by inheriting this class and implementing the virtual functions.
 * The following events will be called once it happens during testing.
 * * testStart     : called when the test starts.
 * * testCaseStart : called when the test case starts.
 * * testCaseEnd   : called when the test case ends.
 * * subCaseStart  : called when the subcase starts.
 * * subCaseEnd    : called when the subcase ends.
 * * testEnd       : called when the test ends.
 */
class IReporter {
public:
    virtual ~IReporter() = default;

    virtual std::string getName() const = 0;

    // There are a list of events
    virtual void testStart()                                           = 0;
    virtual void testCaseStart(const TestCase& tc, std::stringbuf& sb) = 0;
    virtual void testCaseEnd(const TestCase& tc, std::stringbuf& sb, const TestContext& ctx,
                             int type)                                 = 0;
    virtual void subCaseStart(const TestCase& tc, std::stringbuf& sb)  = 0;
    virtual void subCaseEnd(const TestCase& tc, std::stringbuf& sb, const TestContext& ctx,
                            int type)                                  = 0;
    virtual void testEnd(const TestContext& tc)                        = 0;

    /**
     * @brief Create the reporter object with the given name.
     * @param name The name of the reporter. Available reporters are: console, xml.
     * @param ut The unit test object that will be used to configure the test.
     */
    static IReporter* create(const std::string& name, UnitTest& ut);

    IReporter(UnitTest& ut) : ut(ut) {}

protected:
    UnitTest& ut;
};

/**
 * @brief TestType is a enum describe the type of the test case.
 */
enum TestType { test_case = 1, sub_case = 1 << 1, bench = 1 << 2, fuzz_test = 1 << 3 };

namespace detail {

/**
 * @brief regTest is a class that is used to register the test case.
 * It will be used as global variables and the constructor will be called to register the test case.
 */
struct regTest {
    explicit regTest(const TestCase& tc, TestType type = test_case);
};

/**
 * @brief regReporter is a class that is used to register the reporter.
 * It will be used as global variables and the constructor will be called to register the reporter.
 */
struct regReporter {
    explicit regReporter(IReporter*);
};
}  // namespace detail


/**
 * @brief CombinationalTest is a class that is used to cross test a few lists of arguments.
 * One example
 * ```cpp
 *   TestArgs<int> a{1, 2, 3};
 *   TestArgs<int> b{4, 5, 6};
 *   CombinationalTest test([&]{
 *       CHECK(targetFunc(a, b) == (a+b));
 *   });
 *   test(a, b);
 * ```
 *
 * This will test the targetFunc with all the combinations of a and b, e.g. (1,4), (1,5), (1,6),
 * (2,4), (2,5) ... etc.
 */
class CombinationalTest {
public:
    CombinationalTest(std::function<void()> func) : func(func) {}
    std::function<void()> func;

    template <typename T>
    void operator()(T& arg) {
        arg.reset();
        for (size_t i = 0; i < arg.size(); ++i, ++arg) {
            func();
        }
    }

    template <typename T, typename... Args>
    void operator()(T& arg, Args&... args) {
        arg.reset();
        for (size_t i = 0; i < arg.size(); ++i, ++arg) {
            operator()(args...);
        }
    }
};


/**
 * @brief TestArgs is a class that is used to store the test arguments.
 */
template <typename T>
class TestArgs {
public:
    TestArgs(std::initializer_list<T> args) : args(args) {}
    std::vector<T> args;

    operator T() const { return args[index]; }
    TestArgs& operator++() {
        index++;
        return *this;
    }
    size_t size() const { return args.size(); }
    void   reset() { index = 0; }

private:
    int index = 0;
};


class Decorator {
public:
    // Called when the test registered, return true can block the test registering
    virtual bool onStartup(const TestCase&) { return false; }

    // Called when the test executing, return true can block the test execution
    virtual bool onExecution(const TestCase&) { return false; }

    // Called on each assertion, return true can skip the assertion
    virtual bool onAssertion() { return false; }

    // Called when the test finished, return true means the test containing errors
    virtual bool onFinish(const TestCase&, const TestContext&) { return false; }
};

Decorator* skip(bool isSkip = true);
Decorator* timeout(float timeout = 0.1f);  // in seconds
Decorator* may_fail(bool isMayFail = true);
Decorator* should_fail(bool isShouldFail = true);

}  // namespace zeroerr

ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#pragma once












#include <exception>
#include <functional>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>

ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH

#define ZEROERR_CREATE_FUZZ_TEST_FUNC(function, name, ...)                                  \
    static void                     function(zeroerr::TestContext*);                        \
    static zeroerr::detail::regTest ZEROERR_NAMEGEN(_zeroerr_reg)(                          \
        {name, __FILE__, __LINE__, function, {__VA_ARGS__}}, zeroerr::TestType::fuzz_test); \
    static void function(ZEROERR_UNUSED(zeroerr::TestContext* _ZEROERR_TEST_CONTEXT))

#define FUZZ_TEST_CASE(name, ...) \
    ZEROERR_CREATE_FUZZ_TEST_FUNC(ZEROERR_NAMEGEN(_zeroerr_testcase), name, __VA_ARGS__)

#define FUZZ_FUNC(func) zeroerr::FuzzFunction(func, _ZEROERR_TEST_CONTEXT)


namespace zeroerr {

namespace detail {

/**
 * @brief FunctionDecomposition is a meta function to decompose the function type.
 *        The ValueType and numOfArgs will be the result of the decomposition.
 */
template <typename... Args>
struct FunctionDecomposition {
    static constexpr size_t numOfArgs = sizeof...(Args);
    using ValueType                   = std::tuple<Args...>;
};

template <typename... Args>
FunctionDecomposition<typename std::decay<Args>::type...> FunctionDecompositionImpl(
    void (*)(Args...));

template <typename... Args>
FunctionDecomposition<typename std::decay<Args>::type...> FunctionDecompositionImpl(
    std::function<void(Args...)>);


template <typename T>
struct memfun_type {
    using type = void;
};

template <typename Ret, typename Class, typename... Args>
struct memfun_type<Ret (Class::*)(Args...) const> {
    using ret_type = FunctionDecomposition<typename std::decay<Args>::type...>;
};

template <typename F>
typename memfun_type<decltype(&F::operator())>::ret_type FunctionDecompositionImpl(F);


template <typename T>
using FunctionDecompositionT = decltype(FunctionDecompositionImpl(std::declval<T>()));

}  // namespace detail


/**
 * @brief FuzzTest is a template class to create a fuzz test for the target function.
 * @tparam TargetFunction The target function to fuzz.
 * @tparam FuncType is the decomposition result of the target function.
 */
template <typename TargetFunction,
          typename FuncType = detail::FunctionDecompositionT<TargetFunction>>
struct FuzzTest;

template <typename TargetFunction, typename FuncType, typename Domain,
          typename Base = FuzzTest<TargetFunction, FuncType>>
struct FuzzTestWithDomain;

struct IFuzzTest {
    virtual void        Run(int count = 1000, int seed = 0)          = 0;
    virtual void        RunOneTime(const uint8_t* data, size_t size) = 0;
    virtual std::string MutateData(const uint8_t* data, size_t size, size_t max_size,
                                   unsigned int seed)                = 0;

    int  count = 0, max_count = 0;
    bool should_stop() { return count == max_count; }
};

class FuzzFinishedException : public std::exception {
public:
    virtual const char* what() const throw() { return "Fuzzing finished"; }
};

// The details are described in the declaration of the class.
template <typename TargetFunction, typename FuncType>
struct FuzzTest : IFuzzTest {
    using ValueType = typename FuncType::ValueType;
    FuzzTest(TargetFunction func, TestContext* context) : func(func), context(context) {}
    FuzzTest(const FuzzTest& other)
        : func(other.func), context(other.context), seeds(other.seeds) {}

    template <typename... T>
    using FuzzTestWithDomainType =
        FuzzTestWithDomain<TargetFunction, FuncType,
                           AggregateOf<std::tuple<typename T::ValueType...>, T...>>;

    /**
     * @tparam T The domain type to use.
     */
    template <typename... T>
    FuzzTestWithDomainType<T...> WithDomains(
        AggregateOf<std::tuple<typename T::ValueType...>, T...> domain) {
        return FuzzTestWithDomainType<T...>(*this, domain);
    }

    /**
     * @tparam T The domain type to use.
     * This function will decompose the domain type and use tuple to represent the list of domains.
     * The previous function is matched in this call.
     */
    template <typename... T>
    FuzzTestWithDomainType<T...> WithDomains(T&&... domains) {
        return WithDomains(TupleOf(std::forward<T>(domains)...));
    }

    FuzzTest& WithSeeds(std::vector<ValueType> _seeds) {
        this->seeds.insert(this->seeds.end(), _seeds.begin(), _seeds.end());
        return *this;
    }

    // This should create default domains
    virtual void        Run(int = 1000, int = 0) {}
    virtual void        RunOneTime(const uint8_t*, size_t) {}
    virtual std::string MutateData(const uint8_t*, size_t, size_t, unsigned int) { return ""; }

    TargetFunction         func;
    TestContext*           context;
    std::vector<ValueType> seeds;
};

extern void RunFuzzTest(IFuzzTest& fuzz_test, int seed = 0, int runs = 1000, int max_len = 0,
                        int timeout = 1200, int len_control = 100);


/**
 * @brief FuzzTestWithDomain implements the Base class with the domain passed into.
 * @tparam TargetFunction The target function to fuzz.
 * @tparam FuncType is the decomposition result of the target function.
 * @tparam Domain The domain to use.
 * @tparam Base The base class to inherit from.
 */
template <typename TargetFunction, typename FuncType, typename Domain, typename Base>
struct FuzzTestWithDomain : public Base {
    FuzzTestWithDomain(const Base& ft, const Domain& domain) : Base(ft), m_domain(domain) {}

    virtual void Run(int _count = 1000, int _seed = 0) override {
        Base::count     = 1;
        Base::max_count = _count;
        m_rng           = new Rng(_seed);
        RunFuzzTest(*this, _seed, _count, 500, 1200, 1);
        delete m_rng;
        m_rng = nullptr;
    }

    typename Domain::CorpusType GetRandomCorpus() {
        Rng& rng = *this->m_rng;
        if (Base::seeds.size() > 0 && rng.bounded(2) == 0) {
            return m_domain.FromValue(Base::seeds[rng() % Base::seeds.size()]);
        }
        return m_domain.GetRandomCorpus(rng);
    }

    typename Domain::CorpusType TryParse(const std::string& input) {
        try {
            IRObject obj = IRObject::FromString(input);
            if (obj.type == IRObject::Type::Undefined) return GetRandomCorpus();
            return m_domain.ParseCorpus(obj);
        } catch (...) {
            return GetRandomCorpus();
        }
    }

    template <typename T, unsigned... I>
    void apply(T args, detail::seq<I...>) {
        this->func(std::get<I>(args)...);
    }

    virtual void RunOneTime(const uint8_t* data, size_t size) override {
        Base::count++;
        std::string                 input  = std::string((const char*)data, size);
        typename Domain::CorpusType corpus = TryParse(input);
        typename Domain::ValueType  value  = m_domain.GetValue(corpus);
        apply(value, detail::gen_seq<std::tuple_size<typename Domain::ValueType>::value>{});
    }

    virtual std::string MutateData(const uint8_t* data, size_t size, size_t max_size,
                                   unsigned int seed) override {
        Rng                         rng(seed);
        std::string                 input  = std::string((const char*)data, size);
        typename Domain::CorpusType corpus = TryParse(input);
        m_domain.Mutate(rng, corpus, false);
        IRObject mutated_obj = m_domain.SerializeCorpus(corpus);
        return IRObject::ToString(mutated_obj);
    }

    Domain m_domain;
    Rng*   m_rng;
};


template <typename T>
FuzzTest<T> FuzzFunction(T func, TestContext* context) {
    return FuzzTest<T>(func, context);
}

template <typename T>
std::vector<T> ReadCorpusFromDir(std::string dir);


}  // namespace zeroerr


ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
#ifdef ZEROERR_IMPLEMENTATION


#include <random>
#include <stdexcept>
#include <string>

namespace zeroerr {

Rng::Rng() : mX(0), mY(0) {
    std::random_device                      rd;
    std::uniform_int_distribution<uint64_t> dist;
    do {
        mX = dist(rd);
        mY = dist(rd);
    } while (mX == 0 && mY == 0);
}

static uint64_t splitMix64(uint64_t& state) noexcept {
    uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15));
    z          = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
    z          = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb);
    return z ^ (z >> 31U);
}

// Seeded as described in romu paper (update april 2020)
Rng::Rng(uint64_t seed) noexcept : mX(splitMix64(seed)), mY(splitMix64(seed)) {
    for (size_t i = 0; i < 10; ++i) {
        operator()();
    }
}

// only internally used to copy the RNG.
Rng::Rng(uint64_t x, uint64_t y) noexcept : mX(x), mY(y) {}

Rng Rng::copy() const noexcept { return Rng{mX, mY}; }

Rng::Rng(std::vector<uint64_t> const& data) : mX(0), mY(0) {
    if (data.size() != 2) {
        throw std::runtime_error("Rng::Rng: needed exactly 2 entries in data, but got " +
                                 std::to_string(data.size()));
    }
    mX = data[0];
    mY = data[1];
}

std::vector<uint64_t> Rng::state() const {
    std::vector<uint64_t> data(2);
    data[0] = mX;
    data[1] = mY;
    return data;
}


uint64_t Rng::min() { return 0; }

uint64_t Rng::max() { return (std::numeric_limits<uint64_t>::max)(); }


uint64_t Rng::rotl(uint64_t x, unsigned k) noexcept { return (x << k) | (x >> (64U - k)); }


}  // namespace zeroerr





#if !defined(ZEROERR_ALWAYS_COLORFUL) && !defined(ZEROERR_DISABLE_COLORFUL)
namespace zeroerr {

static const char* _Reset      = "\x1b[0m";
static const char* _Bright     = "\x1b[1m";
static const char* _Dim        = "\x1b[2m";
static const char* _Underscore = "\x1b[4m";
static const char* _Blink      = "\x1b[5m";
static const char* _Reverse    = "\x1b[7m";
static const char* _Hidden     = "\x1b[8m";

static const char* _FgBlack   = "\x1b[30m";
static const char* _FgRed     = "\x1b[31m";
static const char* _FgGreen   = "\x1b[32m";
static const char* _FgYellow  = "\x1b[33m";
static const char* _FgBlue    = "\x1b[34m";
static const char* _FgMagenta = "\x1b[35m";
static const char* _FgCyan    = "\x1b[36m";
static const char* _FgWhite   = "\x1b[37m";

static const char* _BgBlack   = "\x1b[40m";
static const char* _BgRed     = "\x1b[41m";
static const char* _BgGreen   = "\x1b[42m";
static const char* _BgYellow  = "\x1b[43m";
static const char* _BgBlue    = "\x1b[44m";
static const char* _BgMagenta = "\x1b[45m";
static const char* _BgCyan    = "\x1b[46m";
static const char* _BgWhite   = "\x1b[47m";

const char* Reset      = _Reset;
const char* Bright     = _Bright;
const char* Dim        = _Dim;
const char* Underscore = _Underscore;
const char* Blink      = _Blink;
const char* Reverse    = _Reverse;
const char* Hidden     = _Hidden;

const char* FgBlack   = _FgBlack;
const char* FgRed     = _FgRed;
const char* FgGreen   = _FgGreen;
const char* FgYellow  = _FgYellow;
const char* FgBlue    = _FgBlue;
const char* FgMagenta = _FgMagenta;
const char* FgCyan    = _FgCyan;
const char* FgWhite   = _FgWhite;

const char* BgBlack   = _BgBlack;
const char* BgRed     = _BgRed;
const char* BgGreen   = _BgGreen;
const char* BgYellow  = _BgYellow;
const char* BgBlue    = _BgBlue;
const char* BgMagenta = _BgMagenta;
const char* BgCyan    = _BgCyan;
const char* BgWhite   = _BgWhite;

ZEROERR_MUTEX(m);

void disableColorOutput() {
    ZEROERR_LOCK(m);
    Reset      = "";
    Bright     = "";
    Dim        = "";
    Underscore = "";
    Blink      = "";
    Reverse    = "";
    Hidden     = "";

    FgBlack   = "";
    FgRed     = "";
    FgGreen   = "";
    FgYellow  = "";
    FgBlue    = "";
    FgMagenta = "";
    FgCyan    = "";
    FgWhite   = "";

    BgBlack   = "";
    BgRed     = "";
    BgGreen   = "";
    BgYellow  = "";
    BgBlue    = "";
    BgMagenta = "";
    BgCyan    = "";
    BgWhite   = "";
}

void enableColorOutput() {
    ZEROERR_LOCK(m);
    Reset      = _Reset;
    Bright     = _Bright;
    Dim        = _Dim;
    Underscore = _Underscore;
    Blink      = _Blink;
    Reverse    = _Reverse;
    Hidden     = _Hidden;

    FgBlack   = _FgBlack;
    FgRed     = _FgRed;
    FgGreen   = _FgGreen;
    FgYellow  = _FgYellow;
    FgBlue    = _FgBlue;
    FgMagenta = _FgMagenta;
    FgCyan    = _FgCyan;
    FgWhite   = _FgWhite;

    BgBlack   = _BgBlack;
    BgRed     = _BgRed;
    BgGreen   = _BgGreen;
    BgYellow  = _BgYellow;
    BgBlue    = _BgBlue;
    BgMagenta = _BgMagenta;
    BgCyan    = _BgCyan;
    BgWhite   = _BgWhite;
}


#ifndef ZEROERR_DISABLE_AUTO_INIT
static struct ColorInit {
    ColorInit() {
        if (isTerminalOutput(STDERR)) {
            enableColorOutput();
        } else {
            disableColorOutput();
        }
    }
} colorInit;
#endif


}  // namespace zeroerr

#endif


#include <iostream>

namespace zeroerr {


Printer& getStdoutPrinter() {
    static Printer printer(std::cout);
    return printer;
}

Printer& getStderrPrinter() {
    static Printer printer(std::cerr);
    return printer;
}

}  // namespace zeroerr

#include <cstdio>

#ifdef ZEROERR_OS_UNIX
#include <sys/ioctl.h>
#include <unistd.h>
#endif

#ifdef ZEROERR_OS_WINDOWS
#define NOMINMAX
#include <Windows.h>
#endif

namespace zeroerr {

#ifdef ZEROERR_OS_UNIX
bool isTerminalOutput(OutputStream stream) {
    switch (stream) {
        case STDOUT: return isatty(fileno(stdout)) != 0;
        case STDERR: return isatty(fileno(stderr)) != 0;
        default:     return false;
    }
}

TerminalSize getTerminalWidth() {
    struct winsize ws;
    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
        return {80, 24};
    }
    return {ws.ws_col, ws.ws_row};
}

#endif
#ifdef ZEROERR_OS_WINDOWS
bool isTerminalOutput(OutputStream stream) {
    switch (stream) {
        case STDOUT: return GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR;
        case STDERR: return GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR;
        default:     return false;
    }
}

TerminalSize getTerminalWidth() {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    return {csbi.dwSize.X, csbi.dwSize.Y};
}

#endif


}  // namespace zeroerr



#include <iomanip>
#include <unordered_set>

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#endif

const char* ZEROERR_LOG_CATEGORY = "default";


namespace zeroerr {

int _ZEROERR_G_VERBOSE = 0;

thread_local std::vector<IContextScope*> _ZEROERR_G_CONTEXT_SCOPE_VECTOR;

static std::string       DefaultLogCallback(const LogMessage& msg, bool colorful);
static LogCustomCallback log_custom_callback = DefaultLogCallback;
void setLogCustomCallback(LogCustomCallback callback) { log_custom_callback = callback; }


LogInfo::LogInfo(const char* filename, const char* function, const char* message,
                 const char* category, unsigned line, unsigned size, LogSeverity severity)
    : filename(filename),
      function(function),
      message(message),
      category(category),
      line(line),
      size(size),
      severity(severity),
      names() {
    for (const char* p = message; *p; p++)
        if (*p == '{') {
            const char* q = p + 1;
            while (*q && *q != '}') q++;
            if (*q == '}') {
                std::string N(p + 1, (size_t)(q - p - 1));
                names[N] = static_cast<int>(names.size());
                p        = q;
            }
        }
}


constexpr size_t LogStreamMaxSize = 1 * 1024 - 16;

struct DataBlock {
    ZEROERR_ATOMIC(size_t) size;
    DataBlock* next = nullptr;
    char       data[LogStreamMaxSize];

    DataBlock() : size(0) {}

    LogMessage* begin() { return (LogMessage*)data; }
    LogMessage* end() { return (LogMessage*)&data[size]; }
};

LogStream::LogStream() {
    first = m_last = new DataBlock();
    prepare        = new DataBlock();
#ifndef ZEROERR_NO_THREAD_SAFE
    mutex = new std::mutex();
#endif
    setStderrLogger();
}

LogStream::~LogStream() {
    while (first) {
        DataBlock* next = first->next;
        logger->flush(first);
        delete first;
        first = next;
    }
    if (logger) delete logger;
#ifndef ZEROERR_NO_THREAD_SAFE
    delete mutex;
#endif
}

void* LogStream::alloc_block(unsigned size) {
    if (size > LogStreamMaxSize) {
        throw std::runtime_error("LogStream::push: size > LogStreamMaxSize");
    }
    ZEROERR_LOCK(*mutex);
    auto* last = ZEROERR_LOAD(m_last);
    if (last->size + size > LogStreamMaxSize) {
        if (flush_mode == FLUSH_WHEN_FULL) {
            logger->flush(last);
            last->size = 0;
        } else {
            last->next = new DataBlock();
            last       = last->next;
        }
    }
    void* p = last->data + last->size;
    last->size += size;
    return p;
}

void* LogStream::alloc_block_lockfree(unsigned size) {
#ifndef ZEROERR_NO_THREAD_SAFE
    if (size > LogStreamMaxSize) {
        throw std::runtime_error("LogStream::push: size > LogStreamMaxSize");
    }
    DataBlock* last = ZEROERR_LOAD(m_last);
    while (true) {
        size_t p = last->size.load();
        if (p <= (LogStreamMaxSize - size)) {
            if (last->size.compare_exchange_strong(p, p + size)) return last->data + p;
        } else {
            if (m_last.compare_exchange_strong(last, prepare)) {
                if (flush_mode == FLUSH_WHEN_FULL) {
                    logger->flush(last);
                    last->size = 0;
                    prepare    = last;
                } else {
                    prepare->next = last;
                    prepare       = new DataBlock();
                }
            }
        }
    }
#else
    return alloc_block(size);
#endif
}
LogIterator LogStream::current(std::string message, std::string function_name, int line) {
    LogIterator iter(*this, message, function_name, line);
    DataBlock*  last = ZEROERR_LOAD(m_last);
    iter.p           = last;
    iter.q           = reinterpret_cast<LogMessage*>(&(last->data[ZEROERR_LOAD(last->size)]));
    return iter;
}

void LogStream::flush() {
    ZEROERR_LOCK(*mutex);
    DataBlock* last = ZEROERR_LOAD(m_last);
    for (DataBlock* p = first; p != last; p = p->next) {
        logger->flush(p);
        delete p;
    }
    logger->flush(last);
    last->size = 0;
    first      = last;
}

static LogMessage* moveBytes(LogMessage* p, unsigned size) {
    char* src = (char*)p;
    char* dst = src + size;
    return (LogMessage*)dst;
}

void* LogStream::getRawLog(std::string func, unsigned line, std::string name) {
    for (DataBlock* p = first; p; p = p->next)
        for (auto q = p->begin(); q < p->end(); q = moveBytes(q, q->info->size))
            if (line == q->info->line && func == q->info->function) return q->getRawLog(name);
    return nullptr;
}

static bool startWith(const std::string& str, const std::string& prefix) {
    return str.rfind(prefix, 0) == 0;
}

void* LogStream::getRawLog(std::string func, std::string msg, std::string name) {
    for (DataBlock* p = first; p; p = p->next)
        for (auto q = p->begin(); q < p->end(); q = moveBytes(q, q->info->size))
            if (startWith(q->info->message, msg) && func == q->info->function)
                return q->getRawLog(name);
    return nullptr;
}

LogIterator::LogIterator(LogStream& stream, std::string message, std::string function_name,
                         int line)
    : p(stream.first),
      q(stream.first->begin()),
      message_filter(message),
      function_name_filter(function_name),
      line_filter(line) {
    while (!check_filter() && p) next();
}

void LogIterator::check_at_safe_pos() {
    if (static_cast<size_t>((char*)q - p->data) >= ZEROERR_LOAD(p->size)) {
        p = p->next;
        q = reinterpret_cast<LogMessage*>(p->data);
    }
}

void LogIterator::next() {
    if (q < p->end()) {
        q = moveBytes(q, q->info->size);
        if (q >= p->end()) next();
    } else {
        p = p->next;
        if (p)
            q = p->begin();
        else
            q = nullptr;
    }
}

LogIterator& LogIterator::operator++() {
    do {
        next();
    } while (p && !check_filter());
    return *this;
}

bool LogIterator::check_filter() {
    if (!message_filter.empty() && startWith(q->info->message, message_filter)) return false;
    if (!function_name_filter.empty() && q->info->function != function_name_filter) return false;
    if (line_filter != -1 && static_cast<int>(q->info->line) != line_filter) return false;
    return true;
}

class FileLogger : public Logger {
public:
    FileLogger(std::string name) { file = fopen(name.c_str(), "w"); }
    ~FileLogger() {
        if (file) fclose(file);
    }
    void flush(DataBlock* msg) override {
        if (file) {
            for (auto p = msg->begin(); p < msg->end(); p = moveBytes(p, p->info->size)) {
                auto ss = log_custom_callback(*p, false);
                fwrite(ss.c_str(), ss.size(), 1, file);
            }
            fflush(file);
        }
    }

protected:
    FILE* file;
};


#ifdef _WIN32
static char split = '\\';
#else
static char split = '/';
#endif

static void make_dir(std::string path) {
    size_t pos = 0;
    do {
        pos             = path.find_first_of(split, pos + 1);
        std::string sub = path.substr(0, pos);
#ifdef _WIN32
        CreateDirectory(sub.c_str(), NULL);
#else
        struct stat st;
        if (stat(sub.c_str(), &st) == -1) {
            mkdir(sub.c_str(), 0755);
        }
#endif
    } while (pos != std::string::npos);
}

struct FileCache {
    std::map<std::string, FILE*> files;
    ~FileCache() {
        for (auto& p : files) {
            fclose(p.second);
        }
    }

    FILE* get(const std::string& name) {
        auto it = files.find(name);
        if (it != files.end()) return it->second;
        auto        p    = name.find_last_of(split);
        std::string path = name.substr(0, p);
        make_dir(path.c_str());

        FILE* file = fopen(name.c_str(), "w");
        if (file) {
            files[name] = file;
            return file;
        }
        return nullptr;
    }
};


class DirectoryLogger : public Logger {
public:
    DirectoryLogger(std::string path, LogStream::DirMode dir_mode[3]) : dirpath(path) {
        make_dir(path.c_str());
        for (int i = 0; i < 3; i++) {
            this->dir_mode[i] = dir_mode[i];
        }
    }
    ~DirectoryLogger() {}

    void flush(DataBlock* msg) override {
        FileCache cache;
        for (auto p = msg->begin(); p < msg->end(); p = moveBytes(p, p->info->size)) {
            auto ss = log_custom_callback(*p, false);

            std::stringstream path;
            path << dirpath;
            if (dirpath.back() != split) path << split;

            int last = 0;
            for (int i = 0; i < 3; i++) {
                if (last != 0 && dir_mode[i] != 0) path << split;
                switch (dir_mode[i]) {
                    case LogStream::DAILY_FILE: {
                        std::time_t t  = std::chrono::system_clock::to_time_t(p->time);
                        std::tm     tm = *std::localtime(&t);
                        path << std::put_time(&tm, "%Y-%m-%d");
                        break;
                    }
                    case LogStream::SPLIT_BY_SEVERITY: {
                        path << to_string(p->info->severity);
                        break;
                    }
                    case LogStream::SPLIT_BY_CATEGORY: {
                        path << to_category(p->info->category);
                        break;
                    }
                    default: continue;
                }
                last = 1;
            }
            std::cerr << path.str() << std::endl;

            FILE* file = cache.get(path.str());
            fwrite(ss.c_str(), ss.size(), 1, file);
        }
    }

protected:
    std::string to_string(LogSeverity severity) {
        switch (severity) {
            case INFO_l:  return "INFO";
            case LOG_l:   return "LOG";
            case WARN_l:  return "WARN";
            case ERROR_l: return "ERROR";
            case FATAL_l: return "FATAL";
        }
        return "";
    }

    std::string to_category(const char* category) {
        std::string cat = category;
        for (auto& c : cat) {
            if (c == '/') c = split;
        }
        return cat;
    }

    LogStream::DirMode dir_mode[3];
    std::string        dirpath;
};

class OStreamLogger : public Logger {
public:
    OStreamLogger(std::ostream& os) : os(os) {}

    void flush(DataBlock* msg) override {
        for (auto p = msg->begin(); p < msg->end(); p = moveBytes(p, p->info->size)) {
            os << log_custom_callback(*p, true);
        }
        os.flush();
    }

protected:
    std::ostream& os;
};


LogStream& LogStream::getDefault() {
    static LogStream stream;
    return stream;
}

void LogStream::setFileLogger(std::string name, DirMode mode1, DirMode mode2, DirMode mode3) {
    if (logger) delete logger;

    if (mode1 == 0 || mode2 == 0 || mode3 == 0)
        logger = new FileLogger(name);
    else {
        LogStream::DirMode dir_mode_group[3] = {mode1, mode2, mode3};

        logger = new DirectoryLogger(name, dir_mode_group);
    }
}

void LogStream::setStdoutLogger() {
    if (logger) delete logger;
    logger = new OStreamLogger(std::cout);
}

void LogStream::setStderrLogger() {
    if (logger) delete logger;
    logger = new OStreamLogger(std::cerr);
}


static LogSeverity LogLevel;

static std::unordered_set<std::string> LogCategory;
static std::vector<std::string>        AllLogCategory;


void setLogLevel(LogSeverity level) { LogLevel = level; }

void setLogCategory(const char* categories) {
    LogCategory.clear();
    std::string str = categories;
    std::string cat;
    for (auto c : str) {
        if (c == ',') {
            LogCategory.insert(cat);
            cat.clear();
        } else {
            cat.push_back(c);
        }
    }
    if (!cat.empty()) {
        LogCategory.insert(cat);
    }
}

static LogStream::FlushMode saved_flush_mode = LogStream::FlushMode::FLUSH_AT_ONCE;

void suspendLog() {
    saved_flush_mode = LogStream::getDefault().getFlushMode();
    LogStream::getDefault().setFlushMode(LogStream::FLUSH_MANUALLY);
}

void resumeLog() {
    LogStream::getDefault().setFlushMode(saved_flush_mode);
    LogStream::getDefault().flush();
}

#define zeroerr_color(x) (colorful ? x : "")
static std::string DefaultLogCallback(const LogMessage& msg, bool colorful) {
    std::stringstream ss;
    std::time_t       t  = std::chrono::system_clock::to_time_t(msg.time);
    std::tm           tm = *std::localtime(&t);

    ss << zeroerr_color(Dim) << '[' << zeroerr_color(Reset);
    switch (msg.info->severity) {
        case INFO_l:  ss << "INFO "; break;
        case LOG_l:   ss << zeroerr_color(FgGreen) << "LOG  " << zeroerr_color(Reset); break;
        case WARN_l:  ss << zeroerr_color(FgYellow) << "WARN " << zeroerr_color(Reset); break;
        case ERROR_l: ss << zeroerr_color(FgRed) << "ERROR" << zeroerr_color(Reset); break;
        case FATAL_l: ss << zeroerr_color(FgMagenta) << "FATAL" << zeroerr_color(Reset); break;
    }
    ss << " " << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");

    std::string fileName(msg.info->filename);

    auto p = fileName.find_last_of('/');
    if (p != std::string::npos) fileName = fileName.substr(p + 1);
    auto q = fileName.find_last_of('\\');
    if (q != std::string::npos) fileName = fileName.substr(q + 1);

    ss << " " << fileName << ":" << msg.info->line;
    ss << zeroerr_color(Dim) << ']' << zeroerr_color(Reset) << "  " << msg.str();
    ss << std::endl;
    return ss.str();
}
#undef zeroerr_color

}  // namespace zeroerr





#include <algorithm>
#include <map>
#include <sstream>

namespace zeroerr {

// clang-format off

static Table::Style ASCII{
"+","-","-","+",
"|"," ","|","|",
"|","-","+","|",
"|"," ","|","|",
"|","-","+","|",
"|","-","+","|",
"|"," ","|","|",
"+","-","-","+",
};

static Table::Style ASCII2{
"+","-","+","+",
"|"," ","|","|",
"+","-","+","+",
"|"," ","|","|",
"+","-","+","+",
"+","-","+","+",
"|"," ","|","|",
"+","-","+","+",
};

static Table::Style ASCII_DOUBLE_HEAD{
"+","-","+","+",
"|"," ","|","|",
"+","=","+","+",
"|"," ","|","|",
"+","-","+","+",
"+","-","+","+",
"|"," ","|","|",
"+","-","+","+",
};

static Table::Style SQUARE{
"┌","─","┬","┐",
"│"," ","│","│",
"├","─","┼","┤",
"│"," ","│","│",
"├","─","┼","┤",
"├","─","┼","┤",
"│"," ","│","│",
"└","─","┴","┘",
};

static Table::Style SQUARE_DOUBLE_HEAD{
"┌","─","┬","┐",
"│"," ","│","│",
"╞","═","╪","╡",
"│"," ","│","│",
"├","─","┼","┤",
"├","─","┼","┤",
"│"," ","│","│",
"└","─","┴","┘",
};

static Table::Style MINIMAL{
" "," ","╷"," ",
" "," ","│"," ",
"╶","─","┼","╴",
" "," ","│"," ",
"╶","─","┼","╴",
"╶","─","┼","╴",
" "," ","│"," ",
" "," ","╵"," ",
};


static Table::Style MINIMAL_HEAVY_HEAD{
" "," ","╷"," ",
" "," ","│"," ",
"╺","━","┿","╸",
" "," ","│"," ",
"╶","─","┼","╴",
"╶","─","┼","╴",
" "," ","│"," ",
" "," ","╵"," ",
};

static Table::Style MINIMAL_DOUBLE_HEAD{
" "," ","╷"," ",
" "," ","│"," ",
" ","═","╪"," ",
" "," ","│"," ",
" ","─","┼"," ",
" ","─","┼"," ",
" "," ","│"," ",
" "," ","╵"," ",
};


static Table::Style SIMPLE{
" "," "," "," ",
" "," "," "," ",
" ","─","─"," ",
" "," "," "," ",
" "," "," "," ",
" ","─","─"," ",
" "," "," "," ",
" "," "," "," ",
};

static Table::Style SIMPLE_HEAD{
" "," "," "," ",
" "," "," "," ",
" ","─","─"," ",
" "," "," "," ",
" "," "," "," ",
" "," "," "," ",
" "," "," "," ",
" "," "," "," ",
};


static Table::Style SIMPLE_HEAVY{
" "," "," "," ",
" "," "," "," ",
" ","━","━"," ",
" "," "," "," ",
" "," "," "," ",
" ","━","━"," ",
" "," "," "," ",
" "," "," "," ",
};


static Table::Style HORIZONTALS{
" ","─","─"," ",
" "," "," "," ",
" ","─","─"," ",
" "," "," "," ",
" ","─","─"," ",
" ","─","─"," ",
" "," "," "," ",
" ","─","─"," ",
};

static Table::Style ROUNDED{
"╭","─","┬","╮",
"│"," ","│","│",
"├","─","┼","┤",
"│"," ","│","│",
"├","─","┼","┤",
"├","─","┼","┤",
"│"," ","│","│",
"╰","─","┴","╯",
};

static Table::Style HEAVY{
"┏","━","┳","┓",
"┃"," ","┃","┃",
"┣","━","╋","┫",
"┃"," ","┃","┃",
"┣","━","╋","┫",
"┣","━","╋","┫",
"┃"," ","┃","┃",
"┗","━","┻","┛",
};

static Table::Style HEAVY_EDGE{
"┏","━","┯","┓",
"┃"," ","│","┃",
"┠","─","┼","┨",
"┃"," ","│","┃",
"┠","─","┼","┨",
"┠","─","┼","┨",
"┃"," ","│","┃",
"┗","━","┷","┛",
};

static Table::Style HEAVY_HEAD{
"┏","━","┳","┓",
"┃"," ","┃","┃",
"┡","━","╇","┩",
"│"," ","│","│",
"├","─","┼","┤",
"├","─","┼","┤",
"│"," ","│","│",
"└","─","┴","┘",
};

static Table::Style DOUBLE{
"╔","═","╦","╗",
"║"," ","║","║",
"╠","═","╬","╣",
"║"," ","║","║",
"╠","═","╬","╣",
"╠","═","╬","╣",
"║"," ","║","║",
"╚","═","╩","╝",
};

static Table::Style DOUBLE_EDGE{
"╔","═","╤","╗",
"║"," ","│","║",
"╟","─","┼","╢",
"║"," ","│","║",
"╟","─","┼","╢",
"╟","─","┼","╢",
"║"," ","│","║",
"╚","═","╧","╝",
};

// clang-format on

struct StyleManager {
    std::map<std::string, Table::Style> styles;

    static StyleManager& instance() {
        static StyleManager instance;
        instance.styles["ascii"]               = ASCII;
        instance.styles["ascii2"]              = ASCII2;
        instance.styles["ascii_double_head"]   = ASCII_DOUBLE_HEAD;
        instance.styles["square"]              = SQUARE;
        instance.styles["square_double_head"]  = SQUARE_DOUBLE_HEAD;
        instance.styles["simple"]              = SIMPLE;
        instance.styles["simple_head"]         = SIMPLE_HEAD;
        instance.styles["simple_heavy"]        = SIMPLE_HEAVY;
        instance.styles["horizontal"]          = HORIZONTALS;
        instance.styles["rounded"]             = ROUNDED;
        instance.styles["heavy"]               = HEAVY;
        instance.styles["heavy_edge"]          = HEAVY_EDGE;
        instance.styles["heavy_head"]          = HEAVY_HEAD;
        instance.styles["double"]              = DOUBLE;
        instance.styles["double_edge"]         = DOUBLE_EDGE;
        instance.styles["minimal"]             = MINIMAL;
        instance.styles["minimal_heavy_head"]  = MINIMAL_HEAVY_HEAD;
        instance.styles["minimal_double_head"] = MINIMAL_DOUBLE_HEAD;
        return instance;
    }
};

void Table::registerStyle(std::string name, Table::Style style) {
    StyleManager::instance().styles[name] = style;
}
Table::Style Table::getStyle(std::string name) { return StyleManager::instance().styles[name]; }

// TODO: Refactor those macros into functions
#define left  (c.show_lr_border ? s.m_args[p] : "")
#define space s.m_args[p + 1]
#define bar   (c.show_col_split ? s.m_args[p + 2] : s.m_args[p + 1])
#define right (c.show_lr_border ? s.m_args[p + 3] : "")

#define last (i == header.size() - 1)
#define for_row          \
    if (!skip_lb)        \
        ss << std::endl; \
    else                 \
        skip_lb = false; \
    ss << left;          \
    for (size_t i = 0; i < header.size(); ++i)


inline std::string _rept(unsigned k, std::string j, Table::Style&) {
    std::stringstream ss;
    for (unsigned i = 0; i < k; i++) {
        ss << j;
    }
    return ss.str();
}

#define rep(k, t) _rept(k, t, s)
#define remain(k) (col_width[i] - static_cast<unsigned>(k.size()))

std::string Table::str(Config c, Table::Style s) {
    std::stringstream ss;

    for (auto& row : cells) {
        REQUIRE(row.size() == header.size());
    }

    if (col_width.size() == 0) {
        for (size_t i = 0; i < header.size(); ++i) {
            size_t max_width = 0;
            for (auto& row : cells) {
                max_width = std::max<size_t>(row[i].size(), max_width);
            }
            max_width = std::max<size_t>(max_width, header[i].size());
            col_width.push_back(static_cast<unsigned>(max_width));
        }
    }

    int  p;
    bool skip_lb = true;
    // Header
    if (c.show_tb_border) {
        p = 0;
        for_row { ss << rep(col_width[i] + 2, space) << (last ? right : bar); }
    }

    p = 4;
    for_row {
        REQUIRE(col_width[i] >= header[i].size());
        ss << space << rep(remain(header[i]), space) << header[i] << space << (last ? right : bar);
    }

    if (c.show_header_split) {
        p = 8;
        for_row { ss << rep(col_width[i] + 2, space) << (last ? right : bar); }
    }

    p = 12;
    // Body
    bool first = true;
    for (auto& row : cells) {
        if (!first && c.show_row_split) {
            p += 4;
            for_row { ss << rep(col_width[i] + 2, space) << (last ? right : bar); }
            p -= 4;
        } else
            first = false;
        for_row {
            REQUIRE(col_width[i] >= row[i].size());
            ss << space << rep(remain(row[i]), space) << row[i] << space << (last ? right : bar);
        }
    }

    if (footer.size() != 0) {
    }

    // Bottom
    if (c.show_tb_border) {
        p = 28;
        for_row { ss << rep(col_width[i] + 2, space) << (last ? right : bar); }
    }
    return ss.str();
}

#undef left
#undef space
#undef bar
#undef right
#undef last
#undef for_row
#undef rep
#undef remain

}  // namespace zeroerr








#include <iomanip>
#include <iostream>
#include <ostream>
#include <regex>
#include <set>
#include <string>
#include <vector>

namespace zeroerr {

namespace detail {
static std::set<TestCase> getRegisteredTests(unsigned type);
}  // namespace detail

// This function update both sum and local.
// Local need to be updated since the reporter needs to know the result of the subcase.
int TestContext::add(TestContext& local) {
    int type = 0;
    if (local.failed_as == 0 && local.warning_as == 0) {
        passed += 1;
        local.passed += 1;
    } else if (local.failed_as == 0) {
        warning += 1;
        local.warning += 1;
        type = 1;
    } else {
        failed += 1;
        local.failed += 1;
        type = 2;
    }
    passed_as += local.passed_as;
    warning_as += local.warning_as;
    failed_as += local.failed_as;

    return type;
}

void TestContext::save_output() {
    std::fstream file;
    file.open("output.txt", std::ios::in);
    std::stringbuf* outbuf = static_cast<std::stringbuf*>(std::cerr.rdbuf());
    if (file.is_open()) {
        std::stringstream buffer;
        buffer << file.rdbuf();
        if (buffer.str() != outbuf->str()) {
            std::cerr << "Output mismatch" << std::endl;
            throw std::runtime_error("Output mismatch");
        } else {
            std::cerr << "Output match" << std::endl;
        }
    } else {
        file.open("output.txt", std::ios::out);
        file << outbuf->str();
    }
    file.close();
}

void TestContext::reset() {
    passed = warning = failed = skipped = 0;
    passed_as = warning_as = failed_as = skipped_as = 0;
}

static inline std::string getFileName(std::string file) {
    std::string fileName(file);
    auto        p = fileName.find_last_of('/');
    if (p == std::string::npos) p = fileName.find_last_of('\\');
    if (p != std::string::npos) fileName = fileName.substr(p + 1);
    return fileName;
}

SubCase::SubCase(std::string name, std::string file, unsigned line, TestContext* context,
                 std::vector<Decorator*> decorators)
    : TestCase(name, file, line, decorators), context(context) {}

void SubCase::operator<<(std::function<void(TestContext*)> op) {
    func = op;
    std::stringbuf new_buf;
    context->reporter.subCaseStart(*this, new_buf);
    TestContext     local(context->reporter);
    std::streambuf* orig_buf = std::cerr.rdbuf();
    std::cerr.rdbuf(&new_buf);
    try {
        op(&local);
    } catch (const AssertionData&) {
    } catch (const FuzzFinishedException&) {
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        if (local.failed_as == 0) {
            local.failed_as = 1;
        }
    }
    std::cerr.rdbuf(orig_buf);
    int type = context->add(local);

    context->reporter.subCaseEnd(*this, new_buf, local, type);
}

struct Filters {
    std::vector<std::regex> name, name_exclude;
    std::vector<std::regex> file, file_exclude;
};

UnitTest& UnitTest::parseArgs(int argc, const char** argv) {
    filters             = new Filters();
    auto convert_to_vec = [=]() {
        std::vector<std::string> result;
        for (int i = 1; i < argc; i++) {
            result.emplace_back(argv[i]);
        }
        return result;
    };

    auto parse_char = [&](char arg) {
        if (arg == 'v') {
            this->silent = false;
            return true;
        }
        if (arg == 'q') {
            this->silent = true;
            return true;
        }
        if (arg == 'b') {
            this->run_bench = true;
            return true;
        }
        if (arg == 'f') {
            this->run_fuzz = true;
            return true;
        }
        if (arg == 'l') {
            this->list_test_cases = true;
            return true;
        }
        if (arg == 'x') {
            this->reporter_name = "xml";
            return true;
        }
        return false;
    };

    auto parse_token = [&](std::string arg) {
        if (arg == "verbose") {
            this->silent = false;
            return true;
        }
        if (arg == "quiet") {
            this->silent = true;
            return true;
        }
        if (arg == "bench") {
            this->run_bench = true;
        }
        if (arg == "fuzz") {
            this->run_fuzz = true;
        }
        if (arg == "list-test-cases") {
            this->list_test_cases = true;
        }
        if (arg == "no-color") {
            this->no_color = true;
            disableColorOutput();
        }
        if (arg == "log-to-report") {
            this->log_to_report = true;
        }
        if (arg.substr(0, 9) == "reporters") {
            this->reporter_name = arg.substr(10);
            return true;
        }
        if (arg.substr(0, 8) == "testcase") {
            filters->name.push_back(std::regex(arg.substr(9)));
            return true;
        }
        if (arg.substr(0, 14) == "testcase-exclude") {
            filters->name_exclude.push_back(std::regex(arg.substr(15)));
            return true;
        }
        if (arg.substr(0, 5) == "file") {
            filters->file.push_back(std::regex(arg.substr(6)));
            return true;
        }
        if (arg.substr(0, 11) == "file-exclude") {
            filters->file_exclude.push_back(std::regex(arg.substr(12)));
            return true;
        }
        return false;
    };

    auto parse_pos = [&](const std::vector<std::string>& args, size_t pos) {
        if (args[pos].size() == 2 && args[pos][0] == '-') {
            return parse_char(args[pos][1]);
        }
        if (args[pos].size() > 2 && args[pos][0] == '-' && args[pos][1] == '-') {
            return parse_token(args[pos].substr(2));
        }
        return false;
    };

    auto args = convert_to_vec();
    for (size_t i = 0; i < args.size(); ++i) parse_pos(args, i);

    binary = argv[0];
    return *this;
}


static std::string insertIndentation(std::string str) {
    std::stringstream result;
    std::stringstream ss(str);

    std::string line;
    while (std::getline(ss, line)) {
        result << line << std::endl << "    ";
    }

    return result.str();
}

bool UnitTest::run_filter(const TestCase& tc) {
    if (filters == nullptr) return true;
    for (auto& r : filters->name)
        if (!std::regex_match(tc.name, r)) return false;
    for (auto& r : filters->name_exclude)
        if (std::regex_match(tc.name, r)) return false;
    for (auto& r : filters->file)
        if (!std::regex_match(tc.file, r)) return false;
    for (auto& r : filters->file_exclude)
        if (std::regex_match(tc.file, r)) return false;
    return true;
}

static bool runOnExecution(const TestCase& tc) {
    for (auto& decorator : tc.decorators) {
        if (decorator->onExecution(tc)) return true;
    }
    return false;
}

static bool runOnFinish(const TestCase& tc, const TestContext& ctx) {
    for (auto& decorator : tc.decorators) {
        if (decorator->onFinish(tc, ctx)) return true;
    }
    return false;
}

int UnitTest::run() {
    IReporter* reporter = IReporter::create(reporter_name, *this);
    if (!reporter) reporter = IReporter::create("console", *this);

    TestContext context(*reporter), sum(*reporter);
    reporter->testStart();
    std::stringbuf new_buf;

    unsigned types = TestType::test_case;
    if (run_bench) types |= TestType::bench;
    if (run_fuzz) types |= TestType::fuzz_test;
    std::set<TestCase> test_cases = detail::getRegisteredTests(types);

    for (auto& tc : test_cases) {
        if (!run_filter(tc)) continue;
        if (runOnExecution(tc)) {
            sum.skipped += 1;
            continue;
        }
        reporter->testCaseStart(tc, new_buf);
        if (!list_test_cases) {
            std::streambuf* orig_buf = std::cerr.rdbuf();
            std::cerr.rdbuf(&new_buf);
            std::cerr << std::endl;
            auto start = std::chrono::high_resolution_clock::now();
            try {
                tc.func(&context);  // run the test case
            } catch (const AssertionData&) {
            } catch (const FuzzFinishedException&) {
            } catch (const std::exception& e) {
                std::cerr << e.what() << std::endl;
                if (context.failed_as == 0) {
                    context.failed_as = 1;
                }
            }
            auto end         = std::chrono::high_resolution_clock::now();
            context.duration = end - start;
            std::cerr.rdbuf(orig_buf);
        }
        int type = sum.add(context);
        if (runOnFinish(tc, context)) {
            if (type != 2) {
                sum.failed += 1;
                type = 2;
            }
        }
        reporter->testCaseEnd(tc, new_buf, context, type);
        context.reset();
        new_buf.str("");
    }
    reporter->testEnd(sum);
    delete reporter;
    return 0;
}

// sorted by file names and line numbers
bool TestCase::operator<(const TestCase& rhs) const {
    return (file < rhs.file) || (file == rhs.file && line < rhs.line);
}

namespace detail {

std::set<TestCase>& getTestSet(TestType type) {
    static std::set<TestCase> test_set, bench_set, fuzz_set;
    switch (type) {
        case TestType::test_case: return test_set;
        case TestType::bench:     return bench_set;
        case TestType::fuzz_test: return fuzz_set;
        case TestType::sub_case:  return test_set;
    }
    throw std::runtime_error("Invalid test type");
}

static std::set<TestCase> getRegisteredTests(unsigned type) {
    std::set<TestCase> result;
    if (type & TestType::test_case)
        result.insert(getTestSet(test_case).begin(), getTestSet(test_case).end());
    if (type & TestType::bench) result.insert(getTestSet(bench).begin(), getTestSet(bench).end());
    if (type & TestType::fuzz_test)
        result.insert(getTestSet(fuzz_test).begin(), getTestSet(fuzz_test).end());
    return result;
}

regTest::regTest(const TestCase& tc, TestType type) {
    for (auto& decorator : tc.decorators) {
        if (decorator->onStartup(tc)) return;
    }
    getTestSet(type).insert(tc);
}

static std::set<IReporter*>& getRegisteredReporters() {
    static std::set<IReporter*> data;
    return data;
}

regReporter::regReporter(IReporter* reporter) { getRegisteredReporters().insert(reporter); }

}  // namespace detail


class ConsoleReporter : public IReporter {
public:
    std::string getName() const override { return "console"; }

    virtual void testStart() override {
        setlocale(LC_ALL, "en_US.utf8");
        std::cerr << "ZeroErr Unit Test" << std::endl;
    }

    virtual void testEnd(const TestContext& sum) override {
        std::cerr << "----------------------------------------------------------------"
                  << std::endl;
        std::cerr << "             " << FgGreen << "PASSED" << Reset << "   |   " << FgYellow
                  << "WARNING" << Reset << "   |   " << FgRed << "FAILED" << Reset << "   |   "
                  << Dim << "SKIPPED" << Reset << std::endl;
        std::cerr << "TEST CASE:   " << std::setw(6) << sum.passed << "       " << std::setw(7)
                  << sum.warning << "       " << std::setw(6) << sum.failed << "       "
                  << std::setw(7) << sum.skipped << std::endl;
        std::cerr << "ASSERTION:   " << std::setw(6) << sum.passed_as << "       " << std::setw(7)
                  << sum.warning_as << "       " << std::setw(6) << sum.failed_as << "       "
                  << std::setw(7) << sum.skipped_as << std::endl;
    }

    virtual void testCaseStart(const TestCase& tc, std::stringbuf&) override {
        std::cerr << "TEST CASE " << Dim << "[" << getFileName(tc.file) << ":" << tc.line << "] "
                  << Reset << FgCyan << tc.name << Reset << std::endl;
    }

    virtual void testCaseEnd(const TestCase&, std::stringbuf& sb, const TestContext&,
                             int type) override {
        if (!(ut.silent && type == 0)) std::cerr << insertIndentation(sb.str()) << std::endl;
    }

    virtual void subCaseStart(const TestCase& tc, std::stringbuf&) override {
        std::cerr << "SUB CASE " << Dim << "[" << getFileName(tc.file) << ":" << tc.line << "] "
                  << Reset << FgCyan << tc.name << Reset << std::endl;
    }

    virtual void subCaseEnd(const TestCase&, std::stringbuf& sb, const TestContext&,
                            int type) override {
        if (!(ut.silent && type == 0)) std::cerr << insertIndentation(sb.str()) << std::endl;
    }

    ConsoleReporter(UnitTest& ut) : IReporter(ut) {}
};


namespace detail {

// =================================================================================================
// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp
// =================================================================================================
class XmlEncode {
public:
    enum ForWhat { ForTextNodes, ForAttributes };
    XmlEncode(const std::string& str, ForWhat forWhat = ForTextNodes);
    void                 encodeTo(std::ostream& os) const;
    friend std::ostream& operator<<(std::ostream& os, const XmlEncode& xmlEncode);

private:
    std::string m_str;
    ForWhat     m_forWhat;
};

class XmlWriter {
public:
    class ScopedElement {
    public:
        ScopedElement(XmlWriter* writer);
        ScopedElement(ScopedElement&& other) noexcept;
        ScopedElement& operator=(ScopedElement&& other) noexcept;
        ~ScopedElement();

        ScopedElement& writeText(const std::string& text, bool indent = true, bool new_line = true);

        template <typename T>
        ScopedElement& writeAttribute(const std::string& name, const T& attribute) {
            m_writer->writeAttribute(name, attribute);
            return *this;
        }

    private:
        mutable XmlWriter* m_writer = nullptr;
    };

    XmlWriter(std::ostream& os = std::cerr);
    ~XmlWriter();

    XmlWriter(const XmlWriter&)            = delete;
    XmlWriter& operator=(const XmlWriter&) = delete;

    XmlWriter&    startElement(const std::string& name);
    ScopedElement scopedElement(const std::string& name);
    XmlWriter&    endElement();

    XmlWriter& writeAttribute(const std::string& name, const std::string& attribute);
    XmlWriter& writeAttribute(const std::string& name, const char* attribute);
    XmlWriter& writeAttribute(const std::string& name, bool attribute);

    template <typename T>
    XmlWriter& writeAttribute(const std::string& name, const T& attribute) {
        std::stringstream rss;
        rss << attribute;
        return writeAttribute(name, rss.str());
    }

    XmlWriter& writeText(const std::string& text, bool indent = true, bool new_line = true);

    void ensureTagClosed(bool new_line = true);
    void writeDeclaration();

private:
    void newlineIfNecessary();

    bool                     m_tagIsOpen    = false;
    bool                     m_needsNewline = false;
    bool                     m_needsIndent  = false;
    std::vector<std::string> m_tags;
    std::string              m_indent;
    std::ostream&            m_os;
};

using uchar = unsigned char;

static size_t trailingBytes(unsigned char c) {
    if ((c & 0xE0) == 0xC0) {
        return 2;
    }
    if ((c & 0xF0) == 0xE0) {
        return 3;
    }
    if ((c & 0xF8) == 0xF0) {
        return 4;
    }
    throw std::runtime_error("Invalid multibyte utf-8 start byte encountered");
}

static uint32_t headerValue(unsigned char c) {
    if ((c & 0xE0) == 0xC0) {
        return c & 0x1F;
    }
    if ((c & 0xF0) == 0xE0) {
        return c & 0x0F;
    }
    if ((c & 0xF8) == 0xF0) {
        return c & 0x07;
    }
    throw std::runtime_error("Invalid multibyte utf-8 start byte encountered");
}

static void hexEscapeChar(std::ostream& os, unsigned char c) {
    std::ios_base::fmtflags f(os.flags());
    os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
       << static_cast<int>(c);
    os.flags(f);
}

XmlEncode::XmlEncode(const std::string& str, ForWhat forWhat) : m_str(str), m_forWhat(forWhat) {}

void XmlEncode::encodeTo(std::ostream& os) const {
    // Apostrophe escaping not necessary if we always use " to write attributes
    // (see: https://www.w3.org/TR/xml/#syntax)

    for (std::size_t idx = 0; idx < m_str.size(); ++idx) {
        uchar c = m_str[idx];
        switch (c) {
            case '<': os << "&lt;"; break;
            case '&': os << "&amp;"; break;

            case '>':
                // See: https://www.w3.org/TR/xml/#syntax
                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
                    os << "&gt;";
                else
                    os << c;
                break;

            case '\"':
                if (m_forWhat == ForAttributes)
                    os << "&quot;";
                else
                    os << c;
                break;

            default:
                // Check for control characters and invalid utf-8
                // Escape control characters in standard ascii, see:
                // https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
                    hexEscapeChar(os, c);
                    break;
                }

                // Plain ASCII: Write it to stream
                if (c < 0x7F) {
                    os << c;
                    break;
                }

                // UTF-8 territory
                // Check if the encoding is valid and if it is not, hex escape bytes.
                // Important: We do not check the exact decoded values for validity, only the
                // encoding format First check that this bytes is a valid lead byte: This means that
                // it is not encoded as 1111 1XXX Or as 10XX XXXX
                if (c < 0xC0 || c >= 0xF8) {
                    hexEscapeChar(os, c);
                    break;
                }

                auto encBytes = trailingBytes(c);
                // Are there enough bytes left to avoid accessing out-of-bounds memory?
                if (idx + encBytes - 1 >= m_str.size()) {
                    hexEscapeChar(os, c);
                    break;
                }
                // The header is valid, check data
                // The next encBytes bytes must together be a valid utf-8
                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
                bool     valid = true;
                uint32_t value = headerValue(c);
                for (std::size_t n = 1; n < encBytes; ++n) {
                    uchar nc = m_str[idx + n];
                    valid &= ((nc & 0xC0) == 0x80);
                    value = (value << 6) | (nc & 0x3F);
                }

                if (
                    // Wrong bit pattern of following bytes
                    (!valid) ||
                    // Overlong encodings
                    (value < 0x80) ||
                    (value < 0x800 &&
                     encBytes > 2) ||  // removed "0x80 <= value &&" because redundant
                    (0x800 < value && value < 0x10000 && encBytes > 3) ||
                    // Encoded value out of range
                    (value >= 0x110000)) {
                    hexEscapeChar(os, c);
                    break;
                }

                // If we got here, this is in fact a valid(ish) utf-8 sequence
                for (std::size_t n = 0; n < encBytes; ++n) {
                    os << m_str[idx + n];
                }
                idx += encBytes - 1;
                break;
        }
    }
}

std::ostream& operator<<(std::ostream& os, const XmlEncode& xmlEncode) {
    xmlEncode.encodeTo(os);
    return os;
}

XmlWriter::ScopedElement::ScopedElement(XmlWriter* writer) : m_writer(writer) {}

XmlWriter::ScopedElement::ScopedElement(ScopedElement&& other) noexcept : m_writer(other.m_writer) {
    other.m_writer = nullptr;
}

XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=(ScopedElement&& other) noexcept {
    if (m_writer) {
        m_writer->endElement();
    }
    m_writer       = other.m_writer;
    other.m_writer = nullptr;
    return *this;
}

XmlWriter::ScopedElement::~ScopedElement() {
    if (m_writer) m_writer->endElement();
}

XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText(const std::string& text, bool indent,
                                                              bool new_line) {
    m_writer->writeText(text, indent, new_line);
    return *this;
}

XmlWriter::XmlWriter(std::ostream& os) : m_os(os) {}

XmlWriter::~XmlWriter() {
    while (!m_tags.empty()) endElement();
}

XmlWriter& XmlWriter::startElement(const std::string& name) {
    ensureTagClosed();
    newlineIfNecessary();
    m_os << m_indent << '<' << name;
    m_tags.push_back(name);
    m_indent += "  ";
    m_tagIsOpen = true;
    return *this;
}

XmlWriter::ScopedElement XmlWriter::scopedElement(const std::string& name) {
    ScopedElement scoped(this);
    startElement(name);
    return scoped;
}

XmlWriter& XmlWriter::endElement() {
    newlineIfNecessary();
    m_indent = m_indent.substr(0, m_indent.size() - 2);
    if (m_tagIsOpen) {
        m_os << "/>";
        m_tagIsOpen = false;
    } else {
        if (m_needsIndent)
            m_os << m_indent;
        else
            m_needsIndent = true;
        m_os << "</" << m_tags.back() << ">";
    }
    m_os << std::endl;
    m_tags.pop_back();
    return *this;
}

XmlWriter& XmlWriter::writeAttribute(const std::string& name, const std::string& attribute) {
    if (!name.empty() && !attribute.empty())
        m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"';
    return *this;
}

XmlWriter& XmlWriter::writeAttribute(const std::string& name, const char* attribute) {
    if (!name.empty() && attribute && attribute[0] != '\0')
        m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"';
    return *this;
}

XmlWriter& XmlWriter::writeAttribute(const std::string& name, bool attribute) {
    m_os << ' ' << name << "=\"" << (attribute ? "true" : "false") << '"';
    return *this;
}

XmlWriter& XmlWriter::writeText(const std::string& text, bool indent, bool new_line) {
    if (!text.empty()) {
        bool tagWasOpen = m_tagIsOpen;
        ensureTagClosed(new_line);
        if (tagWasOpen && indent) m_os << m_indent;
        m_os << XmlEncode(text);
        m_needsNewline = new_line;
        m_needsIndent  = new_line;
    }
    return *this;
}

void XmlWriter::ensureTagClosed(bool new_line) {
    if (m_tagIsOpen) {
        m_os << ">";
        if (new_line) m_os << std::endl;
        m_tagIsOpen = false;
    }
}

void XmlWriter::writeDeclaration() { m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; }

void XmlWriter::newlineIfNecessary() {
    if (m_needsNewline) {
        m_os << std::endl;
        m_needsNewline = false;
    }
}

// =================================================================================================
// End of copy-pasted code from Catch
// =================================================================================================
}  // namespace detail

class XmlReporter : public IReporter {
public:
    detail::XmlWriter xml;

    struct TestCaseTemp {
        const TestCase* tc;
    };

    std::vector<TestCaseTemp> current;

    virtual std::string getName() const override { return "xml"; }

    // There are a list of events
    virtual void testStart() override {
        xml.writeDeclaration();
        xml.startElement("ZeroErr")
            .writeAttribute("binary", ut.binary)
            .writeAttribute("version", ZEROERR_VERSION_STR);
        xml.startElement("TestSuite");
    }

    virtual void testCaseStart(const TestCase& tc, std::stringbuf&) override {
        current.push_back({&tc});
        xml.startElement("TestCase")
            .writeAttribute("name", tc.name)
            .writeAttribute("filename", tc.file)
            .writeAttribute("line", tc.line)
            .writeAttribute("skipped", "false");
        if (ut.log_to_report) suspendLog();
    }

    virtual void testCaseEnd(ZEROERR_UNUSED(const TestCase&), std::stringbuf& sb,
                             const TestContext& ctx, int) override {
        current.pop_back();
        xml.scopedElement("Result")
            .writeAttribute("time", 0)
            .writeAttribute("passed", ctx.passed)
            .writeAttribute("warnings", ctx.warning)
            .writeAttribute("failed", ctx.failed)
            .writeAttribute("skipped", ctx.skipped);
        xml.scopedElement("ResultAsserts")
            .writeAttribute("passed", ctx.passed_as)
            .writeAttribute("warnings", ctx.warning_as)
            .writeAttribute("failed", ctx.failed_as)
            .writeAttribute("skipped", ctx.skipped_as);
        xml.scopedElement("Output").writeText(sb.str());

        if (ut.log_to_report) {
            xml.startElement("Log");
            LogIterator begin = LogStream::getDefault().begin();
            LogIterator end   = LogStream::getDefault().end();
            for (auto p = begin; p != end; ++p) {
                xml.startElement("LogEntry")
                    .writeAttribute("function", p->info->function)
                    .writeAttribute("line", p->info->line)
                    .writeAttribute("message", p->info->message)
                    .writeAttribute("category", p->info->category)
                    .writeAttribute("severity", p->info->severity);
                for (auto pair : p->getData()) {
                    xml.scopedElement(pair.first).writeText(pair.second, false, false);
                }
                xml.endElement();
            }
            xml.endElement();
            resumeLog();
        }
        xml.endElement();
    }

    virtual void subCaseStart(const TestCase& tc, std::stringbuf& sb) override {
        testCaseStart(tc, sb);
    }

    virtual void subCaseEnd(const TestCase& tc, std::stringbuf& sb, const TestContext& ctx,
                            int) override {
        testCaseEnd(tc, sb, ctx, 0);
    }

    virtual void testEnd(const TestContext& tc) override {
        xml.endElement();

        xml.startElement("OverallResults")
            .writeAttribute("errors", tc.failed_as)
            .writeAttribute("failures", tc.failed)
            .writeAttribute("tests", tc.passed + tc.failed + tc.warning);
        xml.endElement();

        xml.endElement();
    }

    XmlReporter(UnitTest& ut) : IReporter(ut), xml(std::cout) {}
};

IReporter* IReporter::create(const std::string& name, UnitTest& ut) {
    if (name == "console") return new ConsoleReporter(ut);
    if (name == "xml") return new XmlReporter(ut);
    return nullptr;
}


class SkipDecorator : public Decorator {
    bool onExecution(const TestCase&) override { return true; }
};

Decorator* skip(bool isSkip) {
    static SkipDecorator skip_dec;
    if (isSkip) return &skip_dec;
    return nullptr;
}

class TimeoutDecorator : public Decorator {
    float timeout;

public:
    TimeoutDecorator() : timeout(0) {}
    TimeoutDecorator(float timeout) : timeout(timeout) {}

    bool onFinish(const TestCase& tc, const TestContext& ctx) override {
        if (ctx.duration > std::chrono::duration<double>(timeout)) {
            std::cerr << FgRed <<  "Timeout: " << Reset << ctx.duration.count() << "s > " << timeout << "s" << std::endl;
            return true;
        }
        return false;
    }
};

Decorator* timeout(float timeout) {
    static std::map<float, TimeoutDecorator> timeout_dec;
    if (timeout_dec.find(timeout) == timeout_dec.end()) {
        timeout_dec[timeout] = TimeoutDecorator(timeout);
    }
    return &timeout_dec[timeout];
}

class FailureDecorator : public Decorator {
public:
    enum FailureType { may_fail, should_fail };
    FailureDecorator(FailureType type) : type(type) {}

private:
    FailureType type;
};


Decorator* may_fail(bool isMayFail) {
    static FailureDecorator may_fail_dec(FailureDecorator::may_fail);
    if (isMayFail) return &may_fail_dec;
    return nullptr;
}

Decorator* should_fail(bool isShouldFail) {
    static FailureDecorator should_fail_dec(FailureDecorator::should_fail);
    if (isShouldFail) return &should_fail_dec;
    return nullptr;
}


}  // namespace zeroerr


int main(int argc, const char** argv) {
    zeroerr::UnitTest().parseArgs(argc, argv).run();
    std::_Exit(0);
}





#include <cstring>
#ifdef ZEROERR_ENABLE_FUZZING
extern "C" int LLVMFuzzerRunDriver(int* argc, char*** argv,
                                   int (*user_callback)(const uint8_t* data, size_t size));

extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size,
                                          unsigned int seed);
#endif

namespace zeroerr {
static IFuzzTest* current_fuzz_test = nullptr;
}  // namespace zeroerr

size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed) {
    const std::string mutated_data =
        zeroerr::current_fuzz_test->MutateData(data, size, max_size, seed);
    if (mutated_data.size() > max_size) {
        WARN("Mutated data is larger than the limit({limit}). Returning the original data ({ori})",
             max_size, size);
        return size;
    }
    memcpy(data, mutated_data.data(), mutated_data.size());
    return mutated_data.size();
}

namespace zeroerr {

void RunFuzzTest(IFuzzTest& fuzz_test, int seed, int runs, int max_len, int timeout,
                 int len_control) {
#ifdef ZEROERR_ENABLE_FUZZING
    current_fuzz_test  = &fuzz_test;
    int         argc   = 6;
    std::string argv[] = {
        "fuzztest",
        "-max_len=" + std::to_string(max_len),
        "-timeout=" + std::to_string(timeout),
        "-runs=" + std::to_string(runs),
        "-seed=" + std::to_string(seed),
        "-len_control=" + std::to_string(len_control),
    };
    char** argv_c = new char*[argc];
    for (int i = 0; i < argc; i++) {
        argv_c[i] = (char*)argv[i].c_str();
    }
    LOG("Running fuzz test");

    LLVMFuzzerRunDriver(&argc, &argv_c, [](const uint8_t* data, size_t size) -> int {
        LOG("Running RunOneTime");
        if (current_fuzz_test->should_stop()) {
            throw FuzzFinishedException();
        }
        current_fuzz_test->RunOneTime(data, size);
        return 0;
    });

    current_fuzz_test = nullptr;
#else
    (void) fuzz_test;
    (void) seed;
    (void) runs;
    (void) max_len;
    (void) timeout;
    (void) len_control;
#endif
}

}  // namespace zeroerr



#include <sstream>
namespace zeroerr {

IRObject* IRObject::alloc(size_t size) {
    IRObject* list = new IRObject[size + 1];
    list[0].i      = size;
    return list + 1;
}

char* IRObject::alloc_str(size_t size) {
    char* s = new char[size + 1];
    s[size] = 0;
    return s;
}

static std::string escape(std::string str) {
    std::string result;
    for (char c : str) {
        switch (c) {
            case ' ': result += "\\s"; break;
            case '\n': result += "\\n"; break;
            case '\t': result += "\\t"; break;
            case '\r': result += "\\r"; break;
            case '\f': result += "\\f"; break;
            case '\v': result += "\\v"; break;
            case '\\': result += "\\\\"; break;
            case '"': result += "\\\""; break;
            default: {
                if (c < 32 || c > 126) {
                    result += "\\x";
                    result += "0123456789abcdef"[c >> 4];
                    result += "0123456789abcdef"[c & 15];
                    continue;
                }
                result += c;
            }
        }
    }
    return result;
}

static std::string unescape(std::string str) {
    std::string result;
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == '\\') {
            switch (str[++i]) {
                case 's': result += ' '; break;
                case 'n': result += '\n'; break;
                case 't': result += '\t'; break;
                case 'r': result += '\r'; break;
                case 'f': result += '\f'; break;
                case 'v': result += '\v'; break;
                case '\\': result += '\\'; break;
                case '"': result += '"'; break;
                case 'x': {
                    char c = 0;
                    for (int j = 0; j < 2; ++j) {
                        c *= 16;
                        if (str[i + 1] >= '0' && str[i + 1] <= '9') {
                            c += str[i + 1] - '0';
                        } else if (str[i + 1] >= 'a' && str[i + 1] <= 'f') {
                            c += str[i + 1] - 'a' + 10;
                        } else if (str[i + 1] >= 'A' && str[i + 1] <= 'F') {
                            c += str[i + 1] - 'A' + 10;
                        }
                    }
                    result += c;
                    i += 2;
                }
            }
        } else {
            result += str[i];
        }
    }
    return result;
}

static void to_string(IRObject obj, std::stringstream& ss) {
    switch (obj.type) {
        case IRObject::Type::Int: ss << obj.i; break;
        case IRObject::Type::Float: ss << obj.f << 'f'; break;
        case IRObject::Type::String: ss << '"' << escape(obj.s) << '"'; break;
        case IRObject::Type::ShortString: ss << '"' << escape(obj.ss) << '"'; break;
        case IRObject::Type::Object:
            ss << "{ ";
            auto c = obj.GetChildren();
            for (unsigned i = 0; i < c.size; ++i) {
                to_string(*(c.children + i), ss);
                ss << " ";
            }
            ss << "}";
            break;
    }
}

static IRObject from_string(std::stringstream& ss, std::string& token) {
    IRObject obj;
    if (token == "{") {
        std::vector<IRObject> children;
        while (ss >> token) {
            if (token == "}") break;
            IRObject child = from_string(ss, token);
            if (child.type == IRObject::Type::Undefined)
                return obj;
            children.push_back(child);
        }
        IRObject* child = IRObject::alloc(children.size());
        for (unsigned i = 0; i < children.size(); ++i) {
            child[i] = children[i];
        }
        obj.SetChildren(child);
        return obj;
    }
    if (token[0] == '"') {
        CHECK(token.size() > 1 AND token.back() == '"');
        obj.SetScalar(unescape(token.substr(1, token.size() - 2)));
        return obj;
    }
    if (token.back() == 'f') {
        obj.SetScalar(std::stod(token.substr(0, token.size() - 1)));
        return obj;
    }
    if (token[0] == '-' || (token[0] >= '0' && token[0] <= '9')) {
        obj.SetScalar(std::stoll(token));
        return obj;
    }
    return obj;
}

std::string IRObject::ToString(IRObject obj) {
    std::stringstream ss;
    to_string(obj, ss);
    return ss.str();
}

IRObject IRObject::FromString(std::string str) {
    std::stringstream ss(str);
    std::string       token;
    ss >> token;
    return from_string(ss, token);
}

std::vector<uint8_t> IRObject::ToBinary(IRObject obj) {
    std::vector<uint8_t> bin;
    return bin;
}

IRObject IRObject::FromBinary(std::vector<uint8_t> bin) {
    IRObject obj;
    return obj;
}


}  // namespace zeroerr





#include <cstring>
#include <iostream>
#include <map>
#include <random>
#include <stdexcept>


// #ifdef _WIN32
// #define ZEROERR_ETW 1
// #endif


namespace zeroerr {

// determines resolution of the given clock. This is done by measuring multiple times and returning
// the minimum time difference.
Clock::duration calcClockResolution(size_t numEvaluations) noexcept {
    auto              bestDuration = Clock::duration::max();
    Clock::time_point tBegin;
    Clock::time_point tEnd;
    for (size_t i = 0; i < numEvaluations; ++i) {
        tBegin = Clock::now();
        do {
            tEnd = Clock::now();
        } while (tBegin == tEnd);
        bestDuration = (std::min)(bestDuration, tEnd - tBegin);
    }
    return bestDuration;
}

// Calculates clock resolution once, and remembers the result
Clock::duration clockResolution() noexcept {
    static Clock::duration sResolution = calcClockResolution(20);
    return sResolution;
}

// helpers to get double values
template <typename T>
static inline double d(T t) noexcept {
    return static_cast<double>(t);
}
static inline double d(Clock::duration duration) noexcept {
    return std::chrono::duration_cast<std::chrono::duration<double, std::nano>>(duration).count();
}

struct BenchState {
    BenchState(Benchmark& bench) : bench(bench), stage(UnInit) {
        targetEpochTime = clockResolution() * bench.minimalResolutionMutipler;
        targetEpochTime = std::max(targetEpochTime, bench.mMinEpochTime);
        targetEpochTime = std::min(targetEpochTime, bench.mMaxEpochTime);
        numEpoch        = bench.epochs;
        numIteration    = 0;
        elapsed         = Clock::duration(0);
    }

    Benchmark& bench;

    enum { UnInit, WarmUp, UpScaling, Measurement } stage;

    Clock::duration elapsed;
    Clock::duration targetEpochTime;
    uint64_t        numIteration, numEpoch;

    Rng mRng{1024};

    bool isCloseEnoughForMeasurements() const noexcept {
        return elapsed * 3 >= targetEpochTime * 2;
    }


    uint64_t calcBestNumIters() noexcept {
        double Elapsed               = d(elapsed);
        double TargetRuntimePerEpoch = d(targetEpochTime);
        double NewIters              = TargetRuntimePerEpoch * d(numIteration) / Elapsed;

        NewIters *= (1.0 + 0.2 * mRng.uniform01());

        // +1 for correct rounding when casting and make sure there are at least 1 iteration
        return static_cast<uint64_t>(NewIters + 1);
    }

    void upscale() {
        if (elapsed * 10 < targetEpochTime) {
            // we are far below the target runtime. Multiply iterations by 10 (with overflow check)
            if (numIteration * 10 < numIteration) {
                // overflow :-(
                printf("iterations overflow. Maybe your code got optimized away?\n");
                numIteration = 0;
                return;
            }
            if (elapsed * 100 < targetEpochTime)
                numIteration *= 100;
            else
                numIteration *= 10;
        } else {
            numIteration = calcBestNumIters();
        }
    }

    void nextStage() noexcept {
        switch (stage) {
            case UnInit:
                if (bench.warmup != 0) {
                    stage        = WarmUp;
                    numIteration = bench.warmup;
                } else if (bench.iter_per_epoch != 0) {
                    stage        = Measurement;
                    numIteration = bench.iter_per_epoch;
                } else {
                    stage        = UpScaling;
                    numIteration = 1;
                }
                break;
            case WarmUp:
                if (bench.iter_per_epoch != 0) {
                    stage        = Measurement;
                    numIteration = bench.iter_per_epoch;
                } else if (isCloseEnoughForMeasurements()) {
                    stage        = Measurement;
                    numIteration = calcBestNumIters();
                } else {
                    stage = UpScaling;
                    nextStage();
                }
                break;
            case UpScaling:
                if (isCloseEnoughForMeasurements()) {
                    stage        = Measurement;
                    numIteration = calcBestNumIters();
                } else {
                    stage = UpScaling;
                    upscale();
                }
                break;
            case Measurement:
                if (numEpoch) {
                    numEpoch--;
                } else {
                    numIteration = 0;
                }
                break;
        }
    }

    BenchResult result;
};
BenchState* createBenchState(Benchmark& benchmark) { return new BenchState(benchmark); }
void        destroyBenchState(BenchState* state) { delete state; }

size_t getNumIter(BenchState* state) {
    state->nextStage();
    return state->numIteration;
}

void runIteration(BenchState* state) {
    auto& pc       = PerformanceCounter::inst();
    state->elapsed = pc.elapsed;
    pc.updateResults(state->numIteration);

    if (state->stage == BenchState::Measurement) {
        PerfCountSet<double> pcset;
        pcset.iterations    = d(state->numIteration);
        pcset.timeElapsed() = d(state->elapsed) / pcset.iterations;

        for (int i = 1; i < 7; ++i) {
            if (pc.has().data[i]) {
                pcset.data[i] = d(pc.val().data[i]) / pcset.iterations;
            }
        }

        state->result.epoch_details.push_back(pcset);
    }
}

void moveResult(BenchState* state, std::string name) {
    auto& pc           = PerformanceCounter::inst();
    state->result.name = name;
    state->result.has  = pc.has();

    state->bench.result.push_back(state->result);
    destroyBenchState(state);
}


PerfCountSet<double> BenchResult::average() const {
    PerfCountSet<double> avg;
    for (int i = 0; i < 7; ++i) {
        if (has.data[i]) {
            double sum = 0;
            for (auto& pcset : epoch_details) {
                sum += pcset.data[i];
            }
            sum /= epoch_details.size();
            avg.data[i] = sum;
        }
    }
    return avg;
}
PerfCountSet<double> BenchResult::min() const {
    PerfCountSet<double> min;
    for (int i = 0; i < 7; ++i) {
        if (has.data[i]) {
            double min_val = std::numeric_limits<double>::max();
            for (auto& pcset : epoch_details) {
                min_val = std::min(min_val, pcset.data[i]);
            }
            min.data[i] = min_val;
        }
    }
    return min;
}
PerfCountSet<double> BenchResult::max() const {
    PerfCountSet<double> max;
    for (int i = 0; i < 7; ++i) {
        if (has.data[i]) {
            double max_val = std::numeric_limits<double>::min();
            for (auto& pcset : epoch_details) {
                max_val = std::max(max_val, pcset.data[i]);
            }
            max.data[i] = max_val;
        }
    }
    return max;
}
PerfCountSet<double> BenchResult::mean() const {
    PerfCountSet<double> mean;

    return mean;
}

void Benchmark::report() {
    static const char* names[] = {
        "elapsed(ns)", "page faults", "cpu_cycles", "ctx switch", "inst", "branch", "branch misses",
    };
    std::cerr << "" << title << ":" << std::endl;

    std::vector<std::string> headers{""};
    for (unsigned i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
        if (result[0].has.data[i]) headers.push_back(names[i]);
    }
    Table output;
    output.set_header(headers);
    for (auto& row : result) {
        auto                row_avg = row.average();
        std::vector<double> values;
        for (int j = 0; j < 7; ++j)
            if (row.has.data[j]) values.push_back(row_avg.data[j]);
        output.add_row(row.name, values);
    }
    std::cerr << output.str() << std::endl;
}


namespace detail {
// Windows version of doNotOptimizeAway
// see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307
// see https://github.com/facebook/folly/blob/master/folly/Benchmark.h#L280
// see https://docs.microsoft.com/en-us/cpp/preprocessor/optimize
#if defined(_MSC_VER)
#pragma optimize("", off)
void doNotOptimizeAwaySink(void const*) {}
#pragma optimize("", on)
#endif

}  // namespace detail

#ifdef ZEROERR_PERF
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>

namespace detail {
struct LinuxPerformanceCounter {
    inline void beginMeasure() {
        if (mHasError) return;

        mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
        if (mHasError) return;

        mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
    }
    inline void endMeasure() {
        if (mHasError) return;

        mHasError = (-1 == ioctl(mFd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP));
        if (mHasError) return;

        auto const numBytes = sizeof(uint64_t) * mCounters.size();
        auto       ret      = read(mFd, mCounters.data(), numBytes);
        mHasError           = ret != static_cast<ssize_t>(numBytes);
    }


    // rounded integer division
    template <typename T>
    static inline T divRounded(T a, T divisor) {
        return (a + divisor / 2) / divisor;
    }

    static inline uint32_t mix(uint32_t x) noexcept {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        return x;
    }

    template <typename Op>
    void calibrate(Op&& op) {
        // clear current calibration data,
        for (auto& v : mCalibratedOverhead) {
            v = UINT64_C(0);
        }

        // create new calibration data
        auto newCalibration = mCalibratedOverhead;
        for (auto& v : newCalibration) {
            v = std::numeric_limits<uint64_t>::max();
        }
        for (size_t iter = 0; iter < 100; ++iter) {
            beginMeasure();
            op();
            endMeasure();
            if (mHasError) return;

            for (size_t i = 0; i < newCalibration.size(); ++i) {
                auto diff = mCounters[i];
                if (newCalibration[i] > diff) {
                    newCalibration[i] = diff;
                }
            }
        }

        mCalibratedOverhead = std::move(newCalibration);

        {
            // calibrate loop overhead. For branches & instructions this makes sense, not so much
            // for everything else like cycles. marsaglia's xorshift: mov, sal/shr, xor. Times 3.
            // This has the nice property that the compiler doesn't seem to be able to optimize
            // multiple calls any further. see https://godbolt.org/z/49RVQ5
            uint64_t const numIters = 100000U + (std::random_device{}() & 3);
            uint64_t       n        = numIters;
            uint32_t       x        = 1234567;

            beginMeasure();
            while (n-- > 0) {
                x = mix(x);
            }
            endMeasure();
            detail::doNotOptimizeAway(x);
            auto measure1 = mCounters;

            n = numIters;
            beginMeasure();
            while (n-- > 0) {
                // we now run *twice* so we can easily calculate the overhead
                x = mix(x);
                x = mix(x);
            }
            endMeasure();
            detail::doNotOptimizeAway(x);
            auto measure2 = mCounters;

            for (size_t i = 0; i < mCounters.size(); ++i) {
                // factor 2 because we have two instructions per loop
                auto m1 =
                    measure1[i] > mCalibratedOverhead[i] ? measure1[i] - mCalibratedOverhead[i] : 0;
                auto m2 =
                    measure2[i] > mCalibratedOverhead[i] ? measure2[i] - mCalibratedOverhead[i] : 0;
                auto overhead = m1 * 2 > m2 ? m1 * 2 - m2 : 0;

                mLoopOverhead[i] = divRounded(overhead, numIters);
            }
        }
    }


    struct Target {
        uint64_t* targetValue;
        bool      correctMeasuringOverhead;
        bool      correctLoopOverhead;
    };

    std::map<uint64_t, Target> mIdToTarget{};

    // start with minimum size of 3 for read_format
    std::vector<uint64_t> mCounters{3};
    std::vector<uint64_t> mCalibratedOverhead{3};
    std::vector<uint64_t> mLoopOverhead{3};

    uint64_t mTimeEnabledNanos = 0;
    uint64_t mTimeRunningNanos = 0;

    int  mFd       = -1;
    bool mHasError = false;

    ~LinuxPerformanceCounter() {
        if (mFd != -1) close(mFd);
    }

    bool monitor(perf_sw_ids swId, Target target) {
        return monitor(PERF_TYPE_SOFTWARE, swId, target);
    }

    bool monitor(perf_hw_id hwId, Target target) {
        return monitor(PERF_TYPE_HARDWARE, hwId, target);
    }

    bool monitor(uint32_t type, uint64_t eventid, Target target) {
        *target.targetValue = (std::numeric_limits<uint64_t>::max)();
        if (mHasError) return false;

        auto pea = perf_event_attr();
        std::memset(&pea, 0, sizeof(perf_event_attr));
        pea.type           = type;
        pea.size           = sizeof(perf_event_attr);
        pea.config         = eventid;
        pea.disabled       = 1;  // start counter as disabled
        pea.exclude_kernel = 1;
        pea.exclude_hv     = 1;

        pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED |
                          PERF_FORMAT_TOTAL_TIME_RUNNING;

        const int pid = 0;         // the current process
        const int cpu = -1;        // all CPUs
#if defined(PERF_FLAG_FD_CLOEXEC)  // since Linux 3.14
        const unsigned long flags = PERF_FLAG_FD_CLOEXEC;
#else
        const unsigned long flags = 0;
#endif

        auto fd = static_cast<int>(syscall(__NR_perf_event_open, &pea, pid, cpu, mFd, flags));
        if (-1 == fd) return false;
        // first call: set to fd, and use this from now on
        if (-1 == mFd) mFd = fd;

        uint64_t id = 0;
        if (-1 == ioctl(fd, PERF_EVENT_IOC_ID, &id)) return false;

        // insert into map, rely on the fact that map's references are constant.
        mIdToTarget.emplace(id, target);

        // prepare readformat with the correct size (after the insert)
        auto size = 3 + 2 * mIdToTarget.size();
        mCounters.resize(size);
        mCalibratedOverhead.resize(size);
        mLoopOverhead.resize(size);

        return true;
    }

    void updateResults(uint64_t numIters) {
        // clear old data
        for (auto& id_value : mIdToTarget) {
            *id_value.second.targetValue = UINT64_C(0);
        }

        if (mHasError) return;

        mTimeEnabledNanos = mCounters[1] - mCalibratedOverhead[1];
        mTimeRunningNanos = mCounters[2] - mCalibratedOverhead[2];

        for (uint64_t i = 0; i < mCounters[0]; ++i) {
            auto idx = static_cast<size_t>(3 + i * 2 + 0);
            auto id  = mCounters[idx + 1U];

            auto it = mIdToTarget.find(id);
            if (it != mIdToTarget.end()) {
                auto& tgt        = it->second;
                *tgt.targetValue = mCounters[idx];
                if (tgt.correctMeasuringOverhead) {
                    if (*tgt.targetValue >= mCalibratedOverhead[idx]) {
                        *tgt.targetValue -= mCalibratedOverhead[idx];
                    } else {
                        *tgt.targetValue = 0U;
                    }
                }
                if (tgt.correctLoopOverhead) {
                    auto correctionVal = mLoopOverhead[idx] * numIters;
                    if (*tgt.targetValue >= correctionVal) {
                        *tgt.targetValue -= correctionVal;
                    } else {
                        *tgt.targetValue = 0U;
                    }
                }
            }
        }
    }
};
}  // namespace detail
#endif


#ifdef ZEROERR_ETW
#define INITGUID
#define NOMINMAX
#include <Windows.h>
#include <evntrace.h>
#include <wmistr.h>

namespace detail {
struct WindowsPerformanceCounter {
    TRACEHANDLE             mTraceHandle;
    std::string             name = "ZeroErr ETW";
    PEVENT_TRACE_PROPERTIES traceProperties;

    inline void beginMeasure() {
        size_t buffersize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);

        traceProperties = (PEVENT_TRACE_PROPERTIES)malloc(buffersize);
        ZeroMemory(traceProperties, buffersize);

        traceProperties->Wnode.BufferSize    = buffersize;
        traceProperties->Wnode.Flags         = WNODE_FLAG_TRACED_GUID;
        traceProperties->Wnode.Guid          = SystemTraceControlGuid;
        traceProperties->Wnode.ClientContext = 1;  // QPC clock resolution

        traceProperties->BufferSize     = 32;  // 32 KB
        traceProperties->MinimumBuffers = 32;  // 32 buffers
        traceProperties->MaximumBuffers = 32;  // 32 buffers

        traceProperties->LogFileMode = EVENT_TRACE_BUFFERING_MODE;
        traceProperties->EnableFlags = EVENT_TRACE_FLAG_CSWITCH;

        traceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);

        ULONG status = StartTrace(&mTraceHandle, KERNEL_LOGGER_NAME, traceProperties);

        if (ERROR_SUCCESS != status) {
            if (ERROR_ALREADY_EXISTS == status) {
                printf("The NT Kernel Logger session is already in use.\n");
            } else {
                printf("EnableTrace() failed with %lu\n", status);
            }
            goto cleanup;
        }

        // I got those values from here:
        // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ke/profobj/kprofile_source.htm
        // TotalIssues TotalCycles CacheMisses BranchMispredictions
        unsigned long perf_counter[4] = {0x02, 0x13, 0x0A, 0x0B};
        TraceSetInformation(mTraceHandle, TracePmcCounterListInfo, perf_counter,
                            sizeof(perf_counter));

    cleanup:
        if (mTraceHandle) {
            status = ControlTrace(mTraceHandle, KERNEL_LOGGER_NAME, traceProperties,
                                  EVENT_TRACE_CONTROL_STOP);

            if (ERROR_SUCCESS != status) printf("ControlTrace(stop) failed with %lu\n", status);
        }
        if (traceProperties) {
            free(traceProperties);
            traceProperties = nullptr;
        }
    }

    inline void endMeasure() {
        StopTrace(mTraceHandle, KERNEL_LOGGER_NAME, traceProperties);
        if (traceProperties) {
            free(traceProperties);
            traceProperties = nullptr;
        }
    }
};
}  // namespace detail
#endif


PerformanceCounter::PerformanceCounter() {
    _has.timeElapsed() = true;  // this should be always available
#ifdef ZEROERR_PERF
    _perf        = new detail::LinuxPerformanceCounter();
    using Target = detail::LinuxPerformanceCounter::Target;

    // clang-format off
    _has.pageFaults()         = _perf->monitor(PERF_COUNT_SW_PAGE_FAULTS,         Target{&_val.pageFaults(),         true, false});
    _has.cpuCycles()          = _perf->monitor(PERF_COUNT_HW_CPU_CYCLES,          Target{&_val.cpuCycles(),          true, false});
    _has.contextSwitches()    = _perf->monitor(PERF_COUNT_SW_CONTEXT_SWITCHES,    Target{&_val.contextSwitches(),    true, false});
    _has.instructions()       = _perf->monitor(PERF_COUNT_HW_INSTRUCTIONS,        Target{&_val.instructions(),       true, true });
    _has.branchInstructions() = _perf->monitor(PERF_COUNT_HW_BRANCH_INSTRUCTIONS, Target{&_val.branchInstructions(), true, false});
    _has.branchMisses()       = _perf->monitor(PERF_COUNT_HW_BRANCH_MISSES,       Target{&_val.branchMisses(),       true, false});
    // clang-format on

    _perf->calibrate([] {
        auto before = Clock::now();
        auto after  = Clock::now();
        (void)before;
        (void)after;
    });

    if (_perf->mHasError) {
        // something failed, don't monitor anything.
        _has = PerfCountSet<bool>{};
    }
#endif

#ifdef ZEROERR_ETW
    win_perf = new detail::WindowsPerformanceCounter();

#endif
}
PerformanceCounter::~PerformanceCounter() {
#ifdef ZEROERR_PERF
    delete _perf;
#endif
}

PerformanceCounter& PerformanceCounter::inst() {
    static PerformanceCounter counter;
    return counter;
}

void PerformanceCounter::beginMeasure() {
#ifdef ZEROERR_PERF
    _perf->beginMeasure();
#endif
    _start = Clock::now();
}
void PerformanceCounter::endMeasure() {
    elapsed = Clock::now() - _start;
#ifdef ZEROERR_PERF
    _perf->endMeasure();
#endif
}
void PerformanceCounter::updateResults(uint64_t numIters) {
#ifdef ZEROERR_PERF
    _perf->updateResults(numIters);
#else
    (void)numIters;
#endif
}


}  // namespace zeroerr

#endif // ZEROERR_IMPLEMENTATION