From e78091b647c16f44e7944e5c7d1cf2a69f039f7d Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 12:40:15 +0530 Subject: [PATCH] refactor: Enhance docs, code, add tests in `KaprekarNumbers` --- .../thealgorithms/maths/KaprekarNumbers.java | 114 ++++++++--- .../maths/KaprekarNumbersTest.java | 185 ++++++++++++++---- 2 files changed, 233 insertions(+), 66 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java b/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java index eb9750f9ce08..99842e2f4f5e 100644 --- a/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java +++ b/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java @@ -4,23 +4,51 @@ import java.util.ArrayList; import java.util.List; +/** + * Utility class for identifying and working with Kaprekar Numbers. + *

+ * 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. + *

+ * For example: + *

+ *

+ * Note: The right part can have leading zeros, but must not be all zeros. + * + * @see Kaprekar Number + * - Wikipedia + * @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 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 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 list = new ArrayList<>(); + ArrayList list = new ArrayList<>(); for (long i = start; i <= end; i++) { if (isKaprekarNumber(i)) { list.add(i); @@ -30,24 +58,60 @@ public static List 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. + *

+ * The algorithm works as follows: + *

    + *
  1. Square the number
  2. + *
  3. Split the squared number into two parts: left and right
  4. + *
  5. The right part has the same number of digits as the original number
  6. + *
  7. Add the left and right parts
  8. + *
  9. If the sum equals the original number, it's a Kaprekar number
  10. + *
+ *

+ * 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); } } diff --git a/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java b/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java index 05e58cf88e22..a3cd7500b30c 100644 --- a/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java +++ b/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java @@ -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 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 result = KaprekarNumbers.kaprekarNumberInRange(1, 10); + List expected = Arrays.asList(1L, 9L); + assertEquals(expected, result); + } + + @Test + void testKaprekarNumbersInMediumRange() { + List result = KaprekarNumbers.kaprekarNumberInRange(1, 100); + List expected = Arrays.asList(1L, 9L, 45L, 55L, 99L); + assertEquals(expected, result); + } + + @Test + void testKaprekarNumbersInLargeRange() { + List rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000); + List 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 result = KaprekarNumbers.kaprekarNumberInRange(9, 9); + List expected = Arrays.asList(9L); + assertEquals(expected, result); + } + + @Test + void testKaprekarNumbersInRangeWithNoKaprekarNumbers() { + List result = KaprekarNumbers.kaprekarNumberInRange(2, 8); + assertTrue(result.isEmpty()); + } + + @Test + void testKaprekarNumbersInRangeStartingFromZero() { + List result = KaprekarNumbers.kaprekarNumberInRange(0, 5); + List 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 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)); } }