diff --git a/src/main/java/com/thealgorithms/maths/LeonardoNumber.java b/src/main/java/com/thealgorithms/maths/LeonardoNumber.java index bbeec052777f..9ac3691a6285 100644 --- a/src/main/java/com/thealgorithms/maths/LeonardoNumber.java +++ b/src/main/java/com/thealgorithms/maths/LeonardoNumber.java @@ -1,25 +1,79 @@ package com.thealgorithms.maths; /** - * https://en.wikipedia.org/wiki/Leonardo_number + * Utility class for calculating Leonardo Numbers. + *
+ * Leonardo numbers are a sequence of numbers defined by the recurrence: + * L(n) = L(n-1) + L(n-2) + 1, with L(0) = 1 and L(1) = 1 + *
+ * The sequence begins: 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ... + *
+ * This class provides both a recursive implementation and an optimized + * iterative + * implementation for calculating Leonardo numbers. + * + * @see Leonardo Number + * - Wikipedia + * @see OEIS A001595 */ public final class LeonardoNumber { private LeonardoNumber() { } /** - * Calculate nth Leonardo Number (1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...) + * Calculates the nth Leonardo Number using recursion. + *
+ * Time Complexity: O(2^n) - exponential due to repeated calculations + * Space Complexity: O(n) - due to recursion stack + *
+ * Note: This method is not recommended for large values of n due to exponential + * time complexity. + * Consider using {@link #leonardoNumberIterative(int)} for better performance. * - * @param n the index of Leonardo Number to calculate - * @return nth number of Leonardo sequences + * @param n the index of the Leonardo Number to calculate (must be non-negative) + * @return the nth Leonardo Number + * @throws IllegalArgumentException if n is negative */ public static int leonardoNumber(int n) { if (n < 0) { - throw new ArithmeticException(); + throw new IllegalArgumentException("Input must be non-negative. Received: " + n); } if (n == 0 || n == 1) { return 1; } - return (leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1); + return leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1; + } + + /** + * Calculates the nth Leonardo Number using an iterative approach. + *
+ * This method provides better performance than the recursive version for large + * values of n. + *
+ * Time Complexity: O(n) + * Space Complexity: O(1) + * + * @param n the index of the Leonardo Number to calculate (must be non-negative) + * @return the nth Leonardo Number + * @throws IllegalArgumentException if n is negative + */ + public static int leonardoNumberIterative(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input must be non-negative. Received: " + n); + } + if (n == 0 || n == 1) { + return 1; + } + + int previous = 1; + int current = 1; + + for (int i = 2; i <= n; i++) { + int next = current + previous + 1; + previous = current; + current = next; + } + + return current; } } diff --git a/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java b/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java index 705f1a1006fa..baf4540cf239 100644 --- a/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java +++ b/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java @@ -1,29 +1,171 @@ package com.thealgorithms.maths; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class LeonardoNumberTest { +/** + * Test cases for {@link LeonardoNumber} class. + *
+ * Tests both recursive and iterative implementations with various input values + * including edge cases and boundary conditions. + */ +class LeonardoNumberTest { + + // Tests for recursive implementation + + @Test + void testLeonardoNumberNegative() { + Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-1)); + } + + @Test + void testLeonardoNumberNegativeLarge() { + Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-100)); + } + + @Test + void testLeonardoNumberZero() { + Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(0)); + } + + @Test + void testLeonardoNumberOne() { + Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(1)); + } + + @Test + void testLeonardoNumberTwo() { + Assertions.assertEquals(3, LeonardoNumber.leonardoNumber(2)); + } + + @Test + void testLeonardoNumberThree() { + Assertions.assertEquals(5, LeonardoNumber.leonardoNumber(3)); + } + + @Test + void testLeonardoNumberFour() { + Assertions.assertEquals(9, LeonardoNumber.leonardoNumber(4)); + } + + @Test + void testLeonardoNumberFive() { + Assertions.assertEquals(15, LeonardoNumber.leonardoNumber(5)); + } + + @Test + void testLeonardoNumberSix() { + Assertions.assertEquals(25, LeonardoNumber.leonardoNumber(6)); + } + + @Test + void testLeonardoNumberSeven() { + Assertions.assertEquals(41, LeonardoNumber.leonardoNumber(7)); + } + + @Test + void testLeonardoNumberEight() { + Assertions.assertEquals(67, LeonardoNumber.leonardoNumber(8)); + } + + @Test + void testLeonardoNumberTen() { + Assertions.assertEquals(177, LeonardoNumber.leonardoNumber(10)); + } + + @Test + void testLeonardoNumberFifteen() { + Assertions.assertEquals(1973, LeonardoNumber.leonardoNumber(15)); + } + @Test - void leonardoNumberNegative() { - assertThrows(ArithmeticException.class, () -> LeonardoNumber.leonardoNumber(-1)); + void testLeonardoNumberTwenty() { + Assertions.assertEquals(21891, LeonardoNumber.leonardoNumber(20)); } + + // Tests for iterative implementation + + @Test + void testLeonardoNumberIterativeNegative() { + Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-1)); + } + + @Test + void testLeonardoNumberIterativeNegativeLarge() { + Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-50)); + } + + @Test + void testLeonardoNumberIterativeZero() { + Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(0)); + } + + @Test + void testLeonardoNumberIterativeOne() { + Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(1)); + } + + @Test + void testLeonardoNumberIterativeTwo() { + Assertions.assertEquals(3, LeonardoNumber.leonardoNumberIterative(2)); + } + + @Test + void testLeonardoNumberIterativeThree() { + Assertions.assertEquals(5, LeonardoNumber.leonardoNumberIterative(3)); + } + + @Test + void testLeonardoNumberIterativeFour() { + Assertions.assertEquals(9, LeonardoNumber.leonardoNumberIterative(4)); + } + @Test - void leonardoNumberZero() { - assertEquals(1, LeonardoNumber.leonardoNumber(0)); + void testLeonardoNumberIterativeFive() { + Assertions.assertEquals(15, LeonardoNumber.leonardoNumberIterative(5)); } + @Test - void leonardoNumberOne() { - assertEquals(1, LeonardoNumber.leonardoNumber(1)); + void testLeonardoNumberIterativeSix() { + Assertions.assertEquals(25, LeonardoNumber.leonardoNumberIterative(6)); } + @Test - void leonardoNumberFive() { - assertEquals(15, LeonardoNumber.leonardoNumber(5)); + void testLeonardoNumberIterativeSeven() { + Assertions.assertEquals(41, LeonardoNumber.leonardoNumberIterative(7)); } + + @Test + void testLeonardoNumberIterativeEight() { + Assertions.assertEquals(67, LeonardoNumber.leonardoNumberIterative(8)); + } + + @Test + void testLeonardoNumberIterativeTen() { + Assertions.assertEquals(177, LeonardoNumber.leonardoNumberIterative(10)); + } + + @Test + void testLeonardoNumberIterativeFifteen() { + Assertions.assertEquals(1973, LeonardoNumber.leonardoNumberIterative(15)); + } + + @Test + void testLeonardoNumberIterativeTwenty() { + Assertions.assertEquals(21891, LeonardoNumber.leonardoNumberIterative(20)); + } + + @Test + void testLeonardoNumberIterativeTwentyFive() { + Assertions.assertEquals(242785, LeonardoNumber.leonardoNumberIterative(25)); + } + + // Consistency tests between recursive and iterative implementations + @Test - void leonardoNumberTwenty() { - assertEquals(21891, LeonardoNumber.leonardoNumber(20)); + void testConsistencyBetweenImplementations() { + for (int i = 0; i <= 15; i++) { + Assertions.assertEquals(LeonardoNumber.leonardoNumber(i), LeonardoNumber.leonardoNumberIterative(i), "Mismatch at index " + i); + } } }