-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathConditionsAndLoops.java
More file actions
563 lines (506 loc) · 20.9 KB
/
ConditionsAndLoops.java
File metadata and controls
563 lines (506 loc) · 20.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* A collection of example methods demonstrating conditions and loops in Java.
* Updated for Java 21+ with modern idioms, switch expressions, and better naming.
*
* @author Ilkka Kokkarinen
*/
public class ConditionsAndLoops {
// A seeded random number generator for repeatable results.
private static final Random rng = new Random(12345);
// -----------------------------------------------------------------------
// Sign of an integer.
// Demonstrates: if-else chain returning one of three possibilities.
// -----------------------------------------------------------------------
/**
* Return the sign of the integer argument as a {@code char}:
* {@code '-'}, {@code '0'}, or {@code '+'}.
*
* @param value the integer whose sign to determine
* @return the sign character
*/
public static char sign(int value) {
if (value < 0) { return '-'; }
if (value > 0) { return '+'; }
return '0';
}
// -----------------------------------------------------------------------
// Maximum of three values.
// Demonstrates: sequential comparisons with a running maximum.
// -----------------------------------------------------------------------
/**
* Return the largest of three integers.
*
* @param a the first number
* @param b the second number
* @param c the third number
* @return the largest of the three
*/
public static int maximum(int a, int b, int c) {
int max = a;
if (b > max) { max = b; }
if (c > max) { max = c; }
return max;
}
// -----------------------------------------------------------------------
// Median of three values.
// Demonstrates: nested conditions, short-circuit reasoning.
// -----------------------------------------------------------------------
/**
* Return the median (middle value) of three integers.
*
* @param a the first number
* @param b the second number
* @param c the third number
* @return the median of the three
*/
public static int median(int a, int b, int c) {
if (a > b && a > c) {
// a is the largest → median is the larger of b and c.
return Math.max(b, c);
}
if (a < b && a < c) {
// a is the smallest → median is the smaller of b and c.
return Math.min(b, c);
}
// a is neither largest nor smallest → a is the median.
return a;
}
// -----------------------------------------------------------------------
// Days in a month — three versions showing language evolution.
// Demonstrates: if-else ladder, old switch statement, modern switch expression.
// -----------------------------------------------------------------------
/**
* Return the number of days in the given month using an if-else ladder.
*
* @param month the month (1 = January, 12 = December)
* @param leapYear whether the current year is a leap year
* @return the number of days, or 0 for an invalid month
*/
public static int daysInMonthIfElse(int month, boolean leapYear) {
if (month < 1 || month > 12) { return 0; }
if (month == 2) { return leapYear ? 29 : 28; }
if (month == 4 || month == 6 || month == 9 || month == 11) { return 30; }
return 31; // The last step of a ladder is typically unconditional.
}
/**
* Same logic using the classic switch statement (pre-Java 14).
* Note the required {@code break} after each case — forgetting one
* causes the dreaded "fall-through" bug. This is one of the most
* infamous design mistakes inherited from C.
*
* @param month the month (1 = January, 12 = December)
* @param leapYear whether the current year is a leap year
* @return the number of days, or 0 for an invalid month
*/
public static int daysInMonthOldSwitch(int month, boolean leapYear) {
int days;
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31; break;
case 2:
days = leapYear ? 29 : 28; break;
case 4: case 6: case 9: case 11:
days = 30; break;
default:
days = 0;
}
return days;
}
/**
* Same logic using a modern switch expression (Java 14+). The arrow
* syntax eliminates the need for break statements, and the compiler
* ensures exhaustive coverage. This is the preferred style in modern Java.
*
* @param month the month (1 = January, 12 = December)
* @param leapYear whether the current year is a leap year
* @return the number of days
* @throws IllegalArgumentException if month is not in 1..12
*/
public static int daysInMonth(int month, boolean leapYear) {
return switch (month) {
case 1, 3, 5, 7, 8, 10, 12 -> 31;
case 2 -> leapYear ? 29 : 28;
case 4, 6, 9, 11 -> 30;
default -> throw new IllegalArgumentException(
"Invalid month: " + month);
};
}
// -----------------------------------------------------------------------
// Leap year determination — three equivalent implementations.
// Demonstrates: early return, boolean expressions, verification testing.
// -----------------------------------------------------------------------
/**
* Determine whether the given year is a leap year, using early returns
* that progressively narrow the possibilities.
*
* @param year the year to examine
* @return whether that year is a leap year
*/
public static boolean isLeapYear(int year) {
if (year % 4 != 0) { return false; }
// Now we know the year is divisible by 4.
if (year % 100 != 0) { return true; }
// Now we know it is divisible by 100; the decision depends
// on whether it is also divisible by 400.
return year % 400 == 0;
}
/**
* Same logic, but checking divisibility from the largest factor down.
*/
public static boolean isLeapYearTopDown(int year) {
if (year % 400 == 0) { return true; }
if (year % 100 == 0) { return false; }
return year % 4 == 0;
}
/**
* Same logic as a single boolean expression. Compact, but harder to read.
*/
public static boolean isLeapYearOneLiner(int year) {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/**
* Verify that all three leap year implementations agree for every year
* in the range 2525..9595 — the extremes of the famous song "In The
* Year 2525" by Zager and Evans.
*
* @return {@code true} if all three methods agree on every year
*/
public static boolean testLeapYearMethods() {
return IntStream.rangeClosed(2525, 9595).allMatch(year -> {
boolean r1 = isLeapYear(year);
boolean r2 = isLeapYearTopDown(year);
boolean r3 = isLeapYearOneLiner(year);
return r1 == r2 && r2 == r3;
});
}
// -----------------------------------------------------------------------
// Euclid's GCD algorithm — the oldest surviving algorithm in history.
// Demonstrates: while loop, integer remainder, temporary variables.
// -----------------------------------------------------------------------
/**
* Compute the greatest common divisor of two positive integers using
* Euclid's algorithm. Works whether {@code a > b} or not.
*
* @param a the first integer
* @param b the second integer
* @param verbose whether to print each step
* @return the greatest common divisor of {@code a} and {@code b}
*/
public static int gcd(int a, int b, boolean verbose) {
while (b > 0) {
if (verbose) {
System.out.printf(" a = %d, b = %d%n", a, b);
}
int remainder = a % b;
a = b;
b = remainder;
}
if (verbose) {
System.out.printf(" a = %d, b = %d → returning %d%n", a, b, a);
}
return a;
}
/** Non-verbose overload. */
public static int gcd(int a, int b) {
return gcd(a, b, false);
}
// -----------------------------------------------------------------------
// Collatz conjecture (3n+1 problem).
// Demonstrates: ternary operator, while loop, conditional output.
// See https://en.wikipedia.org/wiki/Collatz_conjecture
// -----------------------------------------------------------------------
/**
* Compute the length of the Collatz sequence starting from the given
* number. At each step, even numbers are halved and odd numbers become
* {@code 3n + 1}. The conjecture (still unproven!) states that this
* always eventually reaches 1.
*
* @param start the starting number
* @param verbose whether to print the sequence
* @return the number of steps to reach 1
*/
public static int collatz(int start, boolean verbose) {
int current = start;
int steps = 0;
while (current > 1) {
if (verbose) {
System.out.print((steps > 0 ? ", " : "") + current);
}
steps++;
current = (current % 2 == 0) ? current / 2 : 3 * current + 1;
}
if (verbose) {
System.out.println((steps > 0 ? ", " : "") + current);
}
return steps;
}
// -----------------------------------------------------------------------
// Count Unicode letters — just how many are there?
// Demonstrates: Character utility methods, large-range iteration.
// -----------------------------------------------------------------------
/**
* Count how many Unicode code points are classified as letters. Java
* supports the full Unicode range, so identifiers can contain characters
* from scripts all around the world.
*
* @return the total number of letter code points
*/
public static int countUnicodeLetters() {
// IntStream makes this a clean one-liner, though slower than a raw loop.
return (int) IntStream.range(0, Character.MAX_CODE_POINT)
.filter(Character::isLetter)
.count();
}
// -----------------------------------------------------------------------
// FizzBuzz — the internet-famous job interview quick rejection puzzle.
// See https://blog.codinghorror.com/why-cant-programmers-program/
// Demonstrates: StringBuilder, modulo operator, conditional logic.
// -----------------------------------------------------------------------
/**
* Generate the FizzBuzz sequence for the range {@code [start, end]}.
* Numbers divisible by 3 become "fizz", by 5 become "buzz", and by
* both become "fizzbuzz".
*
* @param start the first number (inclusive)
* @param end the last number (inclusive)
* @return the comma-separated FizzBuzz string
*/
public static String fizzBuzz(int start, int end) {
return IntStream.rangeClosed(start, end)
.mapToObj(i -> {
if (i % 15 == 0) { return "fizzbuzz"; }
if (i % 3 == 0) { return "fizz"; }
if (i % 5 == 0) { return "buzz"; }
return String.valueOf(i);
})
.collect(Collectors.joining(", "));
}
// -----------------------------------------------------------------------
// Dice rolling utilities.
// Demonstrates: do-while loop, Random, simulation.
// -----------------------------------------------------------------------
/**
* Roll two dice until both show one ("snake eyes"). Return the number
* of rolls required. This naturally calls for a do-while loop, since
* we cannot check the condition until we have rolled at least once.
*
* @return the number of rolls needed to get snake eyes
*/
public static int rollUntilSnakeEyes() {
int rolls = 0;
int die1, die2;
do {
die1 = rng.nextInt(6) + 1;
die2 = rng.nextInt(6) + 1;
rolls++;
} while (die1 > 1 || die2 > 1);
return rolls;
}
/**
* Roll an {@code n}-sided die a given number of times and return the sum.
*
* @param rolls the number of rolls
* @param sides the number of sides on the die
* @return the total of all rolls
*/
public static int rollDice(int rolls, int sides) {
int total = 0;
for (int i = 0; i < rolls; i++) {
total += rng.nextInt(sides) + 1;
}
return total;
}
// -----------------------------------------------------------------------
// Demonstrate pseudorandom number determinism.
// Demonstrates: seeding a Random, reproducibility.
// -----------------------------------------------------------------------
/**
* Show that "random" numbers are fully deterministic once the seed
* is fixed — as predictable as a train following its tracks.
*
* @param seed the seed value
* @param count how many random numbers to generate
*/
public static void outputRandomSeries(int seed, int count) {
rng.setSeed(seed);
for (int i = 0; i < count; i++) {
System.out.print(rng.nextInt() + " ");
}
System.out.println();
}
// Note: class Random is adequate for toy games and simple demos. For
// serious simulations, use java.security.SecureRandom instead.
// -----------------------------------------------------------------------
// Nested loop classics: rectangle and triangle of characters.
// In the 1980s, it was essentially a law that every programming
// textbook had to include these two exercises about nested loops.
// -----------------------------------------------------------------------
/**
* Print a rectangle of the given character.
*
* @param rows the number of rows
* @param cols the number of columns
* @param ch the fill character
*/
public static void printRectangle(int rows, int cols, char ch) {
for (int row = 0; row < rows; row++) {
System.out.println(String.valueOf(ch).repeat(cols));
}
}
/**
* Print a right triangle of the given character, with row {@code i}
* containing {@code i + 1} characters.
*
* @param rows the number of rows
* @param ch the fill character
*/
public static void printTriangle(int rows, char ch) {
for (int row = 0; row < rows; row++) {
System.out.println(String.valueOf(ch).repeat(row + 1));
}
}
// After mastering these two, try outputting a diamond shape, e.g. for rows = 7:
// $
// $$$
// $$$$$
// $$$$$$$
// $$$$$
// $$$
// $
// -----------------------------------------------------------------------
// Primality testing and prime factorization.
// Demonstrates: trial division, early exit, StringBuilder.
// -----------------------------------------------------------------------
/**
* Test whether {@code n} is a prime number using trial division up to
* √n. Only odd candidate divisors are tested after checking for 2.
*
* @param n the integer to test
* @return {@code true} if {@code n} is prime
*/
public static boolean isPrime(int n) {
if (n < 2) { return false; }
if (n == 2) { return true; }
if (n % 2 == 0) { return false; }
// Test odd divisors from 3 up to √n.
for (int divisor = 3; divisor * divisor <= n; divisor += 2) {
if (n % divisor == 0) { return false; }
}
return true;
}
/**
* List the prime factors of a positive integer as a bracketed,
* comma-separated string. Factors appear with multiplicity:
* for example, {@code primeFactors(12)} returns {@code "[2, 2, 3]"}.
*
* @param n the positive integer to factorize
* @return the prime factorization as a string
*/
public static String primeFactors(int n) {
var result = new StringBuilder("[");
boolean first = true;
int divisor = 2;
while (n > 1) {
if (n % divisor == 0) {
n /= divisor;
if (!first) { result.append(", "); }
result.append(divisor);
first = false;
} else {
// Advance to the next candidate: 2 → 3, then only odd numbers.
divisor = (divisor == 2) ? 3 : divisor + 2;
}
}
result.append("]");
return result.toString();
}
// -----------------------------------------------------------------------
// Main method — exercise each example and display results.
// -----------------------------------------------------------------------
public static void main(String[] args) {
// --- Sign ---
System.out.println("--- sign ---");
for (int value : new int[]{-42, 0, 99}) {
System.out.printf("sign(%d) = '%c'%n", value, sign(value));
}
// --- Maximum and median ---
System.out.println("\n--- maximum and median ---");
System.out.printf("maximum(3, 7, 5) = %d%n", maximum(3, 7, 5));
System.out.printf("median(3, 7, 5) = %d%n", median(3, 7, 5));
// --- Days in month (all three versions) ---
System.out.println("\n--- daysInMonth (three versions) ---");
for (int month : new int[]{1, 2, 4, 7}) {
System.out.printf("Month %2d: if-else=%d, old switch=%d, new switch=%d%n",
month,
daysInMonthIfElse(month, false),
daysInMonthOldSwitch(month, false),
daysInMonth(month, false));
}
System.out.printf("Feb in leap year: %d%n", daysInMonth(2, true));
// --- Leap year ---
System.out.println("\n--- isLeapYear ---");
for (int year : new int[]{1997, 2000, 2012, 2020, 2100}) {
System.out.printf("Is %d a leap year? %s%n", year, isLeapYear(year));
}
System.out.println("All three methods agree on 2525..9595: "
+ testLeapYearMethods());
// --- GCD and LCM ---
System.out.println("\n--- gcd ---");
int a = 2 * 3 * 5 * 11 * 13;
int b = 2 * 2 * 3 * 7 * 13 * 23;
System.out.printf("Computing gcd(%d, %d):%n", a, b);
int divisor = gcd(a, b, true);
// Group operations to avoid overflow: compute a/gcd first, then multiply by b.
long lcm = (long) a / divisor * b;
System.out.printf("lcm(%d, %d) = %d%n", a, b, lcm);
// --- Collatz sequences ---
System.out.println("\n--- Collatz sequences ---");
for (int start : new int[]{10, 100, 1_000, 12_345, 987_654}) {
System.out.printf("Collatz(%d): ", start);
int steps = collatz(start, true);
System.out.printf(" → reached 1 in %d steps%n", steps);
}
// --- Unicode letter count ---
System.out.println("\n--- countUnicodeLetters ---");
System.out.println("Counting... (this may take a moment)");
int letterCount = countUnicodeLetters();
System.out.printf("Found %,d Unicode code points classified as letters.%n",
letterCount);
// --- FizzBuzz ---
System.out.println("\n--- fizzBuzz(1, 100) ---");
System.out.println(fizzBuzz(1, 100));
// --- Snake eyes simulation ---
System.out.println("\n--- rollUntilSnakeEyes ---");
int totalRolls = 0;
int trials = 100_000;
for (int i = 0; i < trials; i++) {
totalRolls += rollUntilSnakeEyes();
}
System.out.printf("Average rolls to snake eyes over %,d trials: %.2f%n",
trials, totalRolls / (double) trials);
System.out.printf("Theoretical expected value: %.2f (= 36)%n", 36.0);
// --- Primality ---
System.out.println("\n--- isPrime ---");
long primeCount = IntStream.rangeClosed(0, 1_000_000)
.filter(ConditionsAndLoops::isPrime)
.count();
System.out.printf("There are exactly %,d primes up to one million.%n", primeCount);
// --- Prime factorization ---
System.out.println("\n--- primeFactors ---");
for (int value : new int[]{a, b, 360, 997}) {
System.out.printf("primeFactors(%d) = %s%n", value, primeFactors(value));
}
// --- Rectangle and triangle ---
System.out.println("\nA 5×8 rectangle of dollar signs:");
printRectangle(5, 8, '$');
System.out.println("\nA triangle of 10 rows:");
printTriangle(10, '$');
// --- Random determinism demo ---
System.out.println("\nSame seed → same sequence (twice):");
outputRandomSeries(42, 8);
outputRandomSeries(42, 8);
}
}