diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..ed82eaa --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,39 @@ +on: push + +env: + LIB_NAME: matrix_lib + +jobs: + build: + runs-on: ubuntu-latest + container: leshiy1295/gcc_linters_valgrind_cmake_gtest + steps: + - uses: actions/checkout@v2 + - run: make build + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: executable_file + path: build/main + test: + runs-on: ubuntu-latest + container: leshiy1295/gcc_linters_valgrind_cmake_gtest + needs: [build] + steps: + - uses: actions/checkout@v2 + - name: install lcov + run: apt-get install lcov -y + - name: Build, run and test + run: | + make build TEST_OPT=ON + make run + make test + - name: Test coverage + run: make coverage + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: coverage-info + path: build/report + - name: launch valgrind + run: make valgrind_tests \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c30609f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/build +_test.cpp +main +/.vscode \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c5a8b2d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.0.0) +project(main) + +set(CMAKE_CXX_STANDARD 17) + +option(TEST_OPT "build test version" OFF) +option(DEBUG_OPT "build debug version" OFF) + +add_subdirectory(matrix_lib) + +add_executable(${PROJECT_NAME} main.cpp) + +if(TEST_OPT) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage -lgcov" ) +endif() + +if(DEBUG_OPT) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") +endif() + + +target_include_directories(${PROJECT_NAME} PUBLIC ${MATRIX_LIB_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${MATRIX_LIB_LIBRARIES}) + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ac94323 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +LIB_DIR = matrix_lib +BUILD_DIR = build + +TARGET = ./${BUILD_DIR}/main +TESTS_EXE = tests +TESTS_DIR = tests + +TEST_OPT = OFF +DEBUG_OPT = OFF + +.PHONY: build rebuild test run + +build: clean + mkdir ${BUILD_DIR} + cd ${BUILD_DIR} && cmake .. -DTEST_OPT=${TEST_OPT} -DDEBUG_OPT=${DEBUG_OPT} && $(MAKE) --no-print-directory + +clean: + (rm -r ${BUILD_DIR} 2>/dev/null) || exit 0 + +run: + cd ${BUILD_DIR} && $(MAKE) --no-print-directory + ${TARGET} + +test: + ./${BUILD_DIR}/${LIB_DIR}/tests/tests + +coverage: + cd ${BUILD_DIR} && lcov -t "testing_${LIB_DIR}" -o coverage.info -c -d ${LIB_DIR}/CMakeFiles \ + && lcov --remove coverage.info -o coverage.info '/usr/include/*' '/usr/lib/*' \ + && genhtml -o report coverage.info + +valgrind_tests: + valgrind --tool=memcheck --leak-check=yes --error-exitcode=1 ./${BUILD_DIR}/${LIB_DIR}/${TESTS_DIR}/${TESTS_EXE} + diff --git a/README.md b/README.md index ac390f9..29d7185 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # techopark_cpp_hw Репозиторий для сдачи домашних работ по С++ в Технопарке + +#### Домашняя работа 1 + +##### Студент: Михаил Мяделец WEB-11 +##### Ментор: Денис Кириллов +##### Преподаватель: Иван Возвахов + +`Вариант #1` + +Необходимо разработать классы для работы с вещественными числами, векторами (строками размерности 1xN и столбцами размерности Nx1) и матрицами размерности MxN и реализовать операции над ними: +1) обращение по индексам к элементам, извлечение диагонали, строки или столбца - 1б (после модификации исходной матрицы извлечённые элементы меняться не должны); +2) возможность создания матрицы из векторов или чисел, вектора из чисел - 1б; +3) поэлементное сложение/вычитание/умножение объектов одинаковой размерности - 1б; +4) умножение числа на матрицу, вектора на матрицу, матрицы на вектор и матрицы на матрицу - 1б; +5) суммирование/вычитание числа и вектора/матрицы, матрицы и вектора (с возможностью выбора - по строкам/по столбцам) - 1б; +6) получение транспонированной и обратной матриц - 1б; +7) подсчёт определителя матрицы - 1б. + +Все основные операции должны быть реализованы через перегрузку операторов (операторы могут быть модифицирующие (+= и др.) и немодифицирующие (+ и др.)). + +Доп. баллы: +- поддержка "слайсов, как в питоне" (на уровне методов, т.к. операторы такой синтаксис не поддерживают). В качестве примерного интерфейса можно опираться на то, как это сделано в " аналоге numpy на C++" https://github.com/dpilger26/NumCpp -1б; +- размерность матриц может задаваться с помощью шаблонных параметров -2б; + +Для сдачи необходимо развернуть базовый CI, в котором будут осуществляться автоматическая сборка и тестирование проекта (хотя бы один интеграционный тест, показывающий работоспособность всех реализованных пунктов). Без покрытия тестами каждый пункт оценивается в 50% стоимости. diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0576bcd --- /dev/null +++ b/main.cpp @@ -0,0 +1,17 @@ +#include + +#include "dvector.h" +#include "dmatrix_compile.h" +#include "dmatrix.h" + +int main() +{ + DMatrix matrix = { + {1, -2, 1}, + {2, 1, -1}, + {3, 2, -2} + }; + DVector vector = {3, 4, 5}; + DMatrix newMatrix = 2 * vector.Dot(matrix.Inv()); + std::cout << newMatrix; +} diff --git a/matrix_lib/CMakeLists.txt b/matrix_lib/CMakeLists.txt new file mode 100644 index 0000000..9594fa9 --- /dev/null +++ b/matrix_lib/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.0.0) +project(matrix_lib) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -O0") + +if(TEST_OPT) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage -lgcov" ) +endif() + +if(DEBUG_OPT) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") +endif() + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# find_package(Boost COMPONENTS system REQUIRED) + +# if(NOT Boost_FOUND) +# message("Not found Boost") +# return() +# endif() + +# Boost_VERSION_MACRO +# Boost_LIBRARY_DIR +# Boost_INCLUDE_DIR + +add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS}) + +target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS}) + +# без PARENT_SCOPE для тестов +# с PARENT_SCOPE для главного CMakeLists.txt +set(MATRIX_LIB_LIBRARIES ${PROJECT_NAME} ) +set(MATRIX_LIB_LIBRARIES ${PROJECT_NAME} PARENT_SCOPE) +set(MATRIX_LIB_INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) +set(MATRIX_LIB_INCLUDE_DIRS ${INCLUDE_DIRS} ) + +if (TEST_OPT) + enable_testing() + add_subdirectory(tests) +endif() diff --git a/matrix_lib/include/dmatrix.h b/matrix_lib/include/dmatrix.h new file mode 100644 index 0000000..aea9e64 --- /dev/null +++ b/matrix_lib/include/dmatrix.h @@ -0,0 +1,135 @@ +#pragma once + +#include "dvector.h" + +enum class ORIENT { ROW = 0, COL }; +enum SLICE { ROW = 0, COL }; + +// Matrix of doubles number +class DMatrix +{ +private: + DVector *m_matrix = nullptr; + size_t m_nRows = 0; + size_t m_nCols = 0; + size_t m_capacity = 0; + void grow(); +public: + DMatrix() = default; + DMatrix(DVector *matrix, size_t nRows); + // Создание матрицы как вектор-строку или вектор-столбец + DMatrix(DVector const &dvec, ORIENT orientation = ORIENT::ROW); + DMatrix(size_t rows, size_t cols, double fill_value = 0); + DMatrix(std::initializer_list> const &matrix); + DMatrix(DMatrix const &other); + DMatrix(DMatrix &&other); + DMatrix &operator=(DMatrix other); + ~DMatrix(); + + /* ****** Задание размера через шаблон *******/ + template + static DMatrix Create(double fill_value = 0); + +public: + void Clear(); + bool Empty(); + void Swap(DMatrix &other); + + void PushRowBack(DVector const &dvec); + void PushRowBack(std::initializer_list const &init_list); + void PopRowBack(); + + void PushColBack(DVector const &dvec); + void PushColBack(std::initializer_list const &init_list); + void PopColBack(); + + void EraseByIndex(size_t index, ORIENT orientation = ORIENT::ROW); + + + /* ****** ЗАДАНИЕ 1 ****** */ + DVector GetDiag() const; + DVector GetRow(size_t index) const; + DVector GetCol(size_t index) const; + + /* ****** ЗАДАНИЕ 4 ****** */ + DMatrix Dot(DMatrix const &other) const; + DVector Dot(DVector const &dvec) const; + + size_t nRows() const; + size_t nCols() const; + size_t Capacity() const; + + /* ****** ЗАДАНИЕ 5 ****** */ + // модифицирующие операции + void AddNum(double value); + void SubNum(double value); + void AddVec(DVector const &dvec, ORIENT orientation = ORIENT::ROW); + void SubVec(DVector const &dvec, ORIENT orientation = ORIENT::ROW); + + /* ****** ЗАДАНИЯ 6 и 7 ****** */ + DMatrix T() const; + double Det() const; + double Minor(size_t iIndex, size_t jIndex) const; + DMatrix Adj(); + DMatrix Inv(); + + DVector const &operator[](size_t index) const; + DVector &operator[](size_t index); + + /* + Пояснение: + enum SLICE {ROW = 0, COL}, чтобы + 1. не писать класс, если в параметрах ROW / COL + 2. кастилось к enum SLICE, если в параметрах 0 / 1 + */ + + /* ****** Slices ****** */ + DMatrix operator()(size_t begin, size_t end, int step = 1, uint8_t sliceType = SLICE::ROW) const; + + DMatrix SliceRow(size_t begin, size_t end, int step = 1) const; + DMatrix SliceCol(size_t begin, size_t end, int step = 1) const; +}; + +// matrix *= value +DMatrix &operator/=(DMatrix &matrix, double value); +DMatrix &operator*=(DMatrix &matrix, double value); + +// matrix * value +DMatrix operator/(DMatrix matrix, double value); +DMatrix operator*(DMatrix matrix, double value); + +// value *= matrix + +DMatrix &operator/=(double value, DMatrix &matrix); +DMatrix &operator*=(double value, DMatrix &matrix); + +// value * matrix +DMatrix operator/(double value, DMatrix matrix); +DMatrix operator*(double value, DMatrix matrix); + +// matrix += matrix + +DMatrix &operator+=(DMatrix &left, DMatrix const &right); +DMatrix &operator-=(DMatrix &left, DMatrix const &right); +DMatrix &operator/=(DMatrix &left, DMatrix const &right); +DMatrix &operator*=(DMatrix &left, DMatrix const &right); + +// matrix + matrix +DMatrix operator+(DMatrix left, DMatrix const &right); +DMatrix operator-(DMatrix left, DMatrix const &right); +DMatrix operator/(DMatrix left, DMatrix const &right); +DMatrix operator*(DMatrix left, DMatrix const &right); + +std::ostream &operator<<(std::ostream &out, DMatrix const &matrix); + +void Print(DMatrix const &matrix, std::string const &msg = std::string{}); + +template +DMatrix DMatrix::Create(double fill_value) +{ + if ((rows == 0) ^ (cols == 0)) + { + throw std::runtime_error("Creation is impossible"); + } + return DMatrix(rows, cols, fill_value); +} diff --git a/matrix_lib/include/dmatrix_compile.h b/matrix_lib/include/dmatrix_compile.h new file mode 100644 index 0000000..3d37ff1 --- /dev/null +++ b/matrix_lib/include/dmatrix_compile.h @@ -0,0 +1,142 @@ +#include "dmatrix.h" +#include "dvector.h" + +#define ERROR_LENGTH "In a matrix, all row and column lengths must be the same" +#define ERROR_RANGE "Index out of range" +#define ERROR_SIZE "An arithmetic operation is not possible because of different sizes" + +template +class DMatrixCompile +{ +private: + DMatrix dmatRuntime; +public: + DMatrixCompile(double fill_value = 0); + + size_t nRows() { return rows; } + size_t nCols() { return cols; } + + DMatrix GetDynamicMatrix() const { return dmatRuntime; } + + template + DMatrix operator+(DMatrixCompile const &dmatCompile); + + template + DMatrix operator-(DMatrixCompile const &dmatCompile); + + template + DMatrix operator*(DMatrixCompile const &dmatCompile); + + template + DMatrix operator/(DMatrixCompile const &dmatCompile); + + template + DVector const &At() const; + + template + DVector GetRow() const; + + template + DVector GetCol() const; + + template + DVector &At(); + + template + DMatrix Dot(DMatrixCompile const &dmatCompile) const; + + DMatrix T() const; +}; + +template +DMatrixCompile::DMatrixCompile(double fill_value) : dmatRuntime(rows, cols, fill_value) +{ + // pass +} + + +template +template +DVector const &DMatrixCompile::At() const +{ + static_assert(index < rows, ERROR_RANGE); + return dmatRuntime[index]; +} + +template +template +DVector &DMatrixCompile::At() +{ + static_assert(index < rows, ERROR_RANGE); + return dmatRuntime[index]; +} + +template +template +DVector DMatrixCompile::GetRow() const +{ + static_assert(index < rows, ERROR_RANGE); + return dmatRuntime.GetRow(index); +} + +template +template +DVector DMatrixCompile::GetCol() const +{ + static_assert(index < cols, ERROR_RANGE); + return dmatRuntime.GetCol(index); +} + +template +template +DMatrix DMatrixCompile::operator+(DMatrixCompile const &dmatCompile) +{ + static_assert(rows == rowsOther, ERROR_LENGTH); + static_assert(cols == colsOther, ERROR_LENGTH); + return dmatRuntime + dmatCompile.dmatRuntime; +} + +template +template +DMatrix DMatrixCompile::operator-(DMatrixCompile const &dmatCompile) +{ + static_assert(rows == rowsOther, ERROR_LENGTH); + static_assert(cols == colsOther, ERROR_LENGTH); + return dmatRuntime - dmatCompile.dmatRuntime; +} + +template +template +DMatrix DMatrixCompile::operator*(DMatrixCompile const &dmatCompile) +{ + static_assert(rows == rowsOther, ERROR_LENGTH); + static_assert(cols == colsOther, ERROR_LENGTH); + return dmatRuntime * dmatCompile.dmatRuntime; +} + +template +template +DMatrix DMatrixCompile::operator/(DMatrixCompile const &dmatCompile) +{ + static_assert(rows == rowsOther, ERROR_LENGTH); + static_assert(cols == colsOther, ERROR_LENGTH); + return dmatRuntime / dmatCompile.dmatRuntime; +} + +template +template +DMatrix DMatrixCompile::Dot(DMatrixCompile const &dmatCompile) const +{ + static_assert(cols == rowsOther, ERROR_SIZE); + /* + нельзя обратиться напрямую к приватному полю, потому класс, инстанцированный с другими шаблонными параметрами, уже является другим + */ + return dmatRuntime.Dot(dmatCompile.GetDynamicMatrix()); +} + +template +DMatrix DMatrixCompile::T() const +{ + static_assert(cols == rows, ERROR_LENGTH); + return dmatRuntime.T(); +} diff --git a/matrix_lib/include/dvector.h b/matrix_lib/include/dvector.h new file mode 100644 index 0000000..1dbe99a --- /dev/null +++ b/matrix_lib/include/dvector.h @@ -0,0 +1,83 @@ +#pragma once + +class DMatrix; + +// Vector of doubles number +class DVector +{ +private: + double *m_array = nullptr; + size_t m_capacity = 0; + size_t m_size = 0; + void grow(); +public: + DVector() = default; + explicit DVector(size_t size, double fill_value = 0); + DVector(const double *begin, const double *end); + DVector(std::initializer_list const &init_list); + DVector(DVector const &other); + DVector(DVector &&other); + DVector &operator=(DVector other); + ~DVector(); + +public: + const double *CBegin() const; + const double *CEnd() const; + + void Swap(DVector &other); + void Fill(double fill_value); + + bool Empty() const; + size_t Size() const; + size_t Capacity() const; + void Clear(); + + double *Find(double value) const; + double *Erase(double *ptr_value); + + void PushBack(double value); + void PopBack(); + + double Front() const; + double Back() const; + + double const &operator[](size_t index) const; + double &operator[](size_t index); + + /* ****** ЗАДАНИЕ 4 ****** */ + double Dot(DVector const &other) const; + DVector Dot(DMatrix const &matrix) const; + + + /* ****** ЗАДАНИЕ 5 ****** */ + // модифицирующие операции + void AddNum(double value); + void SubNum(double value); + + /* ****** Slices ****** */ + DVector operator()(size_t begin, size_t end, int step = 1) const; +}; + +// vector *= value +DVector &operator/=(DVector &left, double value); +DVector &operator*=(DVector &left, double value); + +// vector * value +DVector operator/(DVector left, double value); +DVector operator*(DVector left, double value); + +// vector += vector +DVector &operator+=(DVector &left, DVector const &right); +DVector &operator-=(DVector &left, DVector const &right); +DVector &operator/=(DVector &left, DVector const &right); +DVector &operator*=(DVector &left, DVector const &right); + +// vector + vector +DVector operator+(DVector left, DVector const &right); +DVector operator-(DVector left, DVector const &right); +DVector operator/(DVector left, DVector const &right); +DVector operator*(DVector left, DVector const &right); + +void Print(DVector const &dvector, std::string const &message = std::string{}); + +std::ostream &operator<<(std::ostream &out, DVector const &vector); diff --git a/matrix_lib/include/stdafx.h b/matrix_lib/include/stdafx.h new file mode 100644 index 0000000..2c79aa8 --- /dev/null +++ b/matrix_lib/include/stdafx.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include +#include +#include diff --git a/matrix_lib/src/dmatrix.cpp b/matrix_lib/src/dmatrix.cpp new file mode 100644 index 0000000..fc5c81c --- /dev/null +++ b/matrix_lib/src/dmatrix.cpp @@ -0,0 +1,692 @@ +#include "stdafx.h" +#include "dmatrix.h" + +// т.к. const имеет внутреннее связывание, добавляем extern, чтобы можно было включить в другой .cpp файл +extern const std::string ERROR_EMPTY = "Data structure is empty"; +extern const std::string ERROR_SIZE = "An arithmetic operation is not possible because of different sizes"; +extern const std::string ERROR_RANGE = "Index out of range"; +const std::string ERROR_LENGTH = "In a matrix, all row and column lengths must be the same"; +const std::string ERROR_RECT_MATRIX = "Matrix is not square"; + +// проверка на равный размер, иначе исключение +void CheckEqualSize(size_t size1, size_t size2, std::string const &msgError = "Exception") +{ + if (size1 != size2) + { + throw std::runtime_error(msgError); + } +} + +DMatrix::DMatrix(size_t nRows, size_t nCols, double fill_value) : m_nRows(nRows), m_nCols(nCols), m_capacity(nRows) +{ + // можно ли указать конкретный конструктор для каждого элемента дин. массива? + try + { + m_matrix = new DVector[m_nRows](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + for (size_t i = 0; i < m_nRows; ++i) + { + m_matrix[i] = DVector(m_nCols, fill_value); + } +} + +DMatrix::DMatrix(std::initializer_list> const &init_list) : m_nRows(init_list.size()), m_nCols(init_list.begin()->size()), m_capacity(init_list.size()) +{ + // проверка на прямоугольность init_list + if (std::any_of(init_list.begin() + 1, init_list.end(), [n = m_nCols](std::initializer_list const &list) + { + return list.size() != n; + })) + { + throw std::runtime_error("DMatrix: " + ERROR_LENGTH); + } + try + { + m_matrix = new DVector[m_nRows](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + int i = 0; + for (auto row = init_list.begin(); row < init_list.end(); ++row) + { + m_matrix[i] = DVector(row->begin(), row->end()); + ++i; + } +} + +DMatrix::DMatrix(DMatrix const &other) : m_nRows(other.nRows()), m_nCols(other.nCols()), m_capacity(other.nRows()) +{ + try + { + m_matrix = new DVector[other.nRows()](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + for (size_t i = 0; i < other.m_nRows; ++i) + { + m_matrix[i] = DVector(other[i].CBegin(), other[i].CEnd()); + } +} + +DMatrix::DMatrix(DMatrix &&other) : m_matrix(other.m_matrix), m_nRows(other.m_nRows), m_nCols(other.nCols()), m_capacity(other.m_nRows) +{ + // обнуление + other.Clear(); +} + +DMatrix::DMatrix(DVector *matrix, size_t nRows) : m_matrix(matrix), m_nRows(nRows), m_nCols(matrix[0].Size()), m_capacity(nRows) +{ + // проверка на прямоугольность DVector * + // намного читабильнее, чем std::any_of.... + for (size_t i = 1; i < m_nCols; ++i) + { + if (matrix[i].Size() != m_nCols) + { + throw std::runtime_error("DMatrix: " + ERROR_LENGTH); + } + } +} + +DMatrix::~DMatrix() +{ + if (m_matrix) + { + delete[] m_matrix; + } + m_matrix = nullptr; +} + +void DMatrix::Swap(DMatrix &other) +{ + std::swap(m_matrix, other.m_matrix); + std::swap(m_nRows, other.m_nRows); + std::swap(m_nCols, other.m_nCols); +} + +DMatrix &DMatrix::operator=(DMatrix other) +{ + Swap(other); + return *this; +} + +DMatrix::DMatrix(DVector const &dvec, ORIENT orientation) +{ + if (orientation == ORIENT::ROW) + { + try + { + m_matrix = new DVector[1]{ DVector(dvec) }; + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + m_nRows = 1; + m_nCols = dvec.Size(); + m_capacity = m_nRows; + } + else + { + try + { + m_matrix = new DVector[dvec.Size()](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + for (size_t i = 0; i < dvec.Size(); ++i) + { + m_matrix[i] = DVector(1, dvec[i]); + } + m_nRows = dvec.Size(); + m_nCols = 1; + m_capacity = m_nRows; + } +} + +void DMatrix::Clear() +{ + m_matrix = nullptr; + m_nRows = 0; + m_nCols = 0; + m_capacity = 0; +} + +size_t DMatrix::nRows() const +{ + return m_nRows; +} + +size_t DMatrix::nCols() const +{ + return m_nCols; +} + + +DVector const &DMatrix::operator[](size_t index) const +{ + if (index > m_nRows - 1) + { + throw std::runtime_error("operator[]: " + ERROR_RANGE); + } + return m_matrix[index]; +} + +DVector &DMatrix::operator[](size_t index) +{ + if (index > m_nRows - 1) + { + throw std::runtime_error("operator[]: " + ERROR_RANGE); + } + return m_matrix[index]; +} + +bool DMatrix::Empty() +{ + return m_nRows == 0; +} + +size_t DMatrix::Capacity() const +{ + return m_capacity; +} + +void DMatrix::PushRowBack(DVector const &dvec) +{ + if (m_nRows > 0 && dvec.Size() != m_nCols) + { + throw std::runtime_error("PushRowBack: " + ERROR_LENGTH); + } + m_nCols = dvec.Size(); + if (m_nRows == m_capacity) + { + grow(); + } + m_matrix[m_nRows] = dvec; + ++m_nRows; +} + +void DMatrix::PushRowBack(std::initializer_list const &init_list) +{ + if (m_nRows > 0 && init_list.size() != m_nCols) + { + throw std::runtime_error("PushRowBack: " + ERROR_LENGTH); + } + m_nCols = init_list.size(); + if (m_nRows == m_capacity) + { + grow(); + } + m_matrix[m_nRows] = DVector(init_list.begin(), init_list.end()); + ++m_nRows; +} + +void DMatrix::PopRowBack() +{ + if (Empty()) + { + throw std::runtime_error("PopRowBack: " + ERROR_EMPTY); + } + --m_nRows; + if (m_nRows == 0) + { + if (m_matrix) + { + delete[] m_matrix; + } + Clear(); + } +} + +void DMatrix::PushColBack(DVector const &dvec) +{ + if (m_nRows > 0 && dvec.Size() != m_nRows) + { + throw std::runtime_error("PushColBack: " + ERROR_LENGTH); + } + if (m_nRows == 0) + { + try + { + m_matrix = new DVector[dvec.Size()](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + m_capacity = dvec.Size(); + } + m_nRows = dvec.Size(); + ++m_nCols; + for (size_t i = 0; i < m_nRows; ++i) + { + m_matrix[i].PushBack(dvec[i]); + } +} + +void DMatrix::PushColBack(std::initializer_list const &init_list) +{ + if (m_nRows > 0 && init_list.size() != m_nRows) + { + throw std::runtime_error("PushColBack: " + ERROR_LENGTH); + } + if (m_nRows == 0) + { + try + { + m_matrix = new DVector[init_list.size()](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + m_capacity = init_list.size(); + } + m_nRows = init_list.size(); + ++m_nCols; + for (size_t i = 0; i < m_nRows; ++i) + { + m_matrix[i].PushBack(*(init_list.begin() + i)); + } +} + +void DMatrix::PopColBack() +{ + if (Empty()) + { + throw std::runtime_error("PopColBack: " + ERROR_EMPTY); + } + --m_nCols; + for (size_t i = 0; i < m_nRows; ++i) + { + m_matrix[i].PopBack(); + } + if (m_nCols == 0) + { + if (m_matrix) + { + delete[] m_matrix; + } + Clear(); + } +} + +DVector DMatrix::GetDiag() const +{ + size_t nElemsDiag = std::min(m_nRows, m_nCols); + DVector dvec(nElemsDiag); + for (size_t i = 0; i < nElemsDiag; ++i) + { + dvec[i] = m_matrix[i][i]; + } + return dvec; +} + +DVector DMatrix::GetRow(size_t index) const +{ + if (index >= m_nRows) + { + throw std::runtime_error("GetRow: " + ERROR_RANGE); + } + DVector dvec(m_nCols); + for (size_t i = 0; i < m_nCols; ++i) + { + dvec[i] = m_matrix[index][i]; + } + return dvec; +} + +DVector DMatrix::GetCol(size_t index) const +{ + if (index >= m_nCols) + { + throw std::runtime_error("GetCol: " + ERROR_RANGE); + } + DVector dvec(m_nRows); + for (size_t i = 0; i < m_nRows; ++i) + { + dvec[i] = m_matrix[i][index]; + } + return dvec; +} + +DMatrix DMatrix::Dot(DMatrix const &other) const +{ + CheckEqualSize(m_nCols, other.nRows(), "Dot: " + ERROR_SIZE); + + DMatrix dmat(m_nRows, other.nCols()); + for (size_t i = 0; i < m_nRows; ++i) + { + for (size_t j = 0; j < other.nCols(); ++j) + { + double elem_i_j = 0; + for (size_t k = 0; k < other.nRows(); ++k) + { + elem_i_j += m_matrix[i][k] * other.m_matrix[k][j]; + } + dmat[i][j] = elem_i_j; + } + } + return dmat; +} + +// подразуемевается что матрица умножается на вектор-столбец +// результат - DVector +DVector DMatrix::Dot(DVector const &dvec) const +{ + CheckEqualSize(m_nCols, dvec.Size(), "Dot: " + ERROR_SIZE); + + DVector dvecRes(m_nRows); + for (size_t i = 0; i < m_nRows; ++i) + { + double elem_i_j = 0; + for (size_t j = 0; j < m_nCols; ++j) + { + elem_i_j += m_matrix[i][j] * dvec[j]; + } + dvecRes[i] = elem_i_j; + } + return dvecRes; +} + +void DMatrix::AddNum(double value) +{ + std::for_each(m_matrix, &m_matrix[m_nRows - 1] + 1, [value](DVector &vec) + { + vec.AddNum(value); + }); +} + +void DMatrix::SubNum(double value) +{ + std::for_each(m_matrix, &m_matrix[m_nRows - 1] + 1, [value](DVector &vec) + { + vec.SubNum(value); + }); +} + +void DMatrix::AddVec(DVector const &dvec, ORIENT orientation) +{ + if (orientation == ORIENT::ROW) + { + CheckEqualSize(dvec.Size(), m_nCols, "AddVec: " + ERROR_SIZE); + // почему capture принимает по ссылке, хотя в сигнатуре const? + std::for_each(m_matrix, &m_matrix[m_nRows - 1] + 1, [&dvec](DVector &vec) + { + vec += dvec; + }); + } + else + { + CheckEqualSize(dvec.Size(), m_nRows, "AddVec: " + ERROR_SIZE); + for (size_t i = 0; i < dvec.Size(); ++i) + { + m_matrix[i].AddNum(dvec[i]); + } + } +} + +void DMatrix::SubVec(DVector const &dvec, ORIENT orientation) +{ + if (orientation == ORIENT::ROW) + { + CheckEqualSize(dvec.Size(), m_nCols, "SubVec: " + ERROR_SIZE); + std::for_each(m_matrix, &m_matrix[m_nRows - 1] + 1, [&dvec](DVector &vec) + { + vec -= dvec; + }); + } + else + { + CheckEqualSize(dvec.Size(), m_nRows, "SubVec: " + ERROR_SIZE); + for (size_t i = 0; i < dvec.Size(); ++i) + { + m_matrix[i].SubNum(dvec[i]); + } + } +} + +DMatrix DMatrix::T() const +{ + CheckEqualSize(m_nRows, m_nCols, "Transponse: " + ERROR_RECT_MATRIX); + DMatrix dmatRes(m_nRows, m_nCols); + for (size_t i = 0; i < m_nRows; ++i) + { + for (size_t j = 0; j < m_nCols; ++j) + { + dmatRes[j][i] = m_matrix[i][j]; + } + } + return dmatRes; +} + +double DMatrix::Det() const +{ + CheckEqualSize(m_nRows, m_nCols, "Determinant: " + ERROR_RECT_MATRIX); + if (m_nRows == 2) + { + return m_matrix[0][0] * m_matrix[1][1] - m_matrix[0][1] * m_matrix[1][0]; + } + else + { + double res = 0; + DMatrix *dmats = nullptr; + try + { + dmats = new DMatrix[m_nCols](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + // определитель матрицы через разложение по первой строке + for (size_t i = 0; i < m_nCols; ++i) + { + dmats[i] = *this; + dmats[i].EraseByIndex(0, ORIENT::ROW); + dmats[i].EraseByIndex(i, ORIENT::COL); + int8_t sign = i % 2 == 0 ? 1 : -1; + res += sign * m_matrix[0][i] * dmats[i].Det(); + } + delete[] dmats; + return res; + } +} + +double DMatrix::Minor(size_t iIndex, size_t jIndex) const +{ + DMatrix dmat = *this; + dmat.EraseByIndex(iIndex, ORIENT::ROW); + dmat.EraseByIndex(jIndex, ORIENT::COL); + return dmat.Det(); +} + +DMatrix DMatrix::Adj() +{ + DMatrix adjMat(m_nRows, m_nCols); + for (size_t i = 0; i < m_nRows; ++i) + { + for (size_t j = 0; j < m_nCols; ++j) + { + int8_t sign = (i + j) % 2 == 0 ? 1 : -1; + adjMat[i][j] = sign * Minor(i, j); + } + } + return adjMat; +} + +DMatrix DMatrix::Inv() +{ + double det = Det(); + if (det == 0) + { + return DMatrix{}; + } + DMatrix invMat = (1 / det) * Adj().T(); + return invMat; +} + +void DMatrix::EraseByIndex(size_t index, ORIENT orientation) +{ + if (m_nRows == 0) + { + throw std::runtime_error("EraseByIndex: " + ERROR_RANGE); + } + if (orientation == ORIENT::ROW) + { + if (index > m_nRows - 1) + { + throw std::runtime_error("EraseByIndex: " + ERROR_RANGE); + } + for (size_t i = index; i < m_nRows - 1; ++i) + { + m_matrix[i] = m_matrix[i + 1]; + } + --m_nRows; + if (m_nRows == 0) + { + delete[] m_matrix; + Clear(); + } + } + else + { + if (index > m_nCols - 1) + { + throw std::runtime_error("EraseByIndex: " + ERROR_RANGE); + } + for (size_t i = 0; i < m_nRows; ++i) + { + m_matrix[i].Erase(&m_matrix[i][index]); + } + m_nCols--; + if (m_nCols == 0) + { + delete[] m_matrix; + Clear(); + } + } +} + +DMatrix DMatrix::operator()(size_t begin, size_t end, int step, uint8_t sliceType) const +{ + if (sliceType == SLICE::ROW) + { + return SliceRow(begin, end, step); + } + else + { + return SliceCol(begin, end, step); + } +} + +DMatrix DMatrix::SliceRow(size_t begin, size_t end, int step) const +{ + if (begin >= end) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + if (begin >= m_nRows || end > m_nRows) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + DMatrix dmatSlice; + if (step > 0) + { + for (int i = begin; i < (int)end; i += step) + { + dmatSlice.PushRowBack(m_matrix[i]); + } + } + else if (step < 0) + { + for (int i = end - 1; i >= (int)begin; i += step) + { + dmatSlice.PushRowBack(m_matrix[i]); + } + } + return dmatSlice; +} + +DMatrix DMatrix::SliceCol(size_t begin, size_t end, int step) const +{ + if (begin >= end) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + if (begin >= m_nCols || end > m_nCols) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + DMatrix dmatSlice; + if (step > 0) + { + for (int i = begin; i < (int)end; i += step) + { + dmatSlice.PushColBack(GetCol(i)); + } + } + else if (step < 0) + { + for (int i = end - 1; i >= (int)begin; i += step) + { + dmatSlice.PushColBack(GetCol(i)); + } + } + return dmatSlice; +} + +void DMatrix::grow() +{ + size_t new_capacity = std::max(1, static_cast(m_capacity * 2)); + DVector *new_matrix = nullptr; + try + { + new_matrix = new DVector[new_capacity](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + std::copy(m_matrix, &m_matrix[m_nRows - 1] + 1, new_matrix); + delete[] m_matrix; + m_matrix = new_matrix; + m_capacity = new_capacity; +} + + +void Print(DMatrix const &matrix, std::string const &msg) +{ + if (!msg.empty()) + { + std::cout << msg << std::endl; + } + for (size_t i = 0; i < matrix.nRows(); ++i) + { + Print(matrix[i]); + } +} + +std::ostream &operator<<(std::ostream &out, DMatrix const &matrix) +{ + Print(matrix); + return out; +} diff --git a/matrix_lib/src/dmatrix_operators.cpp b/matrix_lib/src/dmatrix_operators.cpp new file mode 100644 index 0000000..28e7196 --- /dev/null +++ b/matrix_lib/src/dmatrix_operators.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "dmatrix.h" + +extern const std::string ERROR_EMPTY; +extern const std::string ERROR_SIZE; + +extern void CheckEqualSize(size_t size1, size_t size2, std::string const &msgError); + +// --------------------------------------------------- +// value *= matrix + +DMatrix &operator*=(double value, DMatrix &matrix) +{ + return matrix *= value; +} + +DMatrix &operator/=(double value, DMatrix &matrix) +{ + return matrix /= value; +} + +// --------------------------------------------------- +// value * matrix + +DMatrix operator*(double value, DMatrix matrix) +{ + return matrix *= value; +} + +DMatrix operator/(double value, DMatrix matrix) +{ + return matrix /= value; +} + +// --------------------------------------------------- +// matrix += matrix + +DMatrix &operator+=(DMatrix &left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator+= :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator+= :" + ERROR_SIZE); + for (size_t i = 0; i < left.nRows(); ++i) + { + left[i] += right[i]; + } + return left; +} + +DMatrix &operator-=(DMatrix &left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator-= :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator-= :" + ERROR_SIZE); + for (size_t i = 0; i < left.nRows(); ++i) + { + left[i] -= right[i]; + } + return left; +} + +DMatrix &operator*=(DMatrix &left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator*= :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator*= :" + ERROR_SIZE); + for (size_t i = 0; i < left.nRows(); ++i) + { + left[i] *= right[i]; + } + return left; +} + +DMatrix &operator/=(DMatrix &left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator/= :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator/= :" + ERROR_SIZE); + for (size_t i = 0; i < left.nRows(); ++i) + { + left[i] /= right[i]; + } + return left; +} + +// --------------------------------------------------- +// matrix + matrix + +DMatrix operator+(DMatrix left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator+ :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator+ :" + ERROR_SIZE); + return left += right; +} + +DMatrix operator-(DMatrix left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator- :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator-:" + ERROR_SIZE); + return left -= right; +} + +DMatrix operator*(DMatrix left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator* :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator* :" + ERROR_SIZE); + return left *= right; +} + +DMatrix operator/(DMatrix left, DMatrix const &right) +{ + CheckEqualSize(left.nRows(), right.nRows(), "operator/ :" + ERROR_SIZE); + CheckEqualSize(left.nCols(), right.nCols(), "operator/ :" + ERROR_SIZE); + return left /= right; +} + +// --------------------------------------------------- +// matrix *= value + +DMatrix &operator*=(DMatrix &matrix, double value) +{ + for (size_t i = 0; i < matrix.nRows(); ++i) + { + matrix[i] *= value; + } + return matrix; +} + +DMatrix &operator/=(DMatrix &matrix, double value) +{ + for (size_t i = 0; i < matrix.nRows(); ++i) + { + matrix[i] /= value; + } + return matrix; +} + +// --------------------------------------------------- +// matrix * value + +DMatrix operator*(DMatrix matrix, double value) +{ + return matrix *= value; +} + +DMatrix operator/(DMatrix matrix, double value) +{ + return matrix /= value; +} + +// -------------------------------------------------------- diff --git a/matrix_lib/src/dvector.cpp b/matrix_lib/src/dvector.cpp new file mode 100644 index 0000000..f077155 --- /dev/null +++ b/matrix_lib/src/dvector.cpp @@ -0,0 +1,344 @@ +#include + +#include "stdafx.h" +#include "dvector.h" +#include "dmatrix.h" + +extern const std::string ERROR_SIZE; +extern const std::string ERROR_RANGE; +extern const std::string ERROR_EMPTY; + +extern void CheckEqualSize(size_t size1, size_t size2, std::string const &msgError); + +void Print(DVector const &dvector, std::string const &message) +{ + if (!message.empty()) + { + std::cout << message << std::endl; + } + for (size_t i = 0; i < dvector.Size(); ++i) + { + std::cout << dvector[i] << " "; + } + std::cout << std::endl; +} + +DVector::DVector(size_t size, double fill_value) : m_size(size), m_capacity(size) +{ + try + { + m_array = new double[m_capacity](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + if (fill_value) + { + Fill(fill_value); + } +} + +DVector::DVector(std::initializer_list const &init_list) : m_size(init_list.size()), m_capacity(init_list.size()) +{ + try + { + m_array = new double[init_list.size()](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + std::copy(init_list.begin(), init_list.end(), m_array); +} + +DVector::DVector(DVector const &other) +{ + if (m_array) + { + delete[] m_array; + } + try + { + m_array = new double[other.m_capacity](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + m_capacity = other.m_capacity; + m_size = other.m_size; + std::copy(other.m_array, &other.m_array[other.m_size - 1] + 1, m_array); +} + +DVector::DVector(DVector &&other) : m_array(nullptr), m_capacity(0), m_size(0) +{ + Swap(other); +} + +DVector::DVector(const double *begin, const double *end) +{ + ptrdiff_t length = std::distance(begin, end); + try + { + m_array = new double[length](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + m_size = m_capacity = length; + std::copy(begin, end, m_array); +} + +DVector::~DVector() +{ + if (m_array) + { + delete[] m_array; + } + m_array = nullptr; +} + +DVector &DVector::operator=(DVector other) +{ + Swap(other); + return *this; +} + +double const &DVector::operator[](size_t index) const +{ + if (index > m_size - 1) + { + throw std::runtime_error("operator[] : " + ERROR_RANGE); + } + return m_array[index]; +} + +double &DVector::operator[](size_t index) +{ + if (index > m_size - 1) + { + throw std::runtime_error("operator[] : " + ERROR_RANGE); + } + return m_array[index]; +} + +const double *DVector::CBegin() const +{ + return m_array; +} + +// указатель на элемент после последнего +const double *DVector::CEnd() const +{ + return &m_array[m_size - 1] + 1; +} + +void DVector::Fill(double fill_value) +{ + std::fill_n(m_array, m_size, fill_value); +} + +void DVector::Swap(DVector &other) +{ + std::swap(m_array, other.m_array); + std::swap(m_capacity, other.m_capacity); + std::swap(m_size, other.m_size); +} + +bool DVector::Empty() const +{ + return m_size == 0; +} + +size_t DVector::Size() const +{ + return m_size; +} + +size_t DVector::Capacity() const +{ + return m_capacity; +} + +void DVector::Clear() +{ + if (m_array) + { + delete[] m_array; + } + m_array = nullptr; + m_size = 0; + m_capacity = 0; +} + +double DVector::Front() const +{ + if (Empty()) + { + throw std::runtime_error("From: " + ERROR_EMPTY); + } + return m_array[0]; +} + +double DVector::Back() const +{ + if (Empty()) + { + throw std::runtime_error("Back: " + ERROR_EMPTY); + } + return m_array[m_size - 1]; +} + + +void DVector::PushBack(double value) +{ + if (m_size == m_capacity) + { + grow(); + } + m_array[m_size++] = value; +} + +// возвращает элемент, следующий за удаленным либо предыдущий, если удаленный был последним +double *DVector::Erase(double *it_value) +{ + ptrdiff_t diff = reinterpret_cast(it_value) - reinterpret_cast(m_array); + if (!it_value || it_value < m_array || it_value > &m_array[m_size - 1] || diff % sizeof(double) != 0) + { + return nullptr; + } + for (double *it = it_value; it < &m_array[m_size - 1]; it++) + { + *it = *(it + 1); + } + --m_size; + return it_value == &m_array[m_size - 1] + 1 ? it_value - 1 : it_value; + +} + +void DVector::PopBack() +{ + if (Empty()) + { + throw std::runtime_error("PopBack: " + ERROR_EMPTY); + } + --m_size; +} + + +double *DVector::Find(double value) const +{ + double *it_end = &m_array[m_size - 1] + 1; + double *result = std::find(m_array, it_end, value); + if (result == it_end) + { + return nullptr; + } + return result; +} + +void DVector::AddNum(double value) +{ + // можно сделать Begin() и End() приватными + std::for_each(m_array, &m_array[m_size - 1] + 1, [value](double &elem){ + elem += value; + }); +} + +void DVector::SubNum(double value) +{ + std::for_each(m_array, &m_array[m_size - 1] + 1, [value](double &elem){ + elem -= value; + }); +} + + +// подразуемевается что вектор-строка умножается на матрицу +// vector[1xN] * matrix[NxM] = vector[1xM] +DVector DVector::Dot(DMatrix const &matrix) const +{ + CheckEqualSize(m_size, matrix.nRows(), ERROR_SIZE); + + DVector dvecRes(matrix.nCols()); + for (size_t i = 0; i < matrix.nCols(); ++i) + { + double elem_i_j = 0; + for (size_t j = 0; j < m_size; ++j) + { + elem_i_j += matrix[j][i] * m_array[j]; + } + dvecRes[i] = elem_i_j; + } + return dvecRes; +} + +// скалярное произведение векторов, результат - число +double DVector::Dot(DVector const &other) const +{ + CheckEqualSize(m_size, other.Size(), "Dot:" + ERROR_SIZE); + double dotProduct = 0; + for (size_t i = 0; i < m_size; ++i) + { + dotProduct += m_array[i] * other[i]; + } + return dotProduct; +} + +DVector DVector::operator()(size_t begin, size_t end, int step) const +{ + if (begin >= end) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + if (begin >= m_size || end > m_size) + { + throw std::runtime_error("operator(): " + ERROR_RANGE); + } + DVector dvecSlice; + if (step > 0) + { + for (int i = begin; i < (int)end; i += step) + { + dvecSlice.PushBack(m_array[i]); + } + } + else if (step < 0) + { + for (int i = end - 1; i >= (int)begin; i += step) + { + dvecSlice.PushBack(m_array[i]); + } + } + return dvecSlice; +} + +void DVector::grow() +{ + size_t new_capacity = std::max(1, static_cast(m_capacity * 2)); + double *new_array = nullptr; + try + { + new_array = new double[new_capacity](); + } + catch(const std::bad_alloc& e) + { + std::cout << "Allocation failed: " << e.what() << std::endl; + throw; + } + std::copy(m_array, &m_array[m_size - 1] + 1, new_array); + delete[] m_array; + m_array = new_array; + m_capacity = new_capacity; +} + +std::ostream &operator<<(std::ostream &out, DVector const &vector) +{ + Print(vector); + return out; +} diff --git a/matrix_lib/src/dvector_operators.cpp b/matrix_lib/src/dvector_operators.cpp new file mode 100644 index 0000000..d917283 --- /dev/null +++ b/matrix_lib/src/dvector_operators.cpp @@ -0,0 +1,114 @@ +#include + +#include "stdafx.h" +#include "dvector.h" +#include "dmatrix.h" + +extern const std::string ERROR_SIZE; + +extern void CheckEqualSize(size_t size1, size_t size2, std::string const &msgError); + +// --------------------------------------------------- +// vector += vector + +DVector &operator+=(DVector &left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator+= :" + ERROR_SIZE); + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] += right[i]; + } + return left; +} + +DVector &operator-=(DVector &left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator-= :" + ERROR_SIZE); + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] -= right[i]; + } + return left; +} + +DVector &operator*=(DVector &left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator*= :" + ERROR_SIZE); + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] *= right[i]; + } + return left; +} + +DVector &operator/=(DVector &left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator/= :" + ERROR_SIZE); + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] /= right[i]; + } + return left; +} + +// --------------------------------------------------- +// vector + vector + +DVector operator+(DVector left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator+ :" + ERROR_SIZE); + return left += right; +} + +DVector operator-(DVector left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator- :" + ERROR_SIZE); + return left -= right; +} + +DVector operator*(DVector left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator* :" + ERROR_SIZE); + return left *= right; +} + +DVector operator/(DVector left, DVector const &right) +{ + CheckEqualSize(left.Size(), right.Size(), "operator/ :" + ERROR_SIZE); + return left /= right; +} + + +// --------------------------------------------------- +// vector *= value + +DVector &operator*=(DVector &left, double value) +{ + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] *= value; + } + return left; +} + +DVector &operator/=(DVector &left, double value) +{ + for (size_t i = 0; i < left.Size(); ++i) + { + left[i] /= value; + } + return left; +} + +// --------------------------------------------------- +// vector * value + +DVector operator*(DVector left, double value) +{ + return left *= value; +} + +DVector operator/(DVector left, double value) +{ + return left /= value; +} diff --git a/matrix_lib/src/stdafx.cpp b/matrix_lib/src/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/matrix_lib/src/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/matrix_lib/tests/CMakeLists.txt b/matrix_lib/tests/CMakeLists.txt new file mode 100644 index 0000000..58b797d --- /dev/null +++ b/matrix_lib/tests/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.0.0) +project(tests) + +set(CMAKE_CXX_STANDARD 17) + +file(GLOB SOURCES *.cpp) + +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +message("MATRIX_LIB_INCLUDE_DIRS = ${MATRIX_LIB_INCLUDE_DIRS}") +message("MATRIX_LIB_LIBRARIES = ${MATRIX_LIB_LIBRARIES}") + +target_link_libraries(${PROJECT_NAME} PRIVATE ${MATRIX_LIB_LIBRARIES} GTest::GTest gtest_main) + +target_include_directories(${PROJECT_NAME} PUBLIC ${MATRIX_LIB_INCLUDE_DIRS}) + +gtest_discover_tests(${PROJECT_NAME}) \ No newline at end of file diff --git a/matrix_lib/tests/test_matrix.cpp b/matrix_lib/tests/test_matrix.cpp new file mode 100644 index 0000000..fe3be04 --- /dev/null +++ b/matrix_lib/tests/test_matrix.cpp @@ -0,0 +1,750 @@ +#include +#include + +#include "dmatrix.h" + +const double EPS = 0.000001; + +bool CompareMatrices(DMatrix const &matrix, std::initializer_list> const &init_list) +{ + if ((matrix.nRows() != init_list.size()) || (matrix.nCols() != init_list.begin()->size())) + { + return false; + } + for (size_t i = 0; i < matrix.nRows(); ++i) + { + for (size_t j = 0; j < matrix.nCols(); ++j) + { + if (std::abs(matrix[i][j] - *((init_list.begin() + i)->begin() + j)) > EPS) + { + return false; + } + } + } + return true; +} + +bool CompareVectors(DVector const &dvec, std::initializer_list const &init_list) +{ + if (dvec.Size() != init_list.size()) + { + return false; + } + for (size_t i = 0; i < dvec.Size(); ++i) + { + if (std::abs(dvec[i] - *(init_list.begin() + i)) > EPS) + { + return false; + } + } + return true; +} + + +TEST(TestCreateDMatrix, TestConstructorsSpecifySizeAndInitList) +{ + DMatrix dmat1(3, 10, 4); + DMatrix dmat2{ + {1, 2, 3}, + {4, 5, 6} + }; + EXPECT_EQ(dmat1.nCols(), 10); + EXPECT_EQ(dmat1.nRows(), 3); + EXPECT_EQ(dmat1[0][0], 4); + EXPECT_EQ(dmat1[2][9], 4); + + EXPECT_EQ(dmat2.nCols(), 3); + EXPECT_EQ(dmat2.nRows(), 2); + EXPECT_EQ(dmat2[0][0], 1); + EXPECT_EQ(dmat2[0][1], 2); + EXPECT_EQ(dmat2[1][2], 6); +} + +TEST(TestCreateDMatrix, TestConstructorsDVectorsAndCopy) +{ + const size_t ndvecs = 2; + DVector *dvecs = new DVector[ndvecs]{ DVector(2, 7), DVector{3, 8} }; + + DMatrix dmat1(dvecs, ndvecs); + DMatrix dmat2(dmat1); + + EXPECT_NE(dmat1[0].CBegin(), dmat2[0].CBegin()); + EXPECT_EQ(dmat1[1][0], dmat2[1][0]); + + EXPECT_EQ(dmat1[1][0], 3); + EXPECT_EQ(dmat1[0][1], 7); +} + +TEST(TestCreateDMatrix, TestConstructorsSpecifyRowOrCol) +{ + DMatrix dmat(DVector{2, 4, 6, 8}); // ORIENT::ROW by default + + EXPECT_EQ(dmat.nRows(), 1); + EXPECT_EQ(dmat.nCols(), 4); + + EXPECT_TRUE(CompareMatrices(dmat, { + {2, 4, 6, 8} + })); + + dmat = DMatrix(DVector{2, 4, 6, 8}, ORIENT::COL); + EXPECT_EQ(dmat.nRows(), 4); + EXPECT_EQ(dmat.nCols(), 1); + EXPECT_TRUE(CompareMatrices(dmat, { + {2}, + {4}, + {6}, + {8} + })); +} + +TEST(TestCreateDMatrix, TestConstructorsTemplate) +{ + DMatrix dmat = DMatrix::Create<2, 3>(3); + EXPECT_EQ(dmat.nRows(), 2); + EXPECT_EQ(dmat.nCols(), 3); + + EXPECT_TRUE(CompareMatrices(dmat, { + {3, 3, 3}, + {3, 3, 3} + })); + + EXPECT_THROW((DMatrix::Create<0, 3>()), std::runtime_error); + EXPECT_THROW((DMatrix::Create<1, 0>(10)), std::runtime_error); + + DMatrix dmat2 = DMatrix::Create<0, 0>(); + EXPECT_EQ(dmat2.nRows(), 0); + EXPECT_EQ(dmat2.nCols(), 0); +} + +TEST(TestCreateDMatrix, TestOperatorAssign) +{ + DMatrix dmat1{ + {1, 2, 3}, + {4, 5, 6}, + }; + + DMatrix dmat2 = dmat1; + EXPECT_NE(dmat1[0].CBegin(), dmat2[0].CBegin()); + EXPECT_EQ(dmat1.nRows(), dmat2.nRows()); + EXPECT_EQ(dmat1.nCols(), dmat2.nCols()); + + EXPECT_EQ(dmat1[1][0], dmat2[1][0]); + EXPECT_EQ(dmat1[1][0], 4); +} + +TEST(TestErrorHandling, TestAccessAndCreateNotRectangleMatrix) +{ + EXPECT_THROW( DMatrix dmat({ {1, 2}, {4, 5, 6} }), std::runtime_error); + EXPECT_THROW( DMatrix dmat({ {1}, {} }), std::runtime_error); + + DVector *dvecs = new DVector[2]{ DVector{1, 2}, DVector{3, 4, 5} }; + EXPECT_THROW(DMatrix dmat(dvecs, 2), std::runtime_error); + + DMatrix dmat{ {1, 2, 3}, {4, 5, 6} }; + + delete[] dvecs; + + EXPECT_THROW(dmat[2][1], std::runtime_error); + EXPECT_THROW(dmat[0][5], std::runtime_error); +} + +TEST(TestFunctionalityDMatrix, TestPushPopRow) +{ + DMatrix dmat; + EXPECT_THROW(dmat.PopRowBack(), std::runtime_error); + dmat.PushRowBack({1, 2, 3}); + + DVector dvec{4, 5, 6}; + dmat.PushRowBack(dvec); + + EXPECT_THROW(dmat.PushRowBack({20, 20}), std::runtime_error); + DVector dvec2{4, 4, 4, 4}; + EXPECT_THROW(dmat.PushRowBack(dvec2), std::runtime_error); + + + EXPECT_TRUE(CompareMatrices(dmat, { + {1, 2, 3}, + {4, 5, 6} + })); + + EXPECT_EQ(dmat.nCols(), 3); + EXPECT_EQ(dmat.nRows(), 2); + + dmat.PushRowBack({0, 0, 7}); + dmat.PushRowBack({0, 0, 7}); + dmat.PushRowBack({0, 0, 7}); + + + dmat.PopRowBack(); + dmat.PopRowBack(); + EXPECT_EQ(dmat.nRows(), 3); + + EXPECT_EQ(dmat.Capacity(), 8); +} + +// размер матрицы зависит от ее содержимого +// без содержимого матрица не имеет размера и может в будущем принимать любые формы +TEST(TestFunctionalityDMatrix, TestPushPopRow_WithChangeDimensity) +{ + DMatrix dmat; + dmat.PushRowBack({0, 0, 7}); + dmat.PushRowBack({0, 0, 7}); + dmat.PushRowBack({0, 0, 7}); + EXPECT_EQ(dmat.nCols(), 3); + EXPECT_EQ(dmat.nRows(), 3); + EXPECT_EQ(dmat.Capacity(), 4); + + dmat.PopRowBack(); + dmat.PopRowBack(); + dmat.PopRowBack(); + EXPECT_THROW(dmat.PopRowBack(), std::runtime_error); + + dmat.PushRowBack({1}); + + EXPECT_EQ(dmat.nCols(), 1); + EXPECT_EQ(dmat.nRows(), 1); + EXPECT_EQ(dmat.Capacity(), 1); +} + +TEST(TestFunctionalityDMatrix, TestPushPopCol) +{ + DMatrix dmat; + dmat.PushColBack({1, 2, 3}); + EXPECT_EQ(dmat.nCols(), 1); + EXPECT_EQ(dmat.nRows(), 3); + + EXPECT_THROW(dmat.PushColBack({1, 1}), std::runtime_error); + + DVector dvec{4, 5, 6}; + dmat.PushColBack(dvec); + + EXPECT_EQ(dmat.nCols(), 2); + EXPECT_EQ(dmat.nRows(), 3); + + dmat.PopColBack(); + dmat.PopColBack(); + EXPECT_THROW(dmat.PopColBack(), std::runtime_error); + + dmat.PushColBack({1, 2}); + dmat.PushColBack({3, 4}); + + EXPECT_EQ(dmat.nCols(), 2); + EXPECT_EQ(dmat.nRows(), 2); +} + +TEST(TestFunctionalityDMatrix, TestPushPopMix) +{ + DMatrix dmat{ + {1, 2, 3}, + {4, 5, 6}, + }; + dmat.PushColBack({7, 7}); + dmat.PushRowBack({8, 8, 8, 100}); + + EXPECT_TRUE(CompareMatrices(dmat, { + {1, 2, 3, 7}, + {4, 5, 6, 7}, + {8, 8, 8, 100}, + })); + + + EXPECT_EQ(dmat.nCols(), 4); + EXPECT_EQ(dmat.nRows(), 3); + + dmat.PopColBack(); + dmat.PopRowBack(); + dmat.PopRowBack(); + + EXPECT_EQ(dmat.nCols(), 3); + EXPECT_EQ(dmat.nRows(), 1); + + dmat.PopRowBack(); + EXPECT_THROW(dmat.PopRowBack(), std::runtime_error); + + EXPECT_EQ(dmat.nCols(), 0); + EXPECT_EQ(dmat.nRows(), 0); + + dmat = {{1, 2, 3, 4, 5, 6, 7}}; + dmat.PushColBack({8}); + + EXPECT_TRUE(CompareMatrices(dmat, { + {1, 2, 3, 4, 5, 6, 7, 8} + })); + +} + +TEST(TestFunctionalityDMatrix, TestGetRowColDiag) +{ + // GetCol + DMatrix dmat{ + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10} + }; + + DVector dvec1 = dmat.GetCol(0); + EXPECT_EQ(dvec1[0], dmat[0][0]); + EXPECT_EQ(dvec1[1], dmat[1][0]); + + dvec1[0] = 100; + EXPECT_NE(dvec1[0], dmat[0][0]); + + dmat[1][0] = 600; + EXPECT_NE(dvec1[1], dmat[1][0]); + + DVector dvec2 = dmat.GetCol(4); + EXPECT_EQ(dvec2.Size(), 2); + EXPECT_EQ(dvec2[0], dmat[0][4]); + EXPECT_EQ(dvec2[1], dmat[1][4]); + + // GetRow + DVector dvec3 = dmat.GetRow(1); + EXPECT_EQ(dvec3.Size(), 5); + EXPECT_EQ(dvec3[0], dmat[1][0]); + EXPECT_EQ(dvec3[4], dmat[1][4]); + + // GetDiag + DVector dvec4 = dmat.GetDiag(); + EXPECT_EQ(dvec4.Size(), 2); + EXPECT_EQ(dvec4[0], dmat[0][0]); + EXPECT_EQ(dvec4[1], dmat[1][1]); + + DMatrix dmat2{ + {10}, + {20} + }; + + DVector dvec5 = dmat2.GetDiag(); + EXPECT_EQ(dvec5.Size(), 1); + EXPECT_EQ(dvec5[0], dmat2[0][0]); + + DMatrix dmat3{ + {3, 5}, + {7, 9} + }; + + DVector dvec6 = dmat3.GetDiag(); + EXPECT_EQ(dvec6.Size(), 2); + EXPECT_EQ(dvec6[0], dmat3[0][0]); + EXPECT_EQ(dvec6[1], dmat3[1][1]); +} + +TEST(TestFunctionalityDMatrix, TestArithmeticOperators) +{ + DMatrix dmat1{ + {1, 2, 3}, + {6, 7, 8} + }; + DMatrix dmat2{ + {11, 12, 13}, + {14, 15, 16} + }; + DMatrix dmatErr1{ + {1, 1}, + {1, 1} + }; + + DMatrix dmatErr2; + DMatrix dmatErr3{ + {2, 4, 6} + }; + DMatrix dmat3 = dmat1 * 2 + dmat2 / 1; + + EXPECT_THROW(dmat3 + dmatErr1, std::runtime_error); + EXPECT_THROW(dmat3 *= dmatErr1, std::runtime_error); + EXPECT_THROW(dmat3 -= dmatErr2, std::runtime_error); + EXPECT_THROW(dmat3 / dmatErr3, std::runtime_error); + + EXPECT_TRUE(CompareMatrices(dmat3, { + {13, 16, 19}, + {26, 29, 32} + })); + +} + +TEST(TestFunctionalityDMatrix, TestDotProductOfMatrices) +{ + DMatrix dmat1{ + {1, 2, 3}, + {4, 5, 6} + }; + + DMatrix dmat2{ + {2, 3}, + {4, 5}, + {6, 7} + }; + + DMatrix dmatRes = dmat1.Dot(dmat2); + EXPECT_EQ(dmatRes.nRows(), 2); + EXPECT_EQ(dmatRes.nCols(), 2); + + EXPECT_TRUE(CompareMatrices(dmatRes, { {28, 34}, {64, 79} })); +} + + +TEST(TestFunctionalityDMatrix, TestDotProductBetweenMatrixAndVector) +{ + // matrix * vector + + DMatrix dmat1{ + {1, 2, 3}, + {4, 5, 6} + }; + + DVector dvec1{1, 2, 3}; + DVector dvecErr{1, 2, 3, 4}; + + EXPECT_TRUE(CompareVectors(dmat1.Dot(dvec1), { 14, 32 })); + EXPECT_THROW(dmat1.Dot(dvecErr), std::runtime_error); + + // vector * matrix + + DVector dvec2{1, 2}; + EXPECT_TRUE(CompareVectors(dvec2.Dot(dmat1), { 9, 12, 15 })); + + DMatrix dmatErr{ + {1, 2}, + {3, 4}, + {5, 6} + }; + EXPECT_THROW(dvec2.Dot(dmatErr), std::runtime_error); +} + +TEST(TestFunctionalityDMatrix, TestAddSubVectorAndNum) +{ + DMatrix dmat{ + {1, 2, 3}, + {4, 5, 6} + }; + + dmat.AddNum(1); + EXPECT_TRUE(CompareMatrices(dmat, { + {2, 3, 4}, + {5, 6, 7} + })); + + DVector dvec1{10, 15}; + dmat.AddVec(dvec1, ORIENT::COL); + EXPECT_TRUE(CompareMatrices(dmat, { + {12, 13, 14}, + {20, 21, 22} + })); + + DVector dvec2{10, 10, 0}; + dmat.SubVec(dvec2, ORIENT::ROW); // but ORIENT::ROW by default + EXPECT_TRUE(CompareMatrices(dmat, { + {2, 3, 14}, + {10, 11, 22} + })); + + EXPECT_THROW(dmat.AddVec({1}, ORIENT::ROW), std::runtime_error); + EXPECT_THROW(dmat.AddVec({1}, ORIENT::COL), std::runtime_error); +} + +TEST(TestFunctionalityDMatrix, TestTransponse) +{ + DMatrix dmat{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + DMatrix dmatT = dmat.T(); + + EXPECT_TRUE(CompareMatrices(dmatT, { + {1, 4, 7}, + {2, 5, 8}, + {3, 6, 9} + })); + + EXPECT_THROW(DMatrix({{1, 2}}).T(), std::runtime_error); +} + +TEST(TestFunctionalityDMatrix, TestEraseByIndexRow) +{ + // Erase row + DMatrix dmat{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + dmat.EraseByIndex(0); + EXPECT_TRUE(CompareMatrices(dmat, { + {4, 5, 6}, + {7, 8, 9} + })); + dmat.EraseByIndex(1); + EXPECT_TRUE(CompareMatrices(dmat, { + {4, 5, 6} + })); + + + EXPECT_EQ(dmat.nRows(), 1); + EXPECT_EQ(dmat.nCols(), 3); + + EXPECT_THROW(dmat.EraseByIndex(1), std::runtime_error); + dmat.EraseByIndex(0); + EXPECT_THROW(dmat.EraseByIndex(0), std::runtime_error); + + EXPECT_EQ(dmat.nRows(), 0); + EXPECT_EQ(dmat.nCols(), 0); + EXPECT_EQ(dmat.Capacity(), 0); +} + +TEST(TestFunctionalityDMatrix, TestEraseByIndexCol) +{ + DMatrix dmat = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + dmat.EraseByIndex(1, ORIENT::COL); + EXPECT_TRUE(CompareMatrices(dmat, { + {1, 3}, + {4, 6}, + {7, 9} + })); + + EXPECT_EQ(dmat.nRows(), 3); + EXPECT_EQ(dmat.nCols(), 2); + + dmat.EraseByIndex(0, ORIENT::COL); + EXPECT_TRUE(CompareMatrices(dmat, { + {3}, + {6}, + {9} + })); + + dmat.EraseByIndex(0, ORIENT::COL); + EXPECT_TRUE(dmat.Empty()); + + EXPECT_THROW(dmat.EraseByIndex(0, ORIENT::COL), std::runtime_error); + EXPECT_THROW(dmat.EraseByIndex(200, ORIENT::COL), std::runtime_error); +} + +TEST(TestFunctionalityDMatrix, TestDeterminant) +{ + DMatrix dmat1 = { + {3, 5}, + {1, 5} + }; + EXPECT_EQ(dmat1.Det(), 10); + + DMatrix dmat2 = { + {3, 5, 2}, + {1, 5, 6}, + {5, 7, 2} + }; + EXPECT_EQ(dmat2.Det(), 8); + + DMatrix dmat3 = { + {3, 5, 2, 7}, + {1, 5, 6, 4}, + {5, 7, 2, 9}, + {5, 7, 2, 5} + }; + EXPECT_EQ(dmat3.Det(), -32); +} + +TEST(TestFunctionalityDMatrix, TestInvertibleMatrix) +{ + DMatrix dmat1 = { + {1, -2, 1}, + {2, 1, -1}, + {3, 2, -2} + }; + + DMatrix dmatInv = dmat1.Inv(); + + EXPECT_EQ(dmatInv.nRows(), 3); + EXPECT_EQ(dmatInv.nCols(), 3); + + EXPECT_TRUE(CompareMatrices(dmatInv, { + {0, 2, -1}, + {-1, 5, -3}, + {-1, 8, -5} + })); + + DMatrix dmat2 = { + {1, 1, 1, -1}, + {1, -2, 1, -1}, + {1, 1, 3, 1}, + {1, 1, 1, -4} + }; + + dmatInv = dmat2.Inv(); + + EXPECT_TRUE(CompareMatrices(dmatInv, { + {11./6, 1./3, -1./2, -2./3}, + {1./3, -1./3, 0, 0}, + {-5./6, 0, 1./2, 1./3}, + {1./3, 0, 0, -1./3} + })); + + DMatrix dmatErr1 = { + {1, -2, 1}, + {2, 1, -1}, + }; + + // определитель = 0, обратной матрицы не существует + DMatrix dmatErr2 = { + {1, 2}, + {2, 4}, + }; + + EXPECT_THROW(dmatErr1.Inv(), std::runtime_error); + EXPECT_TRUE(dmatErr2.Inv().Empty()); +} + +TEST(TestFunctionalityDMatrix, TestSliceOperatorRow) +{ + DMatrix dmat = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15}, + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25} + }; + + DMatrix dmatSlice1 = dmat(1, 3); + EXPECT_TRUE(CompareMatrices(dmatSlice1, { + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15} + })); + + DMatrix dmatSlice2 = dmat(0, dmat.nRows()); + + EXPECT_TRUE(CompareMatrices(dmatSlice2, { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15}, + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25} + })); + + DMatrix dmatSlice3 = dmat(0, dmat.nRows(), 2); + + EXPECT_TRUE(CompareMatrices(dmatSlice3, { + {1, 2, 3, 4, 5}, + {11, 12, 13, 14, 15}, + {21, 22, 23, 24, 25} + })); + + DMatrix dmatSlice4 = dmat(0, dmat.nRows(), -2); + + EXPECT_TRUE(CompareMatrices(dmatSlice4, { + {21, 22, 23, 24, 25}, + {11, 12, 13, 14, 15}, + {1, 2, 3, 4, 5} + })); + + EXPECT_THROW(dmat(4, 1), std::runtime_error); + EXPECT_THROW(dmat(2, 2), std::runtime_error); + EXPECT_THROW(dmat(100, 103), std::runtime_error); + EXPECT_THROW(dmat(0, 100), std::runtime_error); + + DMatrix dmatSlice5 = dmat(0, 4, 0); + EXPECT_TRUE(dmatSlice5.Empty()); +} + +TEST(TestFunctionalityDMatrix, TestSliceOperatorCol) +{ + DMatrix dmat = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15}, + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25} + }; + + DMatrix dmatSlice1 = dmat(1, 3, 1, COL); + EXPECT_TRUE(CompareMatrices(dmatSlice1, { + {2, 3 }, + {7, 8 }, + {12, 13 }, + {17, 18 }, + {22, 23 } + })); + + DMatrix dmatSlice2 = dmat(1, 4, 2, COL); + + EXPECT_TRUE(CompareMatrices(dmatSlice2, { + {2, 4 }, + {7, 9 }, + {12, 14 }, + {17, 19 }, + {22, 24 } + })); + + DMatrix dmatSlice3 = dmat(0, dmat.nCols(), -1, COL); + + EXPECT_TRUE(CompareMatrices(dmatSlice3, { + {5, 4, 3, 2, 1}, + {10, 9, 8, 7, 6}, + {15, 14, 13, 12, 11}, + {20, 19, 18, 17, 16}, + {25, 24, 23, 22, 21} + })); + + DMatrix dmatSlice4 = dmat(4, dmat.nCols(), 1, COL); + + EXPECT_TRUE(CompareMatrices(dmatSlice4, { + {5}, + {10}, + {15}, + {20}, + {25} + })); +} + +TEST(TestFunctionalityDMatrix, TestSliceOperatorRowColMix) +{ + DMatrix dmat = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15}, + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25} + }; + + DMatrix dmatSlice1 = dmat(0, 3)(1, 3); + + EXPECT_TRUE(CompareMatrices(dmatSlice1, { + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15} + })); + + // ROW = 0, COL = 1 + DMatrix dmatSlice2 = dmat(0, 4, 1, 0)(1, 3, 1, 1); + EXPECT_TRUE(CompareMatrices(dmatSlice2, { + {2, 3}, + {7, 8}, + {12, 13}, + {17, 18} + })); + + DMatrix dmatSlice3 = dmat(1, 2, 1, COL)(3, 4); + EXPECT_TRUE(CompareMatrices(dmatSlice3, { + {17} + })); +} + +TEST(TestFunctionalityDMatrix, TestSliceCallMethod) +{ + DMatrix dmat = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15}, + {16, 17, 18, 19, 20}, + {21, 22, 23, 24, 25} + }; + + DMatrix dmatSlice1 = dmat.SliceRow(0, 4).SliceCol(1, 5, 2); + + EXPECT_TRUE(CompareMatrices(dmatSlice1, { + {2, 4 }, + {7, 9 }, + {12, 14}, + {17, 19} + })); +} diff --git a/matrix_lib/tests/test_matrix_compile.cpp b/matrix_lib/tests/test_matrix_compile.cpp new file mode 100644 index 0000000..3ffe295 --- /dev/null +++ b/matrix_lib/tests/test_matrix_compile.cpp @@ -0,0 +1,117 @@ +#include +#include + +#include "dmatrix_compile.h" + +extern bool CompareMatrices(DMatrix const &matrix, std::initializer_list> const &init_list); + +extern bool CompareVectors(DVector const &dvec, std::initializer_list const &init_list); + + +TEST(TestFunctionalityDMatrixCompile, TestMatrixArithmeticOperator) +{ + auto dmatSum = DMatrixCompile<3, 2>(1) + DMatrixCompile<3, 2>(2); + + /* + Размерности не совпадают: + DMatrixCompile<3, 2>(1) + DMatrixCompile<10, 10>(2); + ERROR: static assertion failed + */ + + EXPECT_TRUE(CompareMatrices(dmatSum, { + {3, 3}, + {3, 3}, + {3, 3} + })); +} + +TEST(TestFunctionalityDMatrixCompile, TestMatrixAccess) +{ + + DMatrixCompile<3, 2> dmatCompile1(3); + + EXPECT_EQ(dmatCompile1.At<0>()[0], 3); + + /* + Доступ к строке за пределами матрицы: + dmatCompile1.At<5>()[0] + ERROR: static assertion failed + */ + + dmatCompile1.At<0>()[0] = 5; + EXPECT_EQ(dmatCompile1.At<0>()[0], 5); +} + +TEST(TestFunctionalityDMatrixCompile, TestMatrixDotProduct) +{ + + DMatrixCompile<3, 2> dmat1(1); + DMatrixCompile<2, 5> dmat2(1); + + // 3x2 * 2x5 - 3x5 + auto dmatProd = dmat1.Dot(dmat2); + + /* + Несоответствие размеров при матричном умножении: + DMatrixCompile<3, 3> dmat1(1); + ... + ERROR: static assertion failed + */ + + EXPECT_TRUE(CompareMatrices(dmatProd, { + {2, 2, 2, 2, 2}, + {2, 2, 2, 2, 2}, + {2, 2, 2, 2, 2} + })); + +} + +TEST(TestFunctionalityDMatrixCompile, TestMatrixTransponse) +{ + + DMatrixCompile<3, 3> dmat1(1); + dmat1.At<0>()[2] = 0; + + EXPECT_TRUE(CompareMatrices(dmat1.GetDynamicMatrix(), { + {1, 1, 0}, + {1, 1, 1}, + {1, 1, 1} + })); + + auto dmatTransponse = dmat1.T(); + + /* + Транспонирование неквадратной матрицы: + DMatrixCompile<5, 3> dmat1(1); + ... + ERROR: static assertion failed + */ + + EXPECT_TRUE(CompareMatrices(dmatTransponse, { + {1, 1, 1}, + {1, 1, 1}, + {0, 1, 1} + })); +} + +TEST(TestFunctionalityDMatrixCompile, TestGetRowAndCol) +{ + + DMatrixCompile<3, 4> dmat1(1); + dmat1.At<2>()[1] = 0; + + auto vec1 = dmat1.GetRow<2>(); + + EXPECT_TRUE(CompareVectors(vec1, { 1, 0, 1, 1 })); + + vec1 = dmat1.GetCol<1>(); + + /* + Изъятие строки или столбца за пределами матрицы: + dmat1.GetRow<5>(); + dmat1.GetCol<5>(); + ERROR: static assertion failed + */ + + EXPECT_TRUE(CompareVectors(vec1, { 1, 1, 0 })); +} diff --git a/matrix_lib/tests/test_vector.cpp b/matrix_lib/tests/test_vector.cpp new file mode 100644 index 0000000..76fca98 --- /dev/null +++ b/matrix_lib/tests/test_vector.cpp @@ -0,0 +1,200 @@ +#include + +#include "dvector.h" + +extern bool CompareVectors(DVector const &dvec, std::initializer_list const &init_list); + +TEST(TestCreateDVector, TestDefaultConstructor) +{ + DVector dvec1; + dvec1.PushBack(1); + dvec1.PushBack(2); + dvec1.PushBack(3); + dvec1.PushBack(4); + EXPECT_EQ(dvec1.Size(), 4); + EXPECT_EQ(dvec1.Capacity(), 4); + EXPECT_TRUE(CompareVectors(dvec1, {1, 2, 3, 4})); +} + +TEST(TestCreateDVector, TestConstructorInitializerList) +{ + DVector dvec2{5, 6, 7, 8, 9}; + EXPECT_EQ(dvec2.Size(), 5); + EXPECT_EQ(dvec2.Capacity(), 5); + EXPECT_TRUE(CompareVectors(dvec2, {5, 6, 7, 8, 9})); +} + +TEST(TestCreateDVector, TestConstructorInputSizeAndFillValue) +{ + DVector dvec3(10, 99); + EXPECT_EQ(dvec3.Size(), 10); + EXPECT_EQ(dvec3.Capacity(), 10); + EXPECT_TRUE(CompareVectors(dvec3, {99, 99, 99, 99, 99, 99, 99, 99, 99, 99})); +} + +TEST(TestCreateDVector, TestCopyConstructor) +{ + DVector dvec1{5, 6, 7, 8, 9}; + DVector dvec4(dvec1); + EXPECT_NE(dvec4.CBegin(), dvec1.CBegin()); + EXPECT_EQ(dvec4.Size(), dvec1.Size()); + EXPECT_EQ(dvec4.Capacity(), dvec1.Capacity()); + EXPECT_EQ(dvec4[0], dvec1[0]); + EXPECT_EQ(dvec4[3], dvec1[3]); +} + +TEST(TestCreateDVector, TestConstructorPointersBeginEnd) +{ + const size_t size = 5; + double vec[size] = {1, 2, 3, 4, 5}; + // + 1, т.к. end - указатель на элемент после последнего + DVector dvec(vec, &vec[size - 2] + 1); + EXPECT_EQ(dvec.Size(), size - 1); + EXPECT_EQ(dvec[size - 2], vec[size - 2]); + EXPECT_THROW(dvec[size - 1], std::runtime_error); + + DVector dvec1(dvec.CBegin(), dvec.CEnd()); + EXPECT_NE(dvec1.CBegin(), dvec.CBegin()); + EXPECT_EQ(dvec1.Size(), dvec.Size()); + + EXPECT_TRUE(CompareVectors(dvec1, {1, 2, 3, 4})); +} + +TEST(TestCreateDVector, TestImplicitConstructor) +{ + auto func = [](DVector const &dvec) -> size_t + { return dvec.Size(); }; + + EXPECT_EQ(func({1, 2, 3}), 3); + // func(5); ERROR: no match for call +} + +TEST(TestCreateDVector, TestOperatorAssign) +{ + DVector dvec1 = {5, 6, 7, 8, 9}; + DVector dvec2({1, 2, 3}); + dvec1 = dvec2; + EXPECT_NE(dvec1.CBegin(), dvec2.CBegin()); + EXPECT_EQ(dvec1.Size(), dvec2.Size()); + EXPECT_EQ(dvec1.Capacity(), dvec2.Capacity()); + + dvec1[0] = 10; + dvec1.PushBack(30); + + dvec2 = std::move(dvec1); + EXPECT_EQ(dvec1.CBegin(), nullptr); + EXPECT_EQ(dvec1.Size(), 0); + EXPECT_EQ(dvec1.Capacity(), 0); + + EXPECT_EQ(dvec2.Size(), 4); + + dvec2 = DVector(20, 5); + EXPECT_EQ(dvec2.Size(), 20); + + dvec2.Clear(); + dvec1 = dvec2; + EXPECT_EQ(dvec1.Size(), 0); + EXPECT_EQ(dvec2.Size(), 0); +} + +TEST(TestErrorHandling, TestAccess) +{ + DVector dvec1{5, 6, 7, 8, 9}; + EXPECT_EQ(dvec1[1], 6); + dvec1[1] = 60; + EXPECT_EQ(dvec1[1], 60); + + EXPECT_THROW(dvec1[5], std::runtime_error); + EXPECT_THROW(dvec1[5] = 12, std::runtime_error); + EXPECT_THROW(dvec1[-1], std::runtime_error); + + const DVector dvec2(dvec1); + EXPECT_EQ(dvec2[1], 60); + // dvec2[1] = 600; ERROR: assignment of read-only location +} + +TEST(TestFunctionalityDVector, TestFindErase) +{ + DVector dvec{5, 6, 7, 8, 9}; + DVector dvec1(dvec); + EXPECT_EQ(dvec1.Find(6), &dvec1[1]); + EXPECT_EQ(dvec1.Find(10), nullptr); + + EXPECT_EQ(dvec1.Erase(dvec1.Find(10)), nullptr); + + EXPECT_EQ(dvec1.Erase(dvec1.Find(7)), &dvec1[2]); + EXPECT_EQ(dvec1.Size(), 4); + + dvec1 = dvec; + EXPECT_EQ(dvec1.Erase(dvec1.Find(9)), &dvec1[3]); + EXPECT_EQ(dvec1.Size(), 4); +} + +TEST(TestFunctionalityDVector, TestPushPopFrontBack) +{ + DVector dvec; + EXPECT_THROW(dvec.PopBack(), std::runtime_error); + dvec.PushBack(1); + dvec.PushBack(2); + dvec.PushBack(3); + EXPECT_EQ(dvec.Back(), 3); + EXPECT_EQ(dvec.Front(), 1); +} + +TEST(TestFunctionalityDVector, TestArithmeticOperators) +{ + DVector dvec1{1, 2, 3}; + DVector dvec2{5, 6, 7}; + (dvec1 *= dvec2) += dvec1; + + EXPECT_TRUE(CompareVectors(dvec1, {10, 24, 42})); + + DVector dvec3{2, 4, 8}; + DVector dvec4{4, 5, 6}; + + DVector dvec5 = (dvec3 * 2 * dvec4) + dvec3 / 2; + + /* + { 4 * 4; 8 * 5; 16 * 6 } + + + { 1; 2; 4 } + = + { 17; 42; 100 } + */ + + dvec5 *= 2; + + EXPECT_EQ(dvec5[0], 17 * 2); + EXPECT_EQ(dvec5[1], 42 * 2); + EXPECT_EQ(dvec5[2], 100 * 2); + + DVector dvec6{3, 4}; + DVector dvec7{10, 2}; + double resDot = dvec6.Dot(dvec7); + EXPECT_EQ(resDot, 38); +} + +TEST(TestFunctionalityDVector, TestAddSubNum) +{ + DVector dvec{1, 2, 3}; + dvec.AddNum(2); + EXPECT_TRUE(CompareVectors(dvec, {3, 4, 5})); + dvec.SubNum(3); + EXPECT_TRUE(CompareVectors(dvec, {0, 1, 2})); +} + +TEST(TestFunctionalityDVector, TestSliceOperator) +{ + DVector dvec{1, 2, 3, 4, 5, 6, 7}; + + EXPECT_TRUE(CompareVectors(dvec(0, 5), {1, 2, 3, 4, 5})); + EXPECT_TRUE(CompareVectors(dvec(0, dvec.Size(), -1), {7, 6, 5, 4, 3, 2, 1})); + + EXPECT_TRUE(CompareVectors(dvec(3, 4), {4})); + + EXPECT_THROW(dvec(4, 4), std::runtime_error); + EXPECT_THROW(dvec(5, 4), std::runtime_error); + EXPECT_THROW(dvec(1, 400), std::runtime_error); + EXPECT_THROW(dvec(20, 22), std::runtime_error); + +}