diff --git a/runtime/lib/bigint.dart b/runtime/lib/bigint.dart index 04b85b7d4d56..adfb0db4cca7 100644 --- a/runtime/lib/bigint.dart +++ b/runtime/lib/bigint.dart @@ -47,6 +47,7 @@ class _Bigint extends _IntegerImplementation implements int { // Bits per digit. static const int _DIGIT_BITS = 32; + static const int _LOG2_DIGIT_BITS = 5; static const int _DIGIT_BASE = 1 << _DIGIT_BITS; static const int _DIGIT_MASK = (1 << _DIGIT_BITS) - 1; @@ -1538,27 +1539,52 @@ class _Bigint extends _IntegerImplementation implements int { return z._revert(r_digits, r_used)._toValidInt(); } - // Returns 1/this % m, with m > 0. - int modInverse(int m) { - if (m is! int) throw new ArgumentError(m); - if (m <= 0) throw new RangeError(m); - if (m == 1) return 0; - m = m._toBigint(); - var t = this; - if (t._neg || (t._absCompare(m) >= 0)) { - t %= m; - t = t._toBigint(); + // If inv is false, returns gcd(x, y). + // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. + // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime"). + static int _binaryGcd(_Bigint x, _Bigint y, bool inv) { + var x_digits = x._digits; + var y_digits = y._digits; + var x_used = x._used; + var y_used = y._used; + var m_used = x_used > y_used ? x_used : y_used; + final m_len = m_used + (m_used & 1); + x_digits = _cloneDigits(x_digits, 0, x_used, m_len); + y_digits = _cloneDigits(y_digits, 0, y_used, m_len); + int s = 0; + if (inv) { + if ((y_used == 1) && (y_digits[0] == 1)) return 1; + if ((y_used == 0) || (y_digits[0].isEven && x_digits[0].isEven)) { + throw new RangeError("Not coprime"); + } + } else { + if ((x_used == 0) || (y_used == 0)) throw new RangeError(0); + if (((x_used == 1) && (x_digits[0] == 1)) || + ((y_used == 1) && (y_digits[0] == 1))) return 1; + bool xy_cloned = false; + while (x.isEven && y.isEven) { + _rsh(x_digits, x_used, 1, x_digits); + _rsh(y_digits, y_used, 1, y_digits); + s++; + } + if (s >= _DIGIT_BITS) { + var sd = s >> _LOG2_DIGIT_BITS; + x_used -= sd; + y_used -= sd; + m_used -= sd; + } + if ((y_digits[0] & 1) == 1) { + var t_digits = x_digits; + var t_used = x_used; + x_digits = y_digits; + x_used = y_used; + y_digits = t_digits; + y_used = t_used; + } } - final bool ac = m.isEven; - final t_used = t._used; - if ((t_used == 1) && (t._digits[0] == 1)) return 1; - if ((t_used == 0) || (ac && t.isEven)) throw new RangeError("Not coprime"); - final m_digits = m._digits; - final m_used = m._used; - final tuv_len = m_used + (m_used & 1); - final t_digits = _cloneDigits(t._digits, 0, t_used, tuv_len); - var u_digits = _cloneDigits(m_digits, 0, m_used, tuv_len); - var v_digits = _cloneDigits(t_digits, 0, t_used, tuv_len); + var u_digits = _cloneDigits(x_digits, 0, x_used, m_len); + var v_digits = _cloneDigits(y_digits, 0, y_used, m_len); + final bool ac = (x_digits[0] & 1) == 0; // Variables a, b, c, and d require one more digit. final abcd_used = m_used + 1; @@ -1585,34 +1611,34 @@ class _Bigint extends _IntegerImplementation implements int { if (((a_digits[0] & 1) == 1) || ((b_digits[0] & 1) == 1)) { if (a_neg) { if ((a_digits[m_used] != 0) || - (_compareDigits(a_digits, m_used, t_digits, m_used)) > 0) { - _absSub(a_digits, abcd_used, t_digits, m_used, a_digits); + (_compareDigits(a_digits, m_used, y_digits, m_used)) > 0) { + _absSub(a_digits, abcd_used, y_digits, m_used, a_digits); } else { - _absSub(t_digits, m_used, a_digits, m_used, a_digits); + _absSub(y_digits, m_used, a_digits, m_used, a_digits); a_neg = false; } } else { - _absAdd(a_digits, abcd_used, t_digits, m_used, a_digits); + _absAdd(a_digits, abcd_used, y_digits, m_used, a_digits); } if (b_neg) { - _absAdd(b_digits, abcd_used, m_digits, m_used, b_digits); + _absAdd(b_digits, abcd_used, x_digits, m_used, b_digits); } else if ((b_digits[m_used] != 0) || - (_compareDigits(b_digits, m_used, m_digits, m_used) > 0)) { - _absSub(b_digits, abcd_used, m_digits, m_used, b_digits); + (_compareDigits(b_digits, m_used, x_digits, m_used) > 0)) { + _absSub(b_digits, abcd_used, x_digits, m_used, b_digits); } else { - _absSub(m_digits, m_used, b_digits, m_used, b_digits); + _absSub(x_digits, m_used, b_digits, m_used, b_digits); b_neg = true; } } _rsh(a_digits, abcd_used, 1, a_digits); } else if ((b_digits[0] & 1) == 1) { if (b_neg) { - _absAdd(b_digits, abcd_used, m_digits, m_used, b_digits); + _absAdd(b_digits, abcd_used, x_digits, m_used, b_digits); } else if ((b_digits[m_used] != 0) || - (_compareDigits(b_digits, m_used, m_digits, m_used) > 0)) { - _absSub(b_digits, abcd_used, m_digits, m_used, b_digits); + (_compareDigits(b_digits, m_used, x_digits, m_used) > 0)) { + _absSub(b_digits, abcd_used, x_digits, m_used, b_digits); } else { - _absSub(m_digits, m_used, b_digits, m_used, b_digits); + _absSub(x_digits, m_used, b_digits, m_used, b_digits); b_neg = true; } } @@ -1624,34 +1650,34 @@ class _Bigint extends _IntegerImplementation implements int { if (((c_digits[0] & 1) == 1) || ((d_digits[0] & 1) == 1)) { if (c_neg) { if ((c_digits[m_used] != 0) || - (_compareDigits(c_digits, m_used, t_digits, m_used) > 0)) { - _absSub(c_digits, abcd_used, t_digits, m_used, c_digits); + (_compareDigits(c_digits, m_used, y_digits, m_used) > 0)) { + _absSub(c_digits, abcd_used, y_digits, m_used, c_digits); } else { - _absSub(t_digits, m_used, c_digits, m_used, c_digits); + _absSub(y_digits, m_used, c_digits, m_used, c_digits); c_neg = false; } } else { - _absAdd(c_digits, abcd_used, t_digits, m_used, c_digits); + _absAdd(c_digits, abcd_used, y_digits, m_used, c_digits); } if (d_neg) { - _absAdd(d_digits, abcd_used, m_digits, m_used, d_digits); + _absAdd(d_digits, abcd_used, x_digits, m_used, d_digits); } else if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); } else { - _absSub(m_digits, m_used, d_digits, m_used, d_digits); + _absSub(x_digits, m_used, d_digits, m_used, d_digits); d_neg = true; } } _rsh(c_digits, abcd_used, 1, c_digits); } else if ((d_digits[0] & 1) == 1) { if (d_neg) { - _absAdd(d_digits, abcd_used, m_digits, m_used, d_digits); + _absAdd(d_digits, abcd_used, x_digits, m_used, d_digits); } else if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); } else { - _absSub(m_digits, m_used, d_digits, m_used, d_digits); + _absSub(x_digits, m_used, d_digits, m_used, d_digits); d_neg = true; } } @@ -1719,6 +1745,12 @@ class _Bigint extends _IntegerImplementation implements int { while ((i > 0) && (u_digits[i - 1] == 0)) --i; if (i == 0) break; } + if (!inv) { + if (s > 0) { + _lsh(v_digits, m_used, s, v_digits); + } + return new _Bigint(false, m_used, v_digits)._toValidInt(); + } // No inverse if v != 1. var i = m_used - 1; while ((i > 0) && (v_digits[i] == 0)) --i; @@ -1726,29 +1758,49 @@ class _Bigint extends _IntegerImplementation implements int { if (d_neg) { if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); } else { - _absSub(m_digits, m_used, d_digits, m_used, d_digits); + _absSub(x_digits, m_used, d_digits, m_used, d_digits); d_neg = false; } } else { - _absSub(m_digits, m_used, d_digits, m_used, d_digits); + _absSub(x_digits, m_used, d_digits, m_used, d_digits); d_neg = false; } } else if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); if ((d_digits[m_used] != 0) || - (_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) { - _absSub(d_digits, abcd_used, m_digits, m_used, d_digits); + (_compareDigits(d_digits, m_used, x_digits, m_used) > 0)) { + _absSub(d_digits, abcd_used, x_digits, m_used, d_digits); } } return new _Bigint(false, m_used, d_digits)._toValidInt(); } + + // Returns 1/this % m, with m > 0. + int modInverse(int m) { + if (m is! int) throw new ArgumentError(m); + if (m <= 0) throw new RangeError(m); + if (m == 1) return 0; + m = m._toBigint(); + var t = this; + if (t._neg || (t._absCompare(m) >= 0)) { + t %= m; + t = t._toBigint(); + } + return _binaryGcd(m, t, true); + } + + // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0. + int gcd(int other) { + if (other is! int) throw new ArgumentError(other); + return _binaryGcd(this, other._toBigint(), false); + } } // Interface for modular reduction. diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart index 1267d1570733..be0cb8fad71a 100644 --- a/runtime/lib/integers.dart +++ b/runtime/lib/integers.dart @@ -290,21 +290,26 @@ class _IntegerImplementation extends _Num { return r; } - // Returns 1/this % m, with m > 0. - int modInverse(int m) { - if (m is! int) throw new ArgumentError(m); - if (m <= 0) throw new RangeError(m); - if (m == 1) return 0; - if (m is _Bigint) { - return _toBigint().modInverse(m); + // If inv is false, returns gcd(x, y). + // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. + // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime"). + static int _binaryGcd(int x, int y, bool inv) { + int s = 0; + if (!inv) { + while (x.isEven && y.isEven) { + x >>= 1; + y >>= 1; + s++; + } + if (y.isOdd) { + var t = x; + x = y; + y = t; + } } - int t = this; - if ((t < 0) || (t >= m)) t %= m; - if (t == 1) return 1; - final bool ac = m.isEven; - if ((t == 0) || (ac && t.isEven)) throw new RangeError("Not coprime"); - int u = m; - int v = t; + final bool ac = x.isEven; + int u = x; + int v = y; int a = 1, b = 0, c = 0, @@ -314,12 +319,12 @@ class _IntegerImplementation extends _Num { u >>= 1; if (ac) { if (!a.isEven || !b.isEven) { - a += t; - b -= m; + a += y; + b -= x; } a >>= 1; } else if (!b.isEven) { - b -= m; + b -= x; } b >>= 1; } @@ -327,12 +332,12 @@ class _IntegerImplementation extends _Num { v >>= 1; if (ac) { if (!c.isEven || !d.isEven) { - c += t; - d -= m; + c += y; + d -= x; } c >>= 1; } else if (!d.isEven) { - d -= m; + d -= x; } d >>= 1; } @@ -346,16 +351,45 @@ class _IntegerImplementation extends _Num { d -= b; } } while (u != 0); + if (!inv) return v << s; if (v != 1) throw new RangeError("Not coprime"); if (d < 0) { - d += m; - if (d < 0) d += m; - } else if (d > m) { - d -= m; - if (d > m) d -= m; + d += x; + if (d < 0) d += x; + } else if (d > x) { + d -= x; + if (d > x) d -= x; } return d; } + + // Returns 1/this % m, with m > 0. + int modInverse(int m) { + if (m is! int) throw new ArgumentError(m); + if (m <= 0) throw new RangeError(m); + if (m == 1) return 0; + if (m is _Bigint) { + return _toBigint().modInverse(m); + } + int t = this; + if ((t < 0) || (t >= m)) t %= m; + if (t == 1) return 1; + if ((t == 0) || (t.isEven && m.isEven)) throw new RangeError("Not coprime"); + return _binaryGcd(m, t, true); + } + + // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0. + int gcd(int other) { + if (other is! int) throw new ArgumentError(other); + if ((this == 0) || (other == 0)) throw new RangeError(0); + int x = this.abs(); + int y = other.abs(); + if ((x == 1) || (y == 1)) return 1; + if (other is _Bigint) { + return _toBigint().gcd(other); + } + return _binaryGcd(x, y, false); + } } class _Smi extends _IntegerImplementation implements int { diff --git a/sdk/lib/_internal/compiler/js_lib/js_number.dart b/sdk/lib/_internal/compiler/js_lib/js_number.dart index 157b433e3b8d..b340247a59fa 100644 --- a/sdk/lib/_internal/compiler/js_lib/js_number.dart +++ b/sdk/lib/_internal/compiler/js_lib/js_number.dart @@ -410,18 +410,26 @@ class JSInt extends JSNumber implements int, double { return r; } - // Returns 1/this % m, with m > 0. - int modInverse(int m) { - if (m is! int) throw argumentErrorValue(m); - if (m <= 0) throw new RangeError(m); - if (m == 1) return 0; - int t = this; - if ((t < 0) || (t >= m)) t %= m; - if (t == 1) return 1; - final bool ac = m.isEven; - if ((t == 0) || (ac && t.isEven)) throw new RangeError("Not coprime"); - int u = m; - int v = t; + // If inv is false, returns gcd(x, y). + // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. + // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime"). + static int _binaryGcd(int x, int y, bool inv) { + int s = 1; + if (!inv) { + while (x.isEven && y.isEven) { + x ~/= 2; + y ~/= 2; + s *= 2; + } + if (y.isOdd) { + var t = x; + x = y; + y = t; + } + } + final bool ac = x.isEven; + int u = x; + int v = y; int a = 1, b = 0, c = 0, @@ -431,12 +439,12 @@ class JSInt extends JSNumber implements int, double { u ~/= 2; if (ac) { if (!a.isEven || !b.isEven) { - a += t; - b -= m; + a += y; + b -= x; } a ~/= 2; } else if (!b.isEven) { - b -= m; + b -= x; } b ~/= 2; } @@ -444,12 +452,12 @@ class JSInt extends JSNumber implements int, double { v ~/= 2; if (ac) { if (!c.isEven || !d.isEven) { - c += t; - d -= m; + c += y; + d -= x; } c ~/= 2; } else if (!d.isEven) { - d -= m; + d -= x; } d ~/= 2; } @@ -463,17 +471,40 @@ class JSInt extends JSNumber implements int, double { d -= b; } } while (u != 0); + if (!inv) return s*v; if (v != 1) throw new RangeError("Not coprime"); if (d < 0) { - d += m; - if (d < 0) d += m; - } else if (d > m) { - d -= m; - if (d > m) d -= m; + d += x; + if (d < 0) d += x; + } else if (d > x) { + d -= x; + if (d > x) d -= x; } return d; } + // Returns 1/this % m, with m > 0. + int modInverse(int m) { + if (m is! int) throw new ArgumentError(m); + if (m <= 0) throw new RangeError(m); + if (m == 1) return 0; + int t = this; + if ((t < 0) || (t >= m)) t %= m; + if (t == 1) return 1; + if ((t == 0) || (t.isEven && m.isEven)) throw new RangeError("Not coprime"); + return _binaryGcd(m, t, true); + } + + // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0. + int gcd(int other) { + if (other is! int) throw new ArgumentError(other); + if ((this == 0) || (other == 0)) throw new RangeError(0); + int x = this.abs(); + int y = other.abs(); + if ((x == 1) || (y == 1)) return 1; + return _binaryGcd(x, y, false); + } + // Assumes i is <= 32-bit and unsigned. static int _bitCount(int i) { // See "Hacker's Delight", section 5-1, "Counting 1-Bits". diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart index 5aee609939f1..58d80b84607f 100644 --- a/sdk/lib/core/int.dart +++ b/sdk/lib/core/int.dart @@ -120,6 +120,14 @@ abstract class int extends num { */ int modInverse(int modulus); + /** + * Returns the greatest common divisor of the absolute value of + * this integer and the absolute value of [other]. + * + * Both this and [other] must be non-zero. + */ + int gcd(int other); + /** Returns true if and only if this integer is even. */ bool get isEven; diff --git a/tests/corelib/big_integer_arith_vm_test.dart b/tests/corelib/big_integer_arith_vm_test.dart index 3353756335d7..419e603d4c9c 100644 --- a/tests/corelib/big_integer_arith_vm_test.dart +++ b/tests/corelib/big_integer_arith_vm_test.dart @@ -293,6 +293,88 @@ testBigintModInverse() { Expect.equals(46296295879629629587962962962, x.modInverse(m)); } +testBigintGcd() { + var x, m; + x = 1; + m = 1; + Expect.equals(1, x.gcd(m)); + x = 693; + m = 609; + Expect.equals(21, x.gcd(m)); + x = 693 << 40; + m = 609 << 40; + Expect.equals(21 << 40, x.gcd(m)); + x = 609 << 40;; + m = 693 << 40;; + Expect.equals(21 <<40, x.gcd(m)); + x = 0; + m = 1000000001; + Expect.throws(() => x.gcd(m), (e) => e is RangeError); + x = 1000000001; + m = 0; + Expect.throws(() => x.gcd(m), (e) => e is RangeError); + x = 1234567890; + m = 19; + Expect.equals(1, x.gcd(m)); + x = 1234567890; + m = 1000000001; + Expect.equals(1, x.gcd(m)); + x = 19; + m = 1000000001; + Expect.equals(19, x.gcd(m)); + x = 19; + m = 1234567890; + Expect.equals(1, x.gcd(m)); + x = 1000000001; + m = 1234567890; + Expect.equals(1, x.gcd(m)); + x = 1000000001; + m = 19; + Expect.equals(19, x.gcd(m)); + x = 12345678901234567890; + m = 19; + Expect.equals(1, x.gcd(m)); + x = 12345678901234567890; + m = 10000000000000000001; + Expect.equals(1, x.gcd(m)); + x = 19; + m = 10000000000000000001; + Expect.equals(1, x.gcd(m)); + x = 19; + m = 12345678901234567890; + Expect.equals(1, x.gcd(m)); + x = 10000000000000000001; + m = 12345678901234567890; + Expect.equals(1, x.gcd(m)); + x = 10000000000000000001; + m = 19; + Expect.equals(1, x.gcd(m)); + x = 12345678901234567890; + m = 10000000000000000001; + Expect.equals(1, x.gcd(m)); + x = 12345678901234567890; + m = 19; + Expect.equals(1, x.gcd(m)); + x = 123456789012345678901234567890; + m = 123456789012345678901234567899; + Expect.equals(9, x.gcd(m)); + x = 123456789012345678901234567890; + m = 123456789012345678901234567891; + Expect.equals(1, x.gcd(m)); + x = 123456789012345678901234567899; + m = 123456789012345678901234567891; + Expect.equals(1, x.gcd(m)); + x = 123456789012345678901234567899; + m = 123456789012345678901234567890; + Expect.equals(9, x.gcd(m)); + x = 123456789012345678901234567891; + m = 123456789012345678901234567890; + Expect.equals(1, x.gcd(m)); + x = 123456789012345678901234567891; + m = 123456789012345678901234567899; + Expect.equals(1, x.gcd(m)); +} + testBigintNegate() { var a = 0xF000000000000000F; var b = ~a; // negate. @@ -325,6 +407,7 @@ main() { testBigintModulo(); testBigintModPow(); testBigintModInverse(); + testBigintGcd(); testBigintNegate(); testShiftAmount(); Expect.equals(12345678901234567890, (12345678901234567890).abs());