Matrix
Goals
Matrix
Use Inheritance, Multiple Inheritance, and Polymorphism to solve a problem Implement Operator Overloading
Lines of Code: 1106 including whitespace and comments
No global variables may be used
You may NOT remove any methods
Again, adding members and methods is completely fine All code must be a part of the Matrix namespace
Everything I've started you with is already inside of it
Matrix Addition Matrix Multiplication
Vector addition is the same as matrix addition. Just treat each vector as a 1 X N matrix Dot Product
Multiply the elements in the corresponding positions together and then sum the products
Scalar Arithmetic
The scalar operations that you have to implement don't technically exist in Vector/Matrix arithmetic. What you should do is add/subtract/multiply the scalar with each element in the Vector/Matrix.
Scalar Arithmetic Examples
{1, 2, 3} + 10 == {11, 12, 13}
{10, 20, 30} - 5 == {5, 15, 25}
{1, 2, 3} * 2 == {2, 4, 6}
Inheritance in the Problem
To avoid rewriting code we will be using inheritance. Admittedly, this is probably the worst reason to use inheritance but it does give us a chance to practice with the technique.
ConstBaseVector
An abstract class that contains all of the operations you can perform on a Vector without modifying it.
BaseVector
An abstract class that represents all of the operations you can perform on a generic Vector. Inherits from ConstBaseVector
Vector
The concrete Vector class. Inherits from BaseVector
VectorRef
A class that represents a reference to a vector. We can't get away with just a Vector& in this problem because when we build the Matrix class we will want to be able to get a column. A column is a Vector, but there won't be a physical Vector in the Matrix class that holds the column so we have to create a VectorRef over this logical Vector.
Inherits from ConstVectorRef AND BaseVector
ConstVectorRef
The const version of VectorRef Inherits from ConstBaseVector
Iterators
You will have to provide iterators for both your Vector and Matrix classes. For Vector, you have VectorIterator and ConstVectorIterator which are random access iterators. Random access means in addition to being able to go forward we can also go backward and are also able to access any element in the container through this iterator.
Matrices have RowIterators, ConstRowIterators, ColumnIterators, and ConstColumnIterators, all of which are random access iterators.
You will find that that 98% of the code across all of the iterators is the same but unless you use some advanced C++ concepts you will have to copy and paste the code across the classes because of type differences. I do recommend doing that copy and pasting though for this assignment as it will save you quite a bit of time.
I highly recommend using iterators in your solution. This would be the standard thing to do if you were in industry and should make writing your solution easier.
This would also be a great time to practice using the functions in the algorithm and functional libraries. For example, you could use transform and negate to easily and concisely implement operator-.
Starter Code
Make sure to start your assignment from the starter code. The starter code comes with all of the CmakeLists.txt as well as the correct structure for what to submit. If you fail to use the starter code not only will your life be much harder as you are writing things from scratch, but it is unlikely that everything will compile together correctly.
The starter code contains all of the source files and methods you have to write in addition to the testing code. The header files contain a description of what each method does and each one should be fairly straightforward to implement. Most of the functions should be implementable in 5 or fewer lines of code. My longest was 8 lines of code so don't feel too overwhelmed by the number of functions you have to write.
You must implement all of the functions given and are not allowed to change their signatures. If you need to change the behavior from the methods that are using the compiler provided defaults you may change them. You are also free to add as many new functions as you wish.
You will need to add data members to some of the classes.
The code I had to write contains some advanced techniques that we haven't covered in class. They are discussed below but I also left them in the provided code everywhere they need to be used.
Virtual Inheritance
This solves the "dreaded diamond problem" by ensuring that the base class only appears a single time in a derived class that inherits from multiple parents that all inherit from a common ancestor. You can read more about here and here.
Files Used In
It is used in BaseVector and ConstVectorRef as VectorRef inherits from both of them.
const_cast
const_cast can be used to add or remove const on a type. It is useful when trying to implement logical constness but bitwise const is preventing you from doing it as well as to make sure the correct function gets called when there are multiple functions with the same signature but only differ based on whether they are const or not.
Logically Const: From the outside nothing appears to change while internally they may have been some change
Bitwise Const: None of the bits in the object change.
While a very powerful tool const_cast can lead to many subtle bugs if not used appropriately and so I suggest not using it in your code yet. You can read more about it here.
Files Used In
const_cast is used in ConstVectorRef to allow nonconst pointers to point to const objects. This is ok because in ConstVectorRef we only perform const operations on the object. This allows VectorRef to later inherit from ConstVectorRef and still modify the elements if it needs to. This saves us from having to recreate everything in ConstVectorRef again inside of VectorRef.
RowIterator and ColumnIterator to ensure that the const version of Matrix::at is called and not the nonconst version.
Interior Type Declarations
You will see a lot of statements of the form using newTypeName = oldTypeName; throughout the code. These are called type aliases and allow you to give a different name to an old type. These are useful because it allows you to update your code quickly if the type of a variable might change. For example, in our code if we wanted to go from a Vector of ints to a Vector of doubles we could just change the line value_type = int to value_type = double in BaseVector.h and then we would be done instead of having to replace each instance of int with double.
When the type aliases are inside of a class like the ones you see in the starter code they are a part of the class and can be accessed by doing ClassName::type_alias. For example, ConstBaseVector::value_type. This form of type aliasing is extremely common with containers, especially with templated ones. For example, did you ever wonder what the type of an iterator of an std::vector<int> is? It is std::vector<int>::iterator.
These type aliases are heavily used by iterator based code and value_type, difference_type, pointer, reference, and iterator_category are required in your iterator for them to be able to be used with the methods
in algorithm.
Files Used In
Pretty much all of them.
Google Test
We are using Google Test to test your assignment. Google Test is a unit testing framework that helps you write tests to verify your code is working. We will be covering Google Test soon but I wanted to show it to you here so you can get some exposure to it. Make sure to take a look at the test files to see what is happening and to get a feel for how to use Google Test.
We will not test you with any invalid input. For example, we won't do Vectors/Matrices of the incorrect size for the operation
Index out of bounds
Dereference an iterator that is out of bounds
In CLion, the configuration to run the tests is called MatrixTester.
Google Test will greatly increase your build time. This is because Google Test uses a lot of templating and macros which makes the compiler do a lot more work than it has had to do in our previous programs.
Not all of the tests are up yet but there are a few to give you an idea of what is to come.
Almost all if not all of the tests depend on your iterators working correctly so if things are failing double check your iterators.
What to Submit
A zip file containing the src directory. Yes, you really are zipping the folder this time.
Hints
Iterators are very, very useful for this problem
Some basic math concepts can be used to reduce the amount of code you write For example, a - b is the same as a + -b
For implementing Matrix *= Matrix it is easiest to implement Matrix * Matrix and then just do *this =
*this * rhs;
I think that this project involves a lot of technicality so please ask questions if you get confused about anything.
- 2022-03-04
- 2022-03-04
- 2022-03-04
- 2022-03-04