# The Mandelbrot set, step by step (2): CLI version

Posted on

Welcome to the second post of the series where I develop an app that displays the Mandelbrot set using the Go programming language.

## Objectives

In this post I will describe how to put into practice the theory behind the Mandelbrot set. Then I will write a first version of the app that displays the set in the Command Line Interface (or CLI).

## Configure the Mandelbrot set

As we saw in the last post, the Mandelbrot set consists of complex numbers that comply with some rules. These complex numbers are numbers like `-1.2 + 0.5i`, where `-1.2` is the real part and `0.5i` is the imaginary part. To display the Mandelbrot set in a two dimensional cartesian plane like an image, we will use the real part as the `X` axis and the imaginary part and the `Y` axis. Both axis will be delimited by maximum and minimum values.

However in a digital image the `X` and `Y` axis are not measured by complex numbers but by pixels! That’s why we are going to need some method to know which complex number `c` is represented by `pixel(x, y)`.

## From pixel to complex number

A good starting point for our app can be developing the next two methods:

• `toReal(xPixel)` calculates the real part of a complex number.
• `toImag(yPixel)` calculates the imaginary part of a complex number.

Let’s think of a real case scenario: we want to generate a `11x11` pixels image of a Mandelbrot set which limits are `[-2.5, 2.5]` for the real part (`X` axis) and `[-1.0, 1.0]` for the imaginary part (`Y` axis).

Looking at the image, it’s easy to spot some cases that will help us check if our transformation is correct:

Pixel coordinate Expected complex number
`(0, 0)` `-2.5 + 1.0i`
`(10, 10)` `2.5 - 1.0i`
`(5, 5)` `0 + 0i`
`(11, 4)` Error! `x` is out of bounds
`(-1, 0)` Error! `x` is out of bounds
`(0, 11)` Error! `y` is out of bounds
`(0, -1)` Error! `y` is out of bounds

For the transformation, we need to know the boundaries of both real and imaginary parts of the Mandelbrot set. We will also need the width and height of the resulting image in pixels. Let’s create a type `Config` that groups all these values:

Now let’s add the `toReal(xPixel)` and `toImag(yPixel)` methods:

Nice! The app is now able to transform every pixel of our image into a complex number. The next step will be detecting if these numbers are part of the Mandelbrot set.

## Is this number inside the Mandelbrot set?

To verify if a complex number is inside the set, we need to iterate the function we saw in the previous post:

To program the iteration, we must know the math behind complex numbers. A good tip: we will use the perfect square formula: `(a + b)² = a² + 2 * a * b + b²`

Let’s see an example with `c=-0.75 + 0.75i`:

But, when do we stop? For a complex number `c`, in every iteration of the function we need to check the absolute value of z (`|z|`):

`|z| = |(a + bi)| = sqrt(a² + b²)`

• If `|z|` is greater than 2, then `c` is not part of the set.
• If after a maximum number of iterations `|z|` is not bigger than 2, then `c` is part of the set.

Let’s see how the absolute value of some complex numbers varies through iterations:

`n=0` `n=1` `n=2` `n=3` `n=4` `n=5`
A: `-1.0 + 0.0i` `0.00` `1.00` `0.00` `1.00` `0.00` `1.00`
B: `-2.5 + 1.0i` `0.00` `2.69`
C: `-0.75 + 0.75i` `0.00` `1.06` `0.84` `1.35` `2.37`

For some complex numbers (like A) its absolute value never gets bigger than 2. But in other cases, like B and C, the continuous iteration makes them diverge at some point. Depending of the iteration, the complex number is part of the set or not. This is why defining a correct threshold is so important.

Let’s create a `type` that is responsible for verifying if a complex number remains bounded after some iterations:

Nice, we are almost there!

## Loop and print

Now all we need to do is loop through all the pixels of our “image” and print the result in the CLI. Let’s edit the main method:

And now, let’s build the project and run the generated executable file:

If you execute this code in your machine, the result should be what you see in the next image:

## Custom parameters

Unfortunately, the parameters of this Mandelbrot set are hardcoded inside the app. Let’s add command-line flags to customize the result!

Remember to rebuild the app:

Now the app accepts custom values entered from the command line:

## Testing and Refactoring

If you take a look at the source code of the app, you’ll find tests for most part of the code (look for `*_test.go` files). I’ve been following a Test Driven Development (TDD) approach, which means writing tests before actually writing the code itself. These tests provide a safety net to refactor the code without breaking things by accident.

Check the `cc79a24` commit (and the following ones) if you want to see a step-by-step TDD approach of how I’ve developed the `toReal(x)` method.

## Next

There are many improvements that could make the code clearer and more robust, but at this point the app is good enough: it does what we want (prints the Mandelbrot set in the CLI) and does is correctly. In the next post I will enhance the app while adding the ability to generate images in black and white.