PageRenderTime 229ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/symja_android_library/matheclipse-gpl/src/main/java/de/tilman_neumann/jml/factor/cfrac/CFrac63.java

https://bitbucket.org/axelclk/symja_android_library
Java | 378 lines | 223 code | 34 blank | 121 comment | 36 complexity | b98caa56fbfc8f770dce8ddc9f1511a4 MD5 | raw file
  1. /*
  2. * java-math-library is a Java library focused on number theory, but not necessarily limited to it. It is based on the PSIQS 4.0 factoring project.
  3. * Copyright (C) 2018 Tilman Neumann (www.tilman-neumann.de)
  4. *
  5. * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  9. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  10. *
  11. * You should have received a copy of the GNU General Public License along with this program;
  12. * if not, see <http://www.gnu.org/licenses/>.
  13. */
  14. package de.tilman_neumann.jml.factor.cfrac;
  15. import java.math.BigInteger;
  16. import java.util.HashSet;
  17. import java.util.Iterator;
  18. import java.util.TreeMap;
  19. import org.apache.log4j.Logger;
  20. import de.tilman_neumann.jml.factor.FactorAlgorithm;
  21. import de.tilman_neumann.jml.factor.FactorException;
  22. import de.tilman_neumann.jml.factor.base.PrimeBaseGenerator;
  23. import de.tilman_neumann.jml.factor.base.congruence.AQPair;
  24. import de.tilman_neumann.jml.factor.base.congruence.CongruenceCollector;
  25. import de.tilman_neumann.jml.factor.base.matrixSolver.FactorTest;
  26. import de.tilman_neumann.jml.factor.base.matrixSolver.FactorTest01;
  27. import de.tilman_neumann.jml.factor.base.matrixSolver.MatrixSolver;
  28. import de.tilman_neumann.jml.factor.cfrac.tdiv.TDiv_CF63;
  29. import static de.tilman_neumann.jml.base.BigIntConstants.*;
  30. import static de.tilman_neumann.jml.factor.base.GlobalFactoringOptions.ANALYZE;
  31. /**
  32. * 63 bit CFrac with Knuth-Schroeppel multiplier.
  33. *
  34. * @author Tilman Neumann
  35. */
  36. public class CFrac63 extends FactorAlgorithm {
  37. private static final Logger LOG = Logger.getLogger(CFrac63.class);
  38. private static final boolean DEBUG = false;
  39. // input
  40. private BigInteger N, kN;
  41. private long floor_sqrt_kN;
  42. /**
  43. * Test all Q or only those Q_i+1 with odd i ? "All Q" boosts performance for N > 45 bits
  44. * approximately
  45. */
  46. private boolean use_all_i; //
  47. // maximum number of SquFoF iterations for a single k
  48. private int stopRoot;
  49. private float stopMult;
  50. private long maxI;
  51. // multiplier
  52. private int ks_adjust;
  53. private KnuthSchroeppel_CFrac ks = new KnuthSchroeppel_CFrac();
  54. // prime base
  55. private float C;
  56. private int primeBaseSize;
  57. private PrimeBaseGenerator primeBaseBuilder = new PrimeBaseGenerator();
  58. private int[] primesArray;
  59. /**
  60. * The union of all reduced prime bases -- this is the number of variables in the equation system
  61. */
  62. private HashSet<Integer> combinedPrimesSet;
  63. // factorizer for Q
  64. private TDiv_CF63 auxFactorizer;
  65. private float maxQRestExponent;
  66. // collects the congruences we find
  67. private CongruenceCollector congruenceCollector;
  68. // the number of congruences we need to find before we try to solve the smooth congruence equation
  69. // system
  70. private int requiredSmoothCongruenceCount;
  71. // extra congruences to have a bigger chance that the equation system solves. the likelihood is >=
  72. // 1-2^(extraCongruences+1)
  73. private int extraCongruences;
  74. /** The solver used for smooth congruence equation systems. */
  75. private MatrixSolver matrixSolver;
  76. // time capturing
  77. private long startTime, linAlgStartTime;
  78. /**
  79. * Standard constructor.
  80. *
  81. * @param use_all_i
  82. * @param stopRoot order of the root to compute the maximum number of iterations
  83. * @param stopMult multiplier to compute the maximum number of iterations
  84. * @param C multiplier for prime base size
  85. * @param maxQRestExponent
  86. * @param auxFactorizer the algorithm to find smooth Q
  87. * @param extraCongruences the number of surplus congruences we collect to have a greater chance
  88. * that the equation system solves.
  89. * @param matrixSolver matrix solver for the smooth congruence equation system
  90. * @param ks_adjust
  91. */
  92. public CFrac63(
  93. boolean use_all_i,
  94. int stopRoot,
  95. float stopMult,
  96. float C,
  97. float maxQRestExponent,
  98. TDiv_CF63 auxFactorizer,
  99. int extraCongruences,
  100. MatrixSolver matrixSolver,
  101. int ks_adjust) {
  102. this.use_all_i = use_all_i;
  103. this.stopRoot = stopRoot;
  104. this.stopMult = stopMult;
  105. this.C = C;
  106. this.maxQRestExponent = maxQRestExponent;
  107. this.auxFactorizer = auxFactorizer;
  108. this.congruenceCollector = new CongruenceCollector();
  109. this.extraCongruences = extraCongruences;
  110. this.matrixSolver = matrixSolver;
  111. this.ks_adjust = ks_adjust;
  112. }
  113. @Override
  114. public String getName() {
  115. return "CFrac63(all_i="
  116. + use_all_i
  117. + ", ks_adjust="
  118. + ks_adjust
  119. + ", stop=("
  120. + stopRoot
  121. + ", "
  122. + stopMult
  123. + "), C="
  124. + C
  125. + ", maxSuSmoothExp="
  126. + maxQRestExponent
  127. + ", "
  128. + auxFactorizer.getName()
  129. + ")";
  130. }
  131. /**
  132. * Test the current N.
  133. *
  134. * @return factor, or null if no factor was found.
  135. */
  136. public BigInteger findSingleFactor(BigInteger N) {
  137. if (ANALYZE) startTime = System.currentTimeMillis();
  138. this.N = N;
  139. // compute prime base size
  140. double N_dbl = N.doubleValue();
  141. double lnN = Math.log(N_dbl);
  142. double lnlnN = Math.log(lnN);
  143. double lnNPow = 0.666667; // heuristics for CFrac
  144. // we want that the exponents of lnN and lnlnN sum to 1
  145. this.primeBaseSize =
  146. 25 + (int) (Math.exp(Math.pow(lnN, lnNPow) * Math.pow(lnlnN, 1 - lnNPow) * C));
  147. if (DEBUG) LOG.debug("N = " + N + ": primeBaseSize = " + primeBaseSize);
  148. this.primesArray = new int[primeBaseSize];
  149. // compute the biggest unfactored rest where some Q is still considered "sufficiently smooth".
  150. double maxQRest = Math.pow(N_dbl, maxQRestExponent);
  151. // initialize sub-algorithms for N
  152. this.auxFactorizer.initialize(N, maxQRest);
  153. FactorTest factorTest = new FactorTest01(N);
  154. this.congruenceCollector.initialize(N, factorTest);
  155. this.combinedPrimesSet = new HashSet<Integer>();
  156. this.matrixSolver.initialize(N, factorTest);
  157. // max iterations: there is no need to account for k, because expansions of smooth kN are
  158. // typically not longer than those for N.
  159. // long maxI is sufficient to hold any practicable number of iteration steps (~9.22*10^18);
  160. // stopRoot must be chosen appropriately such that there is no overflow of long values.
  161. // with stopRoot=5, the overflow of longs starts at N~6.67 * 10^94...
  162. this.maxI = (long) (stopMult * Math.pow(N_dbl, 1.0 / stopRoot));
  163. // compute multiplier k: though k=1 is better than Knuth-Schroeppel for N<47 bits,
  164. // we can ignore that here because that is far out of the optimal CFrac range
  165. TreeMap<Double, Integer> kMap = ks.computeMultiplier(N, ks_adjust);
  166. Iterator<Integer> kIter = kMap.values().iterator();
  167. while (kIter.hasNext()) {
  168. // get a new k, return immediately if kN is square
  169. this.kN = BigInteger.valueOf(kIter.next()).multiply(N);
  170. floor_sqrt_kN =
  171. (long)
  172. Math.sqrt(
  173. kN
  174. .doubleValue()); // faster than BigInteger sqrt; but the type conversion may
  175. // give ceil(sqrt(kN)) for some N >= 54 bit
  176. BigInteger sqrt_kN_big = BigInteger.valueOf(floor_sqrt_kN);
  177. long diff = kN.subtract(sqrt_kN_big.multiply(sqrt_kN_big)).longValue();
  178. if (diff == 0) return N.gcd(sqrt_kN_big);
  179. if (diff < 0) {
  180. // floor_sqrt_kN was too big, diff too small -> compute correction
  181. floor_sqrt_kN--;
  182. diff += (floor_sqrt_kN << 1) + 1;
  183. }
  184. // Create the reduced prime base for kN:
  185. primeBaseBuilder.computeReducedPrimeBase(kN, primeBaseSize, primesArray);
  186. // add new reduced prime base to the combined prime base
  187. for (int i = 0; i < primeBaseSize; i++) combinedPrimesSet.add(primesArray[i]);
  188. // we want: #equations = #variables + some extra congruences
  189. this.requiredSmoothCongruenceCount = combinedPrimesSet.size() + extraCongruences;
  190. try {
  191. // initialize the Q-factorizer with new prime base
  192. this.auxFactorizer.initialize(kN, primeBaseSize, primesArray); // may throw FactorException
  193. // search square Q_i
  194. test(diff);
  195. } catch (FactorException fe) {
  196. if (ANALYZE) {
  197. long endTime = System.currentTimeMillis();
  198. LOG.info(
  199. "Found factor of N="
  200. + N
  201. + " in "
  202. + (endTime - startTime)
  203. + "ms (LinAlgPhase took "
  204. + (endTime - linAlgStartTime)
  205. + "ms)");
  206. }
  207. return fe.getFactor();
  208. }
  209. }
  210. return I_1; // fail, too few Knuth-Schroeppel multipliers
  211. }
  212. protected void test(long Q_ip1) throws FactorException {
  213. // initialization for first iteration step
  214. long i = 0;
  215. BigInteger A_im2 = null;
  216. BigInteger A_im1 = I_1;
  217. BigInteger A_i = BigInteger.valueOf(floor_sqrt_kN);
  218. long P_im1 = 1;
  219. long P_i = floor_sqrt_kN;
  220. long Q_i = 1;
  221. // first iteration step
  222. long two_floor_sqrt_kN = floor_sqrt_kN << 1;
  223. while (true) {
  224. if (DEBUG) verifyCongruence(i, A_i, BigInteger.valueOf(Q_ip1));
  225. // [McMath 2004] points out (on SquFoF) that we have to look for square Q_i at some even i.
  226. // Here I test Q_i+1, so I have to look for square Q_i+1 at odd i.
  227. // In CFRAC, square congruences are also tested in the CongruenceCollector,
  228. // but doing it here before trial division is good for performance.
  229. boolean isSquare = false;
  230. if (i % 2 == 1) {
  231. long Q_ip1_sqrt = (long) Math.sqrt(Q_ip1);
  232. if (Q_ip1_sqrt * Q_ip1_sqrt == Q_ip1) {
  233. // Q_i+1 is square -> test gcd
  234. BigInteger gcd = N.gcd(A_i.subtract(BigInteger.valueOf(Q_ip1_sqrt)));
  235. if (gcd.compareTo(I_1) > 0 && gcd.compareTo(N) < 0) throw new FactorException(gcd);
  236. isSquare = true;
  237. }
  238. }
  239. if (isSquare == false && (use_all_i || i % 2 == 1)) {
  240. // Q_i+1 is not square and the i is right, too -> check for smooth relations.
  241. // Here a constraint on the size of Q would mean a severe performance penalty!
  242. long Q_test = i % 2 == 1 ? Q_ip1 : -Q_ip1; // make Q congruent A^2
  243. AQPair aqPair = auxFactorizer.test(A_i, Q_test);
  244. if (DEBUG) LOG.debug("N = " + N + ": Q_test = " + Q_test + " -> aqPair = " + aqPair);
  245. if (aqPair != null) {
  246. // the Q was sufficiently smooth
  247. if (ANALYZE)
  248. linAlgStartTime = System.currentTimeMillis(); // just in case it really starts
  249. boolean addedSmooth = congruenceCollector.add(aqPair);
  250. if (addedSmooth) {
  251. int smoothCongruenceCount = congruenceCollector.getSmoothCongruenceCount();
  252. if (smoothCongruenceCount >= requiredSmoothCongruenceCount) {
  253. // try to solve equation system
  254. // LOG.debug("#smooths = " + smoothCongruenceCount + ", #requiredSmooths = " +
  255. // requiredSmoothCongruenceCount);
  256. matrixSolver.solve(
  257. congruenceCollector.getSmoothCongruences()); // throws FactorException
  258. // no factor exception -> extend equation system and continue searching smooth
  259. // congruences
  260. requiredSmoothCongruenceCount += extraCongruences;
  261. }
  262. }
  263. }
  264. }
  265. // exit loop ?
  266. if (++i == maxI) return;
  267. // keep values from last round
  268. A_im2 = A_im1;
  269. A_im1 = A_i;
  270. P_im1 = P_i;
  271. long Q_im1 = Q_i;
  272. Q_i = Q_ip1;
  273. // compute next values
  274. long b_i = (floor_sqrt_kN + P_im1) / Q_i; // floor(rational result)
  275. P_i = b_i * Q_i - P_im1;
  276. Q_ip1 = Q_im1 + b_i * (P_im1 - P_i);
  277. // carry along A_i % N from continuant recurrence
  278. A_i = addModN(mulModN(b_i, A_im1), A_im2);
  279. // stop when continuant period is complete
  280. if (b_i == two_floor_sqrt_kN) return;
  281. }
  282. }
  283. /**
  284. * Verify congruence A_i^2 == (-1)^(i+1)*Q_i+1 (mod N)
  285. *
  286. * @param i
  287. * @param A_i
  288. * @param Q_ip1
  289. */
  290. private void verifyCongruence(long i, BigInteger A_i, BigInteger Q_ip1) {
  291. // assertTrue(Q_ip1.signum()>=0);
  292. // verify congruence A^2 == Q (mod N)
  293. BigInteger Q_test = i % 2 == 1 ? Q_ip1 : Q_ip1.negate().mod(N);
  294. BigInteger div[] = A_i.pow(2).subtract(Q_test).divideAndRemainder(N);
  295. // assertEquals(I_0, div[1]); // works
  296. LOG.debug("A^2-Q = " + div[0] + " * N");
  297. LOG.debug("A^2 % N = " + A_i.pow(2).mod(N) + ", Q = " + Q_test);
  298. // assertEquals(Q_test, A_i.pow(2).mod(N)); // works
  299. }
  300. /**
  301. * Addition modulo N, with <code>a, b < N</code>.
  302. *
  303. * @param a
  304. * @param b
  305. * @return (a+b) mod N
  306. */
  307. private BigInteger addModN(BigInteger a, BigInteger b) {
  308. BigInteger sum = a.add(b);
  309. return sum.compareTo(N) < 0 ? sum : sum.subtract(N);
  310. }
  311. /**
  312. * Multiplication (m*a) modulo N, with m often small and <code>a < N</code>.
  313. *
  314. * @param m
  315. * @param a
  316. * @return (m*a) mod N
  317. */
  318. private BigInteger mulModN(long m, BigInteger a) {
  319. if (m < 4) { // 0, 1, 10, 11
  320. int m_int = (int) m;
  321. switch (m_int) {
  322. case 0:
  323. return I_0;
  324. case 1:
  325. return a;
  326. case 2:
  327. {
  328. BigInteger two_a = a.shiftLeft(1); // faster than 2*a or a+a
  329. return two_a.compareTo(N) < 0 ? two_a : two_a.subtract(N);
  330. }
  331. case 3:
  332. {
  333. BigInteger ma_modN = a.shiftLeft(1).add(a); // < 3*N
  334. if (ma_modN.compareTo(N) < 0) return ma_modN;
  335. ma_modN = ma_modN.subtract(N); // < 2*N
  336. return ma_modN.compareTo(N) < 0 ? ma_modN : ma_modN.subtract(N);
  337. }
  338. }
  339. // adding case 4 does not help because then bitLength() does not fit exactly
  340. }
  341. BigInteger product = a.multiply(BigInteger.valueOf(m));
  342. return product.compareTo(N) < 0 ? product : product.mod(N);
  343. }
  344. }