#ifndef __LONGLONG_H__
#define __LONGLONG_H__

#ifdef __cpluspus
extern "C" {
#endif

#ifdef __PUREC__
#pragma warn -stv
#endif

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

#if LL_HAVE_UINT64

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

#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_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_int64_to_float(x) ((float)(x))
#define ll_uint64_to_float(x) ((float)(x))

#define ll_int64_to_double(x) ((double)(x))
#define ll_uint64_to_double(x) ((double)(x))

#define ll_double_to_uint64(x) ((ll_uint64_t)(x))

#else

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, size_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) (((a).hi & 0x80000000UL) != 0)
#define ll_uint64_isnegative(a) (((a).hi & 0x80000000UL) != 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)
#define ll_uint64_to_size_t(x) ((x).lo)

float ll_int64_to_float(ll_int64_t x);
float ll_uint64_to_float(ll_uint64_t x);
#define ll_int64_to_float(x) ((float)(int32_t)ll_uint64_to_uint32(x))
#define ll_uint64_to_float(x) ((float)ll_uint64_to_uint32(x))

double ll_int64_to_double(ll_int64_t x);
double ll_uint64_to_double(ll_uint64_t x);

ll_uint64_t ll_double_to_uint64(double x);

#endif /* LL_HAVE_UINT64 */

#define LL_UINT64_MAX LL_UINT64_C(UINT32_C(0xffffffff), UINT32_C(0xffffffff))

#ifdef __cpluspus
}
#endif

#endif /* __LONGLONG_H__ */
