rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 27 Jan 2016 06:46:03 +0100
changeset 1866 5447274f0321
parent 1858 4dea14fafc31
child 1867 60e77363650a
permissions -rw-r--r--
Introducing Number.prototype.__bit64 with all 64-bit operations
     1 // empty line needed here
     2 
     3 (function(numberPrototype) {
     4     function add32(x, y) {
     5         return (x + y) | 0;
     6     };
     7     function mul32(x, y) {
     8         return (((x * (y >> 16)) << 16) + x * (y & 0xFFFF)) | 0;
     9     };
    10     var __m32 = 0xFFFFFFFF;
    11 
    12     numberPrototype.next32 = function(low) {
    13         if (this === 0) {
    14             return low;
    15         }
    16         var l = new Number(low);
    17         l.hi = this | 0;
    18         return l;
    19     };
    20 
    21     numberPrototype.high32 = function() {
    22         return this.hi ? this.hi : (Math.floor(this / (__m32 + 1))) | 0;
    23     };
    24     numberPrototype.toFP = function() {
    25         return this.hi ? this.hi * (__m32 + 1) + this : this;
    26     };
    27     numberPrototype.toLong = function() {
    28         var hi = (this / (__m32 + 1)) | 0;
    29         var low = (this % (__m32 + 1)) | 0;
    30         if (low < 0) {
    31             low += __m32 + 1;
    32         }
    33 
    34         if (this < 0) {
    35             hi -= 1;
    36         }
    37 
    38         return hi.next32(low);
    39     };
    40 
    41     numberPrototype.toExactString = function() {
    42         if (this.hi) {
    43             // check for Long.MIN_VALUE
    44             if ((this.hi == (0x80000000 | 0)) && (this == 0)) {
    45                 return '-9223372036854775808';
    46             }
    47             var res = 0;
    48             var a = [6, 9, 2, 7, 6, 9, 4, 9, 2, 4];
    49             var s = '';
    50             var digit;
    51             var neg = this.hi < 0;
    52             if (neg) {
    53                 var x = this.neg64();
    54                 var hi = x.hi;
    55                 var low = x;
    56             } else {
    57                 var hi = this.hi;
    58                 var low = this;
    59             }
    60             for (var i = 0; i < a.length; i++) {
    61                 res += hi * a[i];
    62                 var low_digit = low % 10;
    63                 digit = (res % 10) + low_digit;
    64 
    65                 low = Math.floor(low / 10);
    66                 res = Math.floor(res / 10);
    67 
    68                 if (digit >= 10) {
    69                     digit -= 10;
    70                     res++;
    71                 }
    72                 s = String(digit).concat(s);
    73             }
    74             s = String(res).concat(s).replace(/^0+/, '');
    75             return (neg ? '-' : '').concat(s);
    76         }
    77         return String(this);
    78     };
    79 
    80     numberPrototype.add64 = function(x) {
    81         var low = this + x;
    82         carry = 0;
    83         if (low > __m32) {
    84             carry = 1;
    85             low -= (__m32 + 1);
    86         }
    87         var hi = (this.high32() + x.high32() + carry) | 0;
    88         return hi.next32(low);
    89     };
    90 
    91     numberPrototype.sub64 = function(x) {
    92         var low = this - x;
    93         carry = 0;
    94         if (low < 0) {
    95             carry = 1;
    96             low += (__m32 + 1);
    97         }
    98         var hi = (this.high32() - x.high32() - carry) | 0;
    99         return hi.next32(low);
   100     };
   101 
   102     numberPrototype.mul64 = function(x) {
   103         var low = mul32(this, x);
   104         low += (low < 0) ? (__m32 + 1) : 0;
   105         // first count upper 32 bits of (this.low * x.low)
   106         var hi_hi = 0;
   107         var hi_low = 0;
   108         var m = 1;
   109         for (var i = 0; i < 32; i++) {
   110             if (x & m) {
   111                 hi_hi += this >>> 16;
   112                 hi_low += this & 0xFFFF
   113             }
   114             hi_low >>= 1;
   115             hi_low += (hi_hi & 1) ? 0x8000 : 0;
   116             hi_hi >>= 1;
   117             m <<= 1;
   118         }
   119         var hi = (hi_hi << 16) + hi_low;
   120 
   121         var m1 = mul32(this.high32(), x);
   122         var m2 = mul32(this, x.high32());
   123         hi = add32(add32(hi, m1), m2);
   124 
   125         return hi.next32(low);
   126     };
   127 
   128     numberPrototype.and64 = function(x) {
   129         var low = this & x;
   130         low += (low < 0) ? (__m32 + 1) : 0;
   131         if (this.hi && x.hi) {
   132             var hi = this.hi & x.hi;
   133             return hi.next32(low);
   134         }
   135         ;
   136         return low;
   137     };
   138 
   139     numberPrototype.or64 = function(x) {
   140         var low = this | x;
   141         low += (low < 0) ? (__m32 + 1) : 0;
   142         if (this.hi || x.hi) {
   143             var hi = this.hi | x.hi;
   144             return hi.next32(low);
   145         }
   146         ;
   147         return low;
   148     };
   149 
   150     numberPrototype.xor64 = function(x) {
   151         var low = this ^ x;
   152         low += (low < 0) ? (__m32 + 1) : 0;
   153         if (this.hi || x.hi) {
   154             var hi = this.hi ^ x.hi;
   155             return hi.next32(low);
   156         }
   157         ;
   158         return low;
   159     };
   160 
   161     numberPrototype.shl64 = function(x) {
   162         x &= 0x3f;
   163         if (x == 0) return this;
   164         if (x >= 32) {
   165             var hi = this << (x - 32);
   166             return hi.next32(0);
   167         } else {
   168             var hi = this.high32() << x;
   169             var low_reminder = this >> (32 - x);
   170             hi |= low_reminder;
   171             var low = this << x;
   172             low += (low < 0) ? (__m32 + 1) : 0;
   173             return hi.next32(low);
   174         }
   175     };
   176 
   177     numberPrototype.shr64 = function(x) {
   178         x &= 0x3f;
   179         if (x == 0) return this;
   180         if (x >= 32) {
   181             var low = this.high32() >> (x - 32);
   182             low += (low < 0) ? (__m32 + 1) : 0;
   183             return low;
   184         } else {
   185             var low = this >>> x;
   186             var hi_reminder = this.high32() << (32 - x);
   187             low |= hi_reminder;
   188             low += (low < 0) ? (__m32 + 1) : 0;
   189             var hi = this.high32() >> x;
   190             return hi.next32(low);
   191         }
   192     };
   193 
   194     numberPrototype.ushr64 = function(x) {
   195         x &= 0x3f;
   196         if (x == 0) return this;
   197         if (x >= 32) {
   198             var low = this.high32() >>> (x - 32);
   199             low += (low < 0) ? (__m32 + 1) : 0;
   200             return low;
   201         } else {
   202             var low = this >>> x;
   203             var hi_reminder = this.high32() << (32 - x);
   204             low |= hi_reminder;
   205             low += (low < 0) ? (__m32 + 1) : 0;
   206             var hi = this.high32() >>> x;
   207             return hi.next32(low);
   208         }
   209     };
   210 
   211     numberPrototype.compare64 = function(x) {
   212         if (this.high32() === x.high32()) {
   213             return (this < x) ? -1 : ((this > x) ? 1 : 0);
   214         }
   215         return (this.high32() < x.high32()) ? -1 : 1;
   216     };
   217 
   218     numberPrototype.neg64 = function() {
   219         var hi = this.high32();
   220         var low = this;
   221         if ((hi === 0) && (low < 0)) {
   222             return -low;
   223         }
   224         hi = ~hi;
   225         low = ~low;
   226         low += (low < 0) ? (__m32 + 1) : 0;
   227         var ret = hi.next32(low);
   228         return ret.add64(1);
   229     };
   230     
   231     function __handleDivByZero() {
   232         var exception = new vm.java_lang_ArithmeticException;
   233         vm.java_lang_ArithmeticException(false).constructor
   234           .cons__VLjava_lang_String_2.call(exception, "/ by zero");
   235 
   236         throw exception;
   237     }
   238 
   239     function __Int64(hi32, lo32) {
   240         this.hi32 = hi32 | 0;
   241         this.lo32 = lo32 | 0;
   242 
   243         this.get32 = function(bitIndex) {
   244             var v0;
   245             var v1;
   246             bitIndex += 32;
   247             var selector = bitIndex >>> 5;
   248             switch (selector) {
   249                 case 0:
   250                     v0 = 0;
   251                     v1 = this.lo32;
   252                     break;
   253                 case 1:
   254                     v0 = this.lo32;
   255                     v1 = this.hi32;
   256                     break;
   257                 case 2:
   258                     v0 = this.hi32;
   259                     v1 = 0;
   260                     break
   261                 default:
   262                     return 0;
   263             }
   264 
   265             var shift = bitIndex & 31;
   266             if (shift === 0) {
   267                 return v0;
   268             }
   269 
   270             return (v1 << (32 - shift)) | (v0 >>> shift);
   271         }
   272 
   273         this.get16 = function(bitIndex) {
   274             return this.get32(bitIndex) & 0xffff;
   275         }
   276 
   277         this.set16 = function(bitIndex, value) {
   278             bitIndex += 32;
   279             var shift = bitIndex & 15;
   280             var svalue = (value & 0xffff) << shift; 
   281             var smask = 0xffff << shift;
   282             var selector = bitIndex >>> 4;
   283             switch (selector) {
   284                 case 0:
   285                     break;
   286                 case 1:
   287                     this.lo32 = (this.lo32 & ~(smask >>> 16))
   288                                     | (svalue >>> 16);
   289                     break;
   290                 case 2:
   291                     this.lo32 = (this.lo32 & ~smask) | svalue;
   292                     break;
   293                 case 3:
   294                     this.lo32 = (this.lo32 & ~(smask << 16))
   295                                     | (svalue << 16);
   296                     this.hi32 = (this.hi32 & ~(smask >>> 16))
   297                                     | (svalue >>> 16);
   298                     break;
   299                 case 4:
   300                     this.hi32 = (this.hi32 & ~smask) | svalue;
   301                     break;
   302                 case 5:
   303                     this.hi32 = (this.hi32 & ~(smask << 16))
   304                                     | (svalue << 16);
   305                     break;
   306             }
   307         }
   308 
   309         this.getDigit = function(index, shift) {
   310             return this.get16((index << 4) - shift);
   311         }
   312 
   313         this.getTwoDigits = function(index, shift) {
   314             return this.get32(((index - 1) << 4) - shift);
   315         }
   316 
   317         this.setDigit = function(index, shift, value) {
   318             this.set16((index << 4) - shift, value);
   319         }
   320 
   321         this.countSignificantDigits = function() {
   322             var sd;
   323             var remaining;
   324 
   325             if (this.hi32 === 0) {
   326                 if (this.lo32 === 0) {
   327                     return 0;
   328                 }
   329 
   330                 sd = 2;
   331                 remaining = this.lo32;
   332             } else {
   333                 sd = 4;
   334                 remaining = this.hi32;
   335             }
   336 
   337             if (remaining < 0) {
   338                 return sd;
   339             }
   340 
   341             return (remaining < 65536) ? sd - 1 : sd;
   342         }
   343         
   344         this.toNumber = function() {
   345             var lo32 = this.lo32;
   346             if (lo32 < 0) {
   347                 lo32 += 0x100000000;
   348             }
   349 
   350             return this.hi32.next32(lo32);
   351         }
   352     }
   353 
   354     function __countLeadingZeroes16(number) {
   355         var nlz = 0;
   356 
   357         if (number < 256) {
   358             nlz += 8;
   359             number <<= 8;
   360         }
   361 
   362         if (number < 4096) {
   363             nlz += 4;
   364             number <<= 4;
   365         }
   366 
   367         if (number < 16384) {
   368             nlz += 2;
   369             number <<= 2;
   370         }
   371 
   372         return (number < 32768) ? nlz + 1 : nlz;
   373     }
   374     
   375     // q = u / v; r = u - q * v;
   376     // v != 0
   377     function __div64(q, r, u, v) {
   378         var m = u.countSignificantDigits();
   379         var n = v.countSignificantDigits();
   380 
   381         q.hi32 = q.lo32 = 0;
   382 
   383         if (n === 1) {
   384             // v has single digit
   385             var vd = v.getDigit(0, 0);
   386             var carry = 0;
   387             for (var i = m - 1; i >= 0; --i) {
   388                 var ui = (carry << 16) | u.getDigit(i, 0);
   389                 if (ui < 0) {
   390                     ui += 0x100000000;
   391                 }
   392                 var qi = (ui / vd) | 0;
   393                 q.setDigit(i, 0, qi);
   394                 carry = ui - qi * vd;
   395             }
   396 
   397             r.hi32 = 0;
   398             r.lo32 = carry;
   399             return;
   400         }
   401 
   402         r.hi32 = u.hi32;  
   403         r.lo32 = u.lo32;
   404 
   405         if (m < n) {
   406             return;
   407         }
   408 
   409         // Normalize
   410         var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0));
   411 
   412         var vd1 = v.getDigit(n - 1, nrm);                
   413         var vd0 = v.getDigit(n - 2, nrm);
   414         for (var j = m - n; j >= 0; --j) {
   415             // Calculate qj estimate
   416             var ud21 = r.getTwoDigits(j + n, nrm);
   417             var ud2 = ud21 >>> 16;
   418             if (ud21 < 0) {
   419                 ud21 += 0x100000000;
   420             }
   421 
   422             var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0);
   423             var rest = ud21 - qest * vd1;
   424 
   425             // 0 <= (qest - qj) <= 2
   426 
   427             // Refine qj estimate
   428             var ud0 = r.getDigit(j + n - 2, nrm);
   429             while ((qest * vd0) > ((rest * 0x10000) + ud0)) {
   430                 --qest;
   431                 rest += vd1;
   432             }
   433 
   434             // 0 <= (qest - qj) <= 1
   435             
   436             // Multiply and subtract
   437             var carry = 0;
   438             for (var i = 0; i < n; ++i) {
   439                 var vi = qest * v.getDigit(i, nrm);
   440                 var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff);
   441                 r.setDigit(i + j, nrm, ui);
   442                 carry = (vi >>> 16) - (ui >> 16);
   443             }
   444             var uj = ud2 - carry;
   445 
   446             if (uj < 0) {
   447                 // qest - qj = 1
   448 
   449                 // Add back
   450                 --qest;
   451                 var carry = 0;
   452                 for (var i = 0; i < n; ++i) {
   453                     var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm)
   454                                  + carry;
   455                     r.setDigit(i + j, nrm, ui);
   456                     carry = ui >> 16;
   457                 }
   458                 uj += carry;
   459             }
   460 
   461             q.setDigit(j, 0, qest);
   462             r.setDigit(j + n, nrm, uj);
   463         }
   464     }
   465 
   466     numberPrototype.div64 = function(x) {
   467         var negateResult = false;
   468         var u, v;
   469 
   470         if ((this.high32() & 0x80000000) != 0) {
   471             u = this.neg64();
   472             negateResult = !negateResult;
   473         } else {
   474             u = this;        
   475         }
   476 
   477         if ((x.high32() & 0x80000000) != 0) {
   478             v = x.neg64();
   479             negateResult = !negateResult;
   480         } else {
   481             v = x;
   482         }
   483 
   484         if ((v == 0) && (v.high32() === 0)) {
   485             __handleDivByZero();
   486         }
   487 
   488         if (u.high32() === 0) {
   489             if (v.high32() === 0) {
   490                 var result = (u / v) | 0;
   491                 return negateResult ? result.neg64() : result; 
   492             }
   493 
   494             return 0;
   495         }
   496 
   497         var u64 = new __Int64(u.high32(), u);
   498         var v64 = new __Int64(v.high32(), v);
   499         var q64 = new __Int64(0, 0);
   500         var r64 = new __Int64(0, 0);
   501 
   502         __div64(q64, r64, u64, v64);
   503 
   504         var result = q64.toNumber();
   505         return negateResult ? result.neg64() : result; 
   506     }
   507 
   508     numberPrototype.mod64 = function(x) {
   509         var negateResult = false;
   510         var u, v;
   511         
   512         if ((this.high32() & 0x80000000) != 0) {
   513             u = this.neg64();
   514             negateResult = !negateResult;
   515         } else {
   516             u = this;        
   517         }
   518 
   519         if ((x.high32() & 0x80000000) != 0) {
   520             v = x.neg64();
   521         } else {
   522             v = x;
   523         }
   524 
   525         if ((v == 0) && (v.high32() === 0)) {
   526             __handleDivByZero();
   527         }
   528 
   529         if (u.high32() === 0) {
   530             var result = (v.high32() === 0) ? (u % v) : u;
   531             return negateResult ? result.neg64() : result; 
   532         }
   533 
   534         var u64 = new __Int64(u.high32(), u);
   535         var v64 = new __Int64(v.high32(), v);
   536         var q64 = new __Int64(0, 0);
   537         var r64 = new __Int64(0, 0);
   538 
   539         __div64(q64, r64, u64, v64);
   540 
   541         var result = r64.toNumber();
   542         return negateResult ? result.neg64() : result; 
   543     }
   544 
   545     var b = numberPrototype['__bit64'] = {};
   546     b.add64 = function(x,y) { return x.add64(y); };
   547     b.sub64 = function(x,y) { return x.sub64(y); };
   548     b.mul64 = function(x,y) { return x.mul64(y); };
   549     b.div64 = function(x,y) { return x.div64(y); };
   550     b.mod64 = function(x,y) { return x.mod64(y); };
   551     b.and64 = function(x,y) { return x.and64(y); };
   552     b.or64 = function(x,y) { return x.or64(y); };
   553     b.xor64 = function(x,y) { return x.xor64(y); };
   554     b.neg64 = function(x) { return x.neg64(); };
   555     b.shl64 = function(x,y) { return x.shl64(y); };
   556     b.shr64 = function(x,y) { return x.shr64(y); };
   557     b.ushr64 = function(x,y) { return x.ushr64(y); };
   558     b.compare64 = function(x,y) { return y.compare64(x); };
   559 })(Number.prototype);
   560 
   561 vm.java_lang_Number(false);