gaussian_divisors.sf 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 12 June 2022
  4. # https://github.com/trizen
  5. # Find the factors and divisors of a Gaussian integer.
  6. # See also:
  7. # https://oeis.org/A125271
  8. # https://oeis.org/A078930
  9. # https://oeis.org/A078910
  10. # https://oeis.org/A078911
  11. # https://projecteuler.net/problem=153
  12. # https://www.alpertron.com.ar/GAUSSIAN.HTM
  13. # https://en.wikipedia.org/wiki/Table_of_Gaussian_integer_factorizations
  14. func gaussian_factors(Gauss z) {
  15. var n = z.norm
  16. var factors = []
  17. for p,e in (n.factor_exp) {
  18. if (p == 2) {
  19. var t = Gauss(1,1)
  20. while (z % t == 0) {
  21. factors << t
  22. z /= t
  23. }
  24. }
  25. elsif (p % 4 == 3) {
  26. var t = Gauss(p)
  27. while (z % t == 0) {
  28. factors << t
  29. z /= t
  30. }
  31. }
  32. else {
  33. for a,b in (sum_of_squares(p)) {
  34. var t1 = Gauss(a,b)
  35. var t2 = Gauss(b,a)
  36. while (z % t1 == 0) {
  37. factors << t1
  38. z /= t1
  39. }
  40. while (z % t2 == 0) {
  41. factors << t2
  42. z /= t2
  43. }
  44. }
  45. }
  46. }
  47. if (z != Gauss(1)) {
  48. factors << z
  49. }
  50. return factors.sort.run_length
  51. }
  52. func gaussian_divisors(Gauss n) {
  53. var d = [Gauss(1), Gauss(-1), Gauss(0,1), Gauss(0,-1)]
  54. for p,e in (gaussian_factors(n)) {
  55. if ((p.imag == 0) && (p.real.abs == 1)) {
  56. next
  57. }
  58. if ((p.real == 0) && (p.imag.abs == 1)) {
  59. next
  60. }
  61. var r = Gauss(1)
  62. d << gather {
  63. e.times {
  64. r *= p
  65. d.each { |u|
  66. take(u*r)
  67. }
  68. }
  69. }...
  70. }
  71. return d
  72. }
  73. for n in (1..5) {
  74. say "GaussDivisors(#{n}) = #{gaussian_divisors(Gauss(n))}"
  75. }
  76. say gaussian_divisors(Gauss(440,-55)).len #=> 96
  77. say 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos}.len } #=> OEIS: A125271
  78. #
  79. ## Run some tests
  80. #
  81. assert_eq(
  82. 1..5 -> map{gaussian_divisors(Gauss(_)).grep{.real.is_pos}.sum},
  83. [Gauss(1), Gauss(5), Gauss(4), Gauss(13), Gauss(12)]
  84. )
  85. assert_eq(
  86. 1..48 -> map{gaussian_divisors(Gauss(_)).grep{.real.is_pos}.sum}.sum,
  87. Gauss(3654)
  88. )
  89. assert_eq(
  90. 1..5 -> map{gaussian_divisors(Gauss(0, _)).grep{.real.is_pos}.sum},
  91. [Gauss(1), Gauss(5), Gauss(4), Gauss(13), Gauss(12)]
  92. )
  93. assert_eq( # OEIS: A125271
  94. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos}.len },
  95. %n[1, 1, 4, 2, 7, 6, 8, 2, 10, 3, 20, 2, 14, 6, 8, 12, 13, 6, 12, 2, 34, 4, 8, 2, 20, 15, 20, 4, 14, 6]
  96. )
  97. assert_eq( # OEIS: A078930
  98. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos}.sum.real },
  99. %n[1, 1, 5, 4, 13, 12, 20, 8, 29, 13, 56, 12, 52, 24, 40, 48, 61, 28, 65, 20, 144, 32, 60, 24, 116, 81, 112, 40, 104, 44]
  100. )
  101. assert_eq( # OEIS: A078930
  102. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos}.map{.real}.sum },
  103. %n[1, 1, 5, 4, 13, 12, 20, 8, 29, 13, 56, 12, 52, 24, 40, 48, 61, 28, 65, 20, 144, 32, 60, 24, 116, 81, 112, 40, 104, 44]
  104. )
  105. assert_eq( # OEIS: A078930
  106. 30.of { gaussian_divisors(Gauss(_)).grep{.imag.is_pos}.map{.imag}.sum },
  107. %n[1, 1, 5, 4, 13, 12, 20, 8, 29, 13, 56, 12, 52, 24, 40, 48, 61, 28, 65, 20, 144, 32, 60, 24, 116, 81, 112, 40, 104, 44]
  108. )
  109. assert_eq( # OEIS: A078911
  110. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos && .imag.is_pos}.map{.imag}.sum },
  111. %n[0, 0, 1, 0, 3, 3, 4, 0, 7, 0, 19, 0, 12, 5, 8, 12, 15, 5, 13, 0, 51, 0, 12, 0, 28, 25, 35, 0, 24, 7]
  112. )
  113. assert_eq( # OEIS: A078911
  114. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos && .imag.is_pos}.map{.real}.sum },
  115. %n[0, 0, 1, 0, 3, 3, 4, 0, 7, 0, 19, 0, 12, 5, 8, 12, 15, 5, 13, 0, 51, 0, 12, 0, 28, 25, 35, 0, 24, 7]
  116. )
  117. assert_eq( # OEIS: A078911
  118. 30.of { gaussian_divisors(Gauss(_)).grep{.real>=0 || .imag>=0}.sum.real },
  119. %n[0, 0, 1, 0, 3, 3, 4, 0, 7, 0, 19, 0, 12, 5, 8, 12, 15, 5, 13, 0, 51, 0, 12, 0, 28, 25, 35, 0, 24, 7]
  120. )
  121. assert_eq( # OEIS: A078911
  122. 30.of { gaussian_divisors(Gauss(_)).grep{.real>=0 && .imag>0}.sum.real },
  123. %n[0, 0, 1, 0, 3, 3, 4, 0, 7, 0, 19, 0, 12, 5, 8, 12, 15, 5, 13, 0, 51, 0, 12, 0, 28, 25, 35, 0, 24, 7]
  124. )
  125. assert_eq( # OEIS: A078911
  126. 30.of { gaussian_divisors(Gauss(_)).grep{.real>=0 || .imag>=0}.sum.imag },
  127. %n[0, 0, 1, 0, 3, 3, 4, 0, 7, 0, 19, 0, 12, 5, 8, 12, 15, 5, 13, 0, 51, 0, 12, 0, 28, 25, 35, 0, 24, 7]
  128. )
  129. assert_eq( # OEIS: A078910
  130. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos || .imag.is_pos}.map{.real}.sum },
  131. %n[1, 1, 4, 4, 10, 9, 16, 8, 22, 13, 37, 12, 40, 19, 32, 36, 46, 23, 52, 20, 93, 32, 48, 24, 88, 56, 77, 40, 80, 37]
  132. )
  133. assert_eq( # OEIS: A078910
  134. 30.of { gaussian_divisors(Gauss(_)).grep{.real.is_pos || .imag.is_pos}.map{.imag}.sum },
  135. %n[1, 1, 4, 4, 10, 9, 16, 8, 22, 13, 37, 12, 40, 19, 32, 36, 46, 23, 52, 20, 93, 32, 48, 24, 88, 56, 77, 40, 80, 37]
  136. )
  137. assert_eq( # OEIS: A078910
  138. 30.of { gaussian_divisors(Gauss(_)).grep{.real>0 && .imag>=0}.sum.real },
  139. %n[1, 1, 4, 4, 10, 9, 16, 8, 22, 13, 37, 12, 40, 19, 32, 36, 46, 23, 52, 20, 93, 32, 48, 24, 88, 56, 77, 40, 80, 37]
  140. )
  141. assert_eq(
  142. 30.of { gaussian_divisors(Gauss(_)).sum },
  143. 30.of { Gauss(0,0) }
  144. )
  145. __END__
  146. GaussDivisors(1) = [Gauss(-1, 0), Gauss(0, -1), Gauss(0, 1), Gauss(1, 0)]
  147. GaussDivisors(2) = [Gauss(-2, 0), Gauss(-1, -1), Gauss(-1, 0), Gauss(-1, 1), Gauss(0, -2), Gauss(0, -1), Gauss(0, 1), Gauss(0, 2), Gauss(1, -1), Gauss(1, 0), Gauss(1, 1), Gauss(2, 0)]
  148. GaussDivisors(3) = [Gauss(-3, 0), Gauss(-1, 0), Gauss(0, -3), Gauss(0, -1), Gauss(0, 1), Gauss(0, 3), Gauss(1, 0), Gauss(3, 0)]
  149. GaussDivisors(4) = [Gauss(-4, 0), Gauss(-2, -2), Gauss(-2, 0), Gauss(-2, 2), Gauss(-1, -1), Gauss(-1, 0), Gauss(-1, 1), Gauss(0, -4), Gauss(0, -2), Gauss(0, -1), Gauss(0, 1), Gauss(0, 2), Gauss(0, 4), Gauss(1, -1), Gauss(1, 0), Gauss(1, 1), Gauss(2, -2), Gauss(2, 0), Gauss(2, 2), Gauss(4, 0)]
  150. GaussDivisors(5) = [Gauss(-5, 0), Gauss(-2, -1), Gauss(-2, 1), Gauss(-1, -2), Gauss(-1, 0), Gauss(-1, 2), Gauss(0, -5), Gauss(0, -1), Gauss(0, 1), Gauss(0, 5), Gauss(1, -2), Gauss(1, 0), Gauss(1, 2), Gauss(2, -1), Gauss(2, 1), Gauss(5, 0)]