cyclotomic_factorization_method_2.pl 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 23 May 2022
  4. # https://github.com/trizen
  5. # A variant of the Cyclotomic factorization method.
  6. # See also:
  7. # https://www.ams.org/journals/mcom/1989-52-185/S0025-5718-1989-0947467-1/S0025-5718-1989-0947467-1.pdf
  8. use 5.020;
  9. use strict;
  10. use warnings;
  11. use Math::GMPz;
  12. use ntheory qw(:all);
  13. use POSIX qw(ULONG_MAX);
  14. use experimental qw(signatures);
  15. sub cyclotomic_factor ($n, @bases) {
  16. $n = Math::GMPz->new("$n");
  17. Math::GMPz::Rmpz_cmp_ui($n, 1) > 0 or return;
  18. if (@bases) {
  19. @bases = map { Math::GMPz->new("$_") } @bases;
  20. }
  21. else {
  22. @bases = map { Math::GMPz->new($_) } (2 .. logint($n, 2));
  23. }
  24. my $cyclotomicmod = sub ($n, $x, $m) {
  25. my @factor_exp = factor_exp($n);
  26. # Generate the squarefree divisors of n, along
  27. # with the number of prime factors of each divisor
  28. my @sd;
  29. foreach my $pe (@factor_exp) {
  30. my ($p) = @$pe;
  31. push @sd, map { [$_->[0] * $p, $_->[1] + 1] } @sd;
  32. push @sd, [$p, 1];
  33. }
  34. push @sd, [Math::GMPz::Rmpz_init_set_ui(1), 0];
  35. my $prod = Math::GMPz::Rmpz_init_set_ui(1);
  36. foreach my $pair (@sd) {
  37. my ($d, $c) = @$pair;
  38. my $base = Math::GMPz::Rmpz_init();
  39. my $exp = CORE::int($n / $d);
  40. Math::GMPz::Rmpz_powm_ui($base, $x, $exp, $m); # x^(n/d) mod m
  41. Math::GMPz::Rmpz_sub_ui($base, $base, 1);
  42. if ($c % 2 == 1) {
  43. Math::GMPz::Rmpz_invert($base, $base, $m) || return $base;
  44. }
  45. Math::GMPz::Rmpz_mul($prod, $prod, $base);
  46. Math::GMPz::Rmpz_mod($prod, $prod, $m);
  47. }
  48. $prod;
  49. };
  50. my @factors;
  51. state $g = Math::GMPz::Rmpz_init_nobless();
  52. OUTER: foreach my $x (@bases) {
  53. my $limit = 1 + logint($n, $x);
  54. foreach my $k (3 .. $limit) {
  55. my $c = $cyclotomicmod->($k, $x, $n);
  56. Math::GMPz::Rmpz_gcd($g, $n, $c);
  57. if (Math::GMPz::Rmpz_cmp_ui($g, 1) > 0 and Math::GMPz::Rmpz_cmp($g, $n) < 0) {
  58. my $valuation = Math::GMPz::Rmpz_remove($n, $n, $g);
  59. push(@factors, (Math::GMPz::Rmpz_init_set($g)) x $valuation);
  60. if (Math::GMPz::Rmpz_cmp_ui($n, 1) == 0 or is_prob_prime($n)) {
  61. last OUTER;
  62. }
  63. }
  64. }
  65. }
  66. if (Math::GMPz::Rmpz_cmp_ui($n, 1) > 0) {
  67. push @factors, $n;
  68. }
  69. @factors = sort { Math::GMPz::Rmpz_cmp($a, $b) } @factors;
  70. return @factors;
  71. }
  72. say join ' * ', cyclotomic_factor(Math::GMPz->new(2)**120 + 1);
  73. say join ' * ', cyclotomic_factor(Math::GMPz->new(2)**128 - 1);
  74. say join ' * ', cyclotomic_factor(((Math::GMPz->new(10)**258 - 1) / 9 - Math::GMPz->new(10)**(258 >> 1) - 1), 10);
  75. __END__
  76. 257 * 65281 * 4278255361 * 18518800563924107521
  77. 3 * 5 * 17 * 257 * 65537 * 4294967297 * 18446744073709551617
  78. 10 * 11 * 11 * 91 * 101 * 10001 * 100000001 * 10000000000000001 * 100000000000000000000000000000001 * 909090909090909090909090909090909090909091 * 10000000000000000000000000000000000000000000000000000000000000001 * 1098901098901098901098901098901098901098900989010989010989010989010989010989010989011