Support for Long division + tests arithmetic
authorLubomir Nerad <lubomir.nerad@oracle.com>
Tue, 05 Feb 2013 16:40:01 +0100
brancharithmetic
changeset 676eecf6077ec4e
parent 675 7d3da112e2c1
child 677 1ff540c1650f
Support for Long division + tests
emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js
vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java
vm/src/test/java/org/apidesign/vm4brwsr/Numbers.java
     1.1 --- a/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js	Tue Feb 05 16:32:14 2013 +0100
     1.2 +++ b/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js	Tue Feb 05 16:40:01 2013 +0100
     1.3 @@ -113,15 +113,6 @@
     1.4      return hi.next32(low);
     1.5  };
     1.6  
     1.7 -Number.prototype.div64 = function(x) {
     1.8 -    var low = Math.floor(this.toFP() / x.toFP()); // TODO: not exact enough
     1.9 -    if (low > __m32) {
    1.10 -        var hi = Math.floor(low / (__m32+1)) | 0;
    1.11 -        return hi.next32(low % (__m32+1));
    1.12 -    }
    1.13 -    return low;
    1.14 -};
    1.15 -
    1.16  Number.prototype.and64 = function(x) {
    1.17      var low = this & x;
    1.18      low += (low < 0) ? (__m32+1) : 0;
    1.19 @@ -213,3 +204,313 @@
    1.20      var ret = hi.next32(low);
    1.21      return ret.add64(1);
    1.22  };
    1.23 +
    1.24 +function __initDivMod(numberPrototype) {
    1.25 +    function __Int64(hi32, lo32) {
    1.26 +        this.hi32 = hi32 | 0;
    1.27 +        this.lo32 = lo32 | 0;
    1.28 +
    1.29 +        this.get32 = function(bitIndex) {
    1.30 +            var v0;
    1.31 +            var v1;
    1.32 +            bitIndex += 32;
    1.33 +            var selector = bitIndex >>> 5;
    1.34 +            switch (selector) {
    1.35 +                case 0:
    1.36 +                    v0 = 0;
    1.37 +                    v1 = this.lo32;
    1.38 +                    break;
    1.39 +                case 1:
    1.40 +                    v0 = this.lo32;
    1.41 +                    v1 = this.hi32;
    1.42 +                    break;
    1.43 +                case 2:
    1.44 +                    v0 = this.hi32;
    1.45 +                    v1 = 0;
    1.46 +                    break
    1.47 +                default:
    1.48 +                    return 0;
    1.49 +            }
    1.50 +
    1.51 +            var shift = bitIndex & 31;
    1.52 +            if (shift === 0) {
    1.53 +                return v0;
    1.54 +            }
    1.55 +
    1.56 +            return (v1 << (32 - shift)) | (v0 >>> shift);
    1.57 +        }
    1.58 +
    1.59 +        this.get16 = function(bitIndex) {
    1.60 +            return this.get32(bitIndex) & 0xffff;
    1.61 +        }
    1.62 +
    1.63 +        this.set16 = function(bitIndex, value) {
    1.64 +            bitIndex += 32;
    1.65 +            var shift = bitIndex & 15;
    1.66 +            var svalue = (value & 0xffff) << shift; 
    1.67 +            var smask = 0xffff << shift;
    1.68 +            var selector = bitIndex >>> 4;
    1.69 +            switch (selector) {
    1.70 +                case 0:
    1.71 +                    break;
    1.72 +                case 1:
    1.73 +                    this.lo32 = (this.lo32 & ~(smask >>> 16))
    1.74 +                                    | (svalue >>> 16);
    1.75 +                    break;
    1.76 +                case 2:
    1.77 +                    this.lo32 = (this.lo32 & ~smask) | svalue;
    1.78 +                    break;
    1.79 +                case 3:
    1.80 +                    this.lo32 = (this.lo32 & ~(smask << 16))
    1.81 +                                    | (svalue << 16);
    1.82 +                    this.hi32 = (this.hi32 & ~(smask >>> 16))
    1.83 +                                    | (svalue >>> 16);
    1.84 +                    break;
    1.85 +                case 4:
    1.86 +                    this.hi32 = (this.hi32 & ~smask) | svalue;
    1.87 +                    break;
    1.88 +                case 5:
    1.89 +                    this.hi32 = (this.hi32 & ~(smask << 16))
    1.90 +                                    | (svalue << 16);
    1.91 +                    break;
    1.92 +            }
    1.93 +        }
    1.94 +
    1.95 +        this.getDigit = function(index, shift) {
    1.96 +            return this.get16((index << 4) - shift);
    1.97 +        }
    1.98 +
    1.99 +        this.getTwoDigits = function(index, shift) {
   1.100 +            return this.get32(((index - 1) << 4) - shift);
   1.101 +        }
   1.102 +
   1.103 +        this.setDigit = function(index, shift, value) {
   1.104 +            this.set16((index << 4) - shift, value);
   1.105 +        }
   1.106 +
   1.107 +        this.countSignificantDigits = function() {
   1.108 +            var sd;
   1.109 +            var remaining;
   1.110 +
   1.111 +            if (this.hi32 === 0) {
   1.112 +                if (this.lo32 === 0) {
   1.113 +                    return 0;
   1.114 +                }
   1.115 +
   1.116 +                sd = 2;
   1.117 +                remaining = this.lo32;
   1.118 +            } else {
   1.119 +                sd = 4;
   1.120 +                remaining = this.hi32;
   1.121 +            }
   1.122 +
   1.123 +            if (remaining < 0) {
   1.124 +                return sd;
   1.125 +            }
   1.126 +
   1.127 +            return (remaining < 65536) ? sd - 1 : sd;
   1.128 +        }
   1.129 +        
   1.130 +        this.toNumber = function() {
   1.131 +            var lo32 = this.lo32;
   1.132 +            if (lo32 < 0) {
   1.133 +                lo32 += 0x100000000;
   1.134 +            }
   1.135 +
   1.136 +            return this.hi32.next32(lo32);
   1.137 +        }
   1.138 +    }
   1.139 +
   1.140 +    function __countLeadingZeroes16(number) {
   1.141 +        var nlz = 0;
   1.142 +
   1.143 +        if (number < 256) {
   1.144 +            nlz += 8;
   1.145 +            number <<= 8;
   1.146 +        }
   1.147 +
   1.148 +        if (number < 4096) {
   1.149 +            nlz += 4;
   1.150 +            number <<= 4;
   1.151 +        }
   1.152 +
   1.153 +        if (number < 16384) {
   1.154 +            nlz += 2;
   1.155 +            number <<= 2;
   1.156 +        }
   1.157 +
   1.158 +        return (number < 32768) ? nlz + 1 : nlz;
   1.159 +    }
   1.160 +    
   1.161 +    // q = u / v; r = u - q * v;
   1.162 +    // v != 0
   1.163 +    function __div64(q, r, u, v) {
   1.164 +        var m = u.countSignificantDigits();
   1.165 +        var n = v.countSignificantDigits();
   1.166 +
   1.167 +        q.hi32 = q.lo32 = 0;
   1.168 +
   1.169 +        if (n === 1) {
   1.170 +            // v has single digit
   1.171 +            var vd = v.getDigit(0, 0);
   1.172 +            var carry = 0;
   1.173 +            for (var i = m - 1; i >= 0; --i) {
   1.174 +                var ui = (carry << 16) | u.getDigit(i, 0);
   1.175 +                if (ui < 0) {
   1.176 +                    ui += 0x100000000;
   1.177 +                }
   1.178 +                var qi = (ui / vd) | 0;
   1.179 +                q.setDigit(i, 0, qi);
   1.180 +                carry = ui - qi * vd;
   1.181 +            }
   1.182 +
   1.183 +            r.hi32 = 0;
   1.184 +            r.lo32 = carry;
   1.185 +            return;
   1.186 +        }
   1.187 +
   1.188 +        r.hi32 = u.hi32;  
   1.189 +        r.lo32 = u.lo32;
   1.190 +
   1.191 +        if (m < n) {
   1.192 +            return;
   1.193 +        }
   1.194 +
   1.195 +        // Normalize
   1.196 +        var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0));
   1.197 +
   1.198 +        var vd1 = v.getDigit(n - 1, nrm);                
   1.199 +        var vd0 = v.getDigit(n - 2, nrm);
   1.200 +        for (var j = m - n; j >= 0; --j) {
   1.201 +            // Calculate qj estimate
   1.202 +            var ud21 = r.getTwoDigits(j + n, nrm);
   1.203 +            var ud2 = ud21 >>> 16;
   1.204 +            if (ud21 < 0) {
   1.205 +                ud21 += 0x100000000;
   1.206 +            }
   1.207 +
   1.208 +            var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0);
   1.209 +            var rest = ud21 - qest * vd1;
   1.210 +
   1.211 +            // 0 <= (qest - qj) <= 2
   1.212 +
   1.213 +            // Refine qj estimate
   1.214 +            var ud0 = r.getDigit(j + n - 2, nrm);
   1.215 +            while ((qest * vd0) > ((rest * 0x10000) + ud0)) {
   1.216 +                --qest;
   1.217 +                rest += vd1;
   1.218 +            }
   1.219 +
   1.220 +            // 0 <= (qest - qj) <= 1
   1.221 +            
   1.222 +            // Multiply and subtract
   1.223 +            var carry = 0;
   1.224 +            for (var i = 0; i < n; ++i) {
   1.225 +                var vi = qest * v.getDigit(i, nrm);
   1.226 +                var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff);
   1.227 +                r.setDigit(i + j, nrm, ui);
   1.228 +                carry = (vi >>> 16) - (ui >> 16);
   1.229 +            }
   1.230 +            var uj = ud2 - carry;
   1.231 +
   1.232 +            if (uj < 0) {
   1.233 +                // qest - qj = 1
   1.234 +
   1.235 +                // Add back
   1.236 +                --qest;
   1.237 +                var carry = 0;
   1.238 +                for (var i = 0; i < n; ++i) {
   1.239 +                    var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm)
   1.240 +                                 + carry;
   1.241 +                    r.setDigit(i + j, nrm, ui);
   1.242 +                    carry = ui >> 16;
   1.243 +                }
   1.244 +                uj += carry;
   1.245 +            }
   1.246 +
   1.247 +            q.setDigit(j, 0, qest);
   1.248 +            r.setDigit(j + n, nrm, uj);
   1.249 +        }
   1.250 +    }
   1.251 +    
   1.252 +    numberPrototype.div64 = function(x) {
   1.253 +        var negateResult = false;
   1.254 +        var u, v;
   1.255 +        
   1.256 +        if ((this.high32() & 0x80000000) != 0) {
   1.257 +            u = this.neg64();
   1.258 +            negateResult = !negateResult;
   1.259 +        } else {
   1.260 +            u = this;        
   1.261 +        }
   1.262 +
   1.263 +        if ((x.high32() & 0x80000000) != 0) {
   1.264 +            v = x.neg64();
   1.265 +            negateResult = !negateResult;
   1.266 +        } else {
   1.267 +            v = x;
   1.268 +        }
   1.269 +
   1.270 +        if ((v === 0) && (v.high32() === 0)) {
   1.271 +            // TODO: throw
   1.272 +        }
   1.273 +
   1.274 +        if (u.high32() === 0) {
   1.275 +            if (v.high32() === 0) {
   1.276 +                var result = (u / v) | 0;
   1.277 +                return negateResult ? result.neg64() : result; 
   1.278 +            }
   1.279 +
   1.280 +            return 0;
   1.281 +        }
   1.282 +
   1.283 +        var u64 = new __Int64(u.high32(), u);
   1.284 +        var v64 = new __Int64(v.high32(), v);
   1.285 +        var q64 = new __Int64(0, 0);
   1.286 +        var r64 = new __Int64(0, 0);
   1.287 +
   1.288 +        __div64(q64, r64, u64, v64);
   1.289 +
   1.290 +        var result = q64.toNumber();
   1.291 +        return negateResult ? result.neg64() : result; 
   1.292 +    }
   1.293 +
   1.294 +    numberPrototype.mod64 = function(x) {
   1.295 +        var negateResult = false;
   1.296 +        var u, v;
   1.297 +        
   1.298 +        if ((this.high32() & 0x80000000) != 0) {
   1.299 +            u = this.neg64();
   1.300 +            negateResult = !negateResult;
   1.301 +        } else {
   1.302 +            u = this;        
   1.303 +        }
   1.304 +
   1.305 +        if ((x.high32() & 0x80000000) != 0) {
   1.306 +            v = x.neg64();
   1.307 +        } else {
   1.308 +            v = x;
   1.309 +        }
   1.310 +
   1.311 +        if ((v === 0) && (v.high32() === 0)) {
   1.312 +            // TODO: throw
   1.313 +        }
   1.314 +
   1.315 +        if (u.high32() === 0) {
   1.316 +            var result = (v.high32() === 0) ? (u % v) : u;
   1.317 +            return negateResult ? result.neg64() : result; 
   1.318 +        }
   1.319 +
   1.320 +        var u64 = new __Int64(u.high32(), u);
   1.321 +        var v64 = new __Int64(v.high32(), v);
   1.322 +        var q64 = new __Int64(0, 0);
   1.323 +        var r64 = new __Int64(0, 0);
   1.324 +
   1.325 +        __div64(q64, r64, u64, v64);
   1.326 +
   1.327 +        var result = r64.toNumber();
   1.328 +        return negateResult ? result.neg64() : result; 
   1.329 +    }
   1.330 +};
   1.331 +
   1.332 +__initDivMod(Number.prototype);
     2.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Tue Feb 05 16:32:14 2013 +0100
     2.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Tue Feb 05 16:40:01 2013 +0100
     2.3 @@ -570,7 +570,8 @@
     2.4                      emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI());
     2.5                      break;
     2.6                  case opc_lrem:
     2.7 -                    emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL());
     2.8 +                    emit(out, "@1 = @1.mod64(@2);",
     2.9 +                         smapper.getL(1), smapper.popL());
    2.10                      break;
    2.11                  case opc_frem:
    2.12                      emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF());
     3.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java	Tue Feb 05 16:32:14 2013 +0100
     3.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java	Tue Feb 05 16:40:01 2013 +0100
     3.3 @@ -168,7 +168,7 @@
     3.4                  new byte[] { (byte)0x80, (byte)0xfa, (byte)0x37, (byte)0xd7, (byte)0x76, (byte)0x3e, (byte)0x0c, (byte)0xa1 }
     3.5          );
     3.6      }
     3.7 -    
     3.8 +
     3.9      @Test public void longNegate3() throws Exception {
    3.10          final long res = -0xfffffffffffffeddl;
    3.11          assertExec("Long negate",
    3.12 @@ -178,7 +178,6 @@
    3.13          );
    3.14      }
    3.15  
    3.16 -    
    3.17      @Test public void longAddOverflow() throws Exception {
    3.18          final long res = Long.MAX_VALUE + 1l;
    3.19          assertExec("Addition 1+MAX",
    3.20 @@ -278,7 +277,239 @@
    3.21              new byte[] { (byte)0xa7, (byte)0xb3, (byte)0x43, (byte)0x2f, (byte)0xff, (byte)0x00, (byte)0x12, (byte)0x3e }
    3.22          );
    3.23      }
    3.24 -    
    3.25 +
    3.26 +    @Test public void longDivideSmallPositiveNumbers() throws Exception {
    3.27 +        final long res = 0xabcdef / 0x123;
    3.28 +        assertExec("Division Small Positive Numbers",
    3.29 +            Numbers.class, "divL__J_3B_3B",
    3.30 +            Double.valueOf(res),
    3.31 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef },
    3.32 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x23 }
    3.33 +        );
    3.34 +    }
    3.35 +
    3.36 +    @Test public void longDivideSmallNegativeNumbers() throws Exception {
    3.37 +        final long res = -0xabcdef / -0x123;
    3.38 +        assertExec("Division Small Negative Numbers",
    3.39 +            Numbers.class, "divL__J_3B_3B",
    3.40 +            Double.valueOf(res),
    3.41 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x11 },
    3.42 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfe, (byte)0xdd }
    3.43 +        );
    3.44 +    }
    3.45 +
    3.46 +    @Test public void longDivideSmallMixedNumbers() throws Exception {
    3.47 +        final long res = 0xabcdef / -0x123;
    3.48 +        assertExec("Division Small Mixed Numbers",
    3.49 +            Numbers.class, "divL__J_3B_3B",
    3.50 +            Double.valueOf(res),
    3.51 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef },
    3.52 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfe, (byte)0xdd }
    3.53 +        );
    3.54 +    }
    3.55 +
    3.56 +    @Test public void longDividePositiveNumbersOneDigitDenom()
    3.57 +            throws Exception {
    3.58 +        final long res = 0xabcdef0102ffffL / 0x654;
    3.59 +        assertExec("Division Positive Numbers One Digit Denom",
    3.60 +            Numbers.class, "divL__J_3B_3B",
    3.61 +            Double.valueOf(res),
    3.62 +            new byte[] { (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef, (byte)0x01, (byte)0x02, (byte)0xff, (byte)0xff },
    3.63 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x54 }
    3.64 +        );
    3.65 +    }
    3.66 +
    3.67 +    @Test public void longDivideNegativeNumbersOneDigitDenom()
    3.68 +            throws Exception {
    3.69 +        final long res = -0xabcdef0102ffffL / -0x654;
    3.70 +        assertExec("Division Negative Numbers One Digit Denom",
    3.71 +            Numbers.class, "divL__J_3B_3B",
    3.72 +            Double.valueOf(res),
    3.73 +            new byte[] { (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x10, (byte)0xfe, (byte)0xfd, (byte)0x00, (byte)0x01 },
    3.74 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf9, (byte)0xac }
    3.75 +        );
    3.76 +    }
    3.77 +
    3.78 +    @Test public void longDivideMixedNumbersOneDigitDenom()
    3.79 +            throws Exception {
    3.80 +        final long res = -0xabcdef0102ffffL / 0x654;
    3.81 +        assertExec("Division Mixed Numbers One Digit Denom",
    3.82 +            Numbers.class, "divL__J_3B_3B",
    3.83 +            Double.valueOf(res),
    3.84 +            new byte[] { (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x10, (byte)0xfe, (byte)0xfd, (byte)0x00, (byte)0x01 },
    3.85 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x54 }
    3.86 +        );
    3.87 +    }
    3.88 +
    3.89 +    @Test public void longDividePositiveNumbersMultiDigitDenom()
    3.90 +            throws Exception {
    3.91 +        final long res = 0x7ffefc003322aabbL / 0x89ab1000L;
    3.92 +        assertExec("Division Positive Numbers Multi Digit Denom",
    3.93 +            Numbers.class, "divL__J_3B_3B",
    3.94 +            Double.valueOf(res),
    3.95 +            new byte[] { (byte)0x7f, (byte)0xfe, (byte)0xfc, (byte)0x00, (byte)0x33, (byte)0x22, (byte)0xaa, (byte)0xbb },
    3.96 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x89, (byte)0xab, (byte)0x10, (byte)0x00 }
    3.97 +        );
    3.98 +    }
    3.99 +
   3.100 +    @Test public void longDivideNegativeNumbersMultiDigitDenom()
   3.101 +            throws Exception {
   3.102 +        final long res = -0x7ffefc003322aabbL / -0x123489ab1001L;
   3.103 +        assertExec("Division Negative Numbers Multi Digit Denom",
   3.104 +            Numbers.class, "divL__J_3B_3B",
   3.105 +            Double.valueOf(res),
   3.106 +            new byte[] { (byte)0x80, (byte)0x01, (byte)0x03, (byte)0xff, (byte)0xcc, (byte)0xdd, (byte)0x55, (byte)0x45 },
   3.107 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xed, (byte)0xcb, (byte)0x76, (byte)0x54, (byte)0xef, (byte)0xff }
   3.108 +        );
   3.109 +    }
   3.110 +
   3.111 +    @Test public void longDivideMixedNumbersMultiDigitDenom()
   3.112 +            throws Exception {
   3.113 +        final long res = 0x7ffefc003322aabbL / -0x38f49b0b7574e36L;
   3.114 +        assertExec("Division Mixed Numbers Multi Digit Denom",
   3.115 +            Numbers.class, "divL__J_3B_3B",
   3.116 +            Double.valueOf(res),
   3.117 +            new byte[] { (byte)0x7f, (byte)0xfe, (byte)0xfc, (byte)0x00, (byte)0x33, (byte)0x22, (byte)0xaa, (byte)0xbb },
   3.118 +            new byte[] { (byte)0xfc, (byte)0x70, (byte)0xb6, (byte)0x4f, (byte)0x48, (byte)0xa8, (byte)0xb1, (byte)0xca }
   3.119 +        );
   3.120 +    }
   3.121 +
   3.122 +    @Test public void longDivideWithOverflow() throws Exception {
   3.123 +        final long res = 0x8000fffe0000L / 0x8000ffffL;
   3.124 +        assertExec("Division With Overflow",
   3.125 +            Numbers.class, "divL__J_3B_3B",
   3.126 +            Double.valueOf(res),
   3.127 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0xff, (byte)0xfe, (byte)0x00, (byte)0x00 },
   3.128 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0xff, (byte)0xff }
   3.129 +        );
   3.130 +    }
   3.131 +
   3.132 +    @Test public void longDivideWithCorrection() throws Exception {
   3.133 +        final long res = 0x7fff800000000000L / 0x800000000001L;
   3.134 +        assertExec("Division With Correction",
   3.135 +            Numbers.class, "divL__J_3B_3B",
   3.136 +            Double.valueOf(res),
   3.137 +            new byte[] { (byte)0x7f, (byte)0xff, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 },
   3.138 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01 }
   3.139 +        );
   3.140 +    }
   3.141 +
   3.142 +    @Test public void longModuloSmallPositiveNumbers() throws Exception {
   3.143 +        final long res = 0xabcdef % 0x123;
   3.144 +        assertExec("Modulo Small Positive Numbers",
   3.145 +            Numbers.class, "modL__J_3B_3B",
   3.146 +            Double.valueOf(res),
   3.147 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef },
   3.148 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x23 }
   3.149 +        );
   3.150 +    }
   3.151 +
   3.152 +    @Test public void longModuloSmallNegativeNumbers() throws Exception {
   3.153 +        final long res = -0xabcdef % -0x123;
   3.154 +        assertExec("Modulo Small Negative Numbers",
   3.155 +            Numbers.class, "modL__J_3B_3B",
   3.156 +            Double.valueOf(res),
   3.157 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x11 },
   3.158 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfe, (byte)0xdd }
   3.159 +        );
   3.160 +    }
   3.161 +
   3.162 +    @Test public void longModuloSmallMixedNumbers() throws Exception {
   3.163 +        final long res = 0xabcdef % -0x123;
   3.164 +        assertExec("Modulo Small Mixed Numbers",
   3.165 +            Numbers.class, "modL__J_3B_3B",
   3.166 +            Double.valueOf(res),
   3.167 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef },
   3.168 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfe, (byte)0xdd }
   3.169 +        );
   3.170 +    }
   3.171 +
   3.172 +    @Test public void longModuloPositiveNumbersOneDigitDenom()
   3.173 +            throws Exception {
   3.174 +        final long res = 0xabcdef0102ffffL % 0x654;
   3.175 +        assertExec("Modulo Positive Numbers One Digit Denom",
   3.176 +            Numbers.class, "modL__J_3B_3B",
   3.177 +            Double.valueOf(res),
   3.178 +            new byte[] { (byte)0x00, (byte)0xab, (byte)0xcd, (byte)0xef, (byte)0x01, (byte)0x02, (byte)0xff, (byte)0xff },
   3.179 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x54 }
   3.180 +        );
   3.181 +    }
   3.182 +
   3.183 +    @Test public void longModuloNegativeNumbersOneDigitDenom()
   3.184 +            throws Exception {
   3.185 +        final long res = -0xabcdef0102ffffL % -0x654;
   3.186 +        assertExec("Modulo Negative Numbers One Digit Denom",
   3.187 +            Numbers.class, "modL__J_3B_3B",
   3.188 +            Double.valueOf(res),
   3.189 +            new byte[] { (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x10, (byte)0xfe, (byte)0xfd, (byte)0x00, (byte)0x01 },
   3.190 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf9, (byte)0xac }
   3.191 +        );
   3.192 +    }
   3.193 +
   3.194 +    @Test public void longModuloMixedNumbersOneDigitDenom()
   3.195 +            throws Exception {
   3.196 +        final long res = -0xabcdef0102ffffL % 0x654;
   3.197 +        assertExec("Modulo Mixed Numbers One Digit Denom",
   3.198 +            Numbers.class, "modL__J_3B_3B",
   3.199 +            Double.valueOf(res),
   3.200 +            new byte[] { (byte)0xff, (byte)0x54, (byte)0x32, (byte)0x10, (byte)0xfe, (byte)0xfd, (byte)0x00, (byte)0x01 },
   3.201 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x54 }
   3.202 +        );
   3.203 +    }
   3.204 +
   3.205 +    @Test public void longModuloPositiveNumbersMultiDigitDenom()
   3.206 +            throws Exception {
   3.207 +        final long res = 0x7ffefc003322aabbL % 0x89ab1000L;
   3.208 +        assertExec("Modulo Positive Numbers Multi Digit Denom",
   3.209 +            Numbers.class, "modL__J_3B_3B",
   3.210 +            Double.valueOf(res),
   3.211 +            new byte[] { (byte)0x7f, (byte)0xfe, (byte)0xfc, (byte)0x00, (byte)0x33, (byte)0x22, (byte)0xaa, (byte)0xbb },
   3.212 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x89, (byte)0xab, (byte)0x10, (byte)0x00 }
   3.213 +        );
   3.214 +    }
   3.215 +
   3.216 +    @Test public void longModuloNegativeNumbersMultiDigitDenom()
   3.217 +            throws Exception {
   3.218 +        final long res = -0x7ffefc003322aabbL % -0x123489ab1001L;
   3.219 +        assertExec("Modulo Negative Numbers Multi Digit Denom",
   3.220 +            Numbers.class, "modL__J_3B_3B",
   3.221 +            Double.valueOf(res),
   3.222 +            new byte[] { (byte)0x80, (byte)0x01, (byte)0x03, (byte)0xff, (byte)0xcc, (byte)0xdd, (byte)0x55, (byte)0x45 },
   3.223 +            new byte[] { (byte)0xff, (byte)0xff, (byte)0xed, (byte)0xcb, (byte)0x76, (byte)0x54, (byte)0xef, (byte)0xff }
   3.224 +        );
   3.225 +    }
   3.226 +
   3.227 +    @Test public void longModuloMixedNumbersMultiDigitDenom()
   3.228 +            throws Exception {
   3.229 +        final long res = 0x7ffefc003322aabbL % -0x38f49b0b7574e36L;
   3.230 +        assertExec("Modulo Mixed Numbers Multi Digit Denom",
   3.231 +            Numbers.class, "modL__J_3B_3B",
   3.232 +            Double.valueOf(res),
   3.233 +            new byte[] { (byte)0x7f, (byte)0xfe, (byte)0xfc, (byte)0x00, (byte)0x33, (byte)0x22, (byte)0xaa, (byte)0xbb },
   3.234 +            new byte[] { (byte)0xfc, (byte)0x70, (byte)0xb6, (byte)0x4f, (byte)0x48, (byte)0xa8, (byte)0xb1, (byte)0xca }
   3.235 +        );
   3.236 +    }
   3.237 +
   3.238 +    @Test public void longModuloWithOverflow() throws Exception {
   3.239 +        final long res = 0x8000fffe0000L % 0x8000ffffL;
   3.240 +        assertExec("Modulo With Overflow",
   3.241 +            Numbers.class, "modL__J_3B_3B",
   3.242 +            Double.valueOf(res),
   3.243 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0xff, (byte)0xfe, (byte)0x00, (byte)0x00 },
   3.244 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0xff, (byte)0xff }
   3.245 +        );
   3.246 +    }
   3.247 +
   3.248 +    @Test public void longModuloWithCorrection() throws Exception {
   3.249 +        final long res = 0x7fff800000000000L % 0x800000000001L;
   3.250 +        assertExec("Modulo With Correction",
   3.251 +            Numbers.class, "modL__J_3B_3B",
   3.252 +            Double.valueOf(res),
   3.253 +            new byte[] { (byte)0x7f, (byte)0xff, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 },
   3.254 +            new byte[] { (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01 }
   3.255 +        );
   3.256 +    }
   3.257 +
   3.258      @Test public void longShiftL1() throws Exception {
   3.259          final long res = 0x00fa37d7763e0ca1l << 5;
   3.260          assertExec("Long << 5",
     4.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/Numbers.java	Tue Feb 05 16:32:14 2013 +0100
     4.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Numbers.java	Tue Feb 05 16:40:01 2013 +0100
     4.3 @@ -102,12 +102,20 @@
     4.4          return (disX.readLong() * disY.readLong());
     4.5      }
     4.6      
     4.7 -    public static long divL(long x, long y) {
     4.8 -        return (x / y);
     4.9 +    public static long divL(byte[] arrX, byte[] arrY) throws IOException {
    4.10 +        ByteArrayInputStream isX = new ByteArrayInputStream(arrX);
    4.11 +        DataInputStream disX = new DataInputStream(isX);
    4.12 +        ByteArrayInputStream isY = new ByteArrayInputStream(arrY);
    4.13 +        DataInputStream disY = new DataInputStream(isY);
    4.14 +        return (disX.readLong() / disY.readLong());
    4.15      }
    4.16      
    4.17 -    public static long modL(long x, long y) {
    4.18 -        return (x % y);
    4.19 +    public static long modL(byte[] arrX, byte[] arrY) throws IOException {
    4.20 +        ByteArrayInputStream isX = new ByteArrayInputStream(arrX);
    4.21 +        DataInputStream disX = new DataInputStream(isX);
    4.22 +        ByteArrayInputStream isY = new ByteArrayInputStream(arrY);
    4.23 +        DataInputStream disY = new DataInputStream(isY);
    4.24 +        return (disX.readLong() % disY.readLong());
    4.25      }
    4.26      
    4.27      public static long shlL(byte[] arrValue, int nBits) throws IOException {