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 */ clearCVmBigNumCacheReg42 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 */ alloc_memCVmBigNumCacheReg54 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 */ free_memCVmBigNumCacheReg88 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 */ get_ln10_reg(size_t siz,int * expanded)136 char *get_ln10_reg(size_t siz, int *expanded) 137 { return alloc_reg(&ln10_, siz, expanded); } 138 139 /* get the constant pi register */ get_pi_reg(size_t siz,int * expanded)140 char *get_pi_reg(size_t siz, int *expanded) 141 { return alloc_reg(&pi_, siz, expanded); } 142 143 /* get the constant e register */ get_e_reg(size_t siz,int * expanded)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 */ alloc_reg(CVmBigNumCacheReg * reg,size_t siz,int * expanded)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_; get_metaclass_reg()393 class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } 394 395 /* am I of the given metaclass? */ is_of_metaclass(class CVmMetaclass * meta)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 */ is_bignum_obj(VMG_ vm_obj_id_t obj)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 */ notify_new_savept()455 void notify_new_savept() { } apply_undo(VMG_ struct CVmUndoRecord *)456 void apply_undo(VMG_ struct CVmUndoRecord *) { } mark_undo_ref(VMG_ struct CVmUndoRecord *)457 void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } remove_stale_undo_weak_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 */ mark_refs(VMG_ uint)461 void mark_refs(VMG_ uint) { } 462 463 /* remove weak references */ remove_stale_weak_refs(VMG0_)464 void remove_stale_weak_refs(VMG0_) { } 465 466 /* load from an image file */ load_from_image(VMG_ vm_obj_id_t,const char * ptr,size_t)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 */ get_ext()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 */ getp_undef(VMG_ vm_obj_id_t,vm_val_t *,uint *)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 */ getp_pi(VMG_ vm_obj_id_t,vm_val_t * val,uint * argc)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 */ getp_e(VMG_ vm_obj_id_t,vm_val_t * val,uint * argc)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 */ get_prec(const char * ext)890 static size_t get_prec(const char *ext) 891 { return osrp2(ext + VMBN_PREC); } set_prec(char * ext,size_t prec)892 static void set_prec(char *ext, size_t prec) 893 { oswp2(ext + VMBN_PREC, prec); } 894 895 /* get/set the exponent */ get_exp(const char * ext)896 static int get_exp(const char *ext) 897 { return osrp2s(ext + VMBN_EXP); } set_exp(char * ext,int exp)898 static void set_exp(char *ext, int exp) 899 { oswp2(ext + VMBN_EXP, exp); } 900 901 /* get the negative sign flag */ get_neg(const char * ext)902 static int get_neg(const char *ext) 903 { return (ext[VMBN_FLAGS] & VMBN_F_NEG) != 0; } 904 905 /* set/clear negative sign flag */ set_neg(char * ext,int neg)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 */ get_type(const char * ext)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) */ set_type(char * ext,int typ)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) */ get_dig(const char * ext,size_t i)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 */ set_dig(char * ext,size_t i,unsigned int dig)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 */ set_zero(char * ext)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 */ is_zero(const char * ext)1037 static int is_zero(const char *ext) 1038 { return (ext[VMBN_FLAGS] & VMBN_F_ZERO) != 0; } 1039 1040 /* negate a value */ negate(char * ext)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 */ make_negative(char * ext)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 */ is_nan(const char * ext)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 */ get_objid_ext(VMG_ vm_obj_id_t obj_id)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 */ get_objid_obj(VMG_ vm_obj_id_t obj_id)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 */ get_one()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 */ get_meta_name()1154 const char *get_meta_name() const { return "bignumber/030000"; } 1155 1156 /* create from image file */ create_for_image_load(VMG_ vm_obj_id_t id)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 */ create_for_restore(VMG_ vm_obj_id_t id)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 */ create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)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 */ call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)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) 1190