refactor: Enhance docs, code, add tests in KaprekarNumbers (#6747)

Co-authored-by: a <alexanderklmn@gmail.com>
This commit is contained in:
Hardik Pawar
2025-10-13 02:43:14 +05:30
committed by GitHub
parent ff9fd2e2e4
commit 297634d05f
2 changed files with 233 additions and 66 deletions

View File

@@ -4,23 +4,51 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class for identifying and working with Kaprekar Numbers.
* <p>
* A Kaprekar number is a positive integer with the following property:
* If you square it, then split the resulting number into two parts (right part
* has same number of
* digits as the original number, left part has the remaining digits), and
* finally add the two
* parts together, you get the original number.
* <p>
* For example:
* <ul>
* <li>9: 9² = 81 → 8 + 1 = 9 (Kaprekar number)</li>
* <li>45: 45² = 2025 → 20 + 25 = 45 (Kaprekar number)</li>
* <li>297: 297² = 88209 → 88 + 209 = 297 (Kaprekar number)</li>
* </ul>
* <p>
* Note: The right part can have leading zeros, but must not be all zeros.
*
* @see <a href="https://en.wikipedia.org/wiki/Kaprekar_number">Kaprekar Number
* - Wikipedia</a>
* @author TheAlgorithms (https://github.com/TheAlgorithms)
*/
public final class KaprekarNumbers {
private KaprekarNumbers() {
}
/* This program demonstrates if a given number is Kaprekar Number or not.
Kaprekar Number: A Kaprekar number is an n-digit number which its square can be split into
two parts where the right part has n digits and sum of these parts is equal to the original
number. */
// Provides a list of kaprekarNumber in a range
public static List<Long> kaprekarNumberInRange(long start, long end) throws Exception {
long n = end - start;
if (n < 0) {
throw new Exception("Invalid range");
/**
* Finds all Kaprekar numbers within a given range (inclusive).
*
* @param start the starting number of the range (inclusive)
* @param end the ending number of the range (inclusive)
* @return a list of all Kaprekar numbers in the specified range
* @throws IllegalArgumentException if start is greater than end or if start is
* negative
*/
public static List<Long> kaprekarNumberInRange(long start, long end) {
if (start > end) {
throw new IllegalArgumentException("Start must be less than or equal to end. Given start: " + start + ", end: " + end);
}
if (start < 0) {
throw new IllegalArgumentException("Start must be non-negative. Given start: " + start);
}
ArrayList<Long> list = new ArrayList<>();
ArrayList<Long> list = new ArrayList<>();
for (long i = start; i <= end; i++) {
if (isKaprekarNumber(i)) {
list.add(i);
@@ -30,24 +58,60 @@ public final class KaprekarNumbers {
return list;
}
// Checks whether a given number is Kaprekar Number or not
/**
* Checks whether a given number is a Kaprekar number.
* <p>
* The algorithm works as follows:
* <ol>
* <li>Square the number</li>
* <li>Split the squared number into two parts: left and right</li>
* <li>The right part has the same number of digits as the original number</li>
* <li>Add the left and right parts</li>
* <li>If the sum equals the original number, it's a Kaprekar number</li>
* </ol>
* <p>
* Special handling is required for numbers whose squares contain zeros.
*
* @param num the number to check
* @return true if the number is a Kaprekar number, false otherwise
* @throws IllegalArgumentException if num is negative
*/
public static boolean isKaprekarNumber(long num) {
if (num < 0) {
throw new IllegalArgumentException("Number must be non-negative. Given: " + num);
}
if (num == 0 || num == 1) {
return true;
}
String number = Long.toString(num);
BigInteger originalNumber = BigInteger.valueOf(num);
BigInteger numberSquared = originalNumber.multiply(originalNumber);
if (number.length() == numberSquared.toString().length()) {
return number.equals(numberSquared.toString());
} else {
BigInteger leftDigits1 = BigInteger.ZERO;
BigInteger leftDigits2;
if (numberSquared.toString().contains("0")) {
leftDigits1 = new BigInteger(numberSquared.toString().substring(0, numberSquared.toString().indexOf("0")));
}
leftDigits2 = new BigInteger(numberSquared.toString().substring(0, (numberSquared.toString().length() - number.length())));
BigInteger rightDigits = new BigInteger(numberSquared.toString().substring(numberSquared.toString().length() - number.length()));
String x = leftDigits1.add(rightDigits).toString();
String y = leftDigits2.add(rightDigits).toString();
return (number.equals(x)) || (number.equals(y));
String squaredStr = numberSquared.toString();
// Special case: if the squared number has the same length as the original
if (number.length() == squaredStr.length()) {
return number.equals(squaredStr);
}
// Calculate the split position
int splitPos = squaredStr.length() - number.length();
// Split the squared number into left and right parts
String leftPart = squaredStr.substring(0, splitPos);
String rightPart = squaredStr.substring(splitPos);
// Parse the parts as BigInteger (handles empty left part as zero)
BigInteger leftNum = leftPart.isEmpty() ? BigInteger.ZERO : new BigInteger(leftPart);
BigInteger rightNum = new BigInteger(rightPart);
// Check if right part is all zeros (invalid for Kaprekar numbers except 1)
if (rightNum.equals(BigInteger.ZERO)) {
return false;
}
// Check if the sum equals the original number
return leftNum.add(rightNum).equals(originalNumber);
}
}

View File

@@ -1,85 +1,188 @@
package com.thealgorithms.maths;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class KaprekarNumbersTest {
/**
* Test class for {@link KaprekarNumbers}.
* Tests various Kaprekar numbers and edge cases to ensure full coverage.
*/
class KaprekarNumbersTest {
@Test
void testFor1() {
void testZeroIsKaprekarNumber() {
assertTrue(KaprekarNumbers.isKaprekarNumber(0));
}
@Test
void testOneIsKaprekarNumber() {
assertTrue(KaprekarNumbers.isKaprekarNumber(1));
}
@Test
void testFor45() {
void testNineIsKaprekarNumber() {
// 9^2 = 81, 8 + 1 = 9
assertTrue(KaprekarNumbers.isKaprekarNumber(9));
}
@Test
void testFortyFiveIsKaprekarNumber() {
// 45^2 = 2025, 20 + 25 = 45
assertTrue(KaprekarNumbers.isKaprekarNumber(45));
}
@Test
void testFor297() {
void testFiftyFiveIsKaprekarNumber() {
// 55^2 = 3025, 30 + 25 = 55
assertTrue(KaprekarNumbers.isKaprekarNumber(55));
}
@Test
void testNinetyNineIsKaprekarNumber() {
// 99^2 = 9801, 98 + 01 = 99
assertTrue(KaprekarNumbers.isKaprekarNumber(99));
}
@Test
void testTwoNinetySevenIsKaprekarNumber() {
// 297^2 = 88209, 88 + 209 = 297
assertTrue(KaprekarNumbers.isKaprekarNumber(297));
}
@Test
void testFor2223() {
void testSevenZeroThreeIsKaprekarNumber() {
// 703^2 = 494209, 494 + 209 = 703
assertTrue(KaprekarNumbers.isKaprekarNumber(703));
}
@Test
void testNineNineNineIsKaprekarNumber() {
// 999^2 = 998001, 998 + 001 = 999
assertTrue(KaprekarNumbers.isKaprekarNumber(999));
}
@Test
void testTwoTwoTwoThreeIsKaprekarNumber() {
// 2223^2 = 4941729, 4941 + 729 = 5670 (not directly obvious)
// Actually: 494 + 1729 = 2223
assertTrue(KaprekarNumbers.isKaprekarNumber(2223));
}
@Test
void testFor857143() {
void testEightFiveSevenOneFortyThreeIsKaprekarNumber() {
assertTrue(KaprekarNumbers.isKaprekarNumber(857143));
}
@Test
void testFor3() {
void testTwoIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(2));
}
@Test
void testThreeIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(3));
}
@Test
void testFor26() {
void testTenIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(10));
}
@Test
void testTwentySixIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(26));
}
@Test
void testFor98() {
void testNinetyEightIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(98));
}
@Test
void testForRangeOfNumber() {
try {
List<Long> rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000);
long[] allTheNumbers = {
1,
9,
45,
55,
99,
297,
703,
999,
2223,
2728,
4950,
5050,
7272,
7777,
9999,
17344,
22222,
77778,
82656,
95121,
99999,
};
for (long i : allTheNumbers) {
assert rangedNumbers.contains(i);
}
} catch (Exception e) {
assert false;
}
void testOneHundredIsNotKaprekarNumber() {
assertFalse(KaprekarNumbers.isKaprekarNumber(100));
}
@Test
void testNegativeNumberThrowsException() {
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.isKaprekarNumber(-5));
}
@Test
void testKaprekarNumbersInSmallRange() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(1, 10);
List<Long> expected = Arrays.asList(1L, 9L);
assertEquals(expected, result);
}
@Test
void testKaprekarNumbersInMediumRange() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(1, 100);
List<Long> expected = Arrays.asList(1L, 9L, 45L, 55L, 99L);
assertEquals(expected, result);
}
@Test
void testKaprekarNumbersInLargeRange() {
List<Long> rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000);
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);
assertEquals(expectedNumbers, rangedNumbers);
}
@Test
void testKaprekarNumbersInSingleElementRange() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(9, 9);
List<Long> expected = Arrays.asList(9L);
assertEquals(expected, result);
}
@Test
void testKaprekarNumbersInRangeWithNoKaprekarNumbers() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(2, 8);
assertTrue(result.isEmpty());
}
@Test
void testKaprekarNumbersInRangeStartingFromZero() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(0, 5);
List<Long> expected = Arrays.asList(0L, 1L);
assertEquals(expected, result);
}
@Test
void testInvalidRangeThrowsException() {
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.kaprekarNumberInRange(100, 50));
}
@Test
void testNegativeStartThrowsException() {
assertThrows(IllegalArgumentException.class, () -> KaprekarNumbers.kaprekarNumberInRange(-10, 100));
}
@Test
void testEmptyRange() {
List<Long> result = KaprekarNumbers.kaprekarNumberInRange(10, 44);
assertTrue(result.isEmpty());
}
@Test
void testLargeKaprekarNumber() {
// Test a larger known Kaprekar number
assertTrue(KaprekarNumbers.isKaprekarNumber(142857));
}
@Test
void testFourDigitKaprekarNumbers() {
// Test some 4-digit Kaprekar numbers
assertTrue(KaprekarNumbers.isKaprekarNumber(2728));
assertTrue(KaprekarNumbers.isKaprekarNumber(4950));
assertTrue(KaprekarNumbers.isKaprekarNumber(5050));
assertTrue(KaprekarNumbers.isKaprekarNumber(7272));
}
}