/*
 * Integer arithmetic evaluation.
 *
 * (c) Thomas Pornin 2002
 *
 * 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.
 * 4. The name of the authors may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT 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 AUTHORS 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.
 *
 */

#include <limits.h>
#include "arith.h"

#define ARITH_OCTAL(x)   ((x) >= '0' && (x) <= '7')
#define ARITH_OVAL(x)    ((x) - '0')
#define ARITH_DECIM(x)   ((x) >= '0' && (x) <= '9')
#define ARITH_DVAL(x)    ((x) - '0')
#define ARITH_HEXAD(x)   (ARITH_DECIM(x) \
                         || (x) == 'a' || (x) == 'A' \
                         || (x) == 'b' || (x) == 'B' \
                         || (x) == 'c' || (x) == 'C' \
                         || (x) == 'd' || (x) == 'D' \
                         || (x) == 'e' || (x) == 'E' \
                         || (x) == 'f' || (x) == 'F')
#define ARITH_HVAL(x)    (ARITH_DECIM(x) ? ARITH_DVAL(x) \
                         : (x) == 'a' || (x) == 'A' ? 10 \
                         : (x) == 'b' || (x) == 'B' ? 11 \
                         : (x) == 'c' || (x) == 'C' ? 12 \
                         : (x) == 'd' || (x) == 'D' ? 13 \
                         : (x) == 'e' || (x) == 'E' ? 14 : 15)

#ifdef NATIVE_SIGNED
/* ====================================================================== */
/* Arithmetics with native types */
/* ====================================================================== */

/*
 * The following properties are imposed by the C standard:
 *
 * -- Arithmetics on the unsigned type should never overflow; every
 * result is reduced modulo some power of 2. The macro NATIVE_UNSIGNED_BITS
 * should have been defined to that specific exponent.
 *
 * -- The signed type should use either two's complement, one's complement
 * or a sign bit and a magnitude. There should be an integer N such that
 * the maximum signed value is (2^N)-1 and the minimum signed value is
 * either -(2^N) or -((2^N)-1). -(2^N) is possible only for two's complement.
 *
 * -- The maximum signed value is at most equal to the maximum unsigned
 * value.
 *
 * -- Trap representations can only be:
 *    ** In two's complement, 1 as sign bit and 0 for all value bits.
 *       This can happen only if the minimum signed value is -((2^N)-1).
 *    ** In one's complement, all bits set to 1.
 *    ** In mantissa + sign, sign bit to 1 and 0 for all value bits.
 * Unsigned values have no trap representation achievable with numerical
 * operators. Only signed values can have such representations, with
 * operators &, |, ^, ~, << and >>. If trap representations are possible,
 * such occurrences are reported as warnings.
 *
 * -- The operators +, -, * and << may overflow or underflow on signed
 * quantities, which is potentially an error. A warning is emitted.
 *
 * -- The operator >> yields an implementation-defined result on
 * signed negative quantities. Usually, the sign is extended, but this
 * is not guaranteed. A warning is emitted.
 *
 * -- The operators / and % used with a second operand of 0 cannot work.
 * An error is emitted when such a call is performed. Furthermore, in
 * two's complemement representation, with NATIVE_SIGNED_MIN == -(2^N)
 * for some N, the expression `NATIVE_SIGNED_MIN / (-1)' yields an
 * unrepresentable result, which is also an error.
 *
 *
 * For the value checks, we need to consider those different cases. So
 * we calculate the following macros:
 *   -- TWOS_COMPLEMENT: is 1 if representation is two's complement, 0
 *      otherwise.
 *   -- ONES_COMPLEMENT: is 1 if representation is one's complement, 0
 *      otherwise.
 *   -- SIGNED_IS_BIGGER: 1 if the maximum signed value is equal to the
 *      maximum unsigned value, 0 otherwise. NATIVE_SIGNED_MAX cannot
 *      exceed the maximum unsigned value. If SIGNED_IS_BIGGER is 0, then
 *      the maximum unsigned value is strictly superior to twice the
 *      value of NATIVE_SIGNED_MAX (e.g. 65535 to 32767).
 *   -- TRAP_REPRESENTATION: 1 if a trap representation is possible, 0
 *      otherwise. The only way trap representations are guaranteed
 *      impossible is when TWOS_COMPLEMENT is set, and NATIVE_SIGNED_MIN
 *      is equal to -NATIVE_SIGNED_MAX - 1.
 *
 * Those macros are calculated by some preprocessor directives. This
 * supposes that the implementation conforms to C99. Rules on preprocessing
 * were quite looser in C90, and it could be that an old compiler, used
 * for a cross-compiling task, does not get those right. Therefore, if
 * ARCH_DEFINED is defined prior to the inclusion of this file, those
 * four macros are supposed to be already defined. Otherwise they are
 * (re)defined. The macro ARCH_TRAP_DEFINED has the same meaning, but
 * is limited to the TRAP_REPRESENTATION macro (if ARCH_TRAP_DEFINED is
 * defined, the macro TRAP_REPRESENTATION is supposed to be already
 * defined; the three other macros are recalculated).
 *
 *
 * To sum up:
 * -- Whenever a division operator (/ or %) is invoked and would yield
 * an unrepresentable result, ARITH_ERROR() is invoked.
 * -- With ARITHMETIC_CHECKS undefined, ARITH_WARNING() is never invoked.
 * -- With ARITHMETIC_CHECKS defined:
 *    ** If ARCH_DEFINED is defined, the including context must provide
 *       the macros TWOS_COMPLEMENT, ONES_COMPLEMENT, SIGNED_IS_BIGGER
 *       and TRAP_REPRESENTATION.
 *    ** Otherwise, if ARCH_TRAP_DEFINED is defined, the including context
 *       must provide the macro TRAP_REPRESENTATION.
 *    The code then detects all operator invokations that would yield an
 *    overflow, underflow, trap representation, or any implementation
 *    defined result or undefined behaviour. The macro ARITH_WARNING() is
 *    invoked for each detection.
 * -- Trap representation detection code supposes that the operands are
 * _not_ trap representation.
 */

#ifndef ARCH_DEFINED

#undef TWOS_COMPLEMENT
#undef ONES_COMPLEMENT
#undef SIGNED_IS_BIGGER
#ifndef ARCH_TRAP_DEFINED
#undef TRAP_REPRESENTATION
#endif

#if (-1) & 3 == 3
/*
 * Two's complement.
 */
#define TWOS_COMPLEMENT         1
#define ONES_COMPLEMENT         0
#ifndef ARCH_TRAP_DEFINED
#if NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX
#define TRAP_REPRESENTATION     0
#else
#define TRAP_REPRESENTATION     1
#endif
#endif

#elif (-1) & 3 == 2
/*
 * One's complement.
 */
#define TWOS_COMPLEMENT         0
#define ONES_COMPLEMENT         1
#ifndef ARCH_TRAP_DEFINED
#define TRAP_REPRESENTATION     1
#endif

#else
/*
 * Mantissa + sign.
 */
#define TWOS_COMPLEMENT         0
#define ONES_COMPLEMENT         0
#ifndef ARCH_TRAP_DEFINED
#define TRAP_REPRESENTATION     1
#endif

#endif

/*
 * Maximum native unsigned value. The first macro is for #if directives,
 * the second macro is for use as constant expression in C code.
 */
#define NATIVE_UNSIGNED_MAX     ((((NATIVE_UNSIGNED_ONE << (NATIVE_UNSIGNED_BITS - 1)) \
				- NATIVE_UNSIGNED_ONE) << 1) + NATIVE_UNSIGNED_ONE)
#define NATIVE_UNSIGNED_MAX_A   (((((arith_u)1 << (NATIVE_UNSIGNED_BITS - 1)) \
                                - (arith_u)1) << 1) + (arith_u)1)

#if NATIVE_SIGNED_MAX == NATIVE_UNSIGNED_MAX
#define SIGNED_IS_BIGGER        1
#else
#define SIGNED_IS_BIGGER        0
#endif

#endif

#undef NEGATIVE_IS_BIGGER
#if NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX
#define NEGATIVE_IS_BIGGER      1
#else
#define NEGATIVE_IS_BIGGER      0
#endif

/* sanity check: we cannot have a trap representation if we have
   two's complement with NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX */
#if TRAP_REPRESENTATION && NEGATIVE_IS_BIGGER
#error Impossible to get trap representations.
#endif

/* operations on the unsigned type */

ARITH_DECL_MONO_S_U(to_u) { useARI; return (arith_u)x; }
ARITH_DECL_MONO_I_U(fromint) { useARI; return (arith_u)x; }
ARITH_DECL_MONO_L_U(fromulong) { useARI; return (arith_u)x; }

ARITH_DECL_MONO_U_I(toint)
{
	useARI;
#if NATIVE_UNSIGNED_MAX > INT_MAX
	if (x > (arith_u)INT_MAX) return INT_MAX;
#endif
	return (int)x;
}

ARITH_DECL_MONO_U_L(toulong)
{
	useARI;
#if NATIVE_UNSIGNED_MAX > LONG_MAX
	if (x > (arith_u)LONG_MAX) return LONG_MAX;
#endif
	return (long)x;
}

ARITH_DECL_MONO_U_U(neg) { useARI; return -x; }
ARITH_DECL_MONO_U_U(not) { useARI; return ~x; }
ARITH_DECL_MONO_U_I(lnot) { useARI; return !x; }
ARITH_DECL_MONO_U_I(lval) { useARI; return x != 0; }

ARITH_DECL_BI_UU_U(plus) { useARI; return x + y; }
ARITH_DECL_BI_UU_U(minus) { useARI; return x - y; }
ARITH_DECL_BI_UU_I(lt) { useARI; return x < y; }
ARITH_DECL_BI_UU_I(leq) { useARI; return x <= y; }
ARITH_DECL_BI_UU_I(gt) { useARI; return x > y; }
ARITH_DECL_BI_UU_I(geq) { useARI; return x >= y; }
ARITH_DECL_BI_UU_I(same) { useARI; return x == y; }
ARITH_DECL_BI_UU_I(neq) { useARI; return x != y; }
ARITH_DECL_BI_UU_U(and) { useARI; return x & y; }
ARITH_DECL_BI_UU_U(xor) { useARI; return x ^ y; }
ARITH_DECL_BI_UU_U(or) { useARI; return x | y; }
ARITH_DECL_BI_UU_U(star) { useARI; return x * y; }

ARITH_DECL_BI_UI_U(lsh)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (y >= NATIVE_UNSIGNED_BITS)
		ARITH_WARNING(ARITH_EXCEP_LSH_W);
	else if (y < 0)
		ARITH_WARNING(ARITH_EXCEP_LSH_C);
#endif
	return x << y;
}

ARITH_DECL_BI_UI_U(rsh)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (y >= NATIVE_UNSIGNED_BITS)
		ARITH_WARNING(ARITH_EXCEP_RSH_W);
	else if (y < 0)
		ARITH_WARNING(ARITH_EXCEP_RSH_C);
#endif
	return x >> y;
}

ARITH_DECL_BI_UU_U(slash)
{
	useARI;
	if (y == 0) ARITH_ERROR(ARITH_EXCEP_SLASH_D);
	return x / y;
}

ARITH_DECL_BI_UU_U(pct)
{
	useARI;
	if (y == 0) ARITH_ERROR(ARITH_EXCEP_PCT_D);
	return x % y;
}

/* operations on the signed type */

ARITH_DECL_MONO_U_S(to_s)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if !SIGNED_IS_BIGGER
	if (x > (arith_u)NATIVE_SIGNED_MAX)
		ARITH_WARNING(ARITH_EXCEP_CONV_O);
#endif
#endif
	return (arith_s)x;
}

ARITH_DECL_MONO_I_S(fromint) { useARI; return (arith_s)x; }
ARITH_DECL_MONO_L_S(fromlong) { useARI; return (arith_s)x; }

ARITH_DECL_MONO_S_I(toint)
{
	useARI;
#if NATIVE_SIGNED_MIN < INT_MIN
	if (x < (arith_s)INT_MIN) return INT_MIN;
#endif
#if NATIVE_SIGNED_MAX > INT_MAX
	if (x > (arith_s)INT_MAX) return INT_MAX;
#endif
	return (int)x;
}

ARITH_DECL_MONO_S_L(tolong)
{
	useARI;
#if NATIVE_SIGNED_MIN < LONG_MIN
	if (x < (arith_s)LONG_MIN) return LONG_MIN;
#endif
#if NATIVE_SIGNED_MAX > LONG_MAX
	if (x > (arith_s)LONG_MAX) return LONG_MAX;
#endif
	return (long)x;
}

ARITH_DECL_MONO_S_S(neg)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if NEGATIVE_IS_BIGGER
	if (x == NATIVE_SIGNED_MIN)
		ARITH_WARNING(ARITH_EXCEP_NEG_O);
#endif
#endif
	return -x;
}

ARITH_DECL_MONO_S_S(not)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if TRAP_REPRESENTATION
	if (
#if TWOS_COMPLEMENT
		(x == NATIVE_SIGNED_MAX)
#elif ONES_COMPLEMENT
		(x == 0)
#else
		(x == NATIVE_SIGNED_MAX)
#endif
		) ARITH_WARNING(ARITH_EXCEP_NOT_T);
#endif
#endif
	return ~x;
}

ARITH_DECL_MONO_S_I(lnot) { useARI; return !x; }
ARITH_DECL_MONO_S_I(lval) { useARI; return x != 0; }

/*
 * Addition of signed values:
 * -- overflows occur only when both operands are strictly positive
 * -- underflows occur only when both operands are strictly negative
 * -- overflow check (both operands > 0):
 *    ** if SIGNED_IS_BIGGER == 1, overflows are kept as such in the
 *       unsigned world (if the signed addition overflows, so does the
 *       unsigned, and vice versa)
 *    ** if SIGNED_IS_BIGGER == 0, no overflow can happen in the unsigned
 *       world
 * -- underflow check (both operands < 0):
 *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement)
 *       ++ we have a guaranteed underflow if one of the operand is equal
 *          to NATIVE_SIGNED_MIN; otherwise, -x and -y are valid integers,
 *          and we cast them into the unsigned world
 *       ++ if SIGNED_IS_BIGGER == 1, underflows become unsigned overflows
 *          with a non-zero result
 *       ++ if SIGNED_IS_BIGGER == 0, no overflow happens in the unsigned
 *          world; we use the fact that -NATIVE_SIGNED_MIN is then
 *          exaxctly 1 more than NATIVE_SIGNED_MAX
 *    ** if NEGATIVE_IS_BIGGER == 0, underflow check is identical to
 *       overflow check on (signed) -x and -y.
 */
ARITH_DECL_BI_SS_S(plus)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (x > 0 && y > 0 && (
#if SIGNED_IS_BIGGER
		((arith_u)((arith_u)x + (arith_u)y) < (arith_u)x)
#else
		(((arith_u)x + (arith_u)y) > (arith_u)NATIVE_SIGNED_MAX)
#endif
		)) ARITH_WARNING(ARITH_EXCEP_PLUS_O);
	else if (x < 0 && y < 0 && (
#if NEGATIVE_IS_BIGGER
		(x == NATIVE_SIGNED_MIN || y == NATIVE_SIGNED_MIN) ||
#if SIGNED_IS_BIGGER
		(((arith_u)(-x) + (arith_u)(-y) != 0)
			&& (arith_u)((arith_u)(-x) + (arith_u)(-y))
			< (arith_u)(-x))
#else
		(((arith_u)(-x) + (arith_u)(-y))
			> ((arith_u)1 + (arith_u)NATIVE_SIGNED_MAX))
#endif
#else
#if SIGNED_IS_BIGGER
		((arith_u)((arith_u)(-x) + (arith_u)(-y)) < (arith_u)(-x))
#else
		(((arith_u)(-x) + (arith_u)(-y))
			> (arith_u)NATIVE_SIGNED_MAX)
#endif
#endif
		)) ARITH_WARNING(ARITH_EXCEP_PLUS_U);
#endif
	return x + y;
}

/*
 * Subtraction of signed values:
 * -- overflow: only if x > 0 and y < 0
 *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement) and
 *       y == NATIVE_SIGNED_MIN then overflow
 *    ** otherwise, cast x and -y to unsigned, then add and check
 *       for overflows
 * -- underflow: only if x < 0 and y > 0
 *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement):
 *       ++ if x == NATIVE_SIGNED_MIN then underflow
 *       ++ cast -x and y to unsigned, then add. If SIGNED_IS_BIGGER == 0,
 *          just check. Otherwise, check for overflow with non-zero result.
 *    ** if NEGATIVE_IS_BIGGER == 0: cast -x and y to unsigned, then
 *       add. Overflow check as in addition.
 */
ARITH_DECL_BI_SS_S(minus)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (x > 0 && y < 0 && (
#if NEGATIVE_IS_BIGGER
	(y == NATIVE_SIGNED_MIN) ||
#endif
#if SIGNED_IS_BIGGER
	((arith_u)((arith_u)x + (arith_u)(-y)) < (arith_u)x)
#else
	(((arith_u)x + (arith_u)(-y)) > (arith_u)NATIVE_SIGNED_MAX)
#endif
	)) ARITH_WARNING(ARITH_EXCEP_MINUS_O);
	else if (x < 0 && y > 0 && (
#if NEGATIVE_IS_BIGGER
	(x == NATIVE_SIGNED_MIN) ||
#if SIGNED_IS_BIGGER
	((((arith_u)(-x) + (arith_u)y) != 0) &&
		((arith_u)((arith_u)(-x) + (arith_u)y) < (arith_u)(-x)))
#else
	(((arith_u)(-x) + (arith_u)y) >
		((arith_u)1 + (arith_u)NATIVE_SIGNED_MAX))
#endif
#else
#if SIGNED_IS_BIGGER
	((arith_u)((arith_u)(-x) + (arith_u)y) < (arith_u)(-x))
#else
	(((arith_u)(-x) + (arith_u)y) > (arith_u)NATIVE_SIGNED_MAX)
#endif
#endif
	)) ARITH_WARNING(ARITH_EXCEP_MINUS_U);
#endif
	return x - y;
}

ARITH_DECL_BI_SS_I(lt) { useARI; return x < y; }
ARITH_DECL_BI_SS_I(leq) { useARI; return x <= y; }
ARITH_DECL_BI_SS_I(gt) { useARI; return x > y; }
ARITH_DECL_BI_SS_I(geq) { useARI; return x >= y; }
ARITH_DECL_BI_SS_I(same) { useARI; return x == y; }
ARITH_DECL_BI_SS_I(neq) { useARI; return x != y; }

/*
 * Provided neither x nor y is a trap representation:
 * -- one's complement: impossible to get a trap representation
 * -- two's complement and sign + mantissa: trap representation if and
 * only if x and y are strictly negative and (-x) & (-y) == 0
 * (in two's complement, -x is safe because overflow would occur only
 * if x was already a trap representation).
 */
ARITH_DECL_BI_SS_S(and)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if TRAP_REPRESENTATION && !ONES_COMPLEMENT
	if (x < 0 && y < 0 && ((-x) & (-y)) == 0)
		ARITH_WARNING(ARITH_EXCEP_AND_T);
#endif
#endif
	return x & y;
}

/*
 * Provided neither x nor y is a trap representation:
 * -- two's complement: trap if and only if x != NATIVE_SIGNED_MAX && ~x == y
 * -- one's complement: trap if and only if x != 0 && ~x == y
 * -- mantissa + sign: trap if and only if x != 0 && -x == y
 */
ARITH_DECL_BI_SS_S(xor)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if TRAP_REPRESENTATION
	if (
#if TWOS_COMPLEMENT
	(x != NATIVE_SIGNED_MAX && ~x == y)
#elif ONES_COMPLEMENT
	(x != 0 && ~x == y)
#else
	(x != 0 && -x == y)
#endif
		) ARITH_WARNING(ARITH_EXCEP_XOR_T);
#endif
#endif
	return x ^ y;
}

/*
 * Provided neither x nor y is a trap representation:
 * -- two's complement: impossible to trap
 * -- one's complement: trap if and only if x != 0 && y != 0 && (~x & ~y) == 0
 * -- mantissa + sign: impossible to trap
 */
ARITH_DECL_BI_SS_S(or)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
#if TRAP_REPRESENTATION
#if ONES_COMPLEMENT
	if (x != 0 && y != 0 && (~x & ~y) == 0)
		ARITH_WARNING(ARITH_EXCEP_OR_T);
#endif
#endif
#endif
	return x | y;
}

/*
 * Left-shifting by a negative or greater than type width count is
 * forbidden. Left-shifting a negative value is forbidden (underflow).
 * Left-shifting a positive value can trigger an overflow. We check it
 * by casting into the unsigned world and simulating a truncation.
 *
 * If SIGNED_IS_BIGGER is set, then the signed type width is 1 more
 * than the unsigned type width (the sign bit is included in the width);
 * otherwise, if W is the signed type width, 1U << (W-1) is equal to
 * NATIVE_SIGNED_MAX + 1.
 */
ARITH_DECL_BI_SI_S(lsh)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (y < 0) ARITH_WARNING(ARITH_EXCEP_LSH_C);
	else if (
#if SIGNED_IS_BIGGER
		y > NATIVE_UNSIGNED_BITS
#else
		y >= NATIVE_UNSIGNED_BITS
		|| (y > 0 && (((arith_u)1 << (y - 1))
			> (arith_u)NATIVE_SIGNED_MAX))
#endif
		) ARITH_WARNING(ARITH_EXCEP_LSH_W);
	else if (x < 0) ARITH_WARNING(ARITH_EXCEP_LSH_U);
	else if (x > 0 && ((((arith_u)x << y) & NATIVE_SIGNED_MAX) >> y)
		!= (arith_u)x) ARITH_WARNING(ARITH_EXCEP_LSH_O);
#endif
	return x << y;
}

/*
 * Right-shifting is handled as left-shifting, except that the problem
 * is somehow simpler: there is no possible overflow or underflow. Only
 * right-shifting a negative value yields an implementation defined
 * result (_not_ an undefined behaviour).
 */
ARITH_DECL_BI_SI_S(rsh)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (y < 0) ARITH_WARNING(ARITH_EXCEP_RSH_C);
	else if (
#if SIGNED_IS_BIGGER
		y > NATIVE_UNSIGNED_BITS
#else
		y >= NATIVE_UNSIGNED_BITS
		|| (y > 0 && (((arith_u)1 << (y - 1))
			> (arith_u)NATIVE_SIGNED_MAX))
#endif
		) ARITH_WARNING(ARITH_EXCEP_RSH_W);
	else if (x < 0) ARITH_WARNING(ARITH_EXCEP_RSH_N);
#endif
	return x >> y;
}

/*
 * Overflow can happen only if both operands have the same sign.
 * Underflow can happen only if both operands have opposite signs.
 *
 * Overflow checking: this is done quite inefficiently by performing
 * a division on the result and check if it matches the initial operand.
 */
ARITH_DECL_BI_SS_S(star)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (x == 0 || y == 0) return 0;
	if (x > 0 && y > 0) {
		if ((((arith_u)x * (arith_u)y) & (arith_u)NATIVE_SIGNED_MAX)
			/ (arith_u)y != (arith_u)x)
			ARITH_WARNING(ARITH_EXCEP_STAR_O);
	} else if (x < 0 && y < 0) {
		if (
#if NEGATIVE_IS_BIGGER
			(x == NATIVE_SIGNED_MIN || y == NATIVE_SIGNED_MIN) ||
#endif
			(((arith_u)(-x) * (arith_u)(-y))
			& (arith_u)NATIVE_SIGNED_MAX) / (arith_u)(-y)
			!= (arith_u)(-x))
			ARITH_WARNING(ARITH_EXCEP_STAR_O);
	} else if (x > 0 && y < 0) {
		if ((arith_u)x > (arith_u)1 && (
#if NEGATIVE_IS_BIGGER
		y == NATIVE_SIGNED_MIN ||
#endif
		(((arith_u)x * (arith_u)(-y)) & (arith_u)NATIVE_SIGNED_MAX)
		/ (arith_u)(-y) != (arith_u)x))
		ARITH_WARNING(ARITH_EXCEP_STAR_U);
	} else {
		if ((arith_u)y > (arith_u)1 && (
#if NEGATIVE_IS_BIGGER
		x == NATIVE_SIGNED_MIN ||
#endif
		(((arith_u)y * (arith_u)(-x)) & (arith_u)NATIVE_SIGNED_MAX)
		/ (arith_u)(-x) != (arith_u)y))
		ARITH_WARNING(ARITH_EXCEP_STAR_U);
	}
#endif
	return x * y;
}

/*
 * Division by 0 is an error. The only other possible problem is an
 * overflow of the result. Such an overflow can only happen in two's
 * complement representation, when NEGATIVE_IS_BIGGER is set, and
 * one attempts to divide NATIVE_SIGNED_MIN by -1: the result is then
 * -NATIVE_SIGNED_MIN, which is not representable by the type. This is
 * considered as an error, not a warning, because it actually triggers
 * an exception on modern Pentium-based PC.
 */
ARITH_DECL_BI_SS_S(slash)
{
	if (y == 0) ARITH_ERROR(ARITH_EXCEP_SLASH_D);
#if NEGATIVE_IS_BIGGER
	else if (x == NATIVE_SIGNED_MIN && y == (arith_s)(-1))
		ARITH_ERROR(ARITH_EXCEP_SLASH_O);
#endif
	return x / y;
}

/*
 * Only division by 0 needs to be checked.
 */
ARITH_DECL_BI_SS_S(pct)
{
	if (y == 0) ARITH_ERROR(ARITH_EXCEP_PCT_D);
	return x % y;
}

ARITH_DECL_MONO_ST_US(octconst)
{
	arith_u z = 0;

	for (; ARITH_OCTAL(*c); c ++) {
		arith_u w = ARITH_OVAL(*c);
		if (z > (NATIVE_UNSIGNED_MAX_A / 8))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z *= 8;
#if 0
/* obsolete */
/* NATIVE_UNSIGNED_MAX_A is 2^N - 1, 0 <= w <= 7 and 8 divides z */
		if (z > (NATIVE_UNSIGNED_MAX_A - w))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
#endif
		z += w;
	}
	*ru = z;
#if SIGNED_IS_BIGGER
	*rs = z;
	*sp = 1;
#else
	if (z > NATIVE_SIGNED_MAX) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
#endif
	return c;
}

ARITH_DECL_MONO_ST_US(decconst)
{
	arith_u z = 0;

	for (; ARITH_DECIM(*c); c ++) {
		arith_u w = ARITH_DVAL(*c);
		if (z > (NATIVE_UNSIGNED_MAX_A / 10))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z *= 10;
		if (z > (NATIVE_UNSIGNED_MAX_A - w))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z += w;
	}
	*ru = z;
#if SIGNED_IS_BIGGER
	*rs = z;
	*sp = 1;
#else
	if (z > NATIVE_SIGNED_MAX) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
#endif
	return c;
}

ARITH_DECL_MONO_ST_US(hexconst)
{
	arith_u z = 0;

	for (; ARITH_HEXAD(*c); c ++) {
		arith_u w = ARITH_HVAL(*c);
		if (z > (NATIVE_UNSIGNED_MAX_A / 16))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z *= 16;
#if 0
/* obsolete */
/* NATIVE_UNSIGNED_MAX_A is 2^N - 1, 0 <= w <= 15 and 16 divides z */
		if (z > (NATIVE_UNSIGNED_MAX_A - w))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
#endif
		z += w;
	}
	*ru = z;
#if SIGNED_IS_BIGGER
	*rs = z;
	*sp = 1;
#else
	if (z > NATIVE_SIGNED_MAX) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
#endif
	return c;
}

#else
/* ====================================================================== */
/* Arithmetics with a simple simulated type */
/* ====================================================================== */

/*
 * We simulate a type with the following characteristics:
 * -- the signed type width is equal to the unsigned type width (which
 * means that there is one less value bit in the signed type);
 * -- the signed type uses two's complement representation;
 * -- there is no trap representation;
 * -- overflows and underflows are truncated (but a warning is emitted
 * if ARITHMETIC_CHECKS is defined);
 * -- overflow on integer division is still an error;
 * -- right-shifting of a negative value extends the sign;
 * -- the shift count value is first cast to unsigned, then reduced modulo
 * the type size.
 *
 * These characteristics follow what is usually found on modern
 * architectures.
 *
 * The maximum emulated type size is twice the size of the unsigned native
 * type which is used to emulate the type.
 */

#undef SIMUL_ONE_TMP
#undef SIMUL_MSW_TMP1
#undef SIMUL_MSW_MASK
#undef SIMUL_LSW_TMP1
#undef SIMUL_LSW_MASK

#define SIMUL_ONE_TMP     ((SIMUL_ARITH_SUBTYPE)1)
#define SIMUL_MSW_TMP1    (SIMUL_ONE_TMP << (SIMUL_MSW_WIDTH - 1))
#define SIMUL_MSW_MASK    (SIMUL_MSW_TMP1 | (SIMUL_MSW_TMP1 - SIMUL_ONE_TMP))
#define SIMUL_LSW_TMP1    (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1))
#define SIMUL_LSW_MASK    (SIMUL_LSW_TMP1 | (SIMUL_LSW_TMP1 - SIMUL_ONE_TMP))

#undef TMSW
#undef TLSW

#define TMSW(x)           ((x) & SIMUL_MSW_MASK)
#define TLSW(x)           ((x) & SIMUL_LSW_MASK)

#undef SIMUL_ZERO
#undef SIMUL_ONE

#define SIMUL_ZERO        arith_strc(ARITH_TYPENAME, _zero)
#define SIMUL_ONE         arith_strc(ARITH_TYPENAME, _one)

static arith_u SIMUL_ZERO = { 0, 0 };
static arith_u SIMUL_ONE = { 0, 1 };

/*
 * We use the fact that both the signed and unsigned type are the same
 * structure. The difference between the signed and the unsigned type
 * is a type information, and, as such, is considered compile-time and
 * not maintained in the value structure itself. This is a job for
 * the programmer / compiler.
 */
ARITH_DECL_MONO_S_U(to_u) { useARI; return x; }

ARITH_DECL_MONO_I_U(fromint)
{
	arith_u z;

	if (x < 0) return arith_op_u(neg)(aARI_ arith_op_u(fromint)(aARI_ -x));
	/*
	 * This code works because types smaller than int are promoted
	 * by the C compiler before evaluating the >> operator.
	 */
	z.msw = TMSW(((SIMUL_ARITH_SUBTYPE)x >> (SIMUL_LSW_WIDTH - 1)) >> 1);
	z.lsw = TLSW((SIMUL_ARITH_SUBTYPE)x);
	return z;
}

ARITH_DECL_MONO_L_U(fromulong)
{
	arith_u z;
	useARI;

#if (ULONG_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1 == 0
	z.msw = 0;
	z.lsw = x;
#else
	z.msw = TMSW(x >> SIMUL_LSW_WIDTH);
	z.lsw = TLSW((SIMUL_ARITH_SUBTYPE)x);
#endif
	return z;
}

ARITH_DECL_MONO_U_I(toint)
{
	useARI;
#if ((INT_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1) == 0
	if (x.msw != 0 || x.lsw > (SIMUL_ARITH_SUBTYPE)INT_MAX)
		return INT_MAX;
	return (int)x.lsw;
#else
#if (INT_MAX >> (SIMUL_SUBTYPE_BITS - 1)) == 0
	if (x.msw > (SIMUL_ARITH_SUBTYPE)(INT_MAX >> SIMUL_LSW_WIDTH))
		return INT_MAX;
#endif
	return ((int)x.msw << SIMUL_LSW_WIDTH) | (int)x.lsw;
#endif
}

ARITH_DECL_MONO_U_L(toulong)
{
	useARI;
#if ((ULONG_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1) == 0
	if (x.msw != 0 || x.lsw > (SIMUL_ARITH_SUBTYPE)ULONG_MAX)
		return ULONG_MAX;
	return (unsigned long)x.lsw;
#else
#if (ULONG_MAX >> (SIMUL_SUBTYPE_BITS - 1)) == 0
	if (x.msw > (SIMUL_ARITH_SUBTYPE)(ULONG_MAX >> SIMUL_LSW_WIDTH))
		return ULONG_MAX;
#endif
	return ((unsigned long)x.msw << SIMUL_LSW_WIDTH) | (unsigned long)x.lsw;
#endif
}

ARITH_DECL_MONO_U_U(neg)
{
	x = arith_op_u(not)(aARI_ x);
	return arith_op_u(plus)(aARI_ x, SIMUL_ONE);
}

ARITH_DECL_MONO_U_U(not)
{
	useARI;
	x.msw = TMSW(~x.msw);
	x.lsw = TLSW(~x.lsw);
	return x;
}

ARITH_DECL_MONO_U_I(lnot)
{
	useARI;
	return x.msw == 0 && x.lsw == 0;
}

ARITH_DECL_MONO_U_I(lval)
{
	useARI;
	return x.msw != 0 || x.lsw != 0;
}

ARITH_DECL_BI_UU_U(plus)
{
	useARI;
	x.lsw = TLSW(x.lsw + y.lsw);
	x.msw = TMSW(x.msw + y.msw);
	if (x.lsw < y.lsw) x.msw = TMSW(x.msw + 1);
	return x;
}

ARITH_DECL_BI_UU_U(minus)
{
	return arith_op_u(plus)(aARI_ x, arith_op_u(neg)(aARI_ y));
}

ARITH_DECL_BI_UI_U(lsh)
{
	useARI;
	if (y == 0) return x;
#ifdef ARITHMETIC_CHECKS
	if (y < 0) ARITH_WARNING(ARITH_EXCEP_LSH_C);
	else if (y >= SIMUL_NUMBITS) ARITH_WARNING(ARITH_EXCEP_LSH_W);
#endif
	y = (unsigned)y % SIMUL_NUMBITS;
	if (y >= SIMUL_LSW_WIDTH) {
		/*
		 * We use here the fact that the LSW size is always
		 * equal to or greater than the MSW size.
		 */
		x.msw = TMSW(x.lsw << (y - SIMUL_LSW_WIDTH));
		x.lsw = 0;
		return x;
	}
	x.msw = TMSW((x.msw << y) | (x.lsw >> (SIMUL_LSW_WIDTH - y)));
	x.lsw = TLSW(x.lsw << y);
	return x;
}

ARITH_DECL_BI_UI_U(rsh)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (y < 0) ARITH_WARNING(ARITH_EXCEP_RSH_C);
	else if (y >= SIMUL_NUMBITS) ARITH_WARNING(ARITH_EXCEP_RSH_W);
#endif
	y = (unsigned)y % SIMUL_NUMBITS;
	if (y >= SIMUL_LSW_WIDTH) {
		x.lsw = x.msw >> (y - SIMUL_LSW_WIDTH);
		x.msw = 0;
		return x;
	}
	x.lsw = TLSW((x.lsw >> y) | (x.msw << (SIMUL_LSW_WIDTH - y)));
	x.msw >>= y;
	return x;
}

ARITH_DECL_BI_UU_I(lt)
{
	useARI;
	return x.msw < y.msw || (x.msw == y.msw && x.lsw < y.lsw);
}

ARITH_DECL_BI_UU_I(leq)
{
	useARI;
	return x.msw < y.msw || (x.msw == y.msw && x.lsw <= y.lsw);
}

ARITH_DECL_BI_UU_I(gt)
{
	return arith_op_u(lt)(aARI_ y, x);
}

ARITH_DECL_BI_UU_I(geq)
{
	return arith_op_u(leq)(aARI_ y, x);
}

ARITH_DECL_BI_UU_I(same)
{
	useARI;
	return x.msw == y.msw && x.lsw == y.lsw;
}

ARITH_DECL_BI_UU_I(neq)
{
	return !arith_op_u(same)(aARI_ x, y);
}

ARITH_DECL_BI_UU_U(and)
{
	useARI;
	x.msw &= y.msw;
	x.lsw &= y.lsw;
	return x;
}

ARITH_DECL_BI_UU_U(xor)
{
	useARI;
	x.msw ^= y.msw;
	x.lsw ^= y.lsw;
	return x;
}

ARITH_DECL_BI_UU_U(or)
{
	useARI;
	x.msw |= y.msw;
	x.lsw |= y.lsw;
	return x;
}

#undef SIMUL_LSW_ODDLEN
#undef SIMUL_LSW_HALFLEN
#undef SIMUL_LSW_HALFMASK

#define SIMUL_LSW_ODDLEN    (SIMUL_LSW_WIDTH & 1)
#define SIMUL_LSW_HALFLEN   (SIMUL_LSW_WIDTH / 2)
#define SIMUL_LSW_HALFMASK  (~(~(SIMUL_ARITH_SUBTYPE)0 << SIMUL_LSW_HALFLEN))

ARITH_DECL_BI_UU_U(star)
{
	arith_u z;
	SIMUL_ARITH_SUBTYPE a = x.lsw, b = y.lsw, t00, t01, t10, t11, c = 0, t;
#if SIMUL_LSW_ODDLEN
	SIMUL_ARITH_SUBTYPE bms = b & (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1));

	b &= ~(SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1));
#endif
	useARI;

	t00 = (a & SIMUL_LSW_HALFMASK) * (b & SIMUL_LSW_HALFMASK);
	t01 = (a & SIMUL_LSW_HALFMASK) * (b >> SIMUL_LSW_HALFLEN);
	t10 = (a >> SIMUL_LSW_HALFLEN) * (b & SIMUL_LSW_HALFMASK);
	t11 = (a >> SIMUL_LSW_HALFLEN) * (b >> SIMUL_LSW_HALFLEN);
	t = z.lsw = t00;
	z.lsw = TLSW(z.lsw + (t01 << SIMUL_LSW_HALFLEN));
	if (t > z.lsw) c ++;
	t = z.lsw;
	z.lsw = TLSW(z.lsw + (t10 << SIMUL_LSW_HALFLEN));
	if (t > z.lsw) c ++;
#if SIMUL_LSW_ODDLEN
	t = z.lsw;
	z.lsw = TLSW(z.lsw + (t11 << (2 * SIMUL_LSW_HALFLEN)));
	if (t > z.lsw) c ++;
	if (bms && (a & SIMUL_ONE_TMP)) {
		t = z.lsw;
		z.lsw = TLSW(z.lsw + b);
		if (t > z.lsw) c ++;
	}
#endif
	z.msw = TMSW(x.lsw * y.msw + x.msw * y.lsw + c
		+ (t01 >> (SIMUL_LSW_WIDTH - SIMUL_LSW_HALFLEN))
		+ (t10 >> (SIMUL_LSW_WIDTH - SIMUL_LSW_HALFLEN))
		+ (t11 >> (SIMUL_LSW_WIDTH - (2 * SIMUL_LSW_HALFLEN))));
	return z;
}

/*
 * This function calculates the unsigned integer division, yielding
 * both quotient and remainder. The divider (y) MUST be non-zero.
 */
static void arith_op_u(udiv)(pARI_ arith_u x, arith_u y, arith_u *q, arith_u *r)
{
	int i, j;
	arith_u a;

	*q = SIMUL_ZERO;
	for (i = SIMUL_NUMBITS - 1; i >= 0; i --) {
		if (i >= (int)SIMUL_LSW_WIDTH
			&& (y.msw & (SIMUL_ONE_TMP << (i - SIMUL_LSW_WIDTH))))
			break;
		if (i < (int)SIMUL_LSW_WIDTH && (y.lsw & (SIMUL_ONE_TMP << i)))
			break;
	}
	a = arith_op_u(lsh)(aARI_ y, SIMUL_NUMBITS - 1 - i);
	for (j = SIMUL_NUMBITS - 1 - i; j >= SIMUL_LSW_WIDTH; j --) {
		if (arith_op_u(leq)(aARI_ a, x)) {
			x = arith_op_u(minus)(aARI_ x, a);
			q->msw |= SIMUL_ONE_TMP << (j - SIMUL_LSW_WIDTH);
		}
		a = arith_op_u(rsh)(aARI_ a, 1);
	}
	for (; j >= 0; j --) {
		if (arith_op_u(leq)(aARI_ a, x)) {
			x = arith_op_u(minus)(aARI_ x, a);
			q->lsw |= SIMUL_ONE_TMP << j;
		}
		a = arith_op_u(rsh)(aARI_ a, 1);
	}
	*r = x;
}

ARITH_DECL_BI_UU_U(slash)
{
	arith_u q, r;

	if (arith_op_u(same)(aARI_ y, SIMUL_ZERO))
		ARITH_ERROR(ARITH_EXCEP_SLASH_D);
	arith_op_u(udiv)(aARI_ x, y, &q, &r);
	return q;
}

ARITH_DECL_BI_UU_U(pct)
{
	arith_u q, r;

	if (arith_op_u(same)(aARI_ y, SIMUL_ZERO))
		ARITH_ERROR(ARITH_EXCEP_PCT_D);
	arith_op_u(udiv)(aARI_ x, y, &q, &r);
	return r;
}

#undef SIMUL_TRAP
#undef SIMUL_TRAPL
#define SIMUL_TRAP   (SIMUL_ONE_TMP << (SIMUL_MSW_WIDTH - 1))
#define SIMUL_TRAPL  (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1))

ARITH_DECL_MONO_U_S(to_s)
{
	useARI;
#ifdef ARITHMETIC_CHECKS
	if (x.msw & SIMUL_TRAP) ARITH_WARNING(ARITH_EXCEP_CONV_O);
#endif
	return x;
}

ARITH_DECL_MONO_I_S(fromint) { return arith_op_u(fromint)(aARI_ x); }
ARITH_DECL_MONO_L_S(fromlong)
{
	if (x < 0) return arith_op_u(neg)(aARI_
		arith_op_u(fromulong)(aARI_ (unsigned long)(-x)));
	return arith_op_u(fromulong)(aARI_ (unsigned long)x);
}

ARITH_DECL_MONO_S_I(toint)
{
	if (x.msw & SIMUL_TRAP) return -arith_op_u(toint)(aARI_ arith_op_u(neg)(aARI_ x));
	return arith_op_u(toint)(aARI_ x);
}

ARITH_DECL_MONO_S_L(tolong)
{
	if (x.msw & SIMUL_TRAP)
		return -(long)arith_op_u(toulong)(aARI_ arith_op_u(neg)(aARI_ x));
	return (long)arith_op_u(toulong)(aARI_ x);
}

ARITH_DECL_MONO_S_S(neg)
{
#ifdef ARITHMETIC_CHECKS
	if (x.lsw == 0 && x.msw == SIMUL_TRAP)
		ARITH_WARNING(ARITH_EXCEP_NEG_O);
#endif
	return arith_op_u(neg)(aARI_ x);
}

ARITH_DECL_MONO_S_S(not) { return arith_op_u(not)(aARI_ x); }
ARITH_DECL_MONO_S_I(lnot) { return arith_op_u(lnot)(aARI_ x); }
ARITH_DECL_MONO_S_I(lval) { return arith_op_u(lval)(aARI_ x); }

ARITH_DECL_BI_SS_S(plus)
{
	arith_u z = arith_op_u(plus)(aARI_ x, y);

#ifdef ARITHMETIC_CHECKS
	if (x.msw & y.msw & ~z.msw & SIMUL_TRAP)
		ARITH_WARNING(ARITH_EXCEP_PLUS_U);
	else if (~x.msw & ~y.msw & z.msw & SIMUL_TRAP)
		ARITH_WARNING(ARITH_EXCEP_PLUS_O);
#endif
	return z;
}

ARITH_DECL_BI_SS_S(minus)
{
	arith_s z = arith_op_u(minus)(aARI_ x, y);

#ifdef ARITHMETIC_CHECKS
	if (x.msw & ~y.msw & ~z.msw & SIMUL_TRAP)
		ARITH_WARNING(ARITH_EXCEP_MINUS_U);
	else if (~x.msw & y.msw & z.msw & SIMUL_TRAP)
		ARITH_WARNING(ARITH_EXCEP_MINUS_O);
#endif
	return z;
}

/*
 * Since signed and unsigned widths are equal for the simulated type,
 * we can use the unsigned left shift function, which performs the
 * the checks on the type width.
 */
ARITH_DECL_BI_SI_S(lsh)
{
	arith_s z = arith_op_u(lsh)(aARI_ x, y);

#ifdef ARITHMETIC_CHECKS
	if (x.msw & SIMUL_TRAP) ARITH_WARNING(ARITH_EXCEP_LSH_U);
	else {
		/*
		 * To check for possible overflow, we right shift the
		 * result. We need to make the shift count proper so that
		 * we do not emit a double-warning. Besides, the left shift
		 * could have been untruncated but yet affet the sign bit,
		 * so we must test this explicitly.
		 */
		arith_s w = arith_op_u(rsh)(aARI_ z, (unsigned)y % SIMUL_NUMBITS);

		if ((z.msw & SIMUL_TRAP) || w.msw != x.msw || w.lsw != x.lsw)
			ARITH_WARNING(ARITH_EXCEP_LSH_O);
	}
#endif
	return z;
}

/*
 * We define that right shifting a negative value, besides being worth a
 * warning, duplicates the sign bit. This is the most useful and most
 * usually encountered behaviour, and the standard allows it.
 */
ARITH_DECL_BI_SI_S(rsh)
{
	int xn = (x.msw & SIMUL_TRAP) != 0;
	arith_s z = arith_op_u(rsh)(aARI_ x, y);
	int gy = (unsigned)y % SIMUL_NUMBITS;

#ifdef ARITHMETIC_CHECKS
	if (xn) ARITH_WARNING(ARITH_EXCEP_RSH_N);
#endif
	if (xn && gy > 0) {
		if (gy <= SIMUL_MSW_WIDTH) {
			z.msw |= TMSW(~(SIMUL_MSW_MASK >> gy));
		} else {
			z.msw = SIMUL_MSW_MASK;
			z.lsw |= TLSW(~(SIMUL_LSW_MASK
				>> (gy - SIMUL_MSW_WIDTH)));
		}
	}
	return z;
}

ARITH_DECL_BI_SS_I(lt)
{
	int xn = (x.msw & SIMUL_TRAP) != 0;
	int yn = (y.msw & SIMUL_TRAP) != 0;
	useARI;

	if (xn == yn) {
		return x.msw < y.msw || (x.msw == y.msw && x.lsw < y.lsw);
	} else {
		return xn;
	}
}

ARITH_DECL_BI_SS_I(leq)
{
	int xn = (x.msw & SIMUL_TRAP) != 0;
	int yn = (y.msw & SIMUL_TRAP) != 0;
	useARI;

	if (xn == yn) {
		return x.msw < y.msw || (x.msw == y.msw && x.lsw <= y.lsw);
	} else {
		return xn;
	}
}

ARITH_DECL_BI_SS_I(gt)
{
	return arith_op_s(lt)(aARI_ y, x);
}

ARITH_DECL_BI_SS_I(geq)
{
	return arith_op_s(leq)(aARI_ y, x);
}

ARITH_DECL_BI_SS_I(same)
{
	useARI;
	return x.msw == y.msw && x.lsw == y.lsw;
}

ARITH_DECL_BI_SS_I(neq)
{
	return !arith_op_s(same)(aARI_ x, y);
}

ARITH_DECL_BI_SS_S(and)
{
	return arith_op_u(and)(aARI_ x, y);
}

ARITH_DECL_BI_SS_S(xor)
{
	return arith_op_u(xor)(aARI_ x, y);
}

ARITH_DECL_BI_SS_S(or)
{
	return arith_op_u(or)(aARI_ x, y);
}

/*
 * This function calculates the signed integer division, yielding
 * both quotient and remainder. The divider (y) MUST be non-zero.
 */
static void arith_op_s(sdiv)(pARI_ arith_s x, arith_s y, arith_s *q, arith_s *r)
{
	arith_u a = x, b = y, c, d;
	int xn = 0, yn = 0;

	if (x.msw & SIMUL_TRAP) { a = arith_op_u(neg)(aARI_ x); xn = 1; }
	if (y.msw & SIMUL_TRAP) { b = arith_op_u(neg)(aARI_ y); yn = 1; }
	arith_op_u(udiv)(aARI_ a, b, &c, &d);
	if (xn != yn) *q = arith_op_u(neg)(aARI_ c); else *q = c;
	if (xn != yn) *r = arith_op_u(neg)(aARI_ d); else *r = d;
}

/*
 * Overflow/underflow check is done the following way: obvious cases
 * are checked (both upper words non-null, both upper words null...)
 * and border-line occurrences are verified with an unsigned division
 * (which is quite computationaly expensive).
 */
ARITH_DECL_BI_SS_S(star)
{
#ifdef ARITHMETIC_CHECKS
	arith_s z = arith_op_u(star)(aARI_ x, y);
	int warn = 0;

	if (x.msw > 0) {
		if (y.msw > 0
#if SIMUL_LSW_ODDLEN
			|| (y.lsw & SIMUL_TRAPL)
#endif
		) warn = 1;
	}
#if SIMUL_LSW_ODDLEN
	else if (y.msw > 0 && (x.lsw & SIMUL_TRAPL)) warn = 1;
#endif
	if (!warn && (x.msw > 0 || y.msw > 0
#if SIMUL_LSW_ODDLEN
		|| ((x.lsw | y.lsw) & SIMUL_TRAPL)
#endif
	)) {
		if (x.msw == SIMUL_MSW_MASK && x.lsw == SIMUL_LSW_MASK) {
			if (y.msw == SIMUL_TRAP && y.lsw == 0) warn = 1;
		} else if (!(x.msw == 0 && x.lsw == 0)
			&& !arith_op_s(same)(aARI_ arith_op_s(slash)(aARI_ z, x), y)) {
		} warn = 1;
	}
	if (warn) ARITH_WARNING(((x.msw ^ y.msw) & SIMUL_TRAP)
		? ARITH_EXCEP_STAR_U : ARITH_EXCEP_STAR_O);
	return z;
#else
	return arith_op_u(star)(aARI_ x, y);
#endif
}

ARITH_DECL_BI_SS_S(slash)
{
	arith_s q, r;

	if (arith_op_s(same)(aARI_ y, SIMUL_ZERO))
		ARITH_ERROR(ARITH_EXCEP_SLASH_D);
	else if (x.msw == SIMUL_TRAP && x.lsw == 0
		&& y.msw == SIMUL_MSW_MASK && y.lsw == SIMUL_LSW_MASK)
		ARITH_ERROR(ARITH_EXCEP_SLASH_O);
	arith_op_s(sdiv)(aARI_ x, y, &q, &r);
	return q;
}

ARITH_DECL_BI_SS_S(pct)
{
	arith_s q, r;

	if (arith_op_s(same)(aARI_ y, SIMUL_ZERO))
		ARITH_ERROR(ARITH_EXCEP_PCT_D);
	arith_op_s(sdiv)(aARI_ x, y, &q, &r);
	return r;
}

ARITH_DECL_MONO_ST_US(octconst)
{
	arith_u z = { 0, 0 };

	for (; ARITH_OCTAL(*c); c ++) {
		unsigned w = ARITH_OVAL(*c);
		if (z.msw > (SIMUL_MSW_MASK / 8))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z = arith_op_u(lsh)(aARI_ z, 3);
		z.lsw |= w;
	}
	*ru = z;
	if (z.msw & SIMUL_TRAP) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
	return c;
}

ARITH_DECL_MONO_ST_US(decconst)
{
#define ARITH_ALPHA_TRAP    (1U << (SIMUL_MSW_WIDTH - 1))
#define ARITH_ALPHA_MASK    (ARITH_ALPHA_TRAP | (ARITH_ALPHA_TRAP - 1))
#define ARITH_ALPHA     ((ARITH_ALPHA_MASK - 10 * (ARITH_ALPHA_TRAP / 5)) + 1)
#define ARITH_ALPHA_A   ((SIMUL_MSW_MASK - 10 * (SIMUL_TRAP / 5)) + 1)

	arith_u z = { 0, 0 };

	for (; ARITH_DECIM(*c); c ++) {
		unsigned w = ARITH_DVAL(*c);
		SIMUL_ARITH_SUBTYPE t;

		if (z.msw > (SIMUL_MSW_MASK / 10)
			|| (z.msw == (SIMUL_MSW_MASK / 10) &&
/* ARITH_ALPHA is between 1 and 9, inclusive. */
#if ARITH_ALPHA == 5
			z.lsw >= SIMUL_TRAPL
#else
			z.lsw > ((SIMUL_TRAPL / 5) * ARITH_ALPHA_A
			+ ((SIMUL_TRAPL % 5) * ARITH_ALPHA_A) / 5)
#endif
			)) ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z = arith_op_u(plus)(aARI_ arith_op_u(lsh)(aARI_ z, 3),
			arith_op_u(lsh)(aARI_ z, 1));
		t = TLSW(z.lsw + w);
		if (t < z.lsw) {
			if (TMSW(z.msw + 1) == 0)
				ARITH_ERROR(ARITH_EXCEP_CONST_O);
			z.msw ++;
		}
		z.lsw = t;
	}
	*ru = z;
	if (z.msw & SIMUL_TRAP) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
	return c;

#undef ARITH_ALPHA_A
#undef ARITH_ALPHA
#undef ARITH_ALPHA_TRAP
#undef ARITH_ALPHA_MASK
}

ARITH_DECL_MONO_ST_US(hexconst)
{
	arith_u z = { 0, 0 };

	for (; ARITH_HEXAD(*c); c ++) {
		unsigned w = ARITH_HVAL(*c);
		if (z.msw > (SIMUL_MSW_MASK / 16))
			ARITH_ERROR(ARITH_EXCEP_CONST_O);
		z = arith_op_u(lsh)(aARI_ z, 4);
		z.lsw |= w;
	}
	*ru = z;
	if (z.msw & SIMUL_TRAP) {
		*sp = 0;
	} else {
		*rs = z;
		*sp = 1;
	}
	return c;
}

#endif

#undef ARITH_HVAL
#undef ARITH_HEXAD
#undef ARITH_DVAL
#undef ARITH_DECIM
#undef ARITH_OVAL
#undef ARITH_OCTAL
