-
Notifications
You must be signed in to change notification settings - Fork 16
/
jmat_bigint.js
2600 lines (2282 loc) · 91.1 KB
/
jmat_bigint.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
/*
Jmat.js
Copyright (c) 2011-2020, Lode Vandevenne
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// REQUIRES: jmat_real.js
/*
Jmat.BigInt: Big Integer Math
IMPORTANT NOTE: This is not intended for crypto! E.g. JavaScript's Math.random is used, there is no protection against side-channel attacks, there may be bugs, etc...
For convenience:
*) Jmat.BigInt is defined here. Its functions operate on Jmat.BigInt objects only (unless where other types are applicable).
*) Jmat.BigIntC is the same, but its functions are enriched to also take other data types as arguments (internally converting them to Jmat.BigInt ojbects)
*) BigInt in the global namespace, exported by jmat.js, is actually Jmat.BigIntC.
Jmat.BigIntC functions can work with four types of data: Jmat.BigInt (BigInt) objects, strings, arrays, and plain JS numbers.
Any radix is supported, but the native radix is Jmat.BigInt.ARRAYBASE_.
Overview of some functionality:
-elementary operators: BigInt.add, BigInt.sub, BigInt.mul, BigInt.div, BigInt.half
-base conversions: BigInt.convertBase, BigInt.convertArrayBase, BigInt.convertStringBase
-logic operators: BigInt.bitand, BigInt.bitor, BigInt.bitxor, BigInt.bitnot, BigInt.lshift, BigInt.rshift, ...
-tests: BigInt.isOdd, BigInt.isEven, BigInt.perfectsquare, BigInt.{eq, neq, lt, gt, lte, gte}
-compare: BigInt.compare, BigInt.min, BigInt.max
-power and logarithms: BigInt.sqrt, BigInt.root, BigInt.pow, BigInt.modpow, BigInt.log2, BigInt.log10, ...
-primes: BigInt.isPrime, BigInt.factorize, BigInt.nextPrime, BigInt.previousPrime, BigInt.nearestPrime
-Euclidean Algorithm: BigInt.gcd, BigInt.egcd
-factorial: BigInt.factorial, BigInt.primorial
TODO: if supported by browser, add support for native JS BigInts in several computations to speed things up (e.g. pow)
*/
/*
Constructor, but also usable without new as factory function.
All parameters are optional.
TODO: make it such that if a starts with 0x, it parses string as decimal. Also, if a is string, b gives OUTPUT base instead of string base, that may be confusing, consider a different option where b is the base of the string.
TODO: add support for native JS BigInts as input
*/
Jmat.BigInt = function(a, b, minus) {
if(this instanceof Jmat.BigInt) {
this.a = a || []; // least significant in element with *highest* index (TODO: swap this to become little endian?)
this.radix = b || Jmat.BigInt.ARRAYBASE_;
this.minus = minus || false; //if true, represents negative integer
} else {
// No keyword "new" in front, use the convenience factory function instead
return Jmat.BigInt.make(a, b, minus); // This supports several argument types
}
};
// List all functions without underscore: for(var i in Jmat.BigInt) if(i.indexOf('_') == -1) console.log(i)
// default array base, must be power of two (otherwise things like 'and' and 'rshift' break). Larger is faster.
// 32768 is the maximum supported, above that, JS cannot do bit operations on product of two numbers (breaking leemondiv_).
Jmat.BigInt.ARRAYBASE_ = 32768;
Jmat.BigInt.ARRAYBASE_BITS_ = 15; // log2(Jmat.BigInt.ARRAYBASE_)
// the default base for numbers given as string. TODO: support hex strings if they start with 0x
Jmat.BigInt.STRINGBASE_ = 10;
//Typically, a is array, string, number or BigInt, and b is base of output (that of input may be different, e.g. always 10 for string).
//minus is only used for array or no input
Jmat.BigInt.make = function(a, b, minus) {
if(a == undefined) return new Jmat.BigInt(a, b, minus);
if(typeof a == 'number') return Jmat.BigInt.fromInt(a, b);
//if(a.raw && a.length == 1 && typeof a[0] == 'string' && a.raw[0] == a[0]) a = a[0]; // ES6: BigInt`123456789123456789`
if(typeof a == 'string') return Jmat.BigInt.parse(a, Jmat.BigInt.STRINGBASE_, b);
if(a.length != undefined) return new Jmat.BigInt(a, b, minus);
if(a.re != undefined) return Jmat.BigInt.fromInt(a.re, b);
if(a.w != undefined) return Jmat.BigInt.fromInt(a.w, b);
if(!b || a.radix == b) return Jmat.BigInt.copy(a);
return Jmat.BigInt.convertBase(a, b);
};
// casts to a BigInt if not already one.
// if opt_base is undefined, does not care about radix (won't base-convert if v already BigInt). If given, casts or converts to one with that base
//minus is only used for array or no input
Jmat.BigInt.cast = function(v, opt_base, opt_minus) {
if(v == undefined) return new Jmat.BigInt(undefined, opt_base, opt_minus);
if(v.a != undefined && (!opt_base || v.radix == opt_base)) return v;
if(v.radix && opt_base) return Jmat.BigInt.convertBase(v, opt_base);
return Jmat.BigInt(v, opt_base, opt_minus);
};
//aka clone
Jmat.BigInt.copy = function(v) {
return new Jmat.BigInt(v.a.slice(0), v.radix, v.minus);
};
Jmat.BigInt.parse = function(s, opt_stringbase, opt_outbase) {
var stringbase = opt_stringbase || Jmat.BigInt.STRINGBASE_;
var outbase = opt_outbase || Jmat.BigInt.ARRAYBASE_;
var minus = false;
if(s[0] == '-') {
minus = true;
s = s.substr(1);
}
var a = Jmat.BigInt.stringToArray(s, outbase, stringbase);
return new Jmat.BigInt(a, outbase, minus);
};
Jmat.BigInt.toString = function(value, opt_base) {
if(!value || !value.a) return 'invalid';
var s = (value.minus ? '-' : '');
return s + Jmat.BigInt.arrayToString(value.a, value.radix, opt_base);
};
Jmat.BigInt.prototype.toString = function(opt_base) {
return Jmat.BigInt.toString(this, opt_base);
};
//By default, this converts ARRAYBASE_ array to base-10 string. The optional abase and sbase parameters change this.
//must be positive (no '-' in the string)
Jmat.BigInt.stringToArray = function(s, opt_abase, opt_sbase) {
var abase = opt_abase || Jmat.BigInt.ARRAYBASE_;
var sbase = opt_sbase || Jmat.BigInt.STRINGBASE_;
var result = [];
for(var i = 0; i < s.length; i++) {
var v = Jmat.BigInt.di_(s[i]);
result[i] = v;
}
return Jmat.BigInt.convertArrayBase(result, sbase, abase);
};
Jmat.BigInt.arrayToString = function(v, opt_abase, opt_sbase) {
var abase = opt_abase || Jmat.BigInt.ARRAYBASE_;
var sbase = opt_sbase || Jmat.BigInt.STRINGBASE_;
v = Jmat.BigInt.convertArrayBase(v, abase, sbase);
var result = '';
for(var i = 0; i < v.length; i++) {
result += Jmat.BigInt.d_[v[i]];
}
if(v.length == 0) result = '0';
return result;
};
//may return input object if bases are equal
Jmat.BigInt.convertArrayBase = function(s, from, to, opt_powrcache_) {
if(s.length > 8 && to == 10 && Jmat.Real.isPOT(from)) {
// Using larger to-bases for the non-linear algos gives huge speedup.
// TODO: do this for all such to-bases rather than hardcoded for 10.
// NOTE: due to JS number multiplications in baseloop_, the large base can be max 10000000 (10 million),
// because unfortunately 100 million * 100 million is just a little bit bigger than 2 ** 53, the max JS number integer.
var a = Jmat.BigInt.convertArrayBase(s, from, 10000000);
return Jmat.BigInt.convertArrayBase(a, 10000000, 10);
}
var R = Jmat.Real;
var B = Jmat.BigInt;
if(from == to) return s;
var r = [];
s = B.maybecopystrip_(s);
// O(n) algorithm if both bases are powers of two
if(from <= 2147483648 && to <= 2147483648 && R.isPOT(from) && R.isPOT(to)) {
var fbits = R.ilog2(from);
var tbits = R.ilog2(to);
var rlen = Math.ceil(s.length * fbits / tbits);
for(var i = 0; i < rlen; i++) r[i] = 0;
if(from < to) {
var pos = rlen - 1; // element pos in result array
var bp = 0; // bit pos into result element (from right)
for(var i = s.length - 1; i >= 0; i--) {
if(bp + fbits > tbits) {
var a = tbits - bp;
if(a != 0) r[pos] |= ((s[i] & ((1 << a) - 1)) << bp);
pos--;
r[pos] = (s[i] >> a);
bp = fbits - a;
} else {
r[pos] |= (s[i] << bp);
bp += fbits;
}
}
} else {
var mask = to - 1;
var pos = s.length - 1; // element pos in input array
var bp = 0; // bit pos into input element (from right)
for(var i = rlen - 1; i >= 0; i--) {
if(bp + tbits > fbits) {
var a = fbits - bp;
r[i] = s[pos] >> bp;
pos--;
r[i] |= (s[pos] << a);
r[i] &= mask;
bp = tbits - a;
} else {
r[i] |= ((s[pos] >> bp) & mask);
bp += tbits;
}
}
}
B.stripInPlace_(r);
return r;
}
// If one base is a power of the other base, an O(n) algorithm can be used
var p = R.isPowerOf(from, to);
if(p) {
for(var i = 0; i < s.length; i++) {
var el = s[i];
for(var j = 0; j < p; j++) {
r.push(Math.floor(el / (p - j - 1)) % to);
}
for(var j = p - 1; j >= 0; j--) {
r[i * p + j] = el % to;
el = Math.floor(el / to);
}
}
B.stripInPlace_(r);
return r;
}
p = R.isPowerOf(to, from);
if(p) {
var num = Math.ceil(s.length / p);
for(var i = 0; i < num; i++) {
var m = 1;
r[num - 1 - i] = 0;
for(var j = 0; j < p; j++) {
var index = s.length - 1 - (i * p) - j;
if(index < 0) break;
r[num - 1 - i] += s[index] * m;
m *= from;
}
}
B.stripInPlace_(r);
return r;
}
if(s.length > 8) {
// Divide and conquer radix conversion
var h = R.idiv(s.length, 2); // half the digits amount, in the from base
var high = [];
var low = [];
for(var i = 0; i < h; i++) high[i] = s[i];
for(var i = h; i < s.length; i++) low[i - h] = s[i];
// cache for powers since the recursion will often compute the same powers
var cache = opt_powrcache_ || [];
// Recursively call convertArrayBase on two half-sized numbers
var high2 = B.convertArrayBase(high, from, to, cache);
var low2 = B.convertArrayBase(low, from, to, cache);
var e = s.length - h;
var p = cache[e]; // p should get the following value: B.powr(B(from, to), e);
if(!p) {
// instead of computing B.powr(B(from, to), e) directly, previous smaller powers from the cache can be used for more speed
var eh = R.idiv(e, 2);
var el = e - eh;
if(!cache[eh]) cache[eh] = B.powr(B(from, to), eh);
if(!cache[el]) cache[el] = B.powr(B(from, to), el);
var ph = cache[eh];
var pl = cache[el];
p = ph.mul(pl);
cache[e] = p;
}
var res = B(low2, to).add(B(high2, to).mul(p));
return res.a;
}
for(var i = 0; i < s.length; i++) {
r = B.baseloop_(r, 0, from, [], 0, 1, s[i], to, false);
}
if(r.length == 0) r = [0];
return r;
};
Jmat.BigInt.convertStringBase = function(s, from, to) {
if(from == to) return s;
var a = Jmat.BigInt.stringToArray(s, to, from);
return Jmat.BigInt.arrayToString(a, to, to);
};
// Returns a + b in the simplest case
// The basic loop for add, with potential shift, mul, ... a and b are arrays, not BigInt objects. No parameters are optional, use [], 0 or 1.
// Supports also e.g. multiplying and adding a plain number to a single array: Jmat.BigInt.baseloop_(array, 0, mul, [], 0, 1, add, base, false)
// Also ensures the result is stripped (no leading zeroes), to avoid some calculations accidently becoming slower and slower when using many subtracts
// Calculates (a << ashift) * amul + (b << bshift) * bmul + overflow, with the arrays a and b in the given base, and the shifting meaning shifting whole digits of that base.
// If keep_asize is false, strips the result of leading zeroes. True should be the default value to use.
// If keep_asize is true, will ensure the result array size is as big as input a's array size (unless impossible due to leading non-zero value), which may cause it to add leading zeroes, or remove some.
// The result must be positive, that is, when subtracting, ensure b <= a.
// For example: to multiply a with 5, do: baseloop_(a, 0, 5, [], 0, 0, 0, radix, false)
// For example: to calculate a - b, do: baseloop_(a, 0, 1, b, 0, -1, 0, radix, false)
// For example: to calculate a << 5 + b, do: baseloop_(a, 5, 1, b, 0, 1, 0, radix, false)
// NOTE: base * amul and base * bmul should be smaller than 2 ** 53, to avoid JavaScript number overflow giving inaccurate results
Jmat.BigInt.baseloop_ = function(a, ashift, amul, b, bshift, bmul, overflow, base, keep_asize) {
var result = [];
var l = Math.max(a.length + ashift, b.length + bshift);
var asize = a.length;
if(!b.length && !ashift) {
// TODO: bmul is ignored here which is a bug, verify if nothing was trying to use it this way, and add "bmul == 1" to the if if needed
for(var i = 0; i < l; i++) {
overflow += amul * (a[a.length - i - 1]);
result[i] = (overflow + base) % base; // to also support negative values
overflow = Math.floor(overflow / base);
}
} else if(ashift == 0 && bshift == 0 && amul == 1 && bmul == 1) {
for(var i = 0; i < l; i++) {
if(i < a.length) overflow += a[a.length - i - 1];
if(i < b.length) overflow += b[b.length - i - 1];
result[i] = (overflow + base) % base; // to also support negative values
overflow = Math.floor(overflow / base);
}
} else {
for(var i = 0; i < l; i++) {
if(i >= ashift && i < a.length + ashift) overflow += amul * (a[a.length - i - 1 + ashift]);
if(i >= bshift && i < b.length + bshift) overflow += bmul * (b[b.length - i - 1 + bshift]);
result[i] = (overflow + base) % base; // to also support negative values
overflow = Math.floor(overflow / base);
}
}
if(overflow == Infinity) throw 'infinite overflow';
while(overflow > 0) { //"if" would suffice but with while it supports larger than base values in the array, so that e.g. [0] + [100] with base 2 converts 100 to binary [1, 1, 0, 0, 1, 0, 0]
result.push((overflow + base) % base);
overflow = Math.floor(overflow / base);
}
if(keep_asize) {
while(result.length > asize && result[result.length - 1] == 0) result.length--;
while(result.length < asize) result.push(0);
} else {
// strip leading zeroes
while(result.length > 1 && result[result.length - 1] == 0) result.length--;
}
Jmat.BigInt.mirror_(result);
return result;
};
Jmat.BigInt.intToArray = function(i, opt_base) {
// Above JS float's integer precision. A user may think the literal is a correct big number, but JS silently changes it to JS float. Loudly show that, do not return actual results on this.
if(i > Jmat.Real.BIGGESTJSINT) throw 'too large integer literal for JS'; //return undefined;
var base = opt_base || Jmat.BigInt.ARRAYBASE_;
var result = [];
while(i > 0) {
result.push(i % base);
i = Math.floor(i / base);
}
if(result.length == 0) result = [0];
if(result.length > 1) Jmat.BigInt.mirror_(result); //the if is there so that the Jmat.BigInt.ONE etc... assignments can be there without already having mirror_ function.
return result;
};
// If the number is too big to represent in JS precision, returns float approximation or if even too high for that, Infinity.
Jmat.BigInt.arrayToInt = function(v, opt_base) {
var base = opt_base || Jmat.BigInt.ARRAYBASE_;
var result = 0;
var m = 1;
for(var i = 0; i < v.length; i++) {
result += m * v[v.length - 1 - i];
m *= base;
if(result == Infinity || m == Infinity) return Infinity; //avoid it becoming NaN
}
return result;
};
Jmat.BigInt.fromInt = function(i, opt_base) {
var base = opt_base || Jmat.BigInt.ARRAYBASE_;
var minus = false;
if(i < 0) {
minus = true;
i = -i;
}
return new Jmat.BigInt(Jmat.BigInt.intToArray(i, base), base, minus);
};
// If the number is too big to represent in JS precision, returns float approximation or if even too high for that, Infinity.
Jmat.BigInt.toInt = function(v) {
return Jmat.BigInt.arrayToInt(v.a, v.radix) * (v.minus ? -1 : 1);
};
Jmat.BigInt.prototype.toInt = function() {
return Jmat.BigInt.arrayToInt(this.a, this.radix) * (this.minus ? -1 : 1);
};
Jmat.BigInt.ZERO = Jmat.BigInt(0);
Jmat.BigInt.ONE = Jmat.BigInt(1);
Jmat.BigInt.TWO = Jmat.BigInt(2);
//may return input if bases are equal
//only supports BigInt object at input. For arrays or strings use convertArrayBase or convertStringBase.
Jmat.BigInt.convertBase = function(s, to) {
if(s.radix == to) return s;
return new Jmat.BigInt(Jmat.BigInt.convertArrayBase(s.a, s.radix, to), to, s.minus);
};
Jmat.BigInt.cloneArray = function(v) {
return v.slice(0);
};
Jmat.BigInt.cloneArrayTo = function(v, target) {
target.length = v.length;
for(var i = 0; i < v.length; i++) target[i] = v[i];
};
// left shift by s digits (based on a's radix)
// s is regular JS number
Jmat.BigInt.lshift_radix = function(a, s) {
if(s == 0) return a;
if(s < 0) return Jmat.BigInt.rshift_radix(a, -s);
var result = Jmat.BigInt.copy(a);
for(var i = 0; i < s; i++) result.a.push(0);
return result;
};
Jmat.BigInt.prototype.lshift_radix = function(s) {
return Jmat.BigInt.lshift_radix(this, s);
};
// right shift by s digits (based on a's radix)
// s is regular JS number
Jmat.BigInt.rshift_radix = function(a, s) {
if(s == 0) return a;
if(s < 0) return Jmat.BigInt.lshift_radix(a, -s);
var result = Jmat.BigInt.copy(a);
result.a = result.a.slice(0, -s);
return result;
};
Jmat.BigInt.prototype.rshift_radix = function(s) {
return Jmat.BigInt.rshift_radix(this, s);
};
//shift left by b bits
//a is bigint, b is regular js number (integer)
//does not treat negative as two's complement
Jmat.BigInt.lshift = function(a, b) {
var B = Jmat.BigInt;
if(b == 0) return a;
if(b < 0) return B.rshift(a, -b);
a = B.cast(a, Jmat.BigInt.ARRAYBASE_); //ensure power of two base
var result = new B([], a.radix);
var byteshift = Math.floor(b / B.ARRAYBASE_BITS_);
var bitshift = b % B.ARRAYBASE_BITS_;
if(bitshift == 0) {
for(var i = 0; i < a.a.length; i++) result.a[i] = a.a[i];
for(var i = 0; i < byteshift; i++) result.a.push(0);
} else {
result.a = [];
// if byte = xxxxxxxx and bitshift is e.g. 3, then lmask and rmask are indicated by: lllllrrr (with lsb on the right),
// so lmask would be rmask 7, rmask 248 (0b11111000)
var lmask = ((1 << (B.ARRAYBASE_BITS_ - bitshift)) - 1) << bitshift;
var rmask = (1 << bitshift) - 1;
for(var i = 0; i <= a.a.length; i++) {
//xxxxxxxx xxxxxxxxx aaaaaaaa xxxxxxxx
//xxxxxxxx xxxxxxaaa aaaaaxxx xxxxxxxx
var al = (i > 0) ? a.a[i - 1] : 0;
var ar = (i < a.a.length) ? a.a[i] : 0;
var r = ((ar << bitshift) >> B.ARRAYBASE_BITS_) & rmask;
var l = (al << bitshift) & lmask;
var v = l | r;
if(v || result.a.length) result.a.push(v);
}
for(var i = 0; i < byteshift; i++) result.a.push(0);
if(result.a.length == 0) result.a = [0];
}
if(a.minus) result = result.neg();
return result;
};
Jmat.BigInt.prototype.lshift = function(b) {
return Jmat.BigInt.lshift(this, b);
};
//shift right by b bits
//a is bigint, b is regular js number (integer)
//does not treat negative as two's complement
Jmat.BigInt.rshift = function(a, b) {
var B = Jmat.BigInt;
if(b == 0) return a;
if(b < 0) return B.lshift(a, -b);
a = B.cast(a, Jmat.BigInt.ARRAYBASE_); //ensure power of two base
var result = new B([], a.radix);
var byteshift = Math.floor(b / B.ARRAYBASE_BITS_);
var bitshift = b % B.ARRAYBASE_BITS_;
if(bitshift == 0) {
result.a = [];
for(var i = 0; i < a.a.length - byteshift; i++) result.a.push(a.a[i]);
if(result.a.length == 0) result.a = [0];
} else {
result.a = [];
// if byte = xxxxxxxx and bitshift is e.g. 3, then lmask and rmask are indicated by: lllrrrrr (with lsb on the right),
// so lmask would be rmask 31, rmask 224
var lmask = ((1 << bitshift) - 1) << (B.ARRAYBASE_BITS_ - bitshift);
var rmask = (1 << (B.ARRAYBASE_BITS_ - bitshift)) - 1;
for(var i = 0; i < a.a.length - byteshift; i++) {
//xxxxxxxx xxxxxxxxx aaaaaaaa xxxxxxxx
//xxxxxxxx xxxxxxxxx xxxaaaaa aaaxxxxx
var al = (i > 0) ? a.a[i - 1] : 0;
var ar = (i < a.a.length) ? a.a[i] : 0;
var l = ((al << B.ARRAYBASE_BITS_) >> bitshift) & lmask;
var r = (ar >> bitshift) & rmask;
result.a.push(l | r);
}
if(result.a.length == 0) result.a = [0];
}
if(a.minus) result = result.neg();
B.stripInPlace_(result.a);
return result;
};
Jmat.BigInt.prototype.rshift = function(b) {
return Jmat.BigInt.rshift(this, b);
};
// result is always positive. If a or b is negative, the two's complement representation is taken.
// This allows using and for modulo and have correct positive result for negative input.
Jmat.BigInt.bitand = function(a, b) {
var B = Jmat.BigInt;
//ensure power of two base
a = B.cast(a, B.ARRAYBASE_);
b = B.cast(b, B.ARRAYBASE_);
if(a.minus || b.minus) {
// even though internal representation isn't so, use twos complement
var n = Math.max(B.getNumBits(a), B.getNumBits(b));
if(a.minus) a = a.addr(1).bitnot(n);
if(b.minus) b = b.addr(1).bitnot(n);
}
var result = new B([], B.ARRAYBASE_);
var num = Math.min(a.a.length, b.a.length);
for(var i = 0; i < num; i++) {
var ax = a.a[a.a.length - 1 - i] || 0;
var bx = b.a[b.a.length - 1 - i] || 0;
result.a[num - 1 - i] = ax & bx;
}
return result;
};
Jmat.BigInt.prototype.bitand = function(b) {
return Jmat.BigInt.bitand(this, b);
};
// same as bitand, but with b a regular JS number (max 31 bits), and returning a regular JS number
Jmat.BigInt.bitandr = function(a, b) {
var B = Jmat.BigInt;
if(a.minus) {
// TODO: make also this case faster
return B.bitand(a, B(b)).toInt();
}
a = B.cast(a, (b < B.ARRAYBASE_) ? B.ARRAYBASE_ : 2147483648);
return a.a[a.a.length - 1] & b;
};
// result is always positive. If a or b is negative, their two's complement representation is taken.
Jmat.BigInt.bitor = function(a, b) {
var B = Jmat.BigInt;
//ensure power of two base
a = B.cast(a, B.ARRAYBASE_);
b = B.cast(b, B.ARRAYBASE_);
if(a.minus || b.minus) {
// even though internal representation isn't so, use twos complement
var n = Math.max(B.getNumBits(a), B.getNumBits(b));
if(a.minus) a = a.addr(1).bitnot(n);
if(b.minus) b = b.addr(1).bitnot(n);
}
var result = new B([], B.ARRAYBASE_);
var num = Math.max(a.a.length, b.a.length);
for(var i = 0; i < num; i++) {
var ax = a.a[a.a.length - 1 - i] || 0;
var bx = b.a[b.a.length - 1 - i] || 0;
result.a[num - 1 - i] = ax | bx;
}
return result;
};
Jmat.BigInt.prototype.bitor = function(b) {
return Jmat.BigInt.bitor(this, b);
};
// result is always positive. If a or b is negative, their two's complement representation is taken.
Jmat.BigInt.bitxor = function(a, b) {
var B = Jmat.BigInt;
//ensure power of two base
a = B.cast(a, B.ARRAYBASE_);
b = B.cast(b, B.ARRAYBASE_);
if(a.minus || b.minus) {
// even though internal representation isn't so, use twos complement
var n = Math.max(B.getNumBits(a), B.getNumBits(b));
if(a.minus) a = a.addr(1).bitnot(n);
if(b.minus) b = b.addr(1).bitnot(n);
}
var result = new B([], B.ARRAYBASE_);
var num = Math.max(a.a.length, b.a.length);
for(var i = 0; i < num; i++) {
var ax = a.a[a.a.length - 1 - i] || 0;
var bx = b.a[b.a.length - 1 - i] || 0;
result.a[num - 1 - i] = ax ^ bx;
}
return result;
};
Jmat.BigInt.prototype.bitxor = function(b) {
return Jmat.BigInt.bitxor(this, b);
};
// Inverts all the bits of a, and treats as if it's in two's complement
// So gives -(a+1) as result. Result always has opposite sign
// If you want to only invert bits and keep positive, do one of the following:
// -use bitnot
// -use bitxor with as many 1's as you want to invert.
// -add N to the result, where N is 1<<n, where n is amount of bits
Jmat.BigInt.bitneg = function(a) {
var B = Jmat.BigInt;
a = B.cast(a, Jmat.BigInt.ARRAYBASE_);
a = a.addr(1);
a = a.neg();
return a;
};
Jmat.BigInt.prototype.bitneg = function() {
return Jmat.BigInt.bitneg(this);
};
//negates all bits up to the one more than the msb. Ignores (but keeps) sign.
//if opt_bits isn't given, abs of result is always larger: the 0 in front of the msb becomes 1.
//if opt_bits is given, instead negates that many bits
//see bitneg and its comment for alternatives.
Jmat.BigInt.bitnot = function(a, opt_bits) {
if(opt_bits <= 0) return a;
var B = Jmat.BigInt;
a = B.cast(a, B.ARRAYBASE_);
var ar = B.maybecopystrip_(a.a);
var result;
var xinv = B.ARRAYBASE_ - 1; //xor invertor
if(opt_bits != undefined) {
result = B(0);
var b = opt_bits % B.ARRAYBASE_BITS_;
var n = Math.ceil(opt_bits / B.ARRAYBASE_BITS_);
var mask = (1 << b) - 1;
for(var i = 0; i < n; i++) {
var x = ar[ar.length - n + i] || 0;
result.a[i] = x ^ xinv;
if(i == 0 && b != 0) result.a[i] &= mask;
}
} else {
if(!ar.length) return a;
var b = Jmat.Real.ilog2(ar[0]) + 1;
if(b == 0) {
result = B(1); // the new MSB
for(var i = 0; i < ar.length; i++) result.a.push(ar[i] ^ xinv);
} else {
var mask = (2 << b) - 1; // 2<< instead of 1<< because a new MSB gets added
result = B(0);
result.a[0] = (ar[0] ^ xinv) & mask;
for(var i = 1; i < ar.length; i++) result.a[i] = ar[i] ^ xinv;
}
}
result.minus = a.minus;
return result;
};
Jmat.BigInt.prototype.bitnot = function(opt_bits) {
return Jmat.BigInt.bitnot(this, opt_bits);
};
// does NOT copy the array, on purpose (cheap operation). May return the input object.
Jmat.BigInt.abs = function(a) {
return a.abs();
};
Jmat.BigInt.prototype.abs = function() {
if(this.minus) {
return new Jmat.BigInt(this.a, this.radix, false);
}
return this;
};
// dist, cheb and manhattan all return regular real JS numbers for all types. In some types they are all the same, but not for e.g. Complex or Matrix.
// Euclidean distance
Jmat.BigInt.dist = function(a, b) {
return a.sub(b).abs();
};
//Chebyshev distance
Jmat.BigInt.cheb = function(a, b) {
return Jmat.BigInt.dist(a, b);
};
//Manhattan distance
Jmat.BigInt.manhattan = function(a, b) {
return Jmat.BigInt.dist(a, b);
};
// does NOT copy the array, on purpose (cheap operation).
Jmat.BigInt.neg = function(a) {
return a.neg();
};
Jmat.BigInt.prototype.neg = function() {
return new Jmat.BigInt(this.a, this.radix, !this.minus);
};
// result is regular JS number, -1 or 1 (does NOT return 0 for zero, use "sign" for that)
Jmat.BigInt.getSign = function(a) {
return a.getSign();
};
Jmat.BigInt.prototype.getSign = function() {
return this.minus ? -1 : 1;
};
Jmat.BigInt.nonZero = function(a) {
return a.nonZero();
};
Jmat.BigInt.prototype.nonZero = function() {
for(var i = 0; i < this.a.length; i++) {
if(this.a[i]) return true;
}
return false;
};
// result is regular JS number, -1, 0 or 1 (slower than "getSign")
Jmat.BigInt.sign = function(a) {
return a.sign();
};
Jmat.BigInt.prototype.sign = function() {
return this.nonZero() ? this.getSign() : 0;
};
Jmat.BigInt.add = function(a, b) {
return a.add(b);
};
Jmat.BigInt.prototype.add = function(b) {
if(this.minus != b.minus) {
return this.sub(b.neg());
}
b = Jmat.BigInt.cast(b, this.radix); //must have same radix
return new Jmat.BigInt(Jmat.BigInt.baseloop_(this.a, 0, 1, b.a, 0, 1, 0, this.radix, false), this.radix, this.minus);
};
Jmat.BigInt.addr = function(a, b) {
return a.addr(b);
};
Jmat.BigInt.prototype.addr = function(b) {
// TODO: make more efficient by usign baseloop directly
return this.add(Jmat.BigInt.fromInt(b));
};
Jmat.BigInt.sub = function(a, b) {
return a.sub(b);
};
Jmat.BigInt.prototype.sub = function(b) {
if(this.minus != b.minus) {
return this.add(b.neg());
}
b = Jmat.BigInt.cast(b, this.radix); //must have same radix
if(this.abs().gte(b.abs())) {
return new Jmat.BigInt(Jmat.BigInt.baseloop_(this.a, 0, 1, b.a, 0, -1, 0, this.radix, false), this.radix, this.minus);
} else {
return new Jmat.BigInt(Jmat.BigInt.baseloop_(b.a, 0, 1, this.a, 0, -1, 0, this.radix, false), this.radix, !this.minus);
}
};
Jmat.BigInt.subr = function(a, b) {
return a.subr(b);
};
Jmat.BigInt.prototype.subr = function(b) {
// TODO: make more efficient by using baseloop directly
return this.sub(Jmat.BigInt.fromInt(b));
};
Jmat.BigInt.rsub = function(a, b) {
return a.rsub(b);
};
Jmat.BigInt.prototype.rsub = function(b) {
return this.subr(b).neg();
};
Jmat.BigInt.mul = function(a, b) {
return a.mul(b);
};
Jmat.BigInt.prototype.mul = function(b) {
b = Jmat.BigInt.cast(b, this.radix); //must have same radix
var result = Jmat.BigInt.karatsuba_(this.strip().a, b.strip().a, this.radix);
return new Jmat.BigInt(result, this.radix, this.minus != b.minus);
};
Jmat.BigInt.mulr = function(a, b) {
var result = new Jmat.BigInt([], a.radix, a.minus != (b < 0));
result.a = Jmat.BigInt.baseloop_(a.a, 0, b, [], 0, 1, 0, result.radix, false);
return result;
};
Jmat.BigInt.prototype.mulr = function(b) {
var result = new Jmat.BigInt([], this.radix, this.minus != (b < 0));
result.a = Jmat.BigInt.baseloop_(this.a, 0, b, [], 0, 1, 0, result.radix, false);
return result;
};
// Karatsuba multiplication. a and b are arrays with leading zeroes stripped.
// NOTE: radix*radix should be < 2**53, so radix should be < 94906265
Jmat.BigInt.karatsuba_ = function(a, b, radix) {
if(a.length <= 1 || b.length <= 1) return Jmat.BigInt.schoolmul_(a, b, radix);
// ensure a is the one with longest length
if(a.length < b.length) return Jmat.BigInt.karatsuba_(b, a, radix);
// Karatsuba is only faster for really large numbers.
if(a.length <= 20) return Jmat.BigInt.schoolmul_(a, b, radix);
var m = Math.floor(a.length / 2);
var mb = b.length - (a.length - m);
var a0 = a.slice(0, m);
var a1 = a.slice(m, a.length);
var b0 = (mb <= 0 ? [0] : b.slice(0, mb));
var b1 = (mb <= 0 ? b : b.slice(mb, b.length));
if(mb <= 0) {
// Size difference between the numbers very big. Only one is split in half.
var x = Jmat.BigInt.karatsuba_(a0, b1, radix);
var y = Jmat.BigInt.karatsuba_(a1, b1, radix);
return Jmat.BigInt.baseloop_(x, a.length - m, 1, y, 0, 1, 0, radix, false);
} else {
var a01 = Jmat.BigInt.baseloop_(a0, 0, 1, a1, 0, 1, 0, radix, false);
var b01 = Jmat.BigInt.baseloop_(b0, 0, 1, b1, 0, 1, 0, radix, false);
var x = Jmat.BigInt.karatsuba_(a0, b0, radix);
var y = Jmat.BigInt.karatsuba_(a1, b1, radix);
var z = Jmat.BigInt.karatsuba_(a01, b01, radix);
var xy = Jmat.BigInt.baseloop_(x, 0, 1, y, 0, 1, 0, radix, false);
var k = Jmat.BigInt.baseloop_(z, 0, 1, xy, 0, -1, 0, radix, false);
var s1 = a.length - m;
var s2 = s1 * 2;
var ky = Jmat.BigInt.baseloop_(k, s1, 1, y, 0, 1, 0, radix, false);
return Jmat.BigInt.baseloop_(x, s2, 1, ky, 0, 1, 0, radix, false);
}
};
// Schoolbook multiplication. a and b are arrays with leading zeroes stripped.
// NOTE: radix*radix should be < 2**53, so radix should be < 94906265
Jmat.BigInt.schoolmul_ = function(a, b, radix) {
if(a.length == 1 && a[0] == 0) return [0];
if(a.length == 1 && a[0] == 1) return b;
if(b.length == 1 && b[0] == 0) return [0];
if(b.length == 1 && b[0] == 1) return a;
// ensure a is the one with longest length, the loop below is faster that way
if(a.length < b.length) return Jmat.BigInt.schoolmul_(b, a, radix);
var result = [0];
var ashift = 0;
for(var j = 0; j < b.length; j++) {
var d = b[b.length - j - 1];
result = Jmat.BigInt.baseloop_(a, ashift, d, result, 0, 1, 0, radix, false);
ashift++; // left shift a
}
return result;
};
//returns 1 if a > b, -1 if b > a, 0 if equal.
Jmat.BigInt.compare = function(a, b) {
return a.compare(b);
};
Jmat.BigInt.prototype.compare = function(b) {
if(b.minus != this.minus) {
var as = this.sign();
var bs = b.sign();
if(as == bs) return 0; //both signs are 0
if(as < bs) return -1;
return 1;
}
if(b.radix != this.radix) b = Jmat.BigInt.convertBase(b, this.radix);
var l = Math.max(this.a.length, b.a.length);
for(var i = 0; i < l; i++) {
var ai = i - l + this.a.length;
var bi = i - l + b.a.length;
var av = this.a[ai] || 0; // 0 if out of bounds
var bv = b.a[bi] || 0; // 0 if out of bounds
if(av < bv) return -1 * this.getSign();
if(av > bv) return 1 * this.getSign();
}
return 0;
};
//Same as compare, but b is regular JS number
Jmat.BigInt.comparer = function(a, b) {
return a.comparer(b);
};
Jmat.BigInt.prototype.comparer = function(b) {
if((b < 0) != this.minus) {
var as = this.sign();
var bs = Math.sign(b);
if(as == bs) return 0; //both signs are 0
if(as < bs) return -1;
return 1;
}
var sign = this.getSign();
b = Math.abs(b);
var r = 0;
for(var i = 0; i < this.a.length && r <= b; i++) {
r *= this.radix;
r += this.a[i];
}
return ((r < b) ? -1 : (r == b ? 0 : 1)) * sign;
};
Jmat.BigInt.eq = function(a, b) { return Jmat.BigInt.compare(a, b) == 0; };
Jmat.BigInt.prototype.eq = function(b) { return Jmat.BigInt.compare(this, b) == 0; };
Jmat.BigInt.eqr = function(a, b) { return Jmat.BigInt.comparer(a, b) == 0; };
Jmat.BigInt.prototype.eqr = function(b) { return Jmat.BigInt.comparer(this, b) == 0; };
Jmat.BigInt.neq = function(a, b) { return Jmat.BigInt.compare(a, b) != 0; };
Jmat.BigInt.prototype.neq = function(b) { return Jmat.BigInt.compare(this, b) != 0; };
Jmat.BigInt.neqr = function(a, b) { return Jmat.BigInt.comparer(a, b) != 0; };
Jmat.BigInt.prototype.neqr = function(b) { return Jmat.BigInt.comparer(this, b) != 0; };
Jmat.BigInt.gt = function(a, b) { return Jmat.BigInt.compare(a, b) > 0; };
Jmat.BigInt.prototype.gt = function(b) { return Jmat.BigInt.compare(this, b) > 0; };
Jmat.BigInt.gtr = function(a, b) { return Jmat.BigInt.comparer(a, b) > 0; };
Jmat.BigInt.prototype.gtr = function(b) { return Jmat.BigInt.comparer(this, b) > 0; };
Jmat.BigInt.lt = function(a, b) { return Jmat.BigInt.compare(a, b) < 0; };
Jmat.BigInt.prototype.lt = function(b) { return Jmat.BigInt.compare(this, b) < 0; };
Jmat.BigInt.ltr = function(a, b) { return Jmat.BigInt.comparer(a, b) < 0; };
Jmat.BigInt.prototype.ltr = function(b) { return Jmat.BigInt.comparer(this, b) < 0; };
Jmat.BigInt.gte = function(a, b) { return Jmat.BigInt.compare(a, b) >= 0; };
Jmat.BigInt.prototype.gte = function(b) { return Jmat.BigInt.compare(this, b) >= 0; };
Jmat.BigInt.gter = function(a, b) { return Jmat.BigInt.comparer(a, b) >= 0; };
Jmat.BigInt.prototype.gter = function(b) { return Jmat.BigInt.comparer(this, b) >= 0; };
Jmat.BigInt.lte = function(a, b) { return Jmat.BigInt.compare(a, b) <= 0; };
Jmat.BigInt.prototype.lte = function(b) { return Jmat.BigInt.compare(this, b) <= 0; };
Jmat.BigInt.lter = function(a, b) { return Jmat.BigInt.comparer(a, b) <= 0; };
Jmat.BigInt.prototype.lter = function(b) { return Jmat.BigInt.comparer(this, b) <= 0; };
// Returns integer square root (floor), or undefined if negative
Jmat.BigInt.sqrt = function(a) {
var B = Jmat.BigInt;
if(a.eqr(0)) return B(0);
if(a.minus) return undefined;
var low = B([0], a.radix);
var high = Jmat.BigInt.copystrip_(a);
if(high.a.length == 1 && high.a[0] == 1) low = B([1], a.radix); //the algorithm below fails on [1]
high.a = high.a.slice(0, Math.ceil(high.a.length / 2) + 1); // initial estimate for max of sqrt: half amount of digits
if(a.radix > 16) high.a[0] = Math.min(high.a[0], Math.ceil(Math.sqrt(high.a[0] + 1))); // further improve estimate by setting most significant digit to sqrt of itself
var one = B([1], a.radix);
var result;
for (;;) {
var mid = low.add(high).divr(2);
var rr = mid.mul(mid);
var c = B.compare(rr, a);
if(c == 0) {
result = mid;
break;
}
else if(c < 0) low = mid;
else high = mid;
if(B.compare(high.sub(low), one) <= 0) {
result = low;
break;
}
}
return result;
};
// x is odd integer