Here is a solution based on the mathematical intuition behind Gaussian integers. We are working in the "ring" R of all numbers of the form
a + ib
where a, b are integers. This is the ring of Gaussian integers.
Here, i is the square root of -1. So i² = -1.
Such numbers lead to a similar arithmetic as in the case of the (usual) integers. Each such number has a unique decomposition in gaussian primes. (Up to the order of the factors.) Such a domain is called a unique factorization domain, UFD.
Which are the primes in R? (Those elements that cannot be split multiplicatively in more than two non-invertible pieces.) There is a concrete characterization for them.
The classical primes of the shapes 4k + 3 remain primes in R, are inert. So we cannot split primes like 3, 7, 11, 19, 23, 31, ... in R. But we can always split uniquely (up to unit conjugation, a unit being one among 1, -1, i, -i) the (classical) primes of the shape 4k + 1 in R. For instance:
(*)
5 = (2 + i)(2 - i)
13 = (3 + 2i)(3 - 2i)
17 = (4 + i)(4 - i)
29 = (5 + 2i)(5 - 2i)
37 = (6 + i)(6 - i)
41 = (5 + 4i)(5 - 4i)
53 = (7 + 2i)(7 - 2i)
61 = (6 + 5i)(6 - 5i)
and so on, i hope the scheme is clear. For our purpose, the remained prime two is the oddest prime. Since we have its decomposition
2 = (1 + i)(1 -i)
, where the two Gaussian primes (1 + i) and (1 - i) are associated, multiplying with a unit bring one in the other one. I will avoid this prime below.
Now consider the product of some of the numbers on the L.H.S. in (*)
. For instance 5.5.13.17 = 5525 - and let us pick from each of the four (classical) prime factors one of the Gaussian primes inside.
We may thus pick (2 + i) twice from the two 5-factors, (3 - 2i) from 13 and (4 + i) from the 17. We multiply and get:
sage: (2 + i)^2 * (3 - 2*i) * (4 + i)
41*I + 62
And indeed, a = 41 and b = 62 is a solution of 41² + 62² = 5525. Unfortunately 5525 is not a square. OK, let us start with a square, one like
1105² = 5².13².17² = (2+i)²(2-i)² . (3+2i)²(3-2i)² . (4+i)²(4-i)²
and now separate the factors in "two parts", so that in one part we have some factors, and in the other part the conjugates. Here are the possibilities for 25 = 5²:
(2+i)² and (2-i)²
5 and 5
(2-i)² and (2+i)²
There are three possibilities. Do the same for the other two squares, then combine. For instance:
sage: (2 + i)^2 * (3 - 2*i)^2 * 17
-272*I + 1071
And indeed, 272² + 1071² = 1105² . This solution is not "primitive", in the sense that 17 is a divisor of the three involved numbers, 272, 1071, 1105. Well, this happens because we took the factor 17 from the separation of 17² in two (equal) parts. To get some other solutions, we may take
- each possible first part from 5² with...
- each possible first part from 13² with...
- each possible first part from 17²
and thus get "many solutions". Here are they:
sage: [ (m, n) for m in range(1, 1105) for n in range(1, 1105)
....: if m <= n and m2 + n2 == 1105**2 ]
[(47, 1104),
(105, 1100),
(169, 1092),
(264, 1073),
(272, 1071),
(425, 1020),
(468, 1001),
(520, 975),
(561, 952),
(576, 943),
(663, 884),
(700, 855),
(744, 817)]
We expect 3.3.3 solutions. One of them is the trivial one, 1105² = 1105² + 0².
The other solutions of 1105² = a² + b² may be arranged to have a < b. (No chance to get equality.) So we expect (27 - 1)/2 = 13 solutions, yes, the ones above.
Which solution is produced by taking the "first parts" as follows: (2 + i)^2 * (3 - 2*i)^2 * (4 + i)^2 ?!
sage: (2 + i)^2 * (3 - 2*i)^2 * (4 + i)^2
264*I + 1073
And indeed, (264, 1073) is among the solutions above.
So if getting "highly composite" numbers is the issue, with an accent on highly, then just pick for c such a product of primes of the shape 4k + 1.
For instance c = 5³.13.17 or c = 5.13.17.29. Then compute all representations c² = (a + ib)(a - ib) = a² + b² best by using the UFD property of the Gaussian integers.
For instance, in a python3 dialog with the interpreter...
In [16]: L25 = [complex(2, 1)**4, complex(2, 1)**2 * 5, 25, complex(2, -1)**2 * 5, complex(2, -1)**4]
In [17]: L13 = [complex(3, 2)**2, 13, complex(3, -2)**2]
In [18]: L17 = [complex(4, 1)**2, 17, complex(4, -1)**2]
In [19]: solutions = []
In [20]: for z1 in L25:
...: for z2 in L13:
...: for z3 in L17:
...: z = z1 * z2 * z3
...: a, b = int(abs(z.real)), int(abs(z.imag))
...: if a > b:
...: a, b = b, a
...: solutions.append((a, b))
...:
In [21]: solutions = list(set(solutions))
In [22]: solutions.sort()
In [23]: len(solutions)
Out[23]: 23
In [24]: solutions
Out[24]:
[(0, 5525),
(235, 5520),
(525, 5500),
(612, 5491),
(845, 5460),
(1036, 5427),
(1131, 5408),
(1320, 5365),
(1360, 5355),
(1547, 5304),
(2044, 5133),
(2125, 5100),
(2163, 5084),
(2340, 5005),
(2600, 4875),
(2805, 4760),
(2880, 4715),
(3124, 4557),
(3315, 4420),
(3468, 4301),
(3500, 4275),
(3720, 4085),
(3861, 3952)]
We have 23 = 22 + 1 solutions. The last one is the trivial one. All other solutions (a, b) listed have a < b, so there are totally 1 + 22*2 = 45 = 5 * 3 * 3
, as expected from the triple for loop above. A similar code can be written for c = 5 * 13 * 17 * 29 = 32045
leading to (3^4 - 1)/2 = 40
non-trivial solutions.
In [26]: L5 = [complex(2, 1)**2, 5, complex(2, -1)**2]
In [27]: L13 = [complex(3, 2)**2, 13, complex(3, -2)**2]
In [28]: L17 = [complex(4, 1)**2, 17, complex(4, -1)**2]
In [29]: L29 = [complex(5, 2)**2, 29, complex(5, -2)**2]
In [30]: z_list = [z1*z2*z3*z4
...: for z1 in L5 for z2 in L13
...: for z3 in L17 for z4 in L29]
In [31]: ab_list = [(int(abs(z.real)), int(abs(z.imag))) for z in z_list]
In [32]: len(ab_list)
Out[32]: 81
In [33]: ab_list = list(set([(min(a, b), max(a, b)) for (a, b) in ab_list]))
In [34]: ab_list.sort()
In [35]: len(ab_list)
Out[35]: 41
In [36]: ab_list[:10]
Out[36]:
[(0, 32045),
(716, 32037),
(1363, 32016),
(2277, 31964),
(2400, 31955),
(3045, 31900),
(3757, 31824),
(3955, 31800),
(4901, 31668),
(5304, 31603)]
(Feel free to also use powers of two in c.)