UNIX> cp -r /home/jplank/cs202/Labs/Lab5/src . UNIX> cp -r /home/jplank/cs202/Labs/Lab5/include . UNIX> cp -r /home/jplank/cs202/Labs/Lab5/files . UNIX> cp /home/jplank/cs202/Labs/Lab5/makefile . UNIX> mkdir obj UNIX> mkdir bin
You are not allowed to modify include/bitmatrix.hpp or src/bitmatrix_editor.cpp. The TA's will use my versions of these files when they grade.
Let's take some simple examples. Here are four bit-matrices:
files/C003.txt |
files/C244.txt |
files/RV.txt |
files/CV.txt |
When you add bit-matrices, you simply add elements in corresponding rows and columns, modulo two. So, for example:
+ | = |
and
+ | = |
Bit-matrix multiplication is just like standard matrix multiplication, except that addition and multiplication of the individual elements is done modulo two. For example, multiplying files/RV.txt and files/CV.txt will yield a 1 X 1 matrix whose value is:
This is 1+0+1+0+0+0+0+1 which equals 3%2 which equals 1. Thus:
* | = |
Similarly:
* | = |
* | = |
and
* | = |
That last example is interesting -- when the product of two square matrices is equal to the identity matrix, then these two matrices are inverses of each other. Some matrices, like the one below, have no inverses:
#pragma once #include <string> #include <vector> class Bitmatrix { public: /* Bitmatrix creation constructors / methods. */ Bitmatrix(int rows, int cols); /* Create an empty bitmatrix with the given size. Throw the string "Bad rows" if (rows <= 0). Throw the string "Bad cols" if (cols <= 0). */ Bitmatrix(const std::string &fn); /* Read the bitmatrix from a file. Throw "Can't open file" if you can't open the file. Throw "Bad file format" if you can't read the file. */ Bitmatrix *Copy() const; /* Create a new bitmatrix using new, which is a copy of the caller's bitmatrix, and return a pointer to it. */ /* Bitmatrix storage methods. */ bool Write(const std::string &fn) const; /* Write to a file. You will print one line per row of the bitmatrix, and each line is only composed of 0's and 1's. Return true if successful and false if not. */ void Print(size_t w) const; /* Print on standard output. You will print one line per row of the bitmatrix. Each line is composed of 0's and 1's, and there will be a space after every w characters (don't put a space at the end if the number of columns is a multiple of w). Also put a blank line after every w rows (except the last, if the number of rows is a multiple of w). */ bool PGM(const std::string &fn, /* Create a PGM file. Each entry is a p by p square, */ int p, /* which is white for zero and 100 for gray. If border is */ int border) const; /* greater than zero, then there should be a black border of that many pixels separating each square and around the whole matrix. Return true if successful and false if not. */ /* Bitmatrix access methods. */ int Rows() const; /* Return the number of rows */ int Cols() const; /* Return the number of columns */ char Val(int row, int col) const; /* Return the specified element ('0' or '1'). Return 'x' if row or col is bad. */ /* Bitmatrix modification methods. */ bool Set(int row, int col, char val); /* Set the specified element to val. Val must be 0, 1, '0' or '1'. If val is 0 or 1, store '0'/'1' in the matrix. Return true if successful and false if not. */ bool Swap_Rows(int r1, int r2); /* Swap these rows. Return true if successful and false if not. */ bool R1_Plus_Equals_R2(int r1, int r2); /* Set the row r1 to the sum of row r1 and r2. Return true if successful and false if not. */ protected: std::vector <std::string> M; /* The matrix. Elements are '0' or '1'. */ }; /* These four procedures will allocate and create a new bitmatrix from other bitmatrices. They must be written using the methods of the Bitmatrix class above. They should return NULL if they are unsuccessful. */ Bitmatrix *Sum(const Bitmatrix *a1, const Bitmatrix *a2); Bitmatrix *Product(const Bitmatrix *a1, const Bitmatrix *a2); Bitmatrix *Sub_Matrix(const Bitmatrix *a1, const std::vector <int> &rows); Bitmatrix *Inverse(const Bitmatrix *m); /* We are also going to support storage and retrieval of bitmatrices through a hash table. */ class HTE { /* This is a "hash table entry". Our hash table stores */ public: /* vectors of pointers to these, using separate chaining. */ std::string key; Bitmatrix *bm; }; class BM_Hash { /* This is our bitmatrix hash table. */ public: BM_Hash(int size); /* You specify the table size in the constructor. Throw the string "Bad size" if (size <= 0). */ bool Store(const std::string &key, Bitmatrix *bm); /* Store a bitmatrix with the given key. Return true if successful and false if not. Return false if the key is already there. */ Bitmatrix *Recall(const std::string &key) const; /* Retrieve a bitmatrix with the given key. Return NULL if unsuccessful. */ std::vector <HTE> All() const; /* Return all of the hash table entries. */ protected: std::vector < std::vector <HTE> > Table; /* This is the hash table. */ }; |
Here is some additional detail:
The only piece of data in a bitmatrix is a vector of strings named M. If M is storing a r by c matrix, then it will contain r strings, each of which has c characters. The characters are either '0' for zero or '1' for one.
So, you have to implement 12 methods, and only one of them (PGM()) is difficult. Were I you, I'd wait until the end to do PGM().
You have to implement four procedures which operate on pointers. Since these are not part of the data structure, you have to use Rows() Cols(), Set(), and Val() to implement them. You should not modify the input matrices in any way (the "const" keywords won't let you):
Finally, you are also going to implement a hash table to store bit-matrices with keys that are strings. You should use the djb_hash() function from class as the hash function and you should use separate chaining as the collision resolution mechanism. Your hash table is a vector of vectors of HTE's (hash table entries). Each hash table entry stores a key and a pointer to a bit-matrix.
The BM_Hash class has four methods, which are described in the header file. A little more detail on All(): Return a vector of all hash table entries in the table. The vector should be ordered just like the hash table. In other words, suppose "A" hashes to 5, "B" hashes to 1 and "C" hashes to 1. And suppose that "B" was added to the table before "C". Then All() should return the HTE's in the order "B", "C", "A". You should not call new or delete on bit-matrices when you implement any of the hash table methods.
Bitmatrix_editor reads lines of text from standard input. Blank lines and lines that begin with '#' are ignored. Otherwise, the first word on a line is a command and the remaining words are arguments.
At all times, there is one "current matrix." You may also store and recall matrices with single-word keys. Three simple commands are:
UNIX> bin/bitmatrix_editor "BM-Editor>" BM-Editor> READ files/C003.txt BM-Editor> PRINT 10000001 11000000 01100001 00110001 00011001 00001100 00000110 00000011 BM-Editor> PRINT 4 1000 0001 1100 0000 0110 0001 0011 0001 0001 1001 0000 1100 0000 0110 0000 0011 BM-Editor> READ files/RV.txt BM-Editor> PRINT 10100001 BM-Editor> PRINT 4 1010 0001 BM-Editor> QUIT UNIX>bitmatrix_editor also implements the following commands, which of course will only work when you implement the proper methods and procedures:
Some examples with the above matrices:
UNIX> bin/bitmatrix_editor 'BM-Editor>' BM-Editor> READ files/C003.txt BM-Editor> STORE C003 BM-Editor> READ files/C244.txt BM-Editor> STORE C244 BM-Editor> READ files/CV.txt BM-Editor> STORE CV BM-Editor> READ files/RV.txt BM-Editor> STORE RV BM-Editor> READ files/t3.txt BM-Editor> STORE t3 BM-Editor> READ files/t4.txt BM-Editor> STORE t4 BM-Editor> RECALL RV BM-Editor> PRINT 10100001 BM-Editor> ALL CV 8 X 1 RV 1 X 8 t3 16 X 8 t4 8 X 16 C003 8 X 8 C244 8 X 8 BM-Editor> SUM C003 C003 BM-Editor> PRINT 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 BM-Editor> SUM C003 C244 BM-Editor> PRINT 11111110 11111111 10000001 00111110 11100001 11110000 11111000 11111100 BM-Editor> PRODUCT RV CV BM-Editor> PRINT 1 BM-Editor> PRODUCT C003 C244 BM-Editor> PRINT 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 BM-Editor> PRODUCT C003 CV BM-Editor> PRINT 0 1 0 1 1 1 0 1 BM-Editor> RECALL t3 BM-Editor> PRINT 8 10000001 11000000 01100001 00110001 00011001 00001100 00000110 00000011 01111111 00111111 11100000 00001111 11111000 11111100 11111110 11111111 BM-Editor> RECALL t4 BM-Editor> PRINT 8 10000001 01111111 11000000 00111111 01100001 11100000 00110001 00001111 00011001 11111000 00001100 11111100 00000110 11111110 00000011 11111111 BM-Editor> PRODUCT t3 t4 BM-Editor> PRINT 8 10000010 10000000 01000001 01000000 10100010 00100000 01010011 00010000 00101011 00001000 00010101 00000100 00001010 00000010 00000101 00000001 10000000 11010101 01000000 11101010 00100000 10100000 00010000 00000101 00001000 01010111 00000100 10101011 00000010 01010101 00000001 10101010 BM-Editor> PRODUCT t4 t3 BM-Editor> PRINT 8 01010111 10101011 00000010 01010110 01111100 10111110 01011111 10101111 BM-Editor> RECALL C003 BM-Editor> INVERT BM-Editor> PRINT 01111111 00111111 11100000 00001111 11111000 11111100 11111110 11111111 BM-Editor> RECALL C244 BM-Editor> INVERT BM-Editor> PRINT 10000001 11000000 01100001 00110001 00011001 00001100 00000110 00000011 BM-Editor> PRODUCT CV RV BM-Editor> PRINT 10100001 00000000 10100001 10100001 10100001 00000000 00000000 10100001 BM-Editor> INVERT Matrix not invertible. BM-Editor> QUIT UNIX>
Let's perform an example on the matrix in files/Inv-Ex.txt:
10110 01011 10111 11001 01100 |
You first go through each row of M from the first to the last, doing the following steps:
Action | M | Inv |
Start | ||
i=0 No swap necesary. Set row 2 = row 2 + row 0. |
||
i=0 still. Set row 3 = row 3 + row 0. |
||
i=1 No swap necesary. Set row 3 = row 3 + row 1. |
||
i=1 still. Set row 4 = row 4 + row 1. |
||
i=2 Swap row 2 and row 3 |
||
i=2 still. Set row 4 = row 4 + row 2. |
||
i=3 Swap row 3 and row 4 |
Action | M | Inv |
Start: i=4 No action necessary. |
||
i=3 Set row 3 = row 3 + row 4. |
||
i=2 No action necessary. |
||
i=1 Set row 1 = row 1 + row 3. |
||
i=1 still. Set row 1 = row 1 + row 4. |
||
i=0 Set row 0 = row 0 + row 2. |
||
i=0 still. Set row 0 = row 0 + row 3. |
Finally, let's double-check ourselves:
BM-Editor> READ files/Inv-Ex.txt BM-Editor> STORE Inv-Ex BM-Editor> INVERT BM-Editor> PRINT 01101 11011 11010 00111 10100 BM-Editor> STORE Inv BM-Editor> PRODUCT Inv Inv-Ex BM-Editor> PRINT 10000 01000 00100 00010 00001 BM-Editor>
The gradescript will have up to 200 test cases. 30 percent of them will test matrix inversion and 20 percent of them will test the PGM file. Your PGM doesn't have to match mine in format; however, its size and pixels must match mine exactly.