# Start coding

## In this learning path

- [Introduction](https://learn.arm.com/learning-paths/cross-platform/matrix/)
- [Laying the foundations](https://learn.arm.com/learning-paths/cross-platform/matrix/1-foundations/)
- [Test the library](https://learn.arm.com/learning-paths/cross-platform/matrix/2-testing/)
- [Start coding](https://learn.arm.com/learning-paths/cross-platform/matrix/3-code-1/)
- [Implement matrix operations](https://learn.arm.com/learning-paths/cross-platform/matrix/4-code-2/)
- [Next Steps](https://learn.arm.com/learning-paths/cross-platform/matrix/_next-steps/)

With the infrastructure set up to build and test the Matrix library, you can now code the library. In this section, you add the core functionality of the library as well as unit tests to ensure functional integrity.

## About error handling

Error handling is a critical aspect of programming; balancing safety and security with performance. Depending on the context, the correct balance point may vary. For example, in the case of high performance computing for weather forecast, the dataset is extremely large and ever-increasing, but can be considered secure and curated. On the other hand, if the data used in the computations can be adversely computed or altered, it is preferable to have checks, such as out-of-bound access, enabled.

In the Matrix processing library, you implement two types of checks:
- Checks that impact performance, such as checking for out-of bound access at each access. These checks are only enabled in the `Debug` builds and the program exits with an assertion failure if a check fails.
- Checks with minor performance impact, such as checking that the matrices have the correct dimensions in a matrix multiplication. These checks are always enabled and the program exits with a message.

The idea here is to make the program fail in a noticeable way. Of course, in a real world application, the error should be caught and dealt with by the application, if it can. Error handling, and especially recovering from errors, can be a complex topic.

At the top of file `include/Matrix/Matrix.h`, include `<cassert>` to get the C-style assertions declarations for checks in `Debug` mode only, as well as `<cstddef>` which provides standard C declaration like `size_t`:

```cpp
#pragma once

#include <cassert>
#include <cstddef>

namespace MatComp {
```

Next, add a `die` function to call whenever the library needs to exit. Paste the code below right under the `const Version &getVersion();` declaration in `include/Matrix/Matrix.h`:

```cpp
/// Get the Matrix library version information.
const Version &getVersion();

/// Immediately terminates the application with \p reason as the error message
/// and the EXIT_FAILURE error code. It will also print the file name (\p
/// fileName) and line number (\p lineNumber) that caused that application to
/// exit.
[[noreturn]] void die(const char *fileName, size_t lineNumber,
                      const char *reason);
```

Note that `die` has been annotated with the `noreturn` attribute. This is a new feature of C++ that allows the compiler to take advantage of the fact that `die` will *not* return, so no code will be executed after it has been called. This information was previously passed through compiler-specific annotations, but now C++ provides a generic way to pass such information to the different compilers.

You also need to provide a body for the `die` function.

Open `lib/Matrix/Matrix.cpp` and include at the top of the file:
- `<cstdlib>` : to get the declaration of `exit` and `EXIT_FAILURE`.
- `<iostream>` : to get support for input/output, in order to emit useful information about the reason for exiting the program.

Add `die`’s body as shown below:

```cpp
#include "Matrix/Matrix.h"

#include <cstdlib>
#include <iostream>

namespace {
const MatComp::Version version = {.major = 0, .minor = 1, .patch = 0};
} // namespace

namespace MatComp {

const Version &getVersion() { return version; }

void die(const char *fileName, size_t lineNumber, const char *reason) {
    std::cerr << "Fatal: " << reason << " from " << fileName << ':'
              << lineNumber << '\n';
    exit(EXIT_FAILURE);
}
} // namespace MatComp
```

At this stage, the project should still build and compile, try it to confirm:

```bash
cd build
ninja
```

## The Matrix data structure

The Matrix library is able to deal with 1x1 matrices (scalar numbers), 1xN matrices (row-vector), Nx1 matrices (column vectors), and NxM matrices.

The matrix array is a single memory region, where the matrix elements are stored in [row major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order).

Matrices store elements. The Matrix library supports all *arithmetic* element types - signed and unsigned integer types - as well as floating point types.

The Matrix data structure has the following private data members:
- `numRows`: the number of rows in a matrix.
- `numColumns`: the number of columns in a matrix.
- `data`: the actual array of elements in a matrix.

Modern C++ offers constructs in the language to deal safely with memory; you will use `std::unique_ptr` which guarantees that the Matrix class will be safe from a whole range of memory management errors.

Add the following includes at the top of `include/Matrix/Matrix.h`, right under the `<cstddef>` include:

```cpp
#include <cassert>
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <iostream>
#include <memory>
#include <type_traits>
```

`<cstring>` provides useful declarations like `memcpy` that will be used to copy matrices’ content around. `<initializer_list>`, introduced with C++11, provides the declaration of the `initializer_list` type, which is a lightweight abstraction that allows creating an array of constant objects. `<memory>` gives access to the `unique_ptr` and `<type_traits>` allows to query information on the Matrix element types, either to check that a type is allowed or to select an optimized implementation at compile time.

Add the following lines to `include/Matrix/Matrix.h` in the MatComp namespace under the `die` function declaration:

```cpp
/// The Matrix class represents N x M matrices for all arithmetic types.
template <typename Ty>
class Matrix {
    static_assert(std::is_arithmetic<Ty>::value,
                  "Matrix only accept arithmetic (i.e. integer or floating "
                  "point) element types.");

  public:
  private:
    size_t numRows;    ///< The number of rows in this matrix.
    size_t numColumns; ///< The number of columns in this matrix.
    std::unique_ptr<Ty[]> data; ///< The actual data in this matrix, in row-major order.
};
```

This `Matrix` declaration deserves some comments:
- It makes use of `template`, a C++ language feature which allows to support all element types with a single and simple code base.
- It checks at compile time with the `static_assert` that the `Matrix` class was instantiated with an arithmetic data type, so that compilation can fail early with a simple and descriptive error message.
- The query about `Ty` being an arithmetic type is achieved through the use of the `std::is_arithmetic` type traits. Modern C++ provides many standard type traits to analyze types.
- The `Matrix` data is declared as a `std::unique_ptr<Ty[]>`. This means each `Matrix` instance owns its memory array, and that it is in charge of freeing it whenever a matrix is destructed.

At this stage, the project should still build and compile, try it to confirm:

```bash
cd build
ninja
```

## Construct matrices

You have added a bare `Matrix` class which is not very useful because there is no way to create a `Matrix` just yet.

First, add a private helper function `allocate` that the Matrix constructors will use. Add the following in the private section of the `Matrix` class (in `include/Matrix/Matrix.h`):

```cpp
/// Allocate (if need be) numElements to the Matrix. Die if the allocation
/// went wrong.
void allocate(size_t numElements) {
    if (numElements != 0) {
        data = std::make_unique<Ty[]>(numElements);
        if (!data)
            die(__FILE__, __LINE__, "Matrix allocation failure");
    }
}
```

You use this method to allocate memory for the element array. `new` will get enough memory to store `numElements` of type `Ty`. This is stored in the `data` `unique_ptr` with the `reset` function which enforces freeing memory previously referred to by `data`. If allocation fails, which is signaled with a `nullptr` (a zero pointer), `data` will not be valid, and the program should be terminated. This helper method is made private because it is only intended to be used by other methods from the `Matrix` class. Users of the `Matrix` objects have no reason to directly invoke this method.

With this in place, you can add some constructors in the public section of the `Matrix` class.

The very first `Matrix` you should be able to construct is an invalid `Matrix`. While this might sound strange, this is useful in practice, to signal errors. In this case, an invalid `Matrix` is a matrix with 0 rows and 0 columns. You can use the default constructor - that is, a constructor with no parameters) for this.

Add the following code in the public section of class `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Default construct an invalid Matrix.
constexpr Matrix() : numRows(0), numColumns(0), data(nullptr) {}
```

This constructor has been marked as `constexpr` which instructs the compiler that it can optimize this case at compile time because you are essentially constructing a matrix that has no run time dependency. It has zero rows, zero columns, and no memory allocated to it. This is known at compile time and can be propagated for optimizations.

You can now add a boolean conversion, using the conversion operator, that allows checking whether a `Matrix` instance is valid or not. It returns `false` if the `Matrix` object is invalid, `true` otherwise.

Add the following method in the public section of `Matrix`:

```cpp
/// Returns true if this matrix is valid.
operator bool() const {
    return numRows != 0 && numColumns != 0;
}
```

With these two methods in place, it is now time to add some tests.

Create file `tests/Matrix.cpp` and add the following code to it:

```cpp
#include "Matrix/Matrix.h"

#include "gtest/gtest.h"

#include <cstdint>

using MatComp::Matrix;

TEST(Matrix, defaultConstruct) {
    Matrix<int8_t> m0;
    EXPECT_FALSE(m0);

    Matrix<float> m1;
    EXPECT_FALSE(m1);
}

TEST(Matrix, booleanConversion) {
    EXPECT_FALSE(Matrix<int8_t>());    
    EXPECT_FALSE(Matrix<double>());

    EXPECT_FALSE(Matrix<int8_t>(1, 1));
    EXPECT_FALSE(Matrix<double>(1, 1));
}
```

Next, add `tests/Matrix.cpp` to the list of files used for the `Matrix` unit testing in the top-level `CMakeLists.txt` by adding file to the list of source files used by `matrix-test` target:

```cmake
add_executable(matrix-test tests/main.cpp
  tests/Matrix.cpp
  tests/Version.cpp)
```

The `defaultConstruct` test does not do much, it constructs two matrices, one with 8-bit signed integers, the other one with floating point numbers with the default constructor. In both cases, these matrices are expected to be invalid.

You should now check if tests pass:

```bash
cd build
ninja
ninja check
```

Constructing an invalid `Matrix` is very useful, but does not really make the `Matrix` class very helpful.

You can add some getters, methods that allow you to query some information about a `Matrix` object:
- `getNumRows`: get the number of rows this matrix has.
- `getNumColumns`: get the number of columns this matrix has.
- `getNumElements`: get the number of elements this matrix has (rows x columns elements).
- `getSizeInBytes`: get size in bytes used by this matrix (rows x columns x sizeof(element)).

Add those methods in the public section of `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Get the number of rows in this matrix.
size_t getNumRows() const { return numRows; }
/// Get the number of columns in this matrix.
size_t getNumColumns() const { return numColumns; }
/// Get the number of elements in this matrix.
size_t getNumElements() const { return numRows * numColumns; }
/// Get the storage size in bytes of the Matrix array.
size_t getSizeInBytes() const { return numRows * numColumns * sizeof(Ty); }
```

Next, modify the `defaultConstruct` in `tests/Matrix.cpp` test that you previously added so that it now looks like:

```cpp
TEST(Matrix, defaultConstruct) {
    Matrix<int8_t> m0;
    EXPECT_FALSE(m0);
    EXPECT_EQ(m0.getNumRows(), 0);
    EXPECT_EQ(m0.getNumColumns(), 0);
    EXPECT_EQ(m0.getNumElements(), 0);
    EXPECT_EQ(m0.getSizeInBytes(), 0);

    Matrix<float> m1;
    EXPECT_FALSE(m1);
    EXPECT_EQ(m1.getNumRows(), 0);
    EXPECT_EQ(m1.getNumColumns(), 0);
    EXPECT_EQ(m1.getNumElements(), 0);
    EXPECT_EQ(m1.getSizeInBytes(), 0);
}
```

The tests should still pass, check for yourself.

The next step is to be able to construct valid matrices, so add this constructor to the public section of class `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Construct a \p numRows x \p numCols uninitialized Matrix
Matrix(size_t numRows, size_t numCols) 
    : numRows(numRows), numColumns(numCols), data() { 
    allocate(getNumElements());
}
```

Next, add this test to `tests/Matrix.cpp`:

```cpp
TEST(Matrix, uninitializedConstruct) {
    Matrix<int16_t> m0(2, 3);
    EXPECT_TRUE(m0);
    EXPECT_EQ(m0.getNumRows(), 2);
    EXPECT_EQ(m0.getNumColumns(), 3);
    EXPECT_EQ(m0.getNumElements(), 6);
    EXPECT_EQ(m0.getSizeInBytes(), 6 * sizeof(int16_t));

    Matrix<double> m1(3, 4);
    EXPECT_TRUE(m1);
    EXPECT_EQ(m1.getNumRows(), 3);
    EXPECT_EQ(m1.getNumColumns(), 4);
    EXPECT_EQ(m1.getNumElements(), 12);
    EXPECT_EQ(m1.getSizeInBytes(), 12 * sizeof(double));
}
```

This constructs a valid `Matrix` if it contains elements), and the `uninitializedConstruct` test checks that two valid matrices of different types and dimensions can be constructed. You should also update the `booleanConversion` test in this file to check for boolean conversion for valid matrices so it now looks like:

```cpp
TEST(Matrix, booleanConversion) {
    EXPECT_FALSE(Matrix<int8_t>());
    EXPECT_FALSE(Matrix<double>());

    EXPECT_TRUE(Matrix<int8_t>(1, 1));
    EXPECT_TRUE(Matrix<double>(1, 1));
}
```

Compile and test again, all should pass:

```bash
cd build
ninja
ninja check
```

Another constructor that is missing is one that will create and initialize matrices to a known value. Let’s add it to `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Construct a \p numRows x \p numCols Matrix with all elements
/// initialized to value \p val.
Matrix(size_t numRows, size_t numCols, Ty val)
    : Matrix(numRows, numCols) {
    allocate(getNumElements());
    for (size_t i = 0; i < getNumElements(); i++) 
        data[i] = val;
}
```

Add boolean conversion tests for this new constructor by modifying `booleanConversion` in `tests/Matrix.cpp` so it looks like:

```cpp
TEST(Matrix, booleanConversion) {
    EXPECT_FALSE(Matrix<int8_t>());
    EXPECT_FALSE(Matrix<double>());

    EXPECT_TRUE(Matrix<int8_t>(1, 1));
    EXPECT_TRUE(Matrix<double>(1, 1));

    EXPECT_TRUE(Matrix<int8_t>(1, 1, 1));
    EXPECT_TRUE(Matrix<double>(1, 1, 2.0));
}
```

You should be getting the pattern now: each new feature or method comes with tests.

The `Matrix` class is missing two important methods:
- A *getter*, to read the matrix element at (row, col).
- A *setter*, to modify the matrix element at (row, col).

Add them now in the public section of `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Access Matrix element at (\p row, \p col) by reference.
Ty &get(size_t row, size_t col) {
    assert(*this && "Invalid Matrix");
    assert(row < numRows && "Out of bounds row access");
    assert(col < numColumns && "Out of bounds column access");
    return data[row * numColumns + col];
}
/// Access Matrix element at (\p row, \p col) by reference (const version).
const Ty &get(size_t row, size_t col) const {
    assert(*this && "Invalid Matrix");
    assert(row < numRows && "Out of bounds row access");
    assert(col < numColumns && "Out of bounds column access");
    return data[row * numColumns + col];
}
```

Add tests for those 3 methods in `tests/Matrix.cpp`:

```cpp
TEST(Matrix, fillConstruct) {
    Matrix<uint32_t> m0(2, 2, 13);
    EXPECT_TRUE(m0);
    EXPECT_EQ(m0.getNumRows(), 2);
    EXPECT_EQ(m0.getNumColumns(), 2);
    EXPECT_EQ(m0.getNumElements(), 4);
    EXPECT_EQ(m0.getSizeInBytes(), 4 * sizeof(uint32_t));
    for (size_t row = 0; row < m0.getNumRows(); row++)
        for (size_t col = 0; col < m0.getNumColumns(); col++)
            EXPECT_EQ(m0.get(row, col), uint32_t(13));

    Matrix<double> m1(2, 2, 16.0);
    EXPECT_TRUE(m1);
    EXPECT_EQ(m1.getNumRows(), 2);
    EXPECT_EQ(m1.getNumColumns(), 2);
    EXPECT_EQ(m1.getNumElements(), 4);
    EXPECT_EQ(m1.getSizeInBytes(), 4 * sizeof(double));
    for (size_t row = 0; row < m1.getNumRows(); row++)
        for (size_t col = 0; col < m1.getNumColumns(); col++)
            EXPECT_EQ(m1.get(row, col), double(16.0));
}

TEST(Matrix, getElement) {
    Matrix<uint32_t> m0(2, 3, 3);
    EXPECT_TRUE(m0);
    for (size_t row = 0; row < m0.getNumRows(); row++)
        for (size_t col = 0; col < m0.getNumColumns(); col++)
            EXPECT_EQ(m0.get(row, col), 3);
}

TEST(Matrix, setElement) {
    Matrix<uint32_t> m0(2, 3, 3);
    EXPECT_TRUE(m0);
    for (size_t row = 0; row < m0.getNumRows(); row++)
        for (size_t col = 0; col < m0.getNumColumns(); col++)
            EXPECT_EQ(m0.get(row, col), 3);
    m0.get(1, 2) = 65;
    for (size_t row = 0; row < m0.getNumRows(); row++)
        for (size_t col = 0; col < m0.getNumColumns(); col++)
            if (row == 1 && col == 2)
                EXPECT_EQ(m0.get(row, col), 65);
            else
                EXPECT_EQ(m0.get(row, col), 3);
}
```

These three tests and methods need to be added all together as they make use of each other.

Compile and check again. It’s important to ensure that the project works at each step and did not regress any of the previous steps.

```bash
cd build
ninja
ninja check
```

Now you’re ready to add a copy constructor and a move constructor in the public part of `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Copy-construct from the \p other Matrix.
Matrix(const Matrix &other)
    : numRows(other.numRows), numColumns(other.numColumns), data() {
    allocate(getNumElements());
    std::memcpy(data.get(), other.data.get(), getSizeInBytes());
}

/// Move-construct from the \p other Matrix.
Matrix(Matrix &&other)
    : numRows(other.numRows), numColumns(other.numColumns),
      data(std::move(other.data)) {
    // Invalidate other.
    other.numRows = 0;
    other.numColumns = 0;
}
```

Add a test in `tests/Matrix.cpp`:

```cpp
TEST(Matrix, copyConstruct) {
    const Matrix<int8_t> m0(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9});
    Matrix<int8_t> m1(m0);
    EXPECT_EQ(m0.getNumRows(), m1.getNumRows());
    EXPECT_EQ(m0.getNumColumns(), m1.getNumColumns());
    EXPECT_EQ(m0.getNumElements(), m1.getNumElements());
    EXPECT_EQ(m0.getSizeInBytes(), m1.getSizeInBytes());
    for (size_t row = 0; row < m1.getNumRows(); row++)
        for (size_t col = 0; col < m1.getNumColumns(); col++) {
            EXPECT_EQ(m0.get(row, col), m1.get(row, col));
            EXPECT_NE(&m0.get(row, col), &m1.get(row, col));
        }
}

TEST(Matrix, moveConstruct) {
    Matrix<int16_t> m0(3, 2, {1, 2, 3, 4, 5, 6});
    EXPECT_TRUE(m0);
    Matrix<int16_t> m1(std::move(m0));
    EXPECT_FALSE(m0);
    EXPECT_TRUE(m1);
    EXPECT_EQ(m1.getNumRows(), 3);
    EXPECT_EQ(m1.getNumColumns(), 2);
}
```

The other important time is when an object is assigned to, with the copy assignment and the move assignment operators that you will add now in the public part of `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Copy-assign from the \p rhs Matrix.
Matrix &operator=(const Matrix &rhs) {
    reallocate(rhs.getNumElements());
    if (getNumElements() != 0)
        std::memcpy(data.get(), rhs.data.get(), rhs.getSizeInBytes());
    return *this;
}

/// Move-assign from the \p rhs Matrix.
Matrix &operator=(Matrix &&rhs) {
    numRows = rhs.numRows;
    numColumns = rhs.numColumns;
    data = std::move(rhs.data);
    return *this;
}
```

The copy assignment makes use of the `reallocate` helper routine, which you should add in the private section of class `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Reallocate (if need be) numElements to the Matrix. Die if the allocation
/// went wrong. This method assumes \p numRows and \p numColumns have
/// their 'old' values, that will get updated as part of the re-allocation.
void reallocate(size_t newNumRows, size_t newNumColumns) {
    const size_t newNumElements = newNumRows * newNumColumns;
    if (getNumElements() != newNumElements) {
        if (newNumElements != 0) {
            data = std::make_unique<Ty[]>(newNumElements);
            if (!data)
                die(__FILE__, __LINE__, "Matrix re-allocation failure");
        } else
            data.reset(nullptr);
    }
    numRows = newNumRows;
    numColumns = newNumColumns;
}
```

Re-allocation might be necessary in the case of copy-assignment because the destination object might have been constructed with a different number of elements.

With all this in place, you can add the corresponding tests to `tests/Matrix.cpp`:

```cpp
TEST(Matrix, copyAssign) {
    const Matrix<int32_t> m0(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9});
    Matrix<int32_t> m1 = m0;
    EXPECT_EQ(m0.getNumRows(), m1.getNumRows());
    EXPECT_EQ(m0.getNumColumns(), m1.getNumColumns());
    EXPECT_EQ(m0.getNumElements(), m1.getNumElements());
    EXPECT_EQ(m0.getSizeInBytes(), m1.getSizeInBytes());
    for (size_t row = 0; row < m1.getNumRows(); row++)
        for (size_t col = 0; col < m1.getNumColumns(); col++)
            EXPECT_EQ(m0.get(row, col), m1.get(row, col));
}

TEST(Matrix, moveAssign) {
    Matrix<int16_t> m0(3, 2, {1, 2, 3, 4, 5, 6});
    EXPECT_TRUE(m0);
    Matrix<int16_t> m1 = std::move(m0);
    EXPECT_FALSE(m0);
    EXPECT_TRUE(m1);
    EXPECT_EQ(m1.getNumRows(), 3);
    EXPECT_EQ(m1.getNumColumns(), 2);
}
```

Check again if the tests build and pass:

```bash
cd build
ninja
ninja check
```

## Convenience constructors

For convenience, users should be provided with some useful methods to get specific types of matrices:
- `zeros` to get a zero initialized `Matrix`.
- `ones` to get a `Matrix` initialized with 1.
- `identity` to get the identity `Matrix`, which is a square matrix, with 1 on the diagonal and 0 elsewhere.

Users with Python with `NumPy` experience are used to those shortcuts, and they make the user code much more readable, this is often referred to as syntactic sugar, so let’s add these to `Matrix`’s public section in `include/Matrix/Matrix.h`:

```cpp
/// Get a zero initialized Matrix.
static Matrix zeros(size_t numRows, size_t numColumns) {
    return Matrix(numRows, numColumns, Ty(0));
}

/// Get a one initialized Matrix.
static Matrix ones(size_t numRows, size_t numColumns) {
    return Matrix(numRows, numColumns, Ty(1));
}

/// Get the identity Matrix.
static Matrix identity(size_t dimension) {
    Matrix id = zeros(dimension, dimension);
    for (size_t i = 0; i < dimension; i++)
        id.get(i, i) = Ty(1);
    return id;
}
```

They have been marked as `static`, which means these methods are not instance-specific, they are class methods.

Of course, you should have tests for these methods in `tests/Matrix.cpp`:

```cpp
TEST(Matrix, zeros) {
    Matrix<int16_t> z0 = Matrix<int16_t>::zeros(2, 6);
    EXPECT_TRUE(z0);
    EXPECT_EQ(z0.getNumRows(), 2);
    EXPECT_EQ(z0.getNumColumns(), 6);
    EXPECT_EQ(z0.getNumElements(), 12);
    EXPECT_EQ(z0.getSizeInBytes(), 12 * sizeof(int16_t));
    for (size_t row = 0; row < z0.getNumRows(); row++)
        for (size_t col = 0; col < z0.getNumColumns(); col++)
            EXPECT_EQ(z0.get(row, col), int16_t(0));
}

TEST(Matrix, ones) {
    Matrix<uint8_t> o0 = Matrix<uint8_t>::ones(4, 3);
    EXPECT_TRUE(o0);
    EXPECT_EQ(o0.getNumRows(), 4);
    EXPECT_EQ(o0.getNumColumns(), 3);
    EXPECT_EQ(o0.getNumElements(), 12);
    EXPECT_EQ(o0.getSizeInBytes(), 12 * sizeof(uint8_t));
    for (size_t row = 0; row < o0.getNumRows(); row++)
        for (size_t col = 0; col < o0.getNumColumns(); col++)
            EXPECT_EQ(o0.get(row, col), uint8_t(1));
}

TEST(Matrix, identity) {
    Matrix<uint8_t> i0 = Matrix<uint8_t>::identity(5);
    EXPECT_TRUE(i0);
    EXPECT_EQ(i0.getNumRows(), 5);
    EXPECT_EQ(i0.getNumColumns(), 5);
    EXPECT_EQ(i0.getNumElements(), 25);
    EXPECT_EQ(i0.getSizeInBytes(), 25 * sizeof(uint8_t));
    for (size_t row = 0; row < i0.getNumRows(); row++)
        for (size_t col = 0; col < i0.getNumColumns(); col++)
            if (row == col)
                EXPECT_EQ(i0.get(row, col), uint8_t(1));
            else
                EXPECT_EQ(i0.get(row, col), uint8_t(0));
}
```

Compile and check again - all tests should pass:

```bash
cd build
ninja
ninja check
```

## Display matrices

At some point, one will want to *see* the content of a `Matrix`, so add a simple output operator to dump the matrix content to a stream.

Add this code at the very end of `include/Matrix/Matrix.h`, outside of the `MatComp` namespace:

```cpp
/// Dump this Matrix in textual format to output stream \p os.
template <typename Ty>
std::ostream &operator<<(std::ostream &os, const MatComp::Matrix<Ty> &m) {
    for (size_t row = 0; row < m.getNumRows(); row++) {
        for (size_t col = 0; col < m.getNumColumns(); col++)
            os << '\t' << m.get(row, col) << ',';
        os << '\n';
    }
    return os;
}
```

This will print each row of the matrix to a different line, separating the values with commas and tab spaces.

Of course, you also need to add a test for this output operator in `tests/Matrix.cpp`:

```cpp
TEST(Matrix, dump) {
    std::ostringstream osstr;

    // Test horizontal vector.
    osstr << Matrix<int16_t>(1, 3, {1, 2, 3});
    EXPECT_EQ(osstr.str(), "\t1,\t2,\t3,\n");

    osstr.str("");

    // Test vertical vector.
    osstr << Matrix<int32_t>(3, 1, {1, 2, 3});
    EXPECT_EQ(osstr.str(), "\t1,\n\t2,\n\t3,\n");

    osstr.str("");

    // Test matrix.
    osstr << Matrix<int64_t>::identity(2);
    EXPECT_EQ(osstr.str(), "\t1,\t0,\n\t0,\t1,\n");
}
```

This test makes use of string streams, which enable you to capture and check the output without writing to the standard output. You need to add an include file at the top of `tests/Matrix.cpp` for the above test to compile:

```cpp
#include <sstream>
```

## Compare matrices

The last mundane operations you need are `Matrix` equality and inequality operators.

Add these to the public section of `Matrix` in `include/Matrix/Matrix.h`:

```cpp
/// Returns true iff both matrices compare equal.
bool operator==(const Matrix &rhs) const {
    // Invalid matrices compare equal.
    if (!*this && !rhs) return true;
    // If one is invalid, they can never compare equal.
    if (*this ^ rhs) return false;
    // Matrices with different dimensions are not equal.
    if (numRows != rhs.numRows || numColumns != rhs.numColumns) return false;
    // Everything else is equal and sound, compare the elements !
    for (size_t i = 0; i < getNumElements(); i++)
        if (data[i] != rhs.data[i]) return false;
    return true;
}

/// Returns true iff matrices do not compare equal.
bool operator!=(const Matrix &rhs) const { return !(*this == rhs); }
```

Add tests for these two operators in `tests/Matrix.cpp`:

```cpp
TEST(Matrix, equal) {
    EXPECT_TRUE(Matrix<int16_t>() == Matrix<int16_t>());
    
    EXPECT_FALSE(Matrix<int16_t>(2, 3) == Matrix<int16_t>());
    EXPECT_FALSE(Matrix<int16_t>(3, 2) == Matrix<int16_t>());
    EXPECT_FALSE(Matrix<int16_t>() == Matrix<int16_t>(2, 3));
    EXPECT_FALSE(Matrix<int16_t>(3, 2) == Matrix<int16_t>());

    EXPECT_FALSE(Matrix<int16_t>(3, 2) == Matrix<int16_t>(2, 3));
    EXPECT_TRUE(Matrix<int16_t>(3, 2, 1) == Matrix<int16_t>(3, 2, 1));
}

TEST(Matrix, notEqual) {
    EXPECT_FALSE(Matrix<int32_t>() == Matrix<int32_t>());

    EXPECT_TRUE(Matrix<int32_t>(2, 3) != Matrix<int32_t>());
    EXPECT_TRUE(Matrix<int32_t>(3, 2) != Matrix<int32_t>());
    EXPECT_TRUE(Matrix<int32_t>() != Matrix<int32_t>(2, 3));
    EXPECT_TRUE(Matrix<int32_t>(3, 2) != Matrix<int32_t>());

    EXPECT_TRUE(Matrix<int32_t>(3, 2) != Matrix<int32_t>(2, 3));
    EXPECT_FALSE(Matrix<int32_t>(3, 2, 1) != Matrix<int32_t>(3, 2, 1));
}
```

Check again if the tests build and pass:

```bash
cd build
ninja
ninja check
```

Congratulations, you now have a working library!

## What have you achieved so far?

At this stage, the code looks like this:

```
Matrix/
├── CMakeLists.txt
├── build/
...
├── external/
│   └── CMakeLists.txt
├── include/
│   └── Matrix/
│       └── Matrix.h
├── lib/
│   └── Matrix/
│       └── Matrix.cpp
├── src/
│   ├── getVersion.cpp
│   └── howdy.cpp
└── tests/
    ├── Matrix.cpp
    ├── Version.cpp
    └── main.cpp
```

After this rather long exercise, you have a minimalistic, yet fully-functional core for the matrix processing library, with some level of regression testing.

Modern C++ enables you to express move and copy semantics, and to use smart pointers to make memory management easy.

The compiler also catches a large number of type or misuse errors. With this core functionality in place, you have all you need to implement matrix operations in the next section.

You can refer to this chapter source code in `code-examples/learning-paths/cross-platform/matrix/chapter-3` in the archive that you have downloaded earlier.
