Skip to content

Commit dbcf156

Browse files
authored
fix(maths): prevent intermediate integer overflow in CatalanNumbers (#7732)
1 parent 1e59594 commit dbcf156

2 files changed

Lines changed: 19 additions & 12 deletions

File tree

‎src/main/java/com/thealgorithms/maths/CatalanNumbers.java‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
11
package com.thealgorithms.maths;
22

3+
import java.math.BigInteger;
4+
35
/**
46
* Calculate Catalan Numbers
57
*/
68
public final class CatalanNumbers {
9+
private static final int MAX_SUPPORTED_INDEX = 35;
10+
711
private CatalanNumbers() {
812
}
913

1014
/**
11-
* Calculate the nth Catalan number using a recursive formula.
15+
* Calculate the nth Catalan number using the factorial formula.
1216
*
1317
* @param n the index of the Catalan number to compute
1418
* @return the nth Catalan number
19+
* @throws IllegalArgumentException if n is negative or exceeds the maximum supported index for long
1520
*/
1621
public static long catalan(final int n) {
1722
if (n < 0) {
1823
throw new IllegalArgumentException("Index must be non-negative");
1924
}
20-
return factorial(2 * n) / (factorial(n + 1) * factorial(n));
25+
if (n > MAX_SUPPORTED_INDEX) {
26+
throw new IllegalArgumentException("Index exceeds maximum supported value for long");
27+
}
28+
return factorial(2 * n).divide(factorial(n + 1).multiply(factorial(n))).longValueExact();
2129
}
2230

2331
/**
24-
* Calculate the factorial of a number.
32+
* Calculate the factorial of a number using BigInteger to prevent integer overflow.
2533
*
2634
* @param n the number to compute the factorial for
27-
* @return the factorial of n
35+
* @return the factorial of n as BigInteger
2836
*/
29-
private static long factorial(final int n) {
30-
if (n == 0 || n == 1) {
31-
return 1;
32-
}
33-
long result = 1;
37+
private static BigInteger factorial(final int n) {
38+
BigInteger result = BigInteger.ONE;
3439
for (int i = 2; i <= n; i++) {
35-
result *= i;
40+
result = result.multiply(BigInteger.valueOf(i));
3641
}
3742
return result;
3843
}

‎src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ class CatalanNumbersTest {
1919
* [input number, expected Catalan number for that input]
2020
*/
2121
static Stream<Object[]> catalanNumbersProvider() {
22-
return Stream.of(new Object[] {0, 1}, new Object[] {1, 1}, new Object[] {2, 2}, new Object[] {3, 5}, new Object[] {4, 14}, new Object[] {5, 42}, new Object[] {6, 132}, new Object[] {7, 429}, new Object[] {8, 1430}, new Object[] {9, 4862}, new Object[] {10, 16796});
22+
return Stream.of(new Object[] {0, 1L}, new Object[] {1, 1L}, new Object[] {2, 2L}, new Object[] {3, 5L}, new Object[] {4, 14L}, new Object[] {5, 42L}, new Object[] {6, 132L}, new Object[] {7, 429L}, new Object[] {8, 1430L}, new Object[] {9, 4862L}, new Object[] {10, 16796L},
23+
new Object[] {11, 58786L}, new Object[] {15, 9694845L}, new Object[] {35, 3116285494907301262L});
2324
}
2425

2526
/**
@@ -28,7 +29,7 @@ static Stream<Object[]> catalanNumbersProvider() {
2829
*/
2930
@ParameterizedTest
3031
@MethodSource("catalanNumbersProvider")
31-
void testCatalanNumbers(int input, int expected) {
32+
void testCatalanNumbers(int input, long expected) {
3233
assertEquals(expected, CatalanNumbers.catalan(input), () -> String.format("Catalan number for input %d should be %d", input, expected));
3334
}
3435

@@ -39,5 +40,6 @@ void testCatalanNumbers(int input, int expected) {
3940
void testIllegalInput() {
4041
assertThrows(IllegalArgumentException.class, () -> CatalanNumbers.catalan(-1));
4142
assertThrows(IllegalArgumentException.class, () -> CatalanNumbers.catalan(-5));
43+
assertThrows(IllegalArgumentException.class, () -> CatalanNumbers.catalan(36));
4244
}
4345
}

0 commit comments

Comments
 (0)