Page MenuHomec4science

Matrix2D.hpp
No OneTemporary

File Metadata

Created
Sat, Nov 16, 05:49

Matrix2D.hpp

#ifndef MATRIX2D_HPP
#define MATRIX2D_HPP
#include <Matrix.hpp>
#include <vector>
#include <string>
#include <utility> // std::move()
#include <fstream> // ifstream
#include <iostream>
#include <iomanip> // setw(), setprecision(), fixed
#include <sstream> // istringstream
#include <stdexcept> // runtime_error, out_of_range
#define BUFFER_SIZE 4096
/*! The Matrix2D class is a specialisation of the Matrix
* class to make work with 2D matrices easier.
*
*
* A format to save a Matrix2D objects in a binary file is defined. The following values
* are written :
* 1x size_t : the number <N> of dimensions of the Matrix stored. This is the value
* of _dim_size field. This value must be 2 otherwise this is not a 2D
* matrix.
* 2x size_t : the width of the matrix in each dimension. These values correspond
* to the content of the _dim vector and can be loaded inside this
* vector as they are.
* Dx <T> values : the <D> values contained in the matrix, in the _data vector.
* These values can be loaded directly in this vector. <D> is equal to the
* product of the 2 values stored right before. The type <T> depends on
* the type of the data stored in the matrix. The 1st of the D values is
* also the first value of the _data vector.
*
*
* A text format is defined to store such matrices.
* In this format, each row is written on a single line
* and the values should separated by any blank character
* (tab, space, multiple spaces, ...). Empty lines are
* not allowed.
*
* ---- start ----
* 1 2 3
* 4 5 6
* 7 8 9
* ----- end -----
*
* Constructing a matrix from an empty file (0 bytes or only an EOL char) returns a null
* matrix (0x0 dimensions). Writting a null matrix (that is with at least one null
* dimension creates an empty file.
*
*/
template<class T>
class Matrix2D : public Matrix<T>
{
public:
// constructors
Matrix2D() = default ;
/*!
* \brief Constructs a matrix with the given dimensions,
* filled with 0 values.
* \param nrow the number of rows.
* \param ncol the number of columns.
*/
Matrix2D(size_t nrow, size_t ncol) ;
/*!
* \brief Constructs a matrix with the given dimensions and
* initialize the values to the given value.
* \param nrow the number of rows.
* \param ncol the number of columns.
* \param value the value to initialize the matrix content
* with.
*/
Matrix2D(size_t nrow, size_t ncol, T value) ;
/*!
* \brief Copy constructor
* \param other the matrix to copy the values from.
*/
Matrix2D(const Matrix2D& other) ;
/*!
* \brief Move constructor
* \param other the matrix to use the values from.
*/
Matrix2D(Matrix2D&& other) ;
/*!
* \brief Constructs a matrix from a text file. A matrix contructed
* from an empty file (or a file containing only one EOL char) returns
* an empty matrix (null dimensions).
* \param file_address the address of the file containing the matrix.
* \throw std::runtime_error if anything happen while reading the
* file (format error, file not found, etc).
*/
Matrix2D(const std::string& file_address) ;
/*!
* \brief Destructor.
*/
virtual ~Matrix2D() ;
// methods overloaded in Matrix
using Matrix<T>::get ;
using Matrix<T>::set ;
// methods
/*!
* \brief loads a binary file containing
* a matrix.
* \param path the path to the file.
*/
void load(const std::string& file_address) ;
/*!
* \brief Gets the element at the given coordinates.
* \param row the row number of the element to set.
* \param col the column number of the element to set.
* \throw std::out_of_range exception if the coordinates
* are out of range.
* \return the element.
*/
T get(size_t row, size_t col) const ;
/*!
* \brief Sets the element at the given coordinates
* to the given value.
* \param row the row number of the element to set.
* \param col the column number of the element to set.
* \param value the new value.
* \throw std::out_of_range exception if the coordinates
* are out of range.
*/
void set(size_t row, size_t col, T value) ;
/*!
* \brief Gets the number of rows.
* \return the number of rows.
*/
size_t get_nrow() const ;
/*!
* \brief Gets the number of columns.
* \return the number of columns.
*/
size_t get_ncol() const ;
/*!
* \brief Gets the values in the i-th row.
* \param i the row of interest.
* \throw std::out_of_range if i is out of range.
* \return the values in this row.
*/
std::vector<T> get_row(size_t i) const ;
/*!
* \brief Gets the values in the i-th column.
* \param i the column of interest.
* \throw std::out_of_range if i is out of range.
* \return the values in this column.
*/
std::vector<T> get_col(size_t i) const ;
/*!
* \brief Sets the values of a given rows with the values of a given
* vector.
* \param i the row of interest.
* \param values the new values.
* \throw std::out_of_range if i is out of range.
* \throw std::invalid_argument if values does not have a length equal
* to the number of columns of the matrix.
*/
void set_row(size_t i, const std::vector<T>& values) ;
/*!
* \brief Sets the values of a given column with the values of a given
* vector.
* \param i the column of interest.
* \param values the new values.
* \throw std::out_of_range if i is out of range.
* \throw std::invalid_argument if values does not have a length equal
* to the number of rows of the matrix.
*/
void set_col(size_t i, const std::vector<T>& values) ;
/*!
* \brief Produces a nice representation of the matrix on the given
* stream.
* \param stream the stream.
* \param precision the rounding precision.
* \param width the column width in number of characters.
* \param sep the character separator.
*/
virtual void print(std::ostream& stram, size_t precision=4, size_t width=8, char sep=' ') const override ;
// operators
/*!
* Assignment operator.
* \param other an other matrix to copy the values from.
* \return a reference to the current the instance.
*/
Matrix2D<T>& operator = (const Matrix2D<T>& other) ;
/*!
* Move Assignment operator.
* \param other an other matrix to use the values from.
* \return a reference to the instance.
*/
Matrix2D<T>& operator = (Matrix2D<T>&& other) ;
/*!
* \brief Returns a reference to the corrresponding
* element. This method does not perform any check on
* the coordinates.
* \param row the row number of the element to set.
* \param col the column number of the element to set.
* \return a reference to this element.
*/
T& operator () (size_t row, size_t col) ;
/*!
* \brief Returns a const reference to the corrresponding
* element. This method does not perform any check on
* the coordinates.
* \param row the row number of the element to set.
* \param col the column number of the element to set.
* \return a const reference to this element.
*/
const T& operator () (size_t row, size_t col) const ;
private:
/*!
* \brief Converts a pair of VALID (x,y) coordinates to a
* the corresponding offset allowing to get an element in the
* data vector.
* \param row the row index.
* \param col the column index.
* \return the corresponding offset.
*/
size_t convert_to_offset(size_t row, size_t col) const ;
/*!
* \brief Computes and stores the offsets at which
* each row start.
*/
void compute_row_offsets() ;
/*!
* \brief Computes and stores the offsets at which
* each row start.
*/
void compute_col_offsets() ;
/*!
* \brief Contains the offsets at which each row starts.
* Each element corresponds to the corresponding rows
* (1st element -> 1st row).
*/
std::vector<size_t> _row_offsets ;
/*!
* \brief Contains the offsets at which each row starts.
* Each element corresponds to the corresponding rows
* (1st element -> 1st row).
*/
std::vector<size_t> _col_offsets ;
} ;
// operators
/*!
* \brief Addition operator.
* \param m the matrix of interest
* \param value the value to add to each element.
* \return the resulting matrix.
*/
template<class T>
const Matrix2D<T> operator + (Matrix2D<T> m, T value)
{ Matrix2D<T> other(m) ;
m += value ;
return m ;
}
/*!
* \brief Substraction operator
* \param m the matrix of interest.
* \param value the value to substract to each element.
* \return the resulting matrix.
*/
template<class T>
const Matrix2D<T> operator - (Matrix2D<T> m, T value)
{ Matrix2D<T> other(m) ;
m -= value ;
return m ;
}
/*!
* \brief Multiplication operator.
* \param m the matrix of interest.
* \param value the value to multiply each elements by.
* \return the resulting matrix.
*/
template<class T>
const Matrix2D<T> operator * (Matrix2D<T> m, T value)
{ Matrix2D<T> other(m) ;
m *= value ;
return m ;
}
/*!
* \brief Division operator.
* \param m the matrix of interest.
* \param value the value to divide each elements by.
* \throw std::invalid_argument if value is 0.
* \return the resulting matrix.
*/
template<class T>
const Matrix2D<T> operator / (Matrix2D<T> m, T value)
{ if(value == static_cast<T>(0))
{ throw std::invalid_argument("division by 0!") ; }
Matrix2D<T> other(m) ;
other /= value ;
return other ;
}
/*!
* \brief Sends a representation of the matrix to the stream.
* \param stream the stream of interest.
* \param m the matrix of interest.
* \return a reference to the stream.
*/
template<class T>
std::ostream& operator << (std::ostream& stream, const Matrix2D<T>& m)
{ m.print(stream) ;
return stream ;
}
// other usefull functions
/*!
* \brief Produces a transpose of the given matrix.
* \param m a matrix.
*/
template<class T>
Matrix2D<T> transpose(const Matrix2D<T>& m) ;
// method implementation
template<class T>
Matrix2D<T> transpose(const Matrix2D<T>& m)
{ std::vector<size_t> dim = m.get_dim() ;
size_t nrow = dim[0] ;
size_t ncol = dim[1] ;
Matrix2D<T> m2(ncol, nrow, 0) ;
for(size_t i=0; i<ncol; i++)
{ for(size_t j=0; j<nrow; j++)
{ m2(i,j) = m(j,i) ; }
}
return m2 ;
}
template<class T>
Matrix2D<T>::Matrix2D(size_t nrow, size_t ncol)
: Matrix2D<T>(nrow, ncol, static_cast<T>(0))
{}
template<class T>
Matrix2D<T>::Matrix2D(size_t nrow, size_t ncol, T value)
: Matrix<T>({nrow, ncol}, value),
_row_offsets(nrow),
_col_offsets(ncol)
{ this->compute_row_offsets() ;
this->compute_col_offsets() ;
}
template<class T>
Matrix2D<T>::Matrix2D(const Matrix2D<T>& other)
: Matrix<T>(other)
{ this->_row_offsets = other._row_offsets ;
this->_col_offsets = other._col_offsets ;
}
template<class T>
Matrix2D<T>::Matrix2D(Matrix2D&& other)
: Matrix<T>(std::move(other))
{ this->_row_offsets = other._row_offsets ;
this->_col_offsets = other._col_offsets ;
}
template<class T>
Matrix2D<T>::Matrix2D(const std::string &file_address)
{
this->_dim = {0,0} ;
this->_data = new std::vector<T>() ;
this->_dim_size = this->_dim.size() ;
this->_data_size = this->_data->size() ;
this->_dim_prod = std::vector<size_t>(this->_dim_size, 0) ;
std::ifstream file(file_address, std::ifstream::in) ;
if(file.fail())
{ char msg[BUFFER_SIZE] ;
sprintf(msg, "error! cannot open %s", file_address.c_str()) ;
throw std::runtime_error(msg) ;
}
std::string buffer_str ;
std::vector<T> buffer_vec ;
T buffer_T ;
// read file
size_t n_line = 0 ;
size_t row_len = 0 ;
while(getline(file, buffer_str))
{ // check stream status and read content
if(file.fail())
{ file.close() ;
char msg[BUFFER_SIZE] ;
sprintf(msg, "error! while reading %s", file_address.c_str()) ;
throw std::runtime_error(msg) ;
}
if(buffer_str.size() == 0)
{ // this file only contains one eol char and should be considered as empty,
// -> returns empty matrix not an error
if(n_line == 0 and file.peek() == EOF and file.eof())
{ break ; }
file.close() ;
char msg[BUFFER_SIZE] ;
sprintf(msg, "format error! while reading %s (empty line)", file_address.c_str()) ;
throw std::runtime_error(msg) ;
}
// parse line
buffer_vec.clear() ;
std::istringstream buffer_ss(buffer_str) ;
while(buffer_ss >> buffer_T)
{ buffer_vec.push_back(buffer_T) ; }
// check for an error which likely indicates that a value could not be
// casted into a type T (mixed data types in the file)
if(buffer_ss.fail() and not buffer_ss.eof())
{ file.close() ;
char msg[BUFFER_SIZE] ;
sprintf(msg, "format error! could not read a line in %s (incompatible data types)", file_address.c_str()) ;
throw std::runtime_error(msg) ;
}
// check that number of column is constant
if(n_line == 0)
{ row_len = buffer_vec.size() ; }
else if(buffer_vec.size() != row_len)
{ file.close() ;
char msg[BUFFER_SIZE] ;
sprintf(msg, "format error! variable number of columns in %s", file_address.c_str()) ;
throw std::runtime_error(msg) ;
}
// update matrix content
for(auto i : buffer_vec)
{ this->_data->push_back(i) ;
this->_data_size++ ;
}
this->_dim[1]++ ;
n_line++ ;
}
file.close() ;
this->_dim[0] = row_len ;
this->compute_dim_product() ;
this->_row_offsets = std::vector<size_t>(this->_dim[1]) ;
this->_col_offsets = std::vector<size_t>(this->_dim[0]) ;
this->compute_row_offsets() ;
this->compute_col_offsets() ;
}
template<class T>
Matrix2D<T>::~Matrix2D()
{ if(this->_data != nullptr)
{ delete this->_data ;
this->_data = nullptr ;
}
}
template<class T>
void Matrix2D<T>::load(const std::string& file_address)
{
Matrix<T>::load(file_address, 2) ;
this->_row_offsets = std::vector<size_t>(this->_dim[1]) ;
this->_col_offsets = std::vector<size_t>(this->_dim[0]) ;
this->compute_dim_product() ;
this->compute_row_offsets() ;
this->compute_col_offsets() ;
}
template<class T>
T Matrix2D<T>::get(size_t row, size_t col) const
{ try
{ return this->get({row, col}) ; }
catch(std::out_of_range& e)
{ throw e ; }
}
template<class T>
void Matrix2D<T>::set(size_t row, size_t col, T value)
{ try
{ this->set({row, col}, value) ; }
catch(std::out_of_range& e)
{ throw e ; }
}
template<class T>
size_t Matrix2D<T>::get_nrow() const
{ return this->_dim[1] ; }
template<class T>
size_t Matrix2D<T>::get_ncol() const
{ return this->_dim[0] ; }
template<class T>
std::vector<T> Matrix2D<T>::get_row(size_t i) const
{ if(i>=this->get_nrow())
{ throw std::out_of_range("row index is out of range!") ; }
std::vector<T> row(this->get_ncol()) ;
for(size_t j=i*this->get_ncol(), n=0; n<this->get_ncol(); j++, n++)
{ row[n] = (*this->_data)[j] ; }
return row ;
}
template<class T>
std::vector<T> Matrix2D<T>::get_col(size_t i) const
{ if(i>=this->get_ncol())
{ throw std::out_of_range("column index is out of range!") ; }
std::vector<T> col(this->get_nrow()) ;
for(size_t j=i, n=0; n<this->get_nrow(); j+=this->get_ncol(), n++)
{ col[n] = (*this->_data)[j] ; }
return col ;
}
template<class T>
void Matrix2D<T>::set_row(size_t i, const std::vector<T>& values)
{ if(i>=this->get_nrow())
{ throw std::out_of_range("row index is out of range!") ; }
else if(values.size() != this->get_ncol())
{ throw std::invalid_argument("the given vector length is not equal to the number of columns!") ; }
for(size_t j=i*this->get_ncol(), n=0; n<this->get_ncol(); j++, n++)
{ (*this->_data)[j] = values[n] ; }
}
template<class T>
void Matrix2D<T>::set_col(size_t i, const std::vector<T>& values)
{ if(i>=this->get_ncol())
{ throw std::out_of_range("row index is out of range!") ; }
else if(values.size() != this->get_nrow())
{ throw std::invalid_argument("the given vector length is not equal to the number of rows!") ; }
for(size_t n=0, j=i; n<this->get_nrow(); n++, j+=this->get_ncol())
{ (*this->_data)[j] = values[n] ; }
}
template<class T>
void Matrix2D<T>::print(std::ostream& stream, size_t precision, size_t width, char sep) const
{ stream.setf(std::ios::left) ;
stream << std::setprecision(precision) << std::fixed ;
size_t n = 0 ;
size_t n_tot = this->get_nrow()*this->get_ncol() ;
for(size_t i=0; i<this->get_nrow(); i++)
{ for(size_t j=0; j<this->get_ncol(); j++, n++)
{ stream << std::setw(width) << (*this)(i,j) << sep ; }
if(n<n_tot)
{ stream << std::endl ; }
}
}
template<class T>
Matrix2D<T>& Matrix2D<T>::operator = (const Matrix2D<T>& other)
{ /*
this->_dim = other._dim ;
this->_dim_size = other._dim_size ;
this->_data = new std::vector<T>(other._data) ;
this->_data_size = other._data_size ;
this->_dim_prod = other._dim_prod ;
*/
Matrix<T>::operator=(other) ;
this->_row_offsets = other._row_offsets ;
this->_col_offsets = other._col_offsets ;
return *this ;
}
template<class T>
Matrix2D<T>& Matrix2D<T>::operator = (Matrix2D<T>&& other)
{ Matrix<T>::operator=(std::move(other)) ;
this->_row_offsets = other._row_offsets ;
this->_col_offsets = other._col_offsets ;
return *this ;
}
template<class T>
T& Matrix2D<T>::operator () (size_t row, size_t col)
{ return (*this->_data)[this->convert_to_offset(row, col)] ; }
template<class T>
const T& Matrix2D<T>::operator () (size_t row, size_t col) const
{ return (*this->_data)[this->convert_to_offset(row, col)] ; }
template<class T>
void Matrix2D<T>::compute_row_offsets()
{ for(size_t i=0; i<this->_dim[1]; i++)
{ this->_row_offsets[i] = i * this->_dim_prod[1] ; }
}
template<class T>
void Matrix2D<T>::compute_col_offsets()
{ for(size_t i=0; i<this->_dim[0]; i++)
{ this->_col_offsets[i] = i * this->_dim_prod[0] ; }
}
template<class T>
size_t Matrix2D<T>::convert_to_offset(size_t row, size_t col) const
{
size_t offset = this->_row_offsets[row] + this->_col_offsets[col] ;
return offset ;
}
#endif // MATRIX2D_HPP

Event Timeline