diff options
author | dgp <dgp@users.sourceforge.net> | 2005-08-15 03:16:47 (GMT) |
---|---|---|
committer | dgp <dgp@users.sourceforge.net> | 2005-08-15 03:16:47 (GMT) |
commit | d290e66fd54a5f4dc90cad0ce2cd9b1067b85c95 (patch) | |
tree | f45f158d4f7406c58cb8308276ac72f21dd119a0 | |
parent | dfee3096cbbc741e611aba4c35ba9d41877ebcef (diff) | |
download | tcl-d290e66fd54a5f4dc90cad0ce2cd9b1067b85c95.zip tcl-d290e66fd54a5f4dc90cad0ce2cd9b1067b85c95.tar.gz tcl-d290e66fd54a5f4dc90cad0ce2cd9b1067b85c95.tar.bz2 |
[kennykb_numerics_branch]
* generic/tclExecute.c: Updated execution of arithmetic bytecodes
to be bignum-aware, and to allow calculations on NaN to produce
a NaN result. INST_UMINUS updated to call mp_neg.
* generic/tclTomMath.h: Added mp_and, mp_expt_d, and mp_neg to
* unix/Makefile.in: routines from libtommath used by Tcl.
* win/Makefile.in:
* win/makefile.vc:
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | generic/tclExecute.c | 208 | ||||
-rw-r--r-- | generic/tclTomMath.h | 5 | ||||
-rw-r--r-- | unix/Makefile.in | 23 | ||||
-rw-r--r-- | win/Makefile.in | 5 | ||||
-rw-r--r-- | win/makefile.vc | 5 |
6 files changed, 244 insertions, 15 deletions
@@ -1,3 +1,16 @@ +2005-08-14 Don Porter <dgp@users.sourceforge.net> + + [kennykb_numerics_branch] + + * generic/tclExecute.c: Updated execution of arithmetic bytecodes + to be bignum-aware, and to allow calculations on NaN to produce + a NaN result. INST_UMINUS updated to call mp_neg. + + * generic/tclTomMath.h: Added mp_and, mp_expt_d, and mp_neg to + * unix/Makefile.in: routines from libtommath used by Tcl. + * win/Makefile.in: + * win/makefile.vc: + 2005-08-13 Don Porter <dgp@users.sourceforge.net> [kennykb_numerics_branch] diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 731e06c..25fddd8 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclExecute.c,v 1.167.2.19 2005/08/13 20:19:28 dgp Exp $ + * RCS: @(#) $Id: tclExecute.c,v 1.167.2.20 2005/08/15 03:16:47 dgp Exp $ */ #include "tclInt.h" @@ -390,10 +390,12 @@ static void ValidatePcAndStackTop _ANSI_ARGS_(( int stackTop, int stackLowerBound, int checkStack)); #endif /* TCL_COMPILE_DEBUG */ +#if 0 static Tcl_WideInt ExponWide _ANSI_ARGS_((Tcl_WideInt w, Tcl_WideInt w2, int *errExpon)); static long ExponLong _ANSI_ARGS_((long i, long i2, int *errExpon)); +#endif /* @@ -3934,16 +3936,17 @@ TclExecuteByteCode(interp, codePtr) * necessary. We compute value op value2. */ + double d1, d2; + double dResult = 0.0; /* Init. avoids compiler warning. */ + Tcl_Obj *valuePtr,*value2Ptr; +#if 0 Tcl_ObjType *t1Ptr, *t2Ptr; long i = 0, i2 = 0, quot; /* Init. avoids compiler warning. */ - double d1, d2; long iResult = 0; /* Init. avoids compiler warning. */ - double dResult = 0.0; /* Init. avoids compiler warning. */ int doDouble = 0; /* 1 if doing floating arithmetic */ Tcl_WideInt w, w2, wquot; Tcl_WideInt wResult = W0; /* Init. avoids compiler warning. */ int doWide = 0; /* 1 if doing wide arithmetic. */ - Tcl_Obj *valuePtr,*value2Ptr; int length; value2Ptr = *tosPtr; @@ -4217,6 +4220,193 @@ TclExecuteByteCode(interp, codePtr) } NEXT_INST_F(1, 1, 0); } +#else + value2Ptr = *tosPtr; + valuePtr = *(tosPtr - 1); + result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d1); + if (result != TCL_OK) { + if (valuePtr->typePtr == &tclDoubleType) { + /* NaN first argument -> result is also NaN */ + result = TCL_OK; + NEXT_INST_F(1, 1, 0); + } + TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (valuePtr->typePtr? valuePtr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, valuePtr); + goto checkForCatch; + } + result = Tcl_GetDoubleFromObj(NULL, value2Ptr, &d2); + if (result != TCL_OK) { + if (value2Ptr->typePtr == &tclDoubleType) { + /* NaN second argument -> result is also NaN */ + objResultPtr = value2Ptr; + result = TCL_OK; + NEXT_INST_F(1, 2, 1); + } + TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n", + O2S(value2Ptr), O2S(valuePtr), + (value2Ptr->typePtr? value2Ptr->typePtr->name: "null"))); + IllegalExprOperandType(interp, pc, value2Ptr); + goto checkForCatch; + } + if (valuePtr->typePtr == &tclDoubleType + || value2Ptr->typePtr == &tclDoubleType) { + /* At least one of the values is floating-point, so perform + * floating point calculations */ + switch (*pc) { + case INST_ADD: + dResult = d1 + d2; + break; + case INST_SUB: + dResult = d1 - d2; + break; + case INST_MULT: + dResult = d1 * d2; + break; + case INST_DIV: +#ifndef IEEE_FLOATING_POINT + if (d2 == 0.0) { + TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2)); + goto divideByZero; + } +#endif + /* + * We presume that we are running with zero-divide unmasked if + * we're on an IEEE box. Otherwise, this statement might cause + * demons to fly out our noses. + */ + dResult = d1 / d2; + break; + case INST_EXPON: + if (d1==0.0 && d2<0.0) { + TRACE(("%.6g %.6g => EXPONENT OF ZERO\n", d1, d2)); + goto exponOfZero; + } + dResult = pow(d1, d2); + break; + } +#if 0 + /* + * Check now for IEEE floating-point error. + */ + + if (IS_NAN(dResult)) { + TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n", + O2S(valuePtr), O2S(value2Ptr))); + TclExprFloatError(interp, dResult); + result = TCL_ERROR; + goto checkForCatch; + } +#endif + if (Tcl_IsShared(valuePtr)) { + TclNewDoubleObj(objResultPtr, dResult); + NEXT_INST_F(1, 2, 1); + } + TclSetDoubleObj(valuePtr, dResult); + NEXT_INST_F(1, 1, 0); + } else { + /* Both values are some kind of integer */ + /* TODO: optimize use of narrower native integers */ + mp_int big1, big2, bigResult, bigRemainder; + Tcl_GetBignumFromObj(NULL, valuePtr, &big1); + Tcl_GetBignumFromObj(NULL, value2Ptr, &big2); + mp_init(&bigResult); + switch (*pc) { + case INST_ADD: + mp_add(&big1, &big2, &bigResult); + break; + case INST_SUB: + mp_sub(&big1, &big2, &bigResult); + break; + case INST_MULT: + mp_mul(&big1, &big2, &bigResult); + break; + case INST_DIV: + if (mp_iszero(&big2)) { + TRACE(("%s %s => DIVIDE BY ZERO\n", O2S(valuePtr), + O2S(value2Ptr))); + mp_clear(&big1); + mp_clear(&big2); + goto divideByZero; + } + mp_init(&bigRemainder); + mp_div(&big1, &big2, &bigResult, &bigRemainder); + if (!mp_iszero(&bigRemainder) + && (bigRemainder.sign != big2.sign)) { + /* Convert to Tcl's integer division rules */ + mp_int bigOne; + Tcl_GetBignumFromObj(NULL, eePtr->constants[1], &bigOne); + mp_sub(&bigResult, &bigOne, &bigResult); + mp_clear(&bigOne); + } + mp_clear(&bigRemainder); + break; + case INST_EXPON: + if (mp_iszero(&big2)) { + /* Anything to the zero power is 1 */ + mp_clear(&big1); + mp_clear(&big2); + objResultPtr = eePtr->constants[1]; + NEXT_INST_F(1, 2, 1); + } + if (mp_iszero(&big1)) { + /* TODO: Use mp_cmp_d() call instead */ + if (big2.sign == MP_NEG) { + TRACE(("%s %s => EXPONENT OF ZERO\n", O2S(valuePtr), + O2S(value2Ptr))); + mp_clear(&big1); + mp_clear(&big2); + goto exponOfZero; + } + mp_clear(&big1); + mp_clear(&big2); + objResultPtr = eePtr->constants[0]; + NEXT_INST_F(1, 2, 1); + } + if (big2.sign == MP_NEG) { + mp_int bigOne; + Tcl_GetBignumFromObj(NULL, eePtr->constants[1], &bigOne); + if (mp_cmp_mag(&big1, &bigOne) == MP_GT) { + objResultPtr = eePtr->constants[0]; + } else if (big1.sign == MP_ZPOS) { + objResultPtr = eePtr->constants[1]; + } else { + mp_int bigBit; + mp_init(&bigBit); + mp_and(&big2, &bigOne, &bigBit); + if (mp_iszero(&bigBit)) { + objResultPtr = eePtr->constants[1]; + } else { + TclNewIntObj(objResultPtr, -1); + } + mp_clear(&bigBit); + } + mp_clear(&bigOne); + mp_clear(&big1); + mp_clear(&big2); + NEXT_INST_F(1, 2, 1); + } + if (big2.used > 1) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("exponent too large", -1)); + mp_clear(&big1); + mp_clear(&big2); + goto checkForCatch; + } + mp_expt_d(&big1, big2.dp[0], &bigResult); + break; + } + mp_clear(&big1); + mp_clear(&big2); + if (Tcl_IsShared(valuePtr)) { + objResultPtr = Tcl_NewBignumObj(&bigResult); + NEXT_INST_F(1, 2, 1); + } + Tcl_SetBignumObj(valuePtr, &bigResult); + NEXT_INST_F(1, 1, 0); + } +#endif } #if 0 @@ -4431,9 +4621,7 @@ TclExecuteByteCode(interp, codePtr) /* TODO: optimize use of narrower native integers */ mp_int big; Tcl_GetBignumFromObj(NULL, valuePtr, &big); - if (big.used != 0) { - big.sign = !big.sign; - } + mp_neg(&big, &big); if (Tcl_IsShared(valuePtr)) { objResultPtr = Tcl_NewBignumObj(&big); NEXT_INST_F(1, 1, 1); @@ -4627,6 +4815,7 @@ TclExecuteByteCode(interp, codePtr) result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); if ((result == TCL_OK) || valuePtr->typePtr == &tclDoubleType) { /* Value is now numeric (including NaN) */ +#if 0 if ((*pc == INST_TRY_CVT_TO_NUMERIC) && (result != TCL_OK)) { /* Numeric conversion of NaN -> error */ CONST char *s = "domain error: argument not in valid range"; @@ -4636,6 +4825,9 @@ TclExecuteByteCode(interp, codePtr) O2S(objResultPtr))); goto checkForCatch; } +#else + result = TCL_OK; +#endif /* * Ensure that the numeric value has a string rep the same as * the formatted version of its internal rep. This is used, e.g., @@ -6635,6 +6827,7 @@ StringForResultCode(result) return buf; } #endif /* TCL_COMPILE_DEBUG */ +#if 0 /* *---------------------------------------------------------------------- @@ -6770,3 +6963,4 @@ ExponLong(i, i2, errExpon) } return result * i; } +#endif diff --git a/generic/tclTomMath.h b/generic/tclTomMath.h index e717768..4f2bffa 100644 --- a/generic/tclTomMath.h +++ b/generic/tclTomMath.h @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclTomMath.h,v 1.1.2.2 2005/08/10 18:21:53 dgp Exp $ + * RCS: @(#) $Id: tclTomMath.h,v 1.1.2.3 2005/08/15 03:16:48 dgp Exp $ */ #ifndef TCLTOMMATH_H @@ -70,6 +70,7 @@ void* TclBNCalloc( size_t, size_t ); #define bn_reverse TclBN_reverse #define fast_s_mp_mul_digs TclBN_fast_s_mp_mul_digs #define mp_add TclBN_mp_add +#define mp_and TclBN_mp_and #define mp_clamp TclBN_mp_clamp #define mp_clear TclBN_mp_clear #define mp_clear_multi TclBN_mp_clear_multi @@ -83,6 +84,7 @@ void* TclBNCalloc( size_t, size_t ); #define mp_div_2d TclBN_mp_div_2d #define mp_div_3 TclBN_mp_div_3 #define mp_exch TclBN_mp_exch +#define mp_expt_d TclBN_mp_expt_d #define mp_grow TclBN_mp_grow #define mp_init TclBN_mp_init #define mp_init_copy TclBN_mp_init_copy @@ -95,6 +97,7 @@ void* TclBNCalloc( size_t, size_t ); #define mp_mul_2 TclBN_mp_mul_2 #define mp_mul_2d TclBN_mp_mul_2d #define mp_mul_d TclBN_mp_mul_d +#define mp_neg TclBN_mp_neg #define mp_radix_size TclBN_mp_radix_size #define mp_read_radix TclBN_mp_read_radix #define mp_rshd TclBN_mp_rshd diff --git a/unix/Makefile.in b/unix/Makefile.in index c2d889b..535610b 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -5,7 +5,7 @@ # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. # -# RCS: @(#) $Id: Makefile.in,v 1.157.2.13 2005/08/10 18:21:54 dgp Exp $ +# RCS: @(#) $Id: Makefile.in,v 1.157.2.14 2005/08/15 03:16:48 dgp Exp $ VERSION = @TCL_VERSION@ MAJOR_VERSION = @TCL_MAJOR_VERSION@ @@ -314,16 +314,17 @@ GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \ tclTomMathInterface.o TOMMATH_OBJS = bncore.o bn_reverse.o bn_fast_s_mp_mul_digs.o \ - bn_fast_s_mp_sqr.o bn_mp_add.o \ + bn_fast_s_mp_sqr.o bn_mp_add.o bn_mp_and.o \ bn_mp_add_d.o bn_mp_clamp.o bn_mp_clear.o bn_mp_clear_multi.o \ bn_mp_cmp.o bn_mp_cmp_mag.o bn_mp_copy.o bn_mp_count_bits.o \ bn_mp_div.o bn_mp_div_d.o bn_mp_div_2.o bn_mp_div_2d.o bn_mp_div_3.o \ - bn_mp_exch.o bn_mp_grow.o bn_mp_init.o bn_mp_init_copy.o \ - bn_mp_init_multi.o bn_mp_init_set.o \ + bn_mp_exch.o bn_mp_expt_d.o bn_mp_grow.o bn_mp_init.o \ + bn_mp_init_copy.o bn_mp_init_multi.o bn_mp_init_set.o \ bn_mp_init_size.o bn_mp_karatsuba_mul.o \ bn_mp_karatsuba_sqr.o \ bn_mp_lshd.o bn_mp_mod.o bn_mp_mod_2d.o bn_mp_mul.o bn_mp_mul_2.o \ - bn_mp_mul_2d.o bn_mp_mul_d.o bn_mp_radix_size.o bn_mp_radix_smap.o \ + bn_mp_mul_2d.o bn_mp_mul_d.o bn_mp_neg.o \ + bn_mp_radix_size.o bn_mp_radix_smap.o \ bn_mp_read_radix.o bn_mp_rshd.o bn_mp_set.o bn_mp_shrink.o \ bn_mp_sqr.o bn_mp_sub.o bn_mp_sub_d.o \ bn_mp_to_unsigned_bin.o bn_mp_to_unsigned_bin_n.o \ @@ -436,6 +437,7 @@ TOMMATH_SRCS = \ $(TOMMATH_DIR)/bn_fast_s_mp_sqr.c \ $(TOMMATH_DIR)/bn_mp_add.c \ $(TOMMATH_DIR)/bn_mp_add_d.c \ + $(TOMMATH_DIR)/bn_mp_and.c \ $(TOMMATH_DIR)/bn_mp_clamp.c \ $(TOMMATH_DIR)/bn_mp_clear.c \ $(TOMMATH_DIR)/bn_mp_clear_multi.c \ @@ -449,6 +451,7 @@ TOMMATH_SRCS = \ $(TOMMATH_DIR)/bn_mp_div_2d.c \ $(TOMMATH_DIR)/bn_mp_div_3.c \ $(TOMMATH_DIR)/bn_mp_exch.c \ + $(TOMMATH_DIR)/bn_mp_expt_d.c \ $(TOMMATH_DIR)/bn_mp_grow.c \ $(TOMMATH_DIR)/bn_mp_init.c \ $(TOMMATH_DIR)/bn_mp_init_copy.c \ @@ -464,6 +467,7 @@ TOMMATH_SRCS = \ $(TOMMATH_DIR)/bn_mp_mul_2.c \ $(TOMMATH_DIR)/bn_mp_mul_2d.c \ $(TOMMATH_DIR)/bn_mp_mul_d.c \ + $(TOMMATH_DIR)/bn_mp_neg.c \ $(TOMMATH_DIR)/bn_mp_radix_size.c \ $(TOMMATH_DIR)/bn_mp_radix_smap.c \ $(TOMMATH_DIR)/bn_mp_read_radix.c \ @@ -1195,6 +1199,9 @@ bn_mp_add.o: $(TOMMATH_DIR)/bn_mp_add.c bn_mp_add_d.o: $(TOMMATH_DIR)/bn_mp_add_d.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_add_d.c +bn_mp_and.o: $(TOMMATH_DIR)/bn_mp_and.c + $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_and.c + bn_mp_clamp.o: $(TOMMATH_DIR)/bn_mp_clamp.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_clamp.c @@ -1234,6 +1241,9 @@ bn_mp_div_3.o: $(TOMMATH_DIR)/bn_mp_div_3.c bn_mp_exch.o: $(TOMMATH_DIR)/bn_mp_exch.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_exch.c +bn_mp_expt_d.o: $(TOMMATH_DIR)/bn_mp_expt_d.c + $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_expt_d.c + bn_mp_grow.o: $(TOMMATH_DIR)/bn_mp_grow.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_grow.c @@ -1279,6 +1289,9 @@ bn_mp_mul_2d.o: $(TOMMATH_DIR)/bn_mp_mul_2d.c bn_mp_mul_d.o: $(TOMMATH_DIR)/bn_mp_mul_d.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_mul_d.c +bn_mp_neg.o: $(TOMMATH_DIR)/bn_mp_neg.c + $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_neg.c + bn_mp_radix_size.o: $(TOMMATH_DIR)/bn_mp_radix_size.c $(CC) -c $(CC_SWITCHES) $(TOMMATH_DIR)/bn_mp_radix_size.c diff --git a/win/Makefile.in b/win/Makefile.in index e7f103e..062d7f0 100644 --- a/win/Makefile.in +++ b/win/Makefile.in @@ -5,7 +5,7 @@ # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. # -# RCS: @(#) $Id: Makefile.in,v 1.84.2.8 2005/08/10 18:21:54 dgp Exp $ +# RCS: @(#) $Id: Makefile.in,v 1.84.2.9 2005/08/15 03:16:49 dgp Exp $ VERSION = @TCL_VERSION@ @@ -288,6 +288,7 @@ TOMMATH_OBJS = \ bn_fast_s_mp_sqr.${OBJEXT} \ bn_mp_add.${OBJEXT} \ bn_mp_add_d.${OBJEXT} \ + bn_mp_and.${OBJEXT} \ bn_mp_clamp.${OBJEXT} \ bn_mp_clear.${OBJEXT} \ bn_mp_clear_multi.${OBJEXT} \ @@ -301,6 +302,7 @@ TOMMATH_OBJS = \ bn_mp_div_2d.${OBJEXT} \ bn_mp_div_3.${OBJEXT} \ bn_mp_exch.${OBJEXT} \ + bn_mp_expt_d.${OBJEXT} \ bn_mp_grow.${OBJEXT} \ bn_mp_init.${OBJEXT} \ bn_mp_init_copy.${OBJEXT} \ @@ -316,6 +318,7 @@ TOMMATH_OBJS = \ bn_mp_mul_2.${OBJEXT} \ bn_mp_mul_2d.${OBJEXT} \ bn_mp_mul_d.${OBJEXT} \ + bn_mp_neg.${OBJEXT} \ bn_mp_radix_size.${OBJEXT} \ bn_mp_radix_smap.${OBJEXT} \ bn_mp_read_radix.${OBJEXT} \ diff --git a/win/makefile.vc b/win/makefile.vc index fff54cc..1f98bce 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -12,7 +12,7 @@ # Copyright (c) 2001-2004 David Gravereaux. # #------------------------------------------------------------------------------ -# RCS: @(#) $Id: makefile.vc,v 1.135.2.4 2005/08/10 18:21:54 dgp Exp $ +# RCS: @(#) $Id: makefile.vc,v 1.135.2.5 2005/08/15 03:16:49 dgp Exp $ #------------------------------------------------------------------------------ # Check to see we are configured to build with MSVC (MSDEVDIR or MSVCDIR) @@ -340,6 +340,7 @@ TCLOBJS = \ $(TMP_DIR)\bn_fast_s_mp_sqr.obj \ $(TMP_DIR)\bn_mp_add.obj \ $(TMP_DIR)\bn_mp_add_d.obj \ + $(TMP_DIR)\bn_mp_and.obj \ $(TMP_DIR)\bn_mp_clamp.obj \ $(TMP_DIR)\bn_mp_clear.obj \ $(TMP_DIR)\bn_mp_clear_multi.obj \ @@ -353,6 +354,7 @@ TCLOBJS = \ $(TMP_DIR)\bn_mp_div_2d.obj \ $(TMP_DIR)\bn_mp_div_3.obj \ $(TMP_DIR)\bn_mp_exch.obj \ + $(TMP_DIR)\bn_mp_expt_d.obj \ $(TMP_DIR)\bn_mp_grow.obj \ $(TMP_DIR)\bn_mp_init.obj \ $(TMP_DIR)\bn_mp_init_copy.obj \ @@ -368,6 +370,7 @@ TCLOBJS = \ $(TMP_DIR)\bn_mp_mul_2.obj \ $(TMP_DIR)\bn_mp_mul_2d.obj \ $(TMP_DIR)\bn_mp_mul_d.obj \ + $(TMP_DIR)\bn_mp_neg.obj \ $(TMP_DIR)\bn_mp_radix_size.obj \ $(TMP_DIR)\bn_mp_radix_smap.obj \ $(TMP_DIR)\bn_mp_read_radix.obj \ |