-
Notifications
You must be signed in to change notification settings - Fork 0
/
creal.js
1706 lines (1604 loc) · 56.3 KB
/
creal.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Ported from Java to ECMAScript by Christian Lawson-Perfect, 2022.
*/
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* The above license covers additions and changes by AOSP authors.
* The original code is licensed as follows:
*/
//
// Copyright (c) 1999, Silicon Graphics, Inc. -- ALL RIGHTS RESERVED
//
// Permission is granted free of charge to copy, modify, use and distribute
// this software provided you include the entirety of this notice in all
// copies made.
//
// THIS SOFTWARE IS PROVIDED ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
// WARRANTIES THAT THE SUBJECT SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT
// FOR A PARTICULAR PURPOSE OR NON-INFRINGING. SGI ASSUMES NO RISK AS TO THE
// QUALITY AND PERFORMANCE OF THE SOFTWARE. SHOULD THE SOFTWARE PROVE
// DEFECTIVE IN ANY RESPECT, SGI ASSUMES NO COST OR LIABILITY FOR ANY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES
// AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY SUBJECT SOFTWARE IS
// AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
//
// UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING,
// WITHOUT LIMITATION, NEGLIGENCE OR STRICT LIABILITY), CONTRACT, OR
// OTHERWISE, SHALL SGI BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
// INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER WITH RESPECT TO THE
// SOFTWARE INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK
// STOPPAGE, LOSS OF DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL
// OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SGI SHALL HAVE BEEN INFORMED OF
// THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT
// APPLY TO LIABILITY RESULTING FROM SGI's NEGLIGENCE TO THE EXTENT APPLICABLE
// LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
// EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT
// EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
//
// These license terms shall be governed by and construed in accordance with
// the laws of the United States and the State of California as applied to
// agreements entered into and to be performed entirely within California
// between California residents. Any litigation relating to these license
// terms shall be subject to the exclusive jurisdiction of the Federal Courts
// of the Northern District of California (or, absent subject matter
// jurisdiction in such courts, the courts of the State of California), with
// venue lying exclusively in Santa Clara County, California.
// Copyright (c) 2001-2004, Hewlett-Packard Development Company, L.P.
//
// Permission is granted free of charge to copy, modify, use and distribute
// this software provided you include the entirety of this notice in all
// copies made.
//
// THIS SOFTWARE IS PROVIDED ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
// WARRANTIES THAT THE SUBJECT SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT
// FOR A PARTICULAR PURPOSE OR NON-INFRINGING. HEWLETT-PACKARD ASSUMES
// NO RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE.
// SHOULD THE SOFTWARE PROVE DEFECTIVE IN ANY RESPECT,
// HEWLETT-PACKARD ASSUMES NO COST OR LIABILITY FOR ANY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES
// AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY SUBJECT SOFTWARE IS
// AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
//
// UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING,
// WITHOUT LIMITATION, NEGLIGENCE OR STRICT LIABILITY), CONTRACT, OR
// OTHERWISE, SHALL HEWLETT-PACKARD BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
// INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER WITH RESPECT TO THE
// SOFTWARE INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK
// STOPPAGE, LOSS OF DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL
// OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF HEWLETT-PACKARD SHALL
// HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.
// THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY RESULTING
// FROM HEWLETT-PACKARD's NEGLIGENCE TO THE EXTENT APPLICABLE
// LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
// EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT
// EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
//
/**
/** Return the number of bits in the binary representation of `n`.
*
* @private
* @param {BigInt} n
* @returns {integer}
*/
function bitLength(n) {
return n.toString(2).length;
}
const bigIntMath = {
abs: function(n) {
return n < 0n ? -n : n;
},
signum: function(n) {
return n > 0n ? 1 : n < 0n ? -1 : 0;
}
}
class Timeout {
counter = 0;
constructor() {
this.counter = 0;
this.start = new Date();
}
check() {
this.counter += 1;
if(this.counter % 100 == 0) {
const t = new Date();
const dt = t - this.start;
if(dt > CReal.TIMEOUT_DURATION) {
throw new Error(`Operation took too long: ${dt}`);
}
}
}
}
/**
* Constructive real numbers, also known as recursive, or computable reals.
* Each recursive real number is represented as an object that provides an
* approximation function for the real number.
* The approximation function guarantees that the generated approximation
* is accurate to the specified precision.
* Arithmetic operations on constructive reals produce new such objects;
* they typically do not perform any real computation.
* In this sense, arithmetic computations are exact: They produce
* a description which describes the exact answer, and can be used to
* later approximate it to arbitrary precision.
*
* When approximations are generated, e.g. for output, they are
* accurate to the requested precision; no cumulative rounding errors
* are visible.
* In order to achieve this precision, the approximation function will often
* need to approximate subexpressions to greater precision than was originally
* demanded. Thus the approximation of a constructive real number
* generated through a complex sequence of operations may eventually require
* evaluation to very high precision. This usually makes such computations
* prohibitively expensive for large numerical problems.
* But it is perfectly appropriate for use in a desk calculator,
* for small numerical problems, for the evaluation of expressions
* computated by a symbolic algebra system, for testing of accuracy claims
* for floating point code on small inputs, or the like.
*
* We expect that the vast majority of uses will ignore the particular
* implementation, and the member functons `approximate`
* and `get_appr`. Such applications will treat `CReal` as
* a conventional numerical type, with an interface modelled on
* `java.math.BigInteger`. No subclasses of `CReal`
* will be explicitly mentioned by such a program.
*
* All standard arithmetic operations, as well as a few algebraic
* and transcendal functions are provided. Constructive reals are
* immutable; thus all of these operations return a new constructive real.
*
* A few uses will require explicit construction of approximation functions.
* The requires the construction of a subclass of `CReal` with
* an overridden `approximate` function. Note that `approximate`
* should only be defined, but never called. `get_appr`
* provides the same functionality, but adds the caching necessary to obtain
* reasonable performance.
*
* Any operation may also throw an exception if the precision request generated
* during any subcalculation overflows a 28-bit integer. (This should be
* extremely unlikely, except as an outcome of a division by zero, or other
* erroneous computation.)
*/
class CReal {
min_prec = null;
max_appr = null;
appr_valid = false;
/** The maximum amount of time to allow an operation to run for, in milliseconds.
* Nested operations get different timers, so evaluating a single number might take longer than this.
*
* @type {number}
*/
static TIMEOUT_DURATION = 3000;
approximate(precision) {
throw new Error("Approximation is not implemented for this operation");
}
/**
* @private
* @param {integer} n
* @returns {integer}
*/
static bound_log2(n) {
const abs_n = Math.abs(n);
return Math.ceil(Math.log(abs_n+1)/Math.log(2.0));
}
/** Throw an error if the requested precision is outside what can be safely represented with an integer.
* @private
*/
static check_prec(n) {
const high = n >> 28;
const high_shifted = n >> 29;
if( 0 != (high ^ high_shifted) ) {
throw(new Error("Precision overflow: this number can't be represented exactly"));
}
}
/** Multiply k by 2**n.
*
* @param {BigInt} k
* @param {integer} n
* @returns {BigInt}
* @private
*/
static shift(k, n) {
if(n == 0) {
return k;
} else if(n < 0) {
return k >> BigInt(-n);
} else {
return k << BigInt(n);
}
}
/* Multiply k by 2**n, rounding result.
*
* @param {BigInt} k
* @param {integer} n
* @returns {BigInt}
* @private
*/
static scale(k, n) {
if(n >= 0) {
return k << BigInt(n);
} else {
const adj_k = CReal.shift(k, n+1) + 1n;
return adj_k >> 1n;
}
}
/** Returns `value / 2 ** precision` rounded to an integer.
* The error in the result is strictly less than 1.
* Produces the same answer as `approximate`, but uses and maintains a cached approximation.
* Normally not overridden, and called only from `approximate` methods in subclasses.
* Not needed if the provided operations on constructive reals suffice.
*
* @param {integer} precision
* @returns {BigInt}
*/
get_appr(precision) {
CReal.check_prec(precision);
if(this.appr_valid && precision >= this.min_prec) {
return CReal.scale(this.max_appr, this.min_prec - precision);
} else {
const result = this.approximate(precision);
this.min_prec = precision;
this.max_appr = result;
this.appr_valid = true;
return result;
}
}
/** Return the position of the most significant digit (msd).
* If `x.msd() == n` then `2**(n-1) < abs(x) < 2**(n+1)`
* This initial version assumes that `max_appr` is valid and sufficiently removed from zero that the msd is determined.
*
* @returns {integer}
* @private
*/
known_msd() {
let length;
if(this.max_appr >= 0) {
length = bitLength(this.max_appr);
} else {
length = bitLength(-this.max_appr);
}
return this.min_prec + length - 1;
}
/** Return ths position of the most significant digit (msd) - this version may return `Number.MIN_SAFE_INTEGER` if the correct answer is `< n`.
*
* If `n` is not given, it uses `iter_msd` to eventually return a correct answer, unless the constructive real is zero, when it either loops forever or throws a precision overflow error.
*
* @param {integer} n
* @returns {integer}
*/
msd(n) {
if(n===undefined) {
return this.iter_msd(Number.MIN_SAFE_INTEGER);
}
if(!this.appr_valid || this.max_appr <= 1n && this.max_appr >= -1n) {
this.get_appr(n-1);
if(bigIntMath.abs(this.max_appr) <= 1n) {
// msg could still be arbitrarily far to the right.
return Number.MIN_SAFE_INTEGER;
}
}
return this.known_msd();
}
/** Functionally equivalent to `msd`, but iteratively evaluates to higher precision.
*
* @param {integer} n
* @returns {integer}
* @private
*/
iter_msd(n) {
let prec = 0;
for(let prec = 0; prec > n+30; prec = Math.floor((prec*3)/2) - 16) {
let msd = this.msd(prec);
if(msd != Number.MIN_SAFE_INTEGER) {
return msd;
}
CReal.check_prec(prec);
}
return this.msd(n);
}
/** A helper function for `CReal.toString`.
* Generate a string containing n zeros.
*
* @param {integer} n
* @returns {string}
* @private
*/
static zeros(n) {
let a = '';
for(let i=0;i<n;i++) {
a += '0';
}
return a;
}
/** Natural logarithm.
* @returns {CReal}
* @private
*/
simple_ln() {
return new prescaled_ln_CReal(this.subtract(CReal.ONE));
}
/** Atan of integer reciprocal: atan(1/n).
*
* @param {integer} n
* @returns {CReal}
*/
static atan_reciprocal(n) {
return new integral_atan_CReal(n);
}
/**
* Return `0` if `x = y` to within the indicated tolerance,
* `-1` if `x < y`, and `+1` if `x > y`. If `x` and `y` are indeed
* equal, it is guaranteed that `0` will be returned. If
* they differ by less than the tolerance, anything
* may happen. The tolerance allowed is
* the maximum of `(abs(this)+abs(x))*(2**r)` and `2**a`
*
* @param {CReal} x - The other constructive real.
* @param {integer} r - Relative tolerance in bits.
* @param {integer} a - Absolute tolerance in bits.
*/
compareTo(x) {
let r;
let a;
if(arguments.length==3) {
r = arguments[1];
a = arguments[2];
const this_msd = this.iter_msd(a);
const x_msd = x.iter_msd(this_msd > a ? this_msd : a);
const max_msd = (x_msd > this_msd ? x_msd : this_msd);
if(max_msd === Number.MIN_SAFE_INTEGER) {
return 0;
}
CReal.check_prec(r);
const rel = max_msd + r;
const abs_prec = rel > a ? rel : a;
return this.compareTo(x, abs_prec);
} else if(arguments.length==2) {
a = arguments[1];
const needed_prec = a - 1;
const this_appr = this.get_appr(needed_prec);
const x_appr = x.get_appr(needed_prec);
if(this_appr > (x_appr+1n)) {
return 1;
} else if(this_appr < x_appr - 1n){
return -1;
} else {
return 0;
}
} else {
for(a = -20; ; a *= 2) {
CReal.check_prec(a);
let result = this.compareTo(x,a);
if(result != 0) {
return result;
}
}
}
}
/** Return `-1` if negative, `+1` if positive.
* Should be called only if `this != 0`.
* In the 0 case, this will not terminate correctly; typically it will run until it exhausts memory.
* If the two constructive reals may be equal, the one or two argument version of signum should be used.
*
* @param {integer} [a]
* @returns {integer}
*/
signum(a) {
if(a===undefined) {
for(a = -20; ; a *= 2) {
CReal.check_prec(a);
const result = this.signum(a);
if(result != 0) {
return result;
}
}
} else {
if(this.appr_valid) {
if(this.max_appr != 0n) {
return this.max_appr > 0 ? 1 : -1;
}
}
const needed_prec = a - 1;
const this_appr = this.get_appr(needed_prec);
return this_appr.signum();
}
}
/** Return a textual representation accurate to `n` places to the right of the decimal point.
*
* @param {integer} [n=10] - must be non-negative.
* @param {integer} [radix=10] - between 2 and 16, inclusive.
* @example
* // returns "3.141"
* CReal.PI.toString(3)
* @returns {string}
*/
toString(n, radix) {
if(n === undefined) {
n = 10;
}
if(radix === undefined) {
radix = 10;
}
let scaled_CReal;
if(radix == 16) {
scaled_CReal = this.shiftLeft(4*n);
} else {
const scale_factor = BigInt(radix)**BigInt(n);
scaled_CReal = this.multiply(new int_CReal(scale_factor));
}
const scaled_int = scaled_CReal.get_appr(0);
if(scaled_int < 0n) {
return '-'+this.negate().toString(n, radix);
}
let scaled_string = bigIntMath.abs(scaled_int).toString(radix);
let result;
if(n == 0) {
result = scaled_string;
} else {
let len = scaled_string.length;
if(len <= n) {
const z = CReal.zeros(n+1-len);
scaled_string = z + scaled_string;
len = n + 1;
}
const whole = scaled_string.slice(0,len-n);
const fraction = scaled_string.slice(len-n);
result = whole + "." + fraction;
}
if(scaled_int < 0n) {
result = "-" + result;
}
return result;
}
/** A representation of a number in scientific notation.
*
* @typedef StringFloatRep
* @private
* @property {integer} sign
* @property {string} mantissa
* @property {integer} radix
* @property {integer} exponent
*/
/** Return a textual scientific notation representation accurate to `n` places to the right of the decimal point.
* `n` must be non-negative.
* A value smaller than `radix**-m` may be displayed as 0.
* The `mantissa` component of the result is either "0" or exactly `n` digits long.
* The `sign` component is zero exactly when the mantissa is "0".
*
* @param {integer} n - Number of digits (>0) included to the right of the decimal point.
* @param {integer} radix - The base (between 2 and 16) for the resulting representation.
* @param {integer} m - The precision used to distinguish the number from zero. Expressed as a power of `radix`.
* @returns {StringFloatRep}
*/
toStringFloatRep(n, radix, m) {
if(n <= 0) {
throw new Error("Bad precision argument");
}
const log2_radix = Math.log(radix) / Math.log(2);
const big_radix = BigInt(radix);
const msd_prec = Math.round(log2_radix * m);
if(msd_prec > Number.MAX_SAFE_INTEGER || msd_prec < Number.MIN_SAFE_INTEGER) {
throw(new Error("Precision overflow: this number can't be represented exactly"));
}
CReal.check_prec(msd_prec);
const msd = this.iter_msd(msd_prec-2);
if(msd == Number.MIN_SAFE_INTEGER) {
return {
sign: 0,
mantissa: "0",
radix: radix,
exponent: 0
};
}
let exponent = Math.ceil(msd/log2_radix);
const scale_exp = exponent - n;
let scale;
if(scale_exp > 0) {
scale = CReal.valueOf(big_radix ** BigInt(scale_exp)).inverse();
} else {
scale = CReal.valueOf(big_radix ** BigInt(-scale_exp));
}
let scaled_res = this.multiply(scale);
let scaled_int = scaled_res.get_appr(0);
let sign = bigIntMath.signum(scaled_int);
let scaled_string = bigIntMath.abs(scaled_int).toString(radix);
const timeout = new Timeout();
while(scaled_string.length<n) {
timeout.check();
// Exponent was too large. Adjust.
scaled_res = scaled_res.multiply(CReal.valueOf(big_radix));
exponent -= 1;
scaled_int = scaled_res.get_appr(0);
sign = bigIntMath.signum(scaled_int);
scaled_string = bigIntMath.abs(scaled_int).toString(radix);
}
if(scaled_string.length > n) {
// Exponent was too small. Adjust by truncating.
exponent += (scaled_string.length - n);
scaled_string = scaled_string.slice(0,n);
}
return {
sign,
mantissa: scaled_string,
radix,
exponent
};
}
/** Return a BigInt which differs by less than one from the constructive real.
*
* @returns {BigInt}
*/
BigIntValue() {
return this.get_appr(0);
}
/** Return an integer which differs by less than one from the constructive real.
* Behaviour on overflow is undefined.
*
* @returns {integer}
*/
intValue() {
return Number(this.BigIntValue());
}
/** Return a float which differs by less than one in the least represented bit from the constructive real.
* (We're in fact closer to round-to-nearest than that, but we can't and don't promise correct rounding.)
*
* @returns {number}
*/
floatValue() {
const my_msd = this.iter_msd(-1080);
if(my_msd == Number.MIN_SAFE_INTEGER) {
return 0;
}
const needed_prec = my_msd - 60;
let scaled_int = Number(this.get_appr(needed_prec));
return Number(scaled_int) * (2**needed_prec)
/** Original from CReal.java - I'm not sure whether this could work with JS numbers.
/*
const may_underflow = needed_prec < -1000;
const exp_adj = may_underflow ? needed_prec + 96 : needed_prec;
const orig_exp = (scaled_int >> 52) & 0x7ff;
// Original unbiased exponent is > 50. Exp_adj > -1050.
// Thus the sum must be > the smallest representable exponent
// of -1023.
if(orig_exp + exp_adj >= 0x7ff) {
// Exponent overflowed.
if(scaled_int < 0) {
return -Infinity;
} else {
return Infinity;
}
}
scaled_int += exp_adj << 52;
const result = scaled_int;
if(may_underflow) {
const two48 = 1 << 48;
return result/two48/two48;
} else {
return result;
}
*/
}
/** Add two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
add(x) {
return new add_CReal(this, x);
}
/** Multiply a constructive real by `2 ** n`.
*
* @param {integer} n
* @returns {CReal}
*/
shiftLeft(n) {
CReal.check_prec(n);
return new shifted_CReal(this, n);
}
/** Multiply a constructive real by `2 ** -n`.
*
* @param {integer} n
* @returns {CReal}
*/
shiftRight(n) {
CReal.check_prec(n);
return new shifted_CReal(this, -n);
}
/** Produce a constructive real equivalent to the original, assuming the original was an integer.
* Undefined results if the original was not an integer.
* Prevents evaluation of digits to the right of the decimal point, and may thus improve performance.
*
* @returns {CReal}
*/
assumeInt() {
return new assumed_int_CReal(this);
}
/** The additive inverse of a constructive real.
*
* @returns {CReal}
*/
negate() {
return new neg_CReal(this);
}
/** The difference between two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
subtract(x) {
return new add_CReal(this, x.negate());
}
/** The produce of two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
multiply(x) {
return new mult_CReal(this, x);
}
/** The multiplicative inverse of a constructive real.
* `x.inverse()` is equivalent to `CReal.valueOf(1).divide(x)`.
*
* @returns {CReal}
*/
inverse() {
return new inv_CReal(this);
}
/** The quotient of two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
divide(x) {
return new mult_CReal(this, x.inverse());
}
/** The real number `x` if `this < 0`, or `y` otherwise.
* Requires `x = y` if `this = 0`.
* Since comparisons may diverge, this is often a useful alternative to conditionals.
*
* @param {CReal} x
* @param {CReal} y
* @returns {CReal}
*/
select(x, y) {
return new select_CReal(this, x, y);
}
/** The maximum of two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
max(x) {
return this.subtract(x).select(x, this);
}
/** The minimum of two constructive reals.
*
* @param {CReal} x
* @returns {CReal}
*/
min(x) {
return this.subtract(x).select(this, x);
}
/** The absolute value of a constructive real.
* Note that this cannot be written as a conditional.
*
* @returns {CReal}
*/
abs() {
return this.select(this.negate(), this);
}
/** The exponential function, that is `e**this`.
*
* @returns {CReal}
*/
exp() {
const low_prec = -10;
const rough_appr = this.get_appr(low_prec);
// Handle negative arguments directly; negating and computing inverse
// can be very expensive.
if(rough_appr > 2n || rough_appr < -2n) {
const square_root = this.shiftRight(1).exp();
return square_root.multiply(square_root);
} else {
return new prescaled_exp_CReal(this);
}
}
/** To the power of x.
*
* @param {CReal} x
* @returns {CReal}
*/
pow(x) {
return x.multiply(this.ln()).exp();
}
/** The trigonometric cosine function.
*
* @returns {CReal}
*/
cos() {
const halfpi_multiples = this.divide(CReal.PI).get_appr(-1);
const abs_halfpi_multiples = bigIntMath.abs(halfpi_multiples);
if(abs_halfpi_multiples >= 2n) {
// Subtract multiples of PI
const pi_multiples = CReal.scale(halfpi_multiples, -1)
const adjustment = CReal.PI.multiply(CReal.valueOf(pi_multiples));
if((pi_multiples & 1n) != 0n) {
return this.subtract(adjustment).cos().negate();
} else {
return this.subtract(adjustment).cos();
}
} else if(bigIntMath.abs(this.get_appr(-1)) >= 2n) {
// Scale further with double angle formula
const cos_half = this.shiftRight(1).cos();
return cos_half.multiply(cos_half).shiftLeft(1).subtract(CReal.ONE);
} else {
return new prescaled_cos_CReal(this);
}
}
/** The trigonometric sine function.
*
* @returns {CReal}
*/
sin() {
return CReal.half_pi.subtract(this).cos();
}
/** The trigonometric arc (inverse) sine function.
*
* @returns {CReal}
*/
asin() {
const rough_appr = this.get_appr(-10);
if(rough_appr > 750n) { // 1/sqrt(2) + a bit
const new_arg = CReal.ONE.subtract(this.multiply(this)).sqrt();
return new_arg.acos();
} else if(rough_appr < -750n) {
return this.negate().asin().negate();
} else {
return new prescaled_asin_CReal(this);
}
}
/** The trigonometric arc (incerse) cosine function.
*
* @returns {CReal}
*/
acos() {
return CReal.half_pi.subtract(this.asin());
}
/** The natural (base e) logarithm.
*
* @returns {CReal}
*/
ln() {
const low_prec = -4;
const rough_appr = this.get_appr(low_prec); // In sixteenths
if(rough_appr < 0n) {
throw new Error("Logarithm of a negative number");
}
if(rough_appr <= CReal.low_ln_limit) {
return this.inverse().ln().negate();
}
if(rough_appr >= CReal.high_ln_limit) {
if(rough_appr <= CReal.scaled_4) {
const quarter = this.sqrt().sqrt().ln();
return quarter.shiftLeft(2);
} else {
const extra_bits = bitLength(rough_appr) - 3;
const scaled_result = this.shiftRight(extra_bits).ln();
return scaled_result.add(CReal.valueOf(extra_bits).multiply(CReal.ln2));
}
}
return this.simple_ln();
}
/** The square root of a constructive real.
*
* @returns {CReal}
*/
sqrt() {
return new sqrt_CReal(this);
}
}
/** A specialization of CReal for cases in which approximate() calls
* to increase evaluation precision are somewhat expensive.
* If we need to (re)evaluate, we speculatively evaluate to slightly
* higher precision, miminimizing reevaluations.
* Note that this requires any arguments to be evaluated to higher
* precision than absolutely necessary. It can thus potentially
* result in lots of wasted effort, and should be used judiciously.
* This assumes that the order of magnitude of the number is roughly one.
*
* @private
*/
class slow_CReal extends CReal {
static max_prec = -64;
static prec_incr = 32;
get_appr(precision) {
CReal.check_prec(precision);
if(this.appr_valid && precision >= this.min_prec) {
return CReal.scale(this.max_appr, this.min_prec - precision);
} else {
const eval_prec = precision >= this.max_prec ? this.max_prec : (precision - slow_CReal.prec_incr + 1) & ~(slow_CReal.prec_incr - 1);
const result = this.approximate(eval_prec);
this.min_prec = eval_prec;
this.max_appr = result;
this.appr_valid = true;
return CReal.scale(result, eval_prec - precision);
}
}
}
/** Representation of an integer constant.
*
* @private
*/
class int_CReal extends CReal {
/**
* @param {BigInt} n
*/
constructor(n) {
super();
this.value = n;
}
approximate(p) {
return CReal.scale(this.value, -p);
}
}
/** Representation of a number that may not have been completely
* evaluated, but is assumed to be an integer. Hence we never
* evaluate beyond the decimal point.
*
* @private
*/
class assumed_int_CReal extends CReal {
/* @param {CReal} x
*/
constructor(x) {
super();
this.value = x;
}
approximate(p) {
if(p >= 0) {
return this.value.get_appr(p);
} else {
return CReal.scale(this.value.get_appr(0), -p);
}
}
}
/** Representation of the sum of two constructive reals.
*
* @private
*/
class add_CReal extends CReal {
/**
* @param {CReal} x
* @param {CReal} y
*/
constructor(x, y) {
super();
this.op1 = x;
this.op2 = y;
}
approximate(p) {
return CReal.scale(this.op1.get_appr(p-2) + this.op2.get_appr(p-2), -2);
}
}
/** Representation of a CReal multiplied by 2**n.
*
* @private
*/
class shifted_CReal extends CReal {
/**
* @param {CReal} x
* @param {integer} n
*/
constructor(x, n) {
super();
this.op = x;
this.count = n;
}
approximate(p) {
return this.op.get_appr(p - this.count);
}
}
/** Representation of the negation of a CReal.
*
* @private
*/
class neg_CReal extends CReal {
/**
* @param {CReal} x
*/
constructor(x) {
super();
this.op = x;
}
negate() {
return this.op;
}
approximate(p) {
return -this.op.get_appr(p);
}
}
/** Representation of:
* op1 if selector < 0
* op2 if selector >= 0
* Assumes x = y if s = 0.
*