Add disarium-number module: loop, String and stream approaches, dry run, counts to 10^8

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
This commit is contained in:
2026-10-29 03:56:25 +00:00
co-authored by Claude Sonnet 5.5
parent 94306c50ab
commit 4a376b94e9
15 changed files with 331 additions and 0 deletions
+1
View File
@@ -14,6 +14,7 @@ One module per article. This repository currently holds:
| [`junit-mockito`](junit-mockito/) | Top 30 JUnit 6 and Mockito Interview Questions (2026) | this commit | | [`junit-mockito`](junit-mockito/) | Top 30 JUnit 6 and Mockito Interview Questions (2026) | this commit |
| [`hashmap-vs-concurrenthashmap`](hashmap-vs-concurrenthashmap/) | HashMap vs ConcurrentHashMap in Java: Internals, Thread Safety and Interview Answers | this commit | | [`hashmap-vs-concurrenthashmap`](hashmap-vs-concurrenthashmap/) | HashMap vs ConcurrentHashMap in Java: Internals, Thread Safety and Interview Answers | this commit |
| [`multithreading`](multithreading/) | Java Multithreading Programs for Interviews: Odd-Even Printing, Ping-Pong, Ordered Threads, Deadlock | this commit | | [`multithreading`](multithreading/) | Java Multithreading Programs for Interviews: Odd-Even Printing, Ping-Pong, Ordered Threads, Deadlock | this commit |
| [`disarium-number`](disarium-number/) | Disarium Number in Java: Program, Algorithm and All Disarium Numbers from 1 to 1000 | this commit |
## `collections` module ## `collections` module
+25
View File
@@ -0,0 +1,25 @@
# disarium-number
Disarium number checks in plain Java 25: the original `Math.pow` loop, an integer-power loop, a
String version, a Streams version and a `log10` digit-count variant. A test proves they agree, and
`output/` holds the transcripts quoted in the post. Companion to
[Disarium Number in Java: Program, Algorithm and All Disarium Numbers from 1 to 1000](https://ankurm.com/disarium-number-in-java/).
## Versions
| Component | Version |
|---|---|
| JDK | 25.0.5+8 (Temurin LTS), `--release 25` |
| JUnit Jupiter | 6.1.3 |
| Maven | 3.9.11 |
## Run it
```bash
cd disarium-number
./scripts/run.sh check # also: list | dryrun | approaches | digits | count
mvn test # 6 tests
./scripts/run-all.sh # regenerates output/*.txt (count takes ~2 minutes)
```
`count` brute-forces every int up to 10^8; the timing depends on your machine.
@@ -0,0 +1,9 @@
89 -> Disarium
135 -> Disarium
175 -> Disarium
518 -> Disarium
598 -> Disarium
24 -> NOT Disarium
100 -> NOT Disarium
Disarium numbers up to 1000: 1 2 3 4 5 6 7 8 9 89 135 175 518 598
@@ -0,0 +1,2 @@
count=14
1 2 3 4 5 6 7 8 9 89 135 175 518 598
+5
View File
@@ -0,0 +1,5 @@
175 = 1^1 + 7^2 + 5^3 = 1 + 49 + 125 = 175 -> Disarium
89 = 8^1 + 9^2 = 8 + 81 = 89 -> Disarium
135 = 1^1 + 3^2 + 5^3 = 1 + 9 + 125 = 135 -> Disarium
24 = 2^1 + 4^2 = 2 + 16 = 18 -> NOT Disarium (18 != 24)
100 = 1^1 + 0^2 + 0^3 = 1 + 0 + 0 = 1 -> NOT Disarium (1 != 100)
@@ -0,0 +1 @@
compared original, loop, String, stream, log10 for 1..1000000: disagreements=0
@@ -0,0 +1 @@
log10 vs loop digit count around every power of ten: mismatches=0
@@ -0,0 +1,9 @@
up to 10^1: 9
up to 10^2: 10
up to 10^3: 14
up to 10^4: 17
up to 10^5: 17
up to 10^6: 17
up to 10^7: 18
up to 10^8: 18
found at or above 1000: 1306 1676 2427 2646798
@@ -0,0 +1,7 @@
PASS DisariumTest.allApproachesAgreeUpTo200000
PASS DisariumTest.dryRunOf175
PASS DisariumTest.dryRunOf24
PASS DisariumTest.zeroAndNegativesAreNotDisarium
PASS DisariumTest.biggerKnownOnes
PASS DisariumTest.listUpTo1000
6 tests, 0 failures
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ankurm.interviewlab</groupId>
<artifactId>disarium-number</artifactId>
<version>1.0.0</version>
<name>disarium-number</name>
<description>Disarium number checks in Java: loop, String and stream approaches, with a dry run and counts</description>
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>6.1.3</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Regenerates every transcript in output/. Needs JDK 25 and Maven 3.9+.
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p output
./scripts/run.sh check > output/01-check-and-series.txt
./scripts/run.sh list > output/02-list-1-to-1000.txt
./scripts/run.sh dryrun > output/03-dry-run.txt
./scripts/run.sh approaches > output/04-approaches-agree.txt
./scripts/run.sh digits > output/05-log10-digit-count.txt
./scripts/run.sh count > output/06-count-to-10-pow-8.txt
mvn -B -q test > /dev/null 2>&1
python3 - <<'PY' > output/07-test-results.txt
import glob, xml.etree.ElementTree as ET
total = fail = 0
for f in sorted(glob.glob("target/surefire-reports/TEST-*.xml")):
for tc in ET.parse(f).getroot().iter("testcase"):
bad = tc.find("failure") is not None or tc.find("error") is not None
total += 1; fail += bad
print(("FAIL " if bad else "PASS ") + tc.get("classname").split(".")[-1] + "." + tc.get("name"))
print(f"{total} tests, {fail} failures")
PY
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Runs one demo: ./scripts/run.sh check | list | dryrun | approaches | count | digits
set -euo pipefail
cd "$(dirname "$0")/.."
mvn -B -q compile 2>&1 | grep -E 'ERROR' || true
java -cp target/classes com.ankurm.disarium.Main "$@"
@@ -0,0 +1,108 @@
package com.ankurm.disarium;
import java.util.List;
import java.util.stream.IntStream;
/** Three independent ways to test a Disarium number. All must agree (see DisariumTest). */
public final class Disarium {
private Disarium() {}
/** The program from the original post, kept as-is (uses Math.pow). */
public static boolean isDisariumOriginal(int number) {
int original = number;
int len = countDigits(number);
int sum = 0;
int n = number;
while (n > 0) {
int digit = n % 10;
sum += (int) Math.pow(digit, len);
len--;
n /= 10;
}
return sum == original;
}
public static int countDigits(int n) {
int count = 0;
while (n > 0) {
count++;
n /= 10;
}
return count;
}
/** Approach 1: loop over the digits right to left; integer power, no floating point. */
public static boolean isDisariumLoop(int number) {
if (number <= 0) return false;
int len = countDigits(number);
long sum = 0;
for (int n = number; n > 0; n /= 10, len--) {
sum += intPow(n % 10, len);
}
return sum == number;
}
/** Approach 2: String of digits, left to right, position = index + 1. */
public static boolean isDisariumString(int number) {
if (number <= 0) return false;
String s = Integer.toString(number);
long sum = 0;
for (int i = 0; i < s.length(); i++) {
sum += intPow(s.charAt(i) - '0', i + 1);
}
return sum == number;
}
/** Approach 3: Streams over the characters of the String. */
public static boolean isDisariumStream(int number) {
if (number <= 0) return false;
String s = Integer.toString(number);
long sum = IntStream.range(0, s.length())
.mapToLong(i -> intPow(s.charAt(i) - '0', i + 1))
.sum();
return sum == number;
}
/** Digit count via log10 instead of a loop (used by the maths variant). */
public static int digitsLog10(int n) {
return (int) Math.log10(n) + 1;
}
/** Approach 1b: same as the loop, but the digit count comes from log10. */
public static boolean isDisariumLog10(int number) {
if (number <= 0) return false;
int len = digitsLog10(number);
long sum = 0;
for (int n = number; n > 0; n /= 10, len--) {
sum += intPow(n % 10, len);
}
return sum == number;
}
static long intPow(int base, int exp) {
long r = 1;
for (int i = 0; i < exp; i++) r *= base;
return r;
}
public static List<Integer> upTo(int limit) {
return IntStream.rangeClosed(1, limit).filter(Disarium::isDisariumLoop).boxed().toList();
}
/** Human-readable dry run: "175 = 1^1 + 7^2 + 5^3 = 1 + 49 + 125 = 175". */
public static String dryRun(int number) {
String s = Integer.toString(number);
StringBuilder terms = new StringBuilder(), values = new StringBuilder();
long sum = 0;
for (int i = 0; i < s.length(); i++) {
int d = s.charAt(i) - '0';
long v = intPow(d, i + 1);
sum += v;
if (i > 0) { terms.append(" + "); values.append(" + "); }
terms.append(d).append('^').append(i + 1);
values.append(v);
}
return number + " = " + terms + " = " + values + " = " + sum
+ (sum == number ? " -> Disarium" : " -> NOT Disarium (" + sum + " != " + number + ")");
}
}
@@ -0,0 +1,65 @@
package com.ankurm.disarium;
import java.util.List;
/** Usage: Main check | list | dryrun | count | digits */
public final class Main {
public static void main(String[] args) {
String mode = args.length > 0 ? args[0] : "list";
switch (mode) {
case "check" -> {
for (int n : new int[] {89, 135, 175, 518, 598, 24, 100}) {
System.out.printf("%4d -> %s%n", n, Disarium.isDisariumOriginal(n) ? "Disarium" : "NOT Disarium");
}
System.out.println();
System.out.println("Disarium numbers up to 1000: " + join(Disarium.upTo(1000)));
}
case "list" -> {
List<Integer> l = Disarium.upTo(1000);
System.out.println("count=" + l.size());
System.out.println(join(l));
}
case "dryrun" -> {
for (int n : new int[] {175, 89, 135, 24, 100}) System.out.println(Disarium.dryRun(n));
}
case "approaches" -> {
int bad = 0;
for (int n = 1; n <= 1_000_000; n++) {
boolean a = Disarium.isDisariumOriginal(n), b = Disarium.isDisariumLoop(n),
c = Disarium.isDisariumString(n), d = Disarium.isDisariumStream(n),
e = Disarium.isDisariumLog10(n);
if (a != b || b != c || c != d || d != e) { bad++; System.out.println("DISAGREE at " + n); }
}
System.out.println("compared original, loop, String, stream, log10 for 1..1000000: disagreements=" + bad);
}
case "count" -> {
// Brute force up to 10^8 with the loop approach, reporting when each power of ten is passed.
int found = 0;
long limit = 100_000_000L;
long mark = 10;
StringBuilder sb = new StringBuilder();
for (int n = 1; n <= limit; n++) {
if (Disarium.isDisariumLoop(n)) { found++; if (n >= 1000) sb.append(n).append(' '); }
if (n == mark) { System.out.println("up to 10^" + (int) Math.log10(mark) + ": " + found); mark *= 10; }
}
System.out.println("found at or above 1000: " + sb.toString().trim());
}
case "digits" -> {
// Where does Math.log10 digit counting go wrong? Compare with the loop for all ints near powers of ten.
int wrong = 0;
long p = 1;
for (int k = 0; k < 10; k++, p *= 10) {
for (long n = Math.max(1, p - 2); n <= p + 2 && n <= Integer.MAX_VALUE; n++) {
if (Disarium.digitsLog10((int) n) != Disarium.countDigits((int) n)) { wrong++; System.out.println("log10 wrong at " + n); }
}
}
System.out.println("log10 vs loop digit count around every power of ten: mismatches=" + wrong);
}
default -> throw new IllegalArgumentException(mode);
}
}
private static String join(List<Integer> l) {
return String.join(" ", l.stream().map(String::valueOf).toList());
}
}
@@ -0,0 +1,43 @@
package com.ankurm.disarium;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import org.junit.jupiter.api.Test;
class DisariumTest {
static final List<Integer> TO_1000 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175, 518, 598);
@Test void listUpTo1000() { assertEquals(TO_1000, Disarium.upTo(1000)); }
@Test void allApproachesAgreeUpTo200000() {
for (int n = 1; n <= 200_000; n++) {
boolean e = Disarium.isDisariumLoop(n);
assertEquals(e, Disarium.isDisariumOriginal(n), "original " + n);
assertEquals(e, Disarium.isDisariumString(n), "string " + n);
assertEquals(e, Disarium.isDisariumStream(n), "stream " + n);
assertEquals(e, Disarium.isDisariumLog10(n), "log10 " + n);
}
}
@Test void dryRunOf175() {
assertEquals("175 = 1^1 + 7^2 + 5^3 = 1 + 49 + 125 = 175 -> Disarium", Disarium.dryRun(175));
}
@Test void dryRunOf24() {
assertEquals("24 = 2^1 + 4^2 = 2 + 16 = 18 -> NOT Disarium (18 != 24)", Disarium.dryRun(24));
}
@Test void zeroAndNegativesAreNotDisarium() {
assertFalse(Disarium.isDisariumLoop(0));
assertFalse(Disarium.isDisariumString(-5));
assertFalse(Disarium.isDisariumStream(0));
}
@Test void biggerKnownOnes() {
assertTrue(Disarium.isDisariumLoop(2646798));
assertTrue(Disarium.isDisariumStream(2646798));
assertFalse(Disarium.isDisariumStream(12157692));
assertFalse(Disarium.isDisariumString(2646799));
}
}