| | 1 | /* $Header$ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 7 | * on using and copying this software. |
| | 8 | */ |
| | 9 | /* |
| | 10 | Name |
| | 11 | vmbignum.h - big number metaclass |
| | 12 | Function |
| | 13 | |
| | 14 | Notes |
| | 15 | |
| | 16 | Modified |
| | 17 | 02/18/00 MJRoberts - Creation |
| | 18 | */ |
| | 19 | |
| | 20 | #ifndef VMBIGNUM_H |
| | 21 | #define VMBIGNUM_H |
| | 22 | |
| | 23 | #include <stdlib.h> |
| | 24 | #include <os.h> |
| | 25 | #include "vmtype.h" |
| | 26 | #include "vmobj.h" |
| | 27 | #include "vmglob.h" |
| | 28 | |
| | 29 | /* ------------------------------------------------------------------------ */ |
| | 30 | /* |
| | 31 | * Big number temporary register cache. Because big number values are |
| | 32 | * of arbitrary precision, we can't know in advance how much space we'll |
| | 33 | * need for temporary values. Instead, we keep this cache; each time we |
| | 34 | * need a temporary register, we'll look to see if we have one available |
| | 35 | * with sufficient precision, and allocate a new register if not. |
| | 36 | */ |
| | 37 | |
| | 38 | /* internal register descriptor */ |
| | 39 | struct CVmBigNumCacheReg |
| | 40 | { |
| | 41 | /* clear out the register values */ |
| | 42 | void clear() |
| | 43 | { |
| | 44 | buf_ = 0; |
| | 45 | siz_ = 0; |
| | 46 | nxt_ = 0; |
| | 47 | } |
| | 48 | |
| | 49 | /* |
| | 50 | * Allocate memory for this register. Returns true if we had to |
| | 51 | * allocate memory, false if the register was already at the given |
| | 52 | * size. |
| | 53 | */ |
| | 54 | int alloc_mem(size_t siz) |
| | 55 | { |
| | 56 | /* |
| | 57 | * if I'm already at least this large, there's no need to change |
| | 58 | * anything |
| | 59 | */ |
| | 60 | if (siz_ >= siz) |
| | 61 | return FALSE; |
| | 62 | |
| | 63 | /* |
| | 64 | * round up the size a bit - this will avoid repeatedly |
| | 65 | * reallocating at slightly different sizes, which could |
| | 66 | * fragment the heap quite a bit; we'll use a little more memory |
| | 67 | * than the caller actually asked for, but if they come back and |
| | 68 | * ask for slightly more next time, an additional allocation |
| | 69 | * probably won't be necessary, which will save memory in the |
| | 70 | * long run |
| | 71 | */ |
| | 72 | siz = (siz + 63) & ~63; |
| | 73 | |
| | 74 | /* delete any existing memory */ |
| | 75 | free_mem(); |
| | 76 | |
| | 77 | /* remember the new size */ |
| | 78 | siz_ = siz; |
| | 79 | |
| | 80 | /* allocate the memory */ |
| | 81 | buf_ = (char *)t3malloc(siz_); |
| | 82 | |
| | 83 | /* indicate that we allocated memory */ |
| | 84 | return TRUE; |
| | 85 | } |
| | 86 | |
| | 87 | /* free the memory associated with the register, if any */ |
| | 88 | void free_mem() |
| | 89 | { |
| | 90 | /* if we have a buffer, delete it */ |
| | 91 | if (buf_ != 0) |
| | 92 | t3free(buf_); |
| | 93 | } |
| | 94 | |
| | 95 | /* register's buffer */ |
| | 96 | char *buf_; |
| | 97 | |
| | 98 | /* size of register's buffer */ |
| | 99 | size_t siz_; |
| | 100 | |
| | 101 | /* next register in list */ |
| | 102 | CVmBigNumCacheReg *nxt_; |
| | 103 | }; |
| | 104 | |
| | 105 | /* |
| | 106 | * register cache |
| | 107 | */ |
| | 108 | class CVmBigNumCache |
| | 109 | { |
| | 110 | public: |
| | 111 | CVmBigNumCache(size_t max_regs); |
| | 112 | ~CVmBigNumCache(); |
| | 113 | |
| | 114 | /* |
| | 115 | * Allocate a temporary register with a minimum of the given byte |
| | 116 | * size. Returns a pointer to the register's buffer, and fills in |
| | 117 | * '*hdl' with the handle of the register, which must be used to |
| | 118 | * relesae the register. |
| | 119 | */ |
| | 120 | char *alloc_reg(size_t siz, uint *hdl); |
| | 121 | |
| | 122 | /* release a previously-allocated register */ |
| | 123 | void release_reg(uint hdl); |
| | 124 | |
| | 125 | /* release all registers */ |
| | 126 | void release_all(); |
| | 127 | |
| | 128 | /* |
| | 129 | * Get a special dedicated constant value register, reallocating it |
| | 130 | * to the required precision if it's not already available at the |
| | 131 | * required precision or greater. We'll set '*expanded' to true if |
| | 132 | * we had to expand the register, false if not. |
| | 133 | */ |
| | 134 | |
| | 135 | /* get the constant ln(10) register */ |
| | 136 | char *get_ln10_reg(size_t siz, int *expanded) |
| | 137 | { return alloc_reg(&ln10_, siz, expanded); } |
| | 138 | |
| | 139 | /* get the constant pi register */ |
| | 140 | char *get_pi_reg(size_t siz, int *expanded) |
| | 141 | { return alloc_reg(&pi_, siz, expanded); } |
| | 142 | |
| | 143 | /* get the constant e register */ |
| | 144 | char *get_e_reg(size_t siz, int *expanded) |
| | 145 | { return alloc_reg(&e_, siz, expanded); } |
| | 146 | |
| | 147 | private: |
| | 148 | /* make sure a register is allocated to a given size, and return it */ |
| | 149 | char *alloc_reg(CVmBigNumCacheReg *reg, size_t siz, int *expanded) |
| | 150 | { |
| | 151 | /* make sure the register satisfies the size requested */ |
| | 152 | *expanded = reg->alloc_mem(siz); |
| | 153 | |
| | 154 | /* return the register's buffer */ |
| | 155 | return reg->buf_; |
| | 156 | } |
| | 157 | |
| | 158 | /* our register array */ |
| | 159 | CVmBigNumCacheReg *reg_; |
| | 160 | |
| | 161 | /* head of free register list */ |
| | 162 | CVmBigNumCacheReg *free_reg_; |
| | 163 | |
| | 164 | /* head of unallocated register list */ |
| | 165 | CVmBigNumCacheReg *unalloc_reg_; |
| | 166 | |
| | 167 | /* constant register for ln(10) */ |
| | 168 | CVmBigNumCacheReg ln10_; |
| | 169 | |
| | 170 | /* constant register for pi */ |
| | 171 | CVmBigNumCacheReg pi_; |
| | 172 | |
| | 173 | /* constant register for e */ |
| | 174 | CVmBigNumCacheReg e_; |
| | 175 | |
| | 176 | /* maximum number of registers we can create */ |
| | 177 | size_t max_regs_; |
| | 178 | }; |
| | 179 | |
| | 180 | /* ------------------------------------------------------------------------ */ |
| | 181 | /* |
| | 182 | * We store a BigNumber value as a varying-length string of BCD-encoded |
| | 183 | * digits; we store two digits in each byte. Our bytes are stored from |
| | 184 | * most significant to least significant, and each byte has the more |
| | 185 | * significant half in the high part of the byte. |
| | 186 | * |
| | 187 | * UINT2 number_of_digits |
| | 188 | *. INT2 exponent |
| | 189 | *. BYTE flags |
| | 190 | *. BYTE most_significant_byte |
| | 191 | *. ... |
| | 192 | *. BYTE least_significant_byte |
| | 193 | * |
| | 194 | * Note that the number of bytes of the varying length mantissa string |
| | 195 | * is equal to (number_of_digits+1)/2, because one byte stores two |
| | 196 | * digits. |
| | 197 | * |
| | 198 | * The flags are: |
| | 199 | * |
| | 200 | * (flags & 0x0001) - sign bit; zero->non-negative, nonzero->negative |
| | 201 | * |
| | 202 | * (flags & 0x0006): |
| | 203 | *. 0x0000 -> normal number |
| | 204 | *. 0x0002 -> NOT A NUMBER |
| | 205 | *. 0x0004 -> INFINITY (sign bit indicates sign of infinity) |
| | 206 | *. 0x0006 -> reserved - should always be zero for now |
| | 207 | * |
| | 208 | * (flags & 0x0008) - zero bit; if set, the number's value is zero |
| | 209 | * |
| | 210 | * All other flag bits are reserved and should be set to zero. |
| | 211 | * |
| | 212 | * The exponent field gives the base-10 exponent of the number. This is |
| | 213 | * a signed quantity; a negative value indicates that the mantissa is to |
| | 214 | * be divided by (10 ^ abs(exponent)), and a positive value indicates |
| | 215 | * that the mantissa is to be multiplied by (10 ^ exponent). |
| | 216 | * |
| | 217 | * There is an implicit decimal point before the first byte of the |
| | 218 | * mantissa. |
| | 219 | */ |
| | 220 | |
| | 221 | /* byte offsets in byte string of various parts */ |
| | 222 | #define VMBN_PREC 0 |
| | 223 | #define VMBN_EXP 2 |
| | 224 | #define VMBN_FLAGS 4 |
| | 225 | #define VMBN_MANT 5 |
| | 226 | |
| | 227 | /* flags masks */ |
| | 228 | #define VMBN_F_NEG 0x0001 /* negative sign bit flag */ |
| | 229 | #define VMBN_F_TYPE_MASK 0x0006 /* number type mask */ |
| | 230 | #define VMBN_F_ZERO 0x0008 /* zero flag */ |
| | 231 | |
| | 232 | /* number types */ |
| | 233 | #define VMBN_T_NUM 0x0000 /* ordinary number */ |
| | 234 | #define VMBN_T_NAN 0x0002 /* NOT A NUMBER */ |
| | 235 | #define VMBN_T_INF 0x0004 /* INFINITY (negative or positive) */ |
| | 236 | #define VMBN_T_RSRVD 0x0006 /* reserved */ |
| | 237 | |
| | 238 | /* ------------------------------------------------------------------------ */ |
| | 239 | /* |
| | 240 | * Flags for cvt_to_string |
| | 241 | */ |
| | 242 | |
| | 243 | /* always show a sign, even if positive */ |
| | 244 | #define VMBN_FORMAT_SIGN 0x0001 |
| | 245 | |
| | 246 | /* always use exponential format */ |
| | 247 | #define VMBN_FORMAT_EXP 0x0002 |
| | 248 | |
| | 249 | /* always show a sign in the exponent */ |
| | 250 | #define VMBN_FORMAT_EXP_SIGN 0x0004 |
| | 251 | |
| | 252 | /* always show at least a zero before the decimal point */ |
| | 253 | #define VMBN_FORMAT_LEADING_ZERO 0x0008 |
| | 254 | |
| | 255 | /* always show a decimal point */ |
| | 256 | #define VMBN_FORMAT_POINT 0x0010 |
| | 257 | |
| | 258 | /* show the exponential 'e' (if any) in upper-case */ |
| | 259 | #define VMBN_FORMAT_EXP_CAP 0x0020 |
| | 260 | |
| | 261 | /* insert commas to denote thousands, millions, etc */ |
| | 262 | #define VMBN_FORMAT_COMMAS 0x0020 |
| | 263 | |
| | 264 | /* show a leading space if the number is positive */ |
| | 265 | #define VMBN_FORMAT_POS_SPACE 0x0040 |
| | 266 | |
| | 267 | /* use European-style formatting */ |
| | 268 | #define VMBN_FORMAT_EUROSTYLE 0x0080 |
| | 269 | |
| | 270 | /* ------------------------------------------------------------------------ */ |
| | 271 | /* |
| | 272 | * BigNumber metaclass - intrinsic function vector indices |
| | 273 | */ |
| | 274 | enum vmobjbn_meta_fnset |
| | 275 | { |
| | 276 | /* undefined function */ |
| | 277 | VMOBJBN_UNDEF = 0, |
| | 278 | |
| | 279 | /* format to a string */ |
| | 280 | VMOBJBN_FORMAT = 1, |
| | 281 | |
| | 282 | /* equal after rounding? */ |
| | 283 | VMOBJBN_EQUAL_RND = 2, |
| | 284 | |
| | 285 | /* getPrecision */ |
| | 286 | VMOBJBN_GET_PREC = 3, |
| | 287 | |
| | 288 | /* setPrecision */ |
| | 289 | VMOBJBN_SET_PREC = 4, |
| | 290 | |
| | 291 | /* getFraction */ |
| | 292 | VMOBJBN_FRAC = 5, |
| | 293 | |
| | 294 | /* getWhole */ |
| | 295 | VMOBJBN_WHOLE = 6, |
| | 296 | |
| | 297 | /* round to a given number of decimal places */ |
| | 298 | VMOBJBN_ROUND_DEC = 7, |
| | 299 | |
| | 300 | /* absolute value */ |
| | 301 | VMOBJBN_ABS = 8, |
| | 302 | |
| | 303 | /* ceiling */ |
| | 304 | VMOBJBN_CEIL = 9, |
| | 305 | |
| | 306 | /* floor */ |
| | 307 | VMOBJBN_FLOOR = 10, |
| | 308 | |
| | 309 | /* getScale */ |
| | 310 | VMOBJBN_GETSCALE = 11, |
| | 311 | |
| | 312 | /* scale */ |
| | 313 | VMOBJBN_SCALE = 12, |
| | 314 | |
| | 315 | /* negate */ |
| | 316 | VMOBJBN_NEGATE = 13, |
| | 317 | |
| | 318 | /* copySignFrom */ |
| | 319 | VMOBJBN_COPYSIGN = 14, |
| | 320 | |
| | 321 | /* isNegative */ |
| | 322 | VMOBJBN_ISNEG = 15, |
| | 323 | |
| | 324 | /* getRemainder */ |
| | 325 | VMOBJBN_REMAINDER = 16, |
| | 326 | |
| | 327 | /* sine */ |
| | 328 | VMOBJBN_SIN = 17, |
| | 329 | |
| | 330 | /* cosine */ |
| | 331 | VMOBJBN_COS = 18, |
| | 332 | |
| | 333 | /* sine */ |
| | 334 | VMOBJBN_TAN = 19, |
| | 335 | |
| | 336 | /* degreesToRadians */ |
| | 337 | VMOBJBN_D2R = 20, |
| | 338 | |
| | 339 | /* radiansToDegrees */ |
| | 340 | VMOBJBN_R2D = 21, |
| | 341 | |
| | 342 | /* arcsine */ |
| | 343 | VMOBJBN_ASIN = 22, |
| | 344 | |
| | 345 | /* arccosine */ |
| | 346 | VMOBJBN_ACOS = 23, |
| | 347 | |
| | 348 | /* arctangent */ |
| | 349 | VMOBJBN_ATAN = 24, |
| | 350 | |
| | 351 | /* sqrt */ |
| | 352 | VMOBJBN_SQRT = 25, |
| | 353 | |
| | 354 | /* natural log */ |
| | 355 | VMOBJBN_LN = 26, |
| | 356 | |
| | 357 | /* exp */ |
| | 358 | VMOBJBN_EXP = 27, |
| | 359 | |
| | 360 | /* log10 */ |
| | 361 | VMOBJBN_LOG10 = 28, |
| | 362 | |
| | 363 | /* power */ |
| | 364 | VMOBJBN_POW = 29, |
| | 365 | |
| | 366 | /* hyperbolic sine */ |
| | 367 | VMOBJBN_SINH = 30, |
| | 368 | |
| | 369 | /* hyperbolic cosine */ |
| | 370 | VMOBJBN_COSH = 31, |
| | 371 | |
| | 372 | /* hyperbolic tangent */ |
| | 373 | VMOBJBN_TANH = 32, |
| | 374 | |
| | 375 | /* get pi (static method) */ |
| | 376 | VMOBJBN_GET_PI = 33, |
| | 377 | |
| | 378 | /* get e (static method) */ |
| | 379 | VMOBJBN_GET_E = 34 |
| | 380 | }; |
| | 381 | |
| | 382 | /* ------------------------------------------------------------------------ */ |
| | 383 | /* |
| | 384 | * Big Number metaclass |
| | 385 | */ |
| | 386 | class CVmObjBigNum: public CVmObject |
| | 387 | { |
| | 388 | friend class CVmMetaclassBigNum; |
| | 389 | |
| | 390 | public: |
| | 391 | /* metaclass registration object */ |
| | 392 | static class CVmMetaclass *metaclass_reg_; |
| | 393 | class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } |
| | 394 | |
| | 395 | /* am I of the given metaclass? */ |
| | 396 | virtual int is_of_metaclass(class CVmMetaclass *meta) const |
| | 397 | { |
| | 398 | /* try my own metaclass and my base class */ |
| | 399 | return (meta == metaclass_reg_ |
| | 400 | || CVmObject::is_of_metaclass(meta)); |
| | 401 | } |
| | 402 | |
| | 403 | /* |
| | 404 | * write to a 'data' mode file - returns zero on success, non-zero on |
| | 405 | * I/O or other error |
| | 406 | */ |
| | 407 | int write_to_data_file(osfildef *fp); |
| | 408 | |
| | 409 | /* |
| | 410 | * Read from a 'data' mode file, creating a new BigNumber object to |
| | 411 | * hold the result. Returns zero on success, non-zero on failure. On |
| | 412 | * success, *retval will be filled in with the new BigNumber object. |
| | 413 | */ |
| | 414 | static int read_from_data_file(VMG_ vm_val_t *retval, osfildef *fp); |
| | 415 | |
| | 416 | /* create dynamically using stack arguments */ |
| | 417 | static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, |
| | 418 | uint argc); |
| | 419 | |
| | 420 | /* call a static property */ |
| | 421 | static int call_stat_prop(VMG_ vm_val_t *result, |
| | 422 | const uchar **pc_ptr, uint *argc, |
| | 423 | vm_prop_id_t prop); |
| | 424 | |
| | 425 | /* reserve constant data */ |
| | 426 | virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, |
| | 427 | vm_obj_id_t self); |
| | 428 | |
| | 429 | /* convert to constant data */ |
| | 430 | virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, |
| | 431 | vm_obj_id_t self); |
| | 432 | |
| | 433 | /* create with a given precision */ |
| | 434 | static vm_obj_id_t create(VMG_ int in_root_set, size_t digits); |
| | 435 | |
| | 436 | /* create with a given value */ |
| | 437 | static vm_obj_id_t create(VMG_ int in_root_set, long val, size_t digits); |
| | 438 | |
| | 439 | /* determine if an object is a BigNumber */ |
| | 440 | static int is_bignum_obj(VMG_ vm_obj_id_t obj) |
| | 441 | { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } |
| | 442 | |
| | 443 | /* notify of deletion */ |
| | 444 | void notify_delete(VMG_ int in_root_set); |
| | 445 | |
| | 446 | /* set a property */ |
| | 447 | void set_prop(VMG_ class CVmUndo *undo, |
| | 448 | vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); |
| | 449 | |
| | 450 | /* get a property */ |
| | 451 | int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, |
| | 452 | vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); |
| | 453 | |
| | 454 | /* undo operations - we are immutable and hence keep no undo */ |
| | 455 | void notify_new_savept() { } |
| | 456 | void apply_undo(VMG_ struct CVmUndoRecord *) { } |
| | 457 | void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } |
| | 458 | void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } |
| | 459 | |
| | 460 | /* mark references - we have no references so this does nothing */ |
| | 461 | void mark_refs(VMG_ uint) { } |
| | 462 | |
| | 463 | /* remove weak references */ |
| | 464 | void remove_stale_weak_refs(VMG0_) { } |
| | 465 | |
| | 466 | /* load from an image file */ |
| | 467 | void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) |
| | 468 | { ext_ = (char *)ptr; } |
| | 469 | |
| | 470 | /* rebuild for image file */ |
| | 471 | virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); |
| | 472 | |
| | 473 | /* save to a file */ |
| | 474 | void save_to_file(VMG_ class CVmFile *fp); |
| | 475 | |
| | 476 | /* restore from a file */ |
| | 477 | void restore_from_file(VMG_ vm_obj_id_t self, |
| | 478 | class CVmFile *fp, class CVmObjFixup *fixup); |
| | 479 | |
| | 480 | /* add a value */ |
| | 481 | void add_val(VMG_ vm_val_t *result, |
| | 482 | vm_obj_id_t self, const vm_val_t *val); |
| | 483 | |
| | 484 | /* subtract a value */ |
| | 485 | void sub_val(VMG_ vm_val_t *result, |
| | 486 | vm_obj_id_t self, const vm_val_t *val); |
| | 487 | |
| | 488 | /* multiply */ |
| | 489 | void mul_val(VMG_ vm_val_t *result, |
| | 490 | vm_obj_id_t self, const vm_val_t *val); |
| | 491 | |
| | 492 | /* divide */ |
| | 493 | void div_val(VMG_ vm_val_t *result, |
| | 494 | vm_obj_id_t self, const vm_val_t *val); |
| | 495 | |
| | 496 | /* negate */ |
| | 497 | void neg_val(VMG_ vm_val_t *result, vm_obj_id_t self); |
| | 498 | |
| | 499 | /* check a value for equality */ |
| | 500 | int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; |
| | 501 | |
| | 502 | /* calculate a hash value for the number */ |
| | 503 | uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; |
| | 504 | |
| | 505 | /* compare to another value */ |
| | 506 | int compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *) const; |
| | 507 | |
| | 508 | /* |
| | 509 | * Create a string representation of the number. We'll use a |
| | 510 | * default format that uses no more than a maximum number of |
| | 511 | * characters to represent the string. We'll avoid exponential |
| | 512 | * format if possible, but we'll fall back on exponential format if |
| | 513 | * the non-exponential result would exceed our default maximum |
| | 514 | * length. |
| | 515 | */ |
| | 516 | virtual const char *cast_to_string(VMG_ vm_obj_id_t self, |
| | 517 | vm_val_t *new_str) const; |
| | 518 | |
| | 519 | /* |
| | 520 | * Static method to convert big number data to a string. We'll |
| | 521 | * create a new string object and store a reference in new_str, |
| | 522 | * returning a pointer to its data buffer. |
| | 523 | * |
| | 524 | * max_digits is the maximum number of digits we should produce. If |
| | 525 | * our precision is greater than this would allow, we'll round. If |
| | 526 | * we have more digits before the decimal point than this would |
| | 527 | * allow, we'll use exponential notation. |
| | 528 | * |
| | 529 | * whole_places is the number of places before the decimal point |
| | 530 | * that we should produce. This is a minimum; if we need more |
| | 531 | * places (and we're not in exponential notation), we'll take the |
| | 532 | * additional places. If, however, we don't manage to fill this |
| | 533 | * quota, we'll pad with spaces to the left. We ignore whole_places |
| | 534 | * in exponential format. |
| | 535 | * |
| | 536 | * frac_digits is the number of digits after the decimal point that |
| | 537 | * we should produce. We'll round if we have more precision than |
| | 538 | * this would allow, or pad with zeroes if we don't have enough |
| | 539 | * precision. If frac_digits is -1, we will produce as many |
| | 540 | * fractional digits as we need up to the max_digits limit. |
| | 541 | * |
| | 542 | * If the VMBN_FORMAT_EXP flag isn't set, we'll format the number |
| | 543 | * without an exponent as long as we have enough space in max_digits |
| | 544 | * for the part before the decimal point, and we have enough space |
| | 545 | * in max_digits and frac_digits that a number with a small absolute |
| | 546 | * value wouldn't show up as all zeroes. |
| | 547 | * |
| | 548 | * If the VMBN_FORMAT_POINT flag is set, we'll show a decimal point |
| | 549 | * for all numbers. Otherwise, if frac_digits is zero, or |
| | 550 | * frac_digits is -1 and the number has no fractional part, we'll |
| | 551 | * suppress the decimal point. This doesn't matter when frac_digits |
| | 552 | * is greater than zero, or it's -1 and there's a fractional part to |
| | 553 | * display. |
| | 554 | * |
| | 555 | * If exp_digits is non-zero, it specifies the minimum number of |
| | 556 | * digits to display in the exponent. We'll pad with zeroes to make |
| | 557 | * this many digits if necessary. |
| | 558 | * |
| | 559 | * If lead_fill is provided, it must be a string value. We'll fill |
| | 560 | * the string with the characters from this string, looping to |
| | 561 | * repeat the string if necessary. If this string isn't provided, |
| | 562 | * we'll use leading spaces. This is only needed if the |
| | 563 | * whole_places value requires us to insert filler. |
| | 564 | */ |
| | 565 | static const char *cvt_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str, |
| | 566 | const char *ext, |
| | 567 | int max_digits, int whole_places, |
| | 568 | int frac_digits, int exp_digits, |
| | 569 | ulong flags, vm_val_t *lead_fill); |
| | 570 | |
| | 571 | /* format the value into the given buffer */ |
| | 572 | char *cvt_to_string_buf(VMG_ char *buf, size_t buflen, int max_digits, |
| | 573 | int whole_places, int frac_digits, int exp_digits, |
| | 574 | ulong flags); |
| | 575 | |
| | 576 | /* compute a sum */ |
| | 577 | static void compute_sum(VMG_ vm_val_t *result, |
| | 578 | const char *ext1, const char *ext2); |
| | 579 | |
| | 580 | /* compute a difference */ |
| | 581 | static void compute_diff(VMG_ vm_val_t *result, |
| | 582 | const char *ext1, const char *ext2); |
| | 583 | |
| | 584 | /* compute a product */ |
| | 585 | static void compute_prod(VMG_ vm_val_t *result, |
| | 586 | const char *ext1, const char *ext2); |
| | 587 | |
| | 588 | /* compute a quotient */ |
| | 589 | static void compute_quotient(VMG_ vm_val_t *result, |
| | 590 | const char *ext1, const char *ext2); |
| | 591 | |
| | 592 | /* compute a remainder */ |
| | 593 | static void compute_rem(VMG_ vm_val_t *result, |
| | 594 | const char *ext1, const char *ext2); |
| | 595 | |
| | 596 | /* compute a natural logarithm */ |
| | 597 | static void compute_ln_into(VMG_ char *dst, const char *src); |
| | 598 | |
| | 599 | /* compute e^x */ |
| | 600 | static void compute_exp_into(VMG_ char *dst, const char *src); |
| | 601 | |
| | 602 | /* compute a hyperbolic sine or cosine */ |
| | 603 | static void compute_sinhcosh_into(VMG_ char *dst, const char *src, |
| | 604 | int is_cosh, int is_tanh); |
| | 605 | |
| | 606 | /* |
| | 607 | * Determine if two values are exactly equal. If one value has more |
| | 608 | * precision than the other, we'll implicitly extend the shorter |
| | 609 | * value with trailing zeroes. |
| | 610 | */ |
| | 611 | static int compute_eq_exact(const char *ext1, const char *ext2); |
| | 612 | |
| | 613 | /* |
| | 614 | * Determine if two values are equal with rounding. If one value is |
| | 615 | * less precise than the other, we'll round the more precise value |
| | 616 | * to the shorter precision, and compare the shorter number to the |
| | 617 | * rounded longer number. |
| | 618 | */ |
| | 619 | static int compute_eq_round(VMG_ const char *ext1, const char *ext2); |
| | 620 | |
| | 621 | /* |
| | 622 | * Create a rounded value, rounding to the given precision. If |
| | 623 | * always_create is true, we'll create a new number regardless of |
| | 624 | * whether rounding is required; otherwise, when the caller can |
| | 625 | * simply treat the old value as truncated, we'll set new_val to nil |
| | 626 | * and return the original value. |
| | 627 | */ |
| | 628 | static const char *round_val(VMG_ vm_val_t *new_val, const char *ext, |
| | 629 | size_t digits, int always_create); |
| | 630 | |
| | 631 | /* set my value to a given integer value */ |
| | 632 | void set_int_val(long val); |
| | 633 | |
| | 634 | /* set my value to a given string */ |
| | 635 | void set_str_val(const char *str, size_t len); |
| | 636 | |
| | 637 | /* get my data pointer */ |
| | 638 | char *get_ext() const { return ext_; } |
| | 639 | |
| | 640 | /* convert to an integer value */ |
| | 641 | long convert_to_int(); |
| | 642 | |
| | 643 | protected: |
| | 644 | /* create with no extension */ |
| | 645 | CVmObjBigNum(); |
| | 646 | |
| | 647 | /* create with a given precision */ |
| | 648 | CVmObjBigNum(VMG_ size_t digits); |
| | 649 | |
| | 650 | /* create with a given precision, initializing with an integer value */ |
| | 651 | CVmObjBigNum(VMG_ long val, size_t digits); |
| | 652 | |
| | 653 | /* create with a given precision, initializing with a string value */ |
| | 654 | CVmObjBigNum(VMG_ const char *str, size_t len, size_t digits); |
| | 655 | |
| | 656 | /* convert a value to a BigNumber */ |
| | 657 | int cvt_to_bignum(VMG_ vm_obj_id_t self, vm_val_t *val) const; |
| | 658 | |
| | 659 | /* |
| | 660 | * general string conversion routine - converts to a string, storing |
| | 661 | * the result either in the caller's buffer, or in a new string |
| | 662 | * created for the conversion |
| | 663 | */ |
| | 664 | static char *cvt_to_string_gen(VMG_ vm_val_t *new_str, |
| | 665 | const char *ext, |
| | 666 | int max_digits, int whole_places, |
| | 667 | int frac_digits, int exp_digits, |
| | 668 | ulong flags, vm_val_t *lead_fill, |
| | 669 | char *buf, size_t buflen); |
| | 670 | |
| | 671 | /* property evaluator - undefined property */ |
| | 672 | int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } |
| | 673 | |
| | 674 | /* property evaluator - formatString */ |
| | 675 | int getp_format(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 676 | |
| | 677 | /* property evaluator - equalRound */ |
| | 678 | int getp_equal_rnd(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 679 | |
| | 680 | /* property evaluator - getPrecision */ |
| | 681 | int getp_get_prec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 682 | |
| | 683 | /* property evaluator - setPrecision */ |
| | 684 | int getp_set_prec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 685 | |
| | 686 | /* property evaluator - getFraction */ |
| | 687 | int getp_frac(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 688 | |
| | 689 | /* property evaluator - getWhole */ |
| | 690 | int getp_whole(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 691 | |
| | 692 | /* property evaluator - roundToDecimal */ |
| | 693 | int getp_round_dec(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 694 | |
| | 695 | /* property evaluator - absolute value */ |
| | 696 | int getp_abs(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 697 | |
| | 698 | /* property evaluator - ceiling */ |
| | 699 | int getp_ceil(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 700 | |
| | 701 | /* property evaluator - floor */ |
| | 702 | int getp_floor(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 703 | |
| | 704 | /* property evaluator - getScale */ |
| | 705 | int getp_get_scale(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 706 | |
| | 707 | /* property evaluator - scale */ |
| | 708 | int getp_scale(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 709 | |
| | 710 | /* property evaluator - negate */ |
| | 711 | int getp_negate(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 712 | |
| | 713 | /* property evaluator - copySignFrom */ |
| | 714 | int getp_copy_sign(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 715 | |
| | 716 | /* property evaluator - isNegative */ |
| | 717 | int getp_is_neg(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 718 | |
| | 719 | /* property evaluator - remainder */ |
| | 720 | int getp_remainder(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 721 | |
| | 722 | /* property evaluator - sine */ |
| | 723 | int getp_sin(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 724 | |
| | 725 | /* property evaluator - cosine */ |
| | 726 | int getp_cos(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 727 | |
| | 728 | /* property evaluator - tangent */ |
| | 729 | int getp_tan(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 730 | |
| | 731 | /* property evaluator - radiansToDegrees */ |
| | 732 | int getp_rad2deg(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 733 | |
| | 734 | /* property evaluator - degreesToRadians */ |
| | 735 | int getp_deg2rad(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 736 | |
| | 737 | /* property evaluator - arcsine */ |
| | 738 | int getp_asin(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 739 | |
| | 740 | /* property evaluator - arccosine */ |
| | 741 | int getp_acos(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 742 | |
| | 743 | /* property evaluator - arcsine */ |
| | 744 | int getp_atan(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 745 | |
| | 746 | /* property evaluator - square root */ |
| | 747 | int getp_sqrt(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 748 | |
| | 749 | /* property evaluator - natural log */ |
| | 750 | int getp_ln(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 751 | |
| | 752 | /* property evaluator - exp */ |
| | 753 | int getp_exp(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 754 | |
| | 755 | /* property evaluator - log10 */ |
| | 756 | int getp_log10(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 757 | |
| | 758 | /* property evaluator - power */ |
| | 759 | int getp_pow(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 760 | |
| | 761 | /* property evaluator - hyperbolic sine */ |
| | 762 | int getp_sinh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 763 | |
| | 764 | /* property evaluator - hyperbolic cosine */ |
| | 765 | int getp_cosh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 766 | |
| | 767 | /* property evaluator - hyperbolic tangent */ |
| | 768 | int getp_tanh(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 769 | |
| | 770 | /* property evaluator - get pi */ |
| | 771 | int getp_pi(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) |
| | 772 | { return s_getp_pi(vmg_ val, argc); } |
| | 773 | |
| | 774 | /* property evaluator - get e */ |
| | 775 | int getp_e(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) |
| | 776 | { return s_getp_e(vmg_ val, argc); } |
| | 777 | |
| | 778 | /* static property evaluator - get pi */ |
| | 779 | static int s_getp_pi(VMG_ vm_val_t *val, uint *argc); |
| | 780 | |
| | 781 | /* static property evaluator - get e */ |
| | 782 | static int s_getp_e(VMG_ vm_val_t *val, uint *argc); |
| | 783 | |
| | 784 | /* set up for a getp operation with zero arguments */ |
| | 785 | int setup_getp_0(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 786 | uint *argc, char **new_ext); |
| | 787 | |
| | 788 | /* set up for a getp operation with one argument */ |
| | 789 | int setup_getp_1(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 790 | uint *argc, char **new_ext, |
| | 791 | vm_val_t *val2, const char **val2_ext, |
| | 792 | int use_self_prec); |
| | 793 | |
| | 794 | /* set up a return value for a getp operation */ |
| | 795 | int setup_getp_retval(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 796 | char **new_ext, size_t prec); |
| | 797 | |
| | 798 | /* common property evaluator for asin and acos */ |
| | 799 | int calc_asincos(VMG_ vm_obj_id_t self, |
| | 800 | vm_val_t *retval, uint *argc, int is_acos); |
| | 801 | |
| | 802 | /* common property evaluator - sinh, cosh, and tanh */ |
| | 803 | int calc_sinhcosh(VMG_ vm_obj_id_t self, |
| | 804 | vm_val_t *retval, uint *argc, |
| | 805 | int is_cosh, int is_tanh); |
| | 806 | |
| | 807 | /* calculate asin or acos into the given buffer */ |
| | 808 | static void calc_asincos_into(VMG_ char *new_ext, const char *ext, |
| | 809 | int is_acos); |
| | 810 | |
| | 811 | /* |
| | 812 | * Calculate the arcsin series expansion; valid only for small |
| | 813 | * values of x (0 < x < 1/sqrt(2)). The argument value is in ext1, |
| | 814 | * and we return a pointer to the register containing the result. |
| | 815 | */ |
| | 816 | static char *calc_asin_series(char *ext1, char *ext2, |
| | 817 | char *ext3, char *ext4, char *ext5); |
| | 818 | |
| | 819 | /* |
| | 820 | * Compute the ln series expansion. This is valid only for small |
| | 821 | * arguments; the argument is in ext1 initially. Returns a pointer |
| | 822 | * to the register containing the result |
| | 823 | */ |
| | 824 | static char *compute_ln_series_into(VMG_ char *ext1, char *ext2, |
| | 825 | char *ext3, char *ext4, char *ext5); |
| | 826 | |
| | 827 | /* allocate space for a given number of decimal digits */ |
| | 828 | void alloc_bignum(VMG_ size_t digits); |
| | 829 | |
| | 830 | /* calculate how much space we need for a given number of digits */ |
| | 831 | static size_t calc_alloc(size_t digits); |
| | 832 | |
| | 833 | /* initialize a computation for a two-operand operator */ |
| | 834 | static char *compute_init_2op(VMG_ vm_val_t *result, |
| | 835 | const char *ext1, const char *ext2); |
| | 836 | |
| | 837 | /* compute a square root */ |
| | 838 | static void compute_sqrt_into(VMG_ char *new_ext, const char *ext); |
| | 839 | |
| | 840 | /* compute the sum of two operands into the given buffer */ |
| | 841 | static void compute_sum_into(char *new_next, |
| | 842 | const char *ext1, const char *ext2); |
| | 843 | |
| | 844 | /* |
| | 845 | * Compute the sum of the absolute values of the operands into the |
| | 846 | * given buffer. The result is always positive. The result buffer |
| | 847 | * must have a precision at least as large as the larger of the two |
| | 848 | * input precisions. |
| | 849 | */ |
| | 850 | static void compute_abs_sum_into(char *new_ext, |
| | 851 | const char *ext1, const char *ext2); |
| | 852 | |
| | 853 | /* |
| | 854 | * Compute the difference of the absolute values of the operands |
| | 855 | * into the given buffer. The result is positive if the first value |
| | 856 | * is larger than the second, negative if the first value is smaller |
| | 857 | * than the second. The result buffer must have precision at least |
| | 858 | * as large as the larger of the two input precisions. |
| | 859 | */ |
| | 860 | static void compute_abs_diff_into(char *new_ext, |
| | 861 | const char *ext1, const char *ext2); |
| | 862 | |
| | 863 | /* |
| | 864 | * Compute the product of the two values into the given buffer. The |
| | 865 | * result buffer must have precision at least as large as the larger |
| | 866 | * of the two input precisions. |
| | 867 | */ |
| | 868 | static void compute_prod_into(char *new_ext, |
| | 869 | const char *ext1, const char *ext2); |
| | 870 | |
| | 871 | /* |
| | 872 | * Compute the quotient of th etwo values into the given buffer If |
| | 873 | * new_rem_ext is not null, we'll store the remainder there. |
| | 874 | */ |
| | 875 | static void compute_quotient_into(VMG_ char *new_ext, |
| | 876 | char *new_rem_ext, |
| | 877 | const char *ext1, const char *ext2); |
| | 878 | |
| | 879 | /* |
| | 880 | * Compare the absolute values of two numbers. If the first is |
| | 881 | * greater than the second, we'll return a positive result. If the |
| | 882 | * two are equal, we'll return zero. If the first is less than the |
| | 883 | * second, we'll return a negative result. This routine ignores NAN |
| | 884 | * and INF values, so the caller must ensure that only ordinary |
| | 885 | * numbers are passed to this routine. |
| | 886 | */ |
| | 887 | static int compare_abs(const char *ext1, const char *ext2); |
| | 888 | |
| | 889 | /* get/set the digit precision */ |
| | 890 | static size_t get_prec(const char *ext) |
| | 891 | { return osrp2(ext + VMBN_PREC); } |
| | 892 | static void set_prec(char *ext, size_t prec) |
| | 893 | { oswp2(ext + VMBN_PREC, prec); } |
| | 894 | |
| | 895 | /* get/set the exponent */ |
| | 896 | static int get_exp(const char *ext) |
| | 897 | { return osrp2s(ext + VMBN_EXP); } |
| | 898 | static void set_exp(char *ext, int exp) |
| | 899 | { oswp2(ext + VMBN_EXP, exp); } |
| | 900 | |
| | 901 | /* get the negative sign flag */ |
| | 902 | static int get_neg(const char *ext) |
| | 903 | { return (ext[VMBN_FLAGS] & VMBN_F_NEG) != 0; } |
| | 904 | |
| | 905 | /* set/clear negative sign flag */ |
| | 906 | static void set_neg(char *ext, int neg) |
| | 907 | { |
| | 908 | if (neg) |
| | 909 | ext[VMBN_FLAGS] |= VMBN_F_NEG; |
| | 910 | else |
| | 911 | ext[VMBN_FLAGS] &= ~VMBN_F_NEG; |
| | 912 | } |
| | 913 | |
| | 914 | /* get the number type */ |
| | 915 | static int get_type(const char *ext) |
| | 916 | { return ext[VMBN_FLAGS] & VMBN_F_TYPE_MASK; } |
| | 917 | |
| | 918 | /* set the number type (to a VMBN_T_xxx value) */ |
| | 919 | static void set_type(char *ext, int typ) |
| | 920 | { |
| | 921 | /* clear the old number type */ |
| | 922 | ext[VMBN_FLAGS] &= ~VMBN_F_TYPE_MASK; |
| | 923 | |
| | 924 | /* set the new number type */ |
| | 925 | ext[VMBN_FLAGS] |= typ; |
| | 926 | } |
| | 927 | |
| | 928 | /* get a digit at a particular index (0 = most significant) */ |
| | 929 | static unsigned int get_dig(const char *ext, size_t i) |
| | 930 | { |
| | 931 | unsigned int pair; |
| | 932 | |
| | 933 | /* get the digit pair containing our digit */ |
| | 934 | pair = ext[VMBN_MANT + i/2]; |
| | 935 | |
| | 936 | /* |
| | 937 | * If it's an even index, we need the high half. Otherwise, we |
| | 938 | * need the low half. |
| | 939 | * |
| | 940 | * This is a bit tricky, all to avoid a condition branch. If |
| | 941 | * the index is even, (i & 1) will be 0, otherwise (i & 1) will |
| | 942 | * be 1. So, (1 - (i & 1)) will be 1 if even, 0 if odd. That |
| | 943 | * quantity shifted left twice will hence be 4 if the index is |
| | 944 | * even, 0 if the index is odd. Thus, we'll shift the pair |
| | 945 | * right by 4 if the index is even, yielding the high part, or |
| | 946 | * shift right by 0 if the index is odd, keeping the low part. |
| | 947 | */ |
| | 948 | pair >>= ((1 - (i & 1)) << 2); |
| | 949 | |
| | 950 | /* mask to one digit */ |
| | 951 | return (pair & 0x0f); |
| | 952 | } |
| | 953 | |
| | 954 | /* set a digit at a particular index */ |
| | 955 | static void set_dig(char *ext, size_t i, unsigned int dig) |
| | 956 | { |
| | 957 | unsigned char mask; |
| | 958 | |
| | 959 | /* make sure our input digit is just a digit */ |
| | 960 | dig &= 0x0F; |
| | 961 | |
| | 962 | /* |
| | 963 | * If it's an even index, we need to store our digit in the high |
| | 964 | * half. Otherwise, we need to store it in the low half. So, |
| | 965 | * if we're storing in an even index, shift our number left 4 |
| | 966 | * bits so that it's in the high half of its low byte; |
| | 967 | * otherwise, leave the number as-is. |
| | 968 | */ |
| | 969 | dig <<= ((1 - (i & 1)) << 2); |
| | 970 | |
| | 971 | /* |
| | 972 | * We need a mask that we can AND the current value with to |
| | 973 | * preserve the half we're not changing, but clear the other |
| | 974 | * half. So, we need 0x0F if we're setting the high half (even |
| | 975 | * index), or 0xF0 if we're setting the low half (odd index). |
| | 976 | * Use the same trick as above, with the shift sense inverted, |
| | 977 | * so generate our mask. |
| | 978 | */ |
| | 979 | mask = (0x0F << ((i & 1) << 2)); |
| | 980 | |
| | 981 | /* mask out our part from the pair */ |
| | 982 | ext[VMBN_MANT + i/2] &= mask; |
| | 983 | |
| | 984 | /* OR in our digit now that we've masked the place clear */ |
| | 985 | ext[VMBN_MANT + i/2] |= (unsigned char)dig; |
| | 986 | } |
| | 987 | |
| | 988 | /* shift mantissa left/right, leaving the exponent unchanged */ |
| | 989 | static void shift_left(char *ext, unsigned int shift); |
| | 990 | static void shift_right(char *ext, unsigned int shift); |
| | 991 | |
| | 992 | /* multiply a number by a long integer value */ |
| | 993 | static void mul_by_long(char *ext, unsigned long val); |
| | 994 | |
| | 995 | /* divide a number by a long integer value */ |
| | 996 | static void div_by_long(char *ext, unsigned long val); |
| | 997 | |
| | 998 | /* increment a number's absolute value */ |
| | 999 | static void increment_abs(char *ext); |
| | 1000 | |
| | 1001 | /* round a number's value up - increments the least significant digit */ |
| | 1002 | static void round_up_abs(char *ext); |
| | 1003 | |
| | 1004 | /* |
| | 1005 | * copy a value - if the new value has greater precision than the |
| | 1006 | * old value, we'll extend with zeroes in the new least significance |
| | 1007 | * digits; if the new value has smaller precision than the old |
| | 1008 | * value, and 'round' is false, we'll simply truncate the value to |
| | 1009 | * the new precision. If 'round' is true and we're reducing the |
| | 1010 | * precision, we'll round up the value instead of truncating it. |
| | 1011 | */ |
| | 1012 | static void copy_val(char *dst, const char *src, int round); |
| | 1013 | |
| | 1014 | /* normalize a number */ |
| | 1015 | static void normalize(char *ext); |
| | 1016 | |
| | 1017 | /* set a number to zero */ |
| | 1018 | static void set_zero(char *ext) |
| | 1019 | { |
| | 1020 | /* set the exponent to one */ |
| | 1021 | set_exp(ext, 1); |
| | 1022 | |
| | 1023 | /* set the zero flag */ |
| | 1024 | ext[VMBN_FLAGS] |= VMBN_F_ZERO; |
| | 1025 | |
| | 1026 | /* set the sign to non-negative */ |
| | 1027 | set_neg(ext, FALSE); |
| | 1028 | |
| | 1029 | /* set the type to ordinary number */ |
| | 1030 | set_type(ext, VMBN_T_NUM); |
| | 1031 | |
| | 1032 | /* set the mantissa to all zeroes */ |
| | 1033 | memset(ext + VMBN_MANT, 0, (get_prec(ext) + 1)/2); |
| | 1034 | } |
| | 1035 | |
| | 1036 | /* determine if the number equals zero */ |
| | 1037 | static int is_zero(const char *ext) |
| | 1038 | { return (ext[VMBN_FLAGS] & VMBN_F_ZERO) != 0; } |
| | 1039 | |
| | 1040 | /* negate a value */ |
| | 1041 | static void negate(char *ext) |
| | 1042 | { |
| | 1043 | /* only change the sign if the value is non-zero */ |
| | 1044 | if (!is_zero(ext)) |
| | 1045 | { |
| | 1046 | /* it's not zero - invert the sign */ |
| | 1047 | set_neg(ext, !get_neg(ext)); |
| | 1048 | } |
| | 1049 | } |
| | 1050 | |
| | 1051 | /* make a value negative */ |
| | 1052 | static void make_negative(char *ext) |
| | 1053 | { |
| | 1054 | /* only set the sign if the value is non-zero */ |
| | 1055 | if (!is_zero(ext)) |
| | 1056 | set_neg(ext, TRUE); |
| | 1057 | } |
| | 1058 | |
| | 1059 | /* check to see if the fractional part is zero */ |
| | 1060 | static int is_frac_zero(const char *ext); |
| | 1061 | |
| | 1062 | /* |
| | 1063 | * check for NAN and INF conditions - returns true if the number is |
| | 1064 | * a NAN or INF, false if it's an ordinary number |
| | 1065 | */ |
| | 1066 | static int is_nan(const char *ext) |
| | 1067 | { |
| | 1068 | /* if it's anything but an ordinary number, indicate NAN */ |
| | 1069 | return (get_type(ext) != VMBN_T_NUM); |
| | 1070 | } |
| | 1071 | |
| | 1072 | /* calculate a Taylor series for sin(x) */ |
| | 1073 | void calc_sin_series(VMG_ char *new_ext, char *ext1, char *ext2, |
| | 1074 | char *ext3, char *ext4, char *ext5, |
| | 1075 | char *ext6, char *ext7); |
| | 1076 | |
| | 1077 | /* calculate a Taylor series for cos(x) */ |
| | 1078 | void calc_cos_series(VMG_ char *new_ext, char *ext1, char *ext2, |
| | 1079 | char *ext3, char *ext4, char *ext5, |
| | 1080 | char *ext6, char *ext7); |
| | 1081 | |
| | 1082 | /* |
| | 1083 | * given an object number known to refer to a CVmBigNum object, get |
| | 1084 | * the object's extension |
| | 1085 | */ |
| | 1086 | static char *get_objid_ext(VMG_ vm_obj_id_t obj_id) |
| | 1087 | { |
| | 1088 | /* get the object pointer, cast it, and get the extension */ |
| | 1089 | return get_objid_obj(vmg_ obj_id)->get_ext(); |
| | 1090 | } |
| | 1091 | |
| | 1092 | /* |
| | 1093 | * given an object number known to refer to a CVmBigNum object, get |
| | 1094 | * the object pointer |
| | 1095 | */ |
| | 1096 | static CVmObjBigNum *get_objid_obj(VMG_ vm_obj_id_t obj_id) |
| | 1097 | { |
| | 1098 | /* get the object pointer and cast it */ |
| | 1099 | return (CVmObjBigNum *)vm_objp(vmg_ obj_id); |
| | 1100 | } |
| | 1101 | |
| | 1102 | /* allocate a temporary register */ |
| | 1103 | static char *alloc_temp_reg(VMG_ size_t prec, uint *hdl); |
| | 1104 | |
| | 1105 | /* |
| | 1106 | * Allocate a set of temporary registers; throws an error on |
| | 1107 | * failure. For each register, there is an additional pair of |
| | 1108 | * arguments: a (char **) to receive a pointer to the register |
| | 1109 | * memory, and a (uint *) to receive the register handle. |
| | 1110 | */ |
| | 1111 | static void alloc_temp_regs(VMG_ size_t prec, size_t cnt, ...); |
| | 1112 | |
| | 1113 | /* |
| | 1114 | * Release a set of temporary registers. For each register, there |
| | 1115 | * is a uint argument giving the handle of the register to release. |
| | 1116 | */ |
| | 1117 | static void release_temp_regs(VMG_ size_t cnt, ...); |
| | 1118 | |
| | 1119 | /* release a temporary register */ |
| | 1120 | static void release_temp_reg(VMG_ uint hdl); |
| | 1121 | |
| | 1122 | /* |
| | 1123 | * Get the natural logarithm of 10 to the required precision. We'll |
| | 1124 | * return the cached value if available, or compute and cache the |
| | 1125 | * constant to (at least) the required precision if not. |
| | 1126 | */ |
| | 1127 | static const char *cache_ln10(VMG_ size_t prec); |
| | 1128 | |
| | 1129 | /* cache pi to the required precision */ |
| | 1130 | static const char *cache_pi(VMG_ size_t prec); |
| | 1131 | |
| | 1132 | /* cache e to the required precision */ |
| | 1133 | static const char *cache_e(VMG_ size_t prec); |
| | 1134 | |
| | 1135 | /* get the constant value 1 */ |
| | 1136 | static const char *get_one() { return (const char *)one_; } |
| | 1137 | |
| | 1138 | /* constant value 1 */ |
| | 1139 | static const unsigned char one_[]; |
| | 1140 | |
| | 1141 | /* property evaluation function table */ |
| | 1142 | static int (CVmObjBigNum::*func_table_[])(VMG_ vm_obj_id_t self, |
| | 1143 | vm_val_t *retval, uint *argc); |
| | 1144 | }; |
| | 1145 | |
| | 1146 | /* ------------------------------------------------------------------------ */ |
| | 1147 | /* |
| | 1148 | * Registration table object |
| | 1149 | */ |
| | 1150 | class CVmMetaclassBigNum: public CVmMetaclass |
| | 1151 | { |
| | 1152 | public: |
| | 1153 | /* get the global name */ |
| | 1154 | const char *get_meta_name() const { return "bignumber/030000"; } |
| | 1155 | |
| | 1156 | /* create from image file */ |
| | 1157 | void create_for_image_load(VMG_ vm_obj_id_t id) |
| | 1158 | { |
| | 1159 | new (vmg_ id) CVmObjBigNum(); |
| | 1160 | G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); |
| | 1161 | } |
| | 1162 | |
| | 1163 | /* create from restoring from saved state */ |
| | 1164 | void create_for_restore(VMG_ vm_obj_id_t id) |
| | 1165 | { |
| | 1166 | new (vmg_ id) CVmObjBigNum(); |
| | 1167 | G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); |
| | 1168 | } |
| | 1169 | |
| | 1170 | /* create dynamically using stack arguments */ |
| | 1171 | vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) |
| | 1172 | { return CVmObjBigNum::create_from_stack(vmg_ pc_ptr, argc); } |
| | 1173 | |
| | 1174 | /* call a static property */ |
| | 1175 | int call_stat_prop(VMG_ vm_val_t *result, |
| | 1176 | const uchar **pc_ptr, uint *argc, |
| | 1177 | vm_prop_id_t prop) |
| | 1178 | { |
| | 1179 | return CVmObjBigNum::call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 1180 | } |
| | 1181 | }; |
| | 1182 | |
| | 1183 | |
| | 1184 | #endif /* VMBIGNUM_H */ |
| | 1185 | |
| | 1186 | /* |
| | 1187 | * Register the class |
| | 1188 | */ |
| | 1189 | VM_REGISTER_METACLASS(CVmObjBigNum) |