A Complex ADT

This is a warmup exercise. It is not compulsory, and may be completed individually or with your lab partner.

This week, we’re taking some of the new data types that we’ve seen over the last few weeks, and asking, “can we make that an ADT?”

First, though, let’s talk about ADTs: Abstract Data Types.

Concrete vs Abstract

Over the last few weeks, we’ve seen a few variations on a structure for holding complex numbers. For example, in the code you’re provided for assignment 1, you get the following structure in mandelbrot.h:

typedef struct _complex {
    double re;
    double im;
} complex;

We say that a type is concrete if a “user” of that type has knowledge of how it works, and conversely, a type is abstract if a user has no knowledge of how it works. By this definition, complex is a concrete type: you may know what a complex value’s real and imaginary parts are directly.

Using concrete types is dangerous: once a user of a type knows about the insides of that data type, and especially if they are given the power to directly use it or change it, you cannot change the “insides” of the type without breaking current software. If, say, we wanted to change our complex structure to instead store the modulus and argument (or magnitude and phase, if you prefer), we would have to change absolutely everywhere that used our type.

Abstraction is our friend here: if we only expose the real and imaginary part through a pair of functions (and the modulus and argument likewise) it doesn’t really matter how the data is stored – how the implementation works – so long as the interface works.

So far, we’ve used .h files to achieve this, where the functions are declared but not defined.

More powerfully, we can make types abstract: in C, we can refer to a structure without knowing its contents if that structure sits behind a pointer. Other languages have similar features, but fundamentally, they boil down to this exact idea.

Compare the above concrete type with this abstract type:

typedef struct _complex *Complex;

That’s much better! You can expose this to a user, and all they know is that there’s a reference to a structure, but not necessarily what’s in that structure.

So, what’s in that structure? We don’t know, and it doesn’t really matter, because all we need is for the interface functions to work.

So, let’s make an ADT.

An ADT’s Interface

Download Complex.h, or copy it into your current directory on a CSE system by running

$ cp /web/cs1511/17s2/week08/files/Complex.h .

Don’t change Complex.h! If you do, the charge on the protons that make up your teeth will be suppressed.

In Complex.h, there are a few new bits of syntax that we’ll be seeing much more often now we’re working with all the moving parts of ADTs. First up, header guards, which allow you to include a file multiple times without it conflicting with itself.

#ifndef _COMPLEX_H_
#define _COMPLEX_H_

// ... code here ...

#endif // !defined(_COMPLEX_H_)

Next, the typedef, which creates our data type. C lets us typedef a pointer type before we know what the type is, because pointers are all the same size; this trick won’t work in any other place.

typedef struct _complex *Complex;

And finally, some function prototypes. It’s a good habit to document the functions prototyped in a header file. We’ve listed the prototypes below, along with brief documentation of what they do as provided in the header file, along with some notes on how you should implement them.

Implementing an ADT

Download Complex.c, or copy it into your current directory on a CSE system by running

$ cp /web/cs1511/17s2/week08/files/Complex.c .

You should implement these functions, based on the notes above. If you want, you could try doing so without the stub code provided; otherwise, we’ve provided enough code to make Complex.c compile.

As an ADT implementation, Complex.c should not contain a main function. How do you run it, or test for correctness, then? You should write an ADT user, and the Testing a Complex ADT activity shows how to do that. (We also have an Autotest set up, designed in exactly the same way.)

To run some simple automated tests:

$ 1511 autotest complexADT

To run Styl-o-matic:

$ 1511 stylomatic Complex.c
Looks good!

You’ll get advice if you need to make changes to your code.

Submit your work with the give command, like so:

$ give cs1511 wk08_complexADT

Or, if you are working from home, upload the relevant file(s) to the wk08_complexADT activity on Give Online.