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
114 changes: 89 additions & 25 deletions src/main/java/com/thealgorithms/maths/KaprekarNumbers.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,51 @@
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);
Expand All @@ -30,24 +58,60 @@ public static List<Long> kaprekarNumberInRange(long start, long end) throws Exce
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);
}
}
185 changes: 144 additions & 41 deletions src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java
Original file line number Diff line number Diff line change
@@ -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));
}
}