1. Imperative programming

At its core, the virtual machine does nothing more but evaluates expressions and executes statements. The exercises in this chapter focus on the different data types, various operators and conditional execution.

Prerequisites

  • be able to do simple screen output

  • receive user input

  • be able to distinguish between data types

  • be able to declare variables

  • know assignment and operators

  • be able to use conditional statements

  • being able to use loops for repetitions

  • be able to use nested loops

  • be able to declare and implement subroutines with methods

  • know the difference between argument and parameter

  • be able to use overloaded methods

Data types used in this chapter:

1.1. Screen output

In the first chapter, the Java program implemented a simple output. Let’s build on that and learn how to write special characters (such as quotes), set line breaks, or achieve simple formatted output.

1.1.1. Learn about SVG specification ⭐

Graphical representations encourage us to engage in playing, which is why we want our Java programs to draw something. Java SE includes a library that allows you to open a window and draw content, but it’s not done with a few lines of code. Therefore, we want to go a different way, using SVG. The abbreviation stands for Scalable Vector Graphics, a standard for vector graphics. SVG allows two-dimensional vector graphics to be described in text, and text can easily be written by a Java program.

Task:

1.1.2. Write an SVG circle on the console ⭐

There are several approaches for screen output in Java. Usually the method print(…​), println(…​) or printf(…​) is used. These methods are located at the System.out object. Besides System.out there is System.err, but this is reserved for error output. Some developers also use the Console object for output, we stick with System.out.printXXX(…​).

Captain CiaoCiao needs a filled circle for shooting exercises, which can be printed later. For this purpose we want to develop a new Java program.

For screen output we can use:

System.out.print( "Text without subsequent line break" );
System.out.println( "Text followed by a newline" );
System.out.printf( "Text followed by line break%n" );

Task:

  • Create a new class SvgCircle1.

  • Create a main(…​) method so that we can run the program later.

  • Use in the main(…​) the known printXXX() methods to bring the following text to the console:

    <svg height='400' width='1000'><circle cx='100' cy='100' r='50' /></svg>
  • Enter the console output to https://www.w3schools.com/graphics/tryit.asp?filename=trysvg_circle, and after clicking Run" you will see a circle. We want to come back to the web page whenever SVG elements are to be displayed in the following.

    Image 070620 095620.686
    Figure 1. Display an SVG output
  • Modify the program so that line breaks occur in the output. The output should be:

    <svg height='400' width='1000'>
      <circle cx='100' cy='100' r='50' />
    </svg>
  • Change the program again so that instead of single quotes there are now double quotes in the string. The output should be:

    <svg height="400" width="1000">
     <circle cx="100" cy="100" r="50" />
    </svg>

1.2. Variables and data types

Variables store information, and variables always have a type in Java. The compiler knows at all times what kind of variable is declared, and what type of expression is in front of him.

Java has eight built-in data types: boolean, byte, char, short, int, long, float, and double. You can store numeric values and boolean values into them. The boolean values can be set to true and false. For numeric values we have three different groups:

  • We have numeric values for Unicode characters and use the char data type for them. In addition, we have data types for general integers and floating point numbers.

  • Integers always have signs. We have four different types of them: byte, short, int and long. The types differ according to their number of bytes, which means that the data types can hold numbers of different sizes.

  • For floating point numbers, we have float and double available; a double has twice as many bits as a float to store.

The size of the data types is fixed in the Java specification and does not depend on the particular architecture or platform.

1.2.1. Access variables and output their assignments ⭐

Captain CiaoCiao wants to make shooting targets for novices, advanced and professionals. The targets are of different sizes.

A cirlce is described by its coordinates and a radius. Our first program has its fixed circle center and radius, now we want to implement a parameterizable output.

Add from the previous task the main(…​) method.

Task:

  • Declare two int variables x, y and a double variable r in the main(…​) method.

  • Assign values to the variables.

  • Build the assignment of the variables into the output.

Example:

  • If for example x = 100 and y = 110 and r = 20.5, then the console output should be:

    <svg height="100" width="1000">
     <circle cx="100" cy="110" r="20.5" />
    </svg>
  • For manual assignment with x = 10, y = 10 and r = 2.686:

    <svg height="100" width="1000">
     <circle cx="10" cy="10" r="2.686" />
    </svg>

A black circle on a white background is created.

1.2.2. Quiz: value ranges ⭐

What is the result of this expression 1000000 * 1000000? Does anything stand out? Why does this result occur?

1.2.3. Quiz: Does this add up? ⭐⭐⭐

If you compute 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 - 1.0, and output the result with System.out.println(…​), is the output surprising?

1.2.4. Generate random circles ⭐

Random numbers play a bigger role in practice as you might would think.

In Java there is a class Math which provides important mathematical methods. Random numbers can be determined like this:

double rnd = Math.random();

The method random() is thus an offering of the Math class.

Task:

  • Refer to the Javadoc for the range of values in which the result of random() is.

  • Extend the circle program so that the radius is random, in the range including 10, but keeping it smaller than 20. Let the radius still be a floating point number.

Example:

  • If we run the program twice, the outputs might look like this:

    <svg height="100" width="1000">
     <circle cx="100" cy="110" r="19.47493300792351" />
    </svg>
    <svg height="100" width="1000">
     <circle cx="100" cy="110" r="10.218243515543868" />
    </svg>

Alternative variants for forming random numbers in Java are:

double rnd1 = java.util.concurrent.ThreadLocalRandom.current().nextDouble();
double rnd2 = java.util.concurrent.ThreadLocalRandom.current().nextDouble(/* 0 to */ max);
double rnd3 = java.util.concurrent.ThreadLocalRandom.current().nextDouble(min, max);
int rnd4 = java.util.concurrent.ThreadLocalRandom.current().nextInt();
int rnd5 = java.util.concurrent.ThreadLocalRandom.current().nextInt(/* 0 to */max);
int rnd6 = java.util.concurrent.ThreadLocalRandom.current().nextInt(min, max);

1.2.5. Quiz: Dazed and Confused ⭐

Clean Code is a set of best practices that make code easy to read and understand, as well as maintainable, extensible, and testable. Unfortunately, there are also enough counter-examples.

What is wrong with the following example?

double höhe   = 12.34;
double breite = 23.45;
double tmp = 2 * (höhe + breite);
System.out.println( tmp );
tmp = höhe * breite;
System.out.println( tmp );

1.2.6. Process user input ⭐

So far we have done screen output, but have not done any input.

Using new java.util.Scanner(System.in).nextXXX(), we can accept input from the command line. Examples:

int    number1 = new java.util.Scanner( System.in ).nextInt();
double number2 = new java.util.Scanner( System.in ).nextDouble();
String line    = new java.util.Scanner( System.in ).nextLine();

Captain CiaoCiao wants to be able to determine the position of the SVG circle itself.

Task:

  • For the circle, take the assignments for cx and cy as integers from the console, and write the generated SVG fragment back to standard output. The radius remains random.

1.3. Expressions, operands and operators

An expression is evaluated and produces a result. Operators can be used to link operands such as literals or variables.

1.3.1. Quiz: Check in-between ⭐

To test if a number between is real greater than min and real less than max, we can write:

boolean isBetween = between > min && between < max;

Is the following syntax also allowed in Java?

boolean isBetween = min < between < max;

1.3.2. Check if loot can be shared fairly ⭐

After a raid on the distillery, Captain CiaoCiao and his crew snatch countless bottles. Now the loot must be divided, with Captain CiaoCiao basically getting half (if the number of bottles is odd, he gets less than half, the captain is that generous). All other robbers should get exactly the same share. But does this add up?

Task:

  • Write a program that reads the captured number of bottles from the command line and outputs how much Captain CiaoCiao obtains.

  • Output what is left for the crew.

  • Ask for the crew size, and see if the loot can be distributed fairly and equally so that each crew member gets exactly the same number of bottles. An answer in the form true or false is sufficient.

Example:

Number of bottles in total?
123000
Bottles for the captain: 61500
Bottles for all crew members: 61500
Number of crew members?
100
Fair share without remainder? true

Consider how division and remainder value relate.

1.3.3. Do two numbers share the same digit? ⭐⭐

Bonny Brain is playing anchor dominoes, and all the tiles have two squares, with values 0 to 9. Now she wants to know if — also by rotation — two tiles can be placed end to end so that the two squares have the same values.

Task:

  1. Write a program that reads in two numbers, where the numbers are to be in the range from 0 to 99 (both limits inclusive).

  2. If the numbers are above 100, only the last two digits are to be evaluated; 100 or 200 would then be like 00 (i.e. 0), 1111 would be like 11.

  3. Test if the two numbers have a common digit.

Examples:

  • 12 and 31 have 1 as a common digit.

  • 22 and 33 do not have a common digit.

Note: The common digit is not asked, but simply an output true/false. If the number is a single digit, there is a 0 in front, so 01 and 20 have a common digit, which is 0.

1.3.4. Convert currency amount to coins ⭐⭐

In the currency Liretta, there are Liretta coins with denominations of 2 Liretta, 1 Liretta, 50 Lirettacent, 20 Lirettacent, 10 Lirettacent, 5 Lirettacent, 2 Lirettacent and 1 Lirettacent. 100 lirettacent is equal to 1 liretta.

Task:

  1. Create a new class CoinMachine.

  2. The program should first ask for a floating point number for an amount of money.

  3. Print out how the entered amount can be paid out in liereta coins. Not all permutations are requested, but only the smallest number of coins.

Example with input 12.91:

Please enter amount of money:
12.91
6 x 2 Liretta
0 x 1 Liretta
1 x 50 Lirettacent
2 x 20 Lirettacent
0 x 10 Lirettacent
0 x 5 Lirettacent
0 x 2 Lirettacent
1 x 1 Lirettacent

The formatting of the output does not matter.

The Scanner is localized, so it uses the decimal separator of the respective language when entering descendant digits.

1.3.5. One bottle of rum, 10 bottles of rum ⭐

Bonny Brain is a hunter of incorrect language, and she always makes sure that labels are grammatically correct. The rules for the plural are particular in many languages, because it is called, for example, "1 bottle" or "99 bottles", but also "0 bottles". Simplifications such as "1 bottle(s)" are often found in user interfaces.

Task:

  • Create a variable noOfBottles and assign a value to it greater than or equal to 0.

  • Program a grammatically correct output depending on whether there are 0, 1 or many bottles.

Example:

  • "0 bottles of rum".

  • "1 bottle of rum"

  • "99 bottles of rum"

The condition operator (?-: operator) makes the code compact.

1.3.6. Twenty-one ⭐

In Captain CiaoCiao’s casino, the croupier plays the game "Twenty-one" against a player. The goal is to get closer to 21 points with two or more cards than the croupier.

Task:

  1. Read two positive integers dealer and player for the number of points reached by a croupier and player.

  2. Write out whichever value is closer to 21. If one value goes over 21, the other side wins. If both values go over 21, 0 should be output.

Example:

  • Inputs 21 and 18 → output 21

  • Inputs 18 and 21 → output 21

  • Inputs 21 and 21 → output 21

  • Inputs 22 and 23 → output 0

  • Inputs 1 and 10 → lead to the end of the program

  • Inputs 1 and 22 → lead to the end of the program

1.3.7. Quiz: The zero effect ⭐

Does the following class translate? If so, and you run the program, what is the result?

class Application {
 public static void main( String[] args ) {
   int zero = 0;
   int ten = 10;
   double anotherTen = 10;
   System.out.println( anotherTen / zero );
   System.out.println( ten / zero );
 }
}

1.4. Control flow

if-else-statements are important imperative concepts. We use this ability to execute or not execute program parts based on conditions for the following exercises to check and process user input.

1.4.1. Payday ⭐

Bonny Brain sold Tort Ellini an antique pocket watch for 1,000 Liretta. Tort must now pay the watch.

Task:

  1. Write a program that reads in the amount of money on the command line using new java.util.Scanner(System.in).nextDouble().

  2. Bonny Brain is always in a good mood, so she is happy with 10% less. Also, she feels belly-fluffed when Tort gives 20% more. However, if Tort voluntarily pays more than 20%, Bonny Brain has the impression that something is wrong and the pocket watch probably has a valuable hidden function or holds a secret. Consider how to set up the program so that few code changes are needed when the limits shift on a whim.

  3. When Tort has the appropriate amount together, the screen displays "Good boy!"; if the amount is too low or an attempt is made to bribe, it displays "You son of a bi***!".

1.4.2. Quiz: Wrong branching ⭐

The following piece of code wants to swap the contents of the variables x and y if x is greater than y. Is this correct?

int x = 2; y = 1;
if ( x > y )
  int swap = x;
  x = y;
  y = x;
// x should be 1 and y 2

1.4.3. Convert liters ⭐⭐

A program should convert liquid quantities into a form that is easy for Captain CiaoCiao to read.

Task:

  • From the command line, read in a floating point number, the order of magnitude is liters.

  • Convert the number according to the following pattern:

    • 1.0 and greater: output in liters, such as approx. 4 l for the input 4.

    • 0.1 and greater: output in centiliters, about approx. 20 cl when entering 0.2.

    • 0.001 and larger: output in milliliters, about approx. 9 ml when entering 0.009.

  • The output is always an integer. Rounding is fine.

Example:

  • Conversion to ml:

    Enter quantity in liters:
    0.0124134
    approx. 12 ml
  • Conversion in cl:

    Enter quantity in liters:
    0.9876
    about 98 cl
  • Message if the value is too small:

    Enter quantity in liters:
    0.00003435
    Value too small to display
  • Input is already in liters:

    Enter quantity in liters:
    98848548485.445
    approx. 98848548485 l

1.4.4. Create SVG circles with random colors ⭐

In a previous assignment, Captain CiaoCiao called for a black circle on a white background. But there should be more colors in the mix!

Task:

  • Create a new class with a main(…​) method.

  • Output on the command line randomly, and with equal probability, red, green, blue.

  • In SVG, for circles, you can specify the color with the fill attribute, like this: <circle cx="20" cy="20" r="5" fill="blue " />. Give the circle a random color.

Example:

  • With three program starts, there could be the following screen outputs:

    <circle cx="20" cy="20" r="5" fill="green" />
    <circle cx="20" cy="20" r="5" fill="blue" />
    <circle cx="20" cy="20" r="5" fill="blue" />

1.4.5. Quiz: To which block does the else belong? ⭐⭐

Indentations are among the most important principles of clean code. If the indentations are wrong, a reader may misunderstand the program.

What output does the following program produce?

if ( true ) {
if ( false )
if ( 3!=4 )
;
else
System.out.println( "ship's kobold" );
else
System.out.println( "Pumuckl" );
}

Find the result without translating the program.

Hint: First indent the program correctly.

1.4.6. Evaluate input strings for approval ⭐

Bonny Brain expects approval for new projects, and the approval can vary.

Task:

  • Ask for a string from the command line. We assume that the input is always lowercase.

  • If the string is "ay", "aye", "ay, ay", "ja" or "joo", "Keep it up!" should be output to the screen, all other strings result in "Don’t you dare!".

  • Solve this excersise using the switch statement.

1.5. Loops

Besides if-statements, repetitions are the second important imperative property. Java provides different language constructs for loops:

  • while loop

  • do-while loop

  • for loop

  • extended for loop

1.5.1. Create rotated SVG rectangles ⭐

The following vector graphic in SVG rotates a rectangle around the center point (100, 100), by 60 degrees:

<svg height="200" width="200">
 <rect x="50" y="50" width="100" height="100" stroke="black" fill="none"
       transform="rotate(60 100 100)" />
</svg>

Task:

  • Write a program that rotates 36 SVG rectangles 10 degrees on top of each other and outputs them to the screen.

Example:

  • The output starts with:

    <svg height="200" width="200">
     <rect x="50" y="50" width="100" height="100" stroke="black" fill="none" transform="rotate(0 100 100)" />
     <rect x="50" y="50" width="100" height="100" stroke="black" fill="none" transform="rotate(10 100 100)" />
     <rect x="50" y="50" width="100" height="100" stroke="black" fill="none" transform="rotate(20 100 100)" />
    ...

1.5.2. Create SVG-pearl-chain ⭐

Captain CiaoCiao wants to give a pearl necklace to his beloved Bonny Brain. This is made of three different gemstones: Sapphire (blue), Emerald (green), Spessartite Garnet (orange). He would like to have a design proposal in which the colors are randomly arranged.

The following is for an SVG document with three circles:

<svg height="100" width="1000">
 <circle cx="20" cy="20" r="5" fill="blue" />
 <circle cx="30" cy="20" r="5" fill="green" />
 <circle cx="40" cy="20" r="5" fill="orange" />
</svg>

Task:

  • Create an SVG output on the command line with 50 circles side by side.

1.5.3. Sum numbers from the command line ⭐

Captain CiaoCiao needs a program to enter number of captured Liretta from his individual raids. This should be entered from the command line to add them up.

Task:

  • Create a new class SummingCalculator.

  • Take numbers via the Scanner until 0 is entered. Negative numbers are also allowed, because Captain CiaoCiao is also — though rarely — robbed. Ignore possible overflows caused by numbers that are too large.

  • After 0 is entered, the sum should be printed.

Example:

12
3
-1
0
Sum: 14

1.5.4. Go through a mathematical phenomenon ⭐

In mathematics, an iteration is a repeated calculation starting with a starting value until a certain condition is met. In calculations, iterations are an important procedure to improve the solution at each additional step after an initial approximation.

Task:

  • Declare a double variable t between 0 (inclusive) and 10 (exclusive) with the following line:

    double t = Math.random() * 10;
  • Multiply t by 2 if t < 1. However, if t >= 1, subtract 1.

  • Put this calculation into a while loop that should end when t is less than or equal to 0.

Example:

  • The output might evolve like this:

    9.835060881347246
    8.835060881347246
    7.835060881347246
    6.835060881347246
    ...
    0.75
    1.5
    0.5
    1.0

1.5.5. Quiz: How many stars? ⭐

How many asterisks would appear on the console for loop A and B?

A:
for ( int stars = 0; stars <= 7; stars = stars + 2 )
  System.out.println( "***" );
B:
for ( int stars = 10; stars < 0; stars++ )
  System.out.println( "**" );

Remember: stars++ is an abbreviation for stars=stars+1.

1.5.6. Calculate products for faculties ⭐

For the new fleet Rigel VII, Bonny Brain must select the lead officers; the choices are Paul Peldrion, Kate Muggl, Robinson Langdon, Lienn Langdon. However, Bonny Brain is unsure which person should take which role; there are: Commander, First Officer, Second Officer, Third Officer.

There are many possibilities of which person takes which role. How many possible arrangements of different elements there are in a row is told by the so-called permutation. A permutation without repetition is calculated by the factorial. With four persons there are 1 × 2 × 3 × 4 = 24 possible arrangements.

The factorial of a natural number is formed over the product of the numbers according to the following pattern:

n! = 1 × 2 × 3 × …​ × (n - 1) × n

It holds that 0! = 1.

Task:

  • Write a Java program that reads a non-negative integer from the command line and displays the calculation.

Example:

  • Input: 9 → Output: 9! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 = 362880

  • Input: 3 → Output: 3! = 1 * 2 * 3 = 6

  • Input: 0 → Output: 0! = 1

  • Input: 1 → Output: 1! = 1

  • Input: -1 → Output: Number must not be negative

Use the data type long internally.

Question: From which number on will there be "problems"? How do the problems show up, and how can we detect them? Can Math.multiplyExact(long x, long y) help us?

1.5.7. Determine if a number is formed by factorial ⭐

Jar Jar Dumbs has been instructed by Bonny Brain to write down all possible arrangements for a given number of people. Before looking at the list, she counts the number to determine if all permutations have been listed.

We saw how to calculate the factorial for a natural number in the previous assignment. But how can we find out if a number is a factorial? We know that 9! = 362880, but what about 212880 or 28?

Task:

  • Write a program that reads a natural number from the command line, and outputs whether the number is a factorial.

Example:

  • Number is factorial:

    Enter a number:
    362880
    362880 = 9!
  • Number is not a factorial:

    Enter a number:
    1000
    1000 is not a factorial

Test if the number is divisible by 2, 3, 4, 5 …​.

1.5.8. Find smallest and largest digit of a number ⭐

Bonny Brain knows, of course, that decimal numbers consist of digits from 0 to 9. Since the boat ride is long and boring, she has come up with a game: she names an integer to the crew, and whoever names its largest and smallest digits on the fastest way, will obtain one Liretta

Task:

  • Given any number (positive or negative), stored in a long.

  • Using a program, help find the smallest and largest digits of the stored number.

Examples:

  • 12345 → 1, 5

  • 987654 → 4, 9

  • 11111 → 1, 1

  • 0 → 0, 0

  • -23456788888234567L → 2, 8

1.5.9. Quiz: Not like this from 1 to 100 ⭐⭐

The compiler does the work for us of checking programs syntactically. Now let’s play compiler!

The following program is supposed to add the numbers from 1 and up to 100 and output the result. Unfortunately, there are some errors in the program.

class Sümme {
  private static int getSum() {
    int j == 0;
    for ( /* int */ i = 0, i <= 1OO, j++ );
      j += i
    ;
  }
  public static void Main( String aarg ) {
    system.out.println( getsum() );
  }
}

Assign the errors to the following types:

  1. syntactic errors

  2. semantic errors

  3. style guide violations

1.5.10. A flag in the wind through nested loops ⭐

Task:

  • Create the following output that looks like a small flag:

    1
    2 2
    3 3 3
    4 4 4 4
    5 5 5 5 5

Optional: The output should appear as a tree in the sense that all lines are centered.

1.5.11. Output simple chessboard ⭐

Captain CiaoCiao loves German Checkers — a variant of a game of a checkers. He regularly participates in competitions and finds that the board sometimes varies in size. Sometimes it is 8 × 8 squares, sometimes 10 × 10 squares, he has also experienced 12 × 12 and 14 × 14.

In order for Captain CiaoCiao to prepare for all possible board sizes, a program should output a chess board on the screen.

Task:

  • Obtain from the command line the height and width of the game board.

  • Draw the board according to its given dimensions by the symbols # and _.

Example:

Checkerboard width: 10
Checkerboard height: 5
_#_#_#_#_#
#_#_#_#_#_
_#_#_#_#_#
#_#_#_#_#_
_#_#_#_#_#

1.5.12. It’s Christmas time: Displaying trees with ornaments ⭐

Christmas is coming soon, and Bonny Brain wants to print Christmas cards. For this, trees of different sizes are needed.

Task:

  • Using loops, write a triangular tree top with a maximum width width on the screen.

  • In each line the string becomes wider by 2 characters until it becomes >= width.

  • Centering is achieved by preceding spaces.

  • The leaves of the tree consist of the multiplication character *.

  • Randomly sprinkle o characters representing Christmas balls into the tree.

Example:

  • Tree with of width 7 (equal to a tree of width 8):

       *
      *o*
     ***o*
    *o*****

1.5.13. Draw fishy stitching motifs ⭐

Bonny Brain loves the sea and wants a scarf with a fish pattern. The sewing machine can stitch >< and > motifs using the symbols ><> and <><.

The following forms a pattern with a repetition of 1, so that first a fish swims to the right and then a fish swims to the left.

><> <><

Task:

  • Write a program that, according to the assignment of a variable repetitions, first places the fish ><> repetitions times one after the other and then places the fish <>< repetitions times one after the other . The line should itself be repetitions many times one below the other.

Examples:

  • If repetitions is equal to 2, the output shall be:

    ><> ><> <>< <><
    ><> ><> <>< <><
  • If repetitions = 3 the program shall result in the following output:

    ><> ><> ><> <>< <>< <><
    ><> ><> ><> <>< <>< <><
    ><> ><> ><> <>< <>< <><

1.5.14. Trying instead of thinking ⭐⭐

Today’s computers are so fast to try different things at the same time. Password cracking programs work on this principle.

Captain CiaoCiao flips through the "Pirates Daily" and finds a brainteaser:

X

O

L

+

L

X

X

=

T

He has to find a digit for each of the letters L, O, T and X to make the calculation correct. The prize for the puzzle is an old compass, which Captain CiaoCiao desperately wants to win. But unfortunately, he lacks the desire to think.

Task:

  • Develop a program that finds a solution by trying all possibilities.

  • Output all solutions, and mark the solutions in which X, O, L and T are all different.

1.5.15. Get the number of digits of a number ⭐⭐

Bonny Brain wants to right-justify numbers. To do this, spaces are placed in front of the numbers. For example, if you want the width to be 10 characters and the number is 123 (three digits), then seven spaces must be placed before the number to make the width 10.

The first step to determine the number of spaces is to determine the number of digits in a number.

Task:

  • Given is a positive integer n of type int. Output the number of digits of the number. Do not use (""+n).length(), that would be too simple …​

Examples for n and the expected output:

  • 12344

  • 31

  • 01

  • Integer.MAX_VALUE10

1.6. Methods

Methods are important because this way we can centralize common code and also give objects an API to access for clients.

1.6.1. Drawing hearts ⭐

Since Captain CiaoCiao loves his crew, there can’t be enough hearts.

Task:

  1. Create a new class LinePrinter. Put a static method line() into the class, which writes a line of ten hearts. Java can store and output the Unicode character "♥" in strings.

  2. Create a new class LinePrinterDemo that has a main(..) method and calls line().

1.6.2. Implement overloaded line methods ⭐

Next, let’s look at methods that can be passed something. Also, a method name can be used multiple times: We talk about overloaded methods.

Task:

  • A method line(int len) will print a line of length len with minus sign ("-") on the console. For example line(3) will print --- onto the screen.

  • The method line(int len, char c) shall be callable with custom fill characters. So line(2, 'x') outputs the line xx on the screen. Can the first method use the second?

  • Add another overloaded method line(String prefix, int len, char c, String suffix) that sets a start string before the line and an end string after the line. For example, line("╠", 3, '═', "╣") returns ╠══╣. The line inside is 3 characters long, not the entire string.

Remember: you don’t have to implement all three methods completely with a loop. If you’re smart, you’ll forward from one method to the other.

Add the overloaded methods to the class LinePrinter.

1.6.3. Standing straight ⭐

In the past, the simple-minded set the mast at the wrong angle. A pirate doesn’t always have to be upright, but the mast does!

If you take a triangle, it can come in a variety of shapes. There are acute-angled triangles, obtuse-angled triangles, equilateral triangles, and right-angled triangles, among others. As a reminder, triangles are right-angled if c2 = a2 + b2.

Task:

  • Create a new class RightTriangle and write a new method; use the following code as a template:

    class RightTriangle {
      public static boolean isRightTriangle( double a, double b, double c ) {
        // Your implementation goes here
      }
    }
  • The method should take three sides of a triangle and return true if it is a right triangle, false otherwise.

  • Remember: each parameter a, b, c can stand for the cathetus or hypotenuse.

Example:

  • isRightTriangle(3, 4, 5)true

  • isRightTriangle(5, 4, 3)true

  • isRightTriangle(5, 12, 13)true

  • isRightTriangle(1, 2, 3)false

  • isRightTriangle(1, 1, Math.sqrt(2))false

The last example shows well that computational inaccuracy is a problem. Math.sqrt(2) * Math.sqrt(2) is (in the output) 2.00000000000004 and not exactly 2.

1.6.4. Calculate Collatz sequence ⭐

Lothar Collatz defined a sequence of numbers in 1937 that is now known as the Collatz sequence. It is defined as a mapping that follows a number n as follows:

  • nn/2 if n is even,

  • n → 3n + 1, if n is odd.

  • The sequence is finished when 1 is reached.

If we start, say, with n = 7, the algorithm runs through the following numbers:

  • 7 → 22 → 11 → 34 → 17 → 52 → 26 → 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

Every sequence ends with 4, 2, 1, but the reason is still an unsolved one in mathematics.

Task:

  • Create a class Collatz with a method void collatz(long n).

  • Create a main(…​) method, and compute the Collatz sequence sequence with a starting value of 27.

  • Write a new method long collatzMax(long n), which returns the largest intermediate value reached.

  • How can we program collatz(…​) recursively, so that the method returns the maximum assumed value as result? Attention, the signature must be changed! (Why?)

1.6.5. Create multiplication table ⭐

Bonny Brain has added two new products to the Magical Company: flamethrower and fire extinguisher. The flamethrower costs 500 Liretta, the fire extinguisher costs 100 Liretta.

In order to be able to read quickly what the price is in case of a larger purchase, a table should be created in HTML:

QuantityFlamethrowerFire extinguisher

1

500

100

2

1000

200

3

1500

300

…​

…​

…​

In HTML, a table is represented as follows:

<html>
<table>
<tr><th>Quantity</th><th>Flamethrower</th></tr>
<tr><td>1</td><td>500</td></tr>
<tr><td>2</td><td>1000</td></tr>
</table>
</html>

Task:

  • On the screen, create the HTML table shown with the number 1 to 10.

  • Consider where own methods would be useful.

You can copy the generated HTML at https://jsfiddle.net/ and "run" it to see the result.

1.7. Suggested solutions

1.7.1. Write an SVG circle on the console ⭐

com/tutego/exercise/lang/SvgCircle1.java
package com.tutego.exercise.lang;

public class SvgCircle1 {
  public static void main( String[] args ) {
    System.out.println(
     "<svg height='400' width='1000'><circle cx='100' cy='100' r='50' /></svg>"
    );
  }
}

In the first step we build a new class, because program code outside of classes is not allowed in Java. Next, we put into the special main method the class. The JVM calls main(…​) by itself when the program starts, so we put in here exactly the part we want executed at the beginning.

To better group the solutions thematically, they are placed in Java packages; this is what the first declaration, package com.tutego.exercise.lang stands for. Packages are introduced in chapter Classes, Objects, Packages, the own solutions do not need to be put into packages for this time.

To print the circle, we can create a file circle.html, open it in the editor and copy the SVG part into it, then open the file with the web browser and print the page.

com/tutego/exercise/lang/SvgCircle2.java
System.out.println( "<svg height='400' width='1000'>" );
System.out.println( " <circle cx='100' cy='100' r='50' />" );
System.out.println( "</svg>" );

System.out.println(
  "<svg height='400' width='1000'>\n <circle cx='100' cy='100' r='50' />\n</svg>"
);

The difference between print(…​) and println(…​) is that the latter method automatically writes a newline. So if the task is to write several lines one below the other, we can write each line with the println(…​) method.

There is another way, and that is to put a newline inside the string. A string inside double quotes cannot span multiple lines. A line break inside a string is therefore wrong. The solution is a so-called escape sequence; for a line break this is \n. If we use the method println(…​), we do not have to put \n at the end, unless we really want to have two line breaks at the end.

Large strings can be splitted into multiple substrings using the plus operator so that they are more readable. This way we can actually convert each of them into one line of Java code.

com/tutego/exercise/lang/SvgCircle3.java
System.out.println(   "<svg height=\"400\" width=\"1000\">\n"
                    + " <circle cx=\"100\" cy=\"100\" r=\"50\" />\n"
                    + "</svg>" );

The last part of the exercise is also solved with an escape sequence. Besides \n the escape sequence \" puts a double quote in a string.

As of Java 15, there are text blocks for multiline texts. This allows another solution:

com/tutego/exercise/lang/SvgCircle4.java
System.out.println( """
                    <svg height="400" width="1000">
                     <circle cx="100" cy="100" r="50" />
                    </svg>""" );

1.7.2. Access variables and output their assignments

Below I present three different solutions that differ in how the SVG element is output to the screen.

com/tutego/exercise/lang/SvgCircleWithVariables1.java
int x = 100;
int y = 110;
double r = 20.5;

System.out.println( "<svg height=\"100\" width=\"1000\">" );
System.out.print( "  <circle cx=\"" );
System.out.print( x );
System.out.print( "\" cy=\"" );
System.out.print( y );
System.out.print( "\" r=\"" );
System.out.print( r );
System.out.println( "\" />" );
System.out.println( "</svg>" );

The first solution simply splits the constant and variable parts into different System.out.println(…​) and System.out.print(…​) calls.

com/tutego/exercise/lang/SvgCircleWithVariables2.java
int x = 100, y = 110;
double r = 20.5;
System.out.println(
    "<svg height=\"100\" width=\"1000\">\n"
  + " <circle cx=\"" + x + "\" cy=\"" + y + "\" r=\"" + r + "\" />\n"
  + "</svg>"
);

The second solution uses the ability to concatenate strings with the + operator. When concatenating, Java is flexible enough that anything that is not a string is converted to a string and then appended.

com/tutego/exercise/lang/SvgCircleWithVariables3.java
int x = 100, y = 110;
double r = 20.5;

System.out.printf(   "<svg height=\"100\" width=\"1000\">\n"
                   + " <circle cx=\"%d\" cy=\"%d\" r=\"%s\" />\n"
                   + "</svg>\n%n",
                   x, y, r );

Also the third solution results in the same string, only we use a format string and the method printf(…​). There are three placeholders in the format string. That is twice %d for the integer, and we format the floating point number using %s. This format specifier is so generic that it can be used for all data types. We then get the English representation where the decimal places are separated by a period, which is the correct notation for floating point numbers with decimal places for the SVG specification. %n results in a platform-specific end-of-line character and is an alternative to \n.

1.7.3. Quiz: value ranges

The product of 1,000,000 × 1,000,000 is 1E+12, and that exceeds the value range of the int data type, which only goes as high as 2,147,483,647. 1E+12 needs 40 bits, and int offers only a width of 32 bits, so the upper 8 bits are truncated. The situation is different with long, which is 64 bits long and allows a range of values up to 9,223,372,036,854,775,807 (no, you don’t have to remember the ranges digit by digit).

The following gives the correct output because the integers are multiplied as long:

long number = 1_000_000;
System.out.println( number * number );

Large numbers can be made more readable in Java by using an underscore.

Alternatives:

System.out.println( 1_000_000 * 1_000_000L );
System.out.println( 1_000_000L * 1_000_000 );
System.out.println( 1_000_000L * 1_000_000L );
System.out.println( (long) 1_000_000 * 1_000_000 );
System.out.println( 1_000_000 * (long) 1_000_000 );

1.7.4. Quiz: Does this add up?

The result is not exactly 0.0. The reason is that double cannot represent the number 0.1 precisely. 0.1 is a difficult number which, simply put, cannot be represented as a sum of fractions of the type 1/2n. 0.1 has in the IEEE-754 format the bit pattern 0|01111011|1001_1001_1001_1001_101, symbolically | separates the sign bit from the exponent and from the mantissa, and the underscore makes the period visible. This bit pattern corresponds back to 0.100000001490116119384765625, an error of about 1.49 × 10-9. If 0.100000001490116119384765625 is now added ten times, 1.0 cannot come out.

There are several solutions to the problem :

  1. instead of double use the class BigDecimal.

  2. accept the inaccuracy and round in the output, which can be done with printf(…​).

  3. A monetary amount can be stored internally in cents; instead of storing 0.1 Liretta, for example, a program can store 10 Lirettacent and display that later in the output as 0.1.

1.7.5. Generate random circles

com/tutego/exercise/lang/SvgCircleWithRandomRadius.java
int x = 100, y = 110;
double r = Math.random() * 10 + 10;

System.out.printf(
  "<svg height=\"100\" width=\"1000\">\n "
  + "<circle cx=\"%d\" cy=\"%d\" r=\"%s\" />\n</svg>\n%n",
  x, y, r );

System.out.printf(
  Locale.ENGLISH,
  "<svg height=\"100\" width=\"1000\">\n "
  + "<circle cx=\"%d\" cy=\"%d\" r=\"%.2f\" />\n</svg>\n%n",
  x, y, r );

The method Math.random() produces a floating point number from 0 to true less than 1. If we multiply the result by 10, we get a floating point number between 0 and less than 10. If we add 10 to this, the random number will be between 10 and less than 20.

Since the floating point number is random, it also has a large number of decimal places. The format specifier %s also lists all the decimal places. It might look like this:

<svg height="100" width="1000">
 <circle cx="100" cy="110" r="17.807835163311744" />
</svg>

There is another solution, and the listing shows an alternative. We no longer use the %s format specifier, but %f. This is exactly the format specifier intended for floating point numbers. However, it has one property that now gets in the way: the output is localized, i.e. in a local language. If we use our program on an different localized operating system, the separator for the decimal places might not be ., but ,. This is wrong for SVG. The solution is to pass the language as the first argument of the printf(…​) method. We can also specify the number of decimal places. To do this, we make the format specifier a bit more precise. Instead of just %f it will be %.2f for two decimal places. You can see from the output that rounding is also done here.

<svg height="100" width="1000">
 <circle cx="100" cy="110" r="17.81" />
</svg>

1.7.6. Quiz: Dazed and Confused

There are three problems:

  1. Variables should be in English, and not contain for example umlauts.

  2. tmp is not a good name, it is about perimeter or area, so the variable should be called area or diameter.

  3. The function of a variable must not change in the program flow. In the result: better two local variables:

double diameter = 2 * (height + width);
System.out.println( diameter );
double area = height * width;
System.out.println( area );

1.7.7. Process user input

com/tutego/exercise/lang/SvgCircleWithConsoleCoordinates.java
int x = new java.util.Scanner( System.in ).nextInt();
int y = new java.util.Scanner( System.in ).nextInt();

double r = Math.random() * 10 + 10;

System.out.printf(
    Locale.ENGLISH,
    "<svg height=\"100\" width=\"1000\">\n "
    + "<circle cx=\"%d\" cy=\"%d\" r=\"%.2f\" />\n</svg>\n%n",
    x, y, r );

As stated in the exercise, new java.util.Scanner(System.in).nextInt() can be used to read in an integer. We do this twice. The rest of the program remains the same as the previous one.

1.7.8. Quiz: Check in-between

Python knows range checks of the type min < between < max, but this doesn`t work in Java. The reason is that the types do not match anymore. min < between leads to a boolean, and a comparison boolean < double is invalid; the compiler reports operator '<' cannot be applied to 'boolean', 'double' for example.

1.7.9. Check if loot can be shared fairly

com/tutego/exercise/lang/FairShare.java
System.out.println( "Number of bottles in total?" );
int bottles = new java.util.Scanner( System.in ).nextInt();

int captainsBottles = bottles / 2;
int crewsBottles    = bottles - captainsBottles;

System.out.println( "Bottles for the captain: " + captainsBottles );
System.out.println( "Bottles for all crew members: " + crewsBottles );

System.out.println( "Number of crew members?" );
int crewMembers = new java.util.Scanner( System.in ).nextInt();
System.out.println( "Fair share without remainder? " + (crewsBottles % crewMembers == 0) );

In the first step, we read in an integer via the Scanner and initialize our variable bottles for the loot. Since the captain gets half of the bottles, we divide the input by 2. In the next step we calculate what the crew members get, and don`t choose the solution that we divide by 2 again, because with odd numbers we get the problem that the two halves don`t add up anymore. (If you want to try this out, give the result of 5 / 2 + 5 / 2.) To make sure this doesn`t happen, we subtract the captain`s share from bottles to obtain what the crew gets.

Finally, we have to find out if the number of leftover bottles can be divided fairly by the number of crew members. To do this, the program first asks for the number of crew members. The remainder operator will tell us if the division pans out or if there is a remainder. If the division goes up, the remainder will be 0. In that case, the loot can be divided fairly.

1.7.10. Do two numbers share the same digit?

com/tutego/exercise/lang/HasCommonDigits.java
System.out.println( "Enter two numbers between 0 and 99:" );
int number1 = new java.util.Scanner( System.in ).nextInt() % 100;
int number2 = new java.util.Scanner( System.in ).nextInt() % 100;

int number1digit1 = number1 / 10;
int number1digit2 = number1 % 10;
int number2digit1 = number2 / 10;
int number2digit2 = number2 % 10;

boolean hasCommonDigits =    number1digit1 == number2digit1
                          || number1digit1 == number2digit2
                          || number1digit2 == number2digit1
                          || number1digit2 == number2digit2;
System.out.println( hasCommonDigits );

First, we read in two numbers, and the % 100 expression ensures that the numbers do not go over 100, but are correctly — truncated to the range 0 to 99 as required by the specification —.

To understand the solution, let’s go back to the example from the problem definition. We have the numbers 12 and 31. How do we proceed? Ultimately, we have to test whether 1 occurs in 31 or 2 occurs in 31. In other words, we have to test whether 1 equals 3 or 1 equals 1 or 2 equals 3 or 2 equals 1. In our case, 1 equals 1, so the result is true.

To translate the entire procedure into a Java program, we need to extract the first and second digits. For this we need a bit of mathematics. If we divide an integer by 10, it cuts off the last digit. If we divide 12 by 10, we are left with 1. The operations on integers lead to a result that is again an integer. Conversely, if we need the last digit, we use the remainder operator %. Thus 12 % 10 is equal to 2.

This way we create four variables and test if we can find digits of the first number in the second number.

1.7.11. Convert currency amount to coins

com/tutego/exercise/lang/CoinMachine.java
System.out.println( "Please the enter amount of money:" );
double input = new java.util.Scanner( System.in ).nextDouble();
int cents = (int) (input * 100);

System.out.println( cents / 200 + " x 2 Liretta" );
cents %= 200;

System.out.println( cents / 100 + " x 1 Liretta" );
cents %= 100;

System.out.println( cents / 50 + " x 50 Lirettacent" );
cents %= 50;

System.out.println( cents / 20 + " x 20 Lirettacent" );
cents %= 20;

System.out.println( cents / 10 + " x 10 Lirettacent" );
cents %= 10;

System.out.println( cents / 5 + " x 5 Lirettacent" );
cents %= 5;

System.out.println( cents / 2 + " x 2 Lirettacent" );
cents %= 2;

System.out.println( cents + " x 1 Lirettacent" );

The solution of the task is to be realized with the two operators division and remainder. After the command prompt and reading the floating point number, we perform the first trick: We convert the amount to cents. This way it is easier to calculate.

The next steps always occur in pairs. First we calculate the number of coins, then we calculate how many coins remain. Let’s take the example with 12.91 Liretta; that’s 1,291 Lirettacent. The total number of cents divided by 200 gives us a positive integer and is exactly the result of how many 2-Liretta pieces are in total. Now we need to move on to the remainder, and this is where the remainder operator comes in handy. If we take cents % 200, we get a smaller number, and we can continue next with the 1-Liretta piece, which is 100 cents. The game continues like this until we get to one cent. Then there is nothing left.

1.7.12. One bottle of rum, 10 bottles of rum

com/tutego/exercise/lang/NumberOfBottles.java
int noOfBottles = 1;  // or 0, 1, 99, ...

System.out.println(   noOfBottles + " "
                    + (noOfBottles != 1 ? "bottles" : "bottle") + " of rum" );

System.out.printf( "%d bottle%s of rum%n",
                   noOfBottles, noOfBottles != 1 ? "s" : "" );

Depending on the variable noOfBottles we choose the ending. Two ideas are realized here:

  1. The first suggestion uses string concatenation and builds bottle or bottles there depending on noOfBottles. The methods print(…​) and println(…​) allow only one argument, so a string must be concatenated before.

  2. The second suggestion uses printf(…​), which consists of two parts: the formatting string and a number of formatting arguments. Here we take the ending as a single string, which is either "s" or empty. In the formatting string, %s accesses this ending.

1.7.13. Twenty-one

src/main/java/com/tutego/exercise/lang/Blackjack.java
int dealer = new java.util.Scanner( System.in ).nextInt();
int player = new java.util.Scanner( System.in ).nextInt();

if ( player < 2 || dealer < 2 )
  return;

final int MAX_SCORE = 21;

// Both > 21 -> 0
if ( dealer > MAX_SCORE && player > MAX_SCORE )
  System.out.println( 0 );
// One party > 21 -> the other wins
else if ( player > MAX_SCORE )
  System.out.println( dealer );
else if ( dealer > MAX_SCORE )
  System.out.println( player );
// Both are <= 21 -> Max is best
else
  System.out.println( Math.max( player, dealer ) );

In the first step we read in two integers for dealer and player. Then we check if the inputs are correct in principle, because inputs below 2 lead to the end of the program; the maximum is not checked, you could add that.

Then we declare a constant (for this the keyword final), because we use the number 21 more often and it is always good to keep magic values out of the source code.

Several conditional statements follow.

  1. If both inputs are above the maximum 21 both players have lost, and according to the instructions the output is 0.

  2. The next two conditional statements check whether either player was above 21; if so, the other party won. We already know from the previous conditional statement that the other party was not also above 21, which means we do not need to introduce another check.

  3. In the last case, the two values move below 21 — or are equal to 21 — now we just have to figure out which of them is closer to 21. In the end, however, this is just a question about the maximum. We could answer this question manually or use the Math.max(…​) method of the Java Standard Library.

1.7.14. Quiz: The zero effect

The program compiles, but throws an exception at runtime. The output is:

Infinity
Exception in thread "main" java.lang.ArithmeticException:
/ by zero
...

The first println(10.0/0) works, because you can divide two floating point numbers by 0, and the result is Infinity, i.e. positive infinity. Although zero is an integer, since the dividend is a floating point number, the divisor is also converted to a double.

For integers, a division by 0 is a cause for an ArithmeticException. The different behavior is perhaps strange, but easy to explain: Integers have no special bit pattern that can map to infinity. Floating point numbers, on the other hand, have a special bit pattern for three special values: NaN (Not a Number) and plus as well as minus infinity — these are quite "normal numbers."

1.7.15. Payday

com/tutego/exercise/lang/PayDay.java
double tortsPayment = new java.util.Scanner( System.in ).nextDouble();

double minPayment = 1000;
minPayment -= minPayment * 0.1;
double maxPayment = 1000;
maxPayment += maxPayment * 0.2;

// Solution 1
if ( tortsPayment >= minPayment && tortsPayment <= maxPayment )
  System.out.println( "Good boy!" );
else
  System.out.println( "You son of a bi***!" );

// Solution 2
if ( tortsPayment < minPayment || tortsPayment > maxPayment )
  System.out.println( "You son of a bi***!" );
else
  System.out.println( "Good boy!" );

After asking for Tort’s payment, we next calculate the amount Tort must not go below and the amount Tort must not go above. 10% of 1000 is 100, so Tort must not go below 900 Liretta. For the maximum, it is the other way around: 20% of 1000 is 200, so Tort must not go above 1200 Liretta. What is given here as fixed numbers is calculated dynamically in the program. The advantage is that we can change the bound later and the percentage is calculated dynamically.

Now there are two different solutions. The first solution tests whether Tort’s payment is within the bounds. In the second solution we work with a negation and test if Torts payment is not in the range. You can easily see that the if statement is inverted, and also the blocks in the if-else branch are inverted. In practice it will always be the case that for case distinctions with if-else both variants work and one chooses which one thinks is better understandable. The first solution should be better because it should be more understandable to test if a payment is in a range instead of asking if a payment is not in a range.

1.7.16. Quiz: Wrong branching

Two errors are in the program code: First, the block is indented, but the curly braces are missing; visual indentation does not change semantics in Java. If we were to indent the program correctly without the braces, the first error becomes obvious:

if ( x > y )
  int swap = x;
x = y;
y = x;

As a side note, this causes a compiler error because a variable declaration is not allowed at this point.

We use curly braces for the if block to fix the first error.

There is also a logical error. The program code is wrong because both variables get the same value. This is because x is overwritten with y in the meantime; therefore the temporary variable swap is necessary. Correct is:

if ( x > y ) {
  int swap = x;
  x = y;
  y = swap;
}

1.7.17. Convert liters

com/tutego/exercise/lang/HumanReadableLiter.java
System.out.println( "Enter quantity in liters:" );
double value = new java.util.Scanner( System.in ).nextDouble();

if ( value >= 1 )
  System.out.printf( "approx. %d l", (long) value );
else if ( value >= 0.1 )     // 1 l = 100 cl
  System.out.printf( "approx. %d cl", (long) (value * 100) );
else if ( value >= 0.001 )   // 1 l = 1000 ml
  System.out.printf( "approx. %d ml", (long) (value * 1000) );
else
  System.err.println( "Value too small to display" );

The program starts with a screen output and then asks for a floating point number. We have to remember that the Scanner reads floating point numbers in a localized way.

The actual logic is then like this: We start with the largest unit of measure (liter), and if the number is greater than or equal to the one we are looking for, the program formulates the output. If the value is smaller, the else block checks the next smaller unit. Therefore there are also three if statements. Without the else block, the program will not work either, because we must only run into a single block.

Within a block, we multiply the input by a conversion factor, either 1, which we omit, by 100, or by 1000. Furthermore the floating point number will be converted to an integer. The result will be printed on the screen.

During the conversion, it makes sense to use long as a data type, because this allows the given numbers to be much larger than with an int. Before the type conversion will initiate,the program could test if the floating point number is larger than long.MAX_VALUE, because if the double number is larger than long.MAX_VALUE, the result after the type conversion will always be 9223372036854775807. Example:

System.out.println( (long)385823752375823563765. );  // 9223372036854775807
System.out.println( (long)3658273592838474756474. ); // 9223372036854775807

We could consider introducing constants for 100 and 1000:

final int CENTILITERS_PER_LITER = 100;
final int MILLILITERS_PER_LITER = 1000;

However, we would have to ask ourselves whether this makes the program more readable.

1.7.18. Create SVG circles with random colors

com/tutego/exercise/lang/RandomColor.java
String color;
double random = Math.random();
if ( random < 1. / 3 )
  color = "red";
else if ( random < 2. / 3 )
  color = "green";
else
  color = "blue";
System.out.println( color );

System.out.printf( "<circle cx=\"20\" cy=\"20\" r=\"5\" fill=\"%s\"/>",
                   color );

Math.random() returns us a floating point number between 0 and less than 1. Each number is equally probable, which is also true for the value ranges. Numbers between 0 and less than 0.5 are equally probable as numbers between 0.5 and less than 1.0.

We use this to select the three random colors. Random numbers between 0 and smaller 1/3 are assigned to the color red, numbers between 1/3 and smaller 2/3 are assigned to the color green, otherwise the color is blue. Of course, it could have been the other way around.

1.7.19. Quiz: To which block does the else belong?

It is easy to see from the example that the lack of indentation causes us difficulty in understanding. This should be a reminder to always indent source code correctly so as not to get in the way of the reader. Let’s therefore catch up with the indentation:

if ( true ) {
 if ( false )
   if ( 3!=4 )
     ;
   else
     System.out.println( "ship's kobold" );
 else
   System.out.println( "Pumuckl" );
}

Practically, there is little reason to write truth values directly as literals in the condition statements. However, you should not be afraid of this notation either, because it shows what is happening at the core. Each condition statement expects a truth value, and based on it, the block is either executed or not.

Let’s simplify the program. The first statement is an if(true), and consequently this block is always executed.

if ( false )
  if ( 3!=4 )
    ;
  else
    System.out.println( "ship's kobold" );
else
  System.out.println( "Pumuckl" );

The next condition statement checks with if(false), and that means that the block is not executed. This also means that the embedded condition statement including the inner else is not executed either. Consequently, the output pumuckl remains.

1.7.20. Evaluate input strings for approval

The switch statement is flexible in Java and can be used as a statement and since Java 14 as an expression. In the notation you find labels with colon and also in Java 14 the newer arrow notation. Three proposed solutions are shown.

com/tutego/exercise/lang/DoYouAgree.java
String input = new java.util.Scanner( System.in ).nextLine();

switch ( input ) {
  case "ay":
  case "aye":
  case "ay, ay":
  case "ja":
  case "joo":
    System.out.println( "Keep it up!" );
    break;

  default:
    System.out.println( "Don't you dare!" );
}

After Scanner has read the line with nextLine(), switch compares it with different constants — since the input may contain whitespace, we cannot use the Scanner method next(), because otherwise the return will contain only a string up to the first whitespace.

Quite intentionally, the first proposed solution takes advantage of switch’s ability to map multiple `case blocks to the same program code. Also crucial is the break after the console output, so that the program does not accidentally run from one case block into the other when this is not intended. If the yes block does not catch, it goes into the default block. The default block is executed whenever none of the case blocks have caught.

The second proposed solution uses the modern arrow notation, where a break is no longer necessary and where case blocks can be combined:

com/tutego/exercise/lang/DoYouAgree.java
switch ( input ) {
  case "ay", "aye", "ay, ay", "ja", "joo" -> System.out.println( "Keep it up!" );
  default -> System.out.println( "Don't you dare!" );
}

The console output is a code duplication that can be removed; to solution three:

com/tutego/exercise/lang/DoYouAgree.java
System.out.println(
    switch ( input ) {
      case "ay", "aye", "ay, ay", "ja", "joo" -> "Keep it up!";
      default -> "Don't you dare!";
    }
);

Here switch is used as an expression and returns a string that goes to the screen.

1.7.21. Create rotated SVG rectangles

com/tutego/exercise/lang/SvgRotatingRect.java
System.out.println( "<svg height=\"200\" width=\"200\">" );

for ( int rotation = 0; rotation < 360; rotation += 10 )
  System.out.printf(   " <rect x=\"50\" y=\"50\" "
                     + "width=\"100\" height=\"100\" "
                     + "stroke=\"black\" fill=\"none\" "
                     + "transform=\"rotate(%d 100 100)\" />%n",
                     rotation );

System.out.println( "</svg>" );

We break the problem into three parts. In the first part, we write the header of an SVG element, that is, the start of the SVG container. In the second part we write the loop that increments a variable rotation from 0 to real less than 360 in steps of ten. In the body of the loop we access the changing variable rotation by putting the assignment into the output via printf(…​). The third part is the completion of the SVG container.

1.7.22. Create SVG-pearl-chain

com/tutego/exercise/lang/BonnysPearls.java
System.out.println( "<svg height=\"100\" width=\"1000\">" );
for ( int i = 0; i < 50; i++ ) {
  double random = Math.random();
  String color = random < 1./3 ? "blue" :
                 random < 2./3 ? "green" : "orange";
  System.out.printf( "<circle cx=\"%d\" cy=\"20\" r=\"5\" fill=\"%s\"/>%n",
                     20 + (i * 10), color );
}
System.out.println( "</svg>" );

The assignment mentions 50 circles, so there is also a loop in the center that is executed 50 times. In the body of the loop we form a random number random for the random color. The random number is between 0 and less than 1 and we divide it into three ranges, as we did in a previous solution: There are values between 0 and <1/3, between 1/3 and <2/3, and between 2/3 and <1. Instead of an if statement, the solution uses nested condition operators. We could, of course, compute int random = (int)(Math.random() * 3.0) to get random integer values 0, 1, or 2.

To allow the loop counter i to determine the circle center cx, we multiply i by 10 and add 20 so that in each step the circle moves 10 units to the right. The y-axis and the radius remain the same; only the x-axis and the color are parameterized in the formatting string . Another solution would be to introduce a new variable cx, which is incremented by 10 in each loop pass. However, since cx can be calculated directly from i, this second variable is not necessary.

1.7.23. Sum numbers from the command line

Let’s look at two solutions below.

com/tutego/exercise/lang/SummingCalculator.java
final int END_OF_INPUT = 0;
int sum   = 0;
int input = 0;

do {
  input = new java.util.Scanner( System.in ).nextInt();
  sum += input;
} while ( input != END_OF_INPUT );

System.out.printf( "Sum: %d%n", sum );

Whenever a program needs to do something first and then asks if it will continue, a do-while loop is appropriate. This is what the first program does. Since the algorithm must sum, we declare a variable sum and initialize it with 0. Another variable input stores the user input. Because the input ends with 0 and 0 is a magic value, we declare a constant END_OF_INPUT. A value that causes the loop to end is also called a sentinel.

It is unfortunate that the variable must be declared outside the loop body, but that is because it must be accessed in the while part. Inside the loop, we ask for the input and add it to the sum. Since 0 is the neutral element in the addition, we don`t need to check at this point if the input is already terminated by 0. Instead, we move the check to the while part. Here we check if the variable input is not equal to END_OF_INPUT, i.e. 0, and in that case it goes back to the body for input. If the input was equal to END_OF_INPUT, the loop terminates and we output the sum to the console.

Not ideal in this program is that the variable input takes a larger scope than necessary. This is something one would like to avoid in programming — local variables should not be valid far beyond their use to the end. You would like variables that belong only to the loop to be valid only in the loop. One solution would be, of course, to artificially create a block with { }, but there is another variant. Whether the following notation is really better can be doubted, because the complexity is also higher.

com/tutego/exercise/lang/SummingCalculator.java
final int END_OF_INPUT = 0;
int sum = 0;

for ( int input;
     (input=new java.util.Scanner(System.in).nextInt()) != END_OF_INPUT; )
  sum += input;

System.out.printf( "Sum: %d%n", sum );

In the second solution, there is also a variable for the sum. However, we declare the input variable input inside the for loop. The for loop allows three different segments: In the first one we can declare variables, which are then only valid within the loop, in the second segment there is the condition, in the third part there is the continuation expression, which may also be empty. The condition in the for loop is somewhat complex in this solution, because it combines two steps: first the variable input is assigned, and after the variable has been written to, a check is made whether the input was 0 or not. If the input was not 0, the loop continues into the body where the input is added to the variable sum. If the user enters 0, 0 is also not summed as in the first solution, but the loop terminates and the console output follows.

1.7.24. Go through a mathematical phenomenon

com/tutego/exercise/lang/AlwaysEnding.java
double t = Math.random() * 10;

while ( t > 0 ) {
  System.out.println( t );
 //System.out.printf("%64s%n",Long.toBinaryString(Double.doubleToLongBits(t)));
  if ( t < 1 )
    t *= 2;
  else // t >= 1
    t--;
}

After initializing the variable t with a random value between 0 and less than 10, the loop condition t > 0 decides whether the loop body is executed or not. This sounds different from the task: "Loop until t is less than or equal to 0 ". However, it is exactly the same, because the problem statement refers to when the loop should end, because we always need to specify when the loop should continue, not when it should terminate, in the loop conditions. Therefore, we must negate the condition, and from not t <= 0 follows t > 0.

The condition statements are as in the assignment. We have two alternatives: once t < 1 can be, once t >= 1. We should be careful to execute the condition statement as a real alternative and not omit the else block. This is because there is a dependency: if t < 1, t is multiplied by 2, which can cause t >= 1, so it runs into the next condition statement. In our program it doesn`t matter, because on the next loop pass we`ll end up in that condition statement anyway, but such dependencies between values are something we as developers need to keep in mind. It may well be that developers want to go to the code and include a counter for how many times the loop has been run, and then suddenly this loop counter would no longer be correct. This is because the if-else variant results in more runs on average than if there were two if blocks in a row.

It is interesting why the program ends at all. This is due to two characteristics which "reduce" the set bits:

  1. Subtraction makes the number smaller, and the number "loses" bits.

  2. Multiplication doubles the number, but it is less accurate afterwards, and thus "loses" bits. The number possibly becomes larger than 1 by the multiplication, but then in the next step the number becomes smaller again.

If you want to see the bit pattern of the numbers, you can display the bits by using:

System.out.printf( "%64s%n", Long.toBinaryString( Double.doubleToLongBits( t ) ) );

1.7.25. Quiz: How many stars?

A:

There are 12 asterisks in the first for loop.

B:

There is no pass in the second for loop because 10 < 0 is false, so the loop never passes and the loop body never enters.

1.7.26. Calculate products for faculties

com/tutego/exercise/lang/Factorial.java
System.out.println( "Enter a number:" );
int n = new java.util.Scanner( System.in ).nextInt();

if ( n < 0 )
  System.err.println( "Number must not be negative" );
else if ( n < 2 )
  System.out.printf( "%d! = 1%n", n );
else {
  System.out.printf( "%d! = 1", n );
  long factorial = 1;

  for ( int multiplier = 2; multiplier <= n; multiplier++ ) {
    System.out.printf( " * %d", multiplier );
    factorial *= multiplier;
  }

  System.out.printf( " = %d%n", factorial );
}

After entering an integer, we test in the first condition whether the number is negative. If it is, we output a message and terminate the program, because the following alternatives are in else branches.

The next condition checks whether the input was 0 or 1. Then we print a screen message, because the factorial of 0 is valid, and 0! and 1! are both 1.

If the program enters the second else branch, then the number entered is greater than or equal to 2. We therefore start with a screen output showing the beginning of the sequence. In the following iterations, pairs of a multiplication sign and a number are always written.

The number from the input is of type int, but we have to declare the result with the data type long, because the multiplication quickly leads to very large numbers. Our variable factorial is initialized with 2, because this is the next multiplier after 1 — we do not have to multiply with 1, the neutral element. The loop will keep the multiplier running until we reach the input. In the body of the loop, we output the pair of the multiplication sign, and the multiplier to the console, multiply the multiplier by the previous factorial and update the factorial to the next value.

At the end of the loop, we put an equal sign, output the result, and the program is finished.

Factors quickly become very large, and the range of values of an int value is quickly exhausted. Even a long eventually comes to an end.

Table 1. magnitude
factorial/constantvalue

1!

1

2!

2

3!

6

12!

479.001.600

Integer.MAX_VALUE

2.147.483.647

13!

6.227.020.800

20!

2.432.902.008.176.640.000

Long.MAX_VALUE

9,223,372,036,854,775,807

21!

51.090.942.171.709.440.000

While the factorial of 20 is still representable, we are close to the edge of the largest representable long number. Calculating 21! with the program yields the astonishing result -4249290049419214848; the number is suddenly negative because the sign is stored in the uppermost bit and multiplication leads to a bit pattern in which the uppermost bit is set.

There are three strategies to deal with larger numbers:

  1. We can use the previous code if we know that the value ranges are so limited that we never run out of the value range.

  2. If we know that huge numbers occur, long will not help us. Java provides a data type BigInteger that can represent integers of any size. Thus, the numbers are limited only by the size of the memory space.

  3. Java does not alert us to an overflow, so if we exceed the range of values, Java just keeps calculating. There is a useful method in the Math class that the exercise is talking about: the method Math.multiplyExact(long x, long y) helps us to detect overflows, because it throws an exception when the numbers cannot be multiplied anymore because they become too large for a long.

1.7.27. Determine if a number is formed by factorial

com/tutego/exercise/lang/IsFactorial.java
System.out.println( "Enter a number:" );
long n = new Scanner( System.in ).nextLong();

if ( n < 1 )
  System.err.println( "Factorials are always >= 1" );
else {
  long number  = n;
  long divisor = 2;

  while ( number % divisor == 0 ) {
    number /= divisor;
    divisor++;
  }

  if ( number == 1 )
    System.out.printf( "%d = %d!%n", n, divisor - 1 );
  else
    System.out.printf( "%d is not a factorial%n", n );
}

From the command line we ask for a number, and since factorials can get very large, we will query it as long. If the factorial is less than 1, i.e. 0 or negative, we print a message on the command line and terminate the program, because the rest of the code is executed only if n is greater than or equal to 1.

The algorithm is as described in the tip. We start by dividing the factorial by 2, then by 3, 4, and so on. Let us take 6 as an example. The variables number and divisor also occur in the program:

Table 2. iterations for backward calculation of recursion
stepnumberdivisorquotientremainder

1

6

2

6 / 2 = 3

6 % 2 = 0

2

3

3

3 / 3 = 1

3 % 3 = 0

3

1

4

irrelevant

1 % 4 ≠ 0

In the third step, the remainder is not 0, and the loop ends.

The variable number is initialized with the factorial, i.e. the largest value. We divide this variable number repeatedly by the divisor. We do this as long as we have no remainder after an integer division. Whether a number can be divided by a divisor without a remainder is answered by the remainder operator %. If number % divisor is equal to 0, the division came out without remainder.

At the end the loop will stop because there is always a case where the number cannot be divided by the divisor. The variables number and divisor run towards each other. number gets smaller and smaller with each division, and divisor gets larger and larger due to the increment. In the last step there will be a remainder != 0. When number has arrived at 1, we know that we could divide so many times until we arrive at the beginning of the factorial chain and the user input is a factorial; this is what we output on the screen. If the number is not 1, then it is not a factorial.

1.7.28. Find smallest and largest digit of a number

com/tutego/exercise/lang/SmallestLargestDigit.java
final long n = 30;

long largest  = 0;
long smallest = n == 0 ? 0 : 9;

for ( long value = Math.abs( n ); value != 0; value /= 10 ) {
  long lastDigit = value % 10;
  largest  = Math.max( lastDigit, largest );
  smallest = Math.min( lastDigit, smallest );
}

System.out.println( smallest + ", " + largest );

Given a number n, which can be positive or negative. By repeating the operations division and remainder determination, we extract digit by digit and update two local variables: largest should later contain the largest digit value and smallest the smallest digit value. We initialize the variable largest with 0, and it can become larger in the following, if the number for testing is not equal to 0. For the smallest digit in the variable smallest, we can only start with 9 and perhaps find a smaller number if n is not 0. The condition operator checks exactly this case, because if n is 0, then the smallest and also largest digit value is also 0. The following loop is also not run through.

Positive and negative numbers are to be treated the same, so the Math method abs(…​) turns negative numbers into positive numbers.

It is true that Math.abs(Integer.MIN_VALUE) equals Integer.MIN_VALUE, so the result remains negative. The program does not check this special case.

Since we do not want to change the assignment of n, it is final, we introduce a new variable value with the absolute value in the for loop. If this value is not 0, then the body of the method is executed where the remainder operator extracts the last digit and then the variable largest and smallest are adjusted if necessary. After the loop is run, value is divided by 10. If the result is then not equal to 0, it goes back into the loop body. If the result is 0, then there are no more digits to look at, the loop is aborted, and the smallest and largest values are output to the screen.

1.7.29. Quiz: Not like this from 1 to 100

class Sümme { //'ü' - violation of styleguide, should be english
  private static int getSum() { // prefix get unfavorable - tends to violate style guide
    int j == 0; // == instead of = - syntactic error
    for ( /* int */ i = 0, i <= 1OO, j++ );
      // int must be declared - syntactic error
      // twice , instead of ; - syntactic error
      // 1OO -> 100 - syntactic error
      // j instead of i - semantic error
      // ';' at the end of the line is wrong - semantic error
      j += i
    ; // ; place at the preceding line - style guide
    // return j missing - semantic error
  }
  public static void Main( String aarg ) {
    // cannot be started with this particular method - semantic error
    // array specification is missing - semantic error if program should be startable
    system.out.println( getsum() ); // 'S' instead of 's' - 2 * syntactic error
  }
}

1.7.30. A flag in the wind through nested loops

com/tutego/exercise/lang/NestedLoopsForTrees.java
final int MAX = 5;

// Normal output
for ( int i = 1; i <= MAX; i++ ) {
  for ( int j = 1; j <= i; j++ )
    System.out.print( i );

  System.out.println();
}
System.out.println();

// Centered output
for ( int i = 1; i <= MAX; i++ ) {
  for ( int indent = 0; indent < (MAX - i); indent++ )
    System.out.print( " " );

  for ( int j = 1; j <= i; j++ )
    System.out.print( i + " " );

  System.out.println();
}

The main method contains the logic that produces left-aligned output, and then the centered output. Beforehand, we declare a final variable MAX that determines the number of lines. If we want to modify the number of lines to be output, we simply change this variable and do not have to make changes in various places in the source code, such as changing 5 to 9.

The outer loop runs a loop counter i from 1 to 5. In this case, it takes over the job of executing program code for five lines. The inner loop with the loop counter j writes a single line. The loop counter j is written in the line. The end of the loop counter j is in itself bound to the loop counter i of the outer loop. If the outer loop counter i becomes larger, the line is also getting longer. In the body of the loop, we have to work with print(…​) instead of println(…​), because we want to output the numbers one after the other, but only set a newline at the end of the line. Therefore, the line break is not in the inner loop, but in the body of the outer loop, after everything in the line has been written next to each other.

For centered output of any kind, the line starts with white space at the beginning. We first have to calculate how much white space is needed, and in the next step we also have to write this white space. In our case, the width depends on the line. Each new line leads to a different indentation. But the special thing in our example is that the dependency is reversed: the more lines there are, the less indented it is, and in the first line the indentation is maximum.

If we want to produce or output a string with a certain length in Java, we have different possibilities: Special methods can be resorted to, but here we want to take a very simple way of setting several spaces in a row by hand to realize indentation. Before we write the actual line, we prepend a custom for loop that produces spaces. This loop uses its own loop counter indent. The variable indent is bound to i, but inversely: if i gets larger, the indentation gets smaller, so indent only goes up to MAX - i.

It is left to the reader as an exercise to display this tree reasonably centered also for numbers larger than 10.

1.7.31. Output simple chessboard

com/tutego/exercise/lang/Checkerboard.java
System.out.print( "Checkerboard width: " );
int width = new java.util.Scanner( System.in ).nextInt();

System.out.print( "Checkerboard height: " );
int height = new java.util.Scanner( System.in ).nextInt();

for ( int y = 0; y < height; y++ ) {
  for ( int x = 0; x < width; x++ )
    System.out.print( (x + y) % 2 == 1 ? '#' : '_' );
  System.out.println();
}

First, we read the width and height of the checkerboard. Since the checkerboard in our task does not have to be square, two inputs are needed.

Whenever it comes to drawing rectangles or any form of tables, they are usually nested loops. So it is in our case. We have a loop over all the rows and then an inner loop that goes over the columns. We name the variables x and y, alternatively row, col would also have been appropriate.

The exciting part is inside the loop when we have to decide whether to write a # or an underscore. A little logic is needed here. The characters depend on the position. If x and y change, it has a direct effect on the character. The question now is: How can we make the character to be written dependent on x and y? Let’s test what happens when x and y are added:

0

1

2

3

1

2

3

4

2

3

4

5

3

4

5

6

The even numbers are set in bold, and from this we can see the solution: We add the numbers and test with the remainder operator whether the result is even or odd.

We do not have to handle incorrect values in a special way. If negative numbers or 0 are entered, there is no loop pass.

1.7.32. It’s Christmas time: Displaying trees with ornaments

Before we get into the solution of the problem, let’s see the relationships between the number of spaces for indentation and for the width of the tree.

Table 3. indentation (symbolized by underscore) and tree width
linesindentationasterisk/width

___*

3

1

__***

2

3

_*****

1

5

*******

0

7

It is easy to see that the indentation decrements in ones, while the asterisks increase in twos. In the same way, we can formulate our program that uses two variables in a loop, for indentation and for width, and then decrements and increments the variables accordingly.

com/tutego/exercise/lang/XmasTree.java
int width = 7;

for ( int stars = 1, indentation = (width - 1) / 2;
      stars <= width;
      stars += 2, indentation-- ) {

  for ( int i = 0; i < indentation; i++ )
    System.out.print( ' ' );

  for ( int col = 0; col < stars; col++ )
    System.out.print( Math.random() < 0.9 ? '*' : 'o' );

  System.out.println();
}

The variable width stores the width of the tree, which we initialize with 7 in our example. The loop declares the variables stars and indentation, where indentation is calculated from half the width. We subtract 1 from width so that if the width is odd, we don’t mistakenly put an extra space in front. In the body of the main loop, there are two subloops that create the line. In the first step we have to write the indentation, in the second step we have to output the asterisks of the tree. There is a 90% probability that an asterisk will be output, and a 10% probability that a small o will be output. At the end of the line, we set a newline, and then the loop continues.

1.7.33. Draw fishy stitching motifs

com/tutego/exercise/lang/FishPattern.java
int repetitions = 3;

final String RIGHT_FISH = "><>";
final String LEFT_FISH  = "<><";
final String SPACER     = "   ";

for ( int row = 0; row < repetitions; row++ ) {
  for ( int col = 0; col < repetitions; col++ )
    System.out.print( RIGHT_FISH + SPACER );
  for ( int col = 0; col < repetitions; col++ )
    System.out.print( LEFT_FISH + SPACER );
  System.out.println();
}

For the right and left swimming fish we declare two constants and additionally a constant for the distance between the fish. This spacing is also set at the end of the line, but since the white space is not visible, this is not a problem.

The next loop generates several lines. The number of lines is determined by the value of repetitions. Within each line we use two more loops. The first loop prints repetitions right swimming fish, and the second loop prints repetitions of left swimming fish. At the end of the line, a newline is set, and then we can continue with the main loop.

What is striking about the proposed solution is that the two inner for loops are identical except for the body. We can solve this kind of code duplication as follows:

com/tutego/exercise/lang/FishPattern.java
for ( int row = 0; row < repetitions; row++ )
  for ( int col = 0; col < repetitions * 2; col++ ) {
    System.out.print( col < repetitions ? RIGHT_FISH : LEFT_FISH );
    System.out.print( col != repetitions * 2 - 1 ? SPACER : "\n" );
  }

Instead of running two single loops to repetitions, one loop can also run to 2 * repetitions; then only the body has to decide whether the right or left fish is printed. But this information is provided by the loop counter itself: if it is below half, the fish swims to the right, in the second half it swims to the left. And also a second time the condition operator is used. A test checks if the loop counter col is for the last element: If no, white space is output, otherwise a line break. The first variant will probably be easier to understand when reading.

1.7.34. Trying instead of thinking

com/tutego/exercise/lang/XOLLXXTLT.java
for ( int l = 0; l < 10; l++ ) {
  for ( int o = 0; o < 10; o++ ) {
    for ( int x = 0; x < 10; x++ ) {
      for ( int t = 0; t < 10; t++ ) {
        int xol = 100 * x + 10 * o + l;
        int lxx = 100 * l + 10 * x + x;
        int tlt = 100 * t + 10 * l + t;

        if ( xol + lxx == tlt ) {
          if ( (l != o) && (l != x) && (l != t) &&
               (o != x) && (o != t) && (x != t) )
            System.out.print( "All variables are different: " );

          System.out.printf( "l=%d, o=%d, x=%d, t=%d%n", l, o, x, t );
        }
      } // end for t
    } // end for x
  } // end for o
} // end for l

The twist in the solution is to produce all possible assignments of the four variables. We do this with four nested loops. Each of these loops produces all values from 0 to 9. With four loops, there are 10 × 10 × 10 = 10,000 in total repetitions, which is manageable in terms of execution performance. If we had more variables and larger ranges of values, then the runtime would increase rapidly. Our solution would then probably no longer be feasible.

Once we have generated all possible values by the loops, we calculate xol, lxx and tlt by multiplying by 10 and 100 to move the variables to the right place. This is a property of the positional notation. The number 234 is nothing but 2 × 100 + 3 × 10 + 4, and so xol is nothing but x * 100 + o * 10 + l.

Next, we test if xol + lxx is equal to tlt. There are a large number of solutions here; however, if we want to have only the solutions in which all four variables have different assignments, we must test that. This is done by the inner if statement. If all values are different, we output them to the screen.

More of these nice tasks are provided by Alphametic Puzzles (https://www.gtoal.com/wordgames/alphametic/examples).

1.7.35. Get the number of digits of a number

For the exercise, we want to review different ways of solving the problem. Variants 1 and 2 use loops, and therefore the task is also positioned in this section.

The first approach is the following: We divide a number by 10 until the result is 0. Let us take the following three statements as an example:

System.out.println( 123 / 10 );   // 12
System.out.println(  12 / 10 );   // 1
System.out.println(   1 / 10 );   // 0

By dividing by 10 the number gets smaller and smaller until the result is 0 at some point. We just have to loop the divisions, increment a counter, and the number of digits is determined. The termination criteria is when the result of the division is 0.

com/tutego/exercise/lang/NumberOfDigits.java
int digits = 1;
for ( int number = n / 10; number != 0; number /= 10 )
  digits++;
System.out.println( digits );

The number of digits is at least 1, which is why the variable digits defaults to 1.

Divisions are relatively expensive for the computer, so we can also work with the reverse way, multiplying a number by 10 until it is above the given number. This is what the second solution does:

com/tutego/exercise/lang/NumberOfDigits.java
int digits = 1;
for ( long powersOfTen = 10; powersOfTen <= n; powersOfTen *= 10 )
  digits++;
System.out.println( digits );

One detail is the datatype long, because if we multiply a very large integer by 10, it will cause an overflow and the comparison would not be correct.

The other proposed solutions do not use loops, but a slightly different approach.

Proposed solution 3 uses the idea of binary search. However, we do not search for any element, but for the number of digits.

com/tutego/exercise/lang/NumberOfDigits.java
if ( n >= 10_000 ) {
  if ( n >= 10_000_000 ) {
    if ( n >= 100_000_000 ) {
      if ( n >= 1_000_000_000 )
        System.out.println( 10 );
      else
        System.out.println( 9 );
    }
    else
      System.out.println( 8 );
  }
  else if ( n >= 10_0000 ) {
    if ( n >= 1_000_000 )
      System.out.println( 7 );
    else
      System.out.println( 6 );
  }
  else
    System.out.println( 5 );
}
else if ( n >= 100 ) {
  if ( n >= 1000 )
    System.out.println( 4 );
  else
    System.out.println( 3 );
}
else if ( n >= 10 )
  System.out.println( 2 );
else
  System.out.println( 1 );

Numbers of type int can be at most 10 digits long. So first we query whether the number has more or less than five digits, i.e. is greater or less than 10_000. If the number is smaller, then we take half of 5 digits, rounded 3 digits and ask if the value is greater or smaller than 100. If the value is greater than 10_000, then we calculate the arithmetic mean between 5 digits and 10 digits, rounded 8 digits, so 10_000_000. We hard-code all possibilities for one digit, two digits, three digits, up to ten digits.

This procedure has advantages and disadvantages. The advantage is that the number of maximum comparisons is clear in advance; a binary search has a logarithmic runtime. A disadvantage could be that the algorithm does not favor ranges of numbers. If we know that smaller numbers are more likely to occur, then we could do something different with the algorithm.

This is what proposed solution 4 does. It works with several nested condition operators and prefers small numbers. The larger the numbers are, the more comparisons need to be made.

com/tutego/exercise/lang/NumberOfDigits.java
int digits = n < 10 ? 1 :
             n < 100 ? 2 :
             n < 1000 ? 3 :
             n < 10000 ? 4 :
             n < 100_000 ? 5 :
             n < 1_000_000 ? 6 :
             n < 10_000_000 ? 7 :
             n < 100_000_000 ? 8 :
             n < 1_000_000_000 ? 9 :
             10;
System.out.println( digits );

The last proposed solution does not use loops or condition statements at all, but uses the logarithm. To repeat:

bx = ax = logb(a) for all a, b > 0 and b ≠ 1.

If we choose b = 10, we get:

10x = ax = log10(a) for all a > 0.

If we put on the right for a again 10x, one recognizes nicely:

log10(10x) = x

For a number like 10x, x is the number of digits we are looking for. We do not have powers of ten, but we can bring the floating point number to an int.

Table 4. Logarithm for different numbers, relation with the number of digits
log10result (double)result (int)

1

0

0

9

0.954242509439

0

10

1

1

19

1.27875360095

1

99

1.9956351946

1

100

2

2

So we have to calculate the logarithm, adjust the result to int and increase it by 1.

com/tutego/exercise/lang/NumberOfDigits.java
  int digits = n == 0 ? 1 : (int) Math.log10( n ) + 1;
  System.out.println( digits );
}

For numbers between 0 and 1, the function values are negative. Approaching 0 from the right, the function value goes to minus infinity. Therefore we consider 0 as a special case and return 1 digit.

1.7.36. Drawing hearts

com/tutego/exercise/lang/LinePrinter.java
public static void line() {
  System.out.print( "♥♥♥♥♥♥♥♥♥♥" );
}

The implementation can be done without much trouble. The only important thing is that the method is static, so that we can call it from another class without necessarily having to build an object of the class.

com/tutego/exercise/lang/LinePrinterDemo.java
LinePrinter.line();

Since the line() method comes from LinePrinter and is not present in LinePrinterDemo, to access the static method we need to put the LinePrinter class name in front of the method name.

1.7.37. Implement overloaded line methods

com/tutego/exercise/lang/LinePrinter.java
public static void line( int len, char c ) {
  while ( len-- > 0 )
    System.out.print( c );
}

public static void line( int len ) {
  line( len, '-' );
}

public static void line( String prefix, int len, char c, String suffix ) {
  System.out.print( prefix );
  line( len, c );
  System.out.print( suffix );
}

We need to implement three methods. The first method we want to implement is the most important one: the method line(int len, char c), which prints a character c a certain number of len on the screen. This is the typical case for a loop. The solution uses an while loop that counts down the number until it becomes 0. Solutions of this type have a disadvantage in practice, because after the while loop the parameter is destroyed, which means that if after the while loop the variable len had to be accessed again for some reason, we would have a problem. However, since our method is very compact, this is fine for us, we don’t need len again.

For the second method, which outputs a minus by default, it is convenient that we can refer to the previously implemented method, which sets in any character a certain number. We can call this method and thus move the "responsibility" for the output away from us.

The last method also delegates, but writes a prefix beforehand on the screen and a suffix after the character in the line.

A call to the methods looks like this:

com/tutego/exercise/lang/LinePrinterDemo.java
int len = new java.util.Scanner( System.in ).nextInt();
LinePrinter.line( len );
System.out.println();

LinePrinter.line( 4, '*' );
System.out.println();

LinePrinter.line( "{", 4, '*', "}" );
System.out.println();

1.7.38. Standing straight

If triangles are right-angled, they satisfy the equation c² = a² + b². But this only works if a and b are smaller than c. Let’s discuss two possible solutions.

com/tutego/exercise/lang/RightTriangle.java
public static boolean isRightTriangle( double a, double b, double c ) {
  return    a * a == b * b + c * c
         || b * b == a * a + c * c
         || c * c == b * b + a * a;
}

If it is unclear in which order the variables come into the method, all possibilities can be tested, i.e. first assume that a is the hypotenuse, then whether b is the hypotenuse, or alternatively c.

Another approach is to sort the variables a, b and c in such a way that in the end c contains the largest number.

com/tutego/exercise/lang/RightTriangle.java
public static boolean isRightTriangle( double a, double b, double c ) {
  // Step 1: propagate the largest value into c

  // If a > c then swap
  if ( a > c ) {
    double swap = a;
    a = c;
    c = swap;
  }

  // If b > c then swap
  if ( b > c ) {
    double tmp = b;
    b = c;
    c = tmp;
  }

  // Step 2: The test
  return a * a + b * b == c * c;
}

To make sure that c contains the largest number, we first test whether a is larger than c. If so, we swap the contents of a and c. We do the same with b and c: If b is greater than c, we swap b and c. The order of a and b is not important, only that the largest number is in c at the end. We realize the actual swap operation by an intermediate variable.

The actual test is simple with a * a + b * b == c * c. However, there remains a problem with computational precision, because an exact == test is difficult with floating point numbers. Tests with some inaccuracy are an option. One possibility would be to use an additional parameter double tolerance for this, as a tolerance that within the range the values are considered equal. A method could look like this:

public static boolean almostEqual( double a, double b, double tolerance ) {
  return Math.abs( a - b ) < tolerance;
}

1.7.39. Calculate Collatz sequence

com/tutego/exercise/lang/Collatz.java
static void collatz( long n ) {
  while ( n > 1 ) {
    System.out.print( n + " -> " );
    if ( n % 2 == 0 )
      n /= 2;
    else
      n = 3 * n + 1;
  }
  System.out.println( 1 );
}

static long collatzMax( long n ) {
  long max = n;
  while ( n > 1 ) {
    if ( n % 2 == 0 )
      n /= 2;
    else {
      n = 3 * n + 1;
      if ( n > max )
        max = n;
    }
  }
  return max;
}

static long collatz( long n, long max ) {
  if ( n > 1 ) {
    if ( n % 2 == 0 )
      return collatz( n / 2, Math.max( n, max ) );
    return collatz( 3 * n + 1, Math.max( n, max ) );
  }
  return max;
}

public static void main( String[] args ) {
  collatz( 27 );
  System.out.println( collatzMax( 27 ) );
  System.out.println( collatz( 27, 0 ) );
  collatz( 20 );
  System.out.println( collatzMax( 20 ) );
  System.out.println( collatz( 20, 0 ) );
}

We write the method collatz(long) and assume an integer n. We need to repeat if n is greater than 1, and abort if n = 1. To find out whether the number is even or odd, we resort to the remainder operator. If the number is even, we divide it by 2, otherwise we multiply the number by 3 and add 1. n /= 2 is a shortcut for n = n / 2. We cannot abbreviate n = 3 * n + 1 by n *= 3 + 1, not only because the readability should be worse, but because n *= 3 + 1 becomes n = n * (3 + 1).

If we want to determine the maximum, the algorithm is almost identical to the previous one. The only difference is in the declaration of the variable max. At the start max is initialized with the argument from the passing and possibly updated during the loop. Since dividing by 2 results in a smaller value, we do not need to adjust max. But during the loop, if the number is increased by multiplying by 3, we update the variable max if necessary.

In the recursive implementation, we need to change the parameter list because we need to transfer the maximum from one recursion step to the next. If we look at the implementation, we notice similarities. If n is greater than 1, we still have something to do, and we go into recursion. If n is equal to 1, then we are at the end and can return the last maximum. If n was really greater than 1, we have to do the test again, whether the number was even or odd. If the number is even, we divide it by 2 and go back to recursion. We need to adjust the maximum, of course. At this point it is unclear whether the variable max is larger or n, because n was changed from the step before. Therefore, a call to Math.max(…​) helps us to calculate the maximum, and with this value we enter the recursion again. We do the same if n was odd.

1.7.40. Create multiplication table

com/tutego/exercise/lang/MultiplicationTable.java
private static void startTable() { System.out.println( "<table>" ); }

private static void endTable() { System.out.println( "</table>" ); }

private static void startRow() { System.out.print( "<tr>" ); }

private static void endRow() { System.out.println( "</tr>" ); }

private static void headerCell( String value ) {
  System.out.print( "<th>" + value + "</th>" );
}

private static void dataCell( String value ) {
  System.out.print( "<td>" + value + "</td>" );
}

private static void dataCell( int value ) {
  dataCell( Integer.toString( value ) );
}

public static void main( String[] args ) {
  final int BASE_PRICE_FLAMETHROWER      = 500;
  final int BASE_PRICE_FIRE_EXTINGUISHER = 100;

  startTable();

  startRow();
  headerCell( "Quantity" );
  headerCell( "Flamethrower" );
  headerCell( "Fire extinguisher" );
  endRow();

  for ( int i = 1; i <= 10; i++ ) {
    startRow();
    dataCell( i );
    dataCell( BASE_PRICE_FLAMETHROWER * i );
    dataCell( BASE_PRICE_FIRE_EXTINGUISHER * i );
    endRow();
  }

  endTable();
}

HTML is a markup language and is part of the technology, but all technological aspects should be separated from the "business logic". In our case, we write several methods, each of which outputs the HTML tags on the command line. The actual main program then doesn’t see what’s happening in the background. Two methods take care of the start and end of a table, two more methods take care of the start and end of a table row. The two dataCell(…​) methods are an exception because they frame the data in HTML tags. dataCell(…​) is overloaded with two parameters, so we can call the method flexible with an integer as well as with a string. Of course, we don’t need to implement both methods completely; it’s enough that the one method with the integer converts it to a string and then delegates it to the other method. For the table header we have a separate method headerCell(String).

main(…​) starts the table and starts the first row. Then three table cells are written for the columns for the header, and the row is completed. This is followed by a loop that generates rows from 1 to 10. Each table row we have to start in HTML, then the loop counter can be written directly into the first column. The second column is given by 500 (price of a flamethrower) times the loop counter; the last column is given by 100 (price of a fire extinguisher) times the loop counter for the row. Finally, we end the row, and at the end of the loop we end the table. Since the prices might change, the program introduces constants.