Skip to content

Commit 9e8afb4

Browse files
committed
refactor: Enhance docs, code, add tests in HappyNumbersSeq
1 parent f55e221 commit 9e8afb4

File tree

2 files changed

+257
-7
lines changed

2 files changed

+257
-7
lines changed

src/main/java/com/thealgorithms/others/HappyNumbersSeq.java

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,52 @@
55
import java.util.Scanner;
66
import java.util.Set;
77

8+
/**
9+
* A utility class for working with Happy Numbers.
10+
*
11+
* <p>
12+
* A Happy Number is defined by the following process:
13+
* Starting with any positive integer, replace the number by the sum of the
14+
* squares of its digits.
15+
* Repeat the process until the number equals 1 (where it will stay), or it
16+
* loops endlessly in a
17+
* cycle which does not include 1.
18+
* Those numbers for which this process ends in 1 are happy numbers, while those
19+
* that do not end
20+
* in 1 are unhappy (or sad) numbers.
21+
*
22+
* <p>
23+
* For example:
24+
* <ul>
25+
* <li>7 is a happy number: 7 → 49 → 97 → 130 → 10 → 1</li>
26+
* <li>2 is not a happy number (sad number): 2 → 4 → 16 → 37 → 58 → 89 → 145 →
27+
* 42 → 20 → 4 (cycle)</li>
28+
* </ul>
29+
*
30+
* @see <a href="https://en.wikipedia.org/wiki/Happy_number">Happy Number -
31+
* Wikipedia</a>
32+
* @see <a href="https://mathworld.wolfram.com/HappyNumber.html">Happy Number -
33+
* Wolfram MathWorld</a>
34+
*/
835
public final class HappyNumbersSeq {
936
private HappyNumbersSeq() {
1037
}
1138

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

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

27-
private static int sumSquares(int n) {
28-
int s = 0;
29-
for (; n > 0; n /= 10) {
30-
int r = n % 10;
31-
s += r * r;
67+
/**
68+
* Determines if a number is a happy number.
69+
*
70+
* @param n the number to check (must be positive)
71+
* @return {@code true} if the number is happy, {@code false} otherwise
72+
* @throws IllegalArgumentException if n is not positive
73+
*/
74+
public static boolean isHappy(int n) {
75+
if (n <= 0) {
76+
throw new IllegalArgumentException("Number must be positive");
3277
}
33-
return s;
78+
while (n != 1 && !isSad(n)) {
79+
n = sumSquares(n);
80+
}
81+
return n == 1;
82+
}
83+
84+
/**
85+
* Computes the sum of the squares of the digits of a number.
86+
*
87+
* @param n the number whose digits will be squared and summed
88+
* @return the sum of the squares of the digits
89+
*/
90+
static int sumSquares(int n) {
91+
int sum = 0;
92+
while (n > 0) {
93+
int digit = n % 10;
94+
sum += digit * digit;
95+
n /= 10;
96+
}
97+
return sum;
3498
}
3599

36-
private static boolean isSad(int n) {
100+
/**
101+
* Checks if a number is part of the known cycle that indicates a sad number.
102+
*
103+
* @param n the number to check
104+
* @return {@code true} if the number is in the sad cycle, {@code false}
105+
* otherwise
106+
*/
107+
static boolean isSad(int n) {
37108
return CYCLE_NUMS.contains(n);
38109
}
39110
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package com.thealgorithms.others;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
/**
11+
* Test class for {@link HappyNumbersSeq}.
12+
*
13+
* @author Hardvan (https://github.com/Hardvan)
14+
*/
15+
class HappyNumbersSeqTest {
16+
17+
@Test
18+
void testIsHappyWithHappyNumbers() {
19+
// Test known happy numbers
20+
assertTrue(HappyNumbersSeq.isHappy(1));
21+
assertTrue(HappyNumbersSeq.isHappy(7));
22+
assertTrue(HappyNumbersSeq.isHappy(10));
23+
assertTrue(HappyNumbersSeq.isHappy(13));
24+
assertTrue(HappyNumbersSeq.isHappy(19));
25+
assertTrue(HappyNumbersSeq.isHappy(23));
26+
assertTrue(HappyNumbersSeq.isHappy(28));
27+
assertTrue(HappyNumbersSeq.isHappy(31));
28+
assertTrue(HappyNumbersSeq.isHappy(32));
29+
assertTrue(HappyNumbersSeq.isHappy(44));
30+
assertTrue(HappyNumbersSeq.isHappy(49));
31+
assertTrue(HappyNumbersSeq.isHappy(68));
32+
assertTrue(HappyNumbersSeq.isHappy(70));
33+
assertTrue(HappyNumbersSeq.isHappy(79));
34+
assertTrue(HappyNumbersSeq.isHappy(82));
35+
assertTrue(HappyNumbersSeq.isHappy(86));
36+
assertTrue(HappyNumbersSeq.isHappy(91));
37+
assertTrue(HappyNumbersSeq.isHappy(94));
38+
assertTrue(HappyNumbersSeq.isHappy(97));
39+
assertTrue(HappyNumbersSeq.isHappy(100));
40+
}
41+
42+
@Test
43+
void testIsHappyWithSadNumbers() {
44+
// Test known sad numbers
45+
assertFalse(HappyNumbersSeq.isHappy(2));
46+
assertFalse(HappyNumbersSeq.isHappy(3));
47+
assertFalse(HappyNumbersSeq.isHappy(4));
48+
assertFalse(HappyNumbersSeq.isHappy(5));
49+
assertFalse(HappyNumbersSeq.isHappy(6));
50+
assertFalse(HappyNumbersSeq.isHappy(8));
51+
assertFalse(HappyNumbersSeq.isHappy(9));
52+
assertFalse(HappyNumbersSeq.isHappy(11));
53+
assertFalse(HappyNumbersSeq.isHappy(12));
54+
assertFalse(HappyNumbersSeq.isHappy(14));
55+
assertFalse(HappyNumbersSeq.isHappy(15));
56+
assertFalse(HappyNumbersSeq.isHappy(16));
57+
assertFalse(HappyNumbersSeq.isHappy(17));
58+
assertFalse(HappyNumbersSeq.isHappy(18));
59+
assertFalse(HappyNumbersSeq.isHappy(20));
60+
}
61+
62+
@Test
63+
void testIsHappyWithLargeNumbers() {
64+
// Test larger happy numbers
65+
assertTrue(HappyNumbersSeq.isHappy(1000));
66+
assertFalse(HappyNumbersSeq.isHappy(999));
67+
assertFalse(HappyNumbersSeq.isHappy(1001));
68+
}
69+
70+
@Test
71+
void testIsHappyWithInvalidInput() {
72+
// Test with zero
73+
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(0));
74+
75+
// Test with negative numbers
76+
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-1));
77+
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-10));
78+
assertThrows(IllegalArgumentException.class, () -> HappyNumbersSeq.isHappy(-100));
79+
}
80+
81+
@Test
82+
void testSumSquaresSingleDigit() {
83+
assertEquals(0, HappyNumbersSeq.sumSquares(0));
84+
assertEquals(1, HappyNumbersSeq.sumSquares(1));
85+
assertEquals(4, HappyNumbersSeq.sumSquares(2));
86+
assertEquals(9, HappyNumbersSeq.sumSquares(3));
87+
assertEquals(16, HappyNumbersSeq.sumSquares(4));
88+
assertEquals(25, HappyNumbersSeq.sumSquares(5));
89+
assertEquals(36, HappyNumbersSeq.sumSquares(6));
90+
assertEquals(49, HappyNumbersSeq.sumSquares(7));
91+
assertEquals(64, HappyNumbersSeq.sumSquares(8));
92+
assertEquals(81, HappyNumbersSeq.sumSquares(9));
93+
}
94+
95+
@Test
96+
void testSumSquaresMultipleDigits() {
97+
// 10: 1^2 + 0^2 = 1
98+
assertEquals(1, HappyNumbersSeq.sumSquares(10));
99+
100+
// 23: 2^2 + 3^2 = 4 + 9 = 13
101+
assertEquals(13, HappyNumbersSeq.sumSquares(23));
102+
103+
// 82: 8^2 + 2^2 = 64 + 4 = 68
104+
assertEquals(68, HappyNumbersSeq.sumSquares(82));
105+
106+
// 130: 1^2 + 3^2 + 0^2 = 1 + 9 + 0 = 10
107+
assertEquals(10, HappyNumbersSeq.sumSquares(130));
108+
109+
// 999: 9^2 + 9^2 + 9^2 = 81 + 81 + 81 = 243
110+
assertEquals(243, HappyNumbersSeq.sumSquares(999));
111+
}
112+
113+
@Test
114+
void testSumSquaresLargeNumbers() {
115+
// 1234: 1^2 + 2^2 + 3^2 + 4^2 = 1 + 4 + 9 + 16 = 30
116+
assertEquals(30, HappyNumbersSeq.sumSquares(1234));
117+
118+
// 9876: 9^2 + 8^2 + 7^2 + 6^2 = 81 + 64 + 49 + 36 = 230
119+
assertEquals(230, HappyNumbersSeq.sumSquares(9876));
120+
}
121+
122+
@Test
123+
void testIsSadWithCycleNumbers() {
124+
// Test all known cycle numbers
125+
assertTrue(HappyNumbersSeq.isSad(4));
126+
assertTrue(HappyNumbersSeq.isSad(16));
127+
assertTrue(HappyNumbersSeq.isSad(20));
128+
assertTrue(HappyNumbersSeq.isSad(37));
129+
assertTrue(HappyNumbersSeq.isSad(58));
130+
assertTrue(HappyNumbersSeq.isSad(145));
131+
}
132+
133+
@Test
134+
void testIsSadWithNonCycleNumbers() {
135+
// Test numbers that are not in the cycle
136+
assertFalse(HappyNumbersSeq.isSad(1));
137+
assertFalse(HappyNumbersSeq.isSad(7));
138+
assertFalse(HappyNumbersSeq.isSad(10));
139+
assertFalse(HappyNumbersSeq.isSad(13));
140+
assertFalse(HappyNumbersSeq.isSad(19));
141+
assertFalse(HappyNumbersSeq.isSad(23));
142+
}
143+
144+
@Test
145+
void testHappyNumberSequenceFor7() {
146+
// Test the sequence for happy number 7: 7 → 49 → 97 → 130 → 10 → 1
147+
int n = 7;
148+
assertEquals(49, HappyNumbersSeq.sumSquares(n));
149+
n = 49;
150+
assertEquals(97, HappyNumbersSeq.sumSquares(n));
151+
n = 97;
152+
assertEquals(130, HappyNumbersSeq.sumSquares(n));
153+
n = 130;
154+
assertEquals(10, HappyNumbersSeq.sumSquares(n));
155+
n = 10;
156+
assertEquals(1, HappyNumbersSeq.sumSquares(n));
157+
}
158+
159+
@Test
160+
void testHappyNumberSequenceFor19() {
161+
// Test the sequence for happy number 19: 19 → 82 → 68 → 100 → 1
162+
int n = 19;
163+
assertEquals(82, HappyNumbersSeq.sumSquares(n));
164+
n = 82;
165+
assertEquals(68, HappyNumbersSeq.sumSquares(n));
166+
n = 68;
167+
assertEquals(100, HappyNumbersSeq.sumSquares(n));
168+
n = 100;
169+
assertEquals(1, HappyNumbersSeq.sumSquares(n));
170+
}
171+
172+
@Test
173+
void testSadNumberEntersCycle() {
174+
// Test that sad number 2 eventually reaches a cycle number
175+
int n = 2;
176+
assertEquals(4, HappyNumbersSeq.sumSquares(n)); // 2 → 4 (cycle number)
177+
assertTrue(HappyNumbersSeq.isSad(4));
178+
}
179+
}

0 commit comments

Comments
 (0)