/*
 * File: oarith.c
 *  Contents: divide, minus, mod, mult, neg, number, plus, powr
 */

#include <math.h>
#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


#ifdef SUN
#include <signal.h>
#endif					/* SUN */

int over_flow;

/*
 * x / y - divide y into x.
 */

OpDcl(divide,2,"/")
   {
   register int t1, t2;
   double r1, r2;

   /*
    * Arg1 and Arg2 must be numeric.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail)
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Arg1 and Arg2 are both integers, just divide them and return the
       * result.
       */
      if (IntVal(Arg2) == 0L) 
         RunErr(201, &Arg2);

#if MSDOS && LATTICE
      {
      long i, j;
      i = IntVal(Arg1);
      j = i / IntVal(Arg2);
      MakeInt(j, &Arg0);
      }
#else					/* MSDOS && LATTICE */
       MakeInt(IntVal(Arg1) / IntVal(Arg2), &Arg0);
#endif					/* MSDOS && LATTICE */

      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either Arg1 or Arg2 or both is real, convert the real values to
       *  integers, divide them, and return the result.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
         if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

      if (r2 == 0.0) 
         RunErr(-204, NULL);

      if (makereal(r1 / r2, &Arg0) == Error) 
         RunErr(0, NULL);

#ifdef SUN
      if (((struct b_real *)BlkLoc(Arg0))->realval == HUGE)
         kill(getpid(),SIGFPE);
#endif					/* SUN */

      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigdiv(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }


/*
 * x - y - subtract y from x.
 */

OpDcl(minus,2,"-")
   {
   register int t1, t2;
   double r1, r2;

   /*
    * x and y must be numeric.  Save the cvnum return values for later use.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail) 
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Both x and y are integers.  Perform integer subtraction and place
       *  the result in Arg0 as the return value.
       */

      MakeInt(sub(IntVal(Arg1), IntVal(Arg2)), &Arg0);
      if (over_flow)

#ifdef LargeInts
	 if (bigsub(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	    RunErr(0, NULL);
#else					/* LargeInts */
         RunErr(-203, NULL);
#endif					/* LargeInts */

      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either x or y is real, convert the other to a real, perform
       *  the subtraction and place the result in Arg0 as the return value.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
         if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

#ifdef  RTACIS
      {
      double rtbug_temporary;	/* bug with "-" arithmetic as parameter */
      rtbug_temporary = r1 - r2;	
      if (makereal(rtbug_temporary, &Arg0) == Error) 
         RunErr(0, NULL);
#else					/* RTACIS */
      if (makereal(r1 - r2, &Arg0) == Error) 
         RunErr(0, NULL);
#endif					/* RTACIS */

      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigsub(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }


/*
 * x % y - take remainder of x / y.
 */

OpDcl(mod,2,"%")
   {
   register int t1, t2;
   long int_rslt;
   double r1, r2, real_rslt;

   /*
    * x and y must be numeric.  Save the cvnum return values for later use.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail) 
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Both x and y are integers.  If y is 0, generate an error because
       *  it's divide by 0.  Otherwise, just return the modulus of the
       *  two arguments.
       */
      if (IntVal(Arg2) == 0L) 
         RunErr(202, &Arg2);

#if MSDOS && LATTICE
      {
      long i;
      i = IntVal(Arg1);
      int_rslt = i % IntVal(Arg2);
      }
#else					/* MSDOS && LATTICE */
       int_rslt = IntVal(Arg1) % IntVal(Arg2);
#endif					/* MSDOS && LATTICE */

      /*
       * The sign of the result must match that of n1.
       */
      if (IntVal(Arg1) < 0) {
         if (int_rslt > 0)
            int_rslt -= Abs(IntVal(Arg2));
         }
      else if (int_rslt < 0)
         int_rslt += Abs(IntVal(Arg2));
      MakeInt(int_rslt, &Arg0);
      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either x or y is real, convert the other to a real, get
       *  the modulus, convert the result to an integer and place it
       *  in Arg0 as the return value.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
	 if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

      real_rslt = r1 - r2 * (int)(r1 / r2);
      /*
       * The sign of the result must match that of n1.
       */
      if (r1 < 0.0) {
         if (real_rslt > 0.0)
            real_rslt -= fabs(r2);
         }
      else if (real_rslt < 0.0)
         real_rslt += fabs(r2);
      if (makereal(real_rslt, &Arg0) == Error) 
         RunErr(0, NULL);
      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigmod(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }


/*
 * x * y - multiply x and y.
 */

OpDcl(mult,2,"*")
   {
   register int t1, t2;
   double r1, r2;

   /*
    * Arg1 and Arg2 must be numeric.  Save the cvnum return values for later
    *  use.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail) 
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Both Arg1 and Arg2 are integers.  Perform the multiplication and
       *  and place the result in Arg0 as the return value.
       */

      MakeInt(mul(IntVal(Arg1), IntVal(Arg2)), &Arg0);
      if (over_flow)
#ifdef LargeInts
	 if (bigmul(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	    RunErr(0, NULL);
#else					/* LargeInts */
         RunErr(-203, NULL);
#endif					/* LargeInts */
      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either Arg1 or Arg2 is real, convert the other to a real, perform
       *  the subtraction and place the result in Arg0 as the return value.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
	 if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2  = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

      if (makereal(r1 * r2, &Arg0) == Error) 
         RunErr(0, NULL);
      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigmul(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }

/*
 * -x - negate x.
 */

OpDcl(neg,1,"-")
   {

   /*
    * Arg1 must be numeric.
    */
   switch (cvnum(&Arg1)) {

      case T_Integer:
         /*
          * If Arg1 is an integer, check for overflow by negating it and
          *  seeing if the negation didn't "work".  Use MakeInt to
          *  construct the return value.
          */

	 MakeInt(neg(IntVal(Arg1)), &Arg0);
         if (over_flow)

#ifdef LargeInts
	    if (bigneg(&Arg1, &Arg0) == Error)  /* alcbignum failed */
	       RunErr(0, NULL);
#else					/* LargeInts */
         RunErr(-203, &Arg1);
#endif					/* LargeInts */

         break;

#ifdef LargeInts
      case T_Bignum:
	 if (cpbignum(&Arg1, &Arg0) == Error)  /* alcbignum failed */
	    RunErr(0, NULL);
	 BlkLoc(Arg0)->bignumblk.sign ^= 1;
	 break;
#endif					/* LargeInts */

      case T_Real:
         /*
          * Arg1 is real, just negate it and use makereal to construct the
          *  return value.
          */

#ifdef RTACIS
         { 
         double rtbug_temporary;		/* bug with "-" as parameter */
         rtbug_temporary = -BlkLoc(Arg1)->realblk.realval;
         if (makereal(rtbug_temporary, &Arg0) == Error) 
            RunErr(0, NULL);
         }
#else					/* RTACIS */
         if (makereal(-BlkLoc(Arg1)->realblk.realval, &Arg0) == Error) 
            RunErr(0, NULL);
#endif					/* RTACIS */

         break;

      default:
         /*
          * Arg1 is not numeric.
          */
         RunErr(102, &Arg1);
      }
   Return;
   }

/*
 * +x - convert x to numeric type.
 *  Operational definition: generate runerr if x is not numeric.
 */

OpDcl(number,1,"+")
   {

   switch (cvnum(&Arg1)) {

      case T_Integer:

#ifdef LargeInts
      case T_Bignum:
#endif					/* LargeInts */

      case T_Real:
	 Arg0 = Arg1;
         break;

      default:
         RunErr(102, &Arg1);
      }
   Return;
   }

/*
 * x + y - add x and y.
 */

OpDcl(plus,2,"+")
   {
   register int t1, t2;
   double r1, r2;

   /*
    * Arg1 and Arg2 must be numeric.  Save the cvnum return values for later
    *  use.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail) 
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Both Arg1 and Arg2 are integers.  Perform integer addition and plcae
       *  the result in Arg0 as the return value.
       */

      MakeInt(add(IntVal(Arg1), IntVal(Arg2)), &Arg0);
      if (over_flow)

#ifdef LargeInts
	 if (bigadd(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	    RunErr(0, NULL);
#else					/* LargeInts */
         RunErr(-203, NULL);
#endif					/* LargeInts */

      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either Arg1 or Arg2 is real, convert the other to a real, perform
       *  the addition and place the result in Arg0 as the return value.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
	 if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

      if (makereal(r1 + r2, &Arg0) == Error) 
         RunErr(0, NULL);
      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigadd(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }

/*
 * x ^ y - raise x to the y power.
 */

#if AMIGA
#if AZTEC_C
#ifndef RTACIS
#define RTACIS
#define AZTECHACK
#endif					/* RTACIS */
#endif					/* AZTEC_C */
#endif					/* AMIGA */

OpDcl(powr,2,"^")
   {
   register int t1, t2;
   double r1, r2;

   /*
    * Arg1 and Arg2 must be numeric.  Save the cvnum return values for later
    *  use.
    */
   if ((t1 = cvnum(&Arg1)) == CvtFail) 
      RunErr(102, &Arg1);
   if ((t2 = cvnum(&Arg2)) == CvtFail) 
      RunErr(102, &Arg2);

   if (t1 == T_Integer && t2 == T_Integer) {
      /*
       * Both Arg1 and Arg2 are integers.  Perform integer exponentiation
       *  and place the result in Arg0 as the return value.
       */

#ifdef LargeInts
      if (bigpow(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
#else					/* LargeInts */
      MakeInt(ipow(IntVal(Arg1), IntVal(Arg2)), &Arg0);
      if (over_flow)
         RunErr(-203, NULL);
#endif					/* LargeInts */

      }
   else if (t1 == T_Real || t2 == T_Real) {
      /*
       * Either x or y is real, convert the other to a real, perform
       *  real exponentiation and place the result in Arg0 as the
       *  return value.
       */
      if (t1 != T_Real) {

#ifdef LargeInts
	 if (t1 == T_Bignum)
	    r1 = bigtoreal(&Arg1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(Arg1);
         }
      else
	 r1 = BlkLoc(Arg1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(&Arg2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(Arg2);
         }
      else
	 r2 = BlkLoc(Arg2)->realblk.realval;

      if (r1 == 0.0 && r2 <= 0.0) 
         /*
          * Tried to raise zero to a negative power.
          */
         RunErr(-204, NULL);
      if (r1 < 0.0 && t2 == T_Real) 
         /*
          * Tried to raise a negative number to a real power.
          */
         RunErr(-206, NULL);

#undef POWBUG
#ifdef RTACIS
#define POWBUG
#endif					/* RTACIS */
#ifndef POWBUG
#ifdef CRAY
#define POWBUG
#endif					/* CRAY */
#endif					/* POSBUG */

#ifdef POWBUG
      {
       double rtbug_temporary;		/* bug in pow routine for negative x */

       if ((r1 < 0.0) && /* integral? */ (((double)((long int)r2)) == r2)) {
          rtbug_temporary = -r1; 

          /*
           * The following is correct only if the exponent is odd.
           *  If the exponent is even, it should be
           *
           *      pow(-rtbug_temporary,r2);
           *
           */
          rtbug_temporary = -pow(rtbug_temporary, r2); 
          } 
       else
	  rtbug_temporary = pow(r1, r2);
       if (makereal(rtbug_temporary, &Arg0) == Error) 
          RunErr(0, NULL);
      }
#else					/* POWBUG */
      if (makereal(pow(r1, r2), &Arg0) == Error) 
         RunErr(0, NULL);
#endif					/* POWBUG */

      }

#ifdef LargeInts
   else {
      /*
       * Neither Arg1 or Arg2 are real and at least one is a large int.
       */
      if (bigpow(&Arg1, &Arg2, &Arg0) == Error)  /* alcbignum failed */
	 RunErr(0, NULL);
      }
#endif					/* LargeInts */

   Return;
   }

#if AMIGA
#if AZTEC_C
#ifdef AZTECHACK
#undef RTACIS
#endif					/* AZTECHACK */
#endif					/* AZTEC_C */
#endif					/* AMIGA */

#ifndef LargeInts
long ipow(n1, n2)
long n1, n2;
   {
   long result;

   if (n1 == 0 && n2 <= 0) {
      over_flow = 1;
      return 0;
      }
   if (n2 < 0)
      return 0;
   result = 1L;
   while (n2 > 0) {
      if (n2 & 01L)
         result *= n1;
      n1 *= n1;
      n2 >>= 1;
      }
   over_flow = 0;
   return result;
   }
#endif					/* LargeInts */
