Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Implementing the Student ADT (20 23 pts) Consider the design of the Student reco

ID: 3667503 • Letter: I

Question

Implementing the Student ADT (20 23 pts)

Consider the design of the Student record type from Assignment 4.

As a reminder, we’ll repeat the requirements from A4.

1. A Student record should include:

(a) a student’s name (first, and last),

(b) a student number,

(c) an NSID (aaannn format)

(d) an array of 10 assignment grades (for convenience, assume these are scores out of 100)

(e) a midterm exam grade and a final exam grade (for convenience, these are scores out of 100).

You may add any additional fields that you think might be necessary to make this record type complete or manage it more effectively.

2. The Student ADT should have the following operations:

• createStudentRecord() – creates a new blank student record on the heap and returns a reference to the record. It takes no arguments.

• destroyStudentRecord(s) – deallocates the student record s, and any other memory allocated for the record s.

• displayStudentRecord(s) – displays the student record s on the console in a friendly format.

• readStudentRecordFromConsole() – obtains data for a student record on the console, and returns a reference to a new student record populated with the data obtained from the console.

• changeAssignmentGradeForStudent(s,a,g) – adds a given number g to the grade of a given assignment number a for a given student record s.

• changeExamGradeForStudent(s,x,e) – adds a given number e to the x (’M’ = midterm, ’F’ = final) exam grade of a given student record s.

• NEW: 09/02/2016 calculateAverageGrade(s) - calculates average grade for student s and returns the answer.

Assume that the assignments count for 30%, the midterm counts for 25% and the final counts for 45%.

The parameters and details of these operations were not precisely stated, so you were allowed some flexibility on the design.

Note: You may use your own Student ADT design, or you may use the student ADT provided as a model solution.

What to hand in: • Hand in two C++ files:

– Student.cc containing the implementation of your ADT. It will have the implementation of every operation, but no main().

– Student.h containing the interface of your ADT. It will have the record type definition for Student records, and function prototypes for all your operations, and algorithm headers for every operation, but no function implementations (body), and no main(). 2

Grading • (18 21 marks)

Student.cc is a competent implementation of the ADT in C++, as follows: T

he file defines the 6 operations. Each one has comments (1 mark) describing the pre-, post- conditions, and return criteria. Each operation uses references correctly (1 mark1), and does something sensible with the data (1 mark). The following is a detailed description of the expectations for each operation: – createStudentRecord(). Since the main way to initialize a record is through read(), this could be fairly brief. Pointers should be set to NULL, but everything else is optional. – destroyStudentRecord(s) – deallocates the student record.

Care should be taken to deallocate everything that was allocated properly.

– displayStudentRecord(s) – displays the student record on the console. The format is irrelevant.

– readStudentRecordFromConsole() – obtains data for a student record on the console, and returns a new student record.

All memory for student records must be allocated on the heap. – changeAssignmentGradeForStudent(s,a,g) – adds a given number g to the grade of a given assignment number a for a given student record s.

Note: Some students may have limited the grade to 100. This is fine, but is completely optional. This course is not about the fine points of handling grades. – changeExamGradeForStudent(s,x,e) – adds a given number e to the indicated (x = ’M’ or ’F’) exam grade of a given student record s.

Note: Some students may have limited the grade to 100. This is fine, but is completely optional. This course is not about the fine points of handling grades. – NEW calculateAverageGrade(s) - calculates average grade for student s and returns the answer. Assume that the assignments count for 30%, the midterm counts for 25% and the final counts for 45%. Note: Some students may have limited the grade to 100. This is fine, but is completely optional.

This course is not about the fine points of handling grades. –

Generally, for full marks:

Your Student.cc does not have a main() function.

Your program file #includes Student.h Your program file does not define the record type directly (but does indirectly by #including Student.h)

• (2 marks) Student.h contains the interface for the operations: – (1 mark) the definition of the Student record type, – (1 mark) function prototypes of the 6 operations, – and no function definitions (no function body). Implementation guideline: To help you, we’ve provided a guide for the conversion from pseudo-code to C++.

You may use this as a guide, and not a set of strict instructions. There are too many variations that we cannot account for. 1. Copy a student design document (your own, or our solution from A4) into a C++ file called Student.cc. Don’t change the original! Delete the main algorithm you wrote for A4E5 (we’ll use it in the next exercise, but not this one). Comment out every remaining line in the C++ file. 2. Put your name and student information in this file! 3. Convert your pseudo-code to C++; we have a guideline to follow if you wish. (a) Start with the definition of the record type, and then createStudentRecord().

Keep your “algorithm header” (i.e., pre-, post-conditions and return criteria) in your file as comments. 3 (b) Your final submission for Student.cc will not have a main function, but it is handy to have one temporarily as you are working. Create a main() function and test your createStudentRecord() function in it. The testing for this operation can be simple, since a blank record is returned. You basically just want to be sure that a record is created. (c) One function at a time, convert every operation in Student.cc to C++. Keep your “algorithm header” (i.e., pre-, post-conditions and return criteria) in your file as comments. Before you go forward with the next operation, try to test your current operations as thoroughly as you can. Work one function at a time, including testing each function, and don’t rush. This should take a few hours. Don’t forget to add #include if you use cin or cout, and "using namespace std;" Include any other libraries (cmath, cstring, cstdlib) you need. Don’t forget that you can use file redirection to assist with your testing! If you are testing or using readStudentRecordFromConsole(), you can have a file with data in it, so you can avoid typing this information every time. 4. When you have implemented and tested every operation Student.cc, it’s time to prepare the interface. (a) Make a copy of your main() program, and put it in a file called testStudent.cc or something like that. We’ll come back to it, below.

Then delete main() from Student.cc. Leave everything else in Student.cc. (b) Once you’ve moved main() out of Student.cc, compile Student.cc as follows: To compile a single file that has no main(), use the following on the command line: g++ -pedantic -Wall -c The option -c tells the compiler to compile without creating an application; the compiler will issue warnings and errors as before, but it will not produce an a.out file. That’s okay! (c) Assuming Student.cc compiles correctly as above, make a copy of Student.cc called Student.h. i. Edit the Student.h file, turning every function into a function prototype. (Remember Lab 4).

A function prototype has the function header, followed by a semi-colon, and no function body. Leave all algorithm header documentation (as comments)! ii. Make sure that your definition of the Student record type (the “struct”) is defined in Student.h only.

Delete the definition of the record type from Student.cc. iii. Add #include "Student.h" somewhere near the top of Student.cc near the other includes. Be sure to use double quotes, not the <> symbols. iv. Check your files by compiling Student.cc again, as a single file.

Note that you don’t have to put Student.h on the command-line at all! It should be #included in your Student.cc file. v. Check your files by compiling Student.cc again, as above. (d) Open up your testStudent.cc file (or whatever you called it). If you followed the above steps, it will contain the main() function that has all the testing code that you wrote while testing your operations. (e) Add your own student information at the top, and all the includes that main() needs. (f) Add #include "Student.h" just after all the other includes. (g) Compile the test application using the following command line: g++ -pedantic -Wall -o testSR Student.cc testStudent.cc (your file names might be different). Now when you run the testSR application, the testing should run exactly as it did before. (Using ADTs and multifile compilation, you can now put all kinds of tests into testing applications that are run separate from the any project application you may be building)

STUDENT ADT FROM ASSIGNMENT 4.

ssignment 4 Exercise 5
************************************************************************

Student
   refToChar firstName
   refToChar lastName
   Integer studentNumber
   refToChar NSID
   refToFloat assignments
   Float midterm
   Float final
end Student


Algorithm createStudentRecord()
Pre: none
Post: student record created
Return: reference to newly created student record

   refToStudent s <- allocate new Student
   return s


---
Algorithm destroyStudentRecord(s)
Pre:   s :: refToStudent
Post:   Student record s and fields deallocated
Return:   nothing

   if s != null
   then
       deallocate s->firstName
       deallocate s-> lastName
       deallocate s-> NSID
       deallocate s-> assignments
       deallocate s
   end


---
Algorithm displayStudentRecord(s)
Pre:   s :: refToStudent
Post:   fields in s displayed to console
Return: nothing

   if s != NULL
   then
       print "Name: " s->firstName s->lastName
       print "Student Number: " s->studentNumber
       print "NSID: " s->NSID
       print "Assignments: "
  
       for i from 0 to 10 by 1 do
           print i s->assignments[i]
       end
  
       print "Midterm: " s->midterm
       print "Final: " s->final
   end


---
Algorithm copyString(s)
This is a helper function, to ensure that we never make a
mistake copying a C-string. It's not part of the Student ADT Interface
Pre:   s is a reference to a CString
Post: allocates new memory for a copy of s
Return: reference to the new copy

refToChar copy <- allocate new Char[strlen(s) + 1]
strcpy(copy, s)
return copy


---
Algorithm readStudentRecordFromConsole()
Pre:   nothing
Post:   creates new student record and populates fields from console
Return: refToStudent -- newly created student record

   Char firstName[100]
   Char lastName[100]
   Char NSID[100]
   Float midterm
   Float final

   refToStudent newStudent <- createStudentRecord() // 2016/02/09 error correction

   if newStudent != NULL
   then
       read firstName
       newStudent->firstName <- copyString(firstName)

       read lastName
       newStudent->lastName <- copyString(lastName)

       read newStudent->studentNumber

       read NSID  
       newStudent->NSID <- copyString(NSID)

       newStudent->assignments <- allocate new Integer[10]
       for i from 0 to 10 by 1 do
           read newStudent->assignments[i]
       end
  
       read newStudent->midterm
       read newStudent->final
   done

   return newStudent

---
Algorithm ChangeAssignmentGradeForStudent(s, a, g)
Pre:   s :: refToStudent
       a :: Integer -- assignment number
       g :: Integer -- amount to add to grade
Post:   adds g marks to assignment number a for student s
Return: nothing

   if s != NULL
   then
       s->assignments[a] <- s->assignments[a] + g
   done


---
Algorithm ChangeExamGradeForStudent(s, x, e)
Pre:   s :: refToStudent
       x :: Char -- 'M' if midterm, 'F' if final
       e :: Integer -- amount to add to exam grade
Post:   adds e marks to exam (Midterm or Final) for student s
Return: nothing

   if s != NULL
   then
       if x == 'M'
       then
           s->midterm <- s->midterm + e
       else if x == 'F'
       then
           s->final <- s->final + e
       else
  
       done
   done


---
Algorithm calculateAverageGrade(s)
Pre:   s :: refToStudent
Post:   calculates student average final grade
Return:   Float -- final average grade for student

   if s != NULL
   then
       Float midterm <- s->midterm * 0.25
       Float final <- s->final * 0.45
       Float assignmentTotal <- 0
  
       for i from 0 to 10 by 1 do
           assignmentTotal <- assignmentTotal + s->assignments[i]
       end  
  
       assignmentTotal <- assignmentTotal / 10
       assignmentTotal <- assignmentTotal * 0.30
  
       return midterm + final + assignmentTotal
   done

---
Algorithm main()
Pre:   nothing
Post:   create 10 students, get info from console, print students, calculate class average
Return:   nothing


   refToRefToStudent students <- allocate new refToStudent[10]
   Float classTotal <- 0
   Float classAverage <- 0

   for i from 0 to 10 by 1 do
       students[i] <- readStudentRecordFromConsole()
   done

   for i from 0 to 10 by 1 do
       displayStudentRecord(students[i])
   done

   for i from 0 to 10 by 1 do
       classTotal <- calculateAverageGrade(students[i])
   done

   classAverage <- classTotal / 10

   print "The class average is " classAverage

************************************************************************
Assignment 4 Exercise 6
************************************************************************

TrainCar
   refToChar type
   Float capacity
   Integer serial
   refToTrainCar next
end TrainCar


---
Algorithm createTrainCar(kind, cargo, serial)
Pre:   kind :: refToChar -- cstring describing type of train
       cargo :: Integer -- weight capacity of train, in Kg
       serial :: Integer -- unique serial number to identify TrainCar
Post:   new train allocated with provided fields
Return:   refToTrainCar -- newly created train

   refToTrainCar newTrain <- allocate new TrainCar
   newTrain->type <- allocate new Char[strlen(kind) + 1]
   strcpy(newTrain->type, kind)
   newTrain->cargo <- cargo
   newTrain->serial <- serial

   return newTrain

---
Algorithm connectTrains(front, back)
Pre:   front :: refToTrainCar -- pointer to first TrainCar in first train
       back :: refToTrainCar -- pointer to first TrainCar in second train
Post:   last car in front now points to first car in back
Return:   nothing

   refToTrainCar trainWalker
   refToTrainCar tmpCar
   trainWalker <- front

   // if the first train is not a train at all!
   if (front == NULL) then
       return
   endif
  
   // walk along until you reach the end of the front train
   while trainWalker != NULL do
       tmpCar <- trainWalker
       trainWalker <- trainWalker->next
   done

   // now attach the back train on to the end
   tmpCar->next <- back


---
Algorithm disconnectCars(train, serial)
Pre:   train :: refToTrainCar -- pointer to first TrainCar in train to be disconnected
       serial :: Integer -- serial number of car to be disconnected after
Post:   train is disconnected after TrainCar with serial number
Return:   refToTrainCar -- second newly created train from split

   refToTrainCar trainWalker, tmpCar
   trainWalker <- train

   // walk along until the end, or unti you find the car
   while (trainWalker != NULL) and (trainWalker->serial != serial) do
       trainWalker <- trainWalker->next
   done

   // if you didn't find it, return NULL
   if trainWalker == NULL do
       return NULL
   done

   // remember the car that's disconnected
   tmpCar <- trainWalker->next
   trainWalker->next <- NULL

   return tmpCar


---
Algorithm printTrain(train)
Pre:   train :: refToTrainCar -- train to be printed
Post:   all TrainCars in train are printed
Return: nothing

   refToTrainCar luke <- train

   while luke != NULL do
       print luke->type
       print luke->capacity
       print luke->serial
       print newline
       luke <- luke->next
   done

Explanation / Answer

#include #include #include /* * Idea of this data structure is that we have a sorted array * of pixels, where each pixel specifies a row, column, and character * to put in that position. The sort order is row then column. * * This is organized as a queue in the sense that we can push * new pixels on to the end of it, although as it happens we * never actually dequeue anything. */ struct pixel { int row; int col; char value; }; struct queue { size_t top; /* number of elements */ size_t size; /* number of allocated slots */ struct pixel *pixels; /* pixel values, sorted by row then column */ }; #define QUEUE_INITIAL_SIZE (16) /* create new empty queue */ struct queue * queueCreate(void) { struct queue *q; q = malloc(sizeof(struct queue)); assert(q); q->top = 0; q->size = QUEUE_INITIAL_SIZE; q->pixels = malloc(sizeof(struct pixel) * q->size); assert(q->pixels); return q; } /* clean up queue */ void queueDestroy(struct queue *q) { free(q->pixels); free(q); } /* add a new pixel to queue */ void queuePush(struct queue *q, struct pixel p) { while(q->top >= q->size) { q->size *= 2; q->pixels = realloc(q->pixels, sizeof(struct pixel) * q->size); assert(q->pixels); } q->pixels[q->top++] = p; } /* returns malloc'd data, free with queueDestroy */ struct queue * queueRead(const char *filename) { FILE *f; struct queue *q; struct pixel p; int c; q = queueCreate(); f = fopen(filename, "r"); if(f == 0) { perror(filename); exit(1); } p.row = p.col = 0; while((c = getc(f)) != EOF) { switch(c) { case ' ': p.row++; p.col = 0; break; case ' ': p.col++; break; default: p.value = c; queuePush(q, p); p.col++; break; } } fclose(f); return q; } /* write pixels in queue to stdout */ void queueWrite(const struct queue *q) { int outputRow = 0; int outputCol = 0; int i; for(i = 0; i top; i++) { while(outputRow pixels[i].row) { putchar(' '); outputRow++; outputCol = 0; } while(outputCol pixels[i].col) { putchar(' '); outputCol++; } putchar(q->pixels[i].value); outputCol++; } /* end last row */ putchar(' '); } /* * Merge two queues, creating a new, freshly-allocated queue. * New queue is sorted. If there are pixels in both left * and right with the same row and column, the one from right * overwrites the one from left. */ struct queue * queueMerge(const struct queue *left, const struct queue *right) { int l = 0; int r = 0; struct queue *q; q = queueCreate(); while(l top && r top) { if(left->pixels[l].row pixels[r].row) { queuePush(q, left->pixels[l++]); } else if(left->pixels[l].row == right->pixels[r].row) { if(left->pixels[l].col pixels[r].col) { queuePush(q, left->pixels[l++]); } else if(left->pixels[l].col == right->pixels[r].col) { /* right wins but both increment */ queuePush(q, right->pixels[r++]); l++; } else { /* right is earlier */ queuePush(q, right->pixels[r++]); } } else { /* right is earlier */ queuePush(q, right->pixels[r++]); } } /* clean out whichever tail is still nonempty */ while(l top) { queuePush(q, left->pixels[l++]); } while(r top) { queuePush(q, right->pixels[r++]); } return q; } /* in-place offset by r rows and c columns */ void queueOffset(struct queue *q, int r, int c) { int i; for(i = 0; i top; i++) { q->pixels[i].row += r; q->pixels[i].col += c; } } /* max filename size as promised in assignment text */ #define BUFFER_SIZE (2048) int main(int argc, char **argv) { struct queue *merged; /* holding place for result of merge */ struct queue *left; /* accumulated picture */ struct queue *right; /* new picture */ int row; /* row offset for new picture */ int col; /* column offset for new picture */ char filename[BUFFER_SIZE]; /* filename for new picture */ if(argc != 1) { fprintf(stderr, "Usage: %s ", argv[0]); return 1; } for(left = queueCreate(); scanf("%d %d %s", &row, &col, filename) == 3; left = merged) { right = queueRead(filename); queueOffset(right, row, col); merged = queueMerge(left, right); queueDestroy(left); queueDestroy(right); } queueWrite(left); queueDestroy(left); return 0; }