Skip to content

Commit

Permalink
Implement gcd in sdk.
Browse files Browse the repository at this point in the history
R=srdjan@google.com

Review URL: https://codereview.chromium.org//1199513003.
  • Loading branch information
crelier committed Jun 19, 2015
1 parent 2c4605c commit a192ef4
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 102 deletions.
160 changes: 106 additions & 54 deletions runtime/lib/bigint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
}
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -1719,36 +1745,62 @@ 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;
if ((i != 0) || (v_digits[0] != 1)) throw new RangeError("Not coprime");

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.
Expand Down
84 changes: 59 additions & 25 deletions runtime/lib/integers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -314,25 +319,25 @@ 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;
}
while (v.isEven) {
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;
}
Expand All @@ -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 {
Expand Down
Loading

0 comments on commit a192ef4

Please sign in to comment.