Hello,
C++20 generic class Matrix has been updated, with linear system solution.
Previous implementation details have been explained in previous posts: C++ Matrix generic class, C++ concepts and C++20 Matrix generic class updated.
Move operations [update 25 Jan 2024]
The class template has been updated (the source code is here: https://github.com/Fred68/MatrixT/) with the move constructor and move assignment operator:
Matrix(Matrix&& m) : _row{m._row}, _col{m._col}, dat{m.dat}, _iterators{0}
{
m.dat = nullptr;
m._row = m._col = 0;
m._iterators = 0;
#ifdef _DEBUG
cout << "Matrix(Matrix&& m)" << endl;
#endif
}
Matrix<DATA> &operator=(Matrix<DATA> &&m)
{
if(this != &m)
{
_col = m._col;
_row = m._row;
_iterators = m-_iterators;
dat = m.dat;
m.dat = nullptr;
m._row = m._col = 0;
m._iterators = 0;
}
#ifdef _DEBUG
cout << "Matrix::&operator=(Matrix&&)" << endl;
#endif
return *this;
}
The original object is discarded, after the move operation, with a generic shallow destructor: for this reason, the allocated memory is released and indexes zeroed.
Iterator
Generic class Matrix<T> has been enriched with an iterator.
Modern C++ iterators, allowing compact cycles like:
for(auto x : values) {}
have been put aside.
Instead, a simpler approach has been used.
#ifndef MATRIXITERATORH
#define MATRIXITERATORH
/*************************************/
/* */
/* MatrixIterator.h */
/* */
/*************************************/
#include "Matrix.h"
namespace matrix
{
/* Prototyping */
template<class DATA> class Matrix;
template<class DATA> class MatrixIterator
{
private:
Matrix<DATA> *_m;
int ir, ic;
public:
/* Ctor con argomenti */
MatrixIterator(Matrix<DATA> &m)
{
_m = &m;
ir = ic = 0;
_m->_iterators++;
};
/* Dtor con argomenti */
~MatrixIterator()
{
_m->_iterators--;
}
/* Iteration */
DATA *begin();
DATA *end();
DATA *next();
DATA *peek()
{
return _m->dat + ir * _m->_col + ic;
}
};
#include "MatrixIterator.cpp"
}
#endif
#ifndef MATRIXITERATORCPP
#define MATRIXITERATORCPP
#include "MatrixIterator.h"
using namespace matrix;
template <class DATA> DATA *MatrixIterator<DATA>::begin()
{
ir = ic = 0;
if ((_m->_row == 0) || (_m->_col == 0))
{
throw std::runtime_error(MatrixDef::ERR_ZEROSIZE);
}
return _m->dat;
}
template <class DATA> DATA *MatrixIterator<DATA>::end()
{
ir = _m->_row - 1;
ic = _m->_col - 1;
if ((ir < 0) || (ic < 0))
{
throw std::runtime_error(MatrixDef::ERR_ZEROSIZE);
}
return _m->dat[ir * _m->_col + ic];
}
template <class DATA> DATA *MatrixIterator<DATA>::next()
{
ic++;
if (ic >= _m->_col)
{
ic = 0;
ir ++;
}
if (ir >= _m->_row) // Oltre fine matrice
{
ic = ir = 0; // Azzera l'iteratore
return (DATA*) nullptr;
}
return _m->dat + ir * _m->_col + ic;
}
#endif
The usage of this iterator is:
MatrixIterator<int> it(pr1);
cout << "\npr1=\n" << pr1.to_string(MatrixDef::Cmd::detail) << endl;
for (int *x = it.begin(); x != nullptr; x = it.next())
{
cout << *x << " " << *it.peek() << '\t';
}
The generic iterator is prototyped in Matrix header and declared friend in the class declaration.
Considering a MatrixIterator<T> object contains a pointer to the Matrix<T> object on which it handles the iterations, when the Matrix<T> object is destroyed, the iterator might operate on a deallocated memory address.
For this reason, the generic Matrix class contains the number of created iterators. There is only one MatrixIterator<T> constructor, receiving the reference to a Matrix<T> object, so that no empty iterator can be normally created.
Linear systems
Linear system solver has been encapsulated into a specific class, although only necessary functions could have been added to Matrix<T> class. The reason is that the factorization, the solution and some parameters can be controlled in an easier way.
#ifndef LINEARSYSH
#define LINEARSYSH
#include "Matrix.h"
#include <string>
#include <sstream>
#ifdef _DEBUG
using std::cout;
using std::ostream;
#endif
using std::string;
using std::stringstream;
using std::endl;
namespace matrix
{
//template<class DATA> class Matrix; // Prototyping superfluo
template <class DATA, class MOD> class LinearSys
{
private:
Matrix<DATA> a; // Matrice fattorizzata
Matrix<int> pivot; // Vettore di pivot
const MOD EPS = std::numeric_limits<MOD>::epsilon();
const MOD MIN = std::numeric_limits<MOD>::min();
const MOD MAX = std::numeric_limits<MOD>::max();
MOD _epszero; // Valore minimo considerato > 0
MOD _det; // Determinante
public:
/* Ctor. Vincolo nel costruttore, non serve altrove */
LinearSys() requires RQfloatabs<DATA,MOD>
{
_epszero = EPS;
_det = (MOD) 0;
#ifdef _DEBUG
cout << "LinearSys(): epsilon=" << EPS << ", min=" << MIN << ", max=" << MAX << endl;
#endif
}
/* Soglia sotto la quale un valore si considera nullo */
void set_eps_zero(MOD eps_zero)
{
if(eps_zero > EPS)
{
_epszero = eps_zero;
}
}
void set_eps_zero_ratio(MOD eps_zero)
{
if (eps_zero >= (MOD)1)
{
_epszero = eps_zero * EPS;
}
}
MOD get_eps_zero()
{
return _epszero;
}
/* to_string() */
string to_string()
{
stringstream ss;
ss << "Eps_zero=" << _epszero << endl;
ss << "Det=" << _det << endl;
ss << "PLU=" << a.to_string() << endl;
ss << "Pivot=" << pivot.to_string() << endl;
return ss.str();
}
/* Fattorizzazione LU con pivoting parziale e soluzione. Monegato Metodi e algoritmi... CLUT 2008, pag. 41 e succ.*/
bool factor(Matrix <DATA> &A);
bool solve_check(bool throw_exception = true)
{
bool ok = true;
int n = a.rows();
if (n != a.cols())
{
ok = false;
if(throw_exception) throw std::runtime_error(MatrixDef::ERR_NOTSQUARE);
}
if (n < 1)
{
ok = false;
if (throw_exception) throw std::runtime_error(MatrixDef::ERR_ZEROSIZE);
}
if (abs(_det) < _epszero)
{
ok = false;
if (throw_exception) throw std::runtime_error(MatrixDef::ERR_SINGULAR);
}
if ((pivot.rows() != n - 1) || (pivot.cols() != 1))
{
ok = false;
if (throw_exception) throw std::runtime_error(MatrixDef::ERR_PIVOT);
}
return ok;
}
bool solve(Matrix <DATA> &x, Matrix <DATA> &b);
};
#include "LinearSys.cpp"
}
#endif
#ifndef LINEARSYSCPP
#define LINEARSYSCPP
#include "LinearSys.h"
using namespace matrix;
template <class DATA, class MOD> bool LinearSys<DATA, MOD>::factor(Matrix <DATA> &A)
{
int n;
n = A.rows();
if (n != A.cols())
{
throw std::runtime_error(MatrixDef::ERR_NOTSQUARE);
}
if (n < 1)
{
throw std::runtime_error(MatrixDef::ERR_ZEROSIZE);
}
pivot.dim(n-1, 1); // Vettore colonna n-1 elementi
a = A; // Assegnazione della classe Matrix<>
if (n == 1) // Se matrice 1x1: il determinante è il valore.
{
_det = a(0,0);
if (abs(_det) < _epszero)
{
return false;
}
return true;
}
int k, i, io, j; // Ciclo di calcolo
MOD amax;
DATA tmp;
for (_det = (DATA)1, k = 0; k < n - 1; k++) // Ciclo 1 su tutte le righe k=0...n-2
{
for (amax = (MOD)0, io = i = k; i < n; i++) // Cerca la riga (io), dalla k in poi, con il massimo amax sulla colonna k.
{
if (abs(a(i, k)) >= amax)
{
io = i;
amax = abs(a(i, k));
}
}
pivot(k,0) = io; // Imposta il pivot
if (amax < _epszero) // Se il valore massimo dei coefficienti è (quasi) zero...
{
_det = (MOD) 0; // ...azzera il determinante ed esce.
return false;
}
if (io != k) // Se l'indice non è k...
{
for (j = k; j < n; j++) // ...scambia le righe k e io (solo la parte superiore, l'inferiore è nulla)...
{
tmp = a(k, j);
a(k, j) = a(io, j);
a(io, j) = tmp;
}
_det = -_det; // ...e cambia segno al determinante.
}
for (i = k + 1; i < n; i++) // Ciclo 2, per tutte le righe successive alla riga k attuale
{
a(i, k) = -a(i, k) / a(k, k); // Coefficiente per metodo di Gauss (su riga i, colonna k), sotto diagonale.
for (j = k + 1; j < n; j++) // Combina linearmente le due righe, sopra la diagonale
{
a(i, j) = a(i, j) + a(i, k) * a(k, j);
}
} // Fine ciclo 2
_det = _det * a(k, k);
}
if(abs(a(n - 1, n - 1))<_epszero)
{
_det = (MOD) 0;
return false;
}
_det = _det * a(n - 1, n - 1);
return true;
}
template <class DATA, class MOD> bool LinearSys<DATA, MOD>::solve(Matrix <DATA> &x, Matrix <DATA> &b)
{
int n = a.rows();
solve_check();
if ((b.rows() != n) || (b.cols() != 1)) // Verifica dimensioni vettore termini noti
{
throw std::runtime_error(MatrixDef::ERR_SZMISMATCH);
}
x = b;
if (n == 1)
{
if (abs(a(0, 0)) < _epszero)
{
throw std::runtime_error(MatrixDef::ERR_SINGULAR);
}
x(0,0) = x(0,0) / a(0,0);
return true;
}
int k,j,i;
DATA tmp;
for (k = 0; k < n - 1; k++) // Ciclo su tutte le righe, tranne l'ultima
{
j = pivot(k,0); // Ottiene la riga da scambiare con le riga k
if (j != k) // Le scambia, se necessario
{
tmp = x(j,0);
x(j,0) = x(k,0);
x(k, 0) = tmp;
}
for(i=k+1; i<n; i++)
{
x(i, 0) += a(i, k) * x(k, 0);
}
}
x(n-1, 0) = x(n-1, 0) / a(n-1, n-1);
for (i = n - 2; i >= 0; i--)
{
for (tmp = (DATA)0.0, j = i + 1; j < n; j++)
tmp += a(i, j) * x(j, 0);
x(i, 0) = (x(i, 0) - tmp) / a(i, i);
}
return true;
}
#endif
Note that the:
template <class DATA, class MOD> class LinearSys {}
is a generic class with two different data types.
The first one, <DATA>, specifies the data type of the values contained in the Matrix<DATA> working with.
The second one, <MOD>, is the data type of the module (absolute value, function abs) of a <DATA> value. If data are complex<double>, you can (you should….), the absolute value type would be a <double>.
The requirement for the data type is to satisfy the previous requirements (RQfloat) and the existence of a ‘abs()’ function:
template<class T, class A> concept RQfloatabs = RQfloat<T> && RQfloat<A> && requires (T x, A a) { a = abs(x);};
The solution is made in two steps: matrix factorization with partial pivoting and solution with back substitutions.
Details and algorithms are clearly explained in Monegato numeric calculation texts.
Compiler and precision
For linear system class, precision depends on floating point data type, according to std::numeric_limits.
In particular, a small number can be considered a zero value if under a threshold, epsilon (the minimum representable difference between 1.0 and the next number), or a multiple, which can be set in a linear system class object.
In C++, a double-precision floating point number is represented, according to IEEE7454, on 64 bit (15 digits). Unfortunately, long double, while it is a different data type, is represented on the same number of bits, in Microsoft C++. Trying to obtain better precision, I compiled the C++ program with Visual Studio 2022 standard compiler and with Intel OneAPI DPC++ Compiler 2023, but not compared the results, yet.
Usage
The usage of the new classes is explained in this test program:
#include "Pippo.h"
#include "Matrix.h"
#include "MatrixIterator.h"
#include "LinearSys.h"
#include <iostream>
//#include <math.h>
//#include <complex.h>
//#include <tgmath.h>
#pragma region USING
// Namespace necessari (evitare: using namespace std;)
using std::cout;
using std::endl;
using std::getchar;
using std::operator<<;
using std::ostream;
using std::complex;
using namespace matrix;
using std::string;
#pragma endregion
// Prototyping
void stop_msg();
static int ramdomint(int i, int j);
static int ramdomint10(int i, int j);
static std::complex<double> cpxrc(int r, int c);
static float ramdomfloat10(int i, int j);
static double ramdomdouble20(int i, int j);
int main()
{
srand((unsigned)time(NULL));
cout << "\n\nMatrix test\n" << endl;
try
{
Matrix<Pippo> P1;
cout << "P1:\n" << P1.to_string() << endl;
Matrix<Pippo> *P2;
P2 = new Matrix<Pippo>(2, 3);
(*P2)(0, 1) = Pippo(1);
(*P2)(1, 2) = Pippo(-5);
Pippo xpip = (*P2)(1, 2);
cout << "xpip:\t" << xpip << endl;
(*P2)(1, 0) = Pippo(-8);
cout << "P2:\n" << P2 << "\tRighe: " << P2->rows() << "\tColonne: " << P2->cols() << endl;
cout << "P2:\n" << P2->to_string(MatrixDef::Cmd::size) << endl;
cout << "P2:\n" << P2->to_string(MatrixDef::Cmd::detail) << endl;
Matrix<Pippo> P2n, P2o;
P2o = P2n = (*P2);
Matrix<Pippo> P2t = !(*P2);
cout << "P2n:\n" << P2n.to_string() << endl;
cout << "P2o:\n" << P2n.to_string() << endl;
cout << "P2t:\n" << P2t.to_string() << endl;
cout << "!P2:\n" << (!(*P2)).to_string() << endl;
Matrix<float> AR[3] = { Matrix<float>(2,2,0.1f), Matrix<float>(2,2,0.2f), Matrix<float>(2,2,0.3f) };
for (int i = 0; i < sizeof(AR) / sizeof(AR[0]); i++)
{
cout << "AR[" << i << "]:\n" << AR[i].to_string() << endl;
}
Matrix<float> F1(2, 2, 0.5);
cout << "F1:\n" << F1.to_string() << endl;
Matrix<float> F2(F1);
cout << "F2:\n" << F2.to_string() << endl;
Matrix<Pippo> P3(*P2);
cout << "P3:\n" << P3.to_string() << endl;
P1.clear();
cout << "P1:\n" << P1.to_string() << endl;
Matrix<Pippo> P4(2, 2, Pippo(3));
cout << "P4:\n" << P4.to_string() << endl;
P4.clear();
cout << "P4:\n" << P4.to_string() << endl;
Matrix<int> J1(2, 2, 100);
cout << "J1:\n" << J1.to_string(',', ';') << endl;
J1.dim(4, 3);
Matrix<int> J2(J1);
cout << "[J2:]\t" << J2.to_string(MatrixDef::Cmd::detail) << endl;
J1(2, 1) = 3;
J1(3, 2) = -99;
J1.dim(10, 10);
cout << "J1:\n" << J1.to_string() << endl;
J1.dim(J2);
cout << "J1:\n" << J1.to_string() << endl;
J1.trim(1, 2, 1, 2);
cout << "J1:\n" << J1.to_string() << endl;
J1.clear();
J1.dim(5, 5);
int i, j;
for (i = 0; i < J1.rows(); i++)
for (j = 0; j < J1.cols(); j++)
J1(i, j) = rand() % 1000;
cout << "J1:\n" << J1.to_string() << endl;
i=4;
cout << "J1row[" << i << "]:\n" << (J1.get_row(i)).to_string() << endl;
i=3;
cout << "J1col[" << i <<"]:\n" << (J1.get_col(i)).to_string() << endl;
cout << "J1sub:\n" << (J1.get_sub(0,2,0,2)).to_string() << endl;
J1.rem_row_col(1, 3);
cout << "J1:\n" << J1.to_string() << endl;
J1.rem_row(2);
cout << "J1:\n" << J1.to_string() << endl;
J1.transpose();
cout << "J1tr:\n" << J1.to_string() << endl;
J1.rem_col(1);
cout << "J1:\n" << J1.to_string() << endl;
#if false
x = (*P2)(1, 3); // Genera eccezione out of bound
#endif
delete P2;
Matrix<int> m(3,4);
for (i = 0; i < m.rows(); i++)
for (j = 0; j < m.cols(); j++)
m(i, j) = rand() % 1000;
pair<int,int> imin = m.min_row_col();
cout << "m=\n" << m.to_string() << endl << "min= " << m.min() << " @ " << '[' << imin.first << ',' << imin.second << ']' << endl;
#if false
imin = P3.min(); // Vincolo non soddisfatto
#endif
Matrix<int> s1(3,4, &ramdomint);
Matrix<int> s2(s1.rows(), s1.cols()/* se +1: errore size mismatch*/, &ramdomint);
cout << "\ns1=\n" << s1.to_string() << endl;
cout << "\ns2=\n" << s2.to_string() << endl;
cout << "\ns1+s2=\n" << (s1 + s2).to_string() << endl;
cout << "\ns1-s2=\n" << (s1 - s2).to_string() << endl;
s1+=s2;
cout << "\ns1+=s2;\ns1=\n" << s1.to_string() << endl;
s1-=s2;
cout << "\ns1-=s2\n;s1=\n" << s1.to_string() << endl;
Matrix<int> pr1(2, 3, ramdomint10);
Matrix<int> pr2(3, 2, ramdomint10);
Matrix<int> pr3 = pr1 * pr2;
Matrix<int> pr4 = pr1 * 2;
int ii = 2;
Matrix<int> pr5 = ii * pr1;
Matrix<int> pr6 = 2 * pr1;
Matrix<long double> ld1(2,2,1.0001l);
cout << "[ld1:]\t" << ld1.to_string(MatrixDef::Cmd::detail) << endl;
Matrix<complex<double>> cp0(2,2,cpxrc);
Matrix<float> a(1,3, ramdomfloat10), bb(1,3, ramdomfloat10);
{
MatrixIterator<int> it(pr1);
cout << "\npr1=\n" << pr1.to_string(MatrixDef::Cmd::detail) << endl;
for (int *x = it.begin(); x != nullptr; x = it.next())
{
cout << *x << " " << *it.peek() << '\t';
}
}
// Float 80 bit non supportato: long double => double in MS C++
// __float128 bit forse supportato
cout << "\npr2=\n" << pr2.to_string() << endl;
cout << "\npr3=\n" << pr3.to_string() << endl;
cout << "\npr1*pr2=\n" << (pr1 * pr2).to_string() << endl;
cout << "\npr4=\n" << pr4.to_string() << endl;
cout << "\npr5=\n" << pr5.to_string() << endl;
cout << "\npr6=\n" << pr6.to_string() << endl;
cout << "\n2*pr1=\n" << (2 * pr1).to_string() << endl;
cout << "\npr1*2=\n" << (pr1 * 2).to_string() << endl;
cout << "\npr6/2=\n" << (pr6 / 2).to_string() << endl;
cout << "sizeof(char)=" << sizeof(char) << endl;
cout << "sizeof(short)=" << sizeof(short) << endl;
cout << "sizeof(int)=" << sizeof(int) << endl;
cout << "sizeof(long int)=" << sizeof(long int) << endl;
cout << "sizeof(float)=" << sizeof(float) << endl;
cout << "sizeof(double)=" << sizeof(double) << endl;
cout << "sizeof(long double)=" << sizeof(long double) << endl;
cout << "\nld1=\n" << ld1.to_string() << endl;
cout << "\ncp0=\n" << cp0.to_string() << endl;
cout << "\na=\n" << a.to_string() << endl;
cout << "\nb=\n" << bb.to_string() << endl;
cout << "\na^bb=\n" << (a^bb) << endl;
Matrix<complex<double>> idm;
idm = Matrix<complex<double>>::Id(3);
cout << "\nId\n" << idm.to_string() << endl;
Matrix<float> mset;
float arrcost[3][2] = {{1.1f,2.0f},{3.0f,4.0f},{-1.0f,-2.0f}};
mset.set(3,2, *arrcost);
cout << "\nmset.to_string:\n" << mset.to_string() << endl;
Matrix<float> mset1;
// mset1.set(2, 2,{{10,20},{-3,-4}});
mset1.set(2, 2, { 10.0f,20.0f,
-3.0f,-4.0f
});
cout << "\nmset1.to_string:\n" << mset1.to_string() << endl;
// mset1.set(2, 2, { 10,20,-3,-4,5}); // Error, sizes do not match
LinearSys<float, float> *ls1 = new LinearSys<float, float>();
LinearSys<double, double> *ls2 = new LinearSys<double, double>();
LinearSys<complex<float>, float> *ls3 = new LinearSys<complex<float>, float>();
LinearSys<complex<double>, double> *ls4 = new LinearSys<complex<double>, double>();
ls4->set_eps_zero_ratio(10);
cout << "\nls1->to_string:\n" << ls1->to_string() << endl;
cout << "\nls2->to_string:\n" << ls2->to_string() << endl;
cout << "\nls3->to_string:\n" << ls3->to_string() << endl;
cout << "\nls4->to_string:\n" << ls4->to_string() << endl;
// LinearSys<float,string> *ls2 = new LinearSys<float, string>(); // Error C7500: nessuna funzione soddisfa i vincoli
//Matrix<double> A(3,3,ramdomdouble20);
Matrix<double> A, xo, b, x;
A.set(3,3, { 1, 2, 3,
-2, 4, -7,
5, 6, 1 });
xo.set(3, 1, { 1, -5, 8});
/*
A.set(4, 4, { 0, 2, 0, -1,
2, -1, 1, -2,
1, 0, -2, 1,
-1, 3, 1, 1
});
xo.set(4, 1, {1, 0, -3, 3});
*/
b = A * xo;
cout << "\nA:\n" << A.to_string() << endl;
cout << "\nxo:\n" << xo.to_string() << endl;
cout << "\nb=A*xo:\n" << b.to_string() << endl;
ls2->factor(A);
cout << "\nls2:Factor()\n" << ls2->to_string() << endl;
/* Verifica con GNU Octave
>> A = [1 2 3; -2 4 -7; 5 6 1]
A = 1 2 3
-2 4 -7
5 6 1
>> [L,U]=lu(A)
L = 0.2000 0.1250 1.0000
-0.4000 1.0000 0
1.0000 0 0
U = 5.0000 6.0000 1.0000
0 6.4000 -6.6000
0 0 3.6250
>> bb=[5 3 10]'
bb = 5
3
10
>> x=A\bb
x = -0.1552
1.6983
0.5862
>> A*x
ans = 5
3
10
*/
if(ls2->solve_check())
{
ls2->solve(x, b);
}
else
{
cout << "\nsolve_check non superato" << endl;
}
cout << "\nx=\n" << x.to_string() << endl;
cout << "\nA*x=\n" << (A*x).to_string() << endl;
cout << "\nx-xo\n" << (x-xo).to_string() << endl;
}
catch (const std::runtime_error ex)
{
cout << ex.what();
stop_msg();
exit(1);
}
stop_msg();
}
void stop_msg()
{
cout << endl << "<enter> per chiudere" << endl;
char tempchar = getchar();
}
static int ramdomint(int i, int j)
{return 1 + rand() % 100;}
static int ramdomint10(int i, int j)
{return (rand() % 10) - (rand() % 5);}
static float ramdomfloat10(int i, int j)
{
return (float) ((rand() % 10) - (rand() % 5));
}
static double ramdomdouble20(int i, int j)
{
return (double)((rand() % 20) - (rand() % 10));
}
static std::complex<double> cpxrc(int r, int c)
{
return std::complex<double>(r*10+ramdomint10(0,0), c*10+ ramdomint10(0, 0));
}
The user class Pippo, used with Matrix<>:
#ifndef PIPPO
#define PIPPO
#define ERR_NO_ASSIGN // Prova vincolo operatore assegnazione
#undef ERR_NO_ASSIGN
#include <iostream>
using std::cout;
using std::endl;
using std::ostream;
class Pippo
{
protected:
int _p = -1;
double _p2;
int _in[3] = {1,2,3};
public:
Pippo()
{
_p = 0;
_p2 = (double)_p / 2.0;
#ifdef _DEBUG
cout << "ctor Pippo()" << endl;
#endif
}
Pippo(int x)
{
_p = x;
#ifdef _DEBUG
cout << "ctor Pippo(int x)" << endl;
#endif
}
int Get() { return _p; }
void Set(int p) { _p = p; }
friend ostream &operator<<(ostream &os, const Pippo &pp);
Pippo &operator=(const Pippo &pp)
#ifdef ERR_NO_ASSIGN
= delete;
#else
{
if (this != &pp)
{
_p = pp._p;
#ifdef _DEBUG
cout << "Pippo operator=()" << endl;
#endif
}
else
{
#ifdef _DEBUG
cout << "Pippo auto assignment in operator=()" << endl;
#endif
}
return *this;
}
#endif
};
#endif
#include "Pippo.h"
ostream& operator<<(ostream& os, const Pippo &pp)
{
os << '{' << pp._p << '}';
return os;
}
The progrm’s output:
Matrix test
P1:
[] R0 x C0
xpip: {-5}
P2:
000002AC9A782A60 Righe: 2 Colonne: 3
P2:
R2 x C3
P2:
R2xC3,iters=0,dtsz=32,empty={0}
P2n:
[{0} {1} {0}
{-8} {0} {-5}
] R2 x C3
P2o:
[{0} {1} {0}
{-8} {0} {-5}
] R2 x C3
P2t:
[{0} {-8}
{1} {0}
{0} {-5}
] R3 x C2
!P2:
[{0} {-8}
{1} {0}
{0} {-5}
] R3 x C2
AR[0]:
[0.1 0.1
0.1 0.1
] R2 x C2
AR[1]:
[0.2 0.2
0.2 0.2
] R2 x C2
AR[2]:
[0.3 0.3
0.3 0.3
] R2 x C2
F1:
[0.5 0.5
0.5 0.5
] R2 x C2
F2:
[0.5 0.5
0.5 0.5
] R2 x C2
P3:
[{0} {1} {0}
{-8} {0} {-5}
] R2 x C3
P1:
[] R0 x C0
P4:
[{3} {3}
{3} {3}
] R2 x C2
P4:
[] R0 x C0
J1:
[100,100;100,100;] R2 x C2
[J2:] R4xC3,iters=0,dtsz=4,empty=7143491
J1:
[100 100 0 0 0 0 0 0 0 0
100 100 0 0 0 0 0 0 0 0
0 3 0 0 0 0 0 0 0 0
0 0 -99 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
] R10 x C10
J1:
[100 100 0
100 100 0
0 3 0
0 0 -99
] R4 x C3
J1:
[100 0
3 0
] R2 x C2
J1:
[329 455 353 584 158
121 121 237 749 390
486 461 637 758 829
852 402 479 98 960
30 36 578 711 852
] R5 x C5
J1row[4]:
[30 36 578 711 852
] R1 x C5
J1col[3]:
[584
749
758
98
711
] R5 x C1
J1sub:
[] R0 x C0
J1:
[329 455 353 158
486 461 637 829
852 402 479 960
30 36 578 852
] R4 x C4
J1:
[329 455 353 158
486 461 637 829
30 36 578 852
] R3 x C4
J1tr:
[329 486 30
455 461 36
353 637 578
158 829 852
] R4 x C3
J1:
[329 30
455 36
353 578
158 852
] R4 x C2
m=
[817 507 359 382
998 278 31 701
571 620 596 482
] R3 x C4
min= 31 @ [1,2]
s1=
[96 40 31 80
61 75 38 65
99 17 94 87
] R3 x C4
s2=
[10 49 39 84
89 49 61 8
36 85 10 40
] R3 x C4
s1+s2=
[106 89 70 164
150 124 99 73
135 102 104 127
] R3 x C4
s1-s2=
[86 -9 -8 -4
-28 26 -23 57
63 -68 84 47
] R3 x C4
s1+=s2;
s1=
[106 89 70 164
150 124 99 73
135 102 104 127
] R3 x C4
s1-=s2
;s1=
[96 40 31 80
61 75 38 65
99 17 94 87
] R3 x C4
[ld1:] R2xC2,iters=0,dtsz=8,empty=6.23035e-307
pr1=
R2xC3,iters=1,dtsz=4,empty=7143491
2 2 -1 -1 -2 -2 9 9 0 0 1 1
pr2=
[2 4
1 4
-1 0
] R3 x C2
pr3=
[5 4
17 36
] R2 x C2
pr1*pr2=
[5 4
17 36
] R2 x C2
pr4=
[4 -2 -4
18 0 2
] R2 x C3
pr5=
[4 -2 -4
18 0 2
] R2 x C3
pr6=
[4 -2 -4
18 0 2
] R2 x C3
2*pr1=
[4 -2 -4
18 0 2
] R2 x C3
pr1*2=
[4 -2 -4
18 0 2
] R2 x C3
pr6/2=
[2 -1 -2
9 0 1
] R2 x C3
sizeof(char)=1
sizeof(short)=2
sizeof(int)=4
sizeof(long int)=4
sizeof(float)=4
sizeof(double)=8
sizeof(long double)=8
ld1=
[1.0001 1.0001
1.0001 1.0001
] R2 x C2
cp0=
[(-2,3) (0,15)
(7,4) (10,15)
] R2 x C2
a=
[5 -2 6
] R1 x C3
b=
[2 7 -1
] R1 x C3
a^bb=
-10
Id
[(1,0) (0,0) (0,0)
(0,0) (1,0) (0,0)
(0,0) (0,0) (1,0)
] R3 x C3
mset.to_string:
[1.1 2
3 4
-1 -2
] R3 x C2
mset1.to_string:
[10 20
-3 -4
] R2 x C2
ls1->to_string:
Eps_zero=1.19209e-07
Det=0
PLU=[] R0 x C0
Pivot=[] R0 x C0
ls2->to_string:
Eps_zero=2.22045e-16
Det=0
PLU=[] R0 x C0
Pivot=[] R0 x C0
ls3->to_string:
Eps_zero=1.19209e-07
Det=0
PLU=[] R0 x C0
Pivot=[] R0 x C0
ls4->to_string:
Eps_zero=2.22045e-15
Det=0
PLU=[] R0 x C0
Pivot=[] R0 x C0
A:
[1 2 3
-2 4 -7
5 6 1
] R3 x C3
xo:
[1
-5
8
] R3 x C1
b=A*xo:
[15
-78
-17
] R3 x C1
ls2:Factor()
Eps_zero=2.22045e-16
Det=-116
PLU=[5 6 1
0.4 6.4 -6.6
-0.2 -0.125 3.625
] R3 x C3
Pivot=[2
1
] R2 x C1
x=
[1
-5
8
] R3 x C1
A*x=
[15
-78
-17
] R3 x C1
x-xo
[0
0
0
] R3 x C1
<enter> per chiudere
Git hub
The complete source has been made available as a public repository on Github,
at https://github.com/Fred68/Matrix.
The source code has been put into a single header file.
See project https://github.com/Fred68/MatrixT/
Best regards !
Update (January 2024)
Hello,
files have been updated !
Everything has been put inside a unique header file. Static constant definitions have been moved inside the class definition:
class MatrixDef
{
public:
enum class Cmd { size, detail };
inline static const string ERR_ALLOC = "Allocation failed";
inline static const string ERR_OUTOFBOUND = "Subscript out of bounds";
inline static const string ERR_WRONGPARAM = "Wrong parameter";
inline static const string ERR_SZMISMATCH = "Sizes do not match";
inline static const string ERR_NOTVECTOR = "Not a vector";
inline static const string ERR_ZEROSIZE = "Zero size";
inline static const string ERR_NOTSQUARE = "Not square";
inline static const string ERR_SINGULAR = "Singular";
inline static const string ERR_PIVOT = "Wrong pivot";
};
Some constant definitions:
const MOD EPS = std::numeric_limits<MOD>::epsilon();
const MOD MINV = std::numeric_limits<MOD>::min();
have been removed from the template, because min() didn’t compile, probably due to some ambiguity.
This has been solved by removing the constant (re)definition and using the original std:numeric_limits<…> where needed, letting the correct datatype be checked by the ‘requires‘ statement.
See the ctor, as an example:
LinearSys() requires RQfloatabs<DATA, MOD>
{
_epszero = std::numeric_limits<MOD>::epsilon();
_det = (MOD)0;
#ifdef _DEBUG
cout << "LinearSys(): epsilon=" << _epszero << endl;
#endif
}
The updated source code is here: https://github.com/Fred68/MatrixT/
Best regards !