Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions src/main/java/com/thealgorithms/maths/LeonardoNumber.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,79 @@
package com.thealgorithms.maths;

/**
* https://en.wikipedia.org/wiki/Leonardo_number
* Utility class for calculating Leonardo Numbers.
* <p>
* 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
* <p>
* The sequence begins: 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...
* <p>
* This class provides both a recursive implementation and an optimized
* iterative
* implementation for calculating Leonardo numbers.
*
* @see <a href="https://en.wikipedia.org/wiki/Leonardo_number">Leonardo Number
* - Wikipedia</a>
* @see <a href="https://oeis.org/A001595">OEIS A001595</a>
*/
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.
* <p>
* Time Complexity: O(2^n) - exponential due to repeated calculations
* Space Complexity: O(n) - due to recursion stack
* <p>
* 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.
* <p>
* This method provides better performance than the recursive version for large
* values of n.
* <p>
* 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;
}
}
170 changes: 156 additions & 14 deletions src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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);
}
}
}