/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
/* File: o_int.c */
/* Author: David F. Bacon */
#ifndef lint
static char sccsinfo[] = "@(#)o_int.c	1.9 4/19/90";
#endif

#include "ops.h"
#include "sysdep.h"

#define Dst (DstObj->value.integer)
#define Src (SrcObj->value.integer)
#define Src1 (Src1Obj->value.integer)
#define Src2 (Src2Obj->value.integer)

#define signbit(i) ((i) & SIGNBIT)
                                /* returns 0 if positive or 0, non-zero if */
                                /*  negative. */

#define init_integer(objectp, val) \
  (set_init((objectp), dr_integer), (objectp)->value.integer = (val))


extern datarep dr_integer, dr_boolean, dr_ord_enumeration;

NILOP(o_icvt_enum)
{
    OPCHK(SrcObj,integer);
    if (Src < args->qualifiers.integer and Src >= 0) {
        Dst = Src;
	set_init(DstObj, dr_ord_enumeration);
    }
    else 
      raise(RangeError);
}


NILOP(o_ilit)
{
    
    init_integer(DstObj, args->qualifiers.integer);
                                /* for a literal, the value is the qualifier */
				/*  of the operation. */
}




NILOP(o_inegate)
{
    register int s;

    OPCHK(SrcObj,integer);
    s = Src;			/* get source operand */

    init_integer(DstObj, -s);	/* compute result. */
}



NILOP(o_iadd)
{
    register int s1, s2;

    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands... */
    s2 = Src2;

    if (s2 >= 0) {		/* then this is addition */
	if (s1 > MAXINT - s2) {
	    raise(Depletion);	/* overflow */
	    return;
	}
    }
    else {			/* s2 is negative, so this is subtraction */
	if (s1 < MININT - s2) {
	    raise(Depletion);	/* overflow */
	    return;
	}
    }

    init_integer(DstObj, s1 + s2);
				/* all is ok; perform the addition. */
}



NILOP(o_isubtract)
{
    register int s1, s2;


    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands... */
    s2 = Src2;

    if (s2 >= 0) {		/* real subtraction */
	if (s1 < MININT + s2) {
	    raise(Depletion);	/* overflow */
	    return;
	}
    }
    else {			/* this is really addition */ 
	if (s1 > MAXINT + s2) {
	    raise(Depletion);	/* overflow */
	    return;
	}
    }

    init_integer(DstObj, s1 - s2); /* compute result (pant, pant!) */
}



NILOP(o_idivide)
{
    register int s1, s2;


    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands... */
    s2 = Src2;

    if (s2 is 0) {		
	raise(DivideByZero);	/* trying to divide by zero. */
	return;
    }

    init_integer(DstObj, s1 / s2);
}



NILOP(o_irem)
{
    register int s1, s2, d;


    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands... */
    s2 = Src2;

    if (s2 is 0) {		
	raise(DivideByZero);	/* can't get remainder of dividing by zero. */
	return;
    }

    d = s1 % s2;		/* get within |s2| of answer */

    /* result must be same sign as s1... in general C makes no */
    /* guarantee about the sign of d at this point */
    if (signbit(d) isnt signbit(s1) and d isnt 0)
      d += (signbit(d) is signbit(s2)) ? -s2 : s2;
    init_integer(DstObj, d);
}



NILOP(o_imod)
{
    register int s1, s2, d;


    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands... */
    s2 = Src2;

    if (s2 is 0) {		
	raise(DivideByZero);	/* can't get remainder of dividing by zero. */
	return;
    }

    d = s1 % s2;		/* get within |s2| of correct answer */
    
    /* result must be same sign as s2... in general C makes no */
    /* guarantee about the sign of d at this point */
    if (signbit(d) isnt signbit(s2) and d isnt 0)
      d += s2;
    init_integer(DstObj, d);
}



NILOP(o_imultiply)
{
    register int s1, s2, tmp;	/* operands */


    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    s1 = Src1;			/* get operands.... */
    s2 = Src2;
    if (s1 > s2)		/* ensure s1 <= s2 */
      (tmp = s1 , s1 = s2 , s2 = tmp);

    if (s1 is 0 or s2 is 0)	/* special case for zero */
      init_integer(DstObj, 0);
    else if (s1 is 1)		/* special cases for unity */
      init_integer(DstObj, s2);
    else if (s2 is 1)
      init_integer(DstObj, s1);
    else if (s1 is MININT)	/* MININT by anything but 0 or 1 fails */
      raise(Depletion);
    else if (s1 is -1)		/* special cases for negation */
      init_integer(DstObj, -s2);
    else if (s2 is -1)
      init_integer(DstObj, -s1);
    else if (s1 > 0)		/* positive times positive */
      if (s1 > MAXINT / s2)
	raise(Depletion);	/* overflow */
      else
	init_integer(DstObj, s1*s2);
    else if (s2 < 0)		/* negative times negative */
      if (s1 < MAXINT / s2)
	raise(Depletion);	/* overflow */
      else
	init_integer(DstObj, s1*s2);
    else			/* negative times positive */
      if (s1 < MININT / s2)
	raise(Depletion);
      else
	init_integer(DstObj, s1*s2);
}


NILOP(o_igt)
{
    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    if (Src1 > Src2)
      DstObj->value.boolean = nil_true;
    else
      DstObj->value.boolean = nil_false;

    set_init(DstObj, dr_boolean);
}


NILOP(o_ige)
{
    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    if (Src1 >= Src2)
      DstObj->value.boolean = nil_true;
    else
      DstObj->value.boolean = nil_false;

    set_init(DstObj, dr_boolean);
}


NILOP(o_ilt)
{
    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    if (Src1 < Src2)
      DstObj->value.boolean = nil_true;
    else
      DstObj->value.boolean = nil_false;

    set_init(DstObj, dr_boolean);
}


NILOP(o_ile)
{
    OPCHK(Src1Obj,integer);
    OPCHK(Src2Obj,integer);
    if (Src1 <= Src2)
      DstObj->value.boolean = nil_true;
    else
      DstObj->value.boolean = nil_false;

    set_init(DstObj, dr_boolean);
}


status
eq_integer(s1, s2)
valcell s1, s2;
{
    if (s1.integer is s2.integer)
      return(SUCCESS);
    else
      return(FAILURE);
}
