congruence_of_powers_factorization_method.pl 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 04 July 2019
  4. # Edit: 22 March 2022
  5. # https://github.com/trizen
  6. # A simple factorization method, based on congruences of powers.
  7. # Given a composite integer `n`, if we find:
  8. #
  9. # a^k == b^k (mod n)
  10. #
  11. # for some k >= 2, then gcd(a-b, n) may be a non-trivial factor of n.
  12. use 5.020;
  13. use strict;
  14. use warnings;
  15. use Math::GMPz;
  16. use ntheory qw(:all);
  17. use Math::AnyNum qw(ipow);
  18. use experimental qw(signatures);
  19. use constant {
  20. MIN_FACTOR => 1e6, # ignore small factors
  21. LOG_BRANCH => 1, # true to use the log branch in addition to the root branch
  22. FULL_RANGE => 0, # true to use the full range from 0 to log_2(n)
  23. };
  24. sub perfect_power ($n) {
  25. return 1 if ($n == 0);
  26. return 1 if ($n == 1);
  27. return is_power($n);
  28. }
  29. sub cgpow_factor ($n, $verbose = 0) {
  30. my %seen;
  31. if (ref($n) ne 'Math::GMPz') {
  32. $n = Math::GMPz->new("$n");
  33. }
  34. my $f = sub ($r, $e1, $k, $e2) {
  35. my @factors;
  36. my @divs1 = divisors($e1);
  37. my @divs2 = divisors($e2);
  38. foreach my $d1 (@divs1) {
  39. my $x = $r**$d1;
  40. foreach my $d2 (@divs2) {
  41. my $y = $k**$d2;
  42. foreach my $j (-1, 1) {
  43. my $t = $x - $j * $y;
  44. my $g = Math::GMPz->new(gcd($t, $n));
  45. if ($g > MIN_FACTOR and $g < $n and !$seen{$g}++) {
  46. if ($verbose) {
  47. if ($r == $k) {
  48. say "[*] Congruence of powers: a^$d1 == b^$d2 (mod n) -> $g";
  49. }
  50. else {
  51. say "[*] Congruence of powers: $r^$d1 == $k^$d2 (mod n) -> $g";
  52. }
  53. }
  54. push @factors, $g;
  55. }
  56. }
  57. }
  58. }
  59. @factors;
  60. };
  61. my @params;
  62. my $orig = $n;
  63. my $const = 64;
  64. my @range;
  65. if (FULL_RANGE) {
  66. @range = reverse(2 .. logint($n, 2));
  67. }
  68. else {
  69. @range = reverse(2 .. vecmin($const, logint($n, 2)));
  70. }
  71. my $process = sub ($root, $e) {
  72. for my $j (1, 0) {
  73. my $k = $root + $j;
  74. my $u = powmod($k, $e, $n);
  75. foreach my $z ($u, $n - $u) {
  76. if (my $t = perfect_power($z)) {
  77. my $r1 = rootint($z, $t);
  78. ##my $r2 = rootint($z, $e);
  79. push @params, [Math::GMPz->new($r1), $t, Math::GMPz->new($k), $e];
  80. ##push @params, [Math::GMPz->new($r2), $e, Math::GMPz->new($k), $e];
  81. }
  82. }
  83. }
  84. };
  85. for my $e (@range) {
  86. my $root = Math::GMPz->new(rootint($n, $e));
  87. $process->($root, $e);
  88. }
  89. if (LOG_BRANCH) {
  90. for my $root (@range) {
  91. my $e = Math::GMPz->new(logint($n, $root));
  92. $process->($root, $e);
  93. }
  94. my %seen_param;
  95. @params = grep { !$seen_param{join(' ', @$_)}++ } @params;
  96. }
  97. my @divisors;
  98. foreach my $args (@params) {
  99. push @divisors, $f->(@$args);
  100. }
  101. @divisors = sort { $a <=> $b } @divisors;
  102. my @factors;
  103. foreach my $d (@divisors) {
  104. my $g = Math::GMPz->new(gcd($n, $d));
  105. if ($g > MIN_FACTOR and $g < $n) {
  106. while ($n % $g == 0) {
  107. $n /= $g;
  108. push @factors, $g;
  109. }
  110. }
  111. }
  112. push @factors, $orig / vecprod(@factors);
  113. return sort { $a <=> $b } @factors;
  114. }
  115. if (@ARGV) {
  116. say join ', ', cgpow_factor($ARGV[0], 1);
  117. exit;
  118. }
  119. # Large roots
  120. say join ' * ', cgpow_factor(ipow(1009, 24) + ipow(29, 12));
  121. say join ' * ', cgpow_factor(ipow(1009, 24) - ipow(29, 12));
  122. say join ' * ', cgpow_factor(ipow(59388821, 12) - ipow(151, 36));
  123. say '-' x 80;
  124. # Small roots
  125. say join ' * ', cgpow_factor(ipow(2, 256) - 1);
  126. say join ' * ', cgpow_factor(ipow(10, 120) + 1);
  127. say join ' * ', cgpow_factor(ipow(10, 120) - 1);
  128. say join ' * ', cgpow_factor(ipow(10, 120) - 25);
  129. say join ' * ', cgpow_factor(ipow(10, 105) - 1);
  130. say join ' * ', cgpow_factor(ipow(10, 105) + 1);
  131. say join ' * ', cgpow_factor(ipow(10, 120) - 2134 * 2134);
  132. say join ' * ', cgpow_factor((ipow(2, 128) - 1) * (ipow(2, 256) - 1));
  133. say join ' * ', cgpow_factor(ipow(ipow(4, 64) - 1, 3) - 1);
  134. say join ' * ', cgpow_factor((ipow(2, 128) - 1) * (ipow(3, 128) - 1));
  135. say join ' * ', cgpow_factor((ipow(5, 48) + 1) * (ipow(3, 120) + 1));
  136. say join ' * ', cgpow_factor((ipow(5, 48) + 1) * (ipow(3, 120) - 1));
  137. say join ' * ', cgpow_factor((ipow(5, 48) - 1) * (ipow(3, 120) + 1));
  138. __END__
  139. 1074309286591662655506002 * 1154140443257087164049583013000044736320575461201
  140. 1018052 * 1018110 * 1699854 * 45120343 * 14006607073 * 1036518447751 * 1074309285719975471632201
  141. 1038960 * 5594587 * 23044763 * 61015275368249 * 534765538858459 * 4033015478857732019 * 109215797426552565244488121
  142. --------------------------------------------------------------------------------
  143. 4294967295 * 4294967297 * 18446744073709551617 * 340282366920938463463374607431768211457
  144. 100000001 * 9999999900000001 * 99999999000000009999999900000001 * 10000000099999999999999989999999899999999000000000000000100000001
  145. 50851 * 1000001 * 1040949 * 1110111 * 1450031 * 2463661 * 2906161 * 99009901 * 99990001 * 165573604901641 * 9999000099990001 * 100009999999899989999000000010001
  146. 999999999999999999999999999999999999999999999999999999999995 * 1000000000000000000000000000000000000000000000000000000000005
  147. 1111111 * 1269729 * 787569631 * 900900990991 * 900009090090909909099991 * 1109988789001111109989898989900111110998878900111
  148. 1313053 * 10000001 * 1236109099 * 61549824583 * 1099988890111109888900011 * 910009191000909089989898989899909091000919100091
  149. 999999999999999999999999999999999999999999999999999999997866 * 1000000000000000000000000000000000000000000000000000000002134
  150. 1114129 * 2451825 * 6700417 * 16843009 * 1103806595329 * 18446744073709551617 * 18446744073709551617 * 340282366920938463463374607431768211457
  151. 340282366920938463463374607431768211454 * 115792089237316195423570985008687907852929702298719625575994209400481361428481
  152. 7913 * 1109760 * 43046722 * 84215045 * 4294967297 * 926510094425921 * 18446744073709551617 * 1716841910146256242328924544641
  153. 1273028 * 29423041 * 145127617 * 240031591394168814433 * 4892905104216215334417146433664153647610647561409
  154. 1013824 * 1236031 * 1519505 * 43584805 * 47763361 * 1743392201 * 76293945313 * 50446744628921761 * 240031591394168814433
  155. 1083264 * 1331139 * 1971881 * 122070313 * 29802322387695313 * 617180487788001154016207027393267755290289744417