/* * bigint.c -- big integer library */ #include #include #include "bigint.h" /**************************************************************/ /* debugging */ #define DIV_CHK_01 0 #define DIV_CHK_02 0 #define DIV_CHK_03 0 #define DIV_CHK_04 0 #define DIV_CHK_05 0 #define DIV_CHK_06 0 #define DIV_CHK_07 0 #define DIV_CHK_08 0 #define DIV_CHK_09 0 #define DIV_CHK_10 0 #define DIV_CHK_11 0 /**************************************************************/ /* big integer representation */ #define BIG_NEGATIVE ((unsigned char) 0) #define BIG_POSITIVE ((unsigned char) 1) #define BIG_PTR(bigObjRef) ((Big *) (getPrimObjectDataPointer(bigObjRef))) #define GET_ND(bigObjRef) (BIG_PTR(bigObjRef)->nd) #define SET_ND(bigObjRef, val) (BIG_PTR(bigObjRef)->nd = (val)) #define GET_SIGN(bigObjRef) (BIG_PTR(bigObjRef)->sign) #define SET_SIGN(bigObjRef, val) (BIG_PTR(bigObjRef)->sign = (val)) #define GET_DIGIT(bigObjRef, i) (BIG_PTR(bigObjRef)->digits[i]) #define SET_DIGIT(bigObjRef, i, val) (BIG_PTR(bigObjRef)->digits[i] = (val)) /**************************************************************/ /* global data */ /* * registers of the big integer processor */ BIP bip = { NULL, /* op1 */ NULL, /* op2 */ NULL, /* res */ NULL, /* rem */ }; /**************************************************************/ /* * construct a new big integer object * * number of digits is given by parameter * a reference to a proper object is returned * but no component of the big integer is set * * ATTENTION: All object references stored in * places other than the bip registers may become * invalid as soon as this function is called! */ static BigObjRef newBig(int nd) { int dataSize; BigObjRef bigObjRef; dataSize = sizeof(int) + 1 + nd; bigObjRef = newPrimObject(dataSize); return bigObjRef; } /**************************************************************/ /* big integer unsigned arithmetic */ /* * exchange bip.op1 and bip.op2 */ static void bigXchg(void) { BigObjRef tmp; tmp = bip.op1; bip.op1 = bip.op2; bip.op2 = tmp; } /* * big integer unsigned comparison * * operands in bip.op1 and bip.op2 * result is < 0, = 0, or > 0 if and only if the * same relation holds for bip.op1 and bip.op2 */ static int bigUcmp(void) { int nd1; int nd2; int diff; /* compare sizes */ nd1 = GET_ND(bip.op1); nd2 = GET_ND(bip.op2); if (nd1 != nd2) { /* sizes are different: we know the bigger number */ return nd1 - nd2; } /* sizes are equal: we must look at the digits */ while (nd1--) { diff = (int) GET_DIGIT(bip.op1, nd1) - (int) GET_DIGIT(bip.op2, nd1); if (diff != 0) { return diff; } } /* the numbers are equal */ return 0; } /* * big integer unsigned addition * * operands in bip.op1 and bip.op2 * result in bip.res */ static void bigUadd(void) { int nd1; int nd2; int i; unsigned short carry; unsigned short aux; int xchgFlag; /* make sure op1 has at least as many digits as op2 */ nd1 = GET_ND(bip.op1); nd2 = GET_ND(bip.op2); if (nd1 < nd2) { /* exchange operands */ bigXchg(); i = nd1; nd1 = nd2; nd2 = i; xchgFlag = 1; } else { /* don't exchange operands */ xchgFlag = 0; } /* allocate result */ bip.res = newBig(nd1 + 1); /* copy op2 to result */ for (i = 0; i < nd2; i++) { SET_DIGIT(bip.res, i, GET_DIGIT(bip.op2, i)); } /* fill result with 0 up to size of op1 */ for (; i < nd1; i++) { SET_DIGIT(bip.res, i, 0); } /* res = op1 + res */ carry = 0x00; for (i = 0; i < nd1; i++) { aux = (unsigned short) GET_DIGIT(bip.op1, i) + (unsigned short) GET_DIGIT(bip.res, i) + carry; SET_DIGIT(bip.res, i, aux & 0xFF); carry = aux >> 8; } SET_DIGIT(bip.res, i, carry); /* determine actual size of result */ i = nd1 + 1; while (--i >= 0 && GET_DIGIT(bip.res, i) == 0) ; SET_ND(bip.res, i + 1); /* restore operands */ if (xchgFlag) { bigXchg(); } } /* * big integer unsigned subtraction * * operands in bip.op1 and bip.op2 * result in bip.res, must not be negative */ static void bigUsub(void) { int nd1; int nd2; int i; unsigned short carry; unsigned short aux; /* op1 must have at least as many digits as op2 */ nd1 = GET_ND(bip.op1); nd2 = GET_ND(bip.op2); if (nd1 < nd2) { /* unsigned subtraction would yield negative result */ fatalError("internal library error #1 - THIS SHOULD NEVER HAPPEN!"); } /* allocate result */ bip.res = newBig(nd1); /* copy op2 to result */ for (i = 0; i < nd2; i++) { SET_DIGIT(bip.res, i, GET_DIGIT(bip.op2, i)); } /* fill result with 0 up to size of op1 */ for (; i < nd1; i++) { SET_DIGIT(bip.res, i, 0); } /* res = op1 - res */ carry = 0x01; for (i = 0; i < nd1; i++) { aux = (unsigned short) GET_DIGIT(bip.op1, i) - (unsigned short) GET_DIGIT(bip.res, i) + carry + 0xFF; SET_DIGIT(bip.res, i, aux & 0xFF); carry = aux >> 8; } if (carry != 0x01) { /* unsigned subtraction would yield negative result */ fatalError("internal library error #2 - THIS SHOULD NEVER HAPPEN!"); } /* determine actual size of result */ i = nd1; while (--i >= 0 && GET_DIGIT(bip.res, i) == 0) ; SET_ND(bip.res, i + 1); } /* * big integer unsigned multiplication * * operands in bip.op1 and bip.op2 * result in bip.res */ static void bigUmul(void) { int nd1; int nd2; int i, j, k; unsigned short carry; unsigned short aux; /* get sizes of operands */ nd1 = GET_ND(bip.op1); nd2 = GET_ND(bip.op2); /* allocate result */ bip.res = newBig(nd1 + nd2); /* reset lower nd1 digits of result */ for (i = 0; i < nd1; i++) { SET_DIGIT(bip.res, i, 0); } /* res = op1 * op2 */ for (j = 0; j < nd2; j++) { carry = 0x00; for (k = j, i = 0; i < nd1; k++, i++) { aux = (unsigned short) GET_DIGIT(bip.op1, i) * (unsigned short) GET_DIGIT(bip.op2, j) + (unsigned short) GET_DIGIT(bip.res, k) + carry; SET_DIGIT(bip.res, k, aux & 0xFF); carry = aux >> 8; } SET_DIGIT(bip.res, k, carry); } /* determine actual size of result */ i = nd1 + nd2; while (--i >= 0 && GET_DIGIT(bip.res, i) == 0) ; SET_ND(bip.res, i + 1); } /* * big integer unsigned division by single digit divisor * * dividend in bip.rem, divisor in parameter * quotient in bip.rem, remainder is returned */ static unsigned char bigUdiv1(unsigned char divisor) { BigObjRef tmp; int nd; int i; unsigned short d, r; unsigned short aux; /* get size of dividend */ nd = GET_ND(bip.rem); /* check for division by zero */ d = (unsigned short) divisor; if (d == 0) { fatalError("internal library error #3 - THIS SHOULD NEVER HAPPEN!"); } /* allocate result */ tmp = newBig(nd); /* tmp = dividend / divisor, r = dividend % divisor */ r = 0; for (i = nd - 1; i >= 0; i--) { aux = (r << 8) | (unsigned short) GET_DIGIT(bip.rem, i); SET_DIGIT(tmp, i, aux / d); r = aux % d; } /* determine actual size of quotient */ i = nd; while (--i >= 0 && GET_DIGIT(tmp, i) == 0) ; SET_ND(tmp, i + 1); /* store quotient */ bip.rem = tmp; /* return remainder */ return (unsigned char) r; } /* * big integer unsigned division * * dividend in bip.op1, divisor in bip.op2 * quotient in bip.res, remainder in bip.rem */ static void bigUdiv(void) { BigObjRef tmp; int nd1; int nd2; int nd3; int i, j, k, l; unsigned char r; unsigned short scale; unsigned short carry; unsigned short aux; unsigned short qhat; unsigned short v1, v2; unsigned short uj0, uj1, uj2, two; /* get sizes of operands */ nd1 = GET_ND(bip.op1); nd2 = GET_ND(bip.op2); /* check for division by zero */ if (nd2 == 0) { fatalError("division by zero"); } /* check for small dividend */ if (bigUcmp() < 0) { /* res = 0 */ bip.res = newBig(0); SET_ND(bip.res, 0); /* rem = op1; BUT THIS HAS TO BE A COPY! */ bip.rem = newBig(nd1); for (i = 0; i < nd1; i++) { SET_DIGIT(bip.rem, i, GET_DIGIT(bip.op1, i)); } SET_ND(bip.rem, nd1); return; } /* check for single digit divisor */ if (nd2 == 1) { /* yes - use simple division by single digit divisor */ bip.rem = bip.op1; r = bigUdiv1(GET_DIGIT(bip.op2, 0)); bip.res = bip.rem; if (r == 0) { bip.rem = newBig(0); SET_ND(bip.rem, 0); } else { bip.rem = newBig(1); SET_ND(bip.rem, 1); SET_DIGIT(bip.rem, 0, r); } return; } /* * now for the general case */ #if DIV_CHK_01 printf("div_chk #01: division, general case\n"); printf(" dividend = "); bigDump(stdout, bip.op1); printf("\n"); printf(" divisor = "); bigDump(stdout, bip.op2); printf("\n"); #endif /* determine scale factor for normalization */ scale = (unsigned short) 256 / ((unsigned short) GET_DIGIT(bip.op2, nd2 - 1) + 1); #if DIV_CHK_02 printf("div_chk #02: scale factor = %02X\n", scale); #endif /* normalize dividend, result is in bip.rem */ bip.rem = newBig(nd1 + 1); carry = 0x00; for (i = 0; i < nd1; i++) { aux = (unsigned short) GET_DIGIT(bip.op1, i) * scale + carry; SET_DIGIT(bip.rem, i, aux & 0xFF); carry = aux >> 8; } SET_DIGIT(bip.rem, i, carry); SET_ND(bip.rem, nd1 + 1); #if DIV_CHK_03 printf("div_chk #03: normalized dividend = "); bigDump(stdout, bip.rem); printf("\n"); #endif /* normalize divisor, result is in bip.res */ bip.res = newBig(nd2); carry = 0x00; for (i = 0; i < nd2; i++) { aux = (unsigned short) GET_DIGIT(bip.op2, i) * scale + carry; SET_DIGIT(bip.res, i, aux & 0xFF); carry = aux >> 8; } if (carry != 0x00) { /* overflow in divisor normalization */ fatalError("internal library error #4 - THIS SHOULD NEVER HAPPEN!"); } SET_ND(bip.res, nd2); #if DIV_CHK_04 printf("div_chk #04: normalized divisor = "); bigDump(stdout, bip.res); printf("\n"); #endif /* allocate quotient */ nd3 = nd1 - nd2 + 1; tmp = newBig(nd3); /* extract the two most significand digits of divisor */ v1 = (unsigned short) GET_DIGIT(bip.res, nd2 - 1); v2 = (unsigned short) GET_DIGIT(bip.res, nd2 - 2); /* loop on digits of dividend and compute digits of quotient */ /* j is index into dividend, k is index into quotient */ for (j = nd1, k = nd3 - 1; k >= 0; j--, k--) { #if DIV_CHK_05 printf("div_chk #05: j = %d, k = %d\n", j, k); #endif /* calculate qhat */ uj0 = (unsigned short) GET_DIGIT(bip.rem, j); uj1 = (unsigned short) GET_DIGIT(bip.rem, j - 1); uj2 = (unsigned short) GET_DIGIT(bip.rem, j - 2); two = (uj0 << 8) | uj1; if (uj0 == v1) { qhat = (unsigned short) 255; #if DIV_CHK_06 printf("div_chk #06a: qhat = %02X\n", qhat); #endif } else { qhat = two / v1; #if DIV_CHK_06 printf("div_chk #06b: qhat = %02X\n", qhat); #endif } while (qhat * v2 > (((two - qhat * v1) << 8) | uj2)) { qhat--; #if DIV_CHK_07 printf("div_chk #07: qhat decremented, is now %02X\n", qhat); #endif } /* multiply and subtract */ /* l is index into dividend, i is index into divisor */ carry = 0xFF; for (l = j - nd2, i = 0; i < nd2; l++, i++) { aux = (unsigned short) GET_DIGIT(bip.rem, l) - (unsigned short) GET_DIGIT(bip.res, i) * qhat + carry + 0xFE01; SET_DIGIT(bip.rem, l, aux & 0xFF); carry = aux >> 8; } aux = (unsigned short) GET_DIGIT(bip.rem, l) + carry + 0xFE01; SET_DIGIT(bip.rem, l, aux & 0xFF); carry = aux >> 8; #if DIV_CHK_08 printf("div_chk #08: remainder = "); bigDump(stdout, bip.rem); printf("\n"); #endif /* test remainder and possibly add back */ if (carry != 0xFF) { /* qhat is one too large */ qhat--; #if DIV_CHK_09 printf("div_chk #09: qhat final correction, is now %02X\n", qhat); #endif /* add back */ /* l is index into dividend, i is index into divisor */ carry = 0x00; for (l = j - nd2, i = 0; i < nd2; l++, i++) { aux = (unsigned short) GET_DIGIT(bip.rem, l) + (unsigned short) GET_DIGIT(bip.res, i) + carry; SET_DIGIT(bip.rem, l, aux & 0xFF); carry = aux >> 8; } aux = (unsigned short) GET_DIGIT(bip.rem, l) + carry; SET_DIGIT(bip.rem, l, aux & 0xFF); carry = aux >> 8; if (carry != 0x01) { /* missing carry in add-back sum */ fatalError("internal library error #5 - THIS SHOULD NEVER HAPPEN!"); } #if DIV_CHK_10 printf("div_chk #10: remainder = "); bigDump(stdout, bip.rem); printf("\n"); #endif } /* store quotient digit */ SET_DIGIT(tmp, k, qhat); #if DIV_CHK_11 printf("div_chk #11: quotient digit = %02X\n", qhat); #endif } /* finish quotient */ i = nd3; while (--i >= 0 && GET_DIGIT(tmp, i) == 0) ; SET_ND(tmp, i + 1); bip.res = tmp; /* finish and unnormalize remainder */ i = nd1 + 1; while (--i >= 0 && GET_DIGIT(bip.rem, i) == 0) ; SET_ND(bip.rem, i + 1); r = bigUdiv1(scale); if (r != 0) { /* non-zero remainder in unnormalization */ fatalError("internal library error #6 - THIS SHOULD NEVER HAPPEN!"); } } /**************************************************************/ /* nil reference exception */ static void nilRefException(void) { fatalError("big integer library detected illegal nil reference"); } /**************************************************************/ /* big integer arithmetic */ /* * big integer sign * * operand in bip.op1 * result is < 0, = 0, or > 0 if and only if * the same relation holds for bip.op1 */ int bigSgn(void) { if (bip.op1 == NULL) { nilRefException(); } if (GET_ND(bip.op1) == 0) { return 0; } if (GET_SIGN(bip.op1) == BIG_POSITIVE) { return 1; } else { return -1; } } /* * big integer comparison * * operands in bip.op1 and bip.op2 * result is < 0, = 0, or > 0 if and only if the * same relation holds for bip.op1 and bip.op2 */ int bigCmp(void) { if (bip.op1 == NULL || bip.op2 == NULL) { nilRefException(); } if (GET_SIGN(bip.op1) == BIG_POSITIVE) { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 >= 0 and op2 >= 0 */ return bigUcmp(); } else { /* op1 >= 0 and op2 < 0 */ return 1; } } else { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 < 0 and op2 >= 0 */ return -1; } else { /* op1 < 0 and op2 < 0 */ return -bigUcmp(); } } } /* * big integer negation * * operand in bip.op1 * result in bip.res */ void bigNeg(void) { int nd; int i; if (bip.op1 == NULL) { nilRefException(); } /* make copy of operand */ nd = GET_ND(bip.op1); bip.res = newBig(nd); for (i = 0; i < nd; i++) { SET_DIGIT(bip.res, i, GET_DIGIT(bip.op1, i)); } SET_ND(bip.res, nd); /* store inverted sign */ if (GET_SIGN(bip.op1) == BIG_NEGATIVE || nd == 0) { SET_SIGN(bip.res, BIG_POSITIVE); } else { SET_SIGN(bip.res, BIG_NEGATIVE); } } /* * big integer addition * * operands in bip.op1 and bip.op2 * result in bip.res */ void bigAdd(void) { if (bip.op1 == NULL || bip.op2 == NULL) { nilRefException(); } if (GET_SIGN(bip.op1) == BIG_POSITIVE) { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 >= 0 and op2 >= 0 */ bigUadd(); SET_SIGN(bip.res, BIG_POSITIVE); } else { /* op1 >= 0 and op2 < 0 */ if (bigUcmp() >= 0) { /* |op1| >= |op2| */ bigUsub(); SET_SIGN(bip.res, BIG_POSITIVE); } else { /* |op1| < |op2| */ bigXchg(); bigUsub(); SET_SIGN(bip.res, BIG_NEGATIVE); bigXchg(); } } } else { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 < 0 and op2 >= 0 */ if (bigUcmp() <= 0) { /* |op1| <= |op2| */ bigXchg(); bigUsub(); SET_SIGN(bip.res, BIG_POSITIVE); bigXchg(); } else { /* |op1| > |op2| */ bigUsub(); SET_SIGN(bip.res, BIG_NEGATIVE); } } else { /* op1 < 0 and op2 < 0 */ bigUadd(); SET_SIGN(bip.res, BIG_NEGATIVE); } } } /* * big integer subtraction * * operands in bip.op1 and bip.op2 * result in bip.res */ void bigSub(void) { if (bip.op1 == NULL || bip.op2 == NULL) { nilRefException(); } if (GET_SIGN(bip.op1) == BIG_POSITIVE) { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 >= 0 and op2 >= 0 */ if (bigUcmp() >= 0) { /* |op1| >= |op2| */ bigUsub(); SET_SIGN(bip.res, BIG_POSITIVE); } else { /* |op1| < |op2| */ bigXchg(); bigUsub(); SET_SIGN(bip.res, BIG_NEGATIVE); bigXchg(); } } else { /* op1 >= 0 and op2 < 0 */ bigUadd(); SET_SIGN(bip.res, BIG_POSITIVE); } } else { if (GET_SIGN(bip.op2) == BIG_POSITIVE) { /* op1 < 0 and op2 >= 0 */ bigUadd(); SET_SIGN(bip.res, BIG_NEGATIVE); } else { /* op1 < 0 and op2 < 0 */ if (bigUcmp() <= 0) { /* |op1| <= |op2| */ bigXchg(); bigUsub(); SET_SIGN(bip.res, BIG_POSITIVE); bigXchg(); } else { /* |op1| > |op2| */ bigUsub(); SET_SIGN(bip.res, BIG_NEGATIVE); } } } } /* * big integer multiplication * * operands in bip.op1 and bip.op2 * result in bip.res */ void bigMul(void) { if (bip.op1 == NULL || bip.op2 == NULL) { nilRefException(); } bigUmul(); if (GET_SIGN(bip.op1) == GET_SIGN(bip.op2) || GET_ND(bip.res) == 0) { SET_SIGN(bip.res, BIG_POSITIVE); } else { SET_SIGN(bip.res, BIG_NEGATIVE); } } /* * big integer division, truncating towards zero * * dividend in bip.op1, divisor in bip.op2 * quotient in bip.res, remainder in bip.rem */ void bigDiv(void) { if (bip.op1 == NULL || bip.op2 == NULL) { nilRefException(); } bigUdiv(); if (GET_SIGN(bip.op1) == GET_SIGN(bip.op2) || GET_ND(bip.res) == 0) { SET_SIGN(bip.res, BIG_POSITIVE); } else { SET_SIGN(bip.res, BIG_NEGATIVE); } if (GET_SIGN(bip.op1) == BIG_POSITIVE || GET_ND(bip.rem) == 0) { SET_SIGN(bip.rem, BIG_POSITIVE); } else { SET_SIGN(bip.rem, BIG_NEGATIVE); } } /**************************************************************/ /* big integer conversions */ /* * conversion int --> big * * operand in parameter * result in bip.res */ void bigFromInt(int n) { int i; bip.res = newBig(sizeof(int)); if (n < 0) { n = -n; SET_SIGN(bip.res, BIG_NEGATIVE); } else { SET_SIGN(bip.res, BIG_POSITIVE); } for (i = 0; i < sizeof(int); i++) { SET_DIGIT(bip.res, i, n & 0xFF); n >>= 8; } while (--i >= 0 && GET_DIGIT(bip.res, i) == 0) ; SET_ND(bip.res, i + 1); } /* * conversion big --> int * * operand in bip.op1 * result is returned */ int bigToInt(void) { int nd; int i; int res; if (bip.op1 == NULL) { nilRefException(); } nd = GET_ND(bip.op1); if (nd > 4 || (nd == 4 && GET_DIGIT(bip.op1, 3) >= 0x80)) { fatalError("big integer too big for conversion to int"); } res = 0; for (i = nd - 1; i >= 0; i--) { res <<= 8; res |= (unsigned int) GET_DIGIT(bip.op1, i); } if (GET_SIGN(bip.op1) == BIG_NEGATIVE) { res = -res; } return res; } /**************************************************************/ /* big integer I/O */ /* * read a big integer * * stream to read from in parameter * result in bip.res */ void bigRead(FILE *in) { int c; int positive; c = fgetc(in); while (isspace(c)) { c = fgetc(in); } if (c == '-') { positive = 0; c = fgetc(in); } else { positive = 1; if (c == '+') { c = fgetc(in); } } if (!isdigit(c)) { fatalError("no digits in input"); } bigFromInt(10); bip.rem = bip.res; bigFromInt(0); while (isdigit(c)) { bip.op1 = bip.res; bip.op2 = bip.rem; bigUmul(); bip.op1 = bip.res; bigFromInt(c - '0'); bip.op2 = bip.res; bigUadd(); c = fgetc(in); } ungetc(c, in); if (positive || GET_ND(bip.res) == 0) { SET_SIGN(bip.res, BIG_POSITIVE); } else { SET_SIGN(bip.res, BIG_NEGATIVE); } } /* * print a big integer * * stream to write to in parameter * number to print in bip.op1 */ void bigPrint(FILE *out) { int nd; unsigned char r; int skipZero; if (bip.op1 == NULL) { nilRefException(); } nd = GET_ND(bip.op1); if (nd == 0) { fprintf(out, "0"); return; } if (GET_SIGN(bip.op1) == BIG_NEGATIVE) { fprintf(out, "-"); } /* number of digits in base 10 = number of digits in base 256 * log10(256), and log10(256) < 2.5 */ nd = 2 * nd + nd / 2; bip.rem = bip.op1; bigFromInt(10); bip.op2 = bip.res; bigFromInt(1); while (nd != 0) { bip.op1 = bip.res; bigUmul(); nd--; } bip.op1 = bip.rem; bip.op2 = bip.res; skipZero = 1; do { bigUdiv(); if (GET_ND(bip.res) == 0) { if (!skipZero) { fprintf(out, "0"); } } else { if (GET_ND(bip.res) != 1) { fatalError("internal library error #7 - THIS SHOULD NEVER HAPPEN!"); } fprintf(out, "%c", GET_DIGIT(bip.res, 0) + '0'); skipZero = 0; } bip.op1 = bip.rem; bip.rem = bip.op2; r = bigUdiv1(10); bip.op2 = bip.rem; } while (r == 0); } /**************************************************************/ /* debugging */ /* * dump a big integer object */ void bigDump(FILE *out, BigObjRef bigObjRef) { int nd; unsigned char sign; int i; if (bigObjRef == NULL) { nilRefException(); } nd = GET_ND(bigObjRef); sign = GET_SIGN(bigObjRef); fprintf(out, "[%d %c", nd, sign == BIG_POSITIVE ? '+' : '-'); for (i = 0; i < nd; i++) { fprintf(out, " %02X", GET_DIGIT(bigObjRef, i)); } fprintf(out, "]"); }