EEN218 Autumn 2001 Test 23rd October Solutions

 

1. [The original question]

You are to write a program that processes data captured from automatic sensors. The sensors are buried in the surface of a road, and detect and measure passing vehicles. For each vehicle that passes, the sensors take note of the time of day, the vehicle's weight, and the vehicle's speed. These three pieces of information are stored in a file which your program will read.

 

The information for each vehicle is stored on a single line of the file, with the time first (consisting of 3 or 4 digits followed by "am" or "pm", examples: 900am 1131pm, 1200am), then the weight (measured in tons, as a floating point number with 2 digits before the point and three after, examples: 01.750, 00.331, 13.016), and finally the speed (measured in miles per hour as a three digit integer, examples: 030, 072, 005). Each of the three pieces of data are separated by spaces.

                Here is a sample from part of the data file:

 

945am 01.502 035

946am 01.945 029

946am 00.076 043

948am 07.732 028

 

Note that the file contains data collected over a period of many weeks, and may contain many millions of records, so do not attempt to keep all the data in memory.

 

Your program should read all the data from the file, then print a simple report summarising the important things exactly as described below.

 

Anything weighing less that 300 pounds should be completely ignored (it is probably a pedestrian or a pedal-cyclist) and not represented in the summary at all. (There are 2240 pounds in a ton).

 

The summary should report three pieces of information: The average speed of all heavy vehicles (heavy means 2 tons or more), the average speed of all light vehicles (light means less than 2 tons), and the average speed of all vehicles travelling at night (night is from 8:00pm to 5:59am). The output from your program should be printed on three lines exactly as illustrated below; the averages should be printed with one digit after the decimal point, and no unnecessary leading zeros. Make sure your program's output is lined up correctly like the sample.

                Sample output (not corresponding to sample of input)

 

Average speed of: light vehicles: 101.5 mph

                  heavy vehicles:  28.8 mph

                    night travel:  39.3 mph

 

The file containing the data records is called "traffic.dat"; If any information you consider important is not given in this explanation, make a reasonable assumption, state that assumption, and use it without worrying further.

[End of question]

 

We need a program that opens the file, and (in a loop) reads the records one-by-one until there are none left. As it reads the records, it should keep track of the total number of vehicles (excluding those that are supposed to be ignored) and the total of the weights in the three specified categories. As the averages are to be printed as floats, we can reasonably keep the totals as floats too; that way there is no likelihood of overflows.

For reading the data, both scanf and the C++ methods are perfectly adequate. Just don't over-specify. You are told that times have 3 or 4 digits, and weights have 2 digits before the point and 3 after, but just because you know something doesn't mean you have to use it. Scanf's %d format will happily read an integer whatever size it is; the %f format will correctly read a float no matter how many digits it has before or after the point.

Remember that scanf returns as its result the number of items successfully read (or –1 if it fails to read because of end of file). This provides a very simple way of telling when the input is over.

There are many correct ways to deal with the time. I think converting them all to 24-hour format is probably the easiest: 1200am to 1259am is converted to 0000 to 0059, 1200pm to 1259pm is left alone, but all other pm times have 1200 added (so 115 becomes 1315).

Be careful to avoid division by zero when calculating the averages at the end. When you finally print the averages, a little effort is required to get them lined up as required. Spaces in front of the words "heavy" and "light" does half the job, but as we don't know how many digits each of the averages will take, a special %f format is needed: %5.1f means a total of 5 characters (including everything, points and all), with 1 digit after the point.


#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{ FILE *f=fopen("traffic.dat", "r");

  if (f==NULL)

  { printf("Can't open input file\n");

    exit(1); }

  const float three_hundred_pounds = 300.0/2240.0;

  int num_light=0, num_heavy=0, num_night=0;

  float tot_light=0.0, tot_heavy=0.0, tot_night=0.0;

  while (1)

  { int time;

    char ampm1, ampm2;

    float weight, speed;

    int n=fscanf(f, "%d%c%c %f %f",

                 &time, &ampm1, &ampm2, &weight, &speed);

    if (n!=5) break;

    if (weight < three_hundred_pounds)

      continue;

    if (weight < 2.0)

    { num_light+=1;

      tot_light+=speed; }

    else

    { num_heavy+=1;

      tot_heavy+=speed; }

    if (ampm1=='a')

    { if (time >= 1200) time-=1200; }

    if (ampm1=='p')

    { if (time < 1200) time+=1200; }

    if (time>=2000 || time<=559)

    { num_night+=1;

      tot_night+=speed; } }

  fclose(f);

  float avg_light=0.0, avg_heavy=0.0, avg_night=0.0;

  if (num_light!=0)

    avg_light=tot_light/num_light;

  if (num_heavy!=0)

    avg_heavy=tot_heavy/num_heavy;

  if (num_night!=0)

    avg_night=tot_night/num_night;

  printf("Average speed of: light vehicles: %5.1f mph\n", avg_light);

  printf("                  heavy vehicles: %5.1f mph\n", avg_heavy);

  printf("                    night travel: %5.1f mph\n", avg_night); }


2. [The original question]

Define a struct (class or object type) with appropriate members, methods, and constructors, that can be used to represent a point in two-dimensional space and perform useful operations on such points, as described below. A point can be uniquely represented by two numbers, its "x" and "y" coordinates.

 

Your definition should include two constructors, one with the obvious parameters as illustrated below, and one default (no-parameter) constructor. Your definition should also include two methods, one which prints the point in the traditional manner, e.g. (1.0,2.0) for a point whose x is 1 and whose y is 2, and one which simply returns the distance between the point and some other point provided as a parameter.

Also include a method that rotates a point 90 degrees clockwise (around the origin of course). If you are not sure how the rotation is done, draw yourself a sketch showing the axes and the point (x=1, y=2), and notice how it gets rotated to become (x=2, y=-1).

Your definition should work if used with this piece of program:

 

{ point p(1.0, 2.0);

  point origin(0.0, 0.0);

  cout << "The point p is at ";

  p.print();

  cout << ", which is " << p.distance(origin)

       << " from the origin\n";

  p.rotate();

  cout << "after rotation the point p is at ";

  p.print();

  ...

 

Now define a function (not a method) that takes two points as parameters, and returns the distance between them.

 

A closed shape (such as a square or a hexagon) can be represented by an array of points specifying its corners in clockwise order, in which the last point is the same as the first one. For example, the snippet of code below creates the array T representing an isosceles triangle pointing downwards, and prints it:

 

{ point T[] = { point(1,3), point(3,3), point(2,0), point(1,3) };

  for (int i=0; i<4; i+=1)

    T[i].print();

  ...

 

Define a function that takes two parameters: an array of points (like T), and the number of points, and returns the perimeter of the shape described by the array.

[End of question]

 

There is very little to be said about anything in this question; it follows the pattern you've seen in class many times.

 

The question asked for a default constructor, but didn't tell you what it should do. There are two equally reasonable alternatives. Either do nothing, or think of a reasonable value that the components could be initialised to, and do it. Both versions are shown below.

 

If by some chance you have forgotten how to find the distance between two points, it is just a matter of using Pythagoras' theorem: the length of the hypotenuse is the square root of the sum of the squares of the lengths of the other two sides.

 

The rotation operation is also very simple. If you work out rotations properly, you get this formula: newx=xcosa+ysina; newy=ycosa-xsina; for rotating the point through angle "a" clockwise, and being engineering students, you really should be able to handle something like that. However, you don't need to. Rotation through a right angle is trivial. Just draw a couple of pictures (as the question suggested) and you soon see that the rule is newx=oldy; newy=-oldx.

 

The perimeter of a shape is found by adding together the distances between successive corners. Just be careful not to count the same side twice.

 


 

struct point

{ float x, y;

  point(void);

  point(float ix, float iy);

  void print(void);

  float distance(point p);

  void rotate(void); };

 

point::point(void)  // version one

{ }

 

point::point(void)  // version two

{ x=0.0; y=0.0; }

 

point::point(float ix, float iy)

{ x=ix; y=iy; }

 

void point::print(void)

{ printf("(%f,%f)", x, y); }

 

float point::distance(point p)

{ float distx = p.x – x;

  float disty = p.y – y;

  return sqrt(distx*distx + disty*disty); }

 

void point::rotate(void)

{ float oldx = x;

  x = y;

  y = -oldx; }

 

// note that the remaining definitions are plain functions, not methods.

// they are not defined inside the struct, and their names do not begin

// with "point::", but that doesn't stop them from using points.

 

float distance(point a, point b)  // version one

{ return a.distance(b); }

 

float distance(point a, point b)  // version two

{ float distx = a.x – b.x;

  float disty = a.y – b.y;

  return sqrt(distx*distx + disty*disty); }

 

float perimeter(point T[], int np)

{ float total = 0.0;

  for (int i=0; i<np-1; i+=1)

    total += distance(T[i], T[i+1]);

  return total; }