Skip to content

Commit 6ccffa7

Browse files
committed
fix TinyEcm64*.findSingleFactor() for composites with >=2 small factors
1 parent b0852c1 commit 6ccffa7

File tree

13 files changed

+162
-120
lines changed

13 files changed

+162
-120
lines changed

src/main/java/de/tilman_neumann/jml/factor/CombinedFactorAlgorithm.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class CombinedFactorAlgorithm extends FactorAlgorithm {
6161

6262
private TDiv31Barrett tDiv31 = new TDiv31Barrett();
6363
private HartFast2Mult hart = new HartFast2Mult(true); // for general factor arguments, trial division is needed
64-
private TinyEcm64MHInlined tinyEcm = new TinyEcm64MHInlined();
64+
private TinyEcm64MHInlined tinyEcm = new TinyEcm64MHInlined(true); // for general factor arguments, trial division is needed
6565
private PollardRhoBrentMontgomery64MH pollardRhoBrentMontgomery64MH = new PollardRhoBrentMontgomery64MH();
6666
private TDiv tdiv = new TDiv();
6767
private EllipticCurveMethod ecm = new EllipticCurveMethod(0);

src/main/java/de/tilman_neumann/jml/factor/ecm/TinyEcm64.java

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@
5151

5252
/**
5353
* A port of Ben Buhrow's tinyecm.c (https://www.mersenneforum.org/showpost.php?p=521028&postcount=84),
54-
* an ECM implementation for unsigned 64 bit integers.
54+
* an ECM implementation for unsigned 64 bit integers.<br/><br/>
5555
*
56-
* TinyEcm does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.
57-
* This issue has been addressed in the searchFactors() method, but not in findSingleFactor(), which should only be used to find factors of semiprimes
58-
* with not too small factors.
56+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
57+
* If you are not sure about the nature of your test numbers, use doTDivFirst==true.
5958
*
6059
* @author Tilman Neumann
6160
*/
@@ -159,8 +158,6 @@ public ecm_work() {
159158
0, 12, 0, 13, 0, 0, 0, 14, 0, 15,
160159
0, 0, 0, 16, 0, 0, 0, 0, 0, 17,
161160
18, 0 }; // last entry 0 or 1 makes no performance difference
162-
163-
private long LCGSTATE;
164161

165162
private TDiv63 tdiv = new TDiv63();
166163

@@ -170,6 +167,18 @@ public ecm_work() {
170167

171168
private SpRand32 spRand32 = new SpRand32();
172169

170+
private boolean doTDivFirst;
171+
172+
/**
173+
* Standard constructor.<br/><br/>
174+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
175+
*
176+
* @param doTDivFirst
177+
*/
178+
public TinyEcm64(boolean doTDivFirst) {
179+
this.doTDivFirst = doTDivFirst;
180+
}
181+
173182
public String getName() {
174183
return "TinyEcm64";
175184
}
@@ -1095,22 +1104,20 @@ long check_factor(long Z, long n) {
10951104
return (f>1 && f<n) ? f : 0;
10961105
}
10971106

1098-
/**
1099-
* {@inheritDoc}<br/><br/>
1100-
*
1101-
* <strong>WARNING: This implementation does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.</strong>
1102-
* It should only be used when it is clear that this is not the case or after carrying out the required amount of trial division.
1103-
*/
11041107
@Override
11051108
public BigInteger findSingleFactor(BigInteger N) {
1106-
// original rng, not comparable with C version
1107-
//Random rng = new Random();
1108-
//rng.setSeed(42);
1109-
//LCGSTATE = 65537 * rng.nextInt();
1110-
// rng comparable with C version
1111-
LCGSTATE = 4295098403L;
1112-
if (DEBUG) LOG.debug("LCGSTATE = " + LCGSTATE);
1113-
1109+
return findSingleFactor(N, this.doTDivFirst);
1110+
}
1111+
1112+
private BigInteger findSingleFactor(BigInteger N, boolean doTDivFirst) {
1113+
if (doTDivFirst) {
1114+
// Do trial division before ecm.
1115+
// The required amount of trial division has been derived experimentally.
1116+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1117+
final long factor = tdiv.setTestLimit(1<<10).findSingleFactor(N.longValue());
1118+
if (factor > 1) return BigInteger.valueOf(factor);
1119+
}
1120+
11141121
int NBits = N.bitLength();
11151122
if (NBits > MAX_BITS_SUPPORTED) throw new IllegalArgumentException("N=" + N + " has " + NBits + " bit, but tinyEcm only supports arguments <= " + MAX_BITS_SUPPORTED + " bit.");
11161123
// TODO Try to make it work for 63, 64 bit numbers
@@ -1145,23 +1152,30 @@ public BigInteger findSingleFactor(BigInteger N) {
11451152

11461153
@Override
11471154
public void searchFactors(FactorArguments args, FactorResult result) {
1148-
// tinyEcm fails (i.e. runs forever) for some N having >=2 small factors, like 735 = 3*5*7^2.
1149-
// THe required amount of trial division to fix that has been derived experimentally.
1150-
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1151-
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1152-
if (DEBUG) LOG.debug("result after tdiv = " + result);
1153-
1154-
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1155-
// Otherwise we continue
1156-
BigInteger N = result.untestedFactors.firstKey();
1157-
int exp = result.untestedFactors.removeAll(N); // can be > 1
1158-
1159-
if (bpsw.isProbablePrime(N)) {
1160-
result.primeFactors.add(N, exp);
1161-
return;
1155+
BigInteger N;
1156+
int exp;
1157+
if (this.doTDivFirst) {
1158+
// Do trial division before ecm.
1159+
// The required amount of trial division has been derived experimentally.
1160+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1161+
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1162+
if (DEBUG) LOG.debug("result after tdiv = " + result);
1163+
1164+
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1165+
// Otherwise we continue
1166+
N = result.untestedFactors.firstKey();
1167+
exp = result.untestedFactors.removeAll(N); // can be > 1
1168+
1169+
if (bpsw.isProbablePrime(N)) {
1170+
result.primeFactors.add(N, exp);
1171+
return;
1172+
}
1173+
} else {
1174+
N = args.N;
1175+
exp = args.exp;
11621176
}
1163-
1164-
BigInteger factor1 = findSingleFactor(N);
1177+
1178+
BigInteger factor1 = findSingleFactor(N, false);
11651179
if (DEBUG) LOG.debug("result after findSingleFactor() = " + result);
11661180
if (factor1.compareTo(I_1) > 0 && factor1.compareTo(N) < 0) {
11671181
// We found a factor, but here we cannot know if it is prime or composite

src/main/java/de/tilman_neumann/jml/factor/ecm/TinyEcm64MH.java

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@
5151

5252
/**
5353
* A port of Ben Buhrow's tinyecm.c (https://www.mersenneforum.org/showpost.php?p=521028&postcount=84),
54-
* an ECM implementation for unsigned 64 bit integers.
54+
* an ECM implementation for unsigned 64 bit integers.<br/><br/>
5555
*
5656
* In contrast to TinyEcm64, this variant implements montMul64() using _signed_ multiplications of two 64-bit numbers and Math.multiplyHigh() via Uint128.mul64SignedMH().
57-
* Intrinsics support for Math.multiplyHigh() typically requires Java 10 and not too old hardware.
57+
* Intrinsics support for Math.multiplyHigh() typically requires Java 10 and not too old hardware.<br/><br/>
5858
*
59-
* TinyEcm does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.
60-
* This issue has been addressed in the searchFactors() method, but not in findSingleFactor(), which should only be used to find factors of semiprimes
61-
* with not too small factors.
59+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
60+
* If you are not sure about the nature of your test numbers, use doTDivFirst==true.
6261
*
6362
* @author Tilman Neumann
6463
*/
@@ -162,8 +161,6 @@ public ecm_work() {
162161
0, 12, 0, 13, 0, 0, 0, 14, 0, 15,
163162
0, 0, 0, 16, 0, 0, 0, 0, 0, 17,
164163
18, 0 }; // last entry 0 or 1 makes no performance difference
165-
166-
private long LCGSTATE;
167164

168165
private TDiv63 tdiv = new TDiv63();
169166

@@ -173,6 +170,18 @@ public ecm_work() {
173170

174171
private SpRand32 spRand32 = new SpRand32();
175172

173+
private boolean doTDivFirst;
174+
175+
/**
176+
* Standard constructor.<br/><br/>
177+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
178+
*
179+
* @param doTDivFirst
180+
*/
181+
public TinyEcm64MH(boolean doTDivFirst) {
182+
this.doTDivFirst = doTDivFirst;
183+
}
184+
176185
public String getName() {
177186
return "TinyEcm64MH";
178187
}
@@ -1099,21 +1108,19 @@ long check_factor(long Z, long n) {
10991108
return (f>1 && f<n) ? f : 0;
11001109
}
11011110

1102-
/**
1103-
* {@inheritDoc}<br/><br/>
1104-
*
1105-
* <strong>WARNING: This implementation does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.</strong>
1106-
* It should only be used when it is clear that this is not the case or after carrying out the required amount of trial division.
1107-
*/
11081111
@Override
11091112
public BigInteger findSingleFactor(BigInteger N) {
1110-
// original rng, not comparable with C version
1111-
//Random rng = new Random();
1112-
//rng.setSeed(42);
1113-
//LCGSTATE = 65537 * rng.nextInt();
1114-
// rng comparable with C version
1115-
LCGSTATE = 4295098403L;
1116-
if (DEBUG) LOG.debug("LCGSTATE = " + LCGSTATE);
1113+
return findSingleFactor(N, this.doTDivFirst);
1114+
}
1115+
1116+
private BigInteger findSingleFactor(BigInteger N, boolean doTDivFirst) {
1117+
if (doTDivFirst) {
1118+
// Do trial division before ecm.
1119+
// The required amount of trial division has been derived experimentally.
1120+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1121+
final long factor = tdiv.setTestLimit(1<<10).findSingleFactor(N.longValue());
1122+
if (factor > 1) return BigInteger.valueOf(factor);
1123+
}
11171124

11181125
int NBits = N.bitLength();
11191126
if (NBits > MAX_BITS_SUPPORTED) throw new IllegalArgumentException("N=" + N + " has " + NBits + " bit, but tinyEcm only supports arguments <= " + MAX_BITS_SUPPORTED + " bit.");
@@ -1149,23 +1156,30 @@ public BigInteger findSingleFactor(BigInteger N) {
11491156

11501157
@Override
11511158
public void searchFactors(FactorArguments args, FactorResult result) {
1152-
// tinyEcm fails (i.e. runs forever) for some N having >=2 small factors, like 735 = 3*5*7^2.
1153-
// THe required amount of trial division to fix that has been derived experimentally.
1154-
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1155-
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1156-
if (DEBUG) LOG.debug("result after tdiv = " + result);
1157-
1158-
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1159-
// Otherwise we continue
1160-
BigInteger N = result.untestedFactors.firstKey();
1161-
int exp = result.untestedFactors.removeAll(N); // can be > 1
1162-
1163-
if (bpsw.isProbablePrime(N)) {
1164-
result.primeFactors.add(N, exp);
1165-
return;
1159+
BigInteger N;
1160+
int exp;
1161+
if (this.doTDivFirst) {
1162+
// Do trial division before ecm.
1163+
// The required amount of trial division has been derived experimentally.
1164+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1165+
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1166+
if (DEBUG) LOG.debug("result after tdiv = " + result);
1167+
1168+
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1169+
// Otherwise we continue
1170+
N = result.untestedFactors.firstKey();
1171+
exp = result.untestedFactors.removeAll(N); // can be > 1
1172+
1173+
if (bpsw.isProbablePrime(N)) {
1174+
result.primeFactors.add(N, exp);
1175+
return;
1176+
}
1177+
} else {
1178+
N = args.N;
1179+
exp = args.exp;
11661180
}
1167-
1168-
BigInteger factor1 = findSingleFactor(N);
1181+
1182+
BigInteger factor1 = findSingleFactor(N, false);
11691183
if (DEBUG) LOG.debug("result after findSingleFactor() = " + result);
11701184
if (factor1.compareTo(I_1) > 0 && factor1.compareTo(N) < 0) {
11711185
// We found a factor, but here we cannot know if it is prime or composite

src/main/java/de/tilman_neumann/jml/factor/ecm/TinyEcm64MHInlined.java

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@
5151

5252
/**
5353
* A port of Ben Buhrow's tinyecm.c (https://www.mersenneforum.org/showpost.php?p=521028&postcount=84),
54-
* an ECM implementation for unsigned 64 bit integers.
54+
* an ECM implementation for unsigned 64 bit integers.<br/><br/>
5555
*
5656
* This variant implements montMul64() using _signed_ multiplications of two 64-bit numbers and Math.multiplyHigh().
57-
* Intrinsics support for Math.multiplyHigh() typically requires Java 10 and not too old hardware.
57+
* Intrinsics support for Math.multiplyHigh() typically requires Java 10 and not too old hardware.<br/><br/>
5858
*
59-
* TinyEcm does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.
60-
* This issue has been addressed in the searchFactors() method, but not in findSingleFactor(), which should only be used to find factors of semiprimes
61-
* with not too small factors.
59+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
60+
* If you are not sure about the nature of your test numbers, use doTDivFirst==true.
6261
*
6362
* @author Tilman Neumann
6463
*/
@@ -162,8 +161,6 @@ public ecm_work() {
162161
0, 12, 0, 13, 0, 0, 0, 14, 0, 15,
163162
0, 0, 0, 16, 0, 0, 0, 0, 0, 17,
164163
18, 0 }; // last entry 0 or 1 makes no performance difference
165-
166-
private long LCGSTATE;
167164

168165
private TDiv63 tdiv = new TDiv63();
169166

@@ -173,6 +170,18 @@ public ecm_work() {
173170

174171
private SpRand32 spRand32 = new SpRand32();
175172

173+
private boolean doTDivFirst;
174+
175+
/**
176+
* Standard constructor.<br/><br/>
177+
* <strong>WARNING: Without trial division, tinyEcm might fail (i.e. run forever) for some N having >=2 small factors, like 735 = 3*5*7^2.</strong>
178+
*
179+
* @param doTDivFirst
180+
*/
181+
public TinyEcm64MHInlined(boolean doTDivFirst) {
182+
this.doTDivFirst = doTDivFirst;
183+
}
184+
176185
public String getName() {
177186
return "TinyEcm64MHInlined";
178187
}
@@ -1108,21 +1117,19 @@ long check_factor(long Z, long n) {
11081117
return (f>1 && f<n) ? f : 0;
11091118
}
11101119

1111-
/**
1112-
* {@inheritDoc}<br/><br/>
1113-
*
1114-
* <strong>WARNING: This implementation does not work stable (i.e. it would eventually not terminate) for factor arguments having 2 or more factors < 2^9.</strong>
1115-
* It should only be used when it is clear that this is not the case or after carrying out the required amount of trial division.
1116-
*/
11171120
@Override
11181121
public BigInteger findSingleFactor(BigInteger N) {
1119-
// original rng, not comparable with C version
1120-
//Random rng = new Random();
1121-
//rng.setSeed(42);
1122-
//LCGSTATE = 65537 * rng.nextInt();
1123-
// rng comparable with C version
1124-
LCGSTATE = 4295098403L;
1125-
if (DEBUG) LOG.debug("LCGSTATE = " + LCGSTATE);
1122+
return findSingleFactor(N, this.doTDivFirst);
1123+
}
1124+
1125+
private BigInteger findSingleFactor(BigInteger N, boolean doTDivFirst) {
1126+
if (doTDivFirst) {
1127+
// Do trial division before ecm.
1128+
// The required amount of trial division has been derived experimentally.
1129+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1130+
final long factor = tdiv.setTestLimit(1<<10).findSingleFactor(N.longValue());
1131+
if (factor > 1) return BigInteger.valueOf(factor);
1132+
}
11261133

11271134
int NBits = N.bitLength();
11281135
if (NBits > MAX_BITS_SUPPORTED) throw new IllegalArgumentException("N=" + N + " has " + NBits + " bit, but tinyEcm only supports arguments <= " + MAX_BITS_SUPPORTED + " bit.");
@@ -1158,23 +1165,30 @@ public BigInteger findSingleFactor(BigInteger N) {
11581165

11591166
@Override
11601167
public void searchFactors(FactorArguments args, FactorResult result) {
1161-
// tinyEcm fails (i.e. runs forever) for some N having >=2 small factors, like 735 = 3*5*7^2.
1162-
// THe required amount of trial division to fix that has been derived experimentally.
1163-
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1164-
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1165-
if (DEBUG) LOG.debug("result after tdiv = " + result);
1166-
1167-
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1168-
// Otherwise we continue
1169-
BigInteger N = result.untestedFactors.firstKey();
1170-
int exp = result.untestedFactors.removeAll(N); // can be > 1
1171-
1172-
if (bpsw.isProbablePrime(N)) {
1173-
result.primeFactors.add(N, exp);
1174-
return;
1168+
BigInteger N;
1169+
int exp;
1170+
if (this.doTDivFirst) {
1171+
// Do trial division before ecm.
1172+
// The required amount of trial division has been derived experimentally.
1173+
// With a tdiv limit of 2^8 it still fails sometimes; 2^9 is stable; 2^10 is best in terms of performance.
1174+
tdiv.setTestLimit(1<<10).searchFactors(args, result);
1175+
if (DEBUG) LOG.debug("result after tdiv = " + result);
1176+
1177+
if (result.untestedFactors.isEmpty()) return; // N was "easy"
1178+
// Otherwise we continue
1179+
N = result.untestedFactors.firstKey();
1180+
exp = result.untestedFactors.removeAll(N); // can be > 1
1181+
1182+
if (bpsw.isProbablePrime(N)) {
1183+
result.primeFactors.add(N, exp);
1184+
return;
1185+
}
1186+
} else {
1187+
N = args.N;
1188+
exp = args.exp;
11751189
}
1176-
1177-
BigInteger factor1 = findSingleFactor(N);
1190+
1191+
BigInteger factor1 = findSingleFactor(N, false);
11781192
if (DEBUG) LOG.debug("result after findSingleFactor() = " + result);
11791193
if (factor1.compareTo(I_1) > 0 && factor1.compareTo(N) < 0) {
11801194
// We found a factor, but here we cannot know if it is prime or composite

src/main/java/de/tilman_neumann/jml/factor/siqs/tdiv/TDiv_QS_2LP.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public class TDiv_QS_2LP implements TDiv_QS {
9393
private PrPTest prpTest = new PrPTest();
9494

9595
private HartFast2Mult hart = new HartFast2Mult(false);
96-
private TinyEcm64MHInlined tinyEcm = new TinyEcm64MHInlined();
96+
private TinyEcm64MHInlined tinyEcm = new TinyEcm64MHInlined(false);
9797
private PollardRhoBrentMontgomery64MH pollardRhoBrentMontgomery64MH = new PollardRhoBrentMontgomery64MH();
9898
// Nested SIQS is required for quite large N only, > 350 bit ?
9999
private SIQSSmall qsInternal;

0 commit comments

Comments
 (0)