• C++ Programming for Financial Engineering
    Highly recommended by thousands of MFE students. Covers essential C++ topics with applications to financial engineering. Learn more Join!
    Python for Finance with Intro to Data Science
    Gain practical understanding of Python to read, understand, and write professional Python code for your first day on the job. Learn more Join!
    An Intuition-Based Options Primer for FE
    Ideal for entry level positions interviews and graduate studies, specializing in options trading arbitrage and options valuation models. Learn more Join!

SHOW ME THE CODE !

Binary tree

150px-Binary_tree.svg.png

1633990707354.png
 
Exsan, how does your work compare to similar stuff in Boost Intrusive

I have not seen this before, please let me go through it.
However, before any comment I would say that my work is kind of THINK DIFFERENT, so far roughly more than 100K lines source code- fully integrated. I developed most of the library of my product, I try to avoid external libraries, as you can imagine my work is just: OOP + POINTERS
By the way, which version of Boost have you lately installed in your machine? 1.77?
have you tried the new Visual Studio 64 bits? do you recommend it ?
 
Last edited:
Refreshing my C++ programming skills by reviewing the material in the book : C++17 by Ivor Horton; the idea is to try and implement some numerical code in the short term.

Any feedback is super-helpful.

Merge Sort.
Merge Sort:
// MergeSort.cpp : MergeSort on an std::array<T,N>

#include <iostream>
#include <array>
#include <cmath>
#include <iomanip>

template <class T, std::size_t N>
void merge(std::array<T, N>& A, const int l, const int m, const int r)
{
    // A[l..m] and A[m+1..r] are two sorted lists and
    // we are interested to merge them.

    int l1{ l };        //This index always points to the beginning of the first list
    int l2{ m + 1 };    //This index always points to the beginning of the second list
    int i{ 0 };         //This index always points to the beginning of the merged list
  
    std::array<T, N> result {};  //The merged list will be held in a fixed-size array (auto storage duration), until
                                        //merge is complete.

    // Iterate until we do not reach the end of atleast one of the lists
    while (l1 <= m && l2 <= r)
    {
        // Compare the current element of the first list with the second list
        // and find the least element.
        if (A[l1] <= A[l2])
        {
            result[i] = A[l1];
            ++l1;
        }
        else
        {
            result[i] = A[l2];
            ++l2;
        }
        ++i;
    }

    // If the end of either one of the lists is reached, flush all the remaining elements
    // of the other list to result
    while (l1 <= m)
    {
        result[i] = A[l1];
        ++i;
        ++l1;
    }

    while (l2 <= r)
    {
        result[i] = A[l1];
        ++i;
        ++l2;
    }

    //Copy all elements from the result into A[l..r]
    int j{ l };
    for (int k{}; k < r - l + 1; ++k)
    {
        A[j] = result[k];
        ++j;
    }
}

template <class T, int N>
void mergeSort(std::array<T,N>& a, int l, int r)
{
    // If the problem is of size 1, then we are done. There is nothing to be
    // sorted.
    if (N == 1 || l == r)
        return;

    // Divide the problem into 2 smaller subproblems of a smaller size (N/2).
    int m{ static_cast<int>(floor((l + r) / 2.0)) };

    // Recursively conquer the two sub-problems of size (N/2)
    mergeSort<T, N>(a, l, m);
    mergeSort<T, N>(a, m+1, r);

    // Now, we have two sorted lists and we would like to merge them.
    merge<T, N>(a, l, m, r);
}

int main()
{
    std::array<int, 10> A{ 10,9,8,7,6,5,4,3,2,1 };
    std::cout << "Before mergeSort : " << std::endl;
    for (int element : A)
    {
        std::cout << std::setw(5) << element;
    }
    std::cout << std::endl;
    mergeSort(A, 0, 9);

    std::cout << "After mergeSort : " << std::endl;
    for (int element : A)
    {
        std::cout << std::setw(5) << element;
    }
    std::cout << std::endl;
    return 0;
}
 
Last edited:
Is there not a standard STL algorithm?
A while loop with nested if else might be 1) slow, 2) shaky/flaky, 3) difficult to maintain.
e.g. arrays of length 1'000'000, exception handling etc.

It looks like reinvention of wheel. It means less time for 'bigger' stuff.

BTW what's a good rationale for merge()? Just asking. It's not documented.

// Complexity: O(N^2)??
 
Last edited:
Is there not a standard STL algorithm?
A while loop with nested if else might be 1) slow, 2) shaky/flaky, 3) difficult to maintain.
e.g. arrays of length 1'000'000, exception handling etc.

It looks like reinvention of wheel. It means less time for 'bigger' stuff.

BTW what's a good rationale for merge()? Just asking. It's not documented.

// Complexity: O(N^2)??

Daniel, in case of Merge sort, if the array has size 2^M = N , then at level j of the recursion tree, you are left with 2^j arrays of size N/2^j each that you wanna merge. The merge operation is linear so, each stage should take like O(N) work and there are \log N/\log 2 stages so, its ~N \log N; that's atleast what I recall.

merge() is supposed to simply encapsulate the functionality of merging two sorted arrays.

I will add some exception handling; also want to learn to write unit tests for my code (I have used xUnit before, but haven't used a framework for C++).

BTW, i did bookmark this SE post on how to implement classic sorting algorithms in modern C++. But, I don't know the standard library <algorithms> just yet. The book I am using is modular; might take me a little while to write code like in that SE post.
 
Hi guys,

I am writing a matrix class - and wanted to get some feedback. My idea is that one should be capable of creating both fixed-size(compile-time) and dynamic matrices. So, containerType template parameter would be either std::array<T,N> or std::vector<N>.

I should provide proper iterators to traverse through my matrix and also need to overload the assignment operator.

Matrix.h:
#pragma once

#include <array>
#include <vector>

template <typename T, int r, int c, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1>;
using Matrix2d = Matrix<double, 2, 2>;
using Matrix3d = Matrix<double, 3, 3>;
using Matrix4d = Matrix<double, 4, 4>;

using Matrix1i = Matrix<int, 1, 1>;
using Matrix2i = Matrix<int, 2, 2>;
using Matrix3i = Matrix<int, 3, 3>;
using Matrix4i = Matrix<int, 4, 4>;

using Matrix1f = Matrix<float, 1, 1>;
using Matrix2f = Matrix<float, 2, 2>;
using Matrix3f = Matrix<float, 3, 3>;
using Matrix4f = Matrix<float, 4, 4>;

using Vector1d = Matrix<double, 1, 1>;
using Vector2d = Matrix<double, 1, 2>;
using Vector3d = Matrix<double, 1, 3>;
using Vector4d = Matrix<double, 1, 4>;

using Vector1i = Matrix<int, 1, 1>;
using Vector2i = Matrix<int, 2, 2>;
using Vector3i = Matrix<int, 3, 3>;
using Vector4i = Matrix<int, 4, 4>;

using Vector1f = Matrix<float, 1, 1>;
using Vector2f = Matrix<float, 2, 2>;
using Vector3f = Matrix<float, 3, 3>;
using Vector4f = Matrix<float, 4, 4>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime = 0, int colsAtCompileTime = 0, typename containerType = std::array<scalarType,(rowsAtCompileTime * colsAtCompileTime)>>
class Matrix
{
private:
    containerType A;
    int _rows;
    int _cols;
public:
    Matrix() = default;
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
 
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix operator*(const Matrix& m) const;
};

Matrix.cpp:
#pragma once

#include "Matrix.h"
#include <stdexcept>

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::Matrix(const Matrix& m):A{m.A}, _rows{m.rows()}, _cols{m.cols()}, _size{m.size()}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::size() const
{
    return A.size();
}

/// <summary>
/// Coefficient accessors.
/// The primary coefficient accessor is the overloaded parenthesis operators. The data of the matrix
/// is stored in an underlying sequential container such as std::array or std::vector. The element
/// A(i,j) is at an offset of (i*_cols)+j in memory.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i">The row index</param>
/// <param name="j">The col index</param>
/// <returns>The element at position (i,j)</returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j) const
{
    containerType::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

/// <summary>
/// Matrix addition.
/// Overload of the binary operator +. The left hand side and the right hand side matrix must have
/// the same number of rows and columns.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i"></param>
/// <param name="j"></param>
/// <returns></returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType& Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j)
{
    containerType::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }

 
    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator-(const Matrix& B) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }
    return result;
}

template<typename scalarType, int m, int n, int p, typename containerType>
Matrix<scalarType, m, p, containerType> operator*(const Matrix<scalarType, m, n, containerType>& A, const Matrix<scalarType, n, p, containerType>& B)
{
    Matrix<scalarType, m, p, containerType> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }
}
 
Last edited:
Hi guys,

I am writing a matrix class - and wanted to get some feedback. My idea is that one should be capable of creating both fixed-size(compile-time) and dynamic matrices. So, containerType template parameter would be either std::array<T,N> or std::vector<N>.

I should provide proper iterators to traverse through my matrix and also need to overload the assignment operator.

Matrix.h:
#pragma once

#include <array>
#include <vector>

template <typename T, int r, int c, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1>;
using Matrix2d = Matrix<double, 2, 2>;
using Matrix3d = Matrix<double, 3, 3>;
using Matrix4d = Matrix<double, 4, 4>;

using Matrix1i = Matrix<int, 1, 1>;
using Matrix2i = Matrix<int, 2, 2>;
using Matrix3i = Matrix<int, 3, 3>;
using Matrix4i = Matrix<int, 4, 4>;

using Matrix1f = Matrix<float, 1, 1>;
using Matrix2f = Matrix<float, 2, 2>;
using Matrix3f = Matrix<float, 3, 3>;
using Matrix4f = Matrix<float, 4, 4>;

using Vector1d = Matrix<double, 1, 1>;
using Vector2d = Matrix<double, 1, 2>;
using Vector3d = Matrix<double, 1, 3>;
using Vector4d = Matrix<double, 1, 4>;

using Vector1i = Matrix<int, 1, 1>;
using Vector2i = Matrix<int, 2, 2>;
using Vector3i = Matrix<int, 3, 3>;
using Vector4i = Matrix<int, 4, 4>;

using Vector1f = Matrix<float, 1, 1>;
using Vector2f = Matrix<float, 2, 2>;
using Vector3f = Matrix<float, 3, 3>;
using Vector4f = Matrix<float, 4, 4>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime = 0, int colsAtCompileTime = 0, typename containerType = std::array<scalarType,(rowsAtCompileTime * colsAtCompileTime)>>
class Matrix
{
private:
    containerType A;
    int _rows;
    int _cols;
public:
    Matrix() = default;
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
 
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix operator*(const Matrix& m) const;
};

Matrix.cpp:
#pragma once

#include "Matrix.h"
#include <stdexcept>

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::Matrix(const Matrix& m):A{m.A}, _rows{m.rows()}, _cols{m.cols()}, _size{m.size()}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::size() const
{
    return A.size();
}

/// <summary>
/// Coefficient accessors.
/// The primary coefficient accessor is the overloaded parenthesis operators. The data of the matrix
/// is stored in an underlying sequential container such as std::array or std::vector. The element
/// A(i,j) is at an offset of (i*_cols)+j in memory.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i">The row index</param>
/// <param name="j">The col index</param>
/// <returns>The element at position (i,j)</returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j) const
{
    containerType::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

/// <summary>
/// Matrix addition.
/// Overload of the binary operator +. The left hand side and the right hand side matrix must have
/// the same number of rows and columns.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i"></param>
/// <param name="j"></param>
/// <returns></returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType& Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j)
{
    containerType::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }

 
    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator-(const Matrix& B) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }
    return result;
}

template<typename scalarType, int m, int n, int p, typename containerType>
Matrix<scalarType, m, p, containerType> operator*(const Matrix<scalarType, m, n, containerType>& A, const Matrix<scalarType, n, p, containerType>& B)
{
    Matrix<scalarType, m, p, containerType> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }
}
Sorry for the duplication, but posting some working code now. One failure I corrected:
  • When using class templates, the member function definitions should have been in the same header-file Matrix.h, because these are really function templates (not instantiated). Failing to do this results in the compiler not instantiating them, and the linker would complain about missing symbols.
A matrix M can be initialized as, M << 1, 0, 0,
0, 1, 0,
0, 0, 1.

Nice to have functionality.

Any suggestions/feedback is super-helpful.

Matrix.h:
#pragma once

#include <array>
#include <vector>
#include <stdexcept>
#include <type_traits>
#include <iterator>

template <typename T, int r=0, int c=0, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1, std::array<double,1>>;
using Matrix2d = Matrix<double, 2, 2, std::array<double, 4>>;
using Matrix3d = Matrix<double, 3, 3, std::array<double, 9>>;
using Matrix4d = Matrix<double, 4, 4, std::array<double, 16>>;

using Matrix1i = Matrix<int, 1, 1, std::array<int, 1>>;
using Matrix2i = Matrix<int, 2, 2, std::array<int, 4>>;
using Matrix3i = Matrix<int, 3, 3, std::array<int, 9>>;
using Matrix4i = Matrix<int, 4, 4, std::array<int, 16>>;

using Matrix1f = Matrix<float, 1, 1, std::array<float, 1>>;
using Matrix2f = Matrix<float, 2, 2, std::array<float, 4>>;
using Matrix3f = Matrix<float, 3, 3, std::array<float, 9>>;
using Matrix4f = Matrix<float, 4, 4, std::array<float, 16>>;

using Vector1d = Matrix<double, 1, 1, std::vector<double>>;
using Vector2d = Matrix<double, 1, 2, std::vector<double>>;
using Vector3d = Matrix<double, 1, 3, std::vector<double>>;
using Vector4d = Matrix<double, 1, 4, std::vector<double>>;

using Vector1i = Matrix<int, 1, 1, std::vector<double>>;
using Vector2i = Matrix<int, 2, 2, std::vector<double>>;
using Vector3i = Matrix<int, 3, 3, std::vector<double>>;
using Vector4i = Matrix<int, 4, 4, std::vector<double>>;

using Vector1f = Matrix<float, 1, 1, std::vector<double>>;
using Vector2f = Matrix<float, 2, 2, std::vector<double>>;
using Vector3f = Matrix<float, 3, 3, std::vector<double>>;
using Vector4f = Matrix<float, 4, 4, std::vector<double>>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
class Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>
{
private:
    std::array<scalarType, rowsAtCompileTime* colsAtCompileTime> A;
    int _rows;
    int _cols;
    int _size;
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator currentPosition{A.begin()};
public:
    Matrix();
    Matrix(int m, int n);
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
    
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;        //Subscript operator
    scalarType& operator()(const int i, const int j);            //Subscript operator const arrays
    Matrix operator+(const Matrix& m) const;                    
    Matrix operator-(const Matrix& m) const;
    Matrix& operator<<(const scalarType x);
    Matrix& operator,(const scalarType x);
    Matrix& operator=(const Matrix& right_hand_side);
};

template <typename scalarType>
class Matrix<typename scalarType, 0, 0, std::vector<scalarType>>
{
private:
    std::vector<scalarType> A;
    int _rows;
    int _cols;
    int _size;
    typename std::vector<scalarType>::iterator currentPosition{};
public:
    Matrix();
    Matrix(int m, int n);
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;

    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix& operator<<(const scalarType x);
    Matrix& operator,(const scalarType x);
    Matrix& operator=(const Matrix& right_hand_side);
};

// Non-member operator functions
template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::vector<scalarType>> operator*(const Matrix<scalarType, m, n, std::vector<scalarType>>& A, const Matrix<scalarType, p, q, std::vector<scalarType>>& B);

template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::array<scalarType,m*q>> operator*(const Matrix<scalarType, m, n, std::array<scalarType,m*n>>& A, const Matrix<scalarType, p, q, std::array<scalarType,p*q>>& B);


//Default constructor
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::Matrix():_rows{rowsAtCompileTime}, _cols{colsAtCompileTime}, currentPosition{A.begin()}
{
    //Do nothing.
}

//Default constructor
template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix() :_rows{ 0 }, _cols{ 0 }
{
    currentPosition = A.begin();
}

//Fixed-Size matrices
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime * colsAtCompileTime>>::Matrix(int m, int n)
{
    //Do nothing.
}

//Run-time matrices
template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix(int m, int n) : currentPosition{A.begin()}
{
    int numOfElements{ m * n };
    for (int i{}; i < numOfElements; ++i)
    {
        A.push_back(0);
    }
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix(const Matrix& m): A{m.A}, _rows{m.rows()}, _cols{m.cols()}, currentPosition{m.currentPosition}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime*colsAtCompileTime>>::Matrix(const Matrix& m) : A{ m.A }, _rows{ m.rows() }, _cols{ m.cols() }, currentPosition{ m.currentPosition }
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime* colsAtCompileTime>>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::cols() const
{
    return _cols;
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::rows() const
{
    return _rows;
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::size() const
{
    return A.size();
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::size() const
{
    return A.size();
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline scalarType Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator()(const int i, const int j) const
{
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline scalarType& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator()(const int i, const int j)
{
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it1{ A.begin() };
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it2{ m.A.begin() };
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator-(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::const_iterator it1{ A.begin() };
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::const_iterator it2{ m.A.begin() };
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator<<(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator<<(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator,(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator=(const Matrix& right_hand_side)
{
    if (this == &right_hand_side)
        return *this;

    this->A = right_hand_side.A;
    this->_rows = right_hand_side._rows;
    this->_cols = right_hand_side._cols;
    this->currentPosition = right_hand_side.currentPosition;
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator=(const Matrix& right_hand_side)
{
    if (this == &right_hand_side)
        return *this;

    this->A = right_hand_side.A;
    this->_rows = right_hand_side._rows;
    this->_cols = right_hand_side._cols;
    this->currentPosition = right_hand_side.currentPosition;
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator,(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>> Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator+(const Matrix& m) const
{
    Matrix<scalarType, 0, 0, std::vector<scalarType>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::vector<scalarType>::const_iterator it1{ A.begin() };
        typename std::vector<scalarType>::const_iterator it2{ m.A.begin() };
        typename std::vector<scalarType>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>> Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator-(const Matrix& m) const
{
    Matrix<scalarType, 0, 0, std::vector<scalarType>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::vector<scalarType>::const_iterator it1{ A.begin() };
        typename std::vector<scalarType>::const_iterator it2{ m.A.begin() };
        typename std::vector<scalarType>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int m, int n, int p, int q>
inline Matrix<scalarType, m, q, std::vector<scalarType>> operator*(const Matrix<scalarType, m, n, std::vector<scalarType>>& A, const Matrix<scalarType, p, q, std::vector<scalarType>>& B)
{
    if (n != p)
        throw std::logic_error("Error multiplying the matrices; the number of cols(A) must equal the number of rows(B)!");

    Matrix<scalarType, m, q, std::vector<scalarType>> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }

    return result;
}

template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::array<scalarType, m* q>> operator*(const Matrix<scalarType, m, n, std::array<scalarType, m* n>>& A, const Matrix<scalarType, p, q, std::array<scalarType, p* q>>& B)
{
    if (n != p)
        throw std::logic_error("Error multiplying the matrices; the number of cols(A) must equal the number of rows(B)!");

    Matrix<scalarType, m, q, std::array<scalarType, m * q>> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }

    return result;
}

TestMatrix.cpp:
// MatrixImpl.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include "Matrix.h"
#include <iomanip>

int main()
{
    Matrix<int, 3, 3> m1;
    m1 <<1, 0, 0,
        0, 1, 0,
        0, 0, 1;

    Matrix<int, 3, 3> m2;
    m2 << 2, 0, 0,
        0, 1, 0,
        0, 0, 3;

    Matrix<int, 3, 3> m3 = m1 + m2;

    for (int i{}; i < 3; ++i)
    {
        for (int j{}; j < 3; ++j)
        {
            std::cout << std::setw(5) << m3(i, j);
        }
        std::cout << std::endl;
    }
    return 0;
}
 
the notation m << .. is kinda non-standard and a bit weird. See how Python does it. And Boost uBLAS.

C++ initialisation

Hi Daniel,
  • Added a constructor that takes std::initializer_list<std::initializer_list<T>> as a parameter and initializes the matrix.

C++:
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime>::Matrix(std::initializer_list<std::initializer_list<scalarType>> list):Matrix<scalarType,rowsAtCompileTime,colsAtCompileTime>{}    //Delegate to the default constructor to set up the initial array
{
    // Initialize the array from our list.
    typename std::array<scalarType,rowsAtCompileTime*colsAtCompileTime>::iterator A_it{ A.begin() };
    typename std::initializer_list<std::initializer_list<scalarType>>::iterator i{};

    for (i = list.begin(); i < list.end(); ++i)
    {
        typename std::initializer_list<scalarType>::iterator j{};
        for (j = (*i).begin(); j < (*i).end(); ++j)
        {
            *A_it = *j;
            ++A_it;
        }
    }
}

  • Pretty printing the matrix in a rectangular format.
C++:
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
std::ostream& operator<<(std::ostream& os, Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime>& m)
{
    for (int i{}; i < m.rows(); ++i)
    {
        os << "Row(";
        for (int j{}; j < m.cols(); ++j)
        {
            if (j != m.cols() - 1)
                os << m(i, j) << std::setw(5);
            else
                os << m(i, j);
            if (j % 10 == 0 && j > 0)
                os << std::endl;
        }

        os << ")" << std::endl;
    }

    return os;
}
  • Multiplication with scalars.
C++:
template<typename scalarType, int m, int n, typename T>
Matrix<scalarType, m, n>& operator*(const T k, Matrix<scalarType, m, n>& mat)
{
    for (int i{}; i < m.rows(); ++i)
    {
        for (int j{}; j < m.cols(); ++j)
        {
            mat(i, j) *= k;
        }
    }

    return mat;
}

  • Created two separate types : Matrix for compile-time matrices and MatrixX for run-time matrices, since they really are different, instead of shoe-horning them into one.
I think something that might make this piece of code more useful, is the ability to promote a matrix e.g. from Matrix<int> to Matrix<long>(of the same dimensions). Right now, addition is too restrictive.

Quick Question - I am building this project in VS as a DLL. But, libraries are no good use, if traders/structurers/risk managers can't easily use them. I have seen a couple of solutions that allow exposing this functionality to excel. Do you have any suggestions?

Also, I read up a bit on the net, and my understanding is __declspec(dllexport) is used to expose C++ class member functions in a DLL to the the outside. Is my understanding correct?
 
Last edited:
Back
Top