/* * Elliptic Curve Method (ECM) Prime Factorization * * Written by Dario Alejandro Alpern (Buenos Aires - Argentina) * Based in Yuji Kida's implementation for UBASIC interpreter. * Some code "borrowed" from Paul Zimmermann's ECM4C. * Modified for the Symja project by Axel Kramer. * Further refactorings by Tilman Neumann. * * Big thanks to Dario Alpern for his permission to use this piece of software under the GPL3 license. * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses/>. */ package de.tilman_neumann.jml.factor.ecm; import java.math.BigInteger; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import org.apache.log4j.Logger; import de.tilman_neumann.jml.factor.FactorAlgorithm; import de.tilman_neumann.jml.factor.tdiv.TDiv; import de.tilman_neumann.jml.powers.PurePowerTest; import de.tilman_neumann.jml.primes.exact.AutoExpandingPrimesArray; import de.tilman_neumann.jml.primes.probable.PrPTest; import de.tilman_neumann.util.ConfigUtil; import de.tilman_neumann.util.SortedMultiset; import de.tilman_neumann.util.SortedMultiset_BottomUp; import static de.tilman_neumann.jml.base.BigIntConstants.*; /** * <p>Use Elliptic Curve Method to find the prime number factors of a given BigInteger.</p> * * <p> * See <a href="https://en.wikipedia.org/wiki/Lenstra_elliptic_curve_factorization"> Wikipedia: Lenstra elliptic curve * factorization </a> * </p> */ public class EllipticCurveMethod extends FactorAlgorithm { private static final Logger LOG = Logger.getLogger(EllipticCurveMethod.class); static final int NLen = 1200; private static final long DosALa32 = 1L << 32; private static final long DosALa31 = 1L << 31; private static final long DosALa62 = 1L << 62; private static final double dDosALa31 = DosALa31; private static final double dDosALa62 = dDosALa31 * dDosALa31; private static final int ADD = 6; // number of multiplications in an addition private static final int DUP = 5; //number of multiplications in a duplicate /** 1 as "BigNbr" */ private static final int BigNbr1[] = new int[NLen]; /** maximum number of elliptic curves tested for 30, 35, ..., 85, 90 digits */ private static final int limits[] = { 5, 8, 15, 25, 27, 32, 43, 70, 150, 300, 350, 600, 1500 }; /** Primes < 5000 */ private static final int SmallPrime[] = new int[670]; // p_669 = 4999; private static final PrPTest prp = new PrPTest(); private static final PurePowerTest powerTest = new PurePowerTest(); private static final TDiv tdiv = new TDiv(); private MontgomeryMult montgomery; private static final double v[] = { 1.61803398875, 1.72360679775, 1.618347119656, 1.617914406529, 1.612429949509, 1.632839806089, 1.620181980807, 1.580178728295, 1.617214616534, 1.38196601125 }; private final long biTmp[] = new long[NLen]; // Used inside GCD calculations in multiple precision numbers private final int CalcAuxGcdU[] = new int[NLen]; private final int CalcAuxGcdV[] = new int[NLen]; private final int CalcAuxGcdT[] = new int[NLen]; private final int GcdAccumulated[] = new int[NLen]; final int CalcBigNbr[] = new int[NLen]; private final long[] CalcAuxModInvA = new long[NLen]; private final long[] CalcAuxModInvB = new long[NLen]; private final long[] CalcAuxModInvMu = new long[NLen]; private final long[] CalcAuxModInvGamma = new long[NLen]; /** input N as a BigNbr */ private final int TestNbr[] = new int[NLen]; /** Length of multiple precision numbers. */ int NumberLength; // TODO Running operations to the full NumberLength ignoring the true argument sizes is a waste of performance. /** Elliptic Curve number */ private int EC; private int[] fieldTX, fieldTZ, fieldUX, fieldUZ; private int[] fieldAux1, fieldAux2, fieldAux3, fieldAux4; static { BigNbr1[0] = 1; for (int i = 1; i < NLen; i++) { BigNbr1[i] = 0; } final AutoExpandingPrimesArray autoPrimes = AutoExpandingPrimesArray.get().ensureLimit(5000); SmallPrime[0] = 2; for (int indexM = 1; indexM < SmallPrime.length; indexM++) { SmallPrime[indexM] = autoPrimes.getPrime(indexM); } } @Override public String getName() { return "ECM"; } /** * {@inheritDoc} * * Note that the curve limit of this method for finding a single factor is the same as that of factor() * and factorize() for finding all prime factors of N. */ @Override public BigInteger findSingleFactor(BigInteger N) { EC = 1; return fnECM(N); } @Override public SortedMultiset<BigInteger> factor(BigInteger N) { SortedMultiset<BigInteger> allFactors = new SortedMultiset_BottomUp<BigInteger>(); SortedMultiset<BigInteger> compositeFactors = factorize(N, allFactors); allFactors.addAll(compositeFactors); return allFactors; } /** * Find small factors of some N. Returns found factors in <code>primeFactors</code> and eventually some * unfactored composites as return value. * * @param N the number to factor * @param primeFactors the found prime factors. * @return unfactored composites left after stopping ECM, empty map if N has been factored completely */ public SortedMultiset<BigInteger> factorize(BigInteger N, SortedMap<BigInteger, Integer> primeFactors) { // set up new N EC = 1; // Do trial division by all primes < 131072. SortedMultiset<BigInteger> unresolvedComposites = new SortedMultiset_BottomUp<>(); N = tdiv.findSmallFactors(N, 131072, primeFactors); // TODO do outside ECM? if (N.equals(I_1)) { return unresolvedComposites; } // There are factors greater than 131071, and they may be prime or composite. if (isProbablePrime(N)) { addToMap(N, 1, primeFactors); return unresolvedComposites; } // N is composite -> do ECM TreeMap<BigInteger, Integer> compositesToTest = new TreeMap<BigInteger, Integer>(); compositesToTest.put(N, 1); while (!compositesToTest.isEmpty()) { // get next composite to test Entry<BigInteger, Integer> compositeEntry = compositesToTest.pollLastEntry(); N = compositeEntry.getKey(); int exp = compositeEntry.getValue(); // pure power? PurePowerTest.Result r = powerTest.test(N); if (r != null) { // N is a pure power! addToMapDependingOnPrimeTest(r.base, exp*r.exponent, primeFactors, compositesToTest); continue; // test next composite } // ECM final BigInteger NN = fnECM(N); if (NN.equals(I_1)) { // N is composite but could not be resolved addToMap(N, exp, unresolvedComposites); continue; } // NN is a factor of N addToMapDependingOnPrimeTest(NN, exp, primeFactors, compositesToTest); addToMapDependingOnPrimeTest(N.divide(NN), exp, primeFactors, compositesToTest); } return unresolvedComposites; } private boolean isProbablePrime(BigInteger N) { // XXX The 33-bit "guard" is only safe if we did tdiv for all p <= sqrt(2^33) before return N.bitLength() <= 33 || prp.isProbablePrime(N); } private void addToMapDependingOnPrimeTest(BigInteger factor, int exp, SortedMap<BigInteger, Integer> primeFactors, SortedMap<BigInteger, Integer> compositeFactors) { addToMap(factor, exp, isProbablePrime(factor) ? primeFactors : compositeFactors); } private void addToMap(BigInteger N, int exp, SortedMap<BigInteger, Integer> map) { Integer oldExp = map.get(N); // old entry is replaced if oldExp!=null map.put(N, (oldExp == null) ? exp : oldExp+exp); } private BigInteger fnECM(BigInteger N) { int I, J, Pass, Qaux; long L1, L2, LS, P; int[] A0 = new int[NLen]; int[] A02 = new int[NLen]; int[] A03 = new int[NLen]; int[] AA = new int[NLen]; int[] DX = new int[NLen]; // zero-init required int[] DZ = new int[NLen]; // zero-init required int[] GD = new int[NLen]; // zero-init required int[] M = new int[NLen]; // zero-init required int[] TX = new int[NLen]; fieldTX = TX; int[] TZ = new int[NLen]; fieldTZ = TZ; int[] UX = new int[NLen]; fieldUX = UX; int[] UZ = new int[NLen]; fieldUZ = UZ; int[] W1 = new int[NLen]; int[] W2 = new int[NLen]; int[] W3 = new int[NLen]; // zero-init required int[] W4 = new int[NLen]; // zero-init required int[] WX = new int[NLen]; int[] WZ = new int[NLen]; int[] X = new int[NLen]; int[] Z = new int[NLen]; int[] Aux1 = new int[NLen]; fieldAux1 = Aux1; int[] Aux2 = new int[NLen]; fieldAux2 = Aux2; int[] Aux3 = new int[NLen]; fieldAux3 = Aux3; int[] Aux4 = new int[NLen]; fieldAux4 = Aux4; int[] Xaux = new int[NLen]; int[] Zaux = new int[NLen]; int[][] root = new int[480][NLen]; byte[] sieve = new byte[23100]; byte[] sieve2310 = new byte[2310]; int[] sieveidx = new int[480]; // Compute NumberLength, the number of ints in which all computations are carried out this.NumberLength = computeNumberLength(N.toByteArray().length * 8); // Convert BigInteger N into TestNbr, a representation in NumberLength 31-bit integers BigNbrToBigInt(N, TestNbr, NumberLength); // set up Montgomery multiplication this.montgomery = new MontgomeryMult(TestNbr, NumberLength); // More initializations double dN = TestNbr[NumberLength - 1]; if (NumberLength > 1) { dN += TestNbr[NumberLength - 2] / dDosALa31; } if (NumberLength > 2) { dN += TestNbr[NumberLength - 3] / dDosALa62; } final int MontgomeryMultR1[] = new int[NLen]; final int MontgomeryMultR2[] = new int[NLen]; final int MontgomeryMultAfterInv[] = new int[NLen]; MontgomeryMultR1[NumberLength] = 1; for (int jj=NumberLength-1; jj>=0; jj--) { MontgomeryMultR1[jj] = 0; } AdjustModN(MontgomeryMultR1, dN); MultBigNbrModN(MontgomeryMultR1, MontgomeryMultR1, MontgomeryMultR2, dN); montgomery.mul(MontgomeryMultR2, MontgomeryMultR2, MontgomeryMultAfterInv); AddBigNbrModN(MontgomeryMultR1, MontgomeryMultR1, MontgomeryMultR2); // Modular curve loop: It seems to be faster not to repeat previously tested curves for new factors EC--; do { new_curve: do { EC++; int digitsOfN = N.toString().length(); // Get number of digits. if (digitsOfN > 30 && digitsOfN <= 90) { // If between 30 and 90 digits... int limit = limits[((int) digitsOfN - 31) / 5]; if (EC >= limit) { EC += 1; return I_1; // stop ECM } } L1 = 2000; L2 = 200000; LS = 45; /* Number of primes less than 2000 */ if (EC > 25) { if (EC < 326) { L1 = 50000; L2 = 5000000; LS = 224; /* Number of primes less than 50000 */ } else if (EC < 2000) { L1 = 1000000; L2 = 100000000; LS = 1001; /* Number of primes less than 1000000 */ } else { L1 = 11000000; L2 = 1100000000; LS = 3316; /* Number of primes less than 11000000 */ } } // System.out.println(primalityString + EC + "\n" + UpperLine + "\n" + LowerLine); LongToBigNbr(2 * (EC + 1), Aux1); LongToBigNbr(3 * (EC + 1) * (EC + 1) - 1, Aux2); ModInvBigNbr(Aux2, TestNbr, Aux2); MultBigNbrModN(Aux1, Aux2, Aux3, dN); MultBigNbrModN(Aux3, MontgomeryMultR1, A0, dN); montgomery.mul(A0, A0, A02); montgomery.mul(A02, A0, A03); SubtractBigNbrModN(A03, A0, Aux1); MultBigNbrByLongModN(A02, 9, Aux2, dN); SubtractBigNbrModN(Aux2, MontgomeryMultR1, Aux2); montgomery.mul(Aux1, Aux2, Aux3); if (BigNbrIsZero(Aux3)) { continue; } MultBigNbrByLongModN(A0, 4, Z, dN); MultBigNbrByLongModN(A02, 6, Aux1, dN); SubtractBigNbrModN(MontgomeryMultR1, Aux1, Aux1); montgomery.mul(A02, A02, Aux2); MultBigNbrByLongModN(Aux2, 3, Aux2, dN); SubtractBigNbrModN(Aux1, Aux2, Aux1); MultBigNbrByLongModN(A03, 4, Aux2, dN); ModInvBigNbr(Aux2, TestNbr, Aux2); montgomery.mul(Aux2, MontgomeryMultAfterInv, Aux3); montgomery.mul(Aux1, Aux3, A0); AddBigNbrModN(A0, MontgomeryMultR2, Aux1); LongToBigNbr(4, Aux2); ModInvBigNbr(Aux2, TestNbr, Aux3); MultBigNbrModN(Aux3, MontgomeryMultR1, Aux2, dN); montgomery.mul(Aux1, Aux2, AA); // the only write access to AA MultBigNbrByLongModN(A02, 3, Aux1, dN); AddBigNbrModN(Aux1, MontgomeryMultR1, X); /**************/ /* First step */ /**************/ System.arraycopy(X, 0, Xaux, 0, NumberLength); System.arraycopy(Z, 0, Zaux, 0, NumberLength); System.arraycopy(MontgomeryMultR1, 0, GcdAccumulated, 0, NumberLength); for (Pass = 0; Pass < 2; Pass++) { /* For powers of 2 */ for (I = 1; I <= L1; I <<= 1) { duplicate(X, Z, X, Z, AA); } for (I = 3; I <= L1; I *= 3) { duplicate(W1, W2, X, Z, AA); add3(X, Z, X, Z, W1, W2, X, Z); } if (Pass == 0) { montgomery.mul(GcdAccumulated, Z, Aux1); System.arraycopy(Aux1, 0, GcdAccumulated, 0, NumberLength); } else { GcdBigNbr(Z, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } } /* for powers of odd primes */ int indexM = 1; do { P = SmallPrime[indexM++]; for (long IP = P; IP <= L1; IP *= P) { prac((int) P, X, Z, W1, W2, W3, W4, AA); } if (Pass == 0) { montgomery.mul(GcdAccumulated, Z, Aux1); System.arraycopy(Aux1, 0, GcdAccumulated, 0, NumberLength); } else { GcdBigNbr(Z, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } } } while (P <= LS); P += 2; /* Initialize sieve2310[n]: 1 if gcd(P+2n,2310) > 1, 0 otherwise */ int u = (int) P; for (int i = 0; i < 2310; i++) { sieve2310[i] = (u % 3 == 0 || u % 5 == 0 || u % 7 == 0 || u % 11 == 0 ? (byte) 1 : (byte) 0); u += 2; } do { /* Generate sieve */ GenerateSieve((int) P, sieve, sieve2310, SmallPrime); /* Walk through sieve */ for (int i = 0; i < 23100; i++) { if (sieve[i] != 0) continue; /* Do not process composites */ if (P + 2 * i > L1) break; prac((int) (P + 2 * i), X, Z, W1, W2, W3, W4, AA); if (Pass == 0) { montgomery.mul(GcdAccumulated, Z, Aux1); System.arraycopy(Aux1, 0, GcdAccumulated, 0, NumberLength); } else { GcdBigNbr(Z, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } } } P += 46200; } while (P < L1); if (Pass == 0) { if (BigNbrIsZero(GcdAccumulated)) { // If GcdAccumulated is... System.arraycopy(Xaux, 0, X, 0, NumberLength); System.arraycopy(Zaux, 0, Z, 0, NumberLength); continue; // ... a multiple of TestNbr, continue. } GcdBigNbr(GcdAccumulated, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } break; } } /* end for Pass */ /******************************************************/ /* Second step (using improved standard continuation) */ /******************************************************/ int j = 0; for (int u = 1; u < 2310; u += 2) { if (u % 3 == 0 || u % 5 == 0 || u % 7 == 0 || u % 11 == 0) { sieve2310[u / 2] = (byte) 1; } else { sieve2310[(sieveidx[j++] = u / 2)] = (byte) 0; } } System.arraycopy(sieve2310, 0, sieve2310, 1155, 1155); System.arraycopy(X, 0, Xaux, 0, NumberLength); System.arraycopy(Z, 0, Zaux, 0, NumberLength); // (X:Z) -> Q (output from step 1) for (Pass = 0; Pass < 2; Pass++) { System.arraycopy(MontgomeryMultR1, 0, GcdAccumulated, 0, NumberLength); System.arraycopy(X, 0, UX, 0, NumberLength); System.arraycopy(Z, 0, UZ, 0, NumberLength); // (UX:UZ) -> Q ModInvBigNbr(Z, TestNbr, Aux2); montgomery.mul(Aux2, MontgomeryMultAfterInv, Aux1); montgomery.mul(Aux1, X, root[0]); // root[0] <- X/Z (Q) J = 0; AddBigNbrModN(X, Z, Aux1); montgomery.mul(Aux1, Aux1, W1); SubtractBigNbrModN(X, Z, Aux1); montgomery.mul(Aux1, Aux1, W2); montgomery.mul(W1, W2, TX); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, AA, Aux2); AddBigNbrModN(Aux2, W2, Aux3); montgomery.mul(Aux1, Aux3, TZ); // (TX:TZ) -> 2Q SubtractBigNbrModN(X, Z, Aux1); AddBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W1); AddBigNbrModN(X, Z, Aux1); SubtractBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W2); AddBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UZ, X); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UX, Z); // (X:Z) -> 3Q for (I = 5; I < 2310; I += 2) { System.arraycopy(X, 0, WX, 0, NumberLength); System.arraycopy(Z, 0, WZ, 0, NumberLength); SubtractBigNbrModN(X, Z, Aux1); AddBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W1); AddBigNbrModN(X, Z, Aux1); SubtractBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W2); AddBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UZ, X); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UX, Z); // (X:Z) -> 5Q, 7Q, ... if (Pass == 0) { montgomery.mul(GcdAccumulated, Aux1, Aux2); System.arraycopy(Aux2, 0, GcdAccumulated, 0, NumberLength); } else { GcdBigNbr(Aux1, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } } if (I == 1155) { System.arraycopy(X, 0, DX, 0, NumberLength); System.arraycopy(Z, 0, DZ, 0, NumberLength); // (DX:DZ) -> 1155Q } if (I % 3 != 0 && I % 5 != 0 && I % 7 != 0 && I % 11 != 0) { J++; ModInvBigNbr(Z, TestNbr, Aux2); montgomery.mul(Aux2, MontgomeryMultAfterInv, Aux1); montgomery.mul(Aux1, X, root[J]); // root[J] <- X/Z } System.arraycopy(WX, 0, UX, 0, NumberLength); System.arraycopy(WZ, 0, UZ, 0, NumberLength); // (UX:UZ) <- Previous (X:Z) } /* end for I */ AddBigNbrModN(DX, DZ, Aux1); montgomery.mul(Aux1, Aux1, W1); SubtractBigNbrModN(DX, DZ, Aux1); montgomery.mul(Aux1, Aux1, W2); montgomery.mul(W1, W2, X); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, AA, Aux2); AddBigNbrModN(Aux2, W2, Aux3); montgomery.mul(Aux1, Aux3, Z); System.arraycopy(X, 0, UX, 0, NumberLength); System.arraycopy(Z, 0, UZ, 0, NumberLength); // (UX:UZ) -> 2310Q AddBigNbrModN(X, Z, Aux1); montgomery.mul(Aux1, Aux1, W1); SubtractBigNbrModN(X, Z, Aux1); montgomery.mul(Aux1, Aux1, W2); montgomery.mul(W1, W2, TX); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, AA, Aux2); AddBigNbrModN(Aux2, W2, Aux3); montgomery.mul(Aux1, Aux3, TZ); // (TX:TZ) -> 2*2310Q SubtractBigNbrModN(X, Z, Aux1); AddBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W1); AddBigNbrModN(X, Z, Aux1); SubtractBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W2); AddBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UZ, X); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UX, Z); // (X:Z) -> 3*2310Q Qaux = (int) (L1 / 4620); int maxIndexM = (int) (L2 / 4620); for (int indexM = 0; indexM <= maxIndexM; indexM++) { if (indexM >= Qaux) { // If inside step 2 range... if (indexM == 0) { ModInvBigNbr(UZ, TestNbr, Aux2); montgomery.mul(Aux2, MontgomeryMultAfterInv, Aux3); montgomery.mul(UX, Aux3, Aux1); // Aux1 <- X/Z (2310Q) } else { ModInvBigNbr(Z, TestNbr, Aux2); montgomery.mul(Aux2, MontgomeryMultAfterInv, Aux3); montgomery.mul(X, Aux3, Aux1); // Aux1 <- X/Z (3,5,* 2310Q) } /* Generate sieve */ if (indexM % 10 == 0 || indexM == Qaux) { GenerateSieve(indexM / 10 * 46200 + 1, sieve, sieve2310, SmallPrime); } /* Walk through sieve */ J = 1155 + (indexM % 10) * 2310; for (int i = 0; i < 480; i++) { j = sieveidx[i]; // 0 < J < 1155 if (sieve[J + j] != 0 && sieve[J - 1 - j] != 0) { continue; // Do not process if both are composite numbers. } SubtractBigNbrModN(Aux1, root[i], M); montgomery.mul(GcdAccumulated, M, Aux2); System.arraycopy(Aux2, 0, GcdAccumulated, 0, NumberLength); } if (Pass != 0) { GcdBigNbr(GcdAccumulated, TestNbr, GD); if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } } } if (indexM != 0) { // Update (X:Z) System.arraycopy(X, 0, WX, 0, NumberLength); System.arraycopy(Z, 0, WZ, 0, NumberLength); SubtractBigNbrModN(X, Z, Aux1); AddBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W1); AddBigNbrModN(X, Z, Aux1); SubtractBigNbrModN(TX, TZ, Aux2); montgomery.mul(Aux1, Aux2, W2); AddBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UZ, X); SubtractBigNbrModN(W1, W2, Aux1); montgomery.mul(Aux1, Aux1, Aux2); montgomery.mul(Aux2, UX, Z); System.arraycopy(WX, 0, UX, 0, NumberLength); System.arraycopy(WZ, 0, UZ, 0, NumberLength); } } // end for Q if (Pass == 0) { if (BigNbrIsZero(GcdAccumulated)) { // If GcdAccumulated is... System.arraycopy(Xaux, 0, X, 0, NumberLength); System.arraycopy(Zaux, 0, Z, 0, NumberLength); continue; // ... a multiple of TestNbr, continue. } GcdBigNbr(GcdAccumulated, TestNbr, GD); if (BigNbrAreEqual(GD, TestNbr) == true) { break; } if (BigNbrAreEqual(GD, BigNbr1) == false) { break new_curve; // found factor, exit } break; } } /* end for Pass */ } while (true); /* end curve calculation */ assert !(BigNbrAreEqual(GD, TestNbr)); } while (BigNbrAreEqual(GD, TestNbr) == true); // System.out.println(""); // StepECM = 0; /* do not show pass number on screen */ return BigIntToBigNbr(GD); } private static void GenerateSieve(int initial, byte[] sieve, byte[] sieve2310, int[] SmallPrime) { int i, j, Q; for (i = 0; i < 23100; i += 2310) { System.arraycopy(sieve2310, 0, sieve, i, 2310); } j = 5; Q = 13; /* Point to prime 13 */ do { if (initial > Q * Q) { for (i = (int) (((long) initial * ((Q - 1) / 2)) % Q); i < 23100; i += Q) { sieve[i] = 1; /* Composite */ } } else { i = Q * Q - initial; if (i < 46200) { for (i = i / 2; i < 23100; i += Q) { sieve[i] = 1; /* Composite */ } } else { break; } } Q = SmallPrime[++j]; } while (Q < 5000); } /** * Computes nP from P=(x:z) and puts the result in (x:z). Assumes n>2. * * @param n * @param x * @param z * @param xT * @param zT * @param xT2 * @param zT2 */ private void prac(int n, int[] x, int[] z, int[] xT, int[] zT, int[] xT2, int[] zT2, int[] AA) { int d, e, r, i; int[] t; int[] xA = x, zA = z; int[] xB = fieldAux1, zB = fieldAux2; int[] xC = fieldAux3, zC = fieldAux4; /* chooses the best value of v */ r = lucas_cost(n, v[0]); i = 0; for (d = 1; d < 10; d++) { e = lucas_cost(n, v[d]); if (e < r) { r = e; i = d; } } d = n; r = (int) (d / v[i] + 0.5); /* first iteration always begins by Condition 3, then a swap */ d = n - r; e = 2 * r - n; System.arraycopy(xA, 0, xB, 0, NumberLength); // B = A System.arraycopy(zA, 0, zB, 0, NumberLength); System.arraycopy(xA, 0, xC, 0, NumberLength); // C = A System.arraycopy(zA, 0, zC, 0, NumberLength); duplicate(xA, zA, xA, zA, AA); /* A=2*A */ while (d != e) { if (d < e) { r = d; d = e; e = r; t = xA; xA = xB; xB = t; t = zA; zA = zB; zB = t; } /* do the first line of Table 4 whose condition qualifies */ if (4 * d <= 5 * e && ((d + e) % 3) == 0) { /* condition 1 */ r = (2 * d - e) / 3; e = (2 * e - d) / 3; d = r; add3(xT, zT, xA, zA, xB, zB, xC, zC); /* T = f(A,B,C) */ add3(xT2, zT2, xT, zT, xA, zA, xB, zB); /* T2 = f(T,A,B) */ add3(xB, zB, xB, zB, xT, zT, xA, zA); /* B = f(B,T,A) */ t = xA; xA = xT2; xT2 = t; t = zA; zA = zT2; zT2 = t; /* swap A and T2 */ } else if (4 * d <= 5 * e && (d - e) % 6 == 0) { /* condition 2 */ d = (d - e) / 2; add3(xB, zB, xA, zA, xB, zB, xC, zC); /* B = f(A,B,C) */ duplicate(xA, zA, xA, zA, AA); /* A = 2*A */ } else if (d <= (4 * e)) { /* condition 3 */ d -= e; add3(xT, zT, xB, zB, xA, zA, xC, zC); /* T = f(B,A,C) */ t = xB; xB = xT; xT = xC; xC = t; t = zB; zB = zT; zT = zC; zC = t; /* circular permutation (B,T,C) */ } else if ((d + e) % 2 == 0) { /* condition 4 */ d = (d - e) / 2; add3(xB, zB, xB, zB, xA, zA, xC, zC); /* B = f(B,A,C) */ duplicate(xA, zA, xA, zA, AA); /* A = 2*A */ } else if (d % 2 == 0) { /* condition 5 */ d /= 2; add3(xC, zC, xC, zC, xA, zA, xB, zB); /* C = f(C,A,B) */ duplicate(xA, zA, xA, zA, AA); /* A = 2*A */ } else if (d % 3 == 0) { /* condition 6 */ d = d / 3 - e; duplicate(xT, zT, xA, zA, AA); /* T1 = 2*A */ add3(xT2, zT2, xA, zA, xB, zB, xC, zC); /* T2 = f(A,B,C) */ add3(xA, zA, xT, zT, xA, zA, xA, zA); /* A = f(T1,A,A) */ add3(xT, zT, xT, zT, xT2, zT2, xC, zC); /* T1 = f(T1,T2,C) */ t = xC; xC = xB; xB = xT; xT = t; t = zC; zC = zB; zB = zT; zT = t; /* circular permutation (C,B,T) */ } else if ((d + e) % 3 == 0) { /* condition 7 */ d = (d - 2 * e) / 3; add3(xT, zT, xA, zA, xB, zB, xC, zC); /* T1 = f(A,B,C) */ add3(xB, zB, xT, zT, xA, zA, xB, zB); /* B = f(T1,A,B) */ duplicate(xT, zT, xA, zA, AA); add3(xA, zA, xA, zA, xT, zT, xA, zA); /* A = 3*A */ } else if ((d - e) % 3 == 0) { /* condition 8 */ d = (d - e) / 3; add3(xT, zT, xA, zA, xB, zB, xC, zC); /* T1 = f(A,B,C) */ add3(xC, zC, xC, zC, xA, zA, xB, zB); /* C = f(A,C,B) */ t = xB; xB = xT; xT = t; t = zB; zB = zT; zT = t; /* swap B and T */ duplicate(xT, zT, xA, zA, AA); add3(xA, zA, xA, zA, xT, zT, xA, zA); /* A = 3*A */ } else if (e % 2 == 0) { /* condition 9 */ e /= 2; add3(xC, zC, xC, zC, xB, zB, xA, zA); /* C = f(C,B,A) */ duplicate(xB, zB, xB, zB, AA); /* B = 2*B */ } } add3(x, z, xA, zA, xB, zB, xC, zC); } /** returns the number of modular multiplications */ private static int lucas_cost(int n, double v) { int c, d, e, r; d = n; r = (int) (d / v + 0.5); if (r >= n) return (ADD * n); d = n - r; e = 2 * r - n; c = DUP + ADD; /* initial duplicate and final addition */ while (d != e) { if (d < e) { r = d; d = e; e = r; } if (4 * d <= 5 * e && ((d + e) % 3) == 0) { /* condition 1 */ r = (2 * d - e) / 3; e = (2 * e - d) / 3; d = r; c += 3 * ADD; /* 3 additions */ } else if (4 * d <= 5 * e && (d - e) % 6 == 0) { /* condition 2 */ d = (d - e) / 2; c += ADD + DUP; /* one addition, one duplicate */ } else if (d <= (4 * e)) { /* condition 3 */ d -= e; c += ADD; /* one addition */ } else if ((d + e) % 2 == 0) { /* condition 4 */ d = (d - e) / 2; c += ADD + DUP; /* one addition, one duplicate */ } else if (d % 2 == 0) { /* condition 5 */ d /= 2; c += ADD + DUP; /* one addition, one duplicate */ } else if (d % 3 == 0) { /* condition 6 */ d = d / 3 - e; c += 3 * ADD + DUP; /* three additions, one duplicate */ } else if ((d + e) % 3 == 0) { /* condition 7 */ d = (d - 2 * e) / 3; c += 3 * ADD + DUP; /* three additions, one duplicate */ } else if ((d - e) % 3 == 0) { /* condition 8 */ d = (d - e) / 3; c += 3 * ADD + DUP; /* three additions, one duplicate */ } else if (e % 2 == 0) { /* condition 9 */ e /= 2; c += ADD + DUP; /* one addition, one duplicate */ } } return (c); } /** * computes 2P=(x2:z2) from P=(x1:z1), with 5 mul, 4 add/sub, 5 mod. Uses the following global variables: - n : * number to factor - b : (a+2)/4 mod n - u, v, w : auxiliary variables Modifies: x2, z2, u, v, w */ private void duplicate(int[] x2, int[] z2, int[] x1, int[] z1, int[] AA) { int[] u = fieldUZ; int[] v = fieldTX; int[] w = fieldTZ; AddBigNbrModN(x1, z1, w); // w = x1+z1 montgomery.mul(w, w, u); // u = (x1+z1)^2 SubtractBigNbrModN(x1, z1, w); // w = x1-z1 montgomery.mul(w, w, v); // v = (x1-z1)^2 montgomery.mul(u, v, x2); // x2 = u*v = (x1^2 - z1^2)^2 SubtractBigNbrModN(u, v, w); // w = u-v = 4*x1*z1 montgomery.mul(AA, w, u); AddBigNbrModN(u, v, u); // u = (v+b*w) montgomery.mul(w, u, z2); // z2 = (w*u) } /** * Adds Q=(x2:z2) and R=(x1:z1) and puts the result in (x3:z3), using 5/6 mul, 6 add/sub and 6 mod. One assumes that * Q-R=P or R-Q=P where P=(x:z). Uses the following global variables: - n : number to factor - x, z : coordinates of * P - u, v, w : auxiliary variables Modifies: x3, z3, u, v, w. (x3,z3) may be identical to (x2,z2) and to (x,z) * * @param x3 * @param z3 * @param x2 * @param z2 * @param x1 * @param z1 * @param x * @param z */ private void add3(int[] x3, int[] z3, int[] x2, int[] z2, int[] x1, int[] z1, int[] x, int[] z) { int[] t = fieldTX; int[] u = fieldTZ; int[] v = fieldUX; int[] w = fieldUZ; SubtractBigNbrModN(x2, z2, v); // v = x2-z2 AddBigNbrModN(x1, z1, w); // w = x1+z1 montgomery.mul(v, w, u); // u = (x2-z2)*(x1+z1) AddBigNbrModN(x2, z2, w); // w = x2+z2 SubtractBigNbrModN(x1, z1, t); // t = x1-z1 montgomery.mul(t, w, v); // v = (x2+z2)*(x1-z1) AddBigNbrModN(u, v, t); // t = 2*(x1*x2-z1*z2) montgomery.mul(t, t, w); // w = 4*(x1*x2-z1*z2)^2 SubtractBigNbrModN(u, v, t); // t = 2*(x2*z1-x1*z2) montgomery.mul(t, t, v); // v = 4*(x2*z1-x1*z2)^2 if (BigNbrAreEqual(x, x3)) { System.arraycopy(x, 0, u, 0, NumberLength); System.arraycopy(w, 0, t, 0, NumberLength); montgomery.mul(z, t, w); montgomery.mul(v, u, z3); System.arraycopy(w, 0, x3, 0, NumberLength); } else { montgomery.mul(w, z, x3); // x3 = 4*z*(x1*x2-z1*z2)^2 montgomery.mul(x, v, z3); // z3 = 4*x*(x2*z1-x1*z2)^2 } } /** * Convert a long <code>Nbr</code> into a BigNbr <code>Out</code> represented by 31-bit integers. * @param Nbr input * @param Out output */ void LongToBigNbr(long Nbr, int Out[]) { final boolean chSign = Nbr<0 ? true: false; final long nbrPos = Nbr<0 ? -Nbr : Nbr; Out[0] = (int)(nbrPos & 0x7FFFFFFF); Out[1] = (int)((nbrPos >> 31) & 0x7FFFFFFF); if (NumberLength > 2) { // bit 63 needs special consideration Out[2] = (nbrPos >= DosALa62) ? 1 : 0; for (int i = 3; i < NumberLength; i++) { Out[i] = 0; } } if (chSign) { ChSignBigNbr(Out); } } /** * Converts BigInteger N into TestNbr represented by 31-bit ints. * @param N input * @param TestNbr output * @param NumberLength the size of numbers we are working with */ void BigNbrToBigInt(BigInteger N, int[] TestNbr, int NumberLength) { // Temp needs 1 extra int because of TestNbr[j] = p; in BigNbrToBigInt(long[]) long[] Temp = new long[NumberLength+1]; // zero-initialized BigNbrToBigInt(N, Temp, NumberLength); Convert32To31Bits(Temp, TestNbr, NumberLength); } /** * Converts BigInteger N into TestNbr represented by 32-bit ints. * @param N input * @param TestNbr output * @param NumberLength */ void BigNbrToBigInt(BigInteger N, long[] TestNbr, int NumberLength) { byte[] NBytes = N.toByteArray(); int j = 0; int mask = 1; long p = 0; // process unsigned bytes for (int i = NBytes.length - 1; i > 0; i--) { p += mask * (NBytes[i] & 0xFFL); mask <<= 8; if (mask == 0) { // int overflow after 4 shifts TestNbr[j++] = p; mask = 1; p = 0; } } // the most significant byte holds BigIntegers sign bit p += mask * NBytes[0]; mask <<= 8; if (mask == 0) { TestNbr[j++] = p; mask = 1; p = 0; } TestNbr[j] = p; // Zero-initialize all TestNbr entries from j+1 to NumberLength. // It is required that TestNbr[NumberLength] is set to 0, too. while (++j <= NumberLength) { TestNbr[j] = 0; } } static int computeNumberLength(int bitLength) { // The original computation was (bitLength + 30)/31; // added one bit for the sign and another to avoid overflows in additions return (bitLength + 32)/31; } void AddBigNbr(int Nbr1[], int Nbr2[], int Sum[]) { int NumberLength = this.NumberLength; long Cy = 0; for (int i = 0; i < NumberLength; i++) { Cy = (Cy >> 31) + Nbr1[i] + Nbr2[i]; Sum[i] = (int)(Cy & 0x7FFFFFFFL); } } void AddBigNbr32(long Nbr1[], long Nbr2[], long Sum[]) { int NumberLength = this.NumberLength; long Cy = 0; for (int i = 0; i < NumberLength; i++) { Cy = (Cy >> 32) + Nbr1[i] + Nbr2[i]; Sum[i] = Cy & 0xFFFFFFFFl; } } void AddBigNbrModN(int Nbr1[], int Nbr2[], int Sum[]) { int NumberLength = this.NumberLength; long MaxUInt = 0x7FFFFFFFL; long carry = 0; int i; for (i = 0; i < NumberLength; i++) { carry = (carry >> 31) + (long)Nbr1[i] + (long)Nbr2[i] - (long)TestNbr[i]; Sum[i] = (int)(carry & MaxUInt); } if (carry < 0) { carry = 0; for (i = 0; i < NumberLength; i++) { carry = (carry >> 31) + (long)Sum[i] + (long)TestNbr[i]; Sum[i] = (int)(carry & MaxUInt); } } } void SubtractBigNbr(int Nbr1[], int Nbr2[], int Diff[]) { long carry = 0; for (int i = 0; i < NumberLength; i++) { carry = (carry >> 31) + (long)Nbr1[i] - (long)Nbr2[i]; Diff[i] = (int)(carry & 0x7FFFFFFFL); } } void SubtractBigNbr32(long Nbr1[], long Nbr2[], long Diff[]) { int NumberLength = this.NumberLength; long Cy = 0; for (int i = 0; i < NumberLength; i++) { Cy = (Cy >> 32) + Nbr1[i] - Nbr2[i]; Diff[i] = Cy & 0xFFFFFFFFl; } } void SubtractBigNbrModN(int Nbr1[], int Nbr2[], int Diff[]) { int NumberLength = this.NumberLength; long MaxUInt = 0x7FFFFFFFL; // Integer.MAX_VALUE long carry = 0; int i; for (i = 0; i < NumberLength; i++) { carry = (carry >> 31) + (long)Nbr1[i] - (long)Nbr2[i]; Diff[i] = (int)(carry & MaxUInt); } if (carry < 0) { carry = 0; for (i = 0; i < NumberLength; i++) { carry = (carry >> 31) + (long)Diff[i] + (long)TestNbr[i]; Diff[i] = (int)(carry & MaxUInt); } } } void MultBigNbrByLongModN(int Nbr1[], long Nbr2, int Prod[], double dN) { int NumberLength = this.NumberLength; long MaxUInt = 0x7FFFFFFFL; long Pr; int j; Pr = 0; for (j = 0; j < NumberLength; j++) { Pr = (Pr >>> 31) + Nbr2 * Nbr1[j]; Prod[j] = (int)(Pr & MaxUInt); } Prod[j] = (int)(Pr >>> 31); AdjustModN(Prod, dN); } private void MultBigNbrModN(int Nbr1[], int Nbr2[], int Prod[], double dN) { int NumberLength = this.NumberLength; long MaxUInt = 0x7FFFFFFFL; int i, j; long Pr, Nbr; i = NumberLength; do { Prod[--i] = 0; } while (i > 0); i = NumberLength; do { Nbr = Nbr1[--i]; j = NumberLength; do { Prod[j] = Prod[j - 1]; j--; } while (j > 0); Prod[0] = 0; Pr = 0; for (j = 0; j < NumberLength; j++) { Pr = (Pr >>> 31) + Nbr * Nbr2[j] + Prod[j]; Prod[j] = (int)(Pr & MaxUInt); } Prod[j] += (Pr >>> 31); AdjustModN(Prod, dN); } while (i > 0); } private void AdjustModN(int Nbr[], double dN) { int NumberLength = this.NumberLength; long MaxUInt = 0x7FFFFFFFL; long TrialQuotient; long carry; int i; double dAux; dAux = Nbr[NumberLength] * dDosALa31 + Nbr[NumberLength - 1]; if (NumberLength > 1) { dAux += Nbr[NumberLength - 2] / dDosALa31; } TrialQuotient = (long) (dAux / dN) + 3; // Axel: (long) Math.ceil(dAux / dN) + 2; if (TrialQuotient >= DosALa32) { carry = 0; for (i = 0; i < NumberLength; i++) { carry = Nbr[i + 1] - (TrialQuotient >>> 31) * TestNbr[i] - carry; Nbr[i + 1] = (int)(carry & MaxUInt); carry = (MaxUInt - carry) >>> 31; } TrialQuotient &= MaxUInt; } carry = 0; for (i = 0; i < NumberLength; i++) { carry = Nbr[i] - TrialQuotient * TestNbr[i] - carry; Nbr[i] = (int)(carry & MaxUInt); carry = (MaxUInt - carry) >>> 31; } Nbr[NumberLength] -= (int)carry; while ((Nbr[NumberLength] & MaxUInt) != 0) { carry = 0; for (i = 0; i < NumberLength; i++) { carry += (long)Nbr[i] + (long)TestNbr[i]; Nbr[i] = (int)(carry & MaxUInt); carry >>= 31; } Nbr[NumberLength] += (int)carry; } } private void DivBigNbrByLong(int Dividend[], long Divisor, int Quotient[]) { int i; boolean ChSignDivisor = false; long Divid, Rem = 0; if (Divisor < 0) { // If divisor is negative... ChSignDivisor = true; // Indicate to change sign at the end and Divisor = -Divisor; // convert divisor to positive. } if (Dividend[i = NumberLength - 1] >= 0x40000000l) { // If dividend is negative... Rem = Divisor - 1; } for (; i >= 0; i--) { Divid = Dividend[i] + (Rem << 31); Rem = Divid % Divisor; Quotient[i] = (int) (Divid / Divisor); } if (ChSignDivisor) { // Change sign if divisor is negative. ChSignBigNbr(Quotient); // Convert divisor to positive. } } private boolean BigNbrAreEqual(int Nbr1[], int Nbr2[]) { for (int i = 0; i < NumberLength; i++) { if (Nbr1[i] != Nbr2[i]) { return false; } } return true; } private boolean BigNbrIsZero(int Nbr[]) { for (int i = 0; i < NumberLength; i++) { if (Nbr[i] != 0) { return false; } } return true; } private void ChSignBigNbr(int Nbr[]) { int NumberLength = this.NumberLength; int Cy = 0; for (int i = 0; i < NumberLength; i++) { Cy = (Cy >> 31) - Nbr[i]; Nbr[i] = Cy & 0x7FFFFFFF; } } // TODO This may be the problem in in-out-conversion31 of negative inputs void Convert31To32Bits(int[] nbr31, long[] nbr32) { int i, j, k; i = 0; for (j = -1; j < NumberLength; j++) { k = i % 31; if (k == 0) { j++; } if (j == NumberLength) { break; } if (j == NumberLength - 1) { nbr32[i] = nbr31[j] >> k; } else { nbr32[i] = ((nbr31[j] >> k) | (nbr31[j + 1] << (31 - k))) & 0xFFFFFFFFl; } i++; } for (; i < NumberLength; i++) { nbr32[i] = 0; } } private void Convert32To31Bits(long[] nbr32, int[] nbr31, int NumberLength) { int i, j, k; j = 0; for (i = 0; i < NumberLength; i++) { k = i & 0x0000001F; // k = i % 32 if (k == 0) { nbr31[i] = (int)(nbr32[j] & 0x7FFFFFFF); } else { nbr31[i] = (int)(((nbr32[j] >> (32-k)) | (nbr32[j+1] << k)) & 0x7FFFFFFF); j++; } } } /** * Gcd calculation: * <ul> * <li>Step 1: Set k<-0, and then repeatedly set k<-k+1, u<-u/2, v<-v/2 zero or more times until u and v are not * both even.</li> * <li>Step 2: If u is odd, set t<-(-v) and go to step 4. Otherwise set t<-u.</li> * <li>Step 3: Set t<-t/2</li> * <li>Step 4: If t is even, go back to step 3.</li> * <li>Step 5: If t>0, set u<-t, otherwise set v<-(-t).</li> * <li>Step 6: Set t<-u-v. If t!=0, go back to step 3.</li> * <li>Step 7: The GCD is u*2^k.</li> * </ul> * * @param Nbr1 * @param Nbr2 * @param Gcd */ void GcdBigNbr(int Nbr1[], int Nbr2[], int Gcd[]) { int k; int NumberLength = this.NumberLength; System.arraycopy(Nbr1, 0, CalcAuxGcdU, 0, NumberLength); System.arraycopy(Nbr2, 0, CalcAuxGcdV, 0, NumberLength); if (BigNbrIsZero(CalcAuxGcdU)) { System.arraycopy(CalcAuxGcdV, 0, Gcd, 0, NumberLength); return; } if (BigNbrIsZero(CalcAuxGcdV)) { System.arraycopy(CalcAuxGcdU, 0, Gcd, 0, NumberLength); return; } if (CalcAuxGcdU[NumberLength - 1] >= 0x40000000L) { ChSignBigNbr(CalcAuxGcdU); } if (CalcAuxGcdV[NumberLength - 1] >= 0x40000000L) { ChSignBigNbr(CalcAuxGcdV); } k = 0; while ((CalcAuxGcdU[0] & 1) == 0 && (CalcAuxGcdV[0] & 1) == 0) { // Step 1 k++; DivBigNbrByLong(CalcAuxGcdU, 2, CalcAuxGcdU); DivBigNbrByLong(CalcAuxGcdV, 2, CalcAuxGcdV); } if ((CalcAuxGcdU[0] & 1) == 1) { // Step 2 System.arraycopy(CalcAuxGcdV, 0, CalcAuxGcdT, 0, NumberLength); ChSignBigNbr(CalcAuxGcdT); } else { System.arraycopy(CalcAuxGcdU, 0, CalcAuxGcdT, 0, NumberLength); } do { while ((CalcAuxGcdT[0] & 1) == 0) { // Step 4 DivBigNbrByLong(CalcAuxGcdT, 2, CalcAuxGcdT); // Step 3 } if (CalcAuxGcdT[NumberLength - 1] < 0x40000000l) { // Step 5 System.arraycopy(CalcAuxGcdT, 0, CalcAuxGcdU, 0, NumberLength); } else { System.arraycopy(CalcAuxGcdT, 0, CalcAuxGcdV, 0, NumberLength); ChSignBigNbr(CalcAuxGcdV); } SubtractBigNbr(CalcAuxGcdU, CalcAuxGcdV, CalcAuxGcdT); // Step 6 } while (!BigNbrIsZero(CalcAuxGcdT)); System.arraycopy(CalcAuxGcdU, 0, Gcd, 0, NumberLength); // Step 7 while (k > 0) { AddBigNbr(Gcd, Gcd, Gcd); k--; } } /** * <p>Find the multiplicative inverse (1/a) modulo b and return the result in inv.</p> * * <p>This implementation returns dummy values for a-values that are not invertible modulo b. * This is good for the performance of the ECM implementation because the latter works with * accumulated gcd's and then it doesn't matter if a few of the single gcd-values are wrong.</p> * * @param a * @param b * @param inv the result */ void ModInvBigNbr(int[] a, int[] b, int[] inv) { int i; int NumberLength = this.NumberLength; int Dif, E; int st1, st2; long Yaa, Yab; // 2^E * A' = Yaa A + Yab B long Yba, Ybb; // 2^E * B' = Yba A + Ybb B long Ygb0; // 2^E * Mu' = Yaa Mu + Yab Gamma + Ymb0 B0 long Ymb0; // 2^E * Gamma' = Yba Mu + Ybb Gamma + Ygb0 B0 int Iaa, Iab, Iba, Ibb; long Tmp1, Tmp2, Tmp3, Tmp4, Tmp5; int B0l; int invB0l; int Al, Bl, T1, Gl, Ml; long carry1, carry2, carry3, carry4; int Yaah, Yabh, Ybah, Ybbh; int Ymb0h, Ygb0h; long Pr1, Pr2, Pr3, Pr4, Pr5, Pr6, Pr7; long[] B = this.biTmp; long[] CalcAuxModInvA = this.CalcAuxModInvA; long[] CalcAuxModInvB = this.CalcAuxModInvB; long[] CalcAuxModInvMu = this.CalcAuxModInvMu; long[] CalcAuxModInvGamma = this.CalcAuxModInvGamma; Convert31To32Bits(a, CalcAuxModInvA); Convert31To32Bits(b, CalcAuxModInvB); System.arraycopy(CalcAuxModInvB, 0, B, 0, NumberLength); B0l = (int)B[0]; invB0l = B0l; // 2 least significant bits of inverse correct. invB0l = invB0l * (2 - B0l * invB0l); // 4 LSB of inverse correct. invB0l = invB0l * (2 - B0l * invB0l); // 8 LSB of inverse correct. invB0l = invB0l * (2 - B0l * invB0l); // 16 LSB of inverse correct. invB0l = invB0l * (2 - B0l * invB0l); // 32 LSB of inverse correct. for (i = NumberLength - 1; i >= 0; i--) { CalcAuxModInvGamma[i] = 0; CalcAuxModInvMu[i] = 0; } CalcAuxModInvMu[0] = 1; Dif = 0; outer_loop: do { Iaa = Ibb = 1; Iab = Iba = 0; Al = (int) CalcAuxModInvA[0]; Bl = (int) CalcAuxModInvB[0]; E = 0; if (Bl == 0) { for (i = NumberLength - 1; i >= 0; i--) { if (CalcAuxModInvB[i] != 0) break; } if (i < 0) break; // Go out of loop if CalcAuxModInvB = 0 } do { T1 = 0; while ((Bl & 1) == 0) { if (E == 31) { Yaa = Iaa; Yab = Iab; Yba = Iba; Ybb = Ibb; Gl = (int) CalcAuxModInvGamma[0]; Ml = (int) CalcAuxModInvMu[0]; Dif++; T1++; Yaa <<= T1; Yab <<= T1; Ymb0 = (- (int) Yaa * Ml - (int) Yab * Gl) * invB0l; Ygb0 = (-Iba * Ml - Ibb * Gl) * invB0l; carry1 = carry2 = carry3 = carry4 = 0; Yaah = (int) (Yaa >> 32); Yabh = (int) (Yab >> 32); Ybah = (int) (Yba >> 32); Ybbh = (int) (Ybb >> 32); Ymb0h = (int) (Ymb0 >> 32); Ygb0h = (int) (Ygb0 >> 32); Yaa &= 0xFFFFFFFFL; Yab &= 0xFFFFFFFFL; Yba &= 0xFFFFFFFFL; Ybb &= 0xFFFFFFFFL; Ymb0 &= 0xFFFFFFFFL; Ygb0 &= 0xFFFFFFFFL; st1 = Yaah * 6 + Yabh * 2 + Ymb0h; st2 = Ybah * 6 + Ybbh * 2 + Ygb0h; for (i = 0; i < NumberLength; i++) { Pr1 = Yaa * (Tmp1 = CalcAuxModInvMu[i]); Pr2 = Yab * (Tmp2 = CalcAuxModInvGamma[i]); Pr3 = Ymb0 * (Tmp3 = B[i]); Pr4 = (Pr1 & 0xFFFFFFFFL) + (Pr2 & 0xFFFFFFFFL) + (Pr3 & 0xFFFFFFFFL) + carry3; Pr5 = Yaa * (Tmp4 = CalcAuxModInvA[i]); Pr6 = Yab * (Tmp5 = CalcAuxModInvB[i]); Pr7 = (Pr5 & 0xFFFFFFFFL) + (Pr6 & 0xFFFFFFFFL) + carry1; switch (st1) { case -9 : carry3 = -Tmp1 - Tmp2 - Tmp3; carry1 = -Tmp4 - Tmp5; break; case -8 : carry3 = -Tmp1 - Tmp2; carry1 = -Tmp4 - Tmp5; break; case -7 : carry3 = -Tmp1 - Tmp3; carry1 = -Tmp4; break; case -6 : carry3 = -Tmp1; carry1 = -Tmp4; break; case -5 : carry3 = -Tmp1 + Tmp2 - Tmp3; carry1 = -Tmp4 + Tmp5; break; case -4 : carry3 = -Tmp1 + Tmp2; carry1 = -Tmp4 + Tmp5; break; case -3 : carry3 = -Tmp2 - Tmp3; carry1 = -Tmp5; break; case -2 : carry3 = -Tmp2; carry1 = -Tmp5; break; case -1 : carry3 = -Tmp3; carry1 = 0; break; case 0 : carry3 = 0; carry1 = 0; break; case 1 : carry3 = Tmp2 - Tmp3; carry1 = Tmp5; break; case 2 : carry3 = Tmp2; carry1 = Tmp5; break; case 3 : carry3 = Tmp1 - Tmp2 - Tmp3; carry1 = Tmp4 - Tmp5; break; case 4 : carry3 = Tmp1 - Tmp2; carry1 = Tmp4 - Tmp5; break; case 5 : carry3 = Tmp1 - Tmp3; carry1 = Tmp4; break; case 6 : carry3 = Tmp1; carry1 = Tmp4; break; case 7 : carry3 = Tmp1 + Tmp2 - Tmp3; carry1 = Tmp4 + Tmp5; break; case 8 : carry3 = Tmp1 + Tmp2; carry1 = Tmp4 + Tmp5; break; } carry3 += (Pr1 >>> 32) + (Pr2 >>> 32) + (Pr3 >>> 32) + (Pr4 >> 32); carry1 += (Pr5 >>> 32) + (Pr6 >>> 32) + (Pr7 >> 32); if (i > 0) { CalcAuxModInvMu[i - 1] = Pr4 & 0xFFFFFFFFL; CalcAuxModInvA[i - 1] = Pr7 & 0xFFFFFFFFL; } Pr1 = Yba * Tmp1; Pr2 = Ybb * Tmp2; Pr3 = Ygb0 * Tmp3; Pr4 = (Pr1 & 0xFFFFFFFFL) + (Pr2 & 0xFFFFFFFFL) + (Pr3 & 0xFFFFFFFFL) + carry4; Pr5 = Yba * Tmp4; Pr6 = Ybb * Tmp5; Pr7 = (Pr5 & 0xFFFFFFFFL) + (Pr6 & 0xFFFFFFFFL) + carry2; switch (st2) { case -9 : carry4 = -Tmp1 - Tmp2 - Tmp3; carry2 = -Tmp4 - Tmp5; break; case -8 : carry4 = -Tmp1 - Tmp2; carry2 = -Tmp4 - Tmp5; break; case -7 : carry4 = -Tmp1 - Tmp3; carry2 = -Tmp4; break; case -6 : carry4 = -Tmp1; carry2 = -Tmp4; break; case -5 : carry4 = -Tmp1 + Tmp2 - Tmp3; carry2 = -Tmp4 + Tmp5; break; case -4 : carry4 = -Tmp1 + Tmp2; carry2 = -Tmp4 + Tmp5; break; case -3 : carry4 = -Tmp2 - Tmp3; carry2 = -Tmp5; break; case -2 : carry4 = -Tmp2; carry2 = -Tmp5; break; case -1 : carry4 = -Tmp3; carry2 = 0; break; case 0 : carry4 = 0; carry2 = 0; break; case 1 : carry4 = Tmp2 - Tmp3; carry2 = Tmp5; break; case 2 : carry4 = Tmp2; carry2 = Tmp5; break; case 3 : carry4 = Tmp1 - Tmp2 - Tmp3; carry2 = Tmp4 - Tmp5; break; case 4 : carry4 = Tmp1 - Tmp2; carry2 = Tmp4 - Tmp5; break; case 5 : carry4 = Tmp1 - Tmp3; carry2 = Tmp4; break; case 6 : carry4 = Tmp1; carry2 = Tmp4; break; case 7 : carry4 = Tmp1 + Tmp2 - Tmp3; carry2 = Tmp4 + Tmp5; break; case 8 : carry4 = Tmp1 + Tmp2; carry2 = Tmp4 + Tmp5; break; } carry4 += (Pr1 >>> 32) + (Pr2 >>> 32) + (Pr3 >>> 32) + (Pr4 >> 32); carry2 += (Pr5 >>> 32) + (Pr6 >>> 32) + (Pr7 >> 32); if (i > 0) { CalcAuxModInvGamma[i - 1] = Pr4 & 0xFFFFFFFFL; CalcAuxModInvB[i - 1] = Pr7 & 0xFFFFFFFFL; } } if ((int) CalcAuxModInvA[i - 1] < 0) { carry1 -= Yaa; carry2 -= Yba; } if ((int) CalcAuxModInvB[i - 1] < 0) { carry1 -= Yab; carry2 -= Ybb; } if ((int) CalcAuxModInvMu[i - 1] < 0) { carry3 -= Yaa; carry4 -= Yba; } if ((int) CalcAuxModInvGamma[i - 1] < 0) { carry3 -= Yab; carry4 -= Ybb; } CalcAuxModInvA[i - 1] = carry1 & 0xFFFFFFFFL; CalcAuxModInvB[i - 1] = carry2 & 0xFFFFFFFFL; CalcAuxModInvMu[i - 1] = carry3 & 0xFFFFFFFFL; CalcAuxModInvGamma[i - 1] = carry4 & 0xFFFFFFFFL; continue outer_loop; } Bl >>= 1; Dif++; E++; T1++; }; /* end while */ Iaa <<= T1; Iab <<= T1; if (Dif >= 0) { Dif = -Dif; if (((Al + Bl) & 3) == 0) { T1 = Iba; Iba += Iaa; Iaa = T1; T1 = Ibb; Ibb += Iab; Iab = T1; T1 = Bl; Bl += Al; Al = T1; } else { T1 = Iba; Iba -= Iaa; Iaa = T1; T1 = Ibb; Ibb -= Iab; Iab = T1; T1 = Bl; Bl -= Al; Al = T1; } } else { if (((Al + Bl) & 3) == 0) { Iba += Iaa; Ibb += Iab; Bl += Al; } else { Iba -= Iaa; Ibb -= Iab; Bl -= Al; } } Dif--; } while (true); } while (true); if (CalcAuxModInvA[0] != 1) { SubtractBigNbr32(B, CalcAuxModInvMu, CalcAuxModInvMu); } if ((int) CalcAuxModInvMu[i = NumberLength - 1] < 0) { AddBigNbr32(B, CalcAuxModInvMu, CalcAuxModInvMu); } for (; i >= 0; i--) { if (B[i] != CalcAuxModInvMu[i]) break; } if (i < 0 || B[i] < CalcAuxModInvMu[i]) { // If B < Mu SubtractBigNbr32(CalcAuxModInvMu, B, CalcAuxModInvMu); // Mu <- Mu - B } Convert32To31Bits(CalcAuxModInvMu, inv, NumberLength); } BigInteger BigIntToBigNbr(int[] nbr) { int[] nbrCopy = new int[NumberLength+1]; System.arraycopy(nbr, 0, nbrCopy, 0, NumberLength); boolean chSign = false; if (nbr[NumberLength-1] >= 0x40000000L) { ChSignBigNbr(nbrCopy); chSign = true; } long[] Temp = new long[NumberLength]; Convert31To32Bits(nbrCopy, Temp); BigInteger result = BigIntToBigNbr(Temp); if (chSign) { result = result.negate(); } return result; } // This method used to fail for negative inputs with nbr[NumberLength-1]==0. // This has been fixed with a quite ugly workaround. // TODO look for a more elegant solution BigInteger BigIntToBigNbr(long[] nbr) { int trueNumberLength = NumberLength; for (int i = NumberLength-1; i>=0; i--) { if (nbr[i] != 0) break; trueNumberLength--; } boolean isNegative = trueNumberLength>0 && nbr[trueNumberLength-1]<0; if (isNegative) { int NL = trueNumberLength*4; byte[] NBytes = new byte[NL]; for (int i = 0; i < trueNumberLength; i++) { final long digit = nbr[i]; NBytes[NL - 1 - 4 * i] = (byte) (digit & 0xFF); NBytes[NL - 2 - 4 * i] = (byte) ((digit >> 8) & 0xFF); NBytes[NL - 3 - 4 * i] = (byte) ((digit >> 16) & 0xFF); NBytes[NL - 4 - 4 * i] = (byte) ((digit >> 24) & 0xFF); } // remove leading 0-bytes: int zeroCount = 0; for ( ; zeroCount<NL; zeroCount++) { if (NBytes[zeroCount] != 0) break; } if (zeroCount == 0) return new BigInteger(NBytes); int NL2 = NL - zeroCount; if (NL2 == 0) return BigInteger.valueOf(0); byte[] NBytes2 = new byte[NL2]; System.arraycopy(NBytes, zeroCount, NBytes2, 0, NL2); return new BigInteger(NBytes2); } // Old implementation for positive numbers int NL = NumberLength*4; byte[] NBytes = new byte[NL]; for (int i = 0; i < NumberLength /*trueNumberLength*/; i++) { // XXX trueNumberLength works as well ? final long digit = nbr[i]; NBytes[NL - 1 - 4 * i] = (byte) (digit & 0xFF); NBytes[NL - 2 - 4 * i] = (byte) ((digit >> 8) & 0xFF); NBytes[NL - 3 - 4 * i] = (byte) ((digit >> 16) & 0xFF); NBytes[NL - 4 - 4 * i] = (byte) ((digit >> 24) & 0xFF); } return new BigInteger(NBytes); } /** * Converts a BigNbr in 31-bit representation into a String. * @param Nbr * @return decimal string representation of Nbr */ String BigNbrToString(int Nbr[]) { return this.BigIntToBigNbr(Nbr).toString(); } /** * Converts a BigNbr in 32-bit representation into a String. * @param Nbr * @return decimal string representation of Nbr */ String BigNbrToString(long Nbr[]) { return this.BigIntToBigNbr(Nbr).toString(); } public static void main(String[] args) { ConfigUtil.initProject(); BigInteger[] testNums = new BigInteger[] { // easy for ECM new BigInteger("8225267468394993133669189614204532935183709603155231863020477010700542265332938919716662623"), // incomplete result, some unfactored rest is returned new BigInteger("101546450935661953908994991437690198927080333663460351836152986526126114727314353555755712261904130976988029406423152881932996637460315302992884162068350429"), // incomplete result, the factor map contains a composite new BigInteger("1593332576170570774181606244493046197050984933692181475920784855223341"), // = 17 * 1210508704285703 * 2568160569265616473 * 30148619026320753545829271787156467 // but ECM fails to factor 3108780723099354807613175415185519 = 1210508704285703 * 2568160569265616473 // with the maximum number of curves // very hard for ECM, better suited for SIQS //new BigInteger("1794577685365897117833870712928656282041295031283603412289229185967719140138841093599") // = 42181796536350966453737572957846241893933 * 42543889372264778301966140913837516662044603 }; EllipticCurveMethod ecm = new EllipticCurveMethod(); long t0, t1; t0 = System.currentTimeMillis(); for (BigInteger N : testNums) { SortedMap<BigInteger, Integer> primeFactors = new TreeMap<>(); SortedMap<BigInteger, Integer> unfactoredComposites = ecm.factorize(N, primeFactors); if (!unfactoredComposites.isEmpty()) { LOG.debug("N = " + N + " = " + primeFactors + " * " + unfactoredComposites); } else { LOG.debug("N = " + N + " = " + primeFactors); } } t1 = System.currentTimeMillis(); LOG.info("Test suite took " + (t1-t0) + "ms"); } }