|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - downloadIndex: ┃ T b ┃
Length: 28653 (0x6fed) Types: TextFile Names: »bignum.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─ ⟦this⟧ »EUUGD11/gnu-31mar87/scheme/microcode/bignum.c«
/* 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)