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