6.S096 | January IAP 2014 | Undergraduate

# Effective Programming in C and C++

## Assignments

ASSIGNMENTS SUPPORTING FILES
Assignment 1

Problem 1: Floating Point (PDF)

Sample Solution to Assignment 1, Problem 1

Test data: floating.data (ZIP) (This ZIP file contains: 7 .in files and 7 .out files.)

Problem 2: Matrix Manipulation (PDF)

Sample Solution to Assignment 1, Problem 2

Test data: matrix.data (ZIP) (This ZIP file contains: 10 .in files and 10 .out files.)

Problem 3: Matrix Manipulation 2 (PDF)

Sample Solution to Assignment 1, Problem 3

Test data: matrix2.data (ZIP - 20.7MB) (This ZIP file contains: 11 .in files and 11 .out files.)

Problem 4: Transposition Cipher (PDF)

Sample Solution to Assignment 1, Problem 4

Test data: loop.data (ZIP - 19.1MB) (This ZIP file contains: 10 .in files and 10 .out files.)

Assignment 2

Problem 1: Linked List Library (PDF)

Sample Solution to Assignment 2, Problem 1

Solution code and test data: list (ZIP) (This ZIP file contains: 2 .h files, 2 .c files, 1 .make file and 1 .txt file.)

Problem 2: Minimum Spanning Tree (PDF)

Sample Solution to Assignment 2, Problem 2

Solution code and test data: mst (ZIP - 5.3MB) (This ZIP file contains: 1 .make file and 1 .cpp file.)

Problem 3: Rational Number Library (PDF)

Sample Solution to Assignment 2, Problem 3

Solution and test data: rational (ZIP)  (This ZIP file contains: 2 .h files, 3 .cpp files, 1 .make file and 1 .txt file.)

Assignment 3

Problem 1: C++ Linked List Library (PDF)

Sample Solution to Assignment 3, Problem 1

Solution and test data: cpplist (ZIP) (This ZIP file contains: 3 .h files, 4 .cpp files, 1 .make file and 1 .txt file.)

**Assignment 4 (Final Project)
**

Problem: N-Body Gravity Simulation (problem statement available in Lecture Notes for Lecture 8)

Starting environment: nbody (ZIP - 2.2MB) (This ZIP file contains: 5 .h files, 5 .mk files, 7 .cpp files, 1 .make file and 1 .txt file.)

### More Details on Assignment 4 (Final Project)

The following should be included as your final project deliverables:

• Zip file containing the group’s project environment that contains the full source code and can be used for setup.
• Overview of the project: What are the important features you’ve created? What was challenging, and what have you achieved?
• A short write-up describing the division of work. For example, “We decided to have person X and Y work primarily on the physics engine while Z developed the visualization components…”
• A short description of the project’s structure, including descriptions of important classes and their interactions. If you can make a readable diagram of the interactions, you can submit that rather than a text description.
• Screenshots of the results and description of how to operate your project. Examples include images of the visualization in operation, what happens when you run certain n-body systems, etc.

The operative words for the written bits are “short” and “concise”—don’t write an essay. Describe enough to give an overview of the system; if your code is well written, you should have to write very little!

### Code Review

You should choose a bite-sized chunk that will take you approximately 45 minutes to an hour to fully review. The following should be included when submitting a code review:

• The snippet of code you are reviewing: more than 30 lines, less than 100.
• A summary of main points relating to the review (what they did well, major areas for improvement, common issues, general observations).

Use the C++ Coding Standards and Best Practices provided to guide your review.

/*

PROG: floating

LANG: C

*/

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

#include <math.h>

#define ABSOLUTE_WIDTH 31

#define MANTISSA_WIDTH 23

#define EXPONENT_WIDTH 8

#define EXPONENT_BIAS 127

union float_bits {

float f;

uint32_t bits;

};

void print_float( FILE *output, float f ) {

union float_bits t; t.f = f;

uint32_t sign_bit = ( t.bits » ABSOLUTE_WIDTH );

uint32_t exponent = ( t.bits » MANTISSA_WIDTH ) & EXPONENT_MASK;

uint32_t mantissa = ( t.bits  &  MANTISSA_MASK );

if( sign_bit != 0 ) {

fprintf( output, “-” );

}

if( exponent > 2 * EXPONENT_BIAS ) {

fprintf( output, “Inf\n” ); /* Infinity */

return;

} else if( exponent == 0 ) {

fprintf( output, “0.” ); /* Zero or Denormal */

exponent = ( mantissa != 0 ) ? exponent + 1 : exponent;

} else {

fprintf( output, “1.” ); /* Usual */

}

for( int k = MANTISSA_WIDTH - 1; k >= 0; –k ) {

fprintf( output, “%d”, ( mantissa » k ) & 1 );

}

if( exponent != 0 || mantissa != 0 ) {

fprintf( output, " * 2^%d\n", (int) ( exponent - EXPONENT_BIAS ) );

}

}

int main() {

FILE *input  = fopen( “floating.in”,  “r” ),

*output = fopen( “floating.out”, “w” );

size_t N; float f;

fscanf( input, “%zu”, &N );

for( size_t i = 0; i < N; ++i ) {

fscanf( input, “%f”, &f );

print_float( output, f );

}

fclose( input );

fclose( output );

return 0;

}

Below is the output using the test data:

floating:

1: OK [0.004 seconds] OK!

2: OK [0.004 seconds] OK!

3: OK [0.004 seconds] OK!

4: OK [0.004 seconds] OK!

5: OK [0.005 seconds] OK!

6: OK [0.004 seconds] OK!

7: OK [0.004 seconds] OK!

#### « Back to Assignments

/*

PROG: floating

LANG: C

*/

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

#include <math.h>

#define ABSOLUTE_WIDTH 31

#define MANTISSA_WIDTH 23

#define EXPONENT_WIDTH 8

#define EXPONENT_BIAS 127

union float_bits {

float f;

uint32_t bits;

};

void print_float( FILE *output, float f ) {

union float_bits t; t.f = f;

uint32_t sign_bit = ( t.bits » ABSOLUTE_WIDTH );

uint32_t exponent = ( t.bits » MANTISSA_WIDTH ) & EXPONENT_MASK;

uint32_t mantissa = ( t.bits  &  MANTISSA_MASK );

if( sign_bit != 0 ) {

fprintf( output, “-” );

}

if( exponent > 2 * EXPONENT_BIAS ) {

fprintf( output, “Inf\n” ); /* Infinity */

return;

} else if( exponent == 0 ) {

fprintf( output, “0.” ); /* Zero or Denormal */

exponent = ( mantissa != 0 ) ? exponent + 1 : exponent;

} else {

fprintf( output, “1.” ); /* Usual */

}

for( int k = MANTISSA_WIDTH - 1; k >= 0; –k ) {

fprintf( output, “%d”, ( mantissa » k ) & 1 );

}

if( exponent != 0 || mantissa != 0 ) {

fprintf( output, " * 2^%d\n", (int) ( exponent - EXPONENT_BIAS ) );

}

}

int main() {

FILE *input  = fopen( “floating.in”,  “r” ),

*output = fopen( “floating.out”, “w” );

size_t N; float f;

fscanf( input, “%zu”, &N );

for( size_t i = 0; i < N; ++i ) {

fscanf( input, “%f”, &f );

print_float( output, f );

}

fclose( input );

fclose( output );

return 0;

}

Below is the output using the test data:

floating:

1: OK [0.004 seconds] OK!

2: OK [0.004 seconds] OK!

3: OK [0.004 seconds] OK!

4: OK [0.004 seconds] OK!

5: OK [0.005 seconds] OK!

6: OK [0.004 seconds] OK!

7: OK [0.004 seconds] OK!

/*

PROG: matrix

LANG: C

*/

#include <stdio.h>

#include <stdlib.h>

#define MAXN 300

typedef struct Matrix {

size_t R, C;

int index[MAXN][MAXN];

} Matrix;

void read_matrix( FILE *fin, Matrix *matrix ) {

fscanf( fin, “%zu %zu”, &matrix->R, &matrix->C );

if( matrix->R >= MAXN || matrix->C >= MAXN ) {

printf( “Error: tried to read matrix with a dimension larger than %d\n”, MAXN );

exit( EXIT_FAILURE );

}

for( size_t r = 0; r < matrix->R; ++r ) {

for( size_t c = 0; c < matrix->C; ++c ) {

fscanf( fin, “%d”, &matrix->index[r][c] );

}

}

}

void print_matrix( FILE *fout, Matrix *matrix ) {

fprintf( fout, “%zu %zu\n”, matrix->R, matrix->C );

for( size_t r = 0; r < matrix->R; ++r ) {

for( size_t c = 0; c < matrix->C - 1; ++c ) {

fprintf( fout, “%d “, matrix->index[r][c] );

}

fprintf( fout, “%d\n”, matrix->index[r][matrix->C - 1] );

}

}

void mult_matrix( Matrix *a, Matrix *b, Matrix *prod ) {

if( a->C != b->R ) {

printf( “Error: tried to multiply (%zux%zu)x(%zux%zu)\n”, a->R, a->C, b->R, b->C );

exit( EXIT_FAILURE );

}

size_t inner = a->C;

prod->R = a->R;

prod->C = b->C;

for( size_t r = 0; r < prod->R; ++r ) {

for( size_t c = 0; c < prod->C; ++c ) {

prod->index[r][c] = 0;

for( size_t i = 0; i < inner; ++i ) {

prod->index[r][c] += a->index[r][i] * b->index[i][c];

}

}

}

}

int main(void) {

FILE *fin = fopen( “matrix.in”, “r” ),

*fout = fopen( “matrix.out”, “w” );

if( fin == NULL ) {

printf( “Error: could not open matrix.in\n” );

exit( EXIT_FAILURE );

}

if( fin == NULL ) {

printf( “Error: could not open matrix.out\n” );

exit( EXIT_FAILURE );

}

Matrix a, b, c;

fclose( fin );

mult_matrix( &a, &b, &c );

print_matrix( fout, &c );

fclose( fout );

return 0;

}

Below is the output using the test data:

matrix:

1: OK [0.004 seconds]

2: OK [0.004 seconds]

3: OK [0.004 seconds]

4: OK [0.013 seconds]

5: OK [0.009 seconds]

6: OK [0.006 seconds]

7: OK [0.011 seconds]

8: OK [0.011 seconds]

9: OK [0.012 seconds]

10: OK [0.004 seconds]

#### « Back to Assignments

/*

PROG: matrix2

LANG: C

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct Matrix_s {

size_t R, C;

int *index;

} Matrix;

Matrix* allocate_matrix( size_t R, size_t C ) {

Matrix *matrix = malloc( sizeof( Matrix ) );

matrix->R = R;

matrix->C = C;

matrix->index = malloc( R * C * sizeof( int ) );

return matrix;

}

void destroy_matrix( Matrix *matrix ) {

free( matrix->index );

free( matrix );

}

typedef enum {

REGULAR = 0,

TRANSPOSE = 1

} Transpose;

// Allowing reading a matrix in as either regular or transposed

Matrix* read_matrix( FILE *input, Transpose orient ) {

size_t R, C;

fscanf( input, “%zu %zu”, &R, &C );

Matrix *matrix = NULL;

if( orient == REGULAR ) {

matrix = allocate_matrix( R, C );

for( size_t r = 0; r < matrix->R; ++r ) {

for( size_t c = 0; c < matrix->C; ++c ) {

fscanf( input, “%d”, &matrix->index[c + r * C] );

}

}

} else if( orient == TRANSPOSE ) {

matrix = allocate_matrix( C, R );

for( size_t r = 0; r < matrix->C; ++r ) {

for( size_t c = 0; c < matrix->R; ++c ) {

fscanf( input, “%d”, &matrix->index[r + c * R] );

}

}

} else {

fprintf( stderr, “Error: unknown orientation %d.\n”, orient );

exit( EXIT_FAILURE );

}

return matrix;

}

void print_matrix( FILE *output, Matrix *matrix ) {

fprintf( output, “%zu %zu\n”, matrix->R, matrix->C );

for( size_t r = 0; r < matrix->R; ++r ) {

for( size_t c = 0; c < matrix->C - 1; ++c ) {

fprintf( output, “%d “, matrix->index[c + r * matrix->C] );

}

fprintf( output, “%d\n”, matrix->index[matrix->C - 1 + r * matrix->C] );

}

}

Matrix* product_matrix( Matrix *a, Matrix *b ) {

if( a->C != b->C ) {

printf( “Error: tried to multiply (%zux%zu)x(%zux%zu)\n”, a->R, a->C, b->C, b->R );

exit( EXIT_FAILURE );

}

Matrix *prod = allocate_matrix( a->R, b->R );

size_t nRows = prod->R, nCols = prod->C, nInner = a->C;

for( size_t r = 0; r < nRows; ++r ) {

for( size_t c = 0; c < nCols; ++c ) {

prod->index[c + r * nCols] = 0;

for( size_t i = 0; i < nInner; ++i ) {

prod->index[c + r * nCols] += a->index[i + r * nInner] * b->index[i + c * nInner];

}

}

}

return prod;

}

int main(void) {

FILE *fin = fopen( “matrix2.in”, “r” );

if( fin == NULL ) {

printf( “Error: could not open matrix2.in\n” );

exit( EXIT_FAILURE );

}

Matrix *a = read_matrix( fin, REGULAR );

Matrix *b = read_matrix( fin, TRANSPOSE );

fclose( fin );

Matrix *c = product_matrix( a, b );

FILE *output = fopen( “matrix2.out”, “w” );

if( output == NULL ) {

printf( “Error: could not open matrix2.out\n” );

exit( EXIT_FAILURE );

}

print_matrix( output, c );

fclose( output );

destroy_matrix( a );

destroy_matrix( b );

destroy_matrix( c );

return 0;

}

Below is the output using the test data:

matrix2:

1: OK [0.006 seconds]

2: OK [0.007 seconds]

3: OK [0.007 seconds]

4: OK [0.019 seconds]

5: OK [0.017 seconds]

6: OK [0.109 seconds]

7: OK [0.178 seconds]

8: OK [0.480 seconds]

9: OK [0.791 seconds]

10: OK [1.236 seconds]

11: OK [2.088 seconds]

#### « Back to Assignments

``````
#include <"list.h">

#include <stdio.h>
#include <stdlib.h>

struct List_node_s {
List_node *next;
int value;
};

List empty_list( void ) {
return (List) { .length = 0, .front = NULL };
}

List_node* create_node( int value ) {
List_node *new_node = malloc( sizeof( List_node ) );
new_node->value = value;
new_node->next = NULL;
return new_node;
}

void list_append( List *list, int value ) {
if( list->front == NULL ) {
list->front = create_node( value );
} else {
List_node *p = list->front;
for( size_t i = 1; i < list->length; ++i, p = p->next );
p->next = create_node( value );
}
++list->length;
}

void list_delete_from_front( List *list, int  value ) {
List_node *front = list->front;
while( front != NULL && front->value == value ) {
list->front = front->next;
--list->length;
free( front );
front = list->front;
}
}

void list_delete( List *list, int value ) {
list_delete_from_front( list, value );
if( list->front == NULL ) {
return;
}

List_node *prev = list->front;
List_node *p = list->front->next;

while( p != NULL ) {
if( p->value == value ) {
prev->next = p->next;
free( p ); --list->length;
p = prev->next;
} else {
prev = p;
p = prev->next;
}
}
}

void list_insert_before( List *list, int insert, int before ) {
if( list->front != NULL && list->front->value == before ) {
List_node *new_node = create_node( insert );
new_node->next = list->front;
list->front = new_node;
++list->length;
} else {
List_node *prev = list->front;
List_node *next = list->front->next;
while( next != NULL ) {
if( next->value == before ) {
prev->next = create_node( insert );
prev->next->next = next;
++list->length; return;
}
prev = next;
next = next->next;
}
}
}

void list_apply( List *list, int (*function_ptr)( int) ) {
for( List_node *p = list->front; p != NULL; p = p->next ) {
p->value = (*function_ptr)( p->value );
}
}

int list_reduce( List *list, int (*function_ptr)(int, int) ) {
if( list->front == NULL ) {
return 0;
}

int result = list->front->value;

for( List_node *p = list->front->next; p != NULL; p = p->next ) {
result = (*function_ptr) ( result, p->value );
}

return result;
}

void list_print( List list ) {
if( list.front == NULL ) {
printf( "{}\n" );
} else {
printf( "{ " );

List_node *p = list.front;
size_t length = list.length;

while( p->next != NULL && length > 0 ) {
printf( "%d -> ", p->value );
p = p->next; --length;
}
printf( "%d }\n", p->value );

if( length != 1 ) {
printf( "Error: badly formed list.\n" );
exit( EXIT_FAILURE );
}
}
}

void list_clear( List *list ) {
List_node *front = list->front;
size_t length = list->length;

while( front != NULL && length > 0 ) {
List_node *next = front->next;
free( front );
front = next;
--length;
}

if( length != 0 ) {
printf( "Error: failed to clean up list properly.\n" );
exit( EXIT_FAILURE );
}
}
``````

#### Below is the output using the test data:

``````
list:
1: OK [0.002 seconds] OK!
2: OK [0.004 seconds] OK!
3: OK [0.035 seconds] OK!
4: OK [2.175 seconds] OK!
5: OK [0.133 seconds] OK!
6: OK [0.305 seconds] OK!
7: OK [0.061 seconds] OK!
8: OK [0.213 seconds] OK!
9: OK [0.002 seconds] OK!
10: OK [1.054 seconds] OK!
``````
``````
/*
PROG: mst
LANG: C++
*/
#include <vector>
#include <queue>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <unordered_map>

class State {
size_t _node;
double _dist;
public:
inline size_t node()const { return _node; }
inline double dist()const { return _dist; }
};

public:
inline size_t size() const { return _adj.size(); }
void print();
};

inline bool operator<( const State &a, const State &b ) {
return a.dist() > b.dist();
}

size_t nNodes; size_t nEdges; input >> nNodes >> nEdges;

for( size_t e = 0; e < nEdges; ++e ) {
size_t v, w; double weight;
input >> v >> w >> weight;
// Add this edge to both the v and w lists
_adj[v].push_back( State{ w, weight } );
_adj[w].push_back( State{ v, weight } );
}
}

for( size_t i = 0; i < _adj.size(); ++i ) {
std::cout << i << ": ";
for( auto state : _adj[i] ) {
std::cout << "(" << state.node() << ", " << state.dist() << ") ";
}
std::cout << "\n";
}
}

std::unordered_map<int, bool> visited;
std::priority_queue<State> pq;

pq.push( State{ 0, 0.0 } );
double weight = 0.0;

while( visited.size() < adj.size() ) {
auto top = pq.top(); pq.pop();

if( visited.count( top.node() ) == 0 ) {
visited[top.node()] = true;
weight += top.dist();

pq.push( vertex );
}
}
}

return weight;
}

int main() {
std::ifstream input{ "mst.in" };
std::ofstream output{ "mst.out" };

if( input.is_open() ) {
output << std::fixed << std::setprecision( 8 );
output << prim( adj ) << "\n";
} else {
std::cerr << "Could not open mst.in\n";
return 1;
}

return 0;
}
``````

#### Below is the output using the test data:

``````
mst:
1: OK [0.004 seconds]
2: OK [0.004 seconds]
3: OK [0.004 seconds]
4: OK [0.006 seconds]
5: OK [0.093 seconds]
6: OK [0.122 seconds]
7: OK [0.227 seconds]
8: OK [0.229 seconds]
9: OK [0.285 seconds]
10: OK [0.287 seconds]
``````

#### Here are the contents of rational.h:

``````
#ifndef _6S096_RATIONAL_H
#define _6S096_RATIONAL_H

#include <cstdint>
#include <iosfwd>
#include <stdexcept>

class Rational {
intmax_t _num, _den;
public:
enum sign_type { POSITIVE, NEGATIVE };

Rational() : _num{0}, _den{1} {}
Rational( intmax_t numer ) : _num{numer}, _den{1} {}
Rational( intmax_t numer, intmax_t denom ) : _num{numer}, _den{denom} { normalize(); }

inline intmax_t num() const { return _num; }
inline intmax_t den() const { return _den; }

void normalize();
float to_float()const;
double to_double()const;
sign_type sign() const;
Rational inverse() const;
};

std::ostream& operator<<( std::ostream& os, const Rational &ratio );

inline bool operator==( const Rational &lhs, const Rational &rhs ) {
return lhs.num() * rhs.den() == rhs.num() * lhs.den();
}

inline bool operator<( const Rational &lhs, const Rational &rhs ) {
if( lhs.sign() == Rational::POSITIVE && rhs.sign() == Rational::POSITIVE ) {
return lhs.num() * rhs.den() < rhs.num() * lhs.den();
} else if( lhs.sign() == Rational::NEGATIVE && rhs.sign() == Rational::NEGATIVE ) {
return lhs.num() * rhs.den() > rhs.num() * lhs.den();
} else {
return lhs.sign() == Rational::NEGATIVE;
}
}

inline Rational operator*( const Rational &a, const Rational &b ) {
return Rational{ a.num() * b.num(), a.den() * b.den() };
}

inline Rational operator+( const Rational &a, const Rational &b ) {
return Rational{ a.num() * b.den() + b.num() * a.den(), a.den() * b.den() };
}

inline Rational operator-( const Rational &a, const Rational &b ) {
return Rational{ a.num() * b.den() - b.num() * a.den(), a.den() * b.den() };
}

inline Rational operator/( const Rational &a, const Rational &b ) {
return a * b.inverse();
}

class bad_rational : public std::domain_error {
public:
};

#endif // _6S096_RATIONAL_H
``````

#### Here is the source code file rational.cpp:

``````
#include "rational.h"
#include "gcd.h"

#include <stdexcept>
#include <ostream>
#include <iostream>
#include <cmath>

Rational Rational::inverse() const {
return Rational{ _den, _num };
}

Rational::sign_type Rational::sign()const {
return _num >= 0 ? POSITIVE : NEGATIVE;
}

std::ostream& operator<<( std::ostream& os, const Rational &ratio ) {
if( ratio == 0 ) {
os << "0";
} else {
if( ratio.sign() == Rational::NEGATIVE ) {
os << "-";
}
os << std::abs( ratio.num() ) << "/" << std::abs( ratio.den() );
}
return os;
}

void Rational::normalize() {
if( _den == 0 ) {
}

if( _num == 0 ) {
_den = 1; return;
}

auto g = gcd( std::abs( _num ), std::abs( _den ) );
_num /= g; _den /= g;

if( _den < 0 ) {
_num = -_num;
_den = -_den;
}
}

float Rational::to_float() const {
return static_cast<float>( _num ) / static_cast<float>( _den );
}

double Rational::to_double()const {
return static_cast<double>( _num ) / static_cast<double>( _den );
}
``````

#### Below is the output using the test data:

``````
rational:
1: OK [0.007 seconds] OK! add
2: OK [0.006 seconds] OK! mult
3: OK [0.009 seconds] OK! add1024
4: OK [0.014 seconds] OK! add1024
5: OK [0.158 seconds] OK! add32768
6: OK [0.007 seconds] OK! op<<
7: OK [0.289 seconds] OK! div65536 in 0.280000 s
8: OK [0.006 seconds] OK! phi, 0.000000e+00
9: OK [0.006 seconds] OK! (Bad rational: zero denominator)
10: OK [0.006 seconds] OK! xyz
11: OK [0.007 seconds] OK! pow2
12: OK [0.006 seconds] OK! x1z
``````

Look in `list.h` for a sense of the structure of the solution. The big idea to speed up the reduce/apply functions while also giving users a nice way to iterate over the items in the list is to create an “iterator” type within our class. Users will be able to write code similar to the STL:

``````
// Print out every item in the list
for( List::iterator it = list.begin(); it != list.end(); ++it ) {
std::cout < < *it << "\n";
}
``````

To speed up our “append” function, the List class will also store a pointer to the very last element in the current list.

Directory structure:

• include
• apply.h
• list.h
• list_node.h
• reduce.h
• Makefile
• src
• apply.cpp
• list.cpp
• list_iterator.cpp
• list_node.cpp
• reduce.cpp
• test.cpp

#### Here are the contents of apply.h:

``````
#ifndef _6S096_CPPLIST_APPLY_H
#define _6S096_CPPLIST_APPLY_H
#include "list.h"

class ApplyFunction {
protected:
virtual int function( int x ) const = 0;
public:
void apply( List &list ) const;
virtual ~ApplyFunction() {}
};

// An example ApplyFunction (see apply.cpp)
class SquareApply : public ApplyFunction {
int function( int x ) const;
};

#endif // _6S096_CPPLIST_APPLY_H
``````

#### Here are the contents of list.h:

``````
#ifndef _6S096_CPPLIST_H
#define _6S096_CPPLIST_H
#include <cstddef>
#include <stdexcept>

class ApplyFunction;
class ReduceFunction;
class ListNode;

class List {
size_t _length;
ListNode *_begin;
ListNode *_back;

public:
// Can use outside as List::iterator type
class iterator {
// Making List a friend class means we'll be able to access
// the private _node pointer data within the scope of List.
friend class List;
ListNode *_node;
public:
iterator( ListNode *theNode );
iterator& operator++();
int& operator*();
bool operator==( const iterator &rhs );
bool operator!=( const iterator &rhs );
};
// Can use outside as List::const_iterator type
class const_iterator {
// Again, this is basically the only situation you should
// be using the keyword 'friend'
friend class List;
ListNode *_node;
public:
const_iterator( ListNode *theNode );
const_iterator& operator++();
const int& operator*();
bool operator==( const const_iterator &rhs );
bool operator!=( const const_iterator &rhs );
};

List();
List( const List &list );
List& operator=( const List &list );
~List();
size_t length()const;
int& value( size_t pos );
int value( size_t pos ) const;
bool empty() const;

iterator begin();
const_iterator begin() const;
iterator back();
const_iterator back() const;
iterator end();
const_iterator end() const;

iterator find( iterator s, iterator t, int needle );
void append( int theValue );
void deleteAll( int theValue );
void insertBefore( int theValue, int before );
void insert( iterator pos, int theValue );

void apply( const ApplyFunction &interface );
int reduce( const ReduceFunction &interface ) const;
void print() const;
void clear();

private:
ListNode* node( iterator it ) { return it._node; }
ListNode* node( const_iterator it ) { return it._node; }
};

class ListOutOfBounds : public std::range_error {
public:
explicit ListOutOfBounds() : std::range_error( "List index out of bounds" ) {}
};

#endif // _6S096_CPPLIST_H
``````

#### Here are the contents of list_node.h:

``````
#ifndef _6S096_CPPLIST_NODE_H
#define _6S096_CPPLIST_NODE_H

class ListNode {
int _value;
ListNode *_next;
ListNode( const ListNode & ) = delete;
ListNode& operator=( const ListNode & ) = delete;
public:
ListNode();
ListNode( int theValue );
~ListNode();
int& value();
int value() const;
ListNode* next();
void insertAfter( ListNode *before );
void setNext( ListNode *nextNode );
static void deleteNext( ListNode *before );
static void deleteSection( ListNode *before, ListNode *after );

static ListNode* create( int theValue = 0 );
};

#endif // _6S096_CPPLIST_NODE_H
``````

#### Here are the contents of reduce.h:

``````
#ifndef _6S096_CPPLIST_REDUCE_H
#define _6S096_CPPLIST_REDUCE_H
#include "list.h"

class ReduceFunction {
protected:
virtual int function( int x, int y ) const = 0;
public:
int reduce( const List &list ) const;
virtual int identity() const = 0;
virtual ~ReduceFunction() {}
};

// An example ReduceFunction
class SumReduce : public ReduceFunction {
int function( int x, int y ) const;
public:
SumReduce() {}
~SumReduce() {}
int identity() const { return 0; }
};

// Another ReduceFunction
class ProductReduce : public ReduceFunction {
int function( int x, int y ) const;
public:
ProductReduce() {}
~ProductReduce() {}
int identity() const { return 1; }
};

#endif // _6S096_CPPLIST_REDUCE_H
``````

#### Here is the source code file apply.cpp:

``````
#include "list.h"
#include "apply.h"

void ApplyFunction::apply( List &list ) const {
for( auto it = list.begin(); it != list.end(); ++it ) {
*it = function( *it );
}
}

int SquareApply::function( int x ) const {
return x * x;
}
``````

#### Here is the source code file list.cpp:

``````
#include "list.h"
#include "list_node.h"
#include "apply.h"
#include "reduce.h"

#include <iostream>

List::List() : _length{0}, _begin{ nullptr }, _back{ nullptr } {}

List::List( const List &list ) : _length{0}, _begin{nullptr}, _back{nullptr} {
for( auto it = list.begin(); it != list.end(); ++it ) {
append( *it );
}
}

List& List::operator=( const List &list ) {
if( this != &list ) {
clear();
for( auto it = list.begin(); it != list.end(); ++it ) {
append( *it );
}
}
return *this;
}

List::~List() { clear(); }

size_t List::length() const { return _length; }

int& List::value( size_t pos ) {
auto it = begin();
for( size_t i = 0; i < pos && it != end(); ++it, ++i );
if( it == end() ) {
throw ListOutOfBounds();
}

return *it;
}

int List::value( size_t pos ) const {
auto it = begin();
for( size_t i = 0; i < pos && it != end(); ++it, ++i );
if( it == end() ) {
throw ListOutOfBounds();
}

return *it;
}

bool List::empty() const {
return _length == 0;
}

List::iterator List::begin() { return iterator{ _begin }; }
List::const_iterator List::begin() const { return const_iterator{ _begin }; }
List::iterator List::back() { return iterator{ _back }; }
List::const_iterator List::back() const { return const_iterator{ _back }; }
List::iterator List::end() { return iterator{ nullptr }; }
List::const_iterator List::end() const { return const_iterator{ nullptr }; }

void List::append( int theValue ) {
auto *newNode = ListNode::create( theValue );

if( empty() ) {
newNode->setNext( _back );
_begin = newNode;
} else {
newNode->insertAfter( _back );
}

_back = newNode;
++_length;
}

void List::deleteAll( int theValue ) {
if( !empty() ) {
// Delete from the front
while( _begin->value() == theValue && _begin != _back ) {
auto *newBegin = _begin->next();
delete _begin;
_begin = newBegin;
--_length;
}

auto *p = _begin;

if( _begin != _back ) {
// Normal deletion from interior of list
for( ; p->next() != _back; ) {
if( p->next()->value() == theValue ) {
ListNode::deleteNext( p );
--_length;
} else {
p = p->next();
}
}

// Deleting the last item
if( _back->value() == theValue ) {
ListNode::deleteNext( p );
_back = p;
--_length;
}
} else if( _begin->value() == theValue ) {
// Deal with the case where we deleted the whole list
_begin = _back = nullptr;
_length = 0;
}
}
}

List::iterator List::find( iterator s, iterator t, int needle ) {
for( auto it = s; it != t; ++it ) {
if( *it == needle ) {
return it;
}
}
return t;
}

void List::insert( iterator pos, int theValue ) {
auto *posPtr = node( pos );
auto *newNode = ListNode::create( theValue );
newNode->insertAfter( posPtr );
++_length;
}

void List::insertBefore( int theValue, int before ) {
if( !empty() ) {
if( _begin->value() == before ) {
auto *newNode = ListNode::create( theValue );
newNode->setNext( _begin );
_begin = newNode;
++_length;
} else {
auto *p = _begin;
for( ; p != _back && p->next()->value() != before; p = p->next() );
if( p != _back && p->next()->value() == before ) {
auto *newNode = ListNode::create( theValue );
newNode->insertAfter( p );
++_length;
}
}
}
}

void List::apply( const ApplyFunction &interface ) {
interface.apply( *this );
}

int List::reduce( const ReduceFunction &interface ) const {
return interface.reduce( *this );
}

void List::print() const {
std::cout << "{ ";
for( auto it = begin(); it != back(); ++it ) {
std::cout << *it << " -> ";
}
if( !empty() ) {
std::cout << *back() << " ";
}
std::cout << "}\n";
}

void List::clear() {
for( auto *p = _begin; p != nullptr; ) {
auto *p_next = p->next();
delete p;
p = p_next;
}
_length = 0;
_begin = nullptr;
_back = nullptr;
}
``````

#### Here is the source code file list_iterator.cpp:

``````
#include "list.h"
#include "list_node.h"

List::iterator::iterator( ListNode *theNode ) : _node{theNode} {}
List::iterator& List::iterator::operator++() {
_node = _node->next();
return *this;
}
int& List::iterator::operator*() { return _node->value(); }
bool List::iterator::operator==( const iterator &rhs ) { return _node == rhs._node; }
bool List::iterator::operator!=( const iterator &rhs ) { return _node != rhs._node; }

List::const_iterator::const_iterator( ListNode *theNode ) : _node{theNode} {}
List::const_iterator& List::const_iterator::operator++() {
_node = _node->next();
return *this;
}
const int& List::const_iterator::operator*() { return _node->value(); }
bool List::const_iterator::operator==( const const_iterator &rhs ) { return _node == rhs._node; }
bool List::const_iterator::operator!=( const const_iterator &rhs ) { return _node != rhs._node; }
``````

#### Here is the source code file list_node.cpp:

``````
#include "list_node.h"

ListNode::ListNode() : _value{0}, _next{nullptr} {}
ListNode::ListNode( int theValue ) : _value{theValue}, _next{nullptr} {}
ListNode::~ListNode() {}
int& ListNode::value() { return _value; }
int ListNode::value(){const { return _value; }
ListNode* ListNode::next() { return _next; }

void ListNode::insertAfter( ListNode *before ) {
_next = before->next();
before->_next = this;
}

void ListNode::setNext( ListNode *nextNode ) {
_next = nextNode;
}

void ListNode::deleteNext( ListNode *before ) {
auto *after = before->next()->next();
delete before->next();
before->_next = after;
}

void ListNode::deleteSection( ListNode *before, ListNode *after ) {
auto *deleteFront = before->next();
while( deleteFront != after ) {
auto *nextDelete = deleteFront->next();
delete deleteFront;
deleteFront = nextDelete;
}
}

ListNode* ListNode::create( int theValue ) {
return new ListNode{ theValue };
}
``````

#### Here is the source code file reduce.cpp:

``````
#include "list.h"
#include "reduce.h"

int ReduceFunction::reduce(const List &list ) const {
int result = identity();
for( auto it = list.begin(); it != list.end(); ++it ) {
result = function( result, *it );
}
return result;
}

int SumReduce::function( int x, int y ) const {
return x + y;
}

int ProductReduce::function(int x, int y ) const {
return x * y;
}
``````

#### Below is the output using the test data:

``````
cpplist:
1: OK [0.004 seconds] OK!
2: OK [0.005 seconds] OK!
3: OK [0.005 seconds] OK!
4: OK [0.009 seconds] OK!
5: OK [0.006 seconds] OK!
6: OK [0.308 seconds] OK!
7: OK [0.053 seconds] OK!
8: OK [0.007 seconds] OK!
9: OK [0.005 seconds] OK!
10: OK [0.742 seconds] OK!
``````

## Course Info

January IAP 2014
##### Learning Resource Types
Problem Sets with Solutions
Lecture Notes
Programming Assignments with Examples