Skip to content

Commit 297634d

Browse files
Hardvanalxkm
andauthored
refactor: Enhance docs, code, add tests in KaprekarNumbers (TheAlgorithms#6747)
Co-authored-by: a <alexanderklmn@gmail.com>
1 parent ff9fd2e commit 297634d

File tree

2 files changed

+233
-66
lines changed

2 files changed

+233
-66
lines changed

src/main/java/com/thealgorithms/maths/KaprekarNumbers.java

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,51 @@
44
import java.util.ArrayList;
55
import java.util.List;
66

7+
/**
8+
* Utility class for identifying and working with Kaprekar Numbers.
9+
* <p>
10+
* A Kaprekar number is a positive integer with the following property:
11+
* If you square it, then split the resulting number into two parts (right part
12+
* has same number of
13+
* digits as the original number, left part has the remaining digits), and
14+
* finally add the two
15+
* parts together, you get the original number.
16+
* <p>
17+
* For example:
18+
* <ul>
19+
* <li>9: 9² = 81 → 8 + 1 = 9 (Kaprekar number)</li>
20+
* <li>45: 45² = 2025 → 20 + 25 = 45 (Kaprekar number)</li>
21+
* <li>297: 297² = 88209 → 88 + 209 = 297 (Kaprekar number)</li>
22+
* </ul>
23+
* <p>
24+
* Note: The right part can have leading zeros, but must not be all zeros.
25+
*
26+
* @see <a href="https://en.wikipedia.org/wiki/Kaprekar_number">Kaprekar Number
27+
* - Wikipedia</a>
28+
* @author TheAlgorithms (https://github.com/TheAlgorithms)
29+
*/
730
public final class KaprekarNumbers {
831
private KaprekarNumbers() {
932
}
1033

11-
/* This program demonstrates if a given number is Kaprekar Number or not.
12-
Kaprekar Number: A Kaprekar number is an n-digit number which its square can be split into
13-
two parts where the right part has n digits and sum of these parts is equal to the original
14-
number. */
15-
16-
// Provides a list of kaprekarNumber in a range
17-
public static List<Long> kaprekarNumberInRange(long start, long end) throws Exception {
18-
long n = end - start;
19-
if (n < 0) {
20-
throw new Exception("Invalid range");
34+
/**
35+
* Finds all Kaprekar numbers within a given range (inclusive).
36+
*
37+
* @param start the starting number of the range (inclusive)
38+
* @param end the ending number of the range (inclusive)
39+
* @return a list of all Kaprekar numbers in the specified range
40+
* @throws IllegalArgumentException if start is greater than end or if start is
41+
* negative
42+
*/
43+
public static List<Long> kaprekarNumberInRange(long start, long end) {
44+
if (start > end) {
45+
throw new IllegalArgumentException("Start must be less than or equal to end. Given start: " + start + ", end: " + end);
46+
}
47+
if (start < 0) {
48+
throw new IllegalArgumentException("Start must be non-negative. Given start: " + start);
2149
}
22-
ArrayList<Long> list = new ArrayList<>();
2350

51+
ArrayList<Long> list = new ArrayList<>();
2452
for (long i = start; i <= end; i++) {
2553
if (isKaprekarNumber(i)) {
2654
list.add(i);
@@ -30,24 +58,60 @@ public static List<Long> kaprekarNumberInRange(long start, long end) throws Exce
3058
return list;
3159
}
3260

33-
// Checks whether a given number is Kaprekar Number or not
61+
/**
62+
* Checks whether a given number is a Kaprekar number.
63+
* <p>
64+
* The algorithm works as follows:
65+
* <ol>
66+
* <li>Square the number</li>
67+
* <li>Split the squared number into two parts: left and right</li>
68+
* <li>The right part has the same number of digits as the original number</li>
69+
* <li>Add the left and right parts</li>
70+
* <li>If the sum equals the original number, it's a Kaprekar number</li>
71+
* </ol>
72+
* <p>
73+
* Special handling is required for numbers whose squares contain zeros.
74+
*
75+
* @param num the number to check
76+
* @return true if the number is a Kaprekar number, false otherwise
77+
* @throws IllegalArgumentException if num is negative
78+
*/
3479
public static boolean isKaprekarNumber(long num) {
80+
if (num < 0) {
81+
throw new IllegalArgumentException("Number must be non-negative. Given: " + num);
82+
}
83+
84+
if (num == 0 || num == 1) {
85+
return true;
86+
}
87+
3588
String number = Long.toString(num);
3689
BigInteger originalNumber = BigInteger.valueOf(num);
3790
BigInteger numberSquared = originalNumber.multiply(originalNumber);
38-
if (number.length() == numberSquared.toString().length()) {
39-
return number.equals(numberSquared.toString());
40-
} else {
41-
BigInteger leftDigits1 = BigInteger.ZERO;
42-
BigInteger leftDigits2;
43-
if (numberSquared.toString().contains("0")) {
44-
leftDigits1 = new BigInteger(numberSquared.toString().substring(0, numberSquared.toString().indexOf("0")));
45-
}
46-
leftDigits2 = new BigInteger(numberSquared.toString().substring(0, (numberSquared.toString().length() - number.length())));
47-
BigInteger rightDigits = new BigInteger(numberSquared.toString().substring(numberSquared.toString().length() - number.length()));
48-
String x = leftDigits1.add(rightDigits).toString();
49-
String y = leftDigits2.add(rightDigits).toString();
50-
return (number.equals(x)) || (number.equals(y));
91+
String squaredStr = numberSquared.toString();
92+
93+
// Special case: if the squared number has the same length as the original
94+
if (number.length() == squaredStr.length()) {
95+
return number.equals(squaredStr);
96+
}
97+
98+
// Calculate the split position
99+
int splitPos = squaredStr.length() - number.length();
100+
101+
// Split the squared number into left and right parts
102+
String leftPart = squaredStr.substring(0, splitPos);
103+
String rightPart = squaredStr.substring(splitPos);
104+
105+
// Parse the parts as BigInteger (handles empty left part as zero)
106+
BigInteger leftNum = leftPart.isEmpty() ? BigInteger.ZERO : new BigInteger(leftPart);
107+
BigInteger rightNum = new BigInteger(rightPart);
108+
109+
// Check if right part is all zeros (invalid for Kaprekar numbers except 1)
110+
if (rightNum.equals(BigInteger.ZERO)) {
111+
return false;
51112
}
113+
114+
// Check if the sum equals the original number
115+
return leftNum.add(rightNum).equals(originalNumber);
52116
}
53117
}
Lines changed: 144 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,188 @@
11
package com.thealgorithms.maths;
22

3+
import static org.junit.jupiter.api.Assertions.assertEquals;
34
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
46
import static org.junit.jupiter.api.Assertions.assertTrue;
57

8+
import java.util.Arrays;
69
import java.util.List;
710
import org.junit.jupiter.api.Test;
811

9-
public class KaprekarNumbersTest {
12+
/**
13+
* Test class for {@link KaprekarNumbers}.
14+
* Tests various Kaprekar numbers and edge cases to ensure full coverage.
15+
*/
16+
class KaprekarNumbersTest {
1017

1118
@Test
12-
void testFor1() {
19+
void testZeroIsKaprekarNumber() {
20+
assertTrue(KaprekarNumbers.isKaprekarNumber(0));
21+
}
22+
23+
@Test
24+
void testOneIsKaprekarNumber() {
1325
assertTrue(KaprekarNumbers.isKaprekarNumber(1));
1426
}
1527

1628
@Test
17-
void testFor45() {
29+
void testNineIsKaprekarNumber() {
30+
// 9^2 = 81, 8 + 1 = 9
31+
assertTrue(KaprekarNumbers.isKaprekarNumber(9));
32+
}
33+
34+
@Test
35+
void testFortyFiveIsKaprekarNumber() {
36+
// 45^2 = 2025, 20 + 25 = 45
1837
assertTrue(KaprekarNumbers.isKaprekarNumber(45));
1938
}
2039

2140
@Test
22-
void testFor297() {
41+
void testFiftyFiveIsKaprekarNumber() {
42+
// 55^2 = 3025, 30 + 25 = 55
43+
assertTrue(KaprekarNumbers.isKaprekarNumber(55));
44+
}
45+
46+
@Test
47+
void testNinetyNineIsKaprekarNumber() {
48+
// 99^2 = 9801, 98 + 01 = 99
49+
assertTrue(KaprekarNumbers.isKaprekarNumber(99));
50+
}
51+
52+
@Test
53+
void testTwoNinetySevenIsKaprekarNumber() {
54+
// 297^2 = 88209, 88 + 209 = 297
2355
assertTrue(KaprekarNumbers.isKaprekarNumber(297));
2456
}
2557

2658
@Test
27-
void testFor2223() {
59+
void testSevenZeroThreeIsKaprekarNumber() {
60+
// 703^2 = 494209, 494 + 209 = 703
61+
assertTrue(KaprekarNumbers.isKaprekarNumber(703));
62+
}
63+
64+
@Test
65+
void testNineNineNineIsKaprekarNumber() {
66+
// 999^2 = 998001, 998 + 001 = 999
67+
assertTrue(KaprekarNumbers.isKaprekarNumber(999));
68+
}
69+
70+
@Test
71+
void testTwoTwoTwoThreeIsKaprekarNumber() {
72+
// 2223^2 = 4941729, 4941 + 729 = 5670 (not directly obvious)
73+
// Actually: 494 + 1729 = 2223
2874
assertTrue(KaprekarNumbers.isKaprekarNumber(2223));
2975
}
3076

3177
@Test
32-
void testFor857143() {
78+
void testEightFiveSevenOneFortyThreeIsKaprekarNumber() {
3379
assertTrue(KaprekarNumbers.isKaprekarNumber(857143));
3480
}
3581

3682
@Test
37-
void testFor3() {
83+
void testTwoIsNotKaprekarNumber() {
84+
assertFalse(KaprekarNumbers.isKaprekarNumber(2));
85+
}
86+
87+
@Test
88+
void testThreeIsNotKaprekarNumber() {
3889
assertFalse(KaprekarNumbers.isKaprekarNumber(3));
3990
}
4091

4192
@Test
42-
void testFor26() {
93+
void testTenIsNotKaprekarNumber() {
94+
assertFalse(KaprekarNumbers.isKaprekarNumber(10));
95+
}
96+
97+
@Test
98+
void testTwentySixIsNotKaprekarNumber() {
4399
assertFalse(KaprekarNumbers.isKaprekarNumber(26));
44100
}
45101

46102
@Test
47-
void testFor98() {
103+
void testNinetyEightIsNotKaprekarNumber() {
48104
assertFalse(KaprekarNumbers.isKaprekarNumber(98));
49105
}
50106

51107
@Test
52-
void testForRangeOfNumber() {
53-
try {
54-
List<Long> rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000);
55-
long[] allTheNumbers = {
56-
1,
57-
9,
58-
45,
59-
55,
60-
99,
61-
297,
62-
703,
63-
999,
64-
2223,
65-
2728,
66-
4950,
67-
5050,
68-
7272,
69-
7777,
70-
9999,
71-
17344,
72-
22222,
73-
77778,
74-
82656,
75-
95121,
76-
99999,
77-
};
78-
for (long i : allTheNumbers) {
79-
assert rangedNumbers.contains(i);
80-
}
81-
} catch (Exception e) {
82-
assert false;
83-
}
108+
void testOneHundredIsNotKaprekarNumber() {
109+
assertFalse(KaprekarNumbers.isKaprekarNumber(100));
110+
}
111+
112+
@Test
113+
void testNegativeNumberThrowsException() {
114+
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.isKaprekarNumber(-5));
115+
}
116+
117+
@Test
118+
void testKaprekarNumbersInSmallRange() {
119+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(1, 10);
120+
List<Long> expected = Arrays.asList(1L, 9L);
121+
assertEquals(expected, result);
122+
}
123+
124+
@Test
125+
void testKaprekarNumbersInMediumRange() {
126+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(1, 100);
127+
List<Long> expected = Arrays.asList(1L, 9L, 45L, 55L, 99L);
128+
assertEquals(expected, result);
129+
}
130+
131+
@Test
132+
void testKaprekarNumbersInLargeRange() {
133+
List<Long> rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000);
134+
List<Long> expectedNumbers = Arrays.asList(1L, 9L, 45L, 55L, 99L, 297L, 703L, 999L, 2223L, 2728L, 4950L, 5050L, 7272L, 7777L, 9999L, 17344L, 22222L, 77778L, 82656L, 95121L, 99999L);
135+
assertEquals(expectedNumbers, rangedNumbers);
136+
}
137+
138+
@Test
139+
void testKaprekarNumbersInSingleElementRange() {
140+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(9, 9);
141+
List<Long> expected = Arrays.asList(9L);
142+
assertEquals(expected, result);
143+
}
144+
145+
@Test
146+
void testKaprekarNumbersInRangeWithNoKaprekarNumbers() {
147+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(2, 8);
148+
assertTrue(result.isEmpty());
149+
}
150+
151+
@Test
152+
void testKaprekarNumbersInRangeStartingFromZero() {
153+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(0, 5);
154+
List<Long> expected = Arrays.asList(0L, 1L);
155+
assertEquals(expected, result);
156+
}
157+
158+
@Test
159+
void testInvalidRangeThrowsException() {
160+
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.kaprekarNumberInRange(100, 50));
161+
}
162+
163+
@Test
164+
void testNegativeStartThrowsException() {
165+
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.kaprekarNumberInRange(-10, 100));
166+
}
167+
168+
@Test
169+
void testEmptyRange() {
170+
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(10, 44);
171+
assertTrue(result.isEmpty());
172+
}
173+
174+
@Test
175+
void testLargeKaprekarNumber() {
176+
// Test a larger known Kaprekar number
177+
assertTrue(KaprekarNumbers.isKaprekarNumber(142857));
178+
}
179+
180+
@Test
181+
void testFourDigitKaprekarNumbers() {
182+
// Test some 4-digit Kaprekar numbers
183+
assertTrue(KaprekarNumbers.isKaprekarNumber(2728));
184+
assertTrue(KaprekarNumbers.isKaprekarNumber(4950));
185+
assertTrue(KaprekarNumbers.isKaprekarNumber(5050));
186+
assertTrue(KaprekarNumbers.isKaprekarNumber(7272));
84187
}
85188
}

0 commit comments

Comments
 (0)