Please git pull
frequently to get the latest changes.
- 26/06: Fixing up test stub
- 26/06: Moved euclidean norm comment to appropriate section, fixed up test case example in README.md
- 29/06: Exception X and Y substitition clarified in throwing exceptions section
- 30/06: Added second exception for
comp6771::unit
- 05/07: Added exception string clarification for unit vector when euclidean norm is zero - check out exception string
- 07/07: Git submission instructions updated to be VERY clear to push your code to master branch before submitting
- 07/07: Incorrect euclidean_norm pattern used
Write a Euclidean Vector Class Library in C++, with its interface given in include/euclidean_vector.h
and its implementation in source/euclidean_vector.cpp
.
We have outlined all key parts of this class below that should be implemented.
You may have to scroll horizontally to view these tables
t tName | Constructor | Description and Hints | Examples | Exception: Why thrown & what message |
---|---|---|---|---|
Default Constructor | euclidean_vector() |
A constructor that generates a euclidean vector with a dimension of 1 and magnitude of 0.0. You can assume the integer input will always be non-negative. |
|
N/A |
Single-argument Constructor | explicit euclidean_vector(int) |
A constructor that takes the number of dimensions (as a int) but no magnitudes, sets the magnitude in each dimension as 0.0. You can assume the integer input will always be non-negative. |
|
N/A |
Constructor | euclidean_vector(int, double); |
A constructor that takes the number of dimensions (as an int ) and initialises the
magnitude in each dimension as the second argument (a double ). You can assume the
integer input will always be non-negative.
|
|
N/A |
Constructor | euclidean_vector(std::vector<double>::const_iterator, std::vector<double>::const_iterator) |
A constructor (or constructors) that takes the start and end of an iterator to a
std:vector<double> and works out the required dimensions, and sets the
magnitude in each dimension according to the iterated values.
|
|
N/A |
Constructor | euclidean_vector(std::initializer_list<double>) |
A constructor that takes an initialiser list of double s to populate vector
magnitudes. You will have to do your own research to implement this one.
|
|
N/A |
Copy Constructor | euclidean_vector(euclidean_vector const&) |
|
N/A | |
Move Constructor | euclidean_vector(euclidean_vector&&) |
|
N/A |
auto a = comp6771::euclidean_vector(1); // a Euclidean Vector in 1 dimension, with default magnitude 0.0.
auto b = comp6771::euclidean_vector(2, 4.0); // a Euclidean Vector in 2 dimensions with magnitude 4.0 in both dimensions
auto v = std::vector<double>{5.0, 6.5, 7.0};
auto c = comp6771::euclidean_vector(l.begin(), l.end()); // a Euclidean Vector in 3 dimensions constructed from a vector of magnitudes
- You may assume that all arguments supplied by the user are valid. No error checking on constructors is required.
- It's very important your constructors work. If we can't validly construct your objects, we can't test any of your other functions.
You must explicitly declare the destructor as default.
For more info look here
Name | Operator | Description | Examples | Exception: Why thrown & what message |
---|---|---|---|---|
Copy Assignment | euclidean_vector& operator=(euclidean_vector const&) |
A copy assignment operator overload |
|
N/A |
Move Assignment | euclidean_vector& operator=(euclidean_vector&&) |
A move assignment operator |
|
N/A |
Subscript |
operator[]
A const and non-const declaration is needed |
Allows to get and set the value in a given dimension of the Euclidean vector. Hint: you may
need two overloadeds to achieve this requirement. Note: It's a requirement you use asserts to ensure the index passed is valid. |
|
N/A |
Unary plus | euclidean_vector operator+() |
Returns a copy of the current object. |
|
N/A |
Negation | euclidean_vector operator-() |
Returns a copy of the current object, where each scalar value has its sign negated. |
|
N/A |
Compound Addition | euclidean_vector& operator+=(euclidean_vector const&) |
For adding vectors of the same dimension. |
|
Given: X = a.dimensions(), Y = b.dimensions()
When: X != Y Throw: "Dimensions of LHS(X) and RHS(Y) do not match" |
Compound Subtraction | euclidean_vector& operator-=(euclidean_vector const&)
|
For subtracting vectors of the same dimension. |
|
Given: X = a.dimensions(), Y = b.dimensions()
When: X != Y Throw: "Dimensions of LHS(X) and RHS(Y) do not match" |
Compound Multiplication | euclidean_vector& operator*=(double) |
For scalar multiplication, e.g. [1 2] * 3 = [3 6] |
|
N/A |
Compound Division | euclidean_vector& operator/=(double) |
For scalar division, e.g. [3 6] / 2 = [1.5 3] |
|
When: b == 0 Throw: "Invalid vector division by 0" |
Vector Type Conversion |
explicit operator std::vector<double>() |
Operators for type casting to a std::vector |
|
N/A |
List Type Conversion |
explicit operator std::list<double>() |
Operators for type casting to a std::list |
|
N/A |
Prototype | Description | Usage | Exception: Why thrown & what message |
---|---|---|---|
double at(int) const |
Returns the value of the magnitude in the dimension given as the function parameter | a.at(1); |
When: For Input X: when X is < 0 or X is >= number of dimensions Throw: "Index X is not valid for this euclidean_vector object" |
double& at(int) |
Returns the reference of the magnitude in the dimension given as the function parameter | a.at(1); |
When: For Input X: when X is < 0 or X is >= number of dimensions Throw: "Index X is not valid for this euclidean_vector object" |
int dimensions() |
Return the number of dimensions in a particular euclidean_vector | a.dimensions(); |
N/A |
In addition to the operations indicated earlier, the following operations should be supported as friend functions. Note that these friend operations don't modify any of the given operands.
Name | Operator | Description | Usage | Exception: Why thrown & what message |
---|---|---|---|---|
Equal | bool operator==(euclidean_vector const&, euclidean_vector const&) |
True if the two vectors are equal in the number of dimensions and the magnitude in each dimension is equal. |
|
N/A |
Not Equal | bool operator!=(euclidean_vector const&, euclidean_vector const&) |
True if the two vectors are not equal in the number of dimensions or the magnitude in each dimension is not equal. |
|
N/A |
Addition | euclidean_vector operator+(euclidean_vector const&, euclidean_vector const&) |
For adding vectors of the same dimension. |
|
Given: X = b.dimensions() , Y = c.dimensions()
When: X != Y Throw: "Dimensions of LHS(X) and RHS(Y) do not match" |
Subtraction | euclidean_vector operator-(euclidean_vector const&, euclidean_vector const&) |
For substracting vectors of the same dimension. |
|
Given: X = b.dimensions() , Y = c.dimensions()
When: X != Y Throw: "Dimensions of LHS(X) and RHS(Y) do not match" |
Multiply | euclidean_vector operator*(euclidean_vector const&, double) |
For scalar multiplication, e.g. [1 2] * 3 = 3 * [1 2] = [3 6] .
Hint: you'll need two operators, as the scalar can be either side of the vector.
|
|
N/A |
Divide | euclidean_vector operator/(euclidean_vector const&, double) |
For scalar division, e.g. [3 6] / 2 = [1.5 3] |
|
When: c == 0 Throw: "Invalid vector division by 0" |
Output Stream | std::ostream& operator<<(std::ostream&, euclidean_vector const&) |
Prints out the magnitude in each dimension of the Euclidean vector (surrounded by
[ and ] ), e.g. for a 3-dimensional vector: [1 2 3] . Note: When printing the magnitude, simple use the double << operator.
|
|
N/A |
The following are functions that operate on a Euclidean vector, but shouldn't be a part of its interface. They may be friends, if you need access to the implementation, but you should avoid friendship if you can.
Name | Operator | Description | Usage | Exception: Why thrown & what message |
---|---|---|---|---|
auto euclidean_norm(euclidean_vector const& v) -> double; |
Returns the Euclidean norm of the vector as a double . The Euclidean norm is the
square root of the sum of the squares of the magnitudes in each dimension. E.g, for the vector
[1 2 3] the Euclidean norm is sqrt(1*1 + 2*2 + 3*3) = 3.74 .
|
|
When: v.dimensions() == 0 Throw: "euclidean_vector with no dimensions does not have a norm" |
|
auto unit(euclidean_vector const& v) -> euclidean_vector; |
Returns a Euclidean vector that is the unit vector of v . The magnitude for each
dimension in the unit vector is the original vector's magnitude divided by the Euclidean norm.
|
|
When: v.dimensions() == 0 Throw: "euclidean_vector with no dimensions does not have a unit vector" When: comp6771::euclidean_norm(v) == 0 Throw: "euclidean_vector with zero euclidean normal does not have a unit vector" |
|
auto dot(euclidean_vector const& x, euclidean_vector const& y) -> double
|
Computes the dot product of x ⋅ y ; returns a
double . E.g., [1 2] ⋅ [3 4] = 1 * 3 + 2 * 4 = 11
|
|
Given: X = a.dimensions() , Y = b.dimensions()
When: X != Y Throw: "Dimensions of LHS(X) and RHS(Y) do not match" Note: We will not be testing the case of multiplying two 0-dimension vectors together. |
The Euclidean norm should only be calculated when required and ideally should be cached if required again. We may run test cases on large vectors calculating the Euclidean norm many times. Hint: consider using a mutable data member where appropriate in conjunction with another data member to appropriate cache the euclidean norm. This is done for performance reasons.
Your Euclidean vector is required to store the magnitudes of each dimension inside of a
unique_ptr
. This is a unique_ptr
to a C-style double
array.
To create a dynamically allocated C-style double array and add it to a unique pointer, but not
require any direct use of the new
/std::malloc
call, you can use the following:
// ass2 spec requires we use double[]
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
auto magnitudes_ = std::make_unique<double[]>(8); // 8 is an example
Please note, the theory for unique_ptr
will be covered in week 5. Until that point, there will be parts of the assignment (e.g. move constructors, copy constructors) that you may struggle to implement. However, before week 5 lectures you are able to implement many other functions. That is because the unique_ptr
is in many ways an alias for a raw pointer - i.e. you can treat magnitudes_
like a raw pointer
For example:
this->magnitudes_[0] += other.magnitudes_[0]
You are required to throw exceptions in certain cases. These are specified in the tables above. We
have provided a euclidean_vector
exception class for you to throw. You are welcome to throw other
exceptions if you feel they are more appropriate.
Note: while the particular exception thrown does not matter, you are required to pass the strings specified in the tables above. In these strings, please use common sense to substitute values like X and Y for their actual numerical values
You must:
- Include a header guard in
euclidean_vector.h
- Use C++20 style and methods where appropriate
- Make sure that all appropriate member functions are
const
-qualified - Leave a moved-from object in a state with
0
dimensions - Implement this class within the comp6771 namespace
- Must assume that addition, subtraction, multiplication, and division operations on two 0-dimension vectors are valid operations. In all cases the result should still be a 0-dimension vector.
- We're asking you to implement
operator!=
because you'll see it in a lot of production codebases, and it's important that you know how to write it correctly.
You must not:
- Write to any files that aren't provided in the repo (e.g. storing your vector data in an auxilliary file)
- Add a main function
euclidean_vector.cpp
You:
- Should try to mark member functions that will not throw exceptions with
noexcept
- Are not required to make any member function explicit unless directly asked to in the spec.
You must ensure that each operator (3.) and method (4.) appropriately either has:
- A const member function; or
- A non-const member function; or
- Both a const and non-const member function
Please think carefully about this. The function declarations intentionally do not specify their
constness, except for one exception, the at()
operator. This has an explicit const
and non-const
declaration to help you out.
In most cases you will only need a single function, but in a couple of cases you will need both a
const
and non-const
version.
Here is a sample and example of Catch2 tests to write
TEST_CASE("Creation of unit vectors") {
SECTION("You have two identical vectors") {
auto a = comp6771::euclidean_vector(2);
a[0] = 1;
a[1] = 2;
auto b = comp6771::euclidean_vector(2);
b[0] = 1;
b[1] = 2;
auto c = comp6771::unit(a);
auto d = comp6771::unit(b);
REQUIRE(c == d);
}
}
If you haven't done so already, clone this repository.
$ git clone [email protected]:z5555555/20T2-cs6771-ass2
(Note: Replace z5555555 with your zid)
Navigate inside the directory. You can then open vscode with code .
(note the dot).
If you haven't done so already, clone the repository:
Similar to the first tutorial, you simply to have to run Ctrl+Shift+P
and then type Run Test
and
hit enter. VS Code will compile and run all of your tests and produce an output.
Part of your assignment mark will come from the quality and extensiveness of tests that you write.
You can add more test files to the test/euclidean_vector/
directory. Simply copy test/euclidean_vector/euclidean_vector_test1.cpp
into another file in that directory.
Note, everytime that you add a new file to the test/euclidean_vector/
directory you will need to add
another few lines to test/CMakeLists.txt
. You can once again, simply copy the test reference for
euclidean_vector_test1.cpp
and rename the appropriate parts. Every time you update CMakeLists.txt
in
any repository, in VSCode you should codess Ctrl+Shift+P
and run Reload Window
for the changes to
take effect.
This assignment will contribute 15% to your final mark.
The assessment for the assignment recognizes the difficulty of the task, the importance of style, and the importance of appropriate use of programming methods (e.g. using while loops instead of a dozen if statements).
50% |
Correctness The correctness of your program will be determined automatically by tests that we will run against your program. You will not know the full sample of tests used prior to marking. |
25% |
Your tests You are required to write your own tests to ensure your program works. You will write tests in the test/ directory. At the top of each file you will also include a block comment to explain the rational and approach you took to writing tests. Please read the Catch2 tutorial or review lecture/tutorial content to see how to write tests. Tests will be marked on several
factors. These include, but are not limited to:
|
20% |
C++ best practices Your adherence to good C++ best practice in lecture. This is not saying that if you conform to the style guide you will receive full marks for this section. This 20% is also based on how well you use modern C++ methodologies taught in this course as opposed to using backwards-compatible C methods. Examples include: Not using primitive arrays and not using pointers. We will also penalise you for standard poor practices in programming, such as having too many nested loops, poor variable naming, etc. |
5% |
clang-format In your project folder, run the following commands on all cpp/h files in the `source` and `test` directory. $ clang-format-11 -style=file -i /path/to/file.cpp
If, for each of these files, the program outputs nothing (i.e. is linted correctly), you will receive full marks for
this section (5%).
A video explaining how to use clang-format can be found HERE.
|
The following actions will result in a 0/100 mark for this assignment, and in some cases a 0 for COMP6771:
- Knowingly providing your work to anyone and it is subsequently submitted (by anyone).
- Submitting any other person's work. This includes joint work.
The lecturer may vary the assessment scheme after inspecting the assignment submissions but it will remain broadly similar to the description above.
The work you submit must be your own work. Submission of work partially or completely derived from any other person or jointly written with any other person is not permitted.
The penalties for such an offence may include negative marks, automatic failure of the course and possibly other academic discipline. Assignment submissions will be examined both automatically and manually for such submissions.
Relevant scholarship authorities will be informed if students holding scholarships are involved in an incident of plagiarism or other misconduct.
Do not provide or show your assignment work to any other person — apart from the teaching staff of COMP6771.
If you knowingly provide or show your assignment work to another person for any reason, and work derived from it is submitted, you may be penalized, even if the work was submitted without your knowledge or consent. This may apply even if your work is submitted by a third party unknown to you.
Note you will not be penalized if your work has the potential to be taken without your consent or knowledge.
This assignment is due Monday 13th of July, 19:59:59. Submit the assignment using the following comand while logged into the CSE machines:
6771 submit ass2
This will submit whatever is on the master branch of THIS repository (the one this README.md file is contained in) at the moment of submission.
Please ensure that you commit and push your local code TO your gitlab repository (called the origin remote) before submitting, otherwise your code will not be submitted
Please ensure that you can build and run your tests successfully on the CSE machine.
If your assignment is submitted after this date, each hour it is late reduces the maximum mark it can achieve by 2%.
For example if an assignment you submitted with a raw awarded mark of 85% was submitted 5 hours late, the late submission would have no effect (as maximum mark would be 90%).
If the same assignment was submitted 20 hours late it would be awarded 60%, the maximum mark it can achieve at that time.