diff --git a/.gitignore b/.gitignore
index f8305e747aa9833c9c18f69f80a2f0349b69398e..337b081acf511b0876dfef038158c1ba67f37b07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-a.out
\ No newline at end of file
+a.out
+build
diff --git a/CMakeDemo/CMakeLists.txt b/CMakeDemo/CMakeLists.txt
index 25013000a4ba983578849a79ca11bd140c5b99c2..36e83d33fd02ec274cfff5ab4a7be77580bd5544 100644
--- a/CMakeDemo/CMakeLists.txt
+++ b/CMakeDemo/CMakeLists.txt
@@ -16,4 +16,6 @@ endif()
 
 add_executable(hello_world HelloWorld.cpp)
 
-add_executable(sanitizer Sanitizer.cpp)
\ No newline at end of file
+add_executable(sanitizer Sanitizer.cpp)
+
+add_executable(matmult MatMult.cpp Matrix.cpp)
\ No newline at end of file
diff --git a/CMakeDemo/MatMult.cpp b/CMakeDemo/MatMult.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..58e68d14451802f2c66f6f7a0b3838eb5ea3bd7a
--- /dev/null
+++ b/CMakeDemo/MatMult.cpp
@@ -0,0 +1,41 @@
+#include "Matrix.h"
+#include "Timer.h"
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <fstream>
+
+int main( int argc, const char * argv[] )
+{
+    std::vector<std::string> args( argv, argv + argc );
+    
+    std::cout << "Called with arguments: ";
+    for( const auto & arg : args )
+    {
+        std::cout << "\"" << arg << "\" ";
+    }
+    std::cout << "\n";
+    
+    if( args.size() == 4 )
+    {
+        std::cout << "Reading lhs matrix...\n";
+        Matrix m0( readMatrixFromFile( args[1] ) );
+        std::cout << "Reading rhs matrix...\n";
+        Matrix m1( readMatrixFromFile( args[2] ) );
+        
+	Timer timer;
+        std::cout << "Computing product...\n";        
+        timer.start();
+	Matrix m2( m0 * m1 );
+        timer.stop();        
+        std::cout << "Product took " << timer.elapsed() << "ms\n"; 
+        
+        std::cout << "Writing result matrix...\n";
+        writeMatrixToFile( m2, args[3] );
+    }
+    else
+    {
+        std::cerr << "Usage: " << args[0] << " [IN_MATRIX_FILE IN_MATRIX_FILE OUT_MATRIX_FILE]\n";
+    }
+}
diff --git a/CMakeDemo/Matrix.cpp b/CMakeDemo/Matrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..064a46d68b9f0506db78e3551a592766617c07bf
--- /dev/null
+++ b/CMakeDemo/Matrix.cpp
@@ -0,0 +1,103 @@
+#include "Matrix.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <istream>
+#include <fstream>
+#include <iterator>
+
+Matrix::Matrix( std::istream & is )
+{ 
+    is >> m_ >> n_;
+    
+    values_.resize( m_ * n_ );
+    
+    std::copy_n( std::istream_iterator<double>(is), m_ * n_, std::begin( values_ ) );
+
+    if(!is)
+        throw std::runtime_error("Error parsing Matrix!");
+}
+
+Matrix Matrix::operator+( const Matrix & other ) const
+{
+    Matrix result( *this ); // copy *this into the result    
+    std::transform( result.values_.begin(), result.values_.end(), 
+                    other.values_.begin(), result.values_.begin(), 
+                    std::plus<double>() );
+    return result;
+}
+
+Matrix Matrix::operator-( const Matrix & other ) const
+{
+    Matrix result( *this ); // copy *this into the result    
+    std::transform( result.values_.begin(), result.values_.end(),
+                    other.values_.begin(), result.values_.begin(),
+                    std::minus<double>() );
+    return result;
+}
+
+Matrix Matrix::operator*( const Matrix & other ) const
+{
+    if( n_ != other.m_ )
+        throw std::invalid_argument("Matrix multiplication failed due to invalid layout!");
+        
+    Matrix result( m_, other.n_ );
+    
+    for( size_t i = 0; i < m_; ++i )
+        for( size_t j = 0; j < other.n_; ++j )
+            for( size_t k = 0; k < n_; ++k )
+                result.get( i, j ) += get( i, k ) * other.get( k, j );
+    
+    return result;
+}
+
+
+std::ostream & operator<<( std::ostream & os, const Matrix & m )
+{
+    for( size_t i = 0; i < m.rows(); ++i )
+    {
+        if( i != 0 )
+            os << "\n";
+        
+        for( size_t j = 0; j < m.cols(); ++j )
+        {
+            if( j != 0 )
+                os << " ";
+            
+            os << m.get( i, j );
+        }
+    }
+    
+    return os;
+}
+
+void Matrix::transpose()
+{
+    for( size_t i = 0; i < m_; ++i )
+        for( size_t j = i + 1; j < n_; ++j )
+            std::swap( get(i,j), get(j,i) );
+} 
+
+Matrix readMatrixFromFile( const std::string & filename )
+{
+    std::ifstream ifs( filename );
+    if(!ifs)
+        throw std::runtime_error("Error opening file \"" + filename + "\"!" );
+    
+    return Matrix( ifs );
+}
+
+void writeMatrixToFile( const Matrix & m, const std::string & filename )
+{
+    std::ofstream ofs( filename );
+    if(!ofs)
+        throw std::runtime_error("Error opening file \"" + filename + "\" for writing!" );
+    
+    ofs << m.rows() << " " << m.cols() << "\n";
+    std::copy( std::begin( m.getValues() ), std::end( m.getValues() ), std::ostream_iterator<double>(ofs, "\n") );
+    
+    if(!ofs)
+        throw std::runtime_error("Error writing to file \"" + filename + "\"!" );
+    
+}
\ No newline at end of file
diff --git a/CMakeDemo/Matrix.h b/CMakeDemo/Matrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a69971e1630ae1980cd7e89441fcc3e5acfb93a
--- /dev/null
+++ b/CMakeDemo/Matrix.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <vector>
+#include <istream>
+
+class Matrix
+{
+public:
+    Matrix() = default;
+    Matrix( const size_t m, const size_t n, const double v ) 
+        : m_(m), n_(n), values_( m * n, v ) { }
+    Matrix( const size_t m, const size_t n ) : Matrix( m, n, 0.0 ) { }
+    explicit Matrix( std::istream & is );   
+    
+    double   get( const size_t row, const size_t col ) const { return values_[ row * n_ + col ]; }
+    double & get( const size_t row, const size_t col ) { return values_[ row * n_ + col ]; }
+    
+    size_t rows() const { return m_; }
+    size_t cols() const { return n_; }
+    
+    Matrix operator+( const Matrix & other ) const;
+    Matrix operator-( const Matrix & other ) const;
+    Matrix operator*( const Matrix & other ) const;
+    
+    inline bool operator==( const Matrix & other ) const;
+    inline bool operator!=( const Matrix & other ) const;
+    
+    void transpose();
+    
+    const std::vector<double> & getValues() const { return values_; }
+    
+private:
+    size_t m_ = 0;
+    size_t n_ = 0;
+    std::vector<double> values_;
+};
+
+Matrix readMatrixFromFile( const std::string & filename );
+
+void writeMatrixToFile( const Matrix & m, const std::string & filename );
+
+std::ostream & operator<<( std::ostream & os, const Matrix & m );
+
+
+bool Matrix::operator==( const Matrix & other ) const
+{
+    return m_ == other.m_ && n_ == other.n_ && values_ == other.values_;
+}
+
+bool Matrix::operator!=( const Matrix & other ) const
+{
+    return !(*this == other);
+}
\ No newline at end of file
diff --git a/CMakeDemo/Timer.h b/CMakeDemo/Timer.h
new file mode 100644
index 0000000000000000000000000000000000000000..18a2daca60a6cbf9cef04100b08a6a2dd3ad2d13
--- /dev/null
+++ b/CMakeDemo/Timer.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <chrono>
+
+class Timer
+{
+public:
+    Timer() : begin_(), end_( begin_ ) { }
+    
+    void start()
+    {
+        begin_ = std::chrono::high_resolution_clock::now();
+        end_ = begin_;
+    }
+    
+    void stop()
+    {
+        end_ = std::chrono::high_resolution_clock::now();
+    }
+    
+    double elapsed() const
+    {
+        std::chrono::duration<double, std::milli> elapsed = end_ - begin_;
+        return elapsed.count();
+    }
+    
+private:
+    std::chrono::high_resolution_clock::time_point begin_;
+    std::chrono::high_resolution_clock::time_point end_;
+};
+