/*
    		 Crown Copyright (c) 1997, 1998
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.

  $TenDRA: tendra/src/lib/libtdf/INT64a.c,v 1.2 2005/09/26 19:27:57 stefanf Exp $
*/


/*		INT64liba.c
**		-----------
**
**  This file contains functions which perform 64-bit
**  arithmetic on 32-bit machines.
**
**  The functions all use abstract types which refer
**  to some characteristic of that type, so that INT32
**  is a 32-bit signed integer, and UINT64 is a 32-bit
**  unsigned integer.  These types are defined in the
**  file 'INT64_types.h'.
*/


#include <stdio.h>
#include "ossg.h"

#if defined (__TenDRA__)
#include "abstract.h"
#else
#include "concrete.h"
#endif  /* defined(__TenDRA__) */


#undef STATIC
#ifndef DEBUG
#define STATIC static
#else
#define STATIC
#endif  /* ndef DEBUG */

#define UINT32_const(X) ((UINT32) (X))


/* Error Treatments handling */

int __TDFerror;

#define OVERFLOW_ERROR	(__TDFerror = -1)
#if USE_SEPARATE_DIV_ZERO_ERROR
#define DIV_ZERO_ERROR	(__TDFerror = 0)
#else
#define DIV_ZERO_ERROR OVERFLOW_ERROR
#endif
#define CLEAR_ERRORS	(__TDFerror = 1)



/* Declarations of functions used internally */

static TDF_INT64	TDFUplus	(TDF_INT64, TDF_INT64);
static TDF_INT64	TDFUminus	(TDF_INT64, TDF_INT64);
static TDF_INT64	TDFUmult	(TDF_INT64, TDF_INT64);
static TDF_INT64	TDFUdiv_rem	(TDF_INT64, TDF_INT64, UINT32);
static TDF_INT64	TDFUshr		(TDF_INT64, UINT32);



/* Declarations of DEBUG functions */

#if DEBUG
INT64	make_INT64	(INT32, UINT32);
UINT64	make_UINT64	(UINT32, UINT32);
void 	INT64_print	(char *,  INT64, char *);
void	UINT64_print	(char *, UINT64, char *);
#endif


/*  Forward declarations  */

INT64	__TDFUs_plus	(INT64, INT64);
INT64	__TDFUs_minus	(INT64, INT64);
INT64	__TDFUs_mult	(INT64, INT64);
INT64	__TDFUs_div1	(INT64, INT64);
INT64	__TDFUs_div2	(INT64, INT64);
INT64	__TDFUs_rem1	(INT64, INT64);
INT64	__TDFUs_rem2	(INT64, INT64);
INT64	__TDFUneg	(INT64);
INT64	__TDFUabs	(INT64);
INT64	__TDFUsswiden	(INT32);
INT64	__TDFUuswiden	(UINT32);
INT32	__TDFUssshorten	(INT64);
INT32	__TDFUusshorten	(UINT64);
INT64	__TDFUu642s64	(UINT64);
INT64	__TDFUs_max	(INT64, INT64);
INT64	__TDFUs_min	(INT64, INT64);
int	__TDFUs_test	(INT64, INT64);

UINT64	__TDFUu_plus	(UINT64, UINT64);
UINT64	__TDFUu_minus	(UINT64, UINT64);
UINT64	__TDFUu_mult	(UINT64, UINT64);
UINT64	__TDFUu_div2	(UINT64, UINT64);
UINT64	__TDFUu_rem2	(UINT64, UINT64);
UINT64	__TDFUu_shl	(UINT64, UINT32);
UINT64	__TDFUu_shr	(UINT64, UINT32);
UINT64	__TDFUuuwiden	(UINT32);
UINT64	__TDFUsuwiden	(INT32);
UINT32	__TDFUsushorten	(INT64);
UINT32	__TDFUuushorten	(UINT64);
UINT64	__TDFUs642u64	(INT64);
UINT64	__TDFUu_max	(UINT64, UINT64);
UINT64	__TDFUu_min	(UINT64, UINT64);
int	__TDFUu_test	(UINT64, UINT64);
UINT64	__TDFUand	(UINT64, UINT64);
UINT64	__TDFUor	(UINT64, UINT64);
UINT64	__TDFUxor	(UINT64, UINT64);
UINT64	__TDFUnot	(UINT64);



#define TDFUis_positive(X)	((hi_32(X) >= 0))
#define TDFUis_negative(X)	((hi_32(X) < 0))
#define TDFUis_zero(X)		((hi_32(X) == 0) && (lo_32(X) == 0))
#define TDFUis_nonzero(X)	((hi_32(X) != 0) || (lo_32(X) != 0))


/*
** 	TDFUshl:
**
**  Simply masks out the necessary bits as appropriate
**  for shifts of less than 32 and those greater than 32.
**
**  No error checking - should have already been done.
**
**  Defined here as a macro, which is implemented as a
**  TDF token.  This token is then used in the PL_TDF
**  file which implements the remaining functions in the
**  64-bit arithmetic library.
**
**  The token must contain IF, but TDFC only allows this
**  to be done with "procedure tokens".  If non-TenDRA
**  compilation is to be possible, this macro cannot be
**  used directly since constants will produce illegal
**  shift values, so a function has to be defined.  A
**  function cannot have the desired side-effect of updating
**  the first parameter (without making it into a pointer),
**  so another macro is used (which calls the function
**  defined using the macro which produces the definition
**  of the TDF token !!!  Got it ???
*/


#define _TDFUshl(shifted_a,a,n) \
{ \
    if (n > 32)		/* assuming n <= 64 */ \
    { \
	hi_u32 (shifted_a) = lo_u32(a) << (n -32); \
	lo_u32 (shifted_a) = (UINT32) 0x00000000; \
    } \
    else	/*  (0 < n <= 32)  */ \
    { \
	hi_u32 (shifted_a) = (lo_u32(a) >> (32-n)) | (hi_u32(a) << n); \
	lo_u32 (shifted_a) =  lo_u32(a) << n; \
    } \
}

#ifdef __TenDRA__

/* Want to use this value in the .pl file */

#pragma token EXP rvalue : int : BIGEND_INT64 #
#ifndef BIGENDIAN_INT64
#define BIGEND_INT64 0
#else
#define BIGEND_INT64 BIGENDIAN_INT64
#endif



/* Use a "procedure token" to implement TDFUshl */

#pragma token PROC (EXP lvalue : TDF_INT64 : , \
		    EXP rvalue : TDF_INT64 : , \
		    EXP rvalue : UINT32 : ) \
		STATEMENT TDFUshl #
#define TDFUshl(shifted_a,a,n) _TDFUshl((shifted_a),(a),(n))
#else

/* Use a macro (which calls a function) to implement TDFUshl */

TDF_INT64 __TDFUshl(TDF_INT64, UINT32);
TDF_INT64
__TDFUshl(TDF_INT64 a, UINT32 n)
{
    TDF_INT64 tmp;

    _TDFUshl(tmp, a, n);
    return tmp;    
}
#define TDFUshl(shifted_a,a,n) { shifted_a = __TDFUshl(a,n); }
#endif  /* __TenDRA__ */




/*  Function definitions  */



/*
**	TDFUplus:
**
**  Generic function to add two 64-bit integers,
**  interpretting each as an unsigned value.
*/

static TDF_INT64
TDFUplus(TDF_INT64 a, TDF_INT64 b)
{
    TDF_INT64	sum;

    lo_u32 (sum) = lo_u32(a) + lo_u32(b);
    if (lo_u32(sum) < lo_u32(a)) {
	hi_u32 (sum) = hi_u32(a) + hi_u32(b) + 1;
    }
    else {
	hi_u32 (sum) = hi_u32(a) + hi_u32(b);
    }
    return sum;
}



/*
**	__TDFUs_plus:
**
**  Interpret signed numbers as unsigned, add them
**  as unsigned numbers, then reinterpret as signed.
**  Error handling if two positives produce a negative
**  or two negative produce a positive, else alright.
*/

INT64
__TDFUs_plus(INT64 param_a, INT64 param_b)
{
    TDF_INT64	sum, a, b;

    PARAM(a) = param_a;
    PARAM(b) = param_b;
    CLEAR_ERRORS;
    sum = TDFUplus (a, b);	/* add as unsigned numbers */

    if (TDFUis_positive(a)	&&	/* a >= 0 */
	TDFUis_positive(b)	&&	/* b >= 0 */
	TDFUis_negative(sum))		/* a+b < 0 */
    {
	OVERFLOW_ERROR;
    }
    if (TDFUis_negative(a)	&&	/* a < 0 */
	TDFUis_negative(b)	&&	/* b < 0 */
	TDFUis_positive(sum))		/* a+b >= 0 */
    {
	OVERFLOW_ERROR;
    }

    return  PARAM(sum);
}



/*
**	__TDFUu_plus:
**
**  Adds arguments, and tests to see if sum is less
**  than the first argument for error detection.
*/

UINT64
__TDFUu_plus(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	sum, a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;
    sum = TDFUplus (a, b);

    if ((hi_u32(sum) <= hi_u32(a))	&&
	(lo_u32(sum) < lo_u32(a)))	/* sum < a */
    {
	OVERFLOW_ERROR;
    }

    return  UPARAM(sum);
}



/*
**	TDFUminus:
**
**  Generic function to subtracting two 64-bit
**  integers, interpretting each as an unsigned value.
*/

static TDF_INT64
TDFUminus(TDF_INT64 a, TDF_INT64 b)
{
    TDF_INT64	sum;

    lo_u32 (sum) = lo_u32(a) - lo_u32(b);
    if (lo_u32(sum) > lo_u32(a)) {
	hi_u32 (sum) = hi_u32(a) - hi_u32(b) - 1;
    }
    else {
	hi_u32 (sum) = hi_u32(a) - hi_u32(b);
    }
    return sum;
}



/*
**	__TDFUs_minus:
**
**  Interpret signed numbers as unsigned, add them
**  as unsigned numbers, then reinterpret as signed.
**  Error handling if two positives produce a negative
**  or two negative produce a positive, else alright.
*/

INT64
__TDFUs_minus(INT64 param_a, INT64 param_b)
{
    TDF_INT64	diff, a, b;

    PARAM(a) = param_a;
    PARAM(b) = param_b;
    CLEAR_ERRORS;
    diff = TDFUminus (a, b);	/* subtract as unsigned numbers */

    if (TDFUis_positive(a)	&&	/* a >= 0 */
	TDFUis_negative(b)	&&	/* b < 0 */
	TDFUis_negative(diff))		/* a-b < 0 */
    {
	OVERFLOW_ERROR;
    }
    if (TDFUis_negative(a)	&&	/* a < 0 */
	TDFUis_positive(b)	&&	/* b >= 0 */
	TDFUis_positive(diff))		/* a-b >= 0 */
    {
	OVERFLOW_ERROR;
    }

    return  PARAM(diff);
}



/*
**	__TDFUu_minus:
**
**  Adds arguments, and tests to see if sum is less
**  than the first argument for error detection.
*/

UINT64
__TDFUu_minus(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	diff, a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;
    diff = TDFUminus (a, b);

    if ((hi_u32(diff) >= hi_u32(a))	&&
	(lo_u32(diff) > lo_u32(a)))	/* diff > a */
    {
	OVERFLOW_ERROR;
    }

    return  UPARAM(diff);
}









/*
**		a3	a2	a1	a0
**
**	X	b3	b2	b1	b0
**		--------------------------
**			     ----a0 X b0----	(STAGE1)
**
**		     ----a1 X b0----		(STAGE2)
**		     ----a0 X b1----
**
**	     ----a2 X b0----			(STAGE3)
**	     ----a1 X b1----
**	     ----a0 X b2----
**
**   ----a3 X b0----				(STAGE4)
**   ----a2 X b1----
**   ----a1 X b2----
**   ----a0 X b3----
**
**
**  These are the only terms necessary to produce the answer.
**  The other terms must all be used at error checking to
**  ensure that overflow does not occur.
**
**  By adding the contributions in this order, it is possible
**  to control the size of the sum as each stage, i.e. only
**  certain terms need to be considered, reducing the number
**  of steps in the addition.
**
**  If the parameter 'error_check' is non-zero, error-checking
**  is done: its value is reset to zero if non occur.
*/


#define hi_u16(X)	((X) >> 16)
#define lo_u16(X)	((X) & ((UINT32) 0xffff))

static TDF_INT64
TDFUmult(TDF_INT64 a, TDF_INT64 b)
{
    TDF_INT64	prod;
    UINT32	a0, a1, a2, a3;
    UINT32	b0, b1, b2, b3;
    UINT32	work1, work2;

    a0 = lo_u16 (lo_u32(a));
    a1 = hi_u16 (lo_u32(a));
    a2 = lo_u16 (hi_u32(a));
    a3 = hi_u16 (hi_u32(a));

    b0 = lo_u16 (lo_u32(b));
    b1 = hi_u16 (lo_u32(b));
    b2 = lo_u16 (hi_u32(b));
    b3 = hi_u16 (hi_u32(b));

    if ((a1 == 0) && (a2 == 0) && (a3 == 0) &&
	(b1 == 0) && (b2 == 0) && (b3 == 0))
    {
	hi_u32(prod) = 0;	/* result fits into 32 bits */
	lo_u32(prod) = a0 * b0;
	return  prod;
    }

/* Start STAGE1 */

    lo_u32(prod) = a0 * b0;


/* Start STAGE2 */

    work1 = a1 * b0;
    work2 = work1 + (a0 * b1);
    if (work2 < work1) {
	hi_u32(prod) = ((UINT32) 0x10000);		/* set carry in result */
    }
    else {
	hi_u32(prod) = 0;		/* must initialise somewhere */
    }

    work1 = lo_u16(work2);	/* lo-16 bits of sum */
    work2 = hi_u16(work2);	/* hi-16 bits of sum */

    lo_u32(prod) += (work1 << 16);
    if ((work1 << 16) > lo_u32(prod)) {	/* wrap */
	hi_u32(prod) += (1 + work2);
    }
    else {
	hi_u32(prod) += work2;
    }


/* Start STAGE3 */

    work1 = a2 * b0;
    work2 = work1 + (a1 * b1);

    if (work1 > work2) {
	OVERFLOW_ERROR;
    }

    work1 = work2 + (a0 * b2);

    if (work2 > work1) {
	OVERFLOW_ERROR;
    }

    hi_u32(prod) += work1;

    if (work1 > hi_u32(prod)) {
	OVERFLOW_ERROR;
    }


/* Start STAGE4 */

    work1 = a3 * b0;
    work2 = a2 * b1;

    if ((work1 > ((UINT32) 0xffff)) ||
	(work2 > ((UINT32) 0xffff)))
    {
	OVERFLOW_ERROR;
    }

    work1 = work1 + work2;
    work2 = a1 * b2;

    if (work2 > ((UINT32) 0xffff)) {
	OVERFLOW_ERROR;
    }

    work1 = work1 + work2;
    work2 = a0 * b3;
    work1 = work1 + work2;

    if ((work1 > ((UINT32) 0xffff)) ||
	(work2 > ((UINT32) 0xffff)))
    {
	OVERFLOW_ERROR;
    }

    work2 = work1 << 16;	/* We've made sure this will work */
    hi_u32(prod) += work2;

    if ((work2 > hi_u32(prod))	||	/* This mustn't overflow */
	(a3 * b1 != 0)		||
	(a2 * b2 != 0)		||	/* Remaining contributions */
	(a1 * b3 != 0)		||	/*    must all be zero     */
	(a3 * b2 != 0)		||
	(a2 * b3 != 0)		||
	(a3 * b3 != 0))
    {
	OVERFLOW_ERROR;
    }

    return prod;
}





/*
**	__TDFUs_mult:
**
**  Implements long multiplication in binary,
**  checking for errors as it goes along.
*/


INT64
__TDFUs_mult(INT64 param_a, INT64 param_b)
{
    TDF_INT64	prod, a, b;
    int		sign;

    PARAM(a) = param_a;
    PARAM(b) = param_b;
    CLEAR_ERRORS;

    sign = ((hi_32(a) ^ hi_32(b)) < 0);	/* sign of result */

    PARAM(a) = __TDFUabs(param_a);
    PARAM(b) = __TDFUabs(param_b);
    prod = TDFUmult (a, b);				/* Any overflow will be flagged */

    if (hi_32(prod) < 0) {				/* definitely okay if this is false */
	if ((sign == 0) 			   || 	 /* positive int too big for INT_64 */
	    (hi_u32(prod) > ((UINT32) 0x80000000)) ||   /* signed int too big for INT_64 */
	    (lo_u32(prod) > ((UINT32) 0x00000000)))
	{
	    OVERFLOW_ERROR;
	}
    }

    if ((sign == 0)				 ||	/* result is unsigned */
	((hi_u32(prod) == ((UINT32) 0x80000000)) &&	/* - this number has the same     */
	 (lo_u32(prod) != ((UINT32) 0x00000000))))	/* representation as its negation */
    {
	return  PARAM(prod);
    }
    return  __TDFUneg(PARAM(prod));
}




/*
**	__TDFUu_mult:
**
**  Implements long multiplication in binary,
**  checking for errors as it goes along.
*/

#if 0

UINT64
__TDFUu_mult(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	prod, a, b;

    if (__TDFUu_test(param_b, UPARAM(const_u0)) == 0)  return UPARAM(const_u0);

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    prod = const_u0;

    while (__TDFUu_test(UPARAM(b), UPARAM(const_u1)) != 0)
    {
	if ((lo_u32(b) & 1) != 0) {
	    prod = __TDFUu_plus (UPARAM(prod), UPARAM(a));	/* Any overflow will be flagged */
	}
	PARAM(a) = __TDFUu_shl (UPARAM(a), (UINT32) 1);
	PARAM(b) = __TDFUu_shr (UPARAM(b), (UINT32) 1);
    } 

    return  UPARAM (__TDFUu_plus (prod, a));
}

#else

UINT64
__TDFUu_mult(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	prod, a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;
    prod = TDFUmult (a, b);	/* Any overflow will be flagged */
    return  UPARAM(prod);
}

#endif



/*
**	Implementation of div0, div1, rem0 and rem1.
**	===========================================
**
**  Clearly there are no problems for unsigned numbers.
**  I have written a generic function to divide two
**  unsigned 64-bit integers which produces both the
**  result of the divide and the remainder.
**
**  For signed numbers, a and b, it is possible to work
**  out the values of m and n s.t.:
**
**	|a| = n * |b| + m,	(0 <= m < |b|)
**
**  From this information, I believe the correct values
**  for the various functions can be deduced from the
**  following table:
**
**
**	a	b	class	 |	div		rem
**    ======================================================
**	+	+	  1	 |	 n		 m
**	+	+	  2	 |	 n		 m
**	+	-	  1	 | -n or -(n+1)	    0 or (b+m)
**	+	-	  2	 |	-n		 m
**	-	+	  1	 | -n or -(n+1)	    0 or (b-m)
**	-	+	  2	 |	-n		-m
**	-	-	  1	 |	 n		-m
**	-	-	  2	 |	 n		-m
*/




/*
**	TDFUdiv_rem:
**
**  Generic function to divide two 64-bit integers,
**  interpretting each as an unsigned value.  Implements
**  the basic 'shift, compare and subtract' algorithm.
**
**  Since the one function produces both the result and
**  the remainder, it is passed a parameter which tells
**  it which result is expected.  The S_DIV_0_1 flags is
**  used for a signed-div0 call for which the arguments
**  represent integers of different signs; in this case,
**  it is the result depends on the remainder (and so
**  is adjusted according while this information is still
**  freely available.
**
*/

#define S_DIV1		((UINT32) 0)
#define S_DIV2		((UINT32) 2)
#define U_DIV2		((UINT32) 3)
#define S_REM1		((UINT32) 4)
#define S_REM2		((UINT32) 6)
#define U_REM2		((UINT32) 7)
#define S_DIV1_0_1	((UINT32) 8)

#define is_signed(X)	(((X) & 1) == 0)
#define is_class1(X)	(((X) & 2) == 0)
#define is_div(X)	(((X) & 4) == 0)

static TDF_INT64
TDFUdiv_rem(TDF_INT64 a, TDF_INT64 b, UINT32 flags)
{
    TDF_INT64	new_int, a_upper;
    UINT32	i;

    new_int = const_u0;
    a_upper = const_u0;

    if (hi_u32(a) == 0) {
	if (hi_u32(b) == 0) {	/* both a and b fit into 32 bits */
	    lo_u32(new_int) = lo_u32(a) / lo_u32(b);
	    lo_u32(a_upper) = lo_u32(a) % lo_u32(b);
	}
	else {			/* b > a */
	    lo_u32(new_int) = 0;
	    a_upper = a;
	}
    }

    else {
	for (i = 64; i > 0; i--)
	{
	    TDFUshl (new_int, new_int, (UINT32) 1);
	    
	    TDFUshl (a_upper, a_upper, (UINT32) 1);
	    if ((hi_u32(a) >> 31) != 0)
	    {
		hi_u32(a) &= ((UINT32) 0x7fffffff);	/* mask out for shift */
		lo_u32(a_upper) |= 1;	/* do carry */
	    }
	    TDFUshl (a, a, (UINT32) 1);
	    
	    if (__TDFUu_test(UPARAM(a_upper), UPARAM(b)) >= 0) {
		UPARAM(a_upper) = __TDFUu_minus (UPARAM(a_upper), UPARAM(b));
		lo_u32(new_int) |= 1;
	    }
	} 
	
    }

    if (is_div(flags))		/* result of 'div' */
    {
	if ((flags == S_DIV1_0_1)	&&
	    ((lo_32(a_upper) != 0)	||
	     (hi_32(a_upper) != 0)))
	{
	    PARAM(new_int) = __TDFUs_plus(PARAM(new_int), PARAM(const_1));
	}
	return  new_int;
    }

    return  a_upper;		/* result of 'rem' */
}



/*
**	__TDFUs_div1:
**
**  Division-by-zero is the only possible error.
*/

INT64
__TDFUs_div1(INT64 param_a, INT64 param_b)
{
    TDF_INT64	quot, a, b;
    int		is_neg;

    PARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    PARAM(a) = param_a;

    is_neg = ((hi_32(a) ^ hi_32(b)) < 0);		/* is result negative ? */
    PARAM(a) = __TDFUabs(param_a);
    PARAM(b) = __TDFUabs(param_b);

    if (is_neg) {
	quot = TDFUdiv_rem (a, b, S_DIV1_0_1);
	CLEAR_ERRORS;					/* May have been set by __TDFUabs */
	hi_32(quot) = hi_32(quot) ^ ( (INT32) 
		((UINT32) 0xffffffff));		/* Avoid errors, don't call __TDFUneg */
	lo_32(quot) = lo_32(quot) ^ UINT32_const(0xffffffff);
	return  PARAM (TDFUplus (quot, const_1));
    }

    quot = TDFUdiv_rem (a, b, S_DIV1);
    CLEAR_ERRORS;	/* May have been set by __TDFUabs */

    if (TDFUis_negative(quot))		/* Only happens for INT_MAX+1 */
    {
	OVERFLOW_ERROR;
    }
    return  PARAM(quot);
}



/*
**	__TDFUs_div2:
**
**  Division-by-zero is the only possible error.
*/

INT64
__TDFUs_div2(INT64 param_a, INT64 param_b)
{
    TDF_INT64	quot, a, b;
    int		is_neg;

    PARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    PARAM(a) = param_a;

    is_neg = ((hi_32(a) ^ hi_32(b)) < 0);	/* is result negative ? */
    PARAM(a) = __TDFUabs(param_a);
    PARAM(b) = __TDFUabs(param_b);

    quot = TDFUdiv_rem (a, b, S_DIV2);
    CLEAR_ERRORS;	/* May have been set by __TDFUabs */

    if (is_neg) {
	hi_32(quot) = hi_32(quot) ^
	   ( (INT32) ((UINT32) 0xffffffff)); /* Avoid errors, don't call __TDFUneg */
	lo_32(quot) = lo_32(quot) ^ ((UINT32) 0xffffffff);
	return  PARAM(TDFUplus (quot, const_1));
    }

    if (TDFUis_negative(quot))		/* Only happens for INT_MAX+1 */
    {
	OVERFLOW_ERROR;
    }
    return  PARAM(quot);
}



/*
**	__TDFUu_div2:
**
**  Division-by-zero is the only possible error.
*/

UINT64
__TDFUu_div2(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    CLEAR_ERRORS;

    UPARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    UPARAM(a) = param_a;

    return  UPARAM (TDFUdiv_rem (a, b, U_DIV2));
}


/*
**	__TDFUs_rem1:
**
**  Modulo-zero is the only possible error.
*/

INT64
__TDFUs_rem1(INT64 param_a, INT64 param_b)
{
    TDF_INT64	rem, a, b, abs_a, abs_b;
    int		is_neg;

    PARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    PARAM(a) = param_a;
    PARAM(abs_a) = __TDFUabs(param_a);
    PARAM(abs_b) = __TDFUabs(param_b);

    is_neg = TDFUis_negative(b);	/* is result negative ? */
    rem = TDFUdiv_rem (abs_a, abs_b, S_REM1);
    CLEAR_ERRORS;		/* May have been set by __TDFUabs */

    if (TDFUis_positive(a)	&&
	TDFUis_negative(b)	&&
	TDFUis_nonzero(rem))
    {
	return  __TDFUs_plus (param_b, PARAM(rem));
    }

    if (TDFUis_negative(a)	&&
	TDFUis_positive(b)	&&
	TDFUis_nonzero(rem))
    {
	return  __TDFUs_minus (param_b, PARAM(rem));
    }

    if (is_neg) return  __TDFUneg(PARAM(rem));
    return  PARAM(rem);
}



/*
**	__TDFUs_rem2:
**
**  Modulo-zero is the only possible error.
*/

INT64
__TDFUs_rem2(INT64 param_a, INT64 param_b)
{
    TDF_INT64	rem, a, b;
    int		is_neg;

    PARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    PARAM(a) = param_a;

    is_neg = TDFUis_negative(a);	/* is result negative ? */
    PARAM(a) = __TDFUabs(param_a);
    PARAM(b) = __TDFUabs(param_b);

    rem = TDFUdiv_rem (a, b, S_REM2);
    CLEAR_ERRORS;		/* May have been set by __TDFUabs */

    if (is_neg) return  __TDFUneg(PARAM(rem));
    return  PARAM(rem);
}



/*
**	__TDFUu_rem2:
**
**  Modulo-zero is the only possible error.
*/

UINT64
__TDFUu_rem2(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    UPARAM(b) = param_b;
    if (TDFUis_zero(b))
    {
	DIV_ZERO_ERROR;		/* Stop now - return anything */
	return param_b;
    }
    UPARAM(a) = param_a;

    CLEAR_ERRORS;
    return  UPARAM (TDFUdiv_rem (a, b, U_REM2));
}



/*
** 	__TDFUneg:
**
**  Flip all bits and add 1, as for 2's complement.
**  Overflow error if original value was INT64_MIN.
*/

INT64
__TDFUneg(INT64 param_a)
{
    TDF_INT64	a;

    PARAM(a) = param_a;
    hi_32(a) = hi_32(a) ^ ((INT32) ((UINT32) 0xffffffff));
    lo_32(a) = lo_32(a) ^ ((UINT32) 0xffffffff);
    return  __TDFUs_plus (PARAM(a), PARAM(const_1));	/* Should handle any errors */
}




/*
** 	__TDFUs_shl:
**
**  Best implemented in PL_TDF.
*/



/*
** 	__TDFUu_shl:
**
**  Does error checking.  If all is okay, returns the value.
**  
*/

UINT64
__TDFUu_shl(UINT64 param_a, UINT32 n)
{
    TDF_INT64	a;

    CLEAR_ERRORS;
    if (n > 63) {
	OVERFLOW_ERROR;
	return  UPARAM(const_u0);
    }

    if (n == 0)	return param_a;

    /* check that the result won't overflow */

    UPARAM(a) = param_a;
    if (n > 32) {
	if (((lo_u32(a) >> (64-n)) != 0)	||
	    (hi_u32(a) != 0))
	{
	    OVERFLOW_ERROR;
	}
    }
    else if ((hi_u32(a) >> (32-n)) != 0)
    {
	OVERFLOW_ERROR;
    }

    TDFUshl (a, a, n);
    return  UPARAM(a);
}



/*
** 	__TDFUu_shr:
**
**  2 cases: n > 32  - copy high 32-bits across and shift.
**  	     n <= 32 - mask out two contributions and OR together.
*/

static TDF_INT64
TDFUshr(TDF_INT64 a, UINT32 n)
{
    TDF_INT64	shifted_a;

    CLEAR_ERRORS;
    if (n >= 32)	/* 32 <= n < 64 */
    {
	lo_u32 (shifted_a) = hi_u32(a) >> (n -32);
	hi_u32 (shifted_a) = (UINT32) 0x00000000;
    }
    else	/*  (0 < n <= 32)  */
    {
	lo_u32 (shifted_a) = (hi_u32(a) << (32-n)) | (lo_u32(a) >> n);
	hi_u32 (shifted_a) =  hi_u32(a) >> n;
    }

    return  shifted_a;
}



/*
** 	__TDFUs_shr:
**
**  Best implemented in PL_TDF
*/



/*
** 	__TDFUu_shr:
**
**  Error occurs if n > 64 since the result is undefined.
*/

UINT64
__TDFUu_shr(UINT64 param_a, UINT32 n)
{
    TDF_INT64	a;

    CLEAR_ERRORS;
    if (n > 63) {	/* This is undefined */
	OVERFLOW_ERROR;
	return  UPARAM(const_u0);
    }

    if (n == 0) return param_a;
    UPARAM(a) = param_a;
    return  UPARAM (TDFUshr (a, n));
}



/*
** 	__TDFUabs:
**
**  Checks for a negative quantity, for which it
**  calls '__TDFUneg'.  Overflow for, as for __TDFUneg,
**  occurs when the value is INT32_MIN.
**  
*/

INT64
__TDFUabs(INT64 param_a)
{
    TDF_INT64	a;

    PARAM(a) = param_a;
    if (TDFUis_negative(a)) {
	return  __TDFUneg (param_a);	/* This handles any errors */
    }

    CLEAR_ERRORS;
    return  param_a;
}


/*
** 	__TDFUsswiden:
**
**  Extends the sign bit, and returns the new int.
**
*/

INT64
__TDFUsswiden(INT32 a)
{
    TDF_INT64	ext_int;

    CLEAR_ERRORS;
    hi_32(ext_int) = (INT32) ((a < 0) ? -1 : 0);	/* sign-extend a */
    lo_32(ext_int) = (UINT32) a;
    return  PARAM(ext_int);
}


/*
** 	__TDFUsuwiden:
**
**  Error if a is negative.
**  Otherwise extends the sign bit (just to be safe)
*/

UINT64
__TDFUsuwiden(INT32 a)
{
    TDF_INT64	ext_int;

    CLEAR_ERRORS;
    if (a < 0)
    {
	OVERFLOW_ERROR;
    }

    hi_u32(ext_int) = (UINT32) ((a < 0) ? 0xffffffff : 0);
					/* sign-extend a */
    lo_u32(ext_int) = (UINT32) a;
    return  UPARAM(ext_int);
}


/*
** 	__TDFUuuwiden:
**
**  Puts zero in bits 32-63 and returns
*/

UINT64
__TDFUuuwiden(UINT32 a)
{
    TDF_INT64	ext_int;

    CLEAR_ERRORS;
    hi_u32(ext_int) = 0;
    lo_u32(ext_int) = a;
    return  UPARAM(ext_int);
}


/*
** 	__TDFUuswiden:
**
**  Same as __TDFUuuswiden.
**
*/

INT64
__TDFUuswiden(UINT32 a)
{
    TDF_INT64	ext_int;

    CLEAR_ERRORS;
    hi_32(ext_int) = 0;
    lo_32(ext_int) = a;
    return  PARAM(ext_int);
}


/*
** 	__TDFUssshorten:
**
**  Check that bits 31-63 are all set/unset,
**  otherwise signal overflow.  Mask sign
**  bit into bit-31.
*/

INT32
__TDFUssshorten(INT64 param_a)
{
    TDF_INT64	a;

    PARAM(a) = param_a;
    CLEAR_ERRORS;
    switch ((int) hi_32(a)) {
      case 0:
	  if ((lo_32(a) & ((UINT32) 0x80000000)) != 0) {
	      OVERFLOW_ERROR;
	  }
	  break;
      case -1:
	  if ((lo_32(a) & ((UINT32) 0x80000000)) == 0) {
	      OVERFLOW_ERROR;
	  }
	  break;
      default:
	  OVERFLOW_ERROR;
    }

    return  ((((INT32) lo_32(a)) & ((INT32) 0x7fffffff)) |
		      (hi_32(a)  & ((INT32) ((UINT32) 0x80000000))));

}


/*
** 	__TDFUsushorten:
**
**  Check that bits 32-63 are all zero
**  otherwise signal overflow.  Answer
**  is in bits 0-31.
*/

UINT32
__TDFUsushorten(INT64 param_a)
{
    TDF_INT64	a;

    PARAM(a) = param_a;
    CLEAR_ERRORS;
    if (hi_32(a) != 0)
    {
	OVERFLOW_ERROR;
    }

    return  lo_32(a);
}


/*
** 	__TDFUuushorten:
**
**  Error if bits 32-63 are not all zero.
**  Return bits 0-31.
*/

UINT32
__TDFUuushorten(UINT64 param_a)
{
    TDF_INT64	a;

    UPARAM(a) = param_a;
    CLEAR_ERRORS;
    if (hi_u32(a) != 0)
    {
	OVERFLOW_ERROR;
    }

    return  lo_u32(a);
}


/*
** 	__TDFUusshorten:
**
**  Error if bits 31-63 are not all zero.
**  Return bits 0-31.
*/

INT32
__TDFUusshorten(UINT64 param_a)
{
    TDF_INT64	a;

    UPARAM(a) = param_a;
    CLEAR_ERRORS;
    if ((hi_u32(a) != 0)		||
	(((INT32) lo_u32(a)) < 0))	/* Must be an error - started off unsigned */
    {
	OVERFLOW_ERROR;
    }

    return  (INT32) lo_u32(a);
}


/*
** 	__TDFUu642s64:
**
**  Error only if MSB of "a" is set.
**  Return bits 0-31.
*/

INT64
__TDFUu642s64(UINT64 param_a)
{
    TDF_INT64	a;

    UPARAM(a) = param_a;
    CLEAR_ERRORS;
    if (TDFUis_negative(a))	/* Cast and check */
    {
	OVERFLOW_ERROR;
    }

    return  PARAM(a);
}


/*
** 	__TDFUs642u64:
**
**  Error only if "a" < 0.
**  Return bits 0-31.
*/

UINT64
__TDFUs642u64(INT64 param_a)
{
    TDF_INT64	a;

    PARAM(a) = param_a;
    CLEAR_ERRORS;
    if (TDFUis_negative(a))	/* Check and cast */
    {
	OVERFLOW_ERROR;
    }

    return  UPARAM(a);
}




/*
** 	__TDFUs_max:
**
**  Uses: (a>b) ? a : b		No errors.
**
*/

INT64
__TDFUs_max(INT64 param_a, INT64 param_b)
{
    CLEAR_ERRORS;
    if (__TDFUs_test(param_a, param_b) > 0) {
	 return param_a;			/* a = max(a,b) */
    }
    return  param_b;				/* b = max(a,b) */
}


/*
** 	__TDFUu_max:
**
**  Uses: (a>b) ? a : b		No errors.
**
*/

UINT64
__TDFUu_max(UINT64 param_a, UINT64 param_b)
{
    if (__TDFUu_test(param_a, param_b) > 0) {
	 return param_a;			/* a = max(a,b) */
    }
    return  param_b;				/* b = max(a,b) */
}


/*
** 	__TDFUs_min:
**
**  Uses: (a<b) ? a : b		No errors.
**
*/

INT64
__TDFUs_min(INT64 param_a, INT64 param_b)
{
    if (__TDFUs_test(param_a, param_b) < 0) {
	return param_a;				/* a = min(a,b) */
    }
    return param_b;				/* b = min(a,b) */
}


/*
** 	__TDFUu_min:
**
**  Uses: (a<b) ? a : b		No errors.
**
*/

UINT64
__TDFUu_min(UINT64 param_a, UINT64 param_b)
{
    if (__TDFUu_test(param_a, param_b) < 0) {
	return param_a;				/* a = min(a,b) */
    }
    return param_b;				/* b = min(a,b) */
}




/*
**	TDFs_test:
**
**  Returns -1  if a < b;	This gives:  a op b <=> TDFs_test(a,b) op 0
**           0  if a = b;
**	     1  if a > b.
**
*/

int
__TDFUs_test(INT64 param_a, INT64 param_b)
{
    TDF_INT64	a, b;

    PARAM(a) = param_a;
    PARAM(b) = param_b;
    CLEAR_ERRORS;
    if (hi_32(a) < hi_32(b)) return -1;
    if (hi_32(a) > hi_32(b)) return 1;
    if (lo_32(a) < lo_32(b)) return -1;
    if (lo_32(a) > lo_32(b)) return 1;
    return 0;
}


/*
**	__TDFUu_test:  as for __TDFUs_test, but for unsigned types.
*/

int
__TDFUu_test(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;

    if (hi_u32(a) < hi_u32(b)) return -1;
    if (hi_u32(a) > hi_u32(b)) return 1;
    if (lo_u32(a) < lo_u32(b)) return -1;
    if (lo_u32(a) > lo_u32(b)) return 1;
    return 0;
}




/*
**	__TDFUand
**
**  ANDs the hi- and low-words together, and returns.
*/

UINT64
__TDFUand(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;

    hi_u32(a) = (hi_u32(a) & hi_u32(b));
    lo_u32(a) = (lo_u32(a) & lo_u32(b));
    return  UPARAM(a);
}


/*
**	__TDFUor
**
**  ORs the hi- and low-words together, and returns.
*/

UINT64
__TDFUor(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;

    hi_u32(a) = (hi_u32(a) | hi_u32(b));
    lo_u32(a) = (lo_u32(a) | lo_u32(b));
    return  UPARAM(a);
}



/*
**	__TDFUxor
**
**  ORs the hi- and low-words together, and returns.
*/

UINT64
__TDFUxor(UINT64 param_a, UINT64 param_b)
{
    TDF_INT64	a, b;

    UPARAM(a) = param_a;
    UPARAM(b) = param_b;
    CLEAR_ERRORS;

    hi_u32(a) = (hi_u32(a) ^ hi_u32(b));
    lo_u32(a) = (lo_u32(a) ^ lo_u32(b));
    return  UPARAM(a);
}



/*
**	__TDFUnot
**
**  XORs the hi- and low-words each with 0xffffffff
*/

UINT64
__TDFUnot(UINT64 param_a)
{
    TDF_INT64	a;

    UPARAM(a) = param_a;
    CLEAR_ERRORS;
    hi_u32(a) = hi_u32(a) ^ ((UINT32) 0xffffffff);
    lo_u32(a) = lo_u32(a) ^ ((UINT32) 0xffffffff);
    return  UPARAM(a);
}




/* Functions only used for debug purposes - normally hidden */

#if DEBUG
INT64
make_INT64(INT32 new_hi, UINT32 new_lo)
{
    TDF_INT64	new_int;

    hi_32 (new_int) = new_hi;
    lo_32 (new_int) = new_lo;
    return  PARAM(new_int);
}


UINT64
make_UINT64(UINT32 new_hi, UINT32 new_lo)
{
    TDF_INT64	new_int;

    hi_u32 (new_int) = new_hi;
    lo_u32 (new_int) = new_lo;
    return  UPARAM(new_int);
}

void
INT64_print(char *t1, INT64 a, char *t2)
{
    IGNORE printf ("%s(%d,%u)%s", t1, (int) a.hi32, (unsigned) a.lo32, t2);
    return;
}

void
UINT64_print(char *t1, UINT64 a, char *t2)
{
    IGNORE printf ("%s(%u,%u)%s", t1, (unsigned) a.hi32, (unsigned) a.lo32, t2);
    return;
}
#endif  /* DEBUG */


syntax highlighted by Code2HTML, v. 0.9.1