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