Skip to content
Open
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
85 changes: 78 additions & 7 deletions src/main/java/com/thealgorithms/others/HappyNumbersSeq.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,52 @@
import java.util.Scanner;
import java.util.Set;

/**
* A utility class for working with Happy Numbers.
*
* <p>
* A Happy Number is defined by the following process:
* Starting with any positive integer, replace the number by the sum of the
* squares of its digits.
* Repeat the process until the number equals 1 (where it will stay), or it
* loops endlessly in a
* cycle which does not include 1.
* Those numbers for which this process ends in 1 are happy numbers, while those
* that do not end
* in 1 are unhappy (or sad) numbers.
*
* <p>
* For example:
* <ul>
* <li>7 is a happy number: 7 → 49 → 97 → 130 → 10 → 1</li>
* <li>2 is not a happy number (sad number): 2 → 4 → 16 → 37 → 58 → 89 → 145 →
* 42 → 20 → 4 (cycle)</li>
* </ul>
*
* @see <a href="https://en.wikipedia.org/wiki/Happy_number">Happy Number -
* Wikipedia</a>
* @see <a href="https://mathworld.wolfram.com/HappyNumber.html">Happy Number -
* Wolfram MathWorld</a>
*/
public final class HappyNumbersSeq {
private HappyNumbersSeq() {
}

/**
* Known cycle numbers that indicate a sad number.
* If the sequence reaches any of these numbers, it will cycle indefinitely
* without reaching 1.
*/
private static final Set<Integer> CYCLE_NUMS = new HashSet<>(Arrays.asList(4, 16, 20, 37, 58, 145));

/**
* Main method to demonstrate happy number detection.
* Reads a number from user input and displays the sequence until it reaches 1
* (happy)
* or enters a cycle (sad).
*
* @param args command-line arguments (not used)
*/
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter number: ");
Expand All @@ -24,16 +64,47 @@ public static void main(String[] args) {
in.close();
}

private static int sumSquares(int n) {
int s = 0;
for (; n > 0; n /= 10) {
int r = n % 10;
s += r * r;
/**
* Determines if a number is a happy number.
*
* @param n the number to check (must be positive)
* @return {@code true} if the number is happy, {@code false} otherwise
* @throws IllegalArgumentException if n is not positive
*/
public static boolean isHappy(int n) {
if (n <= 0) {
throw new IllegalArgumentException("Number must be positive");
}
return s;
while (n != 1 && !isSad(n)) {
n = sumSquares(n);
}
return n == 1;
}

/**
* Computes the sum of the squares of the digits of a number.
*
* @param n the number whose digits will be squared and summed
* @return the sum of the squares of the digits
*/
static int sumSquares(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}

private static boolean isSad(int n) {
/**
* Checks if a number is part of the known cycle that indicates a sad number.
*
* @param n the number to check
* @return {@code true} if the number is in the sad cycle, {@code false}
* otherwise
*/
static boolean isSad(int n) {
return CYCLE_NUMS.contains(n);
}
}
179 changes: 179 additions & 0 deletions src/test/java/com/thealgorithms/others/HappyNumbersSeqTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package com.thealgorithms.others;

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

/**
* Test class for {@link HappyNumbersSeq}.
*
* @author Hardvan (https://github.com/Hardvan)
*/
class HappyNumbersSeqTest {

@Test
void testIsHappyWithHappyNumbers() {
// Test known happy numbers
assertTrue(HappyNumbersSeq.isHappy(1));
assertTrue(HappyNumbersSeq.isHappy(7));
assertTrue(HappyNumbersSeq.isHappy(10));
assertTrue(HappyNumbersSeq.isHappy(13));
assertTrue(HappyNumbersSeq.isHappy(19));
assertTrue(HappyNumbersSeq.isHappy(23));
assertTrue(HappyNumbersSeq.isHappy(28));
assertTrue(HappyNumbersSeq.isHappy(31));
assertTrue(HappyNumbersSeq.isHappy(32));
assertTrue(HappyNumbersSeq.isHappy(44));
assertTrue(HappyNumbersSeq.isHappy(49));
assertTrue(HappyNumbersSeq.isHappy(68));
assertTrue(HappyNumbersSeq.isHappy(70));
assertTrue(HappyNumbersSeq.isHappy(79));
assertTrue(HappyNumbersSeq.isHappy(82));
assertTrue(HappyNumbersSeq.isHappy(86));
assertTrue(HappyNumbersSeq.isHappy(91));
assertTrue(HappyNumbersSeq.isHappy(94));
assertTrue(HappyNumbersSeq.isHappy(97));
assertTrue(HappyNumbersSeq.isHappy(100));
}

@Test
void testIsHappyWithSadNumbers() {
// Test known sad numbers
assertFalse(HappyNumbersSeq.isHappy(2));
assertFalse(HappyNumbersSeq.isHappy(3));
assertFalse(HappyNumbersSeq.isHappy(4));
assertFalse(HappyNumbersSeq.isHappy(5));
assertFalse(HappyNumbersSeq.isHappy(6));
assertFalse(HappyNumbersSeq.isHappy(8));
assertFalse(HappyNumbersSeq.isHappy(9));
assertFalse(HappyNumbersSeq.isHappy(11));
assertFalse(HappyNumbersSeq.isHappy(12));
assertFalse(HappyNumbersSeq.isHappy(14));
assertFalse(HappyNumbersSeq.isHappy(15));
assertFalse(HappyNumbersSeq.isHappy(16));
assertFalse(HappyNumbersSeq.isHappy(17));
assertFalse(HappyNumbersSeq.isHappy(18));
assertFalse(HappyNumbersSeq.isHappy(20));
}

@Test
void testIsHappyWithLargeNumbers() {
// Test larger happy numbers
assertTrue(HappyNumbersSeq.isHappy(1000));
assertFalse(HappyNumbersSeq.isHappy(999));
assertFalse(HappyNumbersSeq.isHappy(1001));
}

@Test
void testIsHappyWithInvalidInput() {
// Test with zero
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(0));

// Test with negative numbers
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-1));
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-10));
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-100));
}

@Test
void testSumSquaresSingleDigit() {
assertEquals(0, HappyNumbersSeq.sumSquares(0));
assertEquals(1, HappyNumbersSeq.sumSquares(1));
assertEquals(4, HappyNumbersSeq.sumSquares(2));
assertEquals(9, HappyNumbersSeq.sumSquares(3));
assertEquals(16, HappyNumbersSeq.sumSquares(4));
assertEquals(25, HappyNumbersSeq.sumSquares(5));
assertEquals(36, HappyNumbersSeq.sumSquares(6));
assertEquals(49, HappyNumbersSeq.sumSquares(7));
assertEquals(64, HappyNumbersSeq.sumSquares(8));
assertEquals(81, HappyNumbersSeq.sumSquares(9));
}

@Test
void testSumSquaresMultipleDigits() {
// 10: 1^2 + 0^2 = 1
assertEquals(1, HappyNumbersSeq.sumSquares(10));

// 23: 2^2 + 3^2 = 4 + 9 = 13
assertEquals(13, HappyNumbersSeq.sumSquares(23));

// 82: 8^2 + 2^2 = 64 + 4 = 68
assertEquals(68, HappyNumbersSeq.sumSquares(82));

// 130: 1^2 + 3^2 + 0^2 = 1 + 9 + 0 = 10
assertEquals(10, HappyNumbersSeq.sumSquares(130));

// 999: 9^2 + 9^2 + 9^2 = 81 + 81 + 81 = 243
assertEquals(243, HappyNumbersSeq.sumSquares(999));
}

@Test
void testSumSquaresLargeNumbers() {
// 1234: 1^2 + 2^2 + 3^2 + 4^2 = 1 + 4 + 9 + 16 = 30
assertEquals(30, HappyNumbersSeq.sumSquares(1234));

// 9876: 9^2 + 8^2 + 7^2 + 6^2 = 81 + 64 + 49 + 36 = 230
assertEquals(230, HappyNumbersSeq.sumSquares(9876));
}

@Test
void testIsSadWithCycleNumbers() {
// Test all known cycle numbers
assertTrue(HappyNumbersSeq.isSad(4));
assertTrue(HappyNumbersSeq.isSad(16));
assertTrue(HappyNumbersSeq.isSad(20));
assertTrue(HappyNumbersSeq.isSad(37));
assertTrue(HappyNumbersSeq.isSad(58));
assertTrue(HappyNumbersSeq.isSad(145));
}

@Test
void testIsSadWithNonCycleNumbers() {
// Test numbers that are not in the cycle
assertFalse(HappyNumbersSeq.isSad(1));
assertFalse(HappyNumbersSeq.isSad(7));
assertFalse(HappyNumbersSeq.isSad(10));
assertFalse(HappyNumbersSeq.isSad(13));
assertFalse(HappyNumbersSeq.isSad(19));
assertFalse(HappyNumbersSeq.isSad(23));
}

@Test
void testHappyNumberSequenceFor7() {
// Test the sequence for happy number 7: 7 → 49 → 97 → 130 → 10 → 1
int n = 7;
assertEquals(49, HappyNumbersSeq.sumSquares(n));
n = 49;
assertEquals(97, HappyNumbersSeq.sumSquares(n));
n = 97;
assertEquals(130, HappyNumbersSeq.sumSquares(n));
n = 130;
assertEquals(10, HappyNumbersSeq.sumSquares(n));
n = 10;
assertEquals(1, HappyNumbersSeq.sumSquares(n));
}

@Test
void testHappyNumberSequenceFor19() {
// Test the sequence for happy number 19: 19 → 82 → 68 → 100 → 1
int n = 19;
assertEquals(82, HappyNumbersSeq.sumSquares(n));
n = 82;
assertEquals(68, HappyNumbersSeq.sumSquares(n));
n = 68;
assertEquals(100, HappyNumbersSeq.sumSquares(n));
n = 100;
assertEquals(1, HappyNumbersSeq.sumSquares(n));
}

@Test
void testSadNumberEntersCycle() {
// Test that sad number 2 eventually reaches a cycle number
int n = 2;
assertEquals(4, HappyNumbersSeq.sumSquares(n)); // 2 → 4 (cycle number)
assertTrue(HappyNumbersSeq.isSad(4));
}
}