Submission

Assignments are to be submitted to Canvas. Programming assignments are usually split out into a separate submissions, so that we can easily provide you feedback on your code and in some cases execute unit tests on your code. Written work should be typed when possible, but drawings can be scanned and inserted into your documents. Hand-written work is acceptable if converted to a pdf. Affidavits are required, see below.

Assignments must be submitted by 10:30 PM on the day they are due and have a 30 min grace period. Afterwards, assignments are considered late. Unless otherwise specified, late assignments are accepted up to 10:30 PM with a 30 min grace period the next time that the class meets. e.g. If the assignment was due Thu, late assignments are accepted up to Tue at 10:30 PM. Upon occasion, the late due date may be moved up. This will always be announced, and is usually to allow a solution key to be released before an exam or due to an assignment extension.

Submission of programs

When submitting programs, you can either upload your files individually or as a zip archive file. If you use a zip file, make sure that files are at the top-level of the archive. Do not place them a directory within the zip archive. Subdirectories are fine if the program is supposed to have structure (e.g. Python packages).

Affidavits

You are expected to do your own work. While you may consult with peers for general strategies, you may not share code. You may not use material gathered from the Internet, books, etc., without permission. All work must be in your words. When submitting work, you must include an affidavit that the work you submitted is your own. In courses where pair programming has been allowed, use the multiple programmer affidavit if you are submitting a pair programming assignment.

For each assignment, you must submit an affidavit. Failure to do so will result in lost points. When assignments are submitted in two separate submissions (written work and program), place your affidavit in the program. It should be in the comments at the top of the file that contains the program’s entry point. If you are only submitting written work, place it at the top of your document.

Single programmer:

I promise that the attached assignment is my own work. I recognize that should this not be the case, I will be subject to penalties as outlined in the course syllabus. [Your Name]

Multiple programmer:

We the undersigned promise that we have in good faith attempted to follow the principles of pair programming. Although we were free to discuss ideas with others, the implementation is our own. We have shared a common workspace (possibly virtually) and taken turns at the keyboard for the majority of the work that we are submitting. Furthermore, any non programming portions of the assignment were done independently. We recognize that should this not be the case, we will be subject to penalties as outlined in the course syllabus. [Your Names]

Solution keys

Solutions to selected problems are available on Canvas. Solutions to programming assignments are not posted, but they are discussed in class.

Notes on style and commenting

Your programs will be graded not only on functionality, but also on good programming practices. There is no specific style guide that you are required to follow, but you are expected to use good practices such as commenting, descriptive variable names, and appropriate levels of abstraction.  Avoid using magic numbers, values whose meaning is hard to interpret.  For example, if you needed to pass the number of seconds in a day, declaring a constant or preprocessor directive that associated the value s_per_day with the value 86400 (24 * 60 * 60), using the name s_per_day in a function call would be clearer to most readers than 86400.

The following shows the constructor declaration in a C++ header that declares a class called BlockDevice that is designed to simulate a data device that operates on blocks of fixed-size data (e.g. a solid-state disk):

#ifndef BLOCKDEVICE_H
#define BLOCKDEVICE_H

/* BlockDevice
 * Simulates a block device (e.g. solid-state/magnetic drive)
 * on a file.  The file will not necessarily be the same
 * size as the device as we only allocate physical space up to 
 * and including the last used block.
 */
class BlockDevice {

 public:
   /*
   * Success and error codes.
   * If these are changed, be sure to change human/readable
   * message string array in class definition.
   */
  typedef enum {
    success,      // operation completed successfully
    nosuchblock,  // bad block index
    badblock,     // cannot access block
    badreadwrite, // unable to complete read or write
    nosynch,      // unable to synchronize buffers with blocks
  } result;

  // messages corresponding to result enumerations
  // values declared in class definition, e.g. messages[nosuchblock]
  // contains a human readable explanation that the requested block
  // does not exist.
  static const char *messages[];  

 /*
   * BlockDevice(const char *filename, uint32_t blocks, uint32_t
   * block_size)
   * Create or open a simulated block I/O device backed by the
   * specified filename.  If blocks > 0, the device is created,
   * otherwise we try to open the device.  (We don't use two different
   * constructors to ensure C compatibility.)
   */
  BlockDevice(const char *filename, uint32_t blocks = 0,
       uint32_t block_size = 4096);

  ...
In the C++ code, the constructor might look like this:
#include <sys/stat.h>
#include 
#include 

#include "BlockDevice.h"

using namespace std;

/* BlockDevice
 * Simulates a block device (e.g. solid-state/magnetic drive)
 * on a file.  The file will not necessarily be the same
 * size as the device as we only allocate physical space up to 
 * and including the last used block.
 *
 * Arguments:
 * filename - File that will be used to simulate the device.
 * blocks - Maximum capacity of device in data blocks
 * block_size - Number of bytes in each block
 * If blocks is omitted or 0, the constructor opens an existing device file.
 */

BlockDevice::BlockDevice(const char *filename, uint32_t blocks,
 uint32_t block_size)
{

  // If block_size is specified, ensure that it is divisible by 32 bits
  // Real devices are usually a power of 2
  if (block_size && (block_size % sizeof(int32_t) != 0))
    throw runtime_error("Device block size must be a multiple of 4 bytes");
  
  size_t count = 0;  // header fields read/written

  // We really only need a a few bytes (sizeof(device_meta)) for the device metadata
  // However, it will make our life much simpler if we extend that to the standard 
  // block size as everything will be aligned.
  // Find the number of blocks we need to hold the device meta information
  uint32_t complete_blocks = sizeof(device_meta) / block_size;
  uint32_t partial_blocks = sizeof(device_meta) % block_size > 0 ? 1 : 0;
  // Record offset into file where user data begins.
  this->offset = (complete_blocks + partial_blocks) * block_size;
  
  size_t expected_hdr_count = 2;  // # fields in hdr

  if (blocks == 0) {
    // User did not specify device characteristics, assume existing
    // block device and check if simulation file exists.
    struct stat status;
    bool exists = stat(filename, &status) != -1;
    if (! exists)
      throw runtime_error("Device file does not exist");

    // open and read metadata
    this->file_h = fopen(filename, "r+");
    if (this->file_h == NULL)
      throw runtime_error("Unable to open file");

    // Read in metadata and verify that we read the expected amount.
    count += fread(&this->block_size, sizeof(uint32_t), 1, this->file_h);
    count += fread(&this->blocks, sizeof(uint32_t), 1, this->file_h);
    if (count != expected_hdr_count)
      throw runtime_error("Unable to read device metadata");

  } else {
    // New device
    this->file_h = fopen(filename, "w+b");
    if (this->file_h == NULL)
      throw runtime_error("Unable to create new device");

    this->blocks = blocks;
    this->block_size = block_size;
    // Write header
    count += fwrite(&this->block_size, sizeof(uint32_t), 1, this->file_h);
    count += fwrite(&this->blocks, sizeof(uint32_t), 1, this->file_h);
    if (count != expected_hdr_count)
      throw runtime_error("Unable to initialize device metadata");
  }
}

Note that this sample code has code that checks for errors, and comments that indicate what major sections of the code do. We do not comment every line. When constants are needed such as checking to make sure that we wrote two words when creating a new disk file, we assign the constant value to a variable (#define or const would be fine) so that the reader can understand what the number means.

I highly reccomend that you comment your code as you write. I find that it reduces code faults as it makes you thin about what you want to do. It also makes it much easier for people to follow your code. In this class, commenting is mandatory. If you come for help, I will expect to see commented code.