Line data Source code
1 : #pragma once 2 : 3 : #include <cstring> 4 : #include <initializer_list> 5 : #include <iomanip> 6 : #include <iostream> 7 : #include <memory> 8 : #include <vector> 9 : #include "exceptions.hpp" 10 : 11 : // ============ 12 : // Declarations 13 : // ============ 14 : 15 : template <typename T> 16 : class matrix 17 : { 18 : public: 19 : // === Constructors === 20 : 21 : // Constructor to create an UNINITIALIZED matrix 22 : // WARNING: Good for performance, but make sure to never use any uninitialized 23 : // elements! First argument: number of rows Second argument: number of columns 24 : matrix(const size_t &, const size_t &); 25 : 26 : // Constructor to create a diagonal matrix 27 : // Argument: a vector containing the elements on the diagonal 28 : // Number of rows and columns is inferred automatically 29 : matrix(const std::vector<T> &); 30 : 31 : // Constructor to create a diagonal matrix 32 : // Argument: an initializer_list containing the elements on the diagonal 33 : // Number of rows and columns is inferred automatically 34 : matrix(const std::initializer_list<T> &); 35 : 36 : // Constructor to create a matrix and initialize it to the given elements 37 : // First argument: number of rows 38 : // Second argument: number of columns 39 : // Third argument: a vector containing the elements (flattened) 40 : matrix(const size_t &, const size_t &, const std::vector<T> &); 41 : 42 : // Constructor to create a matrix and initialize it to the given elements 43 : // First argument: number of rows 44 : // Second argument: number of columns 45 : // Third argument: an initializer_list containing the elements (flattened) 46 : matrix(const size_t &, const size_t &, const std::initializer_list<T> &); 47 : 48 : // Copy constructor to create a new matrix with the same elements as an 49 : // existing matrix 50 : matrix(const matrix &); 51 : 52 : // Move constructor to move the elements of an existing matrix to a new matrix 53 : matrix(matrix &&); 54 : 55 : // === Member functions === 56 : 57 : // Overloaded operator = to assign the elements of one matrix to another 58 : // matrix 59 : matrix &operator=(const matrix &); 60 : 61 : // Overloaded operator = to move the elements of one matrix to another matrix 62 : matrix &operator=(matrix &&); 63 : 64 : // Member function used to obtain (but not modify) the number of rows in the 65 : // matrix 66 : size_t get_rows() const; 67 : 68 : // Member function used to obtain (but not modify) the number of columns in 69 : // the matrix 70 : size_t get_cols() const; 71 : 72 : // Overloaded operator () used to access matrix elements WITHOUT range 73 : // checking The indices start from 1: m(1, 2) would be the element at row 1, 74 : // column 2 First version: returns a reference, thus allows modification of 75 : // the element 76 : T &operator()(const size_t &, const size_t &); 77 : 78 : // Overloaded operator () used to access matrix elements WITHOUT range 79 : // checking The indices start from 1: m(1, 2) would be the element at row 1, 80 : // column 2 Second version: does not return a reference and declared as const, 81 : // does not allow modification of the element 82 : T operator()(const size_t &, const size_t &) const; 83 : 84 : // Static member function used to set the character width of the elements 85 : // when printing a matrix (will be used with std::setw) 86 : static void set_output_width(const int &); 87 : 88 : // === Friend functions === 89 : 90 : // Overloaded binary operator << used to easily print out a matrix to a stream 91 : template <typename U> 92 : friend std::ostream &operator<<(std::ostream &, const matrix<U> &); 93 : 94 : private: 95 : // The number of rows 96 : size_t rows{0}; 97 : 98 : // The number of columns 99 : size_t cols{0}; 100 : 101 : // A pointer to an array storing the elements of the matrix in flattened 102 : // (1-dimensional) form 103 : T *elements{nullptr}; 104 : 105 : // A smart pointer to manage the memory allocated for the elements 106 : std::unique_ptr<T[]> smart{nullptr}; 107 : 108 : // The character width of the elements when printing a matrix (will be used 109 : // with std::setw) 110 : static int output_width; 111 : }; 112 : 113 : // Initialize output_width to have a default value of 5 114 : template <typename T> 115 : int matrix<T>::output_width{5}; 116 : 117 : // ============== 118 : // Implementation 119 : // ============== 120 : 121 : // === Constructors === 122 : 123 : // Uninitialized constructor 124 : template <typename T> 125 60 : matrix<T>::matrix(const size_t &input_rows, const size_t &input_cols) 126 64 : : rows(input_rows), cols(input_cols) 127 : { 128 60 : if (rows == 0 or cols == 0) 129 2 : throw sayMessage{"Dimentions of matrix must be positive.\n"}; 130 58 : smart.reset(new T[ rows * cols ]); 131 56 : elements = smart.get(); 132 56 : } 133 : 134 : // Diagonal matrix initialized with vector 135 : template <typename T> 136 2 : matrix<T>::matrix(const std::vector<T> &input_diagonal) 137 2 : : rows(input_diagonal.size()), cols(input_diagonal.size()) 138 : { 139 2 : if (rows == 0) throw sayMessage{"Dimentions of matrix must be positive.\n"}; 140 2 : smart.reset(new T[ rows * cols ]); 141 2 : elements = smart.get(); 142 9 : for (size_t i{0}; i < rows; i++) 143 32 : for (size_t j{0}; j < cols; j++) 144 25 : elements[ (cols * i) + j ] = ((i == j) ? input_diagonal[ i ] : 0); 145 2 : } 146 : 147 : // Diagonal matrix initialized with initializer_list 148 : template <typename T> 149 1 : matrix<T>::matrix(const std::initializer_list<T> &input_diagonal) 150 1 : : matrix(std::vector<T>{input_diagonal}) 151 : { 152 1 : } 153 : 154 : // Full matrix initialized with vector 155 : template <typename T> 156 32 : matrix<T>::matrix(const size_t &input_rows, const size_t &input_cols, 157 : const std::vector<T> &input_elements) 158 36 : : rows(input_rows), cols(input_cols) 159 : { 160 32 : if (rows == 0 or cols == 0) 161 2 : throw sayMessage{"Dimentions of matrix must be positive.\n"}; 162 30 : if (input_elements.size() != rows * cols) 163 2 : throw sayMessage{"Size of vector must equal rows*cols.\n"}; 164 28 : smart.reset(new T[ rows * cols ]); 165 28 : elements = smart.get(); 166 394 : for (size_t i{0}; i < rows * cols; i++) elements[ i ] = input_elements[ i ]; 167 28 : } 168 : 169 : // Full matrix initialized with initializer_list 170 : template <typename T> 171 1 : matrix<T>::matrix(const size_t &input_rows, const size_t &input_cols, 172 : const std::initializer_list<T> &input_elements) 173 1 : : matrix(input_rows, input_cols, std::vector<T>{input_elements}) 174 : { 175 1 : } 176 : 177 : // Copy constructor 178 : template <typename T> 179 2 : matrix<T>::matrix(const matrix<T> &m) : rows(m.rows), cols(m.cols) 180 : { 181 2 : smart.reset(new T[ rows * cols ]); 182 2 : elements = smart.get(); 183 2 : std::memcpy(elements, m.elements, rows * cols * sizeof(T)); 184 2 : } 185 : 186 : // Move constructor 187 : template <typename T> 188 1 : matrix<T>::matrix(matrix<T> &&m) : rows(m.rows), cols(m.cols) 189 : { 190 1 : smart = move(m.smart); 191 1 : elements = smart.get(); 192 1 : m.rows = 0; 193 1 : m.cols = 0; 194 1 : m.elements = nullptr; 195 1 : } 196 : 197 : // === Member functions === 198 : 199 : // Copy assignment 200 : template <typename T> 201 3 : matrix<T> &matrix<T>::operator=(const matrix<T> &m) 202 : { 203 3 : if (m.rows != rows or m.cols != cols) 204 1 : throw sayMessage{"Copy assignment requires equal matrix dimensions.\n"}; 205 2 : if (elements == m.elements) return *this; 206 1 : rows = m.rows; 207 1 : cols = m.cols; 208 1 : smart.reset(new T[ rows * cols ]); 209 1 : elements = smart.get(); 210 1 : std::memcpy(elements, m.elements, rows * cols * sizeof(T)); 211 1 : return *this; 212 : } 213 : 214 : // Move assignment 215 : template <typename T> 216 3 : matrix<T> &matrix<T>::operator=(matrix<T> &&m) 217 : { 218 3 : if (m.rows != rows or m.cols != cols) 219 1 : throw sayMessage{"Move assignment requires equal matrix dimensions.\n"}; 220 2 : if (elements == m.elements) return *this; 221 1 : rows = m.rows; 222 1 : cols = m.cols; 223 1 : smart = move(m.smart); 224 1 : elements = smart.get(); 225 1 : m.rows = 0; 226 1 : m.cols = 0; 227 1 : m.elements = nullptr; 228 1 : return *this; 229 : } 230 : 231 : template <typename T> 232 411 : inline size_t matrix<T>::get_rows() const 233 : { 234 411 : return rows; 235 : } 236 : 237 : template <typename T> 238 75 : inline size_t matrix<T>::get_cols() const 239 : { 240 75 : return cols; 241 : } 242 : 243 : // With reference 244 : template <typename T> 245 1039 : inline T &matrix<T>::operator()(const size_t &row, const size_t &col) 246 : { 247 1039 : return elements[ (rows * (col - 1)) + (row - 1) ]; 248 : } 249 : 250 : // No reference 251 : template <typename T> 252 15 : inline T matrix<T>::operator()(const size_t &row, const size_t &col) const 253 : { 254 15 : return elements[ (rows * (col - 1)) + (row - 1) ]; 255 : } 256 : 257 : // For pretty printing 258 : template <typename T> 259 : inline void matrix<T>::set_output_width(const int &w) 260 : { 261 : output_width = w; 262 : } 263 : 264 : // === Friend functions === 265 : 266 : template <typename T> 267 1 : std::ostream &operator<<(std::ostream &out, const matrix<T> &m) 268 : { 269 : // if (m.rows == 0 and m.cols == 0) 270 : // out << "()\n"; 271 : // else 272 : // { 273 4 : for (size_t i{1}; i <= m.rows; i++) 274 : { 275 3 : out << "( "; 276 18 : for (size_t j{1}; j <= m.cols; j++) 277 15 : out << std::setw(m.output_width) << m(i, j) << ' '; 278 3 : out << ")\n"; 279 : } 280 1 : out << '\n'; 281 : // } 282 1 : return out; 283 : }