#include "Matrix.h" #if !defined(NO_STD) #include #include #endif namespace LinearAlgebra { #pragma region Matrix1Of template Matrix1Of::Matrix1Of() {} template Matrix1Of::Matrix1Of(int size) : size(size), externalData(false) { if (this->size <= 0) data = nullptr; else this->data = new T[size](); } template Matrix1Of::Matrix1Of(int size, T* data) : data(data), size(size) { this->externalData = true; } template Matrix1Of::Matrix1Of(const Matrix1Of& m) : size(m.size) { if (this->size <= 0) this->data = nullptr; else { this->data = new T[this->size]; for (unsigned int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } template Matrix1Of::Matrix1Of(Matrix1Of&& m) noexcept : data(m.data), size(m.size) { m.data = nullptr; // Leave the moved-from object in a safe state m.size = 0; } template Matrix1Of::~Matrix1Of() { if (!this->externalData) { if (this->size > 0) delete[] data; } } template Matrix1Of& Matrix1Of::operator=(const Matrix1Of& m) { if (this != &m) { if (this->size > 0) delete[] this->data; // Free the current memory this->size = m.size; this->externalData = m.externalData; if (this->size == 0) this->data = nullptr; else { this->data = new T[this->size]; for (unsigned int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } return *this; } template Matrix1Of& Matrix1Of::operator=(Matrix1Of&& m) noexcept { if (this != &m) { if (this->size > 0) { // printf("Matrix1 Move Assignment Deallocate %d\n", this->size); delete[] this->data; // Free the current memory } // Transfer ownership of resources this->size = m.size; this->data = m.data; this->externalData = m.externalData; // Leave the moved-from object in a valid state m.size = 0; m.data = nullptr; } return *this; } template unsigned int Matrix1Of::Size() const { return this->size; } template void Matrix1Of::Resize(int size) { if (!this->externalData && this->size > 0) delete[] this->data; this->externalData = false; if (size <= 0) { this->data = nullptr; this->size = 0; } else { this->data = new T[size](); } } template Matrix1Of Matrix1Of::Zero(unsigned int size) { Matrix1Of r(size); r.Fill(0); return r; } template void Matrix1Of::Fill(T value) { for (unsigned int ix = 0; ix < this->size; ix++) this->data[ix] = value; } template void Matrix1Of::Fill(T value, unsigned int start, unsigned int stop) { if (start > stop || start > this->size) return; if (stop > this->size) stop = this->size; for (unsigned int ix = start; ix < stop; ix++) this->data[ix] = value; } template Matrix1Of Matrix1Of::FromVector3(Vector3Of v) { Matrix1Of r(3); r.data[0] = v.horizontal; r.data[1] = v.vertical; r.data[2] = v.depth; return r; } template Vector3Of Matrix1Of::ToVector3() { return Vector3Of(this->data[0], this->data[1], this->data[2]); } template Matrix1Of Matrix1Of::FromQuaternion(Quaternion q) { Matrix1Of r(4); r(0) = q.x; r(1) = q.y; r(2) = q.z; r(3) = q.w; return r; } template Quaternion Matrix1Of::ToQuaternion() { float x = static_cast(data[0]); float y = static_cast(data[1]); float z = static_cast(data[2]); float w = static_cast(data[3]); return Quaternion(x, y, z, w); } template Matrix1Of Matrix1Of::Slice(unsigned int start, unsigned int stop) { if (start < 0 || stop >= this->size) std::cerr << "Slice index out of range." << std::endl; Matrix1Of result(stop - start); int resultIx = 0; for (unsigned int ix = start; ix < stop; ix++) result.data[resultIx++] = this->data[ix]; return result; } template void Matrix1Of::UpdateSlice(unsigned int start, unsigned int stop, const Matrix1Of& source) const { if (start > stop || start > this->size || start > source.size) return; if (stop > this->size) stop = this->size; if (stop > source.size) stop = source.size; unsigned int sourceIx = 0; for (unsigned int ix = start; ix < stop; ix++, sourceIx++) { this->data[ix] = source.data[sourceIx]; } } template const T& Matrix1Of::operator()(int ix) const { return data[ix]; } template T& Matrix1Of::operator()(int ix) { return data[ix]; } template class Matrix1Of; template class Matrix1Of; template class Matrix1Of; #pragma endregion Matrix1Of /* #pragma region Matrix1 Matrix1::Matrix1() {} Matrix1::Matrix1(int size) : size(size), externalData(false) { if (this->size <= 0) data = nullptr; else this->data = new float[size](); } Matrix1::Matrix1(int size, float* data) : data(data), size(size) { this->externalData = true; } Matrix1::Matrix1(const Matrix1& m) : size(m.size) { if (this->size <= 0) this->data = nullptr; else { this->data = new float[this->size]; for (int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } Matrix1::Matrix1(Matrix1&& m) noexcept : data(m.data), size(m.size) { m.data = nullptr; // Leave the moved-from object in a safe state m.size = 0; } Matrix1::~Matrix1() { if (!this->externalData) { if (this->size > 0) delete[] data; } } Matrix1& Matrix1::operator=(const Matrix1& m) { if (this != &m) { if (this->size > 0) delete[] this->data; // Free the current memory this->size = m.size; this->externalData = m.externalData; if (this->size == 0) this->data = nullptr; else { this->data = new float[this->size]; for (int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } return *this; } Matrix1& Matrix1::operator=(Matrix1&& m) noexcept { if (this != &m) { if (this->size > 0) { // printf("Matrix1 Move Assignment Deallocate %d\n", this->size); delete[] this->data; // Free the current memory } // Transfer ownership of resources this->size = m.size; this->data = m.data; this->externalData = m.externalData; // Leave the moved-from object in a valid state m.size = 0; m.data = nullptr; } return *this; } unsigned int Matrix1::Size() const { return this->size; } void Matrix1::Resize(int size) { if (!this->externalData && this->size > 0) delete[] this->data; this->externalData = false; if (size <= 0) { this->data = nullptr; this->size = 0; } else { this->data = new float[size](); } } Matrix1 Matrix1::FromVector3(Vector3Float v) { Matrix1 r(3); r.data[0] = v.horizontal; r.data[1] = v.vertical; r.data[2] = v.depth; return r; } Matrix1 Matrix1::Zero(unsigned int size) { Matrix1 r(size); r.Fill(0); return r; } void Matrix1::Fill(float value) { for (int ix = 0; ix < this->size; ix++) this->data[ix] = value; } void Matrix1::Fill(float value, unsigned int start, unsigned int stop) { if (start > stop || start > this->size) return; if (stop > this->size) stop = this->size; for (unsigned int ix = start; ix < stop; ix++) this->data[ix] = value; } Vector3Float Matrix1::ToVector3() { return Vector3Float(this->data[0], this->data[1], this->data[2]); } Matrix1 LinearAlgebra::Matrix1::FromQuaternion(Quaternion q) { Matrix1 r = Matrix1(4); float* data = r.data; data[0] = q.x; data[1] = q.y; data[2] = q.z; data[3] = q.w; return r; } Quaternion LinearAlgebra::Matrix1::ToQuaternion() { return Quaternion(this->data[0], this->data[1], this->data[2], this->data[3]); } Matrix1 Matrix1::Slice(int start, int stop) { if (start < 0 || stop >= this->size) std::cerr << "Slice index out of range." << std::endl; Matrix1 result(stop - start); int resultIx = 0; for (int ix = start; ix < stop; ix++) result.data[resultIx++] = this->data[ix]; return result; } void Matrix1::UpdateSlice(int start, int stop, const Matrix1& source) const { if (start > stop || start > this->size || start > source.size) return; if (stop > this->size) stop = this->size; if (stop > source.size) stop = source.size; unsigned int sourceIx = 0; for (int ix = start; ix < stop; ix++, sourceIx++) { this->data[ix] = source.data[sourceIx]; } } const float& Matrix1::operator()(int ix) const { return data[ix]; } float& Matrix1::operator()(int ix) { return data[ix]; } // Matrix1 #pragma endregion */ /* #pragma region Matrix2 Matrix2::Matrix2() {} Matrix2::Matrix2(int nRows, int nCols) : nRows(nRows), nCols(nCols), externalData(false) { this->size = nRows * nCols; if (this->size <= 0) { this->data = nullptr; return; } // printf("Matrix2 Allocate %d\n", this->size * sizeof(float)); this->data = new float[this->size]; } Matrix2::Matrix2(int nRows, int nCols, float* data) : nRows(nRows), nCols(nCols), data(data) { // printf("Matrix2 No-Allocate %d\n", this->size * sizeof(float)); this->size = nRows * nCols; this->externalData = true; } Matrix2::Matrix2(const Matrix2& m) : nRows(m.nRows), nCols(m.nCols), size(m.size) { // printf("Matrix2 Copy Allocate %d\n", this->size * sizeof(float)); if (this->size == 0) this->data = nullptr; else { this->data = new float[this->size]; for (int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } Matrix2::Matrix2(Matrix2&& m) noexcept : nRows(m.nRows), nCols(m.nCols), size(m.size), data(m.data) { m.nRows = 0; m.nCols = 0; m.size = 0; m.data = nullptr; // Leave the moved-from object in a safe state } Matrix2& Matrix2::operator=(const Matrix2& m) { if (this != &m) { if (this->size > 0) { // printf("Matrix2 Copy Assignment Deallocate %d\n", this->size * sizeof(float)); delete[] this->data; // Free the current memory } this->nRows = m.nRows; this->nCols = m.nCols; this->size = m.size; this->externalData = m.externalData; if (this->size == 0) this->data = nullptr; else { this->data = new float[this->size]; for (int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } return *this; } Matrix2& Matrix2::operator=(Matrix2&& m) noexcept { if (this != &m) { if (this->size > 0) { // printf("Matrix2 Move Assignment Deallocate %d\n", this->size * sizeof(float)); delete[] this->data; // Free the current memory } // Transfer ownership of resources this->nRows = m.nRows; this->nCols = m.nCols; this->size = m.size; this->externalData = m.externalData; this->data = m.data; // Leave the moved-from object in a valid state m.size = 0; m.data = nullptr; } return *this; } Matrix2::~Matrix2() { if (!this->externalData) { if (this->size > 0) { // printf("Matrix2 Deallocate %d\n", this->size * sizeof(float)); delete[] data; } } } unsigned int Matrix2::NRows() const { return this->nRows; } unsigned int Matrix2::NCols() const { return this->nCols; } Matrix2 Matrix2::Zero(int nRows, int nCols) { Matrix2 r = Matrix2(nRows, nCols); for (int ix = 0; ix < r.size; ix++) r.data[ix] = 0; return r; } const float& Matrix2::operator()(int row, int col) const { return data[row * this->nCols + col]; } float& Matrix2::operator()(int row, int col) { return data[row * this->nCols + col]; } void Matrix2::Fill(float value) { for (int ix = 0; ix < this->size; ix++) this->data[ix] = value; } void Matrix2::FillDiagonal(float f) { int n = this->nRows < this->nCols ? nRows : nCols; for (int ix = 0; ix < n; ix++) this->data[ix * this->nCols + ix] = f; } Matrix2 Matrix2::Identity(int size) { return Diagonal(1, size); } Matrix2 Matrix2::Identity(int nRows, int nCols) { Matrix2 m = Matrix2::Zero(nRows, nCols); m.FillDiagonal(1); return m; } Matrix2 Matrix2::Diagonal(float f, int size) { Matrix2 r = Matrix2::Zero(size, size); float* data = r.data; int valueIx = 0; for (int ix = 0; ix < size; ix++) { data[valueIx] = f; valueIx += size + 1; } return r; } Matrix2 Matrix2::SkewMatrix(const Vector3Of& v) { Matrix2 r = Matrix2(3, 3); float* data = r.data; data[0 * 3 + 0] = 0; data[0 * 3 + 1] = -v.depth; // result(0, 1) data[0 * 3 + 2] = v.vertical; // result(0, 2) data[1 * 3 + 0] = v.depth; // result(1, 0) data[1 * 3 + 1] = 0; data[1 * 3 + 2] = -v.horizontal; // result(1, 2) data[2 * 3 + 0] = -v.vertical; // result(2, 0) data[2 * 3 + 1] = v.horizontal; // result(2, 1) data[2 * 3 + 2] = 0; return r; } Matrix2 Matrix2::Transpose() const { Matrix2 r = Matrix2(this->nCols, this->nRows); for (int rowIx = 0; rowIx < this->nRows; rowIx++) { for (int colIx = 0; colIx < this->nCols; colIx++) r.data[colIx * this->nRows + rowIx] = this->data[rowIx * this->nCols + colIx]; } return r; } Matrix2 LinearAlgebra::Matrix2::operator-() const { Matrix2 r = Matrix2(this->nRows, this->nCols); for (int ix = 0; ix < r.size; ix++) r.data[ix] = -this->data[ix]; return r; } Matrix2 LinearAlgebra::Matrix2::operator+(const Matrix2& v) const { Matrix2 r = Matrix2(this->nRows, this->nCols); for (int ix = 0; ix < r.size; ix++) r.data[ix] = this->data[ix] + v.data[ix]; return r; } Matrix2& Matrix2::operator+=(const Matrix2& v) { for (int ix = 0; ix < this->size; ix++) this->data[ix] += v.data[ix]; return *this; } Matrix2 Matrix2::operator-(const Matrix2& v) const { Matrix2 r = Matrix2(this->nRows, this->nCols); for (int ix = 0; ix < r.size; ix++) r.data[ix] = this->data[ix] - v.data[ix]; return r; } Matrix1 operator*(const Matrix2& m, const Matrix1& v) { Matrix1 r = Matrix1(m.nRows); for (int rowIx = 0; rowIx < m.nRows; rowIx++) { int mRowIx = rowIx * m.nCols; for (int colIx = 0; colIx < m.nCols; colIx++) r(rowIx) += m.data[mRowIx + colIx] * v(colIx); // r(rowIx) += m(rowIx, colIx) * v(colIx); } return r; } Matrix2 Matrix2::operator*(const Matrix2& B) const { Matrix2 r = Matrix2(this->nRows, B.nCols); const int ACols = this->nCols; const int BCols = B.nCols; const int ARows = this->nRows; // const int BRows = B.nRows; for (int i = 0; i < ARows; ++i) { // Pre-compute row offsets int ARowOffset = i * ACols; // ARowOffset is constant for each row of A int BColOffset = i * BCols; // BColOffset is constant for each row of B for (int j = 0; j < BCols; ++j) { float sum = 0; // printf(" 0"); int BIndex = j; for (int k = 0; k < ACols; ++k) { // printf(" + %f * %f", this->data[ARowOffset + k], B.data[BIndex]); // std::cout << " + " << this->data[ARowOffset + k] << " * " // << B.data[BIndex]; sum += this->data[ARowOffset + k] * B.data[BIndex]; BIndex += BCols; } r.data[BColOffset + j] = sum; // std::cout << " = " << sum << " ix: " << BColOffset + j << "\n"; // if (sum != std::trunc(sum)) { // int BIndex = j; // printf(" 0"); // for (int k = 0; k < ACols; ++k) { // printf(" + %f * %f", this->data[ARowOffset + k], B.data[BIndex]); // BIndex += BCols; // } // printf(" = %f ix: %d\n", sum, BColOffset + j); // } } } // if (ACols != BRows) // printf("### Acols != Nrows\n"); // for (size_t i = 0; i < ARows; ++i) { // for (size_t p = 0; p < ACols; ++p) { // float a_ip = this->data[i * ACols + p]; // A(i,p) // for (size_t j = 0; j < BCols; ++j) { // r.data[i * BCols + j] += a_ip * B.data[p * BCols + j]; // // C(i,j) += A(i,p)*B(p,j) // } // } // } return r; } Matrix2 Matrix2::GetRows(int from, int to) { if (from < 0 || to >= this->nRows) std::cerr << "Slice index out of range." << std::endl; Matrix2 result = Matrix2(to - from, this->nCols); int resultRowIx = 0; for (int rowIx = from; rowIx < to; rowIx++) { for (int colIx = 0; colIx < this->nCols; colIx++) result.data[resultRowIx * result.nCols + colIx] = this->data[rowIx * this->nCols + colIx]; resultRowIx++; } return result; } Vector3 Matrix2::GetRow3(int rowIx) { int cellIx = rowIx * this->nCols; Vector3 row(this->data[cellIx + 0], this->data[cellIx + 1], this->data[cellIx + 2]); return row; } void Matrix2::SetRow(int rowIx, Matrix1 source) { int cellIx = rowIx * this->nCols; for (int ix = 0; ix < source.Size(); ix++) this->data[cellIx + ix] = source(ix); } void Matrix2::SetRow3(int rowIx, Vector3 v) { int cellIx = rowIx * this->nCols; this->data[cellIx + 0] = v.x; this->data[cellIx + 1] = v.y; this->data[cellIx + 2] = v.z; } Matrix2 Matrix2::Slice(int rowStart, int rowStop, int colStart, int colStop) const { Matrix2 r = Matrix2(rowStop - rowStart, colStop - colStart); for (int i = rowStart; i < rowStop; i++) { for (int j = colStart; j < colStop; j++) { int resultRowIx = i - rowStart; int resultColIx = j - colStart; r.data[resultRowIx * r.nCols + resultColIx] = this->data[i * this->nCols + j]; } } return r; } void Matrix2::UpdateSlice(int rowStart, int rowStop, const Matrix2& m) const { UpdateSlice(rowStart, rowStop, 0, this->nCols, m); } void Matrix2::UpdateSlice(int rowStart, int rowStop, int colStart, int colStop, const Matrix2& m) const { int mRowDataIx = 0; // printf("UpdateSlice ====== (%d, %d) -> (%d, %d)\n", rowStart, rowStop, // colStart, colStop); for (int rowIx = rowStart; rowIx < rowStop; rowIx++) { int rRowDataIx = rowIx * this->nCols; for (int colIx = colStart; colIx < colStop; colIx++) { this->data[rRowDataIx + colIx] = m.data[mRowDataIx + (colIx - colStart)]; // printf("UpdateSlice (%d, %d) -> (%d, %d) = %F\n", mRowDataIx / m.nCols, // (colIx - colStart), rowIx, colIx, m.data[mRowDataIx + (colIx - colStart)]); } mRowDataIx += m.nCols; } } // Matrix2 Matrix2::Inverse() { // int n = this->nRows; // // // Create an identity matrix of the same size as the original matrix // Matrix2 augmentedMatrix(n, 2 * n); // for (int i = 0; i < n; i++) { // for (int j = 0; j < n; j++) { // int ix = i * this->nCols + j; // augmentedMatrix.data[ix] = this->data[ix]; // augmentedMatrix.data[ix + n] = (i == j) ? 1 : 0; // identity matrix; // } // if (n == 3) { // int ix2 = i * this->nCols; // std::cerr << augmentedMatrix.data[ix2] << " " << augmentedMatrix.data[ix2+1] << " " // << augmentedMatrix.data[ix2+2] << " " << augmentedMatrix.data[ix2+3] << " " // << augmentedMatrix.data[ix2+4] << " " << augmentedMatrix.data[ix2+5] << "\n"; // } // } // // Perform Gaussian elimination // for (int i = 0; i < n; i++) { // // Find the pivot row // float pivot = augmentedMatrix.data[i * augmentedMatrix.nCols + i]; // std::cerr << "pivot " << (int) i << " | " << (i * augmentedMatrix.nCols + i) << " = " << pivot // << std::endl; if (abs(pivot) < 1e-10) // Check for singular matrix std::cerr << "Matrix is singular // and cannot be inverted. " << (int)i << std::endl; // // Normalize the pivot row // for (int j = 0; j < 2 * n; j++) // augmentedMatrix.data[i * augmentedMatrix.nCols + j] /= pivot; // // Eliminate the column below the pivot // for (int j = i + 1; j < n; j++) { // float factor = augmentedMatrix.data[j * augmentedMatrix.nCols + i]; // for (int k = 0; k < 2 * n; k++) // augmentedMatrix.data[j * augmentedMatrix.nCols + k] -= // factor * augmentedMatrix.data[i * augmentedMatrix.nCols + k]; // } // } // // Back substitution // for (int i = n - 1; i >= 0; i--) { // // Eliminate the column above the pivot // for (int j = i - 1; j >= 0; j--) { // float factor = augmentedMatrix.data[j * augmentedMatrix.nCols + i]; // for (int k = 0; k < 2 * n; k++) // augmentedMatrix.data[j * augmentedMatrix.nCols + k] -= // factor * augmentedMatrix.data[i * augmentedMatrix.nCols + k]; // } // } // // Extract the inverse matrix from the augmented matrix // Matrix2 inverse(n, n); // for (int i = 0; i < n; i++) { // for (int j = 0; j < n; j++) // inverse.data[i * inverse.nCols + j] = augmentedMatrix.data[i * augmentedMatrix.nCols + j + n]; // } // return inverse; // } Matrix2 Matrix2::Inverse() { int n = this->nRows; // need to check for nRows = nCols... Matrix2 matrix = Matrix2(*this); // clone this matrix Matrix2 inverse = Matrix2::Identity(n); // Forward elimination for (int i = 0; i < n; ++i) { float pivot = matrix(i, i); if (abs(pivot) < 1e-10) // Check for singular matrix std::cerr << "Matrix is singular and cannot be inverted. " << (int)i << std::endl; for (int j = 0; j < n; ++j) { matrix(i, j) /= pivot; // Normalize pivot row inverse(i, j) /= pivot; // Apply the same to inverse } for (int j = 0; j < n; ++j) { if (j != i) { // outside diagonal float factor = matrix(j, i); for (int k = 0; k < n; ++k) { matrix(j, k) -= factor * matrix(i, k); // Eliminate column inverse(j, k) -= factor * inverse(i, k); // Update inverse } } } } return inverse; } Matrix2 Matrix2::DeleteRows(int rowStart, int rowStop) const { Matrix2 r = Matrix2(this->nRows - (rowStop - rowStart), this->nCols); int resultRowIx = 0; for (int i = 0; i < this->nRows; i++) { if (i >= rowStart && i < rowStop) continue; for (int j = 0; j < this->nCols; j++) r.data[resultRowIx * r.nCols + j] = this->data[i * this->nCols + j]; resultRowIx++; } return r; } Matrix2 Matrix2::DeleteColumns(int colStart, int colStop) const { Matrix2 r = Matrix2(this->nRows, this->nCols - (colStop - colStart)); for (int i = 0; i < this->nRows; i++) { int resultColIx = 0; for (int j = 0; j < this->nCols; j++) { if (j >= colStart && j < colStop) continue; r.data[i * r.nCols + resultColIx++] = this->data[i * this->nCols + j]; } } return r; } /// @brief Compute the Omega matrix of a 3D vector /// @param v The vector /// @return 4x4 Omega matrix template Matrix2 Matrix2::Omega(const Vector3Of& v) { Matrix2 r = Matrix2::Zero(4, 4); r.UpdateSlice(0, 3, 0, 3, -Matrix2::SkewMatrix(v)); // set last row to -v int ix = 3 * 4; r.data[ix++] = -v.horizontal; r.data[ix++] = -v.vertical; r.data[ix] = -v.depth; // Set last column to v ix = 3; r.data[ix += 4] = v.horizontal; r.data[ix += 4] = v.vertical; r.data[ix] = v.depth; return r; } // Matrix2 #pragma endregion #pragma region Block // Submatrix2 SubMatrix2::SubMatrix2(const Matrix2& m, int startRow, int startCol, int rowCount, int colCount) : parent(m), startRow(startRow), startCol(startCol), rowCount(rowCount), colCount(colCount) { assert(this->startRow >= 0 && this->startCol >= 0); assert(this->startRow + this->rowCount <= this->parent.NRows() && this->startCol + this->colCount <= this->parent.NCols()); } void SubMatrix2::operator=(const Matrix2& src) { assert(src.NRows() == this->rowCount && src.NCols() == this->colCount); // If the block is contiguous in memory and src is contiguous, we can memcpy // row by row. In row-major layout, each row of the block is contiguous. for (int row = 0; row < this->rowCount; row++) { float* dstRowPtr = this->parent.GetData() + (this->startRow + row) * this->parent.NCols() + this->startCol; const float* srcRowPtr = src.GetData() + row * src.NCols(); // printf("(%d, %d) %d (%d -> %d)\n", this->startRow + row, startCol, // this->colCount, (int)srcRowPtr, (int)dstRowPtr); memcpy each row std::memcpy(dstRowPtr, srcRowPtr, sizeof(float) * this->colCount); } } void SubMatrix2::operator=(const SubMatrix2& src) { assert(src.rowCount == this->rowCount && src.colCount == this->colCount); // If src.parent == parent and regions overlap, we must copy safely (use // temp row buffer). bool alias = (&src.parent == &this->parent) && !((src.startRow + src.rowCount <= this->startRow) || (this->startRow + this->rowCount <= src.startRow) || (src.startCol + src.colCount <= this->startCol) || (this->startCol + this->colCount <= src.startCol)); if (!alias) { // safe to copy row-by-row directly for (int row = 0; row < this->rowCount; row++) { float* dstRowPtr = this->parent.GetData() + (this->startRow + row) * this->parent.NCols() + this->startCol; const float* srcRowPtr = src.parent.GetData() + (src.startRow + row) * src.parent.NCols() + src.startCol; std::memcpy(dstRowPtr, srcRowPtr, sizeof(float) * this->colCount); } } else { // overlapping regions in same parent: copy to temporary then assign Matrix2 tmp(this->rowCount, this->colCount); for (int row = 0; row < this->rowCount; row++) { const float* srcRowPtr = src.parent.GetData() + (src.startRow + row) * src.parent.NCols() + src.startCol; std::memcpy(tmp.GetData() + row * this->colCount, srcRowPtr, sizeof(float) * this->colCount); } // now copy tmp into destination for (int row = 0; row < this->rowCount; row++) { float* dstRowPtr = parent.GetData() + (this->startRow + row) * this->parent.NCols() + this->startCol; std::memcpy(dstRowPtr, tmp.GetData() + row * this->colCount, sizeof(float) * this->colCount); } } } float SubMatrix2::operator()(int row, int col) const { assert(row >= 0 && col >= 0 && row < rowCount && col < colCount); return parent(startRow + row, startCol + col); } const float& SubMatrix2::operator()(int row, int col) { assert(row >= 0 && col >= 0 && row < rowCount && col < colCount); return parent(startRow + row, startCol + col); } #pragma endregion Block */ #pragma region Matrix2Of template Matrix2Of::Matrix2Of() {} template Matrix2Of::Matrix2Of(unsigned int rowCount, unsigned int colCount) : nRows(rowCount), nCols(colCount), externalData(false) { this->size = this->nRows * this->nCols; if (this->size <= 0) { this->data = nullptr; return; } this->data = new T[this->size]; } template Matrix2Of::Matrix2Of(unsigned int rowCount, unsigned int colCount, T* data) : nRows(rowCount), nCols(colCount), data(data), externalData(true) { this->size = this->nRows * this->nCols; } template Matrix2Of::Matrix2Of(const Matrix2Of& m) : nRows(m.nRows), nCols(m.nCols), size(m.size) { if (this->size == 0) this->data = nullptr; else { this->data = new T[this->size]; for (unsigned int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } template Matrix2Of::Matrix2Of(Matrix2Of&& m) noexcept : nRows(m.nRows), nCols(m.nCols), size(m.size), data(m.data) { m.nRows = 0; m.nCols = 0; m.size = 0; m.data = nullptr; // Leave the moved-from object in a safe state } template Matrix2Of::~Matrix2Of() { if (!this->externalData) { if (this->size > 0) delete[] data; } } template Matrix2Of& Matrix2Of::operator=(const Matrix2Of& m) { if (this != &m) { if (this->size > 0) { delete[] this->data; // Free the current memory } this->nRows = m.nRows; this->nCols = m.nCols; this->size = m.size; this->externalData = m.externalData; if (this->size == 0) this->data = nullptr; else { this->data = new T[this->size]; for (unsigned int ix = 0; ix < this->size; ++ix) this->data[ix] = m.data[ix]; } } return *this; } template Matrix2Of& Matrix2Of::operator=(Matrix2Of&& m) noexcept { if (this != &m) { if (this->size > 0) { delete[] this->data; // Free the current memory } // Transfer ownership of resources this->nRows = m.nRows; this->nCols = m.nCols; this->size = m.size; this->externalData = m.externalData; this->data = m.data; // Leave the moved-from object in a valid state m.size = 0; m.data = nullptr; } return *this; } template unsigned int Matrix2Of::RowCount() const { return this->nRows; } template unsigned int Matrix2Of::ColCount() const { return this->nCols; } template const T& Matrix2Of::operator()(unsigned int rowIx, unsigned int colIx) const { return data[rowIx * this->nCols + colIx]; } template T& Matrix2Of::operator()(unsigned int rowIx, unsigned int colIx) { return data[rowIx * this->nCols + colIx]; } template T* Matrix2Of::GetData() { return this->data; } template T* Matrix2Of::GetData() const { return this->data; } template Matrix2Of Matrix2Of::Zero(unsigned int rowCount, unsigned int colCount) { Matrix2Of r(rowCount, colCount); for (unsigned int ix = 0; ix < r.size; ix++) r.data[ix] = 0; return r; } template void Matrix2Of::Fill(T value) { for (unsigned int ix = 0; ix < this->size; ix++) this->data[ix] = value; } template void Matrix2Of::FillDiagonal(T f) { int n = this->nRows < this->nCols ? nRows : nCols; for (int ix = 0; ix < n; ix++) this->data[ix * this->nCols + ix] = f; } template Matrix2Of Matrix2Of::Identity(unsigned int size) { return Diagonal(1, size); } template Matrix2Of Matrix2Of::Identity(unsigned int rowCount, unsigned int colCount) { Matrix2Of m = Matrix2Of::Zero(rowCount, colCount); m.FillDiagonal(1); return m; } template Matrix2Of Matrix2Of::Diagonal(T f, unsigned int size) { Matrix2Of r = Matrix2Of::Zero(size, size); T* data = r.data; int valueIx = 0; for (unsigned int ix = 0; ix < size; ix++) { data[valueIx] = f; valueIx += size + 1; } return r; } template Matrix2Of Matrix2Of::SkewMatrix(const Vector3Of& v) { Matrix2Of r(3, 3); T* data = r.data; data[0 * 3 + 0] = 0; data[0 * 3 + 1] = -v.depth; // result(0, 1) data[0 * 3 + 2] = v.vertical; // result(0, 2) data[1 * 3 + 0] = v.depth; // result(1, 0) data[1 * 3 + 1] = 0; data[1 * 3 + 2] = -v.horizontal; // result(1, 2) data[2 * 3 + 0] = -v.vertical; // result(2, 0) data[2 * 3 + 1] = v.horizontal; // result(2, 1) data[2 * 3 + 2] = 0; return r; } template Matrix1Of Matrix2Of::GetRow(unsigned int rowIx) { Matrix1Of r = Matrix1Of(this->nCols); unsigned int rowStartIx = rowIx * this->nCols; for (unsigned int colIx = 0; colIx < this->nCols; colIx++) r.data[colIx] = this->data[rowStartIx + colIx]; return r; } template Vector3Of Matrix2Of::GetRow3(unsigned int rowIx) { int cellIx = rowIx * this->nCols; Vector3Of row(this->data[cellIx + 0], this->data[cellIx + 1], this->data[cellIx + 2]); return row; } template Matrix2Of Matrix2Of::GetRows(unsigned int fromRowIx, unsigned int toRowIx) { if (toRowIx >= this->nRows) std::cerr << "Slice index out of range." << std::endl; Matrix2Of result = Matrix2Of(toRowIx - fromRowIx, this->nCols); int resultRowIx = 0; for (unsigned int rowIx = fromRowIx; rowIx < toRowIx; rowIx++) { for (unsigned int colIx = 0; colIx < this->nCols; colIx++) result.data[resultRowIx * result.nCols + colIx] = this->data[rowIx * this->nCols + colIx]; resultRowIx++; } return result; } template void Matrix2Of::SetRow(unsigned int rowIx, Matrix1Of source) { int cellIx = rowIx * this->nCols; for (unsigned int ix = 0; ix < source.Size(); ix++) this->data[cellIx + ix] = source(ix); } template void Matrix2Of::SetRow3(unsigned int rowIx, Vector3Of v) { int cellIx = rowIx * this->nCols; this->data[cellIx + 0] = v.horizontal; this->data[cellIx + 1] = v.vertical; this->data[cellIx + 2] = v.depth; } template Matrix2Of Matrix2Of::DeleteRows(unsigned int fromRowIx, unsigned int toRowIx) const { Matrix2Of r(this->nRows - (toRowIx - fromRowIx), this->nCols); int resultRowIx = 0; for (unsigned int i = 0; i < this->nRows; i++) { if (i >= fromRowIx && i < toRowIx) continue; for (unsigned int j = 0; j < this->nCols; j++) r.data[resultRowIx * r.nCols + j] = this->data[i * this->nCols + j]; resultRowIx++; } return r; } template Matrix2Of Matrix2Of::DeleteColumns(unsigned int fromColIx, unsigned int toColIx) const { Matrix2Of r(this->nRows, this->nCols - (toColIx - fromColIx)); for (unsigned int i = 0; i < this->nRows; i++) { int resultColIx = 0; for (unsigned int j = 0; j < this->nCols; j++) { if (j >= fromColIx && j < toColIx) continue; r.data[i * r.nCols + resultColIx++] = this->data[i * this->nCols + j]; } } return r; } template Matrix2Of Matrix2Of::operator-() const { Matrix2Of r(this->nRows, this->nCols); for (unsigned int ix = 0; ix < r.size; ix++) r.data[ix] = -this->data[ix]; return r; } template Matrix2Of Matrix2Of::operator+(const Matrix2Of& v) const { Matrix2Of r(this->nRows, this->nCols); for (unsigned int ix = 0; ix < r.size; ix++) r.data[ix] = this->data[ix] + v.data[ix]; return r; } template Matrix2Of& Matrix2Of::operator+=(const Matrix2Of& v) { for (unsigned int ix = 0; ix < this->size; ix++) this->data[ix] += v.data[ix]; return *this; } template Matrix2Of Matrix2Of::operator-(const Matrix2Of& v) const { Matrix2Of r(this->nRows, this->nCols); for (unsigned int ix = 0; ix < r.size; ix++) r.data[ix] = this->data[ix] - v.data[ix]; return r; } template Matrix2Of& Matrix2Of::operator-=(const Matrix2Of& v) { for (unsigned int ix = 0; ix < this->size; ix++) this->data[ix] -= v.data[ix]; return *this; } template Matrix2Of Matrix2Of::operator*(const Matrix2Of& B) const { Matrix2Of r(this->nRows, B.nCols); const int ACols = this->nCols; const int BCols = B.nCols; const int ARows = this->nRows; for (int i = 0; i < ARows; ++i) { // Pre-compute row offsets int ARowOffset = i * ACols; // ARowOffset is constant for each row of A int BColOffset = i * BCols; // BColOffset is constant for each row of B for (int j = 0; j < BCols; ++j) { T sum = 0; int BIndex = j; for (int k = 0; k < ACols; ++k) { sum += this->data[ARowOffset + k] * B.data[BIndex]; BIndex += BCols; } r.data[BColOffset + j] = sum; } } return r; } template Matrix2Of Matrix2Of::Transposed() const { Matrix2Of r(this->nCols, this->nRows); for (unsigned int rowIx = 0; rowIx < this->nRows; rowIx++) { for (unsigned int colIx = 0; colIx < this->nCols; colIx++) r.data[colIx * this->nRows + rowIx] = this->data[rowIx * this->nCols + colIx]; } return r; } template Matrix2Of Matrix2Of::Inverse() { int n = this->nRows; // need to check for nRows = nCols... Matrix2Of matrix = Matrix2Of(*this); // clone this matrix Matrix2Of inverse = Matrix2Of::Identity(n); // Forward elimination for (int i = 0; i < n; ++i) { T pivot = matrix(i, i); if (abs(pivot) < 1e-10) // Check for singular matrix std::cerr << "Matrix is singular and cannot be inverted. " << (int)i << std::endl; for (int j = 0; j < n; ++j) { matrix(i, j) /= pivot; // Normalize pivot row inverse(i, j) /= pivot; // Apply the same to inverse } for (int j = 0; j < n; ++j) { if (j != i) { // outside diagonal T factor = matrix(j, i); for (int k = 0; k < n; ++k) { matrix(j, k) -= factor * matrix(i, k); // Eliminate column inverse(j, k) -= factor * inverse(i, k); // Update inverse } } } } return inverse; } template Matrix2Of Matrix2Of::Slice(int rowStart, int rowStop, int colStart, int colStop) const { Matrix2Of r(rowStop - rowStart, colStop - colStart); for (int i = rowStart; i < rowStop; i++) { for (int j = colStart; j < colStop; j++) { int resultRowIx = i - rowStart; int resultColIx = j - colStart; r.data[resultRowIx * r.nCols + resultColIx] = this->data[i * this->nCols + j]; } } return r; } template void Matrix2Of::UpdateSlice(int rowStart, int rowStop, const Matrix2Of& m) const { UpdateSlice(rowStart, rowStop, 0, this->nCols, m); } template void Matrix2Of::UpdateSlice(int rowStart, int rowStop, int colStart, int colStop, const Matrix2Of& m) const { int mRowDataIx = 0; // printf("UpdateSlice ====== (%d, %d) -> (%d, %d)\n", rowStart, rowStop, // colStart, colStop); for (int rowIx = rowStart; rowIx < rowStop; rowIx++) { int rRowDataIx = rowIx * this->nCols; for (int colIx = colStart; colIx < colStop; colIx++) { this->data[rRowDataIx + colIx] = m.data[mRowDataIx + (colIx - colStart)]; // printf("UpdateSlice (%d, %d) -> (%d, %d) = %F\n", mRowDataIx / m.nCols, // (colIx - colStart), rowIx, colIx, m.data[mRowDataIx + (colIx - colStart)]); } mRowDataIx += m.nCols; } } // template // Matrix2Of Matrix2Of::Omega(const Vector3Of& v) { // Matrix2Of r = Matrix2Of::Zero(4, 4); // r.UpdateSlice(0, 3, 0, 3, -Matrix2::SkewMatrix(v)); // // set last row to -v // int ix = 3 * 4; // r.data[ix++] = -v.horizontal; // r.data[ix++] = -v.vertical; // r.data[ix] = -v.depth; // // Set last column to v // ix = 3; // r.data[ix += 4] = v.horizontal; // r.data[ix += 4] = v.vertical; // r.data[ix] = v.depth; // return r; // } template class Matrix2Of; template class Matrix2Of; template class Matrix2Of; #pragma endregion Matrix2Of } // namespace LinearAlgebra