#ifndef R2_TYPES_BASE_H
#define R2_TYPES_BASE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <ctype.h>
#include <sys/types.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>

#define cut8 const uint8_t
#define ut64 uint64_t
#define st64 int64_t
#define ut32 uint32_t
#define st32 int32_t
#define ut16 uint16_t
#define st16 int16_t
#define ut8 uint8_t
#define st8 int8_t

#if defined(_MSC_VER)
# define R_ALIGNED(x) __declspec(align(x))
#else
# define R_ALIGNED(x) __attribute__((aligned(x)))
#endif

#if defined(R2_UEFI)
#elif defined(GNU_EFI)
#define R2_UEFI 1
#elif defined(EFIAPI)
/* EDK2/gnu-efi */
#define R2_UEFI 1
#elif defined(MDE_CPU_X64) || defined(MDE_CPU_AARCH64) || defined(MDE_CPU_IA32)
/* EDK2 macros */
#define R2_UEFI 1
#else
#define R2_UEFI 0
#endif

#if defined(__GNUC__)
#define R_LIKELY(x) __builtin_expect((size_t)(x),1)
#define R_UNLIKELY(x) __builtin_expect((size_t)(x),0)
#define R_MUSTUSE __attribute__((warn_unused_result))
#else
#define R_LIKELY(x) (size_t)(x)
#define R_UNLIKELY(x) (size_t)(x)
#define R_MUSTUSE
#endif

#define R_IGNORE_RETURN(x) if ((x)) {;}

// unaligned word access
typedef R_ALIGNED(1) ut16 uut16;
typedef R_ALIGNED(1) ut32 uut32;
typedef R_ALIGNED(1) ut64 uut64;
typedef R_ALIGNED(1) st16 ust16;
typedef R_ALIGNED(1) st32 ust32;
typedef R_ALIGNED(1) st64 ust64;

typedef union {
	ut8 v8;
	ut16 v16;
	ut32 v32;
	ut64 v64;
} utAny;

typedef struct _ut80 {
	ut64 Low;
	ut16 High;
} ut80;
typedef struct _ut96 {
	ut64 Low;
	ut32 High;
} ut96;
typedef struct _ut128 {
	ut64 Low;
	st64 High;
} ut128;
typedef struct _ut256 {
	ut128 Low;
	ut128 High;
} ut256;
typedef struct _utX {
	ut80 v80;
	ut96 v96;
	ut128 v128;
	ut256 v256;
} utX;

#include <stdbool.h>

/* limits */
#undef UT64_MAX
#undef UT64_GT0
#undef UT64_LT0
#undef UT64_MIN
#undef UT32_MAX
#undef UT32_MIN
#undef UT16_MIN
#undef UT8_MIN
#define ST64_MAX ((st64)0x7FFFFFFFFFFFFFFFULL)
#define ST64_MIN ((st64)(-ST64_MAX-1))
#define UT64_MAX ((ut64)0xFFFFFFFFFFFFFFFFULL)
#define UT64_GT0 ((ut64)0x8000000000000000ULL)
#define UT64_LT0 ((ut64)0x7FFFFFFFFFFFFFFFULL)
#define UT64_MIN 0ULL
#define UT64_32U 0xFFFFFFFF00000000ULL
#define UT64_16U 0xFFFFFFFFFFFF0000ULL
#define UT64_8U  0xFFFFFFFFFFFFFF00ULL
#define UT32_MIN 0U
#define UT16_MIN 0U
#define UT32_GT0 0x80000000U
#define UT32_LT0 0x7FFFFFFFU
#define ST32_MAX 0x7FFFFFFF
#define ST32_MIN (-ST32_MAX-1)
#define UT32_MAX 0xFFFFFFFFU
#define UT32_MIN 0U
#define ST16_MAX 0x7FFF
#define ST16_MIN (-ST16_MAX-1)
#define UT16_GT0 0x8000U
#define UT16_MAX 0xFFFFU
#define ST8_MAX  0x7F
#define ST8_MIN  (-ST8_MAX - 1)
#define UT8_GT0  0x80U
#define UT8_MAX  0xFFU
#define UT8_MIN  0x00U
#define ASCII_MIN 32
#define ASCII_MAX 127

#define UT24_MAX 0xFFFFFF
#define UT40_MAX 0xFFFFFFFFFFULL
#define UT48_MAX 0xFFFFFFFFFFFFULL
#define UT56_MAX 0xFFFFFFFFFFFFFFULL

#if SSIZE_MAX == ST32_MAX
#define SZT_MAX  UT32_MAX
#define SZT_MIN  UT32_MIN
#define SSZT_MAX  ST32_MAX
#define SSZT_MIN  ST32_MIN
#else
#define SZT_MAX  UT64_MAX
#define SZT_MIN  UT64_MIN
#define SSZT_MAX  ST64_MAX
#define SSZT_MIN  ST64_MIN
#endif

#define UT64_ALIGN(x) (x + (x - (x % sizeof (ut64))))
#define UT32_ALIGN(x) (x + (x - (x % sizeof (ut32))))
#define UT16_ALIGN(x) (x + (x - (x % sizeof (ut16))))

#define UT32_LO(x) ((ut32)((x)&UT32_MAX))
#define UT32_HI(x) ((ut32)(((ut64)(x))>>32)&UT32_MAX)

// point in range:   x---y---z   (is y inside [x,z]?)
#define R_BETWEEN(x,y,z) (((y)>=(x)) && ((y)<=(z)))
// interval overlap: [a..b] vs [x..y]   (do the two ranges touch?)
#define R_INBETWEEN(a,b,x,y) ((a)<=(y) && (b)>=(x))
#define R_ROUND(x,y) ((x)%(y))?(x)+((y)-((x)%(y))):(x)
#define R_DIM(x,y,z) (((x)<(y))?(y):((x)>(z))?(z):(x))
#ifndef R_MAX_DEFINED
#define R_MAX(x,y) (((x)>(y))?(x):(y))
#define R_MAX_DEFINED
#endif
#ifndef R_MIN_DEFINED
#define R_MIN(x,y) (((x)>(y))?(y):(x))
#define R_MIN_DEFINED
#endif
#define R_ABS(x) (((x)<0)?-(x):(x))
#define R_BTW(x,y,z) (((x)>=(y))&&((y)<=(z)))?y:x
#define R_CLAMP(v,min,max) ((v)<(min)?(min):((v)>(max)?(max):(v)))

#include "r_types_overflow.h"

/* copied from bithacks.h */
#define B_IS_SET(x, n)   (((x) & (1ULL << (n)))? 1: 0)
#define B_SET(x, n)      ((x) |= (1ULL << (n)))
#define B_EVEN(x)        (((x) & 1) == 0)
#define B_ODD(x)         (!B_EVEN((x)))
#define B_UNSET(x, n)    ((x) &= ~(1ULL << (n)))
#define B_TOGGLE(x, n)   ((x) ^= (1ULL << (n)))

#define B11111 31
#define B11110 30
#define B11101 29
#define B11100 28
#define B11011 27
#define B11010 26
#define B11001 25
#define B11000 24
#define B10111 23
#define B10110 22
#define B10101 21
#define B10100 20
#define B10011 19
#define B10010 18
#define B10001 17
#define B10000 16
#define B1111 15
#define B1110 14
#define B1101 13
#define B1100 12
#define B1011 11
#define B1010 10
#define B1001 9
#define B1000 8
#define B0111 7
#define B0110 6
#define B0101 5
#define B0100 4
#define B0011 3
#define B0010 2
#define B0001 1
#define B0000 0
#undef B
#define B4(a,b,c,d) ((a<<12)|(b<<8)|(c<<4)|(d))

/* portable non-c99 inf/nan types */
#ifndef INFINITY
#define INFINITY (1.0f/0.0f)
#endif

#ifndef NAN
#define NAN (0.0f/0.0f)
#endif

#define F32_NAN   (strtof("NAN", NULL))
#define F32_PINF  (strtof("INF", NULL))
#define F32_NINF  (-strtof("INF", NULL))
#define F64_NAN   (strtod("NAN", NULL))
#define F64_PINF  (strtod("INF", NULL))
#define F64_NINF  (-strtod("INF", NULL))
#define F128_NAN  (strtold("NAN", NULL))
#define F128_PINF (strtold("INF", NULL))
#define F128_NINF (-strtold("INF", NULL))

/* A workaround against libc headers redefinition of __attribute__:
 * Standard include has lines like
 * #if (GCC_VERSION < 2007)
 * # define __attribute__(x)
 * #endif
 * So we have do remove this define for TinyCC compiler
 */
#if defined(__TINYC__) && (GCC_VERSION < 2007)
#undef __attribute__
#endif

#ifdef _MSC_VER
#define R_PACKED( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
#elif defined(__GNUC__) || defined(__TINYC__)
#define R_PACKED( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#define R_UNWRAP2(a,b) ((a)? a->b: NULL)
#define R_UNWRAP3(a,b,c) ((a)? a->b? a->b->c: NULL: NULL)
#define R_UNWRAP4(a,b,c,d) ((a)? a->b? a->b->c? a->b->c->d: NULL: NULL: NULL)
#define R_UNWRAP5(a,b,c,d,e) ((a)? a->b? a->b->c? a->b->c->d? a->b->c->d->e: NULL: NULL: NULL: NULL)
#define R_UNWRAP6(a,b,c,d,e,f) ((a)? a->b? a->b->c? a->b->c->d? a->b->c->d->e? a->b->c->d->e: NULL: NULL: NULL: NULL: NULL)

#ifdef __GNUC__
#define R_UNUSED __attribute__((__unused__))
#define R_WEAK __attribute__ ((weak))
#else
#define R_UNUSED /* unused */
#define R_WEAK /* weak */
#endif

#if APPLE_SDK_IPHONESIMULATOR
#undef LIBC_HAVE_FORK
#define LIBC_HAVE_FORK 0
#undef DEBUGGER
#define DEBUGGER 0
#endif

typedef ut64 RVPack;
typedef ut8 RVPackItem;

#define R_VPACK_SIZE 8
#define R_VPACK_MASK 0xff
#define R_VPACK1(x) ((x) * 1ULL)
#define R_VPACK2(x,y) (R_VPACK1 (x) | (R_VPACK1 (y) << R_VPACK_SIZE))
#define R_VPACK3(x,y,z) (R_VPACK2 (x, y) | (R_VPACK1 (z) << (R_VPACK_SIZE * 2)))
#define R_VPACK4(x,y,z,q) (R_VPACK3 (x, y, z) | (R_VPACK1 (q) << (R_VPACK_SIZE * 3)))
#define R_VPACK_FIRST(x) ((RVPackItem)((x) & R_VPACK_MASK))
#define R_VPACK_AT(x,n) ((RVPackItem)(((RVPack)(x) >> (R_VPACK_SIZE * (n))) & R_VPACK_MASK))

static inline bool r_vpack_has(RVPack pack, RVPackItem item) {
	while (pack) {
		if (R_VPACK_FIRST (pack) == item) {
			return true;
		}
		pack >>= R_VPACK_SIZE;
	}
	return false;
}

static inline RVPack r_vpack_add(RVPack pack, RVPackItem item) {
	if (!item || r_vpack_has (pack, item)) {
		return pack;
	}
	int shift;
	for (shift = 0; shift < (int)(sizeof (pack) * 8); shift += R_VPACK_SIZE) {
		if (!((pack >> shift) & R_VPACK_MASK)) {
			return pack | ((RVPack)item << shift);
		}
	}
	return pack;
}

#define R_VPACK_HAS(x,y) r_vpack_has (x, y)
#define R_VPACK_HAS_CONST(x,y) ((y) && ( \
	R_VPACK_AT (x, 0) == (y) || R_VPACK_AT (x, 1) == (y) || \
	R_VPACK_AT (x, 2) == (y) || R_VPACK_AT (x, 3) == (y) || \
	R_VPACK_AT (x, 4) == (y) || R_VPACK_AT (x, 5) == (y) || \
	R_VPACK_AT (x, 6) == (y) || R_VPACK_AT (x, 7) == (y)))

typedef RVPack RSysBits;
typedef RVPackItem RSysBitValue;

#define R_SYS_BITS_SIZE R_VPACK_SIZE
#define R_SYS_BITS_MASK R_VPACK_MASK
#define R_SYS_BITS_PACK(x) R_VPACK1 (x)
#define R_SYS_BITS_PACK1(x) R_VPACK1 (x)
#define R_SYS_BITS_PACK2(x,y) R_VPACK2 (x, y)
#define R_SYS_BITS_PACK3(x,y,z) R_VPACK3 (x, y, z)
#define R_SYS_BITS_PACK4(x,y,z,q) R_VPACK4 (x, y, z, q)
#define R_SYS_BITS_CHECK(x,y) R_VPACK_HAS_CONST (x, y)
#define R_SYS_BITS_CHECK3(x,a,b,c) (R_VPACK_HAS_CONST (x, a) || R_VPACK_HAS_CONST (x, b) || R_VPACK_HAS_CONST (x, c))


#define HEAPTYPE(x) \
	static x* x##_new(x n) {\
		x *m = malloc(sizeof (x));\
		return m? *m = n, m: m; \
	}

#define R_DIRTY_SET(x) (x)->is_dirty = true
#define R_DIRTY_UNSET(x) (x)->is_dirty = false
#define R_DIRTY_CHECK(x) (x)->is_dirty
#define R_DIRTY_VAR bool is_dirty

#define R_TAG(x) (void*)((size_t)(x)|1)
#define R_UNTAG(x) (void*)((((size_t)(x))&(size_t)-2))
#define R_TAG_FREE(x) do { if (!((size_t)(x)&1)) { R_FREE(x); }} while(0)
#define R_TAG_NOP(x) untagged_pointer_check(x)
#define R_IS_TAGGED(x) ((size_t)(x)&1)
#define R_TAGGED
#if R_CHECKS_LEVEL == 0
static inline void *untagged_pointer_check(void *x) {
	return x;
}
#else
static inline void *untagged_pointer_check(void *x) {
	if (R_IS_TAGGED(x)) {
		int *p = (int*)0; *p = 0;
	}
	return x;
}
#endif

#define ALLOC_SIZE_LIMIT 0xffffff

#ifdef __cplusplus
}
#endif

#endif // R2_TYPES_BASE_H
