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
107 changes: 74 additions & 33 deletions src/main/java/com/thealgorithms/maths/KeithNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,98 @@
import java.util.Collections;
import java.util.Scanner;

final class KeithNumber {
/**
* A Keith number is an n-digit positive integer where the sequence formed by
* starting with its digits and repeatedly adding the previous n terms,
* eventually reaches the number itself.
*
* <p>
* For example:
* <ul>
* <li>14 is a Keith number: 1, 4, 5, 9, 14</li>
* <li>19 is a Keith number: 1, 9, 10, 19</li>
* <li>28 is a Keith number: 2, 8, 10, 18, 28</li>
* <li>197 is a Keith number: 1, 9, 7, 17, 33, 57, 107, 197</li>
* </ul>
*
* @see <a href="https://en.wikipedia.org/wiki/Keith_number">Keith Number -
* Wikipedia</a>
* @see <a href="https://mathworld.wolfram.com/KeithNumber.html">Keith Number -
* MathWorld</a>
*/
public final class KeithNumber {
private KeithNumber() {
}

// user-defined function that checks if the given number is Keith or not
static boolean isKeith(int x) {
// List stores all the digits of the X
/**
* Checks if a given number is a Keith number.
*
* <p>
* The algorithm works as follows:
* <ol>
* <li>Extract all digits of the number and store them in a list</li>
* <li>Generate subsequent terms by summing the last n digits</li>
* <li>Continue until a term equals or exceeds the original number</li>
* <li>If a term equals the number, it is a Keith number</li>
* </ol>
*
* @param number the number to check (must be positive)
* @return {@code true} if the number is a Keith number, {@code false} otherwise
* @throws IllegalArgumentException if the number is not positive
*/
public static boolean isKeith(int number) {
if (number <= 0) {
throw new IllegalArgumentException("Number must be positive");
}

// Extract digits and store them in the list
ArrayList<Integer> terms = new ArrayList<>();
// n denotes the number of digits
int temp = x;
int n = 0;
// executes until the condition becomes false
int temp = number;
int digitCount = 0;

while (temp > 0) {
// determines the last digit of the number and add it to the List
terms.add(temp % 10);
// removes the last digit
temp = temp / 10;
// increments the number of digits (n) by 1
n++;
digitCount++;
}
// reverse the List

// Reverse the list to get digits in correct order
Collections.reverse(terms);

// Generate subsequent terms in the sequence
int nextTerm = 0;
int i = n;
// finds next term for the series
// loop executes until the condition returns true
while (nextTerm < x) {
int currentIndex = digitCount;

while (nextTerm < number) {
nextTerm = 0;
// next term is the sum of previous n terms (it depends on number of digits the number
// has)
for (int j = 1; j <= n; j++) {
nextTerm = nextTerm + terms.get(i - j);
// Sum the last 'digitCount' terms
for (int j = 1; j <= digitCount; j++) {
nextTerm = nextTerm + terms.get(currentIndex - j);
}
terms.add(nextTerm);
i++;
currentIndex++;
}
// when the control comes out of the while loop, there will be two conditions:
// either nextTerm will be equal to x or greater than x
// if equal, the given number is Keith, else not
return (nextTerm == x);

// Check if the generated term equals the original number
return (nextTerm == number);
}

// driver code
/**
* Main method for demonstrating Keith number detection.
* Reads a number from standard input and checks if it is a Keith number.
*
* @param args command line arguments (not used)
*/
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
if (isKeith(n)) {
System.out.println("Yes, the given number is a Keith number.");
Scanner scanner = new Scanner(System.in);
System.out.print("Enter a positive integer: ");
int number = scanner.nextInt();

if (isKeith(number)) {
System.out.println("Yes, " + number + " is a Keith number.");
} else {
System.out.println("No, the given number is not a Keith number.");
System.out.println("No, " + number + " is not a Keith number.");
}
in.close();
scanner.close();
}
}
153 changes: 153 additions & 0 deletions src/test/java/com/thealgorithms/maths/KeithNumberTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.thealgorithms.maths;

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 org.junit.jupiter.api.Test;

/**
* Test cases for {@link KeithNumber}.
*/
class KeithNumberTest {

/**
* Tests single-digit Keith numbers.
* All single-digit numbers (1-9) are Keith numbers by definition.
*/
@Test
void testSingleDigitKeithNumbers() {
assertTrue(KeithNumber.isKeith(1));
assertTrue(KeithNumber.isKeith(2));
assertTrue(KeithNumber.isKeith(3));
assertTrue(KeithNumber.isKeith(4));
assertTrue(KeithNumber.isKeith(5));
assertTrue(KeithNumber.isKeith(6));
assertTrue(KeithNumber.isKeith(7));
assertTrue(KeithNumber.isKeith(8));
assertTrue(KeithNumber.isKeith(9));
}

/**
* Tests two-digit Keith numbers.
* Known two-digit Keith numbers: 14, 19, 28, 47, 61, 75.
*/
@Test
void testTwoDigitKeithNumbers() {
assertTrue(KeithNumber.isKeith(14));
assertTrue(KeithNumber.isKeith(19));
assertTrue(KeithNumber.isKeith(28));
assertTrue(KeithNumber.isKeith(47));
assertTrue(KeithNumber.isKeith(61));
assertTrue(KeithNumber.isKeith(75));
}

/**
* Tests three-digit Keith numbers.
* Known three-digit Keith numbers: 197, 742.
*/
@Test
void testThreeDigitKeithNumbers() {
assertTrue(KeithNumber.isKeith(197));
assertTrue(KeithNumber.isKeith(742));
}

/**
* Tests four-digit Keith numbers.
* Known four-digit Keith numbers: 1104, 1537, 2208, 2580, 3684, 4788, 7385,
* 7647, 7909.
*/
@Test
void testFourDigitKeithNumbers() {
assertTrue(KeithNumber.isKeith(1104));
assertTrue(KeithNumber.isKeith(1537));
assertTrue(KeithNumber.isKeith(2208));
}

/**
* Tests two-digit non-Keith numbers.
*/
@Test
void testTwoDigitNonKeithNumbers() {
assertFalse(KeithNumber.isKeith(10));
assertFalse(KeithNumber.isKeith(11));
assertFalse(KeithNumber.isKeith(12));
assertFalse(KeithNumber.isKeith(13));
assertFalse(KeithNumber.isKeith(15));
assertFalse(KeithNumber.isKeith(20));
assertFalse(KeithNumber.isKeith(30));
assertFalse(KeithNumber.isKeith(50));
}

/**
* Tests three-digit non-Keith numbers.
*/
@Test
void testThreeDigitNonKeithNumbers() {
assertFalse(KeithNumber.isKeith(100));
assertFalse(KeithNumber.isKeith(123));
assertFalse(KeithNumber.isKeith(196));
assertFalse(KeithNumber.isKeith(198));
assertFalse(KeithNumber.isKeith(456));
assertFalse(KeithNumber.isKeith(741));
assertFalse(KeithNumber.isKeith(743));
assertFalse(KeithNumber.isKeith(999));
}

/**
* Tests validation for edge case 14 in detail.
* 14 is a Keith number: 1, 4, 5 (1+4), 9 (4+5), 14 (5+9).
*/
@Test
void testKeithNumber14() {
assertTrue(KeithNumber.isKeith(14));
}

/**
* Tests validation for edge case 197 in detail.
* 197 is a Keith number: 1, 9, 7, 17, 33, 57, 107, 197.
*/
@Test
void testKeithNumber197() {
assertTrue(KeithNumber.isKeith(197));
}

/**
* Tests that zero throws an IllegalArgumentException.
*/
@Test
void testZeroThrowsException() {
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(0));
}

/**
* Tests that negative numbers throw an IllegalArgumentException.
*/
@Test
void testNegativeNumbersThrowException() {
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-1));
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-14));
assertThrows(IllegalArgumentException.class, () -> KeithNumber.isKeith(-100));
}

/**
* Tests various edge cases with larger numbers.
*/
@Test
void testLargerNumbers() {
assertTrue(KeithNumber.isKeith(2208));
assertFalse(KeithNumber.isKeith(2207));
assertFalse(KeithNumber.isKeith(2209));
}

/**
* Tests the expected behavior with all two-digit Keith numbers.
*/
@Test
void testAllKnownTwoDigitKeithNumbers() {
int[] knownKeithNumbers = {14, 19, 28, 47, 61, 75};
for (int number : knownKeithNumbers) {
assertTrue(KeithNumber.isKeith(number), "Expected " + number + " to be a Keith number");
}
}
}