You will implement a C++ class called Vector that represents 3D vectors in space (i.e., vectors with x, y, and z components – not to be confused with std::vector).

Boilerplate files for Vector.h and Vector.cc are provided; your task is to fully implement these files.

A demo program called hw7.cc is also provided to test your Vector implementation. You will need to update several function calls in hw7.cc to match your implementation choices. Note that hw7.cc will only compile completely once your Vector class is fully implemented, so feel free to comment out portions of hw7.cc as you incrementally test your code.

A Makefile is not provided. You will need also need to create a Makefile to compile Vector.cc and hw7.cc to run your program. Your Makefile should compile the code with the g++ options -g -Wall -std=c++17.

Vector Properties

You will implement the Vector class with the following properties:

  • The Vector class and its associated functions should be defined within the namespace vector374.
  • Internally, a Vector is represented as an array of three floats corresponding to the magnitudes in the x, y, and z directions (at indices 0, 1, and 2 respectively).
  • This array must be dynamically allocated on the heap when a Vector instance is created, and properly deleted when the Vector is destroyed or no longer in use.

Constructors/Destructor

You will implement three constructors:

  • A default (0-argument) constructor that initializes a Vector to (0, 0, 0).
  • A constructor with three float parameters that sets the initial values for the x, y, and z magnitudes.
  • A copy constructor that creates a new Vector as a copy of an existing one.

You should also implement a destructor that performs any necessary cleanup when a Vector object is deleted. If no cleanup is required, the destructor body may be left empty.

Assignment/Updating Assignment Operators

  • The class should define assignment for vectors (u = v).
  • The class should also define the updating assignment operators += and -= (i.e., u += v and u -= v). These operators must perform element-by-element addition or subtraction of the vector components, respectively.
  • These assignment operators should modify the vector they are used on – in this case, vector u.

Mathematical Operators

  • You should overload the + operator to perform vector addition. Given two Vector instances v1 and v2, the expression v1 + v2 should return a new Vector whose components are the element-wise sums of v1 and v2.
  • Similarly, you should overload the - operator to perform vector subtraction. Given two Vector instances v1 and v2, the expression v1 - v2 should return a new Vector whose components are the element-wise differences of v1 and v2.
  • The operator * should compute the inner product (dot product) of two Vectors.
  • For example, if v1 = (a,b,c) and v2 = (d,e,f), then v1 * v2 should return the scalar value a * d + b * e + c * f as a float.
  • The operator * should also be overloaded to perform scalar multiplication so that if v is a Vector (a,b,c) and k is a float, then both v * k and k * v return new Vectors with each component of v multiplied by k – i.e., (a * k, b * k, c * k).
  • You should create a function called cross that computes the cross product of two Vectors and returns a new Vector instance representing the cross product. The below image shows the formula for computing a cross product of two 3D vectors A and B:

If A = [a,b,c] and B = [d,e,f] then A x B = (b*f - c*e, c*d - a*f, a*e - b*d)

Note

It would be nice to use the x operator since it visually resembles the cross product symbol, but C++ only allows certain operators to be overloaded. And unfortunately, the letter x is not one of them. Therefore, creating a function named cross is the best alternative.

  • All these mathematical operators should not modify the original vectors they are used on. Instead, they should create and return a new Vector instance representing the result. (With the exception of the dot product operator, which should return a scalar value.)

Comparison Operators

  • Vectors should be compared using the == (equals) and != (not equals) operators. These must perform value comparison rather than object equality (i.e., being the same exact object in memory). The result of these operators should be a boolean (i.e., true or false).
  • Two Vectors are equal if each of their corresponding components are exactly equal. That is, if you have a vector v1 = (a,b,c) and vector v2 = (d,e,f) then v1 == v2 if a == d && b == e && c == f.
  • Two Vectors are not equal if at least one pair of corresponding components differ. That is, if you have a vector v1 = (a,b,c) and vector v2 = (d,e,f) then v1 != v2 if a != d || b != e || c != f.

Note

Typically, it is not recommended to compare floating-point values using == or != due to rounding errors that can cause unexpected results. However, for the purposes of this assignment, it is ok to use these comparisons because we will not be testing with edge-case floating-point values. That said, in real-world applications, using direct equality checks on floats is generally discouraged!

Other

  • Vector should define a length function that computes and returns the magnitude of the vector as a float. For a vector with components (a,b,c), the length is computed by: √( a^2 + b^2 + c^2).
  • You may use std::sqrt and/or std::pow to implement this function.
  • The class should also define stream output by overloading the << operator so that writing s << v outputs the Vector v to the output stream s in the format (a,b,c). The x, y, z components should be separated by commas with no spaces, and surrounded by one pair of parentheses. No trailing newline or std::endl should be added.

Any accessors and or mutators (getters and setters) as needed

  • You will need public getter methods if non-member and non-friend functions need access to Vector members.

Tip

You have the flexibility to decide whether each function or operator should be implemented as a member function, non-member function, friend function, or non-friend function, as long as the functions behave correctly and as intended.

Refer to lecture materials for guidance and tips on how to determine the appropriate type for each function.

Note

Several of these functions are required to return new Vectors. This means actual Vector values, not pointers or references to Vectors that have been allocated elsewhere.

Expected Output

Once you have completed your implementations of the Makefile, Vector.h, and Vector.cc (and have updated hw7.cc to correctly call your necessary functions), you should run the program using ./hw7 and expect to see the following output:

default Vector v1, should be (0,0,0): (0,0,0)
Vector v2 with initial values, should be (1,2,3): (1,2,3)
Vector v3 from copy constructor, should be (1,2,3): (1,2,3)
Vector assignment, should have three copies of (3.1,-2.5,2.7):
  v1: (3.1,-2.5,2.7)  v3: (3.1,-2.5,2.7)  v4: (3.1,-2.5,2.7)
Dot product:
  (1,2,3) * (1,2,3) = 14
  (3.1,-2.5,2.7) * (1,2,3) = 6.2
Scalar product:
  (3.1,-2.5,2.7) * 2 = (6.2,-5,5.4)
  -3.2 * (3.1,-2.5,2.7) = (-9.92,8,-8.64)
Update assignment:
  v1 += v2; v1 is now: (4.1,-0.5,5.7)
  v1 -= v2; v1 is now: (3.1,-2.5,2.7)
Sum:
  (1.2,3.4,-5.6) + (-2.1,4.3,6.5) = (-0.9,7.7,0.9)
Difference:
  (-2.1,4.3,6.5) - (1.2,3.4,-5.6) = (-3.3,0.9,12.1)
Cross product:
  (1.2,3.4,-5.6) x (-2.1,4.3,6.5) = (46.18,3.96,12.3)
Length:
  |(1.2,3.4,-5.6)| = 6.66033
  |(-2.1,4.3,6.5)| = 8.07156
Equals:
  (1.2,3.4,-5.6) == (-2.1,4.3,6.5) : false
  (1.2,3.4,-5.6) == (1.2,3.4,-5.6) : true
  (3.1,-2.5,2.7) == (3.1,-2.5,2.7) : true
Not Equals:
  (1.2,3.4,-5.6) != (-2.1,4.3,6.5) : true
  (1.2,3.4,-5.6) != (1.2,3.4,-5.6) : false
  (3.1,-2.5,2.7) != (3.1,-2.5,2.7) : false

Your submission will be assessed based on output correctness. The autograder will compare your program’s output from hw7.cc line by line against the expected output.

A correct implementation must match the expected output exactly.

Please do not attempt to hard-code the output; staff will verify that your output is produced through the proper implementation and usage of the Vector class.

Linting

We have included cpplint.py which you should run to style check your C++ code for both Vector.h and Vector.cc. The autograder will run it on your code as well.

Warning

cpplint may generate warnings about the header guard naming in Vector.h. To suppress these warnings, we have included comments with // NOLINT. Please do not remove these comments.

Memory Errors

Your code should be memory-error free – to test this, you can run valgrind on the produced hw7 executable, just like how you run valgrind with C executables: valgrind --leak-check=full ./hw7.

Tips

You can refer to the Complex class examples from lecture to review C++ class syntax and conventions. You may find it helpful to start with that code and modify it as needed to use the new array representation for the Vector data and make other necessary changes.

You will submit your implementations of Vector.h, Vector.cc, your modified version of hw7.cc that matches the expected output, and your Makefile to Gradescope via Gitlab.