Skip to content

Commit f30d101

Browse files
authored
refactor: Enhance docs, code, add tests in LeonardoNumber (#6743)
1 parent 883a050 commit f30d101

File tree

2 files changed

+216
-20
lines changed

2 files changed

+216
-20
lines changed
Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,79 @@
11
package com.thealgorithms.maths;
22

33
/**
4-
* https://en.wikipedia.org/wiki/Leonardo_number
4+
* Utility class for calculating Leonardo Numbers.
5+
* <p>
6+
* Leonardo numbers are a sequence of numbers defined by the recurrence:
7+
* L(n) = L(n-1) + L(n-2) + 1, with L(0) = 1 and L(1) = 1
8+
* <p>
9+
* The sequence begins: 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...
10+
* <p>
11+
* This class provides both a recursive implementation and an optimized
12+
* iterative
13+
* implementation for calculating Leonardo numbers.
14+
*
15+
* @see <a href="https://en.wikipedia.org/wiki/Leonardo_number">Leonardo Number
16+
* - Wikipedia</a>
17+
* @see <a href="https://oeis.org/A001595">OEIS A001595</a>
518
*/
619
public final class LeonardoNumber {
720
private LeonardoNumber() {
821
}
922

1023
/**
11-
* Calculate nth Leonardo Number (1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...)
24+
* Calculates the nth Leonardo Number using recursion.
25+
* <p>
26+
* Time Complexity: O(2^n) - exponential due to repeated calculations
27+
* Space Complexity: O(n) - due to recursion stack
28+
* <p>
29+
* Note: This method is not recommended for large values of n due to exponential
30+
* time complexity.
31+
* Consider using {@link #leonardoNumberIterative(int)} for better performance.
1232
*
13-
* @param n the index of Leonardo Number to calculate
14-
* @return nth number of Leonardo sequences
33+
* @param n the index of the Leonardo Number to calculate (must be non-negative)
34+
* @return the nth Leonardo Number
35+
* @throws IllegalArgumentException if n is negative
1536
*/
1637
public static int leonardoNumber(int n) {
1738
if (n < 0) {
18-
throw new ArithmeticException();
39+
throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
1940
}
2041
if (n == 0 || n == 1) {
2142
return 1;
2243
}
23-
return (leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1);
44+
return leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1;
45+
}
46+
47+
/**
48+
* Calculates the nth Leonardo Number using an iterative approach.
49+
* <p>
50+
* This method provides better performance than the recursive version for large
51+
* values of n.
52+
* <p>
53+
* Time Complexity: O(n)
54+
* Space Complexity: O(1)
55+
*
56+
* @param n the index of the Leonardo Number to calculate (must be non-negative)
57+
* @return the nth Leonardo Number
58+
* @throws IllegalArgumentException if n is negative
59+
*/
60+
public static int leonardoNumberIterative(int n) {
61+
if (n < 0) {
62+
throw new IllegalArgumentException("Input must be non-negative. Received: " + n);
63+
}
64+
if (n == 0 || n == 1) {
65+
return 1;
66+
}
67+
68+
int previous = 1;
69+
int current = 1;
70+
71+
for (int i = 2; i <= n; i++) {
72+
int next = current + previous + 1;
73+
previous = current;
74+
current = next;
75+
}
76+
77+
return current;
2478
}
2579
}
Lines changed: 156 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,171 @@
11
package com.thealgorithms.maths;
22

3-
import static org.junit.jupiter.api.Assertions.assertEquals;
4-
import static org.junit.jupiter.api.Assertions.assertThrows;
5-
3+
import org.junit.jupiter.api.Assertions;
64
import org.junit.jupiter.api.Test;
75

8-
public class LeonardoNumberTest {
6+
/**
7+
* Test cases for {@link LeonardoNumber} class.
8+
* <p>
9+
* Tests both recursive and iterative implementations with various input values
10+
* including edge cases and boundary conditions.
11+
*/
12+
class LeonardoNumberTest {
13+
14+
// Tests for recursive implementation
15+
16+
@Test
17+
void testLeonardoNumberNegative() {
18+
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-1));
19+
}
20+
21+
@Test
22+
void testLeonardoNumberNegativeLarge() {
23+
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumber(-100));
24+
}
25+
26+
@Test
27+
void testLeonardoNumberZero() {
28+
Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(0));
29+
}
30+
31+
@Test
32+
void testLeonardoNumberOne() {
33+
Assertions.assertEquals(1, LeonardoNumber.leonardoNumber(1));
34+
}
35+
36+
@Test
37+
void testLeonardoNumberTwo() {
38+
Assertions.assertEquals(3, LeonardoNumber.leonardoNumber(2));
39+
}
40+
41+
@Test
42+
void testLeonardoNumberThree() {
43+
Assertions.assertEquals(5, LeonardoNumber.leonardoNumber(3));
44+
}
45+
46+
@Test
47+
void testLeonardoNumberFour() {
48+
Assertions.assertEquals(9, LeonardoNumber.leonardoNumber(4));
49+
}
50+
51+
@Test
52+
void testLeonardoNumberFive() {
53+
Assertions.assertEquals(15, LeonardoNumber.leonardoNumber(5));
54+
}
55+
56+
@Test
57+
void testLeonardoNumberSix() {
58+
Assertions.assertEquals(25, LeonardoNumber.leonardoNumber(6));
59+
}
60+
61+
@Test
62+
void testLeonardoNumberSeven() {
63+
Assertions.assertEquals(41, LeonardoNumber.leonardoNumber(7));
64+
}
65+
66+
@Test
67+
void testLeonardoNumberEight() {
68+
Assertions.assertEquals(67, LeonardoNumber.leonardoNumber(8));
69+
}
70+
71+
@Test
72+
void testLeonardoNumberTen() {
73+
Assertions.assertEquals(177, LeonardoNumber.leonardoNumber(10));
74+
}
75+
76+
@Test
77+
void testLeonardoNumberFifteen() {
78+
Assertions.assertEquals(1973, LeonardoNumber.leonardoNumber(15));
79+
}
80+
981
@Test
10-
void leonardoNumberNegative() {
11-
assertThrows(ArithmeticException.class, () -> LeonardoNumber.leonardoNumber(-1));
82+
void testLeonardoNumberTwenty() {
83+
Assertions.assertEquals(21891, LeonardoNumber.leonardoNumber(20));
1284
}
85+
86+
// Tests for iterative implementation
87+
88+
@Test
89+
void testLeonardoNumberIterativeNegative() {
90+
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-1));
91+
}
92+
93+
@Test
94+
void testLeonardoNumberIterativeNegativeLarge() {
95+
Assertions.assertThrows(IllegalArgumentException.class, () -> LeonardoNumber.leonardoNumberIterative(-50));
96+
}
97+
98+
@Test
99+
void testLeonardoNumberIterativeZero() {
100+
Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(0));
101+
}
102+
103+
@Test
104+
void testLeonardoNumberIterativeOne() {
105+
Assertions.assertEquals(1, LeonardoNumber.leonardoNumberIterative(1));
106+
}
107+
108+
@Test
109+
void testLeonardoNumberIterativeTwo() {
110+
Assertions.assertEquals(3, LeonardoNumber.leonardoNumberIterative(2));
111+
}
112+
113+
@Test
114+
void testLeonardoNumberIterativeThree() {
115+
Assertions.assertEquals(5, LeonardoNumber.leonardoNumberIterative(3));
116+
}
117+
118+
@Test
119+
void testLeonardoNumberIterativeFour() {
120+
Assertions.assertEquals(9, LeonardoNumber.leonardoNumberIterative(4));
121+
}
122+
13123
@Test
14-
void leonardoNumberZero() {
15-
assertEquals(1, LeonardoNumber.leonardoNumber(0));
124+
void testLeonardoNumberIterativeFive() {
125+
Assertions.assertEquals(15, LeonardoNumber.leonardoNumberIterative(5));
16126
}
127+
17128
@Test
18-
void leonardoNumberOne() {
19-
assertEquals(1, LeonardoNumber.leonardoNumber(1));
129+
void testLeonardoNumberIterativeSix() {
130+
Assertions.assertEquals(25, LeonardoNumber.leonardoNumberIterative(6));
20131
}
132+
21133
@Test
22-
void leonardoNumberFive() {
23-
assertEquals(15, LeonardoNumber.leonardoNumber(5));
134+
void testLeonardoNumberIterativeSeven() {
135+
Assertions.assertEquals(41, LeonardoNumber.leonardoNumberIterative(7));
24136
}
137+
138+
@Test
139+
void testLeonardoNumberIterativeEight() {
140+
Assertions.assertEquals(67, LeonardoNumber.leonardoNumberIterative(8));
141+
}
142+
143+
@Test
144+
void testLeonardoNumberIterativeTen() {
145+
Assertions.assertEquals(177, LeonardoNumber.leonardoNumberIterative(10));
146+
}
147+
148+
@Test
149+
void testLeonardoNumberIterativeFifteen() {
150+
Assertions.assertEquals(1973, LeonardoNumber.leonardoNumberIterative(15));
151+
}
152+
153+
@Test
154+
void testLeonardoNumberIterativeTwenty() {
155+
Assertions.assertEquals(21891, LeonardoNumber.leonardoNumberIterative(20));
156+
}
157+
158+
@Test
159+
void testLeonardoNumberIterativeTwentyFive() {
160+
Assertions.assertEquals(242785, LeonardoNumber.leonardoNumberIterative(25));
161+
}
162+
163+
// Consistency tests between recursive and iterative implementations
164+
25165
@Test
26-
void leonardoNumberTwenty() {
27-
assertEquals(21891, LeonardoNumber.leonardoNumber(20));
166+
void testConsistencyBetweenImplementations() {
167+
for (int i = 0; i <= 15; i++) {
168+
Assertions.assertEquals(LeonardoNumber.leonardoNumber(i), LeonardoNumber.leonardoNumberIterative(i), "Mismatch at index " + i);
169+
}
28170
}
29171
}

0 commit comments

Comments
 (0)