cfad47cfa3/tads3/vmbignum.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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)