Skip to content

Commit

Permalink
Implement modInverse (bigint version does not support even modulus yet).
Browse files Browse the repository at this point in the history
Added tests.

See #23502

R=srdjan@google.com

Review URL: https://codereview.chromium.org//1174513004.
  • Loading branch information
crelier committed Jun 10, 2015
1 parent c03b8fe commit f6f338c
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 7 deletions.
116 changes: 110 additions & 6 deletions runtime/lib/bigint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1265,15 +1265,9 @@ class _Bigint extends _IntegerImplementation implements int {
return other._toBigintOrDouble()._mulFromInteger(this);
}
num operator ~/(num other) {
if ((other is int) && (other == 0)) {
throw const IntegerDivisionByZeroException();
}
return other._toBigintOrDouble()._truncDivFromInteger(this);
}
num operator %(num other) {
if ((other is int) && (other == 0)) {
throw const IntegerDivisionByZeroException();
}
return other._toBigintOrDouble()._moduloFromInteger(this);
}
int operator &(int other) {
Expand Down Expand Up @@ -1368,9 +1362,15 @@ class _Bigint extends _IntegerImplementation implements int {
return other._toBigint()._mul(this)._toValidInt();
}
int _truncDivFromInteger(int other) {
if (_used == 0) {
throw const IntegerDivisionByZeroException();
}
return other._toBigint()._div(this)._toValidInt();
}
int _moduloFromInteger(int other) {
if (_used == 0) {
throw const IntegerDivisionByZeroException();
}
_Bigint result = other._toBigint()._rem(this);
if (result._neg) {
if (_neg) {
Expand All @@ -1382,6 +1382,9 @@ class _Bigint extends _IntegerImplementation implements int {
return result._toValidInt();
}
int _remainderFromInteger(int other) {
if (_used == 0) {
throw const IntegerDivisionByZeroException();
}
return other._toBigint()._rem(this)._toValidInt();
}
bool _greaterThanFromInteger(int other) {
Expand Down Expand Up @@ -1534,6 +1537,107 @@ class _Bigint extends _IntegerImplementation implements int {
assert(!is1);
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);
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) {
t %= m;
t = t._toBigint();
}
final t_used = t._used;
if (t_used == 0) {
return 0; // No inverse.
}
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;

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;
}
_rsh(b_digits, m_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;
}
_rsh(d_digits, m_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 (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);
} else {
_absSub(d_digits, m_used, b_digits, m_used, b_digits);
b_neg = !b_neg;
}
} 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);
}
}
} 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);
} else {
_absSub(b_digits, m_used, d_digits, m_used, d_digits);
d_neg = !d_neg;
}
} 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);
}
}
}
// Exit loop if u == 0.
var i = m_used;
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.

if (d_neg) {
_absSub(m_digits, m_used, d_digits, m_used, d_digits);
}
return new _Bigint(false, m_used, d_digits)._toValidInt();
}
}

// Interface for modular reduction.
Expand Down
59 changes: 58 additions & 1 deletion runtime/lib/integers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ class _IntegerImplementation extends _Num {
if (e is _Bigint || m is _Bigint) {
return _toBigint().modPow(e, m);
}
if (e < 1) return 1;
int b = this;
if (b < 0 || b > m) {
b %= m;
Expand All @@ -290,6 +289,64 @@ 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 is _Bigint) {
return _toBigint().modInverse(m);
}
bool ac = m.isEven;
int u = m;
int v = this;
int a = 1,
b = 0,
c = 0,
d = 1;
while (u != 0) {
while (u.isEven) {
u >>= 1;
if (ac) {
if (!a.isEven || !b.isEven) {
a += this;
b -= m;
}
a >>= 1;
} else if (!b.isEven) {
b -= m;
}
b >>= 1;
}
while (v.isEven) {
v >>= 1;
if (ac) {
if (!c.isEven || !d.isEven) {
c += this;
d -= m;
}
c >>= 1;
} else if (!d.isEven) {
d -= m;
}
d >>= 1;
}
if (u >= v) {
u -= v;
if (ac) a -= c;
b -= d;
} else {
v -= u;
if (ac) c -= a;
d -= b;
}
}
if (v != 1) return 0;
if (d > m) return d - m;
if (d < 0) d += m;
if (d < 0) d += m;
return d;
}
}

class _Smi extends _IntegerImplementation implements int {
Expand Down
58 changes: 58 additions & 0 deletions sdk/lib/_internal/compiler/js_lib/js_number.dart
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,64 @@ 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 new ArgumentError(m);
if (m <= 0) throw new RangeError(m);
if (m is _Bigint) {
return _toBigint().modInverse(m);
}
bool ac = m.isEven;
int u = m;
int v = this;
int a = 1,
b = 0,
c = 0,
d = 1;
while (u != 0) {
while (u.isEven) {
u ~/= 2;
if (ac) {
if (!a.isEven || !b.isEven) {
a += this;
b -= m;
}
a ~/= 2;
} else if (!b.isEven) {
b -= m;
}
b ~/= 2;
}
while (v.isEven) {
v ~/= 2;
if (ac) {
if (!c.isEven || !d.isEven) {
c += this;
d -= m;
}
c ~/= 2;
} else if (!d.isEven) {
d -= m;
}
d ~/= 2;
}
if (u >= v) {
u -= v;
if (ac) a -= c;
b -= d;
} else {
v -= u;
if (ac) c -= a;
d -= b;
}
}
if (v != 1) return 0;
if (d > m) return d - m;
if (d < 0) d += m;
if (d < 0) d += m;
return d;
}

// Assumes i is <= 32-bit and unsigned.
static int _bitCount(int i) {
// See "Hacker's Delight", section 5-1, "Counting 1-Bits".
Expand Down
8 changes: 8 additions & 0 deletions sdk/lib/core/int.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ abstract class int extends num {
*/
int modPow(int exponent, int modulus);

/**
* Returns the modular multiplicative inverse of this integer
* modulo [modulus].
*
* The [modulus] must be positive.
*/
int modInverse(int modulus);

/** Returns true if and only if this integer is even. */
bool get isEven;

Expand Down
75 changes: 75 additions & 0 deletions tests/corelib/big_integer_arith_vm_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,80 @@ testBigintModPow() {
Expect.equals(40128068573873018143207285483, x.modPow(e, m));
}

// TODO(regis): Remove once even modulus is implemented.
UnimplementedEvenModulusModInverse(x, m) {
Expect.equals(true, m.isEven);
try {
x.modInverse(m);
Expect.fail("Did not throw UnimplementedError");
} on UnimplementedError catch (e) {
}
}

testBigintModInverse() {
var x, m;
x = 1234567890;
m = 19;
Expect.equals(11, x.modInverse(m));
x = 1234567890;
m = 1000000001;
Expect.equals(189108911, x.modInverse(m));
x = 19;
m = 1000000001;
Expect.equals(0, x.modInverse(m)); // Not coprime.
x = 19;
m = 1234567890;
Expect.equals(519818059, x.modInverse(m));
x = 1000000001;
m = 1234567890;
Expect.equals(1001100101, x.modInverse(m));
x = 1000000001;
m = 19;
Expect.equals(0, x.modInverse(m)); // Not coprime.
x = 12345678901234567890;
m = 19;
Expect.equals(3, x.modInverse(m));
x = 12345678901234567890;
m = 10000000000000000001;
Expect.equals(9736746307686209582, x.modInverse(m));
x = 19;
m = 10000000000000000001;
Expect.equals(6315789473684210527, x.modInverse(m));
x = 19;
m = 12345678901234567890;
UnimplementedEvenModulusModInverse(x, m);
x = 10000000000000000001;
m = 12345678901234567890;
UnimplementedEvenModulusModInverse(x, m);
x = 10000000000000000001;
m = 19;
Expect.equals(7, x.modInverse(m));
x = 12345678901234567890;
m = 10000000000000000001;
Expect.equals(9736746307686209582, x.modInverse(m));
x = 12345678901234567890;
m = 19;
Expect.equals(3, x.modInverse(m));
x = 123456789012345678901234567890;
m = 123456789012345678901234567899;
Expect.equals(0, x.modInverse(m)); // Not coprime.
x = 123456789012345678901234567890;
m = 123456789012345678901234567891;
Expect.equals(123456789012345678901234567890, x.modInverse(m));
x = 123456789012345678901234567899;
m = 123456789012345678901234567891;
Expect.equals(77160493132716049313271604932, x.modInverse(m));
x = 123456789012345678901234567899;
m = 123456789012345678901234567890;
UnimplementedEvenModulusModInverse(x, m);
x = 123456789012345678901234567891;
m = 123456789012345678901234567890;
UnimplementedEvenModulusModInverse(x, m);
x = 123456789012345678901234567891;
m = 123456789012345678901234567899;
Expect.equals(46296295879629629587962962962, x.modInverse(m));
}

testBigintNegate() {
var a = 0xF000000000000000F;
var b = ~a; // negate.
Expand Down Expand Up @@ -254,6 +328,7 @@ main() {
testBigintDiv();
testBigintModulo();
testBigintModPow();
testBigintModInverse();
testBigintNegate();
testShiftAmount();
Expect.equals(12345678901234567890, (12345678901234567890).abs());
Expand Down

0 comments on commit f6f338c

Please sign in to comment.