#ifndef  __COMMON_ASSERT_H__
#define  __COMMON_ASSERT_H__

#include <stddef.h>                // for size_t
#include <stdint.h>                // for uint64_t
#include "simplest-defs.h"  // for STRINGIFY

// Use CHECK for checking something that you really want checked.
//
// Use ASSERT for checking something that you want checked, but the
// functionality of the program won't be compromised if the predicate isn't
// checked, or even run.  It should be correct to define ASSERT as "#define
// ASSERT(x) ((void)0)".  This means that you should not put side effects into
// ASSERT predicates.
//
// Use ASSERT_WITH_SIDE_EFFECTS for checking something you want checked, but
// there may be side effects in the predicate, so we need need to evaluate the
// predicate even if NOASSERT is set.
//
// See wisdom.tex for more discussion.

#ifndef CHECK_ASSERT_SIDE_EFFECTS
#define CHECK_ASSERT_SIDE_EFFECTS 0
#endif

#ifdef                                      NOASSERT
    #define ASSERT(x) ((0) ? ((void)(x), (void)(0)) : (void)(0))
    #define ASSERT0 ASSERT
    #define ASSERTv(x, typeof_val, val) ((0) ? ((void)(x), (void)(val), (void)(0)) : (void)(0))
    #define ASSERT_EQ_u(a, b) ((0) ? ((void)(a), (void)(b), (void)(0))      \
                               : (void)(0))
    #define ASSERT_EQ_i(a, b) ((0) ? ((void)(a), (void)(b), (void)(0))      \
                           : (void)(0))
    #define ASSERT_WITH_SIDE_EFFECTS(expr) ((void)expr)
#elif     !CHECK_ASSERT_SIDE_EFFECTS // && !NOASSERT
    #define ASSERT CHECK
    #define ASSERT0 CHECK0
    #define ASSERTv CHECKv
    #define ASSERT_EQ_u CHECK_EQ_u
    #define ASSERT_EQ_i CHECK_EQ_i
    #define ASSERT_WITH_SIDE_EFFECTS ASSERT
#else  //  CHECK_ASSERT_SIDE_EFFECTS    && !NOASSERT
    // See https://stackoverflow.com/questions/10593492/catching-assert-with-side-effects
    // for this trick for noticing that you have an ASSERT with side effects.
    extern int not_needed_if_expr_has_no_side_effects;
    #define ASSERT(expr) ((void)(not_needed_if_expr_has_no_side_effects || (expr)))
    #define ASSERT0(expr) ASSERT((expr) == 0)
    #define ASSERTv(x, typeof_val, val) ASSERT(x)
    #define ASSERT_EQ_u(a, b) ASSERT((a) == (b))
    #define ASSERT_EQ_i(a, b) ASSERT((a) == (b))
    #define ASSERT_WITH_SIDE_EFFECTS(expr) ((void)(expr))
#endif //  CHECK_ASSERT_SIDE_EFFECTS    && !NOASSERT

#define CHECK(x) ((x) ? (void) (0) : assert_fail (STRINGIFY(x), __FILE__, __LINE__, __PRETTY_FUNCTION__))
#define CHECK0(x) CHECK((x) == 0)
/* Check and print variable of specified type. */
#define CHECKv(x, typeof_val, val) ((x) ? (void) (0) : assert_fail_ ## typeof_val \
    (STRINGIFY(x), #val, val, __FILE__, __LINE__, __PRETTY_FUNCTION__))
#define CHECKvv(x, fmt_args...) ((x) ? (void)(0) : assert_fail_v(STRINGIFY(x), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt_args) )
#define UNREACHABLE() CHECK("UNREACHABLE" && 0)

void assert_fail(const char *assertion,
                 const char *file, size_t line, const char *function)
        __attribute__ ((__noreturn__));
void assert_fail_int(const char *assertion, const char *str_var, int var,
                     const char *file, size_t line, const char *function)
    __attribute__ ((__noreturn__));
void assert_fail_v(const char *assertion,
                   const char *file,
                   size_t line,
                   const char *function,
                   const char *fmt_string,
                   ...)
    __attribute__ ((__noreturn__))
    __attribute__ ((format (printf, 5, 6)));
void
check_eq_u(uintmax_t a, uintmax_t b,
           const char *file, size_t line, const char *function,
           const char *assertion);

#define CHECK_EQ_u(a, b) (                                            \
        check_eq_u((uintmax_t)(a), (uintmax_t)(b),                    \
                     __FILE__, __LINE__, __PRETTY_FUNCTION__,         \
                   STRINGIFY(a) " == " STRINGIFY(b)))


void
check_eq_i(intmax_t a, intmax_t b,
           const char *file, size_t line, const char *function,
           const char *assertion);

#define CHECK_EQ_i(a, b) (                                            \
        check_eq_i((intmax_t)(a), (intmax_t)(b),                      \
                   __FILE__, __LINE__, __PRETTY_FUNCTION__,           \
                   STRINGIFY(a) " == " STRINGIFY(b)))


#endif
