Skip to content

Commit 54bfe51

Browse files
Merge pull request #18893 from nikhil1980/BAEL-8807
BAEL-8807: Check if a number can be written as a sum of two squares i…
2 parents 9792292 + 5ae36f6 commit 54bfe51

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.baeldung.algorithms.sumoftwosquares;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
public class NumberAsSumOfTwoSquares {
7+
8+
private static final Logger LOGGER = LoggerFactory.getLogger(NumberAsSumOfTwoSquares.class);
9+
10+
/**
11+
* Checks if a non-negative integer n can be written as the
12+
* sum of two squares i.e. (a^2 + b^2)
13+
* This implementation is based on Fermat's theorem on sums of two squares.
14+
*
15+
* @param n The number to check (must be non-negative).
16+
* @return true if n can be written as a sum of two squares, false otherwise.
17+
*/
18+
public static boolean isSumOfTwoSquares(int n) {
19+
if (n < 0) {
20+
LOGGER.warn("Input must be non-negative. Returning false for n = {}", n);
21+
return false;
22+
}
23+
if (n == 0) {
24+
return true; // 0 = 0^2 + 0^2
25+
}
26+
27+
// 1. Reduce n to an odd number if n is even.
28+
while (n % 2 == 0) {
29+
n /= 2;
30+
}
31+
32+
// 2. Iterate through odd prime factors starting from 3
33+
for (int i = 3; i * i <= n; i += 2) {
34+
// 2a. Find the exponent of the factor i
35+
int count = 0;
36+
while (n % i == 0) {
37+
count++;
38+
n /= i;
39+
}
40+
41+
// 2b. Check the condition from Fermat's theorem
42+
// If i is of form 4k+3 (i % 4 == 3) and has an odd exponent
43+
if (i % 4 == 3 && count % 2 != 0) {
44+
LOGGER.debug("Failing condition: factor {} (form 4k+3) has odd exponent {}", i, count);
45+
return false;
46+
}
47+
}
48+
49+
// 3. Handle the last remaining factor (which is prime if > 1)
50+
// If n itself is a prime of the form 4k+3, its exponent is 1 (odd).
51+
if (n % 4 == 3) {
52+
LOGGER.debug("Failing condition: remaining factor {} is of form 4k+3", n);
53+
return false;
54+
}
55+
56+
// 4. All 4k+3 primes had even exponents.
57+
return true;
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.baeldung.algorithms.sumoftwosquares;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.DisplayName;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
8+
9+
class NumberAsSumOfTwoSquaresUnitTest {
10+
11+
@Test
12+
@DisplayName("Given input number can be expressed as a sum of squares, when checked, then returns true")
13+
void givenNumberIsSumOfSquares_whenCheckIsCalled_thenReturnsTrue() {
14+
// Simple cases
15+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(0)); // 0^2 + 0^2
16+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(1)); // 1^2 + 0^2
17+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(5)); // 1^2 + 2^2
18+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(8)); // 2^2 + 2^2
19+
20+
// Cases from Fermat theorem
21+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(50)); // 2 * 5^2. No 4k+3 primes.
22+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(45)); // 3^2 * 5. 4k+3 prime (3) has even exp.
23+
assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(18)); // 2 * 3^2. 4k+3 prime (3) has even exp.
24+
}
25+
26+
@Test
27+
@DisplayName("Given input number can't be expressed as a sum of squares, when checked, then returns false")
28+
void givenNumberIsNotSumOfSquares_whenCheckIsCalled_thenReturnsFalse() {
29+
// Simple cases
30+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(3)); // 3 (4k+3, exp 1)
31+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(6)); // 2 * 3 (3 has exp 1)
32+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(7)); // 7 (4k+3, exp 1)
33+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(11)); // 11 (4k+3, exp 1)
34+
35+
// Cases from theorem
36+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(12)); // 2^2 * 3 (3 has exp 1)
37+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(21)); // 3 * 7 (both 3 and 7 have exp 1)
38+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(28)); // 2^2 * 7 (7 has exp 1)
39+
}
40+
41+
@Test
42+
@DisplayName("Given input number is negative, when checked, then returns false")
43+
void givenNegativeNumber_whenCheckIsCalled_thenReturnsFalse() {
44+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(-1)); // Negatives as hygiene
45+
assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(-10)); // Negatives as hygiene
46+
}
47+
}

0 commit comments

Comments
 (0)