Skip to content

Commit

Permalink
Implement modInverse for an even modulus.
Browse files Browse the repository at this point in the history
Update test.

Fixes #23502

R=srdjan@google.com

Review URL: https://codereview.chromium.org//1188843004.
  • Loading branch information
crelier authored and ricowind committed Jun 19, 2015
1 parent adf03e1 commit 1241ad4
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 93 deletions.
218 changes: 164 additions & 54 deletions runtime/lib/bigint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1542,100 +1542,210 @@ class _Bigint extends _IntegerImplementation implements int {
int modInverse(int m) {
if (m is! int) throw new ArgumentError(m);
if (m <= 0) throw new RangeError(m);
if (_used == 0) return 0;
if (m == 1) return 0;
m = m._toBigint();
// TODO(regis): Implement modInverse for an even modulus.
if (m.isEven) throw new UnimplementedError();
var t = this;
if ((t._compare(m) >= 0) || t._neg) {
if (t._neg || (t._absCompare(m) >= 0)) {
t %= m;
t = t._toBigint();
}
final bool ac = m.isEven;
final t_used = t._used;
if (t_used == 0) {
return 0; // No inverse.
}
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 uv_len = m_used + (m_used & 1);
var v_digits = _cloneDigits(t._digits, 0, t_used, uv_len);
var u_digits = _cloneDigits(m_digits, 0, m_used, uv_len);

// Variables b and d require one more digit for carry.
final bd_used = m_used + 1;
final bd_len = bd_used + (bd_used & 1);
var b_digits = new Uint32List(bd_len);
var d_digits = new Uint32List(bd_len);
bool b_neg = false;
bool d_neg = false;

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);

// Variables a, b, c, and d require one more digit.
final abcd_used = m_used + 1;
final abcd_len = abcd_used + (abcd_used & 1) + 2; // +2 to satisfy _absAdd.
var a_digits, b_digits, c_digits, d_digits;
bool a_neg, b_neg, c_neg, d_neg;
if (ac) {
a_digits = new Uint32List(abcd_len);
a_neg = false;
a_digits[0] = 1;
c_digits = new Uint32List(abcd_len);
c_neg = false;
}
b_digits = new Uint32List(abcd_len);
b_neg = false;
d_digits = new Uint32List(abcd_len);
d_neg = false;
d_digits[0] = 1;

while (true) {
while ((u_digits[0] & 1) == 0) {
_rsh(u_digits, m_used, 1, u_digits);
if ((b_digits[0] & 1) == 1) {
_absSub(m_digits, m_used, b_digits, m_used, b_digits);
b_neg = !b_neg;
if (ac) {
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);
} else {
_absSub(t_digits, m_used, a_digits, m_used, a_digits);
a_neg = false;
}
} else {
_absAdd(a_digits, abcd_used, t_digits, m_used, a_digits);
}
if (b_neg) {
_absAdd(b_digits, abcd_used, m_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);
} else {
_absSub(m_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);
} 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);
} else {
_absSub(m_digits, m_used, b_digits, m_used, b_digits);
b_neg = true;
}
}
_rsh(b_digits, m_used, 1, b_digits);
_rsh(b_digits, abcd_used, 1, b_digits);
}
while ((v_digits[0] & 1) == 0) {
_rsh(v_digits, m_used, 1, v_digits);
if ((d_digits[0] & 1) == 1) {
_absSub(m_digits, m_used, d_digits, m_used, d_digits);
d_neg = !d_neg;
if (ac) {
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);
} else {
_absSub(t_digits, m_used, c_digits, m_used, c_digits);
c_neg = false;
}
} else {
_absAdd(c_digits, abcd_used, t_digits, m_used, c_digits);
}
if (d_neg) {
_absAdd(d_digits, abcd_used, m_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);
} else {
_absSub(m_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);
} 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);
} else {
_absSub(m_digits, m_used, d_digits, m_used, d_digits);
d_neg = true;
}
}
_rsh(d_digits, m_used, 1, d_digits);
_rsh(d_digits, abcd_used, 1, d_digits);
}
if (_compareDigits(u_digits, m_used, v_digits, m_used) >= 0) {
_absSub(u_digits, m_used, v_digits, m_used, u_digits);
if (ac) {
if (a_neg == c_neg) {
var a_cmp_c =
_compareDigits(a_digits, abcd_used, c_digits, abcd_used);
if (a_cmp_c > 0) {
_absSub(a_digits, abcd_used, c_digits, abcd_used, a_digits);
} else {
_absSub(c_digits, abcd_used, a_digits, abcd_used, a_digits);
a_neg = !a_neg && (a_cmp_c != 0);
}
} else {
_absAdd(a_digits, abcd_used, c_digits, abcd_used, a_digits);
}
}
if (b_neg == d_neg) {
if (_compareDigits(b_digits, m_used, d_digits, m_used) >= 0) {
_absSub(b_digits, m_used, d_digits, m_used, b_digits);
var b_cmp_d =
_compareDigits(b_digits, abcd_used, d_digits, abcd_used);
if (b_cmp_d > 0) {
_absSub(b_digits, abcd_used, d_digits, abcd_used, b_digits);
} else {
_absSub(d_digits, m_used, b_digits, m_used, b_digits);
b_neg = !b_neg;
_absSub(d_digits, abcd_used, b_digits, abcd_used, b_digits);
b_neg = !b_neg && (b_cmp_d != 0);
}
} else {
_absAdd(b_digits, m_used, d_digits, m_used, b_digits);
if ((b_digits[m_used] > 0) ||
(_compareDigits(b_digits, m_used, m_digits, m_used) > 0)) {
_absSub(b_digits, bd_used, m_digits, m_used, b_digits);
}
_absAdd(b_digits, abcd_used, d_digits, abcd_used, b_digits);
}
} else {
_absSub(v_digits, m_used, u_digits, m_used, v_digits);
if (b_neg == d_neg) {
if (_compareDigits(d_digits, m_used, b_digits, m_used) >= 0) {
_absSub(d_digits, m_used, b_digits, m_used, d_digits);
if (ac) {
if (c_neg == a_neg) {
var c_cmp_a =
_compareDigits(c_digits, abcd_used, a_digits, abcd_used);
if (c_cmp_a > 0) {
_absSub(c_digits, abcd_used, a_digits, abcd_used, c_digits);
} else {
_absSub(a_digits, abcd_used, c_digits, abcd_used, c_digits);
c_neg = !c_neg && (c_cmp_a != 0);
}
} else {
_absSub(b_digits, m_used, d_digits, m_used, d_digits);
d_neg = !d_neg;
_absAdd(c_digits, abcd_used, a_digits, abcd_used, c_digits);
}
} else {
_absAdd(d_digits, m_used, b_digits, m_used, d_digits);
if ((d_digits[m_used] > 0) ||
(_compareDigits(d_digits, m_used, m_digits, m_used) > 0)) {
_absSub(d_digits, bd_used, m_digits, m_used, d_digits);
}
if (d_neg == b_neg) {
var d_cmp_b =
_compareDigits(d_digits, abcd_used, b_digits, abcd_used);
if (d_cmp_b > 0) {
_absSub(d_digits, abcd_used, b_digits, abcd_used, d_digits);
} else {
_absSub(b_digits, abcd_used, d_digits, abcd_used, d_digits);
d_neg = !d_neg && (d_cmp_ab != 0);
}
} else {
_absAdd(d_digits, abcd_used, b_digits, abcd_used, d_digits);
}
}
// Exit loop if u == 0.
var i = m_used;
while ((i > 0) && (u_digits[i - 1] == 0)) {
--i;
}
while ((i > 0) && (u_digits[i - 1] == 0)) --i;
if (i == 0) break;
}
// No inverse if v != 1.
for (var i = m_used - 1; i > 0; --i) {
if (v_digits[i] != 0) return 0; // No inverse.
}
if (v_digits[0] != 1) return 0; // No inverse.
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) {
_absSub(m_digits, m_used, d_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);
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);
} else {
_absSub(m_digits, m_used, d_digits, m_used, d_digits);
d_neg = false;
}
} else {
_absSub(m_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);
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);
}
}
return new _Bigint(false, m_used, d_digits)._toValidInt();
}
Expand Down
28 changes: 18 additions & 10 deletions runtime/lib/integers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -294,23 +294,27 @@ class _IntegerImplementation extends _Num {
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 (this == 0) return 0;
bool ac = m.isEven;
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 = this;
int v = t;
int a = 1,
b = 0,
c = 0,
d = 1;
while (u != 0) {
do {
while (u.isEven) {
u >>= 1;
if (ac) {
if (!a.isEven || !b.isEven) {
a += this;
a += t;
b -= m;
}
a >>= 1;
Expand All @@ -323,7 +327,7 @@ class _IntegerImplementation extends _Num {
v >>= 1;
if (ac) {
if (!c.isEven || !d.isEven) {
c += this;
c += t;
d -= m;
}
c >>= 1;
Expand All @@ -341,11 +345,15 @@ class _IntegerImplementation extends _Num {
if (ac) c -= a;
d -= b;
}
} while (u != 0);
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;
}
if (v != 1) return 0;
if (d > m) return d - m;
if (d < 0) d += m;
if (d < 0) d += m;
return d;
}
}
Expand Down
28 changes: 18 additions & 10 deletions sdk/lib/_internal/compiler/js_lib/js_number.dart
Original file line number Diff line number Diff line change
Expand Up @@ -415,20 +415,24 @@ class JSInt extends JSNumber implements int, double {
int modInverse(int m) {
if (m is! int) throw new ArgumentError(m);
if (m <= 0) throw new RangeError(m);
if (this == 0) return 0;
bool ac = m.isEven;
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 = this;
int v = t;
int a = 1,
b = 0,
c = 0,
d = 1;
while (u != 0) {
do {
while (u.isEven) {
u ~/= 2;
if (ac) {
if (!a.isEven || !b.isEven) {
a += this;
a += t;
b -= m;
}
a ~/= 2;
Expand All @@ -441,7 +445,7 @@ class JSInt extends JSNumber implements int, double {
v ~/= 2;
if (ac) {
if (!c.isEven || !d.isEven) {
c += this;
c += t;
d -= m;
}
c ~/= 2;
Expand All @@ -459,11 +463,15 @@ class JSInt extends JSNumber implements int, double {
if (ac) c -= a;
d -= b;
}
} while (u != 0);
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;
}
if (v != 1) return 0;
if (d > m) return d - m;
if (d < 0) d += m;
if (d < 0) d += m;
return d;
}

Expand Down
2 changes: 1 addition & 1 deletion sdk/lib/core/int.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ abstract class int extends num {
* modulo [modulus].
*
* The [modulus] must be positive.
* Returns 0 if no modular inverse exists.
* Throws a RangeError if no modular inverse exists.
*/
int modInverse(int modulus);

Expand Down
Loading

0 comments on commit 1241ad4

Please sign in to comment.