/*
 * Integer arithmetic evaluation, header file.
 *
 * (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.
 *
 */

/*
 * This arithmetic evaluator uses two files: this header file (arith.h)
 * and the source file (arith.c). To use this code, the source file should
 * be included from another .c file which defines some macros (see below).
 * Then the functions defined in the arith.c file become available to the
 * including source file. If those functions are defined with external
 * linkage (that is, `ARITH_FUNCTION_HEADER' does not contain `static'),
 * it is possible for other source files to use the arithmetic functions
 * by including the arith.h header only. The source file which includes
 * arith.c should *not* include arith.h.
 *
 * If the #include is for arith.h, the following macros should be
 * defined:
 *
 * -- If the evaluator is supposed to use a native type:
 *   NATIVE_SIGNED            the native signed integer type
 *   NATIVE_UNSIGNED          the native unsigned integer type
 *
 * -- If the evaluator is supposed to use an emulated type:
 *   SIMUL_ARITH_SUBTYPE      the native unsigned type used for the simulation
 *   SIMUL_SUBTYPE_BITS       the native unsigned type size
 *   SIMUL_NUMBITS            the emulated type size
 *
 * -- For both cases:
 *   ARITH_TYPENAME           the central arithmetic type name
 *   ARITH_FUNCTION_HEADER    the qualifiers to add to function definitions
 *
 * The presence (respectively absence) of the NATIVE_SIGNED macro triggers
 * the use of the native type evaluator (respectively simulated type
 * evaluator).
 *
 * If the #include is for arith.c, the macros for arith.h should be defined,
 * and the following should be defined as well:
 *
 * -- If the evaluator is supposed to use a native type:
 *   NATIVE_UNSIGNED_BITS     the native unsigned type size
 *   NATIVE_SIGNED_MIN        the native signed minimum value
 *   NATIVE_SIGNED_MAX        the native signed maximum value
 * (the last two macros must evaluate to signed constant expressions)
 *
 * -- For both cases:
 *   ARITH_WARNING(type)      code to perform on warning
 *   ARITH_ERROR(type)        code to perform on error
 *
 * The macro ARITH_WARNING() and ARITH_ERROR() are invoked with a
 * numerical argument which is one of the enumeration constants
 * defined below (ARITH_EXCEP_*) that identifies the specific problem.
 *
 * If the #include is for arith.c, the macro ARITHMETIC_CHECKS may be
 * defined. When this macro is defined, checks are performed so that all
 * operation which would lead to undefined or implementation-defined
 * behaviour are first reported through ARITH_WARNING(). Code is smaller
 * and faster without these checks, of course. Regardless of the status
 * of that macro, divisions by 0 and overflows on signed division are
 * reported as errors through ARITH_ERROR().
 *
 */

#ifndef ARITH_H__
#define ARITH_H__

#define ARITH_TYPENAME             big
#define ARITH_FUNCTION_HEADER      /*static inline*/

#define ARITH_ERROR(type)          z_error(type)
/*static*/ void z_error(int type);

#ifdef ARITHMETIC_CHECKS
#define ARITH_WARNING(type)        z_warn(type)
/*static*/ void z_warn(int type);
#endif

enum {
	/* Warnings */
	ARITH_EXCEP_CONV_O,    /* overflow on conversion */
	ARITH_EXCEP_NEG_O,     /* overflow on unary minus */
	ARITH_EXCEP_NOT_T,     /* trap representation on bitwise inversion */
	ARITH_EXCEP_PLUS_O,    /* overflow on addition */
	ARITH_EXCEP_PLUS_U,    /* underflow on addition */
	ARITH_EXCEP_MINUS_O,   /* overflow on subtraction */
	ARITH_EXCEP_MINUS_U,   /* underflow on subtraction */
	ARITH_EXCEP_AND_T,     /* trap representation on bitwise and */
	ARITH_EXCEP_XOR_T,     /* trap representation on bitwise xor */
	ARITH_EXCEP_OR_T,      /* trap representation on bitwise or */
	ARITH_EXCEP_LSH_W,     /* left shift by type width or more */
	ARITH_EXCEP_LSH_C,     /* left shift by negative count */
	ARITH_EXCEP_LSH_O,     /* overflow on left shift */
	ARITH_EXCEP_LSH_U,     /* underflow on left shift */
	ARITH_EXCEP_RSH_W,     /* right shift by type width or more */
	ARITH_EXCEP_RSH_C,     /* right shift by negative count */
	ARITH_EXCEP_RSH_N,     /* right shift of negative value */
	ARITH_EXCEP_STAR_O,    /* overflow on multiplication */
	ARITH_EXCEP_STAR_U,    /* underflow on multiplication */

	/* Errors */
	ARITH_EXCEP_SLASH_D,   /* division by 0 */
	ARITH_EXCEP_SLASH_O,   /* overflow on division */
	ARITH_EXCEP_PCT_D,     /* division by 0 on modulus operator */
	ARITH_EXCEP_CONST_O    /* constant too large */
};

#define arith_strc_(x, y)	x ## y
#define arith_strc(x, y)	arith_strc_(x, y)

#define arith_u		arith_strc(u_, ARITH_TYPENAME)
#define arith_s		arith_strc(s_, ARITH_TYPENAME)
#define arith_op_u(op)	arith_strc(ARITH_TYPENAME, arith_strc(_u_, op))
#define arith_op_s(op)	arith_strc(ARITH_TYPENAME, arith_strc(_s_, op))

#define ARITH_DECL_MONO_U_U(op)    ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_u(op)(arith_u x)
#define ARITH_DECL_MONO_U_S(op)    ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_u(op)(arith_u x)
#define ARITH_DECL_MONO_U_I(op)    ARITH_FUNCTION_HEADER int \
                                   arith_op_u(op)(arith_u x)
#define ARITH_DECL_MONO_U_L(op)    ARITH_FUNCTION_HEADER unsigned long \
                                   arith_op_u(op)(arith_u x)
#define ARITH_DECL_MONO_S_U(op)    ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_s(op)(arith_s x)
#define ARITH_DECL_MONO_S_S(op)    ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_s(op)(arith_s x)
#define ARITH_DECL_MONO_S_I(op)    ARITH_FUNCTION_HEADER int \
                                   arith_op_s(op)(arith_s x)
#define ARITH_DECL_MONO_S_L(op)    ARITH_FUNCTION_HEADER long \
                                   arith_op_s(op)(arith_s x)
#define ARITH_DECL_MONO_I_U(op)    ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_u(op)(int x)
#define ARITH_DECL_MONO_L_U(op)    ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_u(op)(unsigned long x)
#define ARITH_DECL_MONO_I_S(op)    ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_s(op)(int x)
#define ARITH_DECL_MONO_L_S(op)    ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_s(op)(long x)
#define ARITH_DECL_MONO_ST_US(op)  ARITH_FUNCTION_HEADER char *arith_op_u(op) \
                                   (char *c, arith_u *ru, arith_s *rs, int *sp)

#define ARITH_DECL_BI_UU_U(op)     ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_u(op)(arith_u x, arith_u y)
#define ARITH_DECL_BI_UI_U(op)     ARITH_FUNCTION_HEADER arith_u \
                                   arith_op_u(op)(arith_u x, int y)
#define ARITH_DECL_BI_UU_I(op)     ARITH_FUNCTION_HEADER int \
                                   arith_op_u(op)(arith_u x, arith_u y)
#define ARITH_DECL_BI_SS_S(op)     ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_s(op)(arith_s x, arith_s y)
#define ARITH_DECL_BI_SI_S(op)     ARITH_FUNCTION_HEADER arith_s \
                                   arith_op_s(op)(arith_s x, int y)
#define ARITH_DECL_BI_SS_I(op)     ARITH_FUNCTION_HEADER int \
                                   arith_op_s(op)(arith_s x, arith_s y)

#endif

#ifdef NATIVE_SIGNED

typedef NATIVE_SIGNED arith_s;
typedef NATIVE_UNSIGNED arith_u;

#else

#if SIMUL_NUMBITS > (2 * SIMUL_SUBTYPE_BITS)
#error Native subtype too small for arithmetic simulation.
#endif

#define SIMUL_MSW_WIDTH   (SIMUL_NUMBITS / 2)
#define SIMUL_LSW_WIDTH   ((SIMUL_NUMBITS + 1) / 2)

typedef struct {
	SIMUL_ARITH_SUBTYPE msw, lsw;
} arith_u, arith_s;

#endif

/* functions with the unsigned type */

ARITH_DECL_MONO_S_U(to_u);
ARITH_DECL_MONO_I_U(fromint);
ARITH_DECL_MONO_L_U(fromulong);
ARITH_DECL_MONO_U_I(toint);
ARITH_DECL_MONO_U_L(toulong);

ARITH_DECL_MONO_U_U(neg);
ARITH_DECL_MONO_U_U(not);
ARITH_DECL_MONO_U_I(lnot);
ARITH_DECL_MONO_U_I(lval);

ARITH_DECL_BI_UU_U(plus);
ARITH_DECL_BI_UU_U(minus);
ARITH_DECL_BI_UI_U(lsh);
ARITH_DECL_BI_UI_U(rsh);
ARITH_DECL_BI_UU_I(lt);
ARITH_DECL_BI_UU_I(leq);
ARITH_DECL_BI_UU_I(gt);
ARITH_DECL_BI_UU_I(geq);
ARITH_DECL_BI_UU_I(same);
ARITH_DECL_BI_UU_I(neq);
ARITH_DECL_BI_UU_U(and);
ARITH_DECL_BI_UU_U(xor);
ARITH_DECL_BI_UU_U(or);
ARITH_DECL_BI_UU_U(star);
ARITH_DECL_BI_UU_U(slash);
ARITH_DECL_BI_UU_U(pct);

/* functions with the signed type */

ARITH_DECL_MONO_U_S(to_s);
ARITH_DECL_MONO_I_S(fromint);
ARITH_DECL_MONO_L_S(fromlong);
ARITH_DECL_MONO_S_I(toint);
ARITH_DECL_MONO_S_L(tolong);

ARITH_DECL_MONO_S_S(neg);
ARITH_DECL_MONO_S_S(not);
ARITH_DECL_MONO_S_I(lnot);
ARITH_DECL_MONO_S_I(lval);

ARITH_DECL_BI_SS_S(plus);
ARITH_DECL_BI_SS_S(minus);
ARITH_DECL_BI_SI_S(lsh);
ARITH_DECL_BI_SI_S(rsh);
ARITH_DECL_BI_SS_I(lt);
ARITH_DECL_BI_SS_I(leq);
ARITH_DECL_BI_SS_I(gt);
ARITH_DECL_BI_SS_I(geq);
ARITH_DECL_BI_SS_I(same);
ARITH_DECL_BI_SS_I(neq);
ARITH_DECL_BI_SS_S(and);
ARITH_DECL_BI_SS_S(xor);
ARITH_DECL_BI_SS_S(or);
ARITH_DECL_BI_SS_S(star);
ARITH_DECL_BI_SS_S(slash);
ARITH_DECL_BI_SS_S(pct);

/* conversions from string */
ARITH_DECL_MONO_ST_US(octconst);
ARITH_DECL_MONO_ST_US(hexconst);
ARITH_DECL_MONO_ST_US(decconst);
