• Checkpoint: complete at least 5 puzzles by 11:00pm Tuesday, 15 September
  • Due: 11:00pm Thursday 17 September
  • Starter code: fork wellesleycs240 / cs240-bits (just once!) and add bpw as admin. (Need help?)
  • Submissions:
  • Relevant reading, as needed:
    • CSAPP 2.1.7 - 2.1.10, 2.2 - 2.3
    • K&R 2.7, 2.9
  • Collaboration: This assignment follows the standard collaboration policy. You may discuss problems and high-level strategies with other students, but your solutions must be your own work. You must cite anyone with whom you discussed the problems in a comment at the top of the bits.c file.

Use a CS 240-Approved Computing Environment!

Complete this assignment using one of the GNU/Linux environments supported for this course or meet certain doom. (More or less.) For example, we have seen solutions that fail with the Mac’s C compiler work with GCC on Linux. The reverse is likely possible too.

Before starting to write code for this assignment, get familiar with the CS 240 tools if you did not get a chance during lab. You will use these tools repeatedly during this assignment and the rest of the course.

Contents

Overview

The purpose of this assignment is to become more familiar with data representation at the bit level. You’ll do this by solving a series of programming “puzzles.” Many of these puzzles may seem artificial, but in fact bit manipulations are very useful in cryptography, data encoding, implementing file formats (e.g., MP3), etc. By working your way through these problems, you will get very familiar with bit representations. Hopefully you will have some fun along the way!

The short Integer Arithmancy exercises will help familiarize you with number representations for this assignment.

Instructions

Get the source files for this assignment from the wellesleycs240 / cs240-bits repository, following the same fork-share-clone process described here, as we did in the first lab meeting, but replacing the cs240-lab1 repository with cs240-bits.

Your working copy should contain a number of tools, described later, and a bits.c file containing skeletons for the programming puzzles, along with comment blocks that describe exactly what the functions must do and what restrictions there are on implementation. Your assignment is to complete each function skeleton using:

  • only straightline code (i.e., no loops or conditionals);
  • a limited number of C arithmetic and logical operators; and
  • no constants larger than 8 bits (i.e., 0 - 255 inclusive).

Carefully read the coding rules found in the bits.c file.

The intent of the restrictions is to require you to think about the data as bits – because of the restrictions, your solutions may not be the simplest or most efficient way to accomplish the function’s goal, but the process of working out the solution should make the notion of data as bits completely clear.

Please skim the entire document before starting to work.

Bit Puzzles

This section describes the puzzles that you will be solving in bits.c. More complete documentation is found in the bits.c file itself.

Bit Manipulations

The table below describes a set of functions that manipulate and test sets of bits. The Rating column gives the difficulty rating (the number of points) for each puzzle and the Description column states the desired output for each puzzle along with the constraints. See the comments in bits.c for more details on the desired behavior of the functions. You may also refer to the test functions in tests.c. These are used as reference functions to express the correct behavior of your functions, although they don’t satisfy the coding rules for your functions.

Rating Function Name Description
1 bitAnd x & y using only ~ and |
1 bitXor x ^ y using only ~ and &
1 thirdBits return word with every third bit (starting from the LSB) set to 1
2 getByte Extract byte n from word x
3 logicalShift shift x to the right by n, using a logical shift
4 bang Compute !x without using !
3 Optional:
conditional
x ? y : z

Two’s Complement Arithmetic

The following table describes a set of functions that make use of the two’s complement representation of integers. Again, refer to the comments in bits.c and the reference versions in tests.c for more information.

Rating Function Name Description
2 fitsBits returns 1 if x can be represented as an n-bit, two's complement integer
2 sign return 1 if positive, 0 if zero, and -1 if negative
3 addOK Determine if x+y can be computed without overflow
4 Optional:
isPower2
returns 1 if x is a power of 2, and 0 otherwise

Checking Your Work

We have included a few tools to help you check your work.

dlc: Compliance

dlc is a modified C compiler that you can use to check for compliance with the coding rules for each puzzle. The typical usage is:

$ ./dlc bits.c

The program runs silently unless it detects a problem, such as an illegal operator, too many operators, or non-straightline code in the integer puzzles. Running with the -e switch:

$ ./dlc -e bits.c

causes dlc to print counts of the number of operators used by each function. Type ./dlc -help for a list of command line options.

btest: Testing

btest is a program that checks the functional correctness of the code in bits.c. To build and use it, type the following two commands:

$ make
$ ./btest

Notice that you must rebuild btest each time you modify your bits.c file. (You rebuild it by typing make.) You’ll find it helpful to work through the functions one at a time, testing each one as you go. You can use the -f flag to instruct btest to test only a single function:

$ ./btest -f bitXor

You can feed it specific function arguments using the option flags -1, -2, and -3:

$ ./btest -f bitXor -1 7 -2 0xf

Check the README file for documentation on running the btest program.

We may test your solution on inputs that btest does not check by default and we will check to see if your solutions follow the coding rules.

driver.pl: Combined Testing and Conciseness

The driver.pl tool will both run btest and count the number of operators your solutions, displaying an automatically generated score for each. Here is (part of) the output on our sample solution:

$ ./driver.pl
[ ... status messages omitted here ... ]
Correctness Results     Perf Results
Points	Rating	Errors	Points	Ops	Puzzle
1       1       0       2       4   bitAnd
1       1       0       2       7   bitXor
1       1       0       2       4   thirdBits
2       2       0       2       7   fitsBits
2       2       0       2       4   sign
2       2       0       2       3   getByte
3       3       0       2       14  logicalShift
3       3       0       2       9   addOK
4       4       0       2       6   bang
3       3       0       2       7   conditional
4       4       0       2       11  isPower2

Score = 48/48 [26/26 Corr + 22/22 Perf] (76 total operators)

The “performance” score is based on the number of operators used. Fewer operators = higher score, in a range of 0-2 for each puzzle, but getting the absolute minimum number of operators is not necessary to get the full score. The combined score is not a direct indicator of your grade, but it is useful in estimating how close your solutions are to being correct and optimal. (Remember, it does not test all cases, so there may be others where your code is incorrect. It pays to think about edge cases and run extra tests manually!)

Grading

Your base grade will be derived as follows:

  • 70% correctness
  • 15% low operator count
  • 15% succint and clear comments describing why your implementations work

Comments should demonstrate clearly to the course staff that you understand the series of operations you apply and how and why they work to accomplish the goal. In some cases your comments may be shorter than your code. In some cases they may be longer.

Partial credit may be awarded for partially working solutions or comments describing an intended approach that you did not finish implementing.

Advice

Start early. Try some pencil-and-paper or whiteboard hacking to design your solution before you start typing C.

If you get stuck on one problem, move on. You may find you suddenly realize the solution the next day. Puzzle over the problems yourself, it is much more rewarding to find the solution yourself than stumble upon someone else’s solution. If you do not quite meet the operator limit don’t worry there will be partial credit, but often times working with a suboptimal solution will allow you to see how to improve it.

Commit often. Each time you get a new function working (or even just closer to working), commit your work. Also push your commits to your Bitbucket repository reasonably often. Then if things go wrong later you can always recover your work.

Number Conversion

The ishow tool lets you display integer representations and conversions. It takes hex or decimal input.

$ ./ishow 0x27
Hex = 0x00000027,	Signed = 39,	Unsigned = 39

Debugging

Do not include the stdio.h header file in your bits.c file, as it confuses dlc and results in some non-intuitive error messages. You will still be able to use printf in your bits.c file for debugging without including the stdio.h header, although gcc will print a warning that you can ignore.

Variable Declarations

The dlc program enforces a stricter form of C declarations than gcc enforces. In particular, in a block (what you enclose in curly braces) all your variable declarations must appear before any statement that is not a declaration. For example, dlc will complain about the following code:

int foo(int x) {
    int a = x;
    a *= 3;     /* Statement that is not a declaration */
    int b = a;  /* ERROR: Declaration not allowed here */
}

Instead, you must declare all your variables first, like this:

int foo(int x) {
    int a = x;
    int b;
    a *= 3;
    b = a;
}