C Initializer Syntax, and the Mandelbrot Set

CS 321 2007 Lecture, Dr. Lawlor

The overall plan is to continue covering ways to speed up programs with parallelism--threads and multiple processes.  Today's class was mostly anecdotes, though.

Ariane 5 overflow explosion, and the importance of proper signal handling

The Ariane 5 rocket exploded at its first launch due to a simple uncaught integer overflow signal in a silly little subsystem that shouldn't even have been running.

C Initializer Syntax (a complete aside)

So it often happens that you need to initialize a bunch of array elements, or structs, or arrays of structs, or structs of arrays, etc.

C/C++ provide a beautiful syntax for this called "initializers".  You just put the values that will go in the struct's fields or array's elements inside curly braces, separated by commas.  Here's a trivial example: "int arr[3]={1,7,5};".  Each new struct or array element has its own set of curly braces, so an array of arrays requires nested braces: "int arr[3][2]={{1,2,3},{6,5,4}};".  Here's a more complicated example:
struct bar {
int c;
char *d;
int e[4];
};

/* Here's an array of structs, with "initializers" */
bar b[2]={
{3,"I love bananas", {1,2,3,4} },
{8,"Noooooooooo! Not bananas!", {17,32,24,(int)"hike!"} }
};

for (unsigned int i=0;i<sizeof(b)/sizeof(b[0]);i++) {
std::cout<<"b["<<i<<"]:\n";
std::cout<<" c="<<b[i].c<<" d='"<<b[i].d<<"'\n";

std::cout<<" Values in array: ";
for (int j=0;j<4;j++)
std::cout<<b[i].e[j]<<" ";
std::cout<<"\n";
}
(executable NetRun link)

The PPM Image File Format

There are a bunch of image file formats out there--such as JPEG, PNG, and GIF.  For little programs, I prefer a very simple image format called PPM, since it's easy enough to write by hand.  You can't read PPM images in very many Windows programs, although the excellent Photoshop-like program The GIMP or the command-line netpbm tools will read PPM images.

A PPM image consists of just two parts:
For example, this code writes out a small blue and purple PPM image:
#include <fstream>
int foo(void) {
int wid=64, ht=32;

// Create a PPM output image file header:
std::ofstream out("out.ppm",std::ios_base::binary);
out<<"P6\n"
<<wid<<" "<<ht<<"\n"
<<"255\n";

// Write out pixels of the PPM image:
for (int y=0;y<ht;y++)
for (int x=0;x<wid;x++) {
/* Figure out the output red-green-blue pixel color */
unsigned char r,g,b;
r=(x>40)?255:0;
g=y*255/ht;
b=255;
out<<r<<g<<b;
}

return 0;
}
(executable NetRun link)

Note that NetRun (now) automatically displays PPM images if you just name them "out.ppm".

The Mandelbrot Set

The Mandelbrot Set is pretty, but really easy to draw. To find the color of the Mandelbrot Set at pixel (x,y), you first convert the pixel's coordinates into a complex number c--for example, c==x+i*y (usually with some scaling involved).  You then start a second complex number z at zero, and repeatedly compute the following:
    z = z*z + c;
When z gets bigger than 2, stop repeating and compute the pixel's color from the number of iterations and/or the last value of z.

Here's an example program.  We're going to be using threads and processes to speed up this program, so please familiarize yourself with it!
#include <iostream>
#include <fstream> /* for ofstream */
#include <complex> /* for fractal arithmetic */

/**
A linear function in 2 dimensions: returns a double as a function of (x,y).
*/
class linear2d_function {
public:
double a,b,c;
void set(double a_,double b_,double c_) {a=a_;b=b_;c=c_;}
linear2d_function(double a_,double b_,double c_) {set(a_,b_,c_);}
double evaluate(double x,double y) const {return x*a+y*b+c;}
};

int foo(void)
{
// Figure out how big an image we should render:
int wid=320, ht=240;

// Create a PPM output image file header:
std::ofstream out("out.ppm",std::ios_base::binary);
out<<"P6\n"
<<wid<<" "<<ht<<"\n"
<<"255\n";

// Set up coordinate system to render the Mandelbrot Set:
double scale=2.0/wid;
linear2d_function fx(scale,0.0,-1.0); // returns c given pixels
linear2d_function fy(0.0,scale,0.0);

for (int y=0;y<ht;y++)
for (int x=0;x<wid;x++) {
/* Walk this Mandelbrot Set pixel */
typedef std::complex<double> COMPLEX;
COMPLEX c(fx.evaluate(x,y),fy.evaluate(x,y));
COMPLEX z(0.0);
int count;
enum {max_count=100};
for (count=0;count<max_count;count++) {
z=z*z+c;
if (std::abs(z)>2.0) break;
}

/* Figure out the output pixel color */
unsigned char r,g,b;
r=(unsigned char )(z.real()*(256/2.0));
g=(unsigned char )(z.imag()*(256/2.0));
b=255*count/max_count;
out<<r<<g<<b;
}

return 0;
}
(executable NetRun link)