/*
 * File: rcomp.c
 *  Contents: anycmp, equiv, lexcmp, numcmp
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"

/*
 * anycmp - compare any two objects.
 */

int anycmp(dp1,dp2)
dptr dp1, dp2;
   {
   register int o1, o2;
   register long lresult;
   double rres1, rres2, rresult;

   /*
    * Get a collating number for dp1 and dp2.
    */
   o1 = order(dp1);
   o2 = order(dp2);

   /*
    * If dp1 and dp2 aren't of the same type, compare their collating numbers.
    */
   if (o1 != o2)
      return (o1 > o2 ? Greater : Less);

   if (o1 == 3)
      /*
       * dp1 and dp2 are strings, use lexcmp to compare them.
       */
      return lexcmp(dp1,dp2);

   switch (Type(*dp1)) {
      case T_Integer:
	 lresult = IntVal(*dp1) - IntVal(*dp2);
	 if (lresult == 0)
	    return Equal;
	 return ((lresult > 0) ? Greater : Less);

#ifdef LargeInts
      case T_Bignum:
	 lresult = bigcmp(dp1, dp2);
	 if (lresult == 0)
	    return Equal;
	 return ((lresult > 0) ? Greater : Less);
#endif					/* LargeInts */

      case T_Real:
         GetReal(dp1,rres1);
         GetReal(dp2,rres2);
         rresult = rres1 - rres2;
	 if (rresult == 0.0)
	    return Equal;
	 return ((rresult > 0.0) ? Greater : Less);

      case T_Null:
         return Equal;

      case T_Cset:
         return csetcmp((unsigned int *)((struct b_cset *)BlkLoc(*dp1))->bits,
            (unsigned int *)((struct b_cset *)BlkLoc(*dp2))->bits);

      case T_File:
      case T_Proc:
      case T_List:
      case T_Table:
      case T_Set:
      case T_Record:
      case T_Coexpr:
      case T_External:
	 /*
          * Collate these values according to the relative positions of
          *  their blocks in the heap.
	  */
         lresult = ((long)BlkLoc(*dp1) - (long)BlkLoc(*dp2));
         if (lresult == 0)
            return Equal;
         return ((lresult > 0) ? Greater : Less);

      default:
	 syserr("anycmp: unknown datatype.");
      }
   }

/*
 * order(x) - return collating number for object x.
 */

int order(dp)
dptr dp;
   {
   if (Qual(*dp))
      return 3; 	     /* string */
   switch (Type(*dp)) {
      case T_Null:
	 return 0;
      case T_Integer:
	 return 1;

#ifdef LargeInts
      case T_Bignum:
	 return 1;
#endif					/* LargeInts */

      case T_Real:
	 return 2;
      case T_Cset:
	 return 4;
      case T_Coexpr:
	 return 5;
      case T_File:
	 return 6;
      case T_Proc:
	 return 7;
      case T_List:
	 return 8;
      case T_Table:
	 return 9;
      case T_Set:
	 return 10;
      case T_Record:
	 return 11;
      case T_External:
         return 12;
      default:
	 syserr("order: unknown datatype.");
      }
   }

/*
 * equiv - test equivalence of two objects.
 */

int equiv(dp1, dp2)
dptr dp1, dp2;
   {
   register int result;
   register word i;
   register char *s1, *s2;
   double rres1, rres2;

   result = 0;

      /*
       * If the descriptors are identical, the objects are equivalent.
       */
   if (EqlDesc(*dp1,*dp2))
      result = 1;
   else if (Qual(*dp1) && Qual(*dp2)) {

      /*
       *  If both are strings of equal length, compare their characters.
       */

      if ((i = StrLen(*dp1)) == StrLen(*dp2)) {


	 s1 = StrLoc(*dp1);
	 s2 = StrLoc(*dp2);
	 result = 1;
	 while (i--)
	   if (*s1++ != *s2++) {
	      result = 0;
	      break;
	      }

	 }
      }
   else if (dp1->dword == dp2->dword)
      switch (Type(*dp1)) {
	 /*
	  * For integers and reals, just compare the values.
	  */
	 case T_Integer:
	    result = (IntVal(*dp1) == IntVal(*dp2));
	    break;

#ifdef LargeInts
	 case T_Bignum:
	    result = (bigcmp(dp1, dp2) == 0);
	    break;
#endif					/* LargeInts */


	 case T_Real:
            GetReal(dp1, rres1);
            GetReal(dp2, rres2);
            result = (rres1 == rres2);
	    break;

	 case T_Cset:
	    /*
	     * Compare the bit arrays of the csets.
	     */
	    result = 1;
	    for (i = 0; i < CsetSize; i++)
	       if (BlkLoc(*dp1)->cset.bits[i] != BlkLoc(*dp2)->cset.bits[i]) {
		  result = 0;
		  break;
		  }
	 }
   else
      /*
       * dp1 and dp2 are of different types, so they can't be
       *  equivalent.
       */
      result = 0;

   return result;
   }

/*
 * lexcmp - lexically compare two strings.
 */

int lexcmp(dp1, dp2)
dptr dp1, dp2;
   {


   register char *s1, *s2;
   register word minlen;
   word l1, l2;

   /*
    * Get length and starting address of both strings.
    */
   l1 = StrLen(*dp1);
   s1 = StrLoc(*dp1);
   l2 = StrLen(*dp2);
   s2 = StrLoc(*dp2);

   /*
    * Set minlen to length of the shorter string.
    */
   minlen = Min(l1, l2);

   /*
    * Compare as many bytes as are in the smaller string.  If an
    *  inequality is found, compare the differing bytes.
    */
   while (minlen--)
      if (*s1++ != *s2++)

         return (ToAscii(*--s1 & 0377) > ToAscii(*--s2 & 0377) ?
                 Greater : Less);
   /*
    * The strings compared equal for the length of the shorter.
    */
   if (l1 == l2)
      return Equal;
   else if (l1 > l2)
      return Greater;
   else
      return Less;

   }

/*
 * numcmp - compare two numbers.  Returns -1, 0, 1 for dp1 <, =, > dp2.
 *  dp3 is made into a descriptor for the return value.
 */

int numcmp(dp1, dp2, dp3)
dptr dp1, dp2, dp3;
   {
   int t1, t2;
   double r1, r2;
   /*
    * Be sure that both dp1 and dp2 are numeric.
    */

   if ((t1 = cvnum(dp1)) == CvtFail)
      RetError(102, *dp1);
   if ((t2 = cvnum(dp2)) == CvtFail)
      RetError(102, *dp2);

   if (t1 == T_Integer && t2 == T_Integer) {
   /*
    *  dp1 and dp2 are both integers, compare them and
    *  create an integer descriptor in dp3
    */

      *dp3 = *dp2;
      if (IntVal(*dp1) == IntVal(*dp2))
	 return Equal;
      return ((IntVal(*dp1) > IntVal(*dp2)) ? Greater : Less);
      }
   else if (t1 == T_Real || t2 == T_Real) {

   /*
    *  Either dp1 or dp2 is real. Convert the other to a real,
    *  compare them and create a real descriptor in dp3.
    */

      if (t1 != T_Real) {

#ifdef LargeInts
	 if (t1 == T_Bignum)
	    r1 = bigtoreal(dp1);
	 else
#endif					/* LargeInts */

            r1 = IntVal(*dp1);

         }
      else
	 r1 = BlkLoc(*dp1)->realblk.realval;

      if (t2 != T_Real) {

#ifdef LargeInts
	 if (t2 == T_Bignum)
	    r2 = bigtoreal(dp2);
	 else
#endif					/* LargeInts */

            r2 = IntVal(*dp2);
         }
      else
	 r2 = BlkLoc(*dp2)->realblk.realval;
	 
      if (makereal(r2, dp3) == Error)
         return Error;
      if (r1 == r2)
	 return Equal;
      return ((r1 > r2) ? Greater : Less);
      }
#ifdef LargeInts
   else {
      int result;

      *dp3 = *dp2;
      result = bigcmp(dp1, dp2);
      if (result == 0)
	 return Equal;
      return ((result > 0) ? Greater : Less);
      }
#endif					/* LargeInts */
   }

/*
 * csetcmp - compare two cset bit arrays.
 *  The order defined by this function is identical to the lexical order of
 *  the two strings that the csets would be converted into.
 */

int csetcmp(cs1, cs2)
unsigned int *cs1, *cs2;
   {
   unsigned int nbit, mask, *cs_end;

   if (cs1 == cs2) return Equal;

   /*
    * The longest common prefix of the two bit arrays converts to some
    *  common prefix string.  The first bit on which the csets disagree is
    *  the first character of the conversion strings that disagree, and so this
    *  is the character on which the order is determined.  The cset that has
    *  this first non-common bit = one, has in that position the lowest
    *  character, so this cset is lexically least iff the other cset has some
    *  following bit set.  If the other cset has no bits set after the first
    *  point of disagreement, then it is a prefix of the other, and is therefor
    *  lexically less.
    *
    * Find the first word where cs1 and cs2 are different.
    */
   for (cs_end = cs1 + CsetSize; cs1 < cs_end; cs1++, cs2++)
      if (*cs1 != *cs2) {
	 /*
	  * Let n be the position at which the bits first differ within
	  *  the word.  Set nbit to some integer for which the nth bit
	  *  is the first bit in the word that is one.  Note here and in the
	  *  following, that bits go from right to left within a word, so
	  *  the _first_ bit is the _rightmost_ bit.
	  */
	 nbit = *cs1 ^ *cs2;

	 /* Set mask to an integer that has all zeros in bit positions
	  *  upto and including position n, and all ones in bit positions
	  *  _after_ bit position n.
	  */
	 for (mask = (unsigned)MaxLong << 1; !(~mask & nbit); mask <<= 1);

	 /*
	  * nbit & ~mask contains zeros everywhere except position n, which
	  *  is a one, so *cs2 & (nbit & ~mask) is non-zero iff the nth bit
	  *  of *cs2 is one.
	  */
	 if (*cs2 & (nbit & ~mask)) {
	    /*
	     * If there are bits set in cs1 after bit position n in the
	     *  current word, then cs1 is lexically greater than cs2.
	     */
	    if (*cs1 & mask) return Greater;
	    while (++cs1 < cs_end)
	       if (*cs1) return Greater;

	    /*
	     * Otherwise cs1 is a proper prefix of cs2 and is therefore
	     *  lexically less.
	     */
	     return Less;
	     }

	 /*
	  * If the nth bit of *cs2 isn't one, then the nth bit of cs1
	  *  must be one.  Just reverse the logic for the previous
	  *  case.
	  */
	 if (*cs2 & mask) return Less;
	 cs_end = cs2 + (cs_end - cs1);
	 while (++cs2 < cs_end)
	    if (*cs2) return Less;
	 return Greater;
	 }
   return Equal;
   }
