#ifndef __LONGLONG_H__
#define __LONGLONG_H__

#ifdef __cpluspus
extern "C" {
#endif

#ifdef __PUREC__
#pragma warn -stv
#endif

#if defined(__PUREC__) || defined(__AHCC__) || 0
#define LL_HAVE_UINT64 0
#else
#define LL_HAVE_UINT64 1
#endif

#if LL_HAVE_UINT64

typedef int64_t ll_int64_t;
typedef uint64_t ll_uint64_t;
#define ll_uint64_max LL_UINT64_C(0xffffffffUL, 0xffffffffUL)

#define LL_UINT64_C(hi, lo) ll_uint64_make(hi, lo)

#define ll_uint64_add(a, b) ((a) + (b))
#define ll_uint64_sub(a, b) ((a) - (b))
#define ll_uint64_mul(a, b) ((a) * (b))
#define ll_uint64_div(a, b) ((a) / (b))
#define ll_uint64_mod(a, b) ((a) % (b))
#define ll_uint64_divmod(a, b, r) (*(r) = (a) % (b), (a) / (b))
#define ll_uint64_or(a, b) ((a) | (b))
#define ll_uint64_xor(a, b) ((a) ^ (b))
#define ll_uint64_and(a, b) ((a) & (b))
#define ll_uint64_neg(a) (-(a))
#define ll_uint64_not(a) (~(a))

#define ll_uint64_lshift(a, b) ((ll_uint64_t)(a) << (b))
#define ll_uint64_rshift(a, b) ((ll_uint64_t)(a) >> (b))
#define ll_int64_rshiftsigned(a, b) ((ll_int64_t)(a) >> (b))

#define ll_uint64_make(high, low) (((ll_uint64_t)(uint32_t)(high) << 32) + (ll_uint64_t)(size_t)(low))

#define ll_uint64_zero ll_uint64_make(0, 0)
#define ll_uint64_iszero(x) ((x) == 0)
#define ll_int64_isnegative(x) ((ll_int64_t)(x) < 0)

#define ll_uint64_eq(x, y) ((x) == (y))
#define ll_uint64_ge(x, y) ((x) >= (y))
#define ll_uint64_gt(x, y) ((x) > (y))
#define ll_uint64_le(x, y) ((x) <= (y))
#define ll_uint64_lt(x, y) ((x) < (y))

#define ll_uint64_to_int32(x) ((int32_t)(x))
#define ll_uint64_to_uint32(x) ((uint32_t)(x))
#define ll_uint64_high(x) ((uint32_t)((x) >> 32))
#define ll_uint64_to_size_t(x) ((size_t)(x))
#define ll_uint64_size_make(s) ll_uint64_make(0, s)

#else

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Waggregate-return"
#endif

typedef struct {
	uint32_t hi;
	uint32_t lo;
} ll_uint64_t;
typedef struct {
	int32_t hi;
	uint32_t lo;
} ll_int64_t;
extern ll_uint64_t ll_uint64_max;

#define LL_UINT64_C(hi, lo) { hi, lo }

ll_uint64_t ll_uint64_add(ll_uint64_t x, ll_uint64_t y); /* returns x + y */
ll_uint64_t ll_uint64_sub(ll_uint64_t x, ll_uint64_t y); /* returns x - y */
ll_uint64_t ll_uint64_mul(ll_uint64_t x, ll_uint64_t y); /* returns x * y */
ll_uint64_t ll_uint64_div(ll_uint64_t x, ll_uint64_t y); /* returns x / y */
ll_uint64_t ll_uint64_mod(ll_uint64_t x, ll_uint64_t y); /* returns x % y */
ll_uint64_t ll_uint64_divmod(ll_uint64_t x, ll_uint64_t y, ll_uint64_t *r); /* returns x / y, remainder in r */
ll_uint64_t ll_uint64_or(ll_uint64_t x, ll_uint64_t y); /* returns x | y */
ll_uint64_t ll_uint64_xor(ll_uint64_t x, ll_uint64_t y); /* returns x ^ y */
ll_uint64_t ll_uint64_and(ll_uint64_t x, ll_uint64_t y); /* returns x & y */
ll_uint64_t ll_uint64_neg(ll_uint64_t x);
ll_uint64_t ll_uint64_not(ll_uint64_t x);

ll_uint64_t ll_uint64_lshift(ll_uint64_t x, int bits);
ll_uint64_t ll_uint64_rshift(ll_uint64_t x, int bits);
ll_uint64_t ll_int64_rshiftsigned(ll_int64_t x, int bits);

int ll_uint64_eq(ll_uint64_t x, ll_uint64_t y);
int ll_uint64_ge(ll_uint64_t x, ll_uint64_t y);
int ll_uint64_gt(ll_uint64_t x, ll_uint64_t y);
#define ll_uint64_le(x, y) (!ll_uint64_gt(x, y))
#define ll_uint64_lt(x, y) (!ll_uint64_ge(x, y))

ll_uint64_t ll_uint64_make(uint32_t high, uint32_t low);

#define ll_uint64_zero ll_uint64_make(0, 0)
#define ll_uint64_iszero(x) ((x).hi == 0 && (x).lo == 0)
#define ll_int64_isnegative(a) ((int32_t)((a).hi) < 0)

#define ll_uint64_to_int32(x) ((int32_t)((x).lo))
#define ll_uint64_to_uint32(x) ((x).lo)
#define ll_uint64_high(x) ((x).hi)

#if SIZE_MAX > UINT32_MAX
static inline size_t ll_uint64_to_size_t(ll_uint64_t x)
{
	return ((size_t)x.hi << 32) | x.lo;
}
static inline ll_uint64_t ll_uint64_size_make(size_t s)
{
	return ll_uint64_make((s) >> 32, s);
}
#else
#define ll_uint64_to_size_t(x) ((x).lo)
#define ll_uint64_size_make(s) ll_uint64_make(0, s)
#endif

#endif /* LL_HAVE_UINT64 */

#define LL_UINT64_MAX LL_UINT64_C(0xffffffffUL, 0xffffffffUL)

#ifdef __cpluspus
}
#endif

#endif /* __LONGLONG_H__ */
