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