DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T b

⟦597245039⟧ TextFile

    Length: 28653 (0x6fed)
    Types: TextFile
    Names: »bignum.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/gnu-31mar87/scheme/microcode/bignum.c« 

TextFile

/*          Hey EMACS, this is -*- C -*- code!                 */

/****************************************************************
*                                                               *
*                         Copyright (c) 1984                    *
*               Massachusetts Institute of Technology           *
*                                                               *
* This material was developed by the Scheme project at the      *
* Massachusetts Institute of Technology, Department of          *
* Electrical Engineering and Computer Science.  Permission to   *
* copy this software, to redistribute it, and to use it for any *
* purpose is granted, subject to the following restrictions and *
* understandings.                                               *
*                                                               *
* 1. Any copy made of this software must include this copyright *
* notice in full.                                               *
*                                                               *
* 2. Users of this software agree to make their best efforts (a)*
* to return to the MIT Scheme project any improvements or       *
* extensions that they make, so that these may be included in   *
* future releases; and (b) to inform MIT of noteworthy uses of  *
* this software.                                                *
*                                                               *
* 3.  All materials developed as a consequence of the use of    *
* this software shall duly acknowledge such use, in accordance  *
* with the usual standards of acknowledging credit in academic  *
* research.                                                     *
*                                                               *
* 4. MIT has made no warrantee or representation that the       *
* operation of this software will be error-free, and MIT is     *
* under no obligation to provide any services, by way of        *
* maintenance, update, or otherwise.                            *
*                                                               *
* 5.  In conjunction with products arising from the use of this *
* material, there shall be no use of the name of the            *
* Massachusetts Institute of Technology nor of any adaptation   *
* thereof in any advertising, promotional, or sales literature  *
* without prior written consent from MIT in each case.          *
*                                                               *
****************************************************************/
\f


/* File: BIGNUM.C
 *
 * This file contains the procedures for handling BIGNUM Arithmetic
 *
 */

#include "scheme.h"
#include <math.h>
#include "primitive.h"
#include "bignum.h"
#include "flonum.h"
#include "zones.h"
\f


/* Bignum Comparison Primitives */

/* big_compare() will return either of three cases, determining whether
 * ARG1 is bigger, smaller, or equal to ARG2.
 */

big_compare(ARG1, ARG2)
bigdigit *ARG1, *ARG2;
{ switch(Categorize_Sign(ARG1, ARG2))
  { case BOTH_NEGATIVE : return big_compare_unsigned(ARG2, ARG1);
    case BOTH_POSITIVE : return big_compare_unsigned(ARG1, ARG2);
    case ARG1_NEGATIVE : return TWO_BIGGER;
    case ARG2_NEGATIVE : return ONE_BIGGER;
    default: Sign_Error("big_compare()");
  }
}

/* big_compare_unsigned() compares the magnitudes of two BIGNUM's.
 * Called by big_compare() and minus_unsigned_bignum().
 */

big_compare_unsigned(ARG1, ARG2)
fast bigdigit *ARG1, *ARG2;
{ fast bigdigit *LIMIT;

  if ((LEN(ARG1)) > (LEN(ARG2))) return ONE_BIGGER;
  if ((LEN(ARG1)) < (LEN(ARG2))) return TWO_BIGGER;
  if ((LEN(ARG1)) == 0) return EQUAL;
  LIMIT = Bignum_Bottom(ARG1); 
  ARG1  = Bignum_Top(ARG1);
  ARG2  = Bignum_Top(ARG2);
  while (ARG1 >=  LIMIT)
  { if (*ARG1 > *ARG2) return ONE_BIGGER;
    if (*ARG1 < *ARG2) return TWO_BIGGER;
    ARG1 -= 1;
    ARG2 -= 1;
  }
  return EQUAL;
}
\f


/* Primitives for Coercion */

/* (FIX_TO_BIG FIXNUM)
      [Primitive number 0x67]
      Returns its argument if FIXNUM isn't a fixnum.  Otherwise 
      it returns the corresponding bignum.
*/
Built_In_Primitive(Prim_Fix_To_Big, 1, "FIX->BIG")
{ Primitive_1_Arg();
  Arg_1_Type(TC_FIXNUM);
  return Fix_To_Big(Arg1);
}

Pointer Fix_To_Big(Arg1)
Pointer Arg1;
{ fast bigdigit *Answer, *SCAN, *size;
  long Length, ARG1;
  if (Type_Code(Arg1) != TC_FIXNUM) Primitive_Error(ERR_ARG_1_WRONG_TYPE);
  if (Get_Integer(Arg1) == 0)
  { long Align_0 = Align(0);
    bigdigit *REG;
    Primitive_GC_If_Needed(Free+2);
    REG = BIGNUM(Free);
    Prepare_Header(REG, 0, POSITIVE);
    Free += Align_0;
    return Make_Pointer(TC_BIG_FIXNUM, Free-Align_0);
  }
  Length = Align(FIXNUM_LENGTH_AS_BIGNUM);
  Primitive_GC_If_Needed(Free + Length);
  Sign_Extend(Arg1, ARG1);
  Answer = BIGNUM(Free); 
  Prepare_Header(Answer, 0, (ARG1 >= 0) ? POSITIVE : NEGATIVE);
  size   = &LEN(Answer);
  if (ARG1 < 0) ARG1 = - ARG1;
  for (SCAN = Bignum_Bottom(Answer); ARG1 != 0; *size += 1)
  { *SCAN++ = Rem_Radix(ARG1);
    ARG1    = Div_Radix(ARG1);
  }
  Length = Align(*size);
  *((Pointer *) Answer) = Make_Header(Length);
  Free  += Length;
  Debug_Test(Free-Length);
  return Make_Pointer(TC_BIG_FIXNUM, Free-Length);
}
\f


/* (BIG_TO_FIX BIGNUM)
      [Primitive number 0x68]
      When given a bignum, returns the equivalent fixnum if there is
      one. If BIGNUM is out of range, or isn't a bignum, returns
      BIGNUM.
*/
Built_In_Primitive(Prim_Big_To_Fix, 1, "BIG->FIX")
{ Primitive_1_Arg();
  Arg_1_Type(TC_BIG_FIXNUM);
  return Big_To_Fix(Arg1);
}

Pointer Big_To_Fix(Arg1)
Pointer Arg1;
{ fast bigdigit *SCAN, *ARG1;
  fast long Answer, i;
  long Length;
  if (Type_Code(Arg1) != TC_BIG_FIXNUM) return Arg1;
  ARG1 = BIGNUM(Get_Pointer(Arg1));
  Length = LEN(ARG1);
  if (Length==0) Answer = 0;
  else if (Length > FIXNUM_LENGTH_AS_BIGNUM) return Arg1;
  else if (Length < FIXNUM_LENGTH_AS_BIGNUM)
    for (SCAN=Bignum_Top(ARG1), i=0, Answer=0; i< Length; i++)
      Answer = Mul_Radix(Answer) + *SCAN--;
  else
    /* Length == FIXNUM_LENGTH_AS_BIGNUM */
    for (SCAN=Bignum_Top(ARG1), i=0, Answer=0; i< Length; i++)
    { Answer = Mul_Radix(Answer) + *SCAN--;
      /* This takes care of signed arithmetic */
      if ((Answer < 0) || (!(Fixnum_Fits(Answer)))) return Arg1;
    }
  if NEG_BIGNUM(ARG1) Answer = - Answer;
  return Make_Non_Pointer(TC_FIXNUM, Answer);
}
\f


Pointer Big_To_Float(Arg1)
Pointer Arg1;
{ fast bigdigit *ARG1, *LIMIT;
  fast double F = 0.0;

  ARG1 = BIGNUM(Get_Pointer(Arg1));
  if (!Fits_Into_Flonum(ARG1)) return Arg1;
  Primitive_GC_If_Needed(Free+FLONUM_SIZE+1);
  LIMIT = Bignum_Bottom(ARG1);
  ARG1 = Bignum_Top(ARG1);
  while (ARG1 >= LIMIT)  F = (F * ((double) RADIX)) + ((double) *ARG1--);
  if (NEG_BIGNUM(BIGNUM(Get_Pointer(Arg1)))) F = -F;
  *Free = Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, FLONUM_SIZE);
  Get_Float(C_To_Scheme(Free)) = F;
  Free += FLONUM_SIZE+1;
  return Make_Pointer(TC_BIG_FLONUM, Free-(FLONUM_SIZE+1));
}

Fits_Into_Flonum(Bignum)
bigdigit *Bignum;
{ fast int k;
  quick bigdigit top_digit;
  k = (LEN(Bignum) - 1) * SHIFT;
  for (top_digit = *Bignum_Top(Bignum); top_digit != 0; k++)
       top_digit >>= 1;
/* If precision should not be lost,
  if (k <= FLONUM_MANTISSA_BITS) return true;
   Otherwise,
*/
  if (k <= MAX_FLONUM_EXPONENT) return true;
  return false;
}
\f


double frexp(), ldexp();

Pointer Float_To_Big(flonum)
double flonum;
{ fast double mantissa;
  fast bigdigit *Answer, size;
  int exponent;
  long Align_size;
  if (flonum == 0.0) return return_bignum_zero();
  mantissa = frexp(flonum, &exponent);
  if (flonum < 0) mantissa = -mantissa;
  if (mantissa >= 1.0)
  { mantissa = mantissa/2.0;
    exponent += 1;
  }
  size = (exponent + (SHIFT - 1)) / SHIFT;
  exponent = exponent % SHIFT;
  mantissa = ldexp(mantissa, (exponent == 0) ? 0: exponent - SHIFT);
  Align_size = Align(size);
  Primitive_GC_If_Needed(Free+Align_size);
  Answer = BIGNUM(Free);
  Prepare_Header(Answer, size, (flonum < 0) ? NEGATIVE : POSITIVE);
  Answer = Bignum_Top(Answer)+1;
  while ((size > 0) && (mantissa != 0))
  { mantissa = mantissa * ((double) RADIX);
    *--Answer = ((bigdigit) mantissa);
    mantissa = mantissa - ((double) *Answer);
    size -= 1;
  }
  while (size-- != 0) *--Answer = (bigdigit) 0;
  Free += Align_size;
  Debug_Test(Free-Align_size);
  return Make_Pointer(TC_BIG_FIXNUM, Free-Align_size);
}
\f


/* Addition */

plus_signed_bignum(ARG1, ARG2)
bigdigit *ARG1, *ARG2;
{ /* Special Case for answer being zero */
  if (ZERO_BIGNUM(ARG1) && ZERO_BIGNUM(ARG2))
     return return_bignum_zero();
  switch(Categorize_Sign(ARG1, ARG2))
  { case BOTH_POSITIVE : return(plus_unsigned_bignum(ARG1, ARG2, POSITIVE));
    case ARG1_NEGATIVE : return(minus_unsigned_bignum(ARG2, ARG1, POSITIVE));
    case ARG2_NEGATIVE : return(minus_unsigned_bignum(ARG1, ARG2, POSITIVE));
    case BOTH_NEGATIVE : return(plus_unsigned_bignum(ARG1, ARG2, NEGATIVE));
    default : Sign_Error("plus_bignum()");
  }
}

plus_unsigned_bignum(ARG1,ARG2,sign)
fast bigdigit *ARG1, *ARG2;
bigdigit sign;
{ fast unsigned bigdouble Sum;
  long Size;
  fast bigdigit *Answer;
  fast bigdigit *TOP2, *TOP1;

/* Swap ARG1 and ARG2 so that ARG1 is always longer */

  if (LEN(ARG1) < LEN(ARG2))
  { Answer = ARG1;
    ARG1  = ARG2;
    ARG2  = Answer;
  }

/* Allocate Storage and do GC if needed */

  Size = Align(LEN(ARG1) + 1);
  Primitive_GC_If_Needed(Free + Size);
  Answer = BIGNUM(Free);
  Prepare_Header(Answer, LEN(ARG1)+1, sign);

/* plus_unsigned_bignum continues on the next page */
\f


/* plus_unsigned_bignum, continued */

/* Prepare Scanning Pointers and delimiters */

  TOP1 = Bignum_Top(ARG1);
  TOP2 = Bignum_Top(ARG2);
  ARG1 = Bignum_Bottom(ARG1);
  ARG2 = Bignum_Bottom(ARG2);
  Answer = Bignum_Bottom(Answer);
  Sum  = 0;
/* Starts Looping */
  while (TOP2 >= ARG2)
  { Sum       = *ARG1++ + *ARG2++ + Get_Carry(Sum);
    *Answer++ = Get_Digit(Sum);
  }
/* Let remaining carry propagate */
  while ((TOP1 >= ARG1) && (Get_Carry(Sum) != 0))
  { Sum       = *ARG1++ + 1;
    *Answer++ = Get_Digit(Sum);
  }
/* Copy rest of ARG1 into Answer */
  while (TOP1 >= ARG1) *Answer++ = *ARG1++;
  *Answer = Get_Carry(Sum);
/* Trims Answer.  The trim function is not used because there is at
 * most one leading zero.
 */
  if (*Answer == 0)
  { Answer = BIGNUM(Free);
    LEN(Answer) -= 1;
    *((Pointer *) Answer) = Make_Header(Align(LEN(Answer)));
  }
  Free  += Size;
  return Make_Pointer(TC_BIG_FIXNUM, Free-Size);
}
\f


/* Subtraction */

minus_signed_bignum(ARG1, ARG2)
bigdigit *ARG1, *ARG2;
{ /* Special Case for answer being zero */
  if (ZERO_BIGNUM(ARG1) && ZERO_BIGNUM(ARG2))
     return return_bignum_zero();

/* Dispatches According to Sign of Args */

  switch(Categorize_Sign(ARG1, ARG2))
  { case BOTH_POSITIVE : return(minus_unsigned_bignum(ARG1, ARG2, POSITIVE));
    case ARG1_NEGATIVE : return(plus_unsigned_bignum(ARG1, ARG2, NEGATIVE));
    case ARG2_NEGATIVE : return(plus_unsigned_bignum(ARG1, ARG2, POSITIVE));
    case BOTH_NEGATIVE : return(minus_unsigned_bignum(ARG2, ARG1, POSITIVE));
    default : Sign_Error("minus_bignum()");
  }
}

minus_unsigned_bignum(ARG1, ARG2, sign)
fast bigdigit *ARG1, *ARG2;
bigdigit sign;
{ fast bigdouble Diff;
  fast bigdigit *Answer, *TOP2, *TOP1;
  long Size;

  if (big_compare_unsigned(ARG1, ARG2) == TWO_BIGGER)
  { Answer = ARG1;
    ARG1  = ARG2;
    ARG2  = Answer;
    sign  = !sign;
  }

  Size   = Align(LEN(ARG1));
  Primitive_GC_If_Needed(Free + Size);
  Answer = BIGNUM(Free);
  Prepare_Header(Answer, LEN(ARG1), sign);

/* minus_unsigned_bignum continues on the next page */
\f


/* minus_unsigned_bignum, continued */

  TOP1 = Bignum_Top(ARG1);
  TOP2 = Bignum_Top(ARG2);
  ARG1  = Bignum_Bottom(ARG1);
  ARG2  = Bignum_Bottom(ARG2);
  Answer = Bignum_Bottom(Answer);
  Diff = RADIX;

/* Main Loops for minus_unsigned_bignum */

  while (TOP2 >= ARG2)
  { Diff      =  *ARG1++ + (MAX_DIGIT_SIZE - *ARG2++) + Get_Carry(Diff);
    *Answer++ = Get_Digit(Diff);
  }
  while ((TOP1 >= ARG1) && (Get_Carry(Diff) == 0))
  { Diff      = *ARG1++ + MAX_DIGIT_SIZE;
    *Answer++ = Get_Digit(Diff);
  }
  while (TOP1 >= ARG1) *Answer++ = *ARG1++;
  trim_bignum(Free);
  Free  += Size;
  return Make_Pointer(TC_BIG_FIXNUM, Free-Size);
}
\f


/* Multiplication */

multiply_signed_bignum(ARG1, ARG2)
bigdigit *ARG1, *ARG2;
{ if (ZERO_BIGNUM(ARG1) || ZERO_BIGNUM(ARG2))
     return return_bignum_zero();

  switch(Categorize_Sign(ARG1,ARG2))
  { case BOTH_POSITIVE :
    case BOTH_NEGATIVE :
      return multiply_unsigned_bignum(ARG1, ARG2, POSITIVE);
    case ARG1_NEGATIVE :
    case ARG2_NEGATIVE :
      return multiply_unsigned_bignum(ARG1, ARG2, NEGATIVE);
    default : Sign_Error("multiply_bignum()");
  }
}

multiply_unsigned_bignum(ARG1, ARG2, sign)
fast bigdigit *ARG1, *ARG2;
bigdigit sign;
{ bigdigit *TOP1, *TOP2;
  fast bigdigit *Answer;
  fast bigdouble Prod;
  fast int size;
  long Size;

  Prod   = LEN(ARG1) + LEN(ARG2);
  Size   = Align(Prod);
  Primitive_GC_If_Needed(Free + Size);
  Answer = BIGNUM(Free);
  Prepare_Header(Answer, Prod, sign);
  TOP1 = Bignum_Top(Answer);
  TOP2 = Bignum_Bottom(Answer);
  while (TOP1 >= TOP2) *TOP2++ = 0;

/* multiply_unsigned_bignum continues */
\f


/* Main Loops for MULTIPLY */

  size   = LEN(ARG2);
  Answer = Bignum_Bottom(Answer) +  size;
  TOP1   = Bignum_Top(ARG1);
  TOP2   = Bignum_Top(ARG2);
  ARG2   = TOP2;

  for (ARG1 = Bignum_Bottom(ARG1); TOP1 >= ARG1; ARG1++, Answer++)
  { if (*ARG1 != 0)
    { Prod = 0;
      Answer -= size;
      for (ARG2 = TOP2 - size + 1; TOP2 >= ARG2; ++ARG2)
      { Prod = *ARG1 * *ARG2 + *Answer + Get_Carry(Prod);
        *Answer++  = Get_Digit(Prod);
      }
      *Answer = Get_Carry(Prod);
    }
  }

/* Trims Answer */
  Answer = BIGNUM(Free);
  if (*(Bignum_Top(Answer)) == 0)
  { LEN(Answer) -= 1;
    *((Pointer *) Answer) = Make_Header(Align(LEN(Answer)));
  }
  Free  += Size;
  return Make_Pointer(TC_BIG_FIXNUM, Free-Size);
}
\f


/* (DIVIDE-BIGNUM ONE-BIGNUM ANOTHER_BIGNUM)
 * [Primitive number 0x4F]
 * returns a cons of the bignum quotient and remainder of both arguments.
 */

Built_In_Primitive(Prim_Divide_Bignum, 2, "DIVIDE-BIGNUM")
{ Pointer Result, *End_Of_First, *First, *Second, *Orig_Free=Free;
  Primitive_2_Args();
  Arg_1_Type(TC_BIG_FIXNUM);
  Arg_2_Type(TC_BIG_FIXNUM);
  Set_Time_Zone(Zone_Math);
  Result = div_signed_bignum(BIGNUM(Get_Pointer(Arg1)),
                             BIGNUM(Get_Pointer(Arg2)));
  if (Bignum_Debug)
    printf("\nResult=0x%x [%x %x]\n",
           Result, Fast_Vector_Ref(Result, 0), Fast_Vector_Ref(Result, 1));
  First = Get_Pointer(Fast_Vector_Ref(Result, CONS_CAR));
  Second = Get_Pointer(Fast_Vector_Ref(Result, CONS_CDR));
  if (Bignum_Debug)
    printf("\nFirst=0x%x [%x %x]; Second=0x%x [%x %x]\n",
           First, First[0], First[1], Second, Second[0], Second[1]);
  if (Consistency_Check)
  { if (First > Second)
    { printf("\nBignum_Divide: results swapped.\n");
      Microcode_Termination(TERM_EXIT);
    }
    else if (First != Orig_Free+2)
    { printf("\nBignum Divide: hole at start\n");
      Microcode_Termination(TERM_EXIT);
    }
  }
  End_Of_First = First+1+Get_Integer(First[0]);
  if (Bignum_Debug) printf("\nEnd_Of_First=0x%x\n", End_Of_First);
  if (End_Of_First != Second)
  { *End_Of_First =
      Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, (Second-End_Of_First)-1);
    if (Bignum_Debug) printf("\nGap=0x%x\n", (Second-End_Of_First)-1);
  }
  Free = Second+1+Get_Integer(Second[0]);
  if (Bignum_Debug) printf("\nEnd=0x%x\n", Free);
  return Result;
}
\f


/* div_signed_bignum() differentiates between all the possible
 * cases and allocates storage for the quotient, remainder, and
 * any intrmediate storage needed.
 */

div_signed_bignum(ARG1, ARG2)
bigdigit *ARG1, *ARG2;
{ bigdigit *SARG2;
  bigdigit *QUOT, *REMD;
  Pointer *Cons_Cell;

  if ZERO_BIGNUM(ARG2) Primitive_Error(ERR_ARG_2_BAD_RANGE);
  Primitive_GC_If_Needed(Free + 2);
  Cons_Cell = Free;
  Free += 2;

  if (big_compare_unsigned(ARG1, ARG2) == TWO_BIGGER)
/* Trivial Solution for ARG1 > ARG2 
 * Quotient is zero and the remainder is just a copy of Arg_1.
 */
  { Primitive_GC_If_Needed(Align(0)+Align(LEN(ARG1))+Free);
    QUOT = BIGNUM(Free);
    Free += Align(0);
    Prepare_Header(QUOT, 0, POSITIVE);
    REMD = BIGNUM(Free);
    Free += Align(LEN(ARG1));
    copy_bignum(ARG1, REMD);
  }
  else if (LEN(ARG2)==1)
  /* Divisor is only one digit long.
   * unscale() is used to divide out Arg_1 and the remainder is the
   * single digit returned by unscale(), coerced to a bignum.
   */
  { Primitive_GC_If_Needed(Free+Align(LEN(ARG1))+Align(1));
    QUOT = BIGNUM(Free);
    Free += Align(LEN(ARG1));
    REMD = BIGNUM(Free);
    Free += Align(1);
    Prepare_Header(QUOT, LEN(ARG1), POSITIVE);
    Prepare_Header(REMD, 1, POSITIVE);
    *(Bignum_Bottom(REMD)) =
      unscale(ARG1, QUOT, *(Bignum_Bottom(ARG2)));
    trim_bignum(REMD);
    trim_bignum(QUOT);
  }
  else
\f


/* Usual case. div_internal() is called.  A normalized copy of Arg_1
 * resides in REMD, which ultimately becomes the remainder.  The
 * normalized copy of Arg_2 is in SARG2.
 */
  { bigdouble temp;
    temp = (Align(LEN(ARG1)-LEN(ARG2)+1) + Align(LEN(ARG1)+1)
	    + Align(LEN(ARG2)+1));
    Primitive_GC_If_Needed(Free + temp);
    QUOT = BIGNUM(Free);
    *Free = Make_Header(Align(LEN(ARG1)-LEN(ARG2)+1));
    Free += Align(LEN(ARG1)-LEN(ARG2)+1);
    REMD = BIGNUM(Free);
    *Free = Make_Header(Align(LEN(ARG1)+1));
    Free += Align(LEN(ARG1)+1);
    SARG2 = BIGNUM(Free);
    *Free = Make_Header(Align(LEN(ARG2)+1));
    Free += Align(LEN(ARG2)+1);

    temp = RADIX / (1 + *(Bignum_Top(ARG2)));
    scale(ARG1, REMD, temp);
    scale(ARG2, SARG2, temp);
    div_internal(REMD, SARG2, QUOT);
    unscale(REMD, REMD, temp);
    trim_bignum(REMD);
    trim_bignum(QUOT);
  }
\f


/* Determines sign of the quotient and remainder */

  SIGN(REMD) = POSITIVE;
  SIGN(QUOT) = POSITIVE;
  switch(Categorize_Sign(ARG1,ARG2))
  { case ARG2_NEGATIVE :
      SIGN(QUOT) = NEGATIVE;
      break;
    case ARG1_NEGATIVE :
      SIGN(QUOT) = NEGATIVE;
    case BOTH_NEGATIVE :
      SIGN(REMD) = NEGATIVE;
      break;
    case BOTH_POSITIVE : break;
    default : Sign_Error("divide_bignum()");
  } /* Glue the two results in a list and return as answer */
  Cons_Cell[CONS_CAR] = Make_Pointer(TC_BIG_FIXNUM, QUOT);
  Cons_Cell[CONS_CDR] = Make_Pointer(TC_BIG_FIXNUM, REMD);
  return Make_Pointer(TC_LIST, Cons_Cell);
}
\f


/* Utility for debugging */

print_digits(name, num, how_many)
char *name;
bigdigit *num;
int how_many;
{ int NDigits = LEN(num);
  int limit;
  printf("\n%s = 0x%08x", name, num);
  printf("\n  Sign: %c, Vector length: %d, # Digits: %d",
         ((SIGN(num) == NEGATIVE) ? '-' :
	  ((SIGN(num) == POSITIVE) ? '+' : '?')),
	 Datum(((Pointer *) num)[VECTOR_LENGTH]),
	 NDigits);
  if (how_many == -1) limit = NDigits;
  else limit = ((how_many < NDigits) ? how_many : NDigits);
  num = Bignum_Bottom(num);
  while (--how_many >= 0) printf("\n    0x%04x", *num++);
  if (limit < NDigits) printf("\n    ...");
  printf("\n");
  return;
}
\f


/* This is the guts of the division algorithm. The storage
 * allocation and other hairy prep work is done in the superior
 * routines. ARG1 and ARG2 are fresh copies, ARG1 will 
 * ultimately become the Remainder.  Storage already 
 * allocated for all four parameters.
 */

static Pointer BIG_A[TEMP_SIZE], BIG_B[TEMP_SIZE];

div_internal(ARG1, ARG2, Quotient)
bigdigit *ARG1, *ARG2, *Quotient;
{ fast bigdigit *SCAN,*PROD;
  fast bigdouble Digit, Prod;
  fast bigdouble guess, dvsr2, dvsr1;
  fast bigdigit *LIMIT, *QUOT_SCAN;
  bigdigit *Big_A = BIGNUM(BIG_A);
  bigdigit *Big_B = BIGNUM(BIG_B);

  SCAN = Bignum_Top(ARG2);
  if (*SCAN == 0)
  { LEN(ARG2) -= 1;
    SCAN -= 1;
  }
  dvsr1 = *SCAN--;
  dvsr2 = *SCAN;

  Prepare_Header(Quotient, (LEN(ARG1)-LEN(ARG2)), POSITIVE);

  QUOT_SCAN = Bignum_Top(Quotient);
  ARG1      = Bignum_Top(ARG1);
  SCAN      = ARG1 - LEN(ARG2);
  Quotient  = Bignum_Bottom(Quotient);

/* div_internal() continues */
\f


/* Main Loop for div_internal() */

  while (QUOT_SCAN >= Quotient)
   { if (dvsr1 <= *ARG1) guess = RADIX - 1;
     else
     { /* This should be
	* guess = (Mul_Radix(*ARG1) + *(ARG1 - 1)) / dvsr1;
	* but because of overflow problems ...
	*/

       Prepare_Header(Big_A, 2, POSITIVE);
       *Bignum_Top(Big_A) = *ARG1;
       *Bignum_Bottom(Big_A) = *(ARG1-1);
       unscale(Big_A, Big_A, dvsr1);
       guess = *Bignum_Bottom(Big_A);
     }
     guess += 1; /* To counter first decrementing below. */
     do
     { guess -= 1;
       Prepare_Header(Big_A, 3, POSITIVE);
       LIMIT = Bignum_Top(Big_A);
       *LIMIT-- = *ARG1;
       *LIMIT-- = *(ARG1-1);
       *LIMIT   = *(ARG1-2);
       Prepare_Header(Big_B, 2, POSITIVE);
       *Bignum_Top(Big_B)    = dvsr1;
       *Bignum_Bottom(Big_B) = dvsr2;
       scale(Big_B, Big_B, guess);
       if ((*Bignum_Top(Big_B)) == 0) LEN(Big_B) -= 1;
     } while (big_compare_unsigned(Big_B, Big_A) == ONE_BIGGER);

/* div_internal() continues */
\f


/* div_internal() continued */

     LIMIT = Bignum_Top(ARG2);
     PROD  = Bignum_Bottom(ARG2);
     Digit = RADIX + *SCAN;
     while (LIMIT >= PROD)
      { Prod    = *PROD++ * guess;
        Digit   = Digit - Get_Digit(Prod);
        *SCAN++ = Get_Digit(Digit);
        Digit   = ((*SCAN - Get_Carry(Prod)) +
		   (MAX_DIGIT_SIZE +
		    ((Digit < 0) ? -1 : Get_Carry(Digit))));
      }
     *SCAN++ = Get_Digit(Digit);

     if (Get_Carry(Digit) == 0)
     /* Guess is one too big, add back. */
     { Digit = 0;
       guess -= 1;
       LIMIT = Bignum_Top(ARG2);
       SCAN  = SCAN - LEN(ARG2);
       PROD  = Bignum_Bottom(ARG2);
       while (LIMIT >= PROD)
       { Digit   = *SCAN + *PROD++ + Get_Carry(Digit);
         *SCAN++ = Get_Digit(Digit);
       }
       *SCAN = 0;
     }
     *QUOT_SCAN-- = guess;
     ARG1 -= 1;
     SCAN = ARG1 - LEN(ARG2);
   }
}
\f



/* (LISTIFY_BIGNUM BIGNUM RADIX)
      [Primitive number 0x50]
      Returns a list of numbers, in the range 0 through RADIX-1, which
      represent the BIGNUM in that radix.
*/
Built_In_Primitive(Prim_Listify_Bignum, 2, "LISTIFY-BIGNUM")
{ fast bigdigit *TOP1, *size;
  quick Pointer *RFree;
  fast bigdigit *ARG1;
  fast long pradix;
  Primitive_2_Args();

  Arg_1_Type(TC_BIG_FIXNUM);
  Arg_2_Type(TC_FIXNUM);
  Set_Time_Zone(Zone_Math);

  ARG1 = BIGNUM(Get_Pointer(Arg1));
  size = &LEN(ARG1);  
  if (*size == 0)
  { Primitive_GC_If_Needed(Free + 2);
    *Free++ = FIXNUM_0;
    *Free++ = NIL;
    return Make_Pointer(TC_LIST, Free-2);
  }
  Sign_Extend(Arg2, pradix);
  Primitive_GC_If_Needed(Free+Find_Length(pradix, *size)+Align(*size));
  ARG1  = BIGNUM(Free);
  copy_bignum(BIGNUM(Get_Pointer(Arg1)), ARG1);
  Free += Align(*size);
  RFree = Free;
  size = &LEN(ARG1);
  TOP1 = Bignum_Top(ARG1);
  while (*size > 0)
  { *RFree++ = FIXNUM_0+unscale(ARG1, ARG1, pradix);
    *RFree = Make_Pointer(TC_LIST, RFree-3); 
    RFree += 1; 
    if (*TOP1 == 0) 
    { *size -= 1;
      TOP1--;
    }
  }
  Free[CONS_CDR] = NIL;
  Free = RFree;
  return Make_Pointer(TC_LIST, RFree-2);
}
\f


/* General Purpose Utilities */

return_bignum_zero()
{ bigdigit *REG;
  long Align_0 = Align(0);
  Primitive_GC_If_Needed(Free+Align_0);
  REG = BIGNUM(Free);
  Prepare_Header(REG, 0, POSITIVE);
  Free += Align_0;
  return Make_Pointer(TC_BIG_FIXNUM, Free-Align_0);
}

trim_bignum(ARG)
bigdigit *ARG;
{ fast bigdigit *SCAN;
  fast bigdigit size;
  bigdigit sign;

  sign = SIGN(ARG);
  size = LEN(ARG);

  for (SCAN=Bignum_Top(ARG); ((size!=0)&&(*SCAN==0)); SCAN--)
    size -= 1;

  if (size == 0) sign = POSITIVE;
  Prepare_Header(ARG, size, sign);
}

copy_bignum(SOURCE, TARGET)
fast bigdigit *SOURCE, *TARGET;
{ fast bigdigit *LIMIT = Bignum_Top(SOURCE);
  while (LIMIT >= SOURCE) *TARGET++ = *SOURCE++;
}

Find_Length(pradix,length)
fast long pradix;
bigdigit length;
{ fast int log_pradix = 0;
  while (pradix != 1)
  { pradix = pradix >> 1;
   log_pradix += 1;
  }
  return(((SHIFT / log_pradix) + 1) * length);
}
\f


/* scale() and unscale() used by Division and Listify */

scale(SOURCE, DEST, how_much)
fast bigdigit *SOURCE, *DEST;
fast long how_much;
{ fast unsigned bigdouble prod = 0;
  bigdigit *LIMIT;

  if (how_much == 1)
  { if (SOURCE != DEST) copy_bignum(SOURCE, DEST);
    Prepare_Header(DEST, LEN(SOURCE)+1, SIGN(SOURCE));
    *Bignum_Top(DEST) = 0;
    return;
  }
  /* This must happen before the Prepare_Header if DEST = SOURCE */
  LIMIT = Bignum_Top(SOURCE);
  Prepare_Header(DEST, LEN(SOURCE)+1, SIGN(SOURCE));
  SOURCE = Bignum_Bottom(SOURCE);
  DEST = Bignum_Bottom(DEST);
  while (LIMIT >= SOURCE)
  { prod    = *SOURCE++ * how_much + Get_Carry(prod);
    *DEST++ = Get_Digit(prod);
  }
  *DEST = Get_Carry(prod);
}

unscale(SOURCE, DEST, how_much)
bigdigit *SOURCE;
fast bigdigit *DEST;
fast long how_much;
{ bigdigit carry = 0;
  fast unsigned bigdouble digits;
  fast bigdigit *SCAN;

  if (how_much == 1)
  { if (SOURCE != DEST) copy_bignum(SOURCE, DEST);
    return 0;
  }
  Prepare_Header(DEST, LEN(SOURCE), SIGN(DEST));
  SCAN   = Bignum_Top(SOURCE);
  DEST   = Bignum_Top(DEST);
  SOURCE = Bignum_Bottom(SOURCE);
  while (SCAN >= SOURCE)
  { digits = Mul_Radix(carry) + *SCAN--;
    *DEST  = digits / how_much;
    carry  = digits - (*DEST-- * how_much);
  }
  return carry;   /* returns remainder */
}
\f


/* Top level bignum primitives */

/* All the binary bignum primtives take two arguments and return NIL
   if either of them is not a bignum.  If both arguments are bignums,
   the perform the operation and return the answer.
*/

#define Binary_Primitive(C_Name, S_Name, Op)				\
Built_In_Primitive(C_Name, 2, S_Name)					\
{ Pointer Result, *Orig_Free=Free;					\
  Primitive_2_Args();							\
  Arg_1_Type(TC_BIG_FIXNUM);                                            \
  Arg_2_Type(TC_BIG_FIXNUM);                                            \
  Set_Time_Zone(Zone_Math);						\
  Result = Op(BIGNUM(Get_Pointer(Arg1)), BIGNUM(Get_Pointer(Arg2)));	\
  if (Consistency_Check && (Get_Pointer(Result) != Orig_Free))		\
  { printf("\nBignum operation result at 0x%x, Free was 0x%x\n",	\
           Address(Result), Free);					\
    Microcode_Termination(TERM_EXIT);					\
  }									\
  Free = Nth_Vector_Loc(Result, Vector_Length(Result)+1);		\
  if (Consistency_Check && (Free > Heap_Top))		\
  { printf("\nBignum operation result at 0x%x, length 0x%x\n",		\
           Address(Result), Vector_Length(Result));			\
    Microcode_Termination(TERM_EXIT);					\
  }									\
  return Result;							\
}

Binary_Primitive(Prim_Plus_Bignum, "PLUS-BIGNUM", plus_signed_bignum);
Binary_Primitive(Prim_Minus_Bignum, "MINUS-BIGNUM", minus_signed_bignum);
Binary_Primitive(Prim_Multiply_Bignum,
		 "TIMES-BIGNUM",
		 multiply_signed_bignum);
\f


/* All the unary bignum predicates take one argument and return NIL if
   it is not a bignum.  Otherwise, they return a fixnum 1 if the
   predicate is true or a fixnum 0 if it is false.  This convention of
   NIL/0/1 is used for all numeric predicates so that the generic
   dispatch can detect "inapplicable" as distinct from "false" answer.
*/

#define Unary_Predicate(C_Name, S_Name, Test)				\
Built_In_Primitive(C_Name, 1, S_Name)					\
{ bigdigit *ARG;							\
  Primitive_1_Arg();							\
  Arg_1_Type(TC_BIG_FIXNUM);                                            \
  Set_Time_Zone(Zone_Math);						\
  ARG = BIGNUM(Get_Pointer(Arg1));					\
  return FIXNUM_0 + ((Test) ? 1 : 0);					\
}

Unary_Predicate(Prim_Zero_Bignum, "ZERO-BIGNUM?", LEN(ARG)==0)
Unary_Predicate(Prim_Positive_Bignum,
		"POSITIVE-BIGNUM?",
                (LEN(ARG) != 0) && POS_BIGNUM(ARG))
Unary_Predicate(Prim_Negative_Bignum,
		"NEGATIVE-BIGNUM?",
                (LEN(ARG) != 0) && NEG_BIGNUM(ARG))

/* All the binary bignum predicates take two arguments and return NIL
   if either of them is not a bignum.  Otherwise, they return an
   answer as described above for the unary predicates.
*/

#define Binary_Predicate(C_Name, S_Name, Code)				\
Built_In_Primitive(C_Name, 2, S_Name)					\
{ Primitive_2_Args();							\
  Arg_1_Type(TC_BIG_FIXNUM);                                           \
  Arg_2_Type(TC_BIG_FIXNUM);                                           \
  Set_Time_Zone(Zone_Math);						\
  return FIXNUM_0 +							\
  	  ((big_compare(BIGNUM(Get_Pointer(Arg1)),			\
	                BIGNUM(Get_Pointer(Arg2))) == Code) ? 1 : 0);	\
}

Binary_Predicate(Prim_Equal_Bignum, "EQUAL-BIGNUM?", EQUAL)
Binary_Predicate(Prim_Greater_Bignum, "GREATER-BIGNUM?", ONE_BIGGER)
Binary_Predicate(Prim_Less_Bignum, "LESS-BIGNUM?", TWO_BIGGER)