package numbertheory; import java.math.BigInteger; import java.util.Arrays; import java.util.Random; public class Euclid { public static int gcd(int a, int b) { return b == 0 ? Math.abs(a) : gcd(b, a % b); } public static int gcd2(int a, int b) { while (b != 0) { int t = b; b = a % b; a = t; } return Math.abs(a); } public static int lcm(int a, int b) { return Math.abs(a / gcd(a, b) * b); } // returns { gcd(a,b), x, y } such that gcd(a,b) = a*x + b*y public static long[] euclid(long a, long b) { long x = 1, y = 0, x1 = 0, y1 = 1; // invariant: a=a_orig*x+b_orig*y, b=a_orig*x1+b_orig*y1 while (b != 0) { long q = a / b; long _x1 = x1; long _y1 = y1; long _b = b; x1 = x - q * x1; y1 = y - q * y1; b = a - q * b; x = _x1; y = _y1; a = _b; } return a > 0 ? new long[] {a, x, y} : new long[] {-a, -x, -y}; } public static long[] euclid2(long a, long b) { if (b == 0) return a > 0 ? new long[] {a, 1, 0} : new long[] {-a, -1, 0}; long[] r = euclid2(b, a % b); return new long[] {r[0], r[2], r[1] - a / b * r[2]}; } public static int mod(int a, int m) { a %= m; return a >= 0 ? a : a + m; } public static int mod(long a, int m) { a %= m; return (int) (a >= 0 ? a : a + m); } // precondition: m > 1 && gcd(a, m) = 1 public static int modInverse(int a, int m) { a = mod(a, m); return a == 0 ? 0 : mod((int) ((1 - (long) modInverse(m, a) * m) / a), m); } // precondition: m > 0 && gcd(a, m) = 1 public static int modInverse2(int a, int m) { return mod((int) euclid(a, m)[1], m); } // precondition: p is prime public static int[] generateInverses(int p) { int[] res = new int[p]; res[1] = 1; for (int i = 2; i < p; ++i) res[i] = (int) ((long) (p - p / i) * res[p % i] % p); return res; } // returns x ≡ a[i] (mod p[i]), where gcd(p[i], p[j]) == 1 public static BigInteger garnerRestore(int[] a, int[] p) { int[] x = a.clone(); for (int i = 0; i < x.length; ++i) for (int j = 0; j < i; ++j) x[i] = mod( BigInteger.valueOf(p[j]).modInverse(BigInteger.valueOf(p[i])).longValue() * (x[i] - x[j]), p[i]); BigInteger res = BigInteger.valueOf(x[0]); BigInteger m = BigInteger.ONE; for (int i = 1; i < x.length; i++) { m = m.multiply(BigInteger.valueOf(p[i - 1])); res = res.add(m.multiply(BigInteger.valueOf(x[i]))); } return res; } // returns x ≡ a[i] (mod p[i]), where gcd(p[i], p[j]) == 1 public static int simpleRestore(int[] a, int[] p) { int res = 0; for (int i = 0, m = 1; i < a.length; i++, m *= p[i]) while (res % p[i] != a[i]) res += m; return res; } // Usage example public static void main(String[] args) { Random rnd = new Random(1); for (int steps = 0; steps < 10000; steps++) { int a = rnd.nextInt(20) - 10; int b = rnd.nextInt(20) - 10; BigInteger xa = BigInteger.valueOf(a); BigInteger xb = BigInteger.valueOf(b); long gcd1 = gcd(a, b); long gcd2 = gcd2(a, b); long gcd = xa.gcd(xb).longValue(); long[] euclid1 = euclid(a, b); long[] euclid2 = euclid2(a, b); int inv1 = 0; int inv2 = 0; int inv = 0; if (gcd == 1 && b > 0) { inv1 = modInverse(a, b); inv2 = modInverse2(a, b); inv = xa.modInverse(xb).intValue(); } if (gcd1 != gcd || gcd2 != gcd || !Arrays.equals(euclid1, euclid2) || euclid1[0] != gcd || inv1 != inv || inv2 != inv) { System.err.println(a + " " + b); } } long a = 6; long b = 9; long[] res = euclid(a, b); System.out.println(res[1] + " * (" + a + ") " + " + " + res[2] + " * (" + b + ") = gcd(" + a + "," + b + ") = " + res[0]); System.out.println(Arrays.toString(generateInverses(7))); System.out.println(garnerRestore(new int[] {200_000_125, 300_000_333}, new int[] {1000_000_007, 1000_000_009})); } }