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