diff options
| -rw-r--r-- | a5/Makefile | 4 | ||||
| -rw-r--r-- | a5/exercise.tex | 3 | ||||
| -rw-r--r-- | a5/imatrix.h | 6 | ||||
| -rw-r--r-- | a5/imatrix_nm.h | 80 | ||||
| -rw-r--r-- | a5/matrix.cc | 222 | ||||
| -rw-r--r-- | a5/matrix.h | 136 | 
6 files changed, 349 insertions, 102 deletions
diff --git a/a5/Makefile b/a5/Makefile index 282dfa9..6869fd8 100644 --- a/a5/Makefile +++ b/a5/Makefile @@ -1,4 +1,4 @@  all: matrix -matrix: matrix.cc Makefile imatrix.h imatrix_nm.h matrix.h -	g++ -O2 -Wall -Werror -Wextra -Wconversion -std=c++20 $< -o $@ +matrix: matrix.cc Makefile imatrix.h imatrix_nm.h matrix.h Makefile +	g++ -g -O2 -Wall -Werror -Wextra -Wconversion -std=c++20 $< -o $@ diff --git a/a5/exercise.tex b/a5/exercise.tex index 6889950..9491afe 100644 --- a/a5/exercise.tex +++ b/a5/exercise.tex @@ -60,4 +60,7 @@ needed. This I think is actually not that good.  Adding the requirement to each of the member functions might be  better. + +I actually made some errors in the matrix subscripts in the matrix +multiplication/division code, which was shown to me by an assertion.  \end{document} diff --git a/a5/imatrix.h b/a5/imatrix.h index f96cb0c..b13f60d 100644 --- a/a5/imatrix.h +++ b/a5/imatrix.h @@ -119,7 +119,7 @@ public:  				out.m(i,j) = 0;  				for(std::size_t k = 0; k < other.w; ++k)  				{ -					out.m(i,j) += m(i, j) * other.m(k, j); +					out.m(i,j) += m(i,k) * other.m(k,j);  				}  			}  		} @@ -147,7 +147,7 @@ public:  				out.m(i,j) = 0;  				for(std::size_t k = 0; k < other.w; ++k)  				{ -					out.m(i,j) += m(i, j) / other.m(k, j); +					out.m(i,j) += m(i,k) / other.m(k,j);  				}  			}  		} @@ -201,4 +201,6 @@ private:  	std::valarray<int> data; // default initialized to empty  	std::size_t w{}; // default initialized to 0  	std::size_t h{}; // default initialized to 0 + +	friend std::ostream& operator<<(std::ostream& s, const Imatrix& m);  }; diff --git a/a5/imatrix_nm.h b/a5/imatrix_nm.h index 970031a..f69a099 100644 --- a/a5/imatrix_nm.h +++ b/a5/imatrix_nm.h @@ -5,32 +5,32 @@  #include <valarray>  #include <stdexcept> -template<std::size_t W, std::size_t H> +template<std::size_t N, std::size_t M>  class ImatrixNM  {  public:  	ImatrixNM() -		: data(0, W * H) +		: data(0, N * M)  	{  	} -	ImatrixNM(ImatrixNM<W, H>&& other) +	ImatrixNM(ImatrixNM<N, M>&& other)  	{  		*this = std::move(other);  	} -	ImatrixNM(const ImatrixNM<W, H>& other) +	ImatrixNM(const ImatrixNM<N, M>& other)  	{  		*this = other;  	} -	ImatrixNM& operator=(ImatrixNM<W, H>&& other) +	ImatrixNM& operator=(ImatrixNM<N, M>&& other)  	{  		data = std::move(other.data);  		return *this;  	} -	ImatrixNM& operator=(const ImatrixNM<W, H>& other) +	ImatrixNM& operator=(const ImatrixNM<N, M>& other)  	{  		data = other.data;  		return *this; @@ -38,70 +38,70 @@ public:  	int& m(std::size_t x, std::size_t y)  	{ -		if(x >= W || y >= H) throw std::out_of_range("Subscript out of range."); -		return data[x + y * W]; +		if(x >= N || y >= M) throw std::out_of_range("Subscript out of range."); +		return data[x + y * N];  	}  	const int& m(std::size_t x, std::size_t y) const  	{ -		if(x >= W || y >= H) throw std::out_of_range("Subscript out of range."); -		return data[x + y * W]; +		if(x >= N || y >= M) throw std::out_of_range("Subscript out of range."); +		return data[x + y * N];  	} -	ImatrixNM<W, H> operator+(const ImatrixNM<W, H>& other) const +	ImatrixNM<N, M> operator+(const ImatrixNM<N, M>& other) const  	{  		ImatrixNM m(*this);  		m.data += other.data;  		return m;  	} -	ImatrixNM<W, H> operator+(int val) +	ImatrixNM<N, M> operator+(int val)  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data += val;  		return m;  	} -	ImatrixNM<W, H> operator-(const ImatrixNM<W, H>& other) const +	ImatrixNM<N, M> operator-(const ImatrixNM<N, M>& other) const  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data -= other.data;  		return m;  	} -	ImatrixNM <W, H>operator-(int val) +	ImatrixNM <N, M>operator-(int val)  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data -= val;  		return m;  	} -	ImatrixNM<W, H> operator%(const ImatrixNM<W, H>& other) const +	ImatrixNM<N, M> operator%(const ImatrixNM<N, M>& other) const  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data %= other.data;  		return m;  	} -	ImatrixNM<W, H> operator%(int val) +	ImatrixNM<N, M> operator%(int val)  	{  		ImatrixNM m(*this);  		m.data %= val;  		return m;  	} -	template<std::size_t N> -	ImatrixNM<W, N> operator*(const ImatrixNM<H, N>& other) const +	template<std::size_t R> +	ImatrixNM<N, R> operator*(const ImatrixNM<M, R>& other) const  	{ -		ImatrixNM<W, N> out; -		for(std::size_t i = 0; i < W; ++i) +		ImatrixNM<N, R> out; +		for(std::size_t i = 0; i < N; ++i)  		{  			for(std::size_t j = 0; j < N; ++j)  			{  				out.m(i,j) = 0; -				for(std::size_t k = 0; k < H; ++k) +				for(std::size_t k = 0; k < M; ++k)  				{ -					out.m(i,j) += m(i, j) * other.m(k, j); +					out.m(i,j) += m(i,k) * other.m(k,j);  				}  			}  		} @@ -109,25 +109,25 @@ public:  		return out;  	} -	ImatrixNM<W, H> operator*(int val) const +	ImatrixNM<N, M> operator*(int val) const  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data *= val;  		return m;  	} -	template<std::size_t N> -	ImatrixNM<W, N> operator/(const ImatrixNM<H, N>& other) const +	template<std::size_t R> +	ImatrixNM<N, R> operator/(const ImatrixNM<M, R>& other) const  	{ -		ImatrixNM<W, N> out; -		for(std::size_t i = 0; i < W; ++i) +		ImatrixNM<N, R> out; +		for(std::size_t i = 0; i < N; ++i)  		{  			for(std::size_t j = 0; j < N; ++j)  			{  				out.m(i,j) = 0; -				for(std::size_t k = 0; k < H; ++k) +				for(std::size_t k = 0; k < M; ++k)  				{ -					out.m(i,j) += m(i, j) / other.m(k, j); +					out.m(i,j) += m(i,k) / other.m(k,j);  				}  			}  		} @@ -135,9 +135,9 @@ public:  		return out;  	} -	ImatrixNM<W, H> operator/(int val) const +	ImatrixNM<N, M> operator/(int val) const  	{ -		ImatrixNM<W, H> m(*this); +		ImatrixNM<N, M> m(*this);  		m.data /= val;  		return m;  	} @@ -156,9 +156,9 @@ public:  	std::vector<int> Row(std::size_t n) const  	{ -		if(n >= H) throw std::out_of_range("Subscript out of range."); +		if(n >= M) throw std::out_of_range("Subscript out of range.");  		std::vector<int> out; -		for(std::size_t x = 0; x < W; ++x) +		for(std::size_t x = 0; x < N; ++x)  		{  			out.push_back(m(x, n));  		} @@ -167,9 +167,9 @@ public:  	std::vector<int> Column(std::size_t n) const  	{ -		if(n >= W) throw std::out_of_range("Subscript out of range."); +		if(n >= N) throw std::out_of_range("Subscript out of range.");  		std::vector<int> out; -		for(std::size_t y = 0; y < H; ++y) +		for(std::size_t y = 0; y < M; ++y)  		{  			out.push_back(m(n, y));  		} diff --git a/a5/matrix.cc b/a5/matrix.cc index 01744fa..4a34fa0 100644 --- a/a5/matrix.cc +++ b/a5/matrix.cc @@ -2,26 +2,226 @@  #include "imatrix_nm.h"  #include "matrix.h" +#include <iostream> + +template<typename T, std::size_t N, std::size_t M> +std::ostream& operator<<(std::ostream& s, const Matrix<T,N,M>& m) +{ +	for(auto j = 0u; j < M; ++j) +	{ +		for(auto i = 0u; i < N; ++i) +		{ +			s << m.m(i,j) << ' '; +		} +		s << '\n'; +	} +	return s; +} + +template<std::size_t N, std::size_t M> +std::ostream& operator<<(std::ostream& s, const ImatrixNM<N,M>& m) +{ +	for(auto j = 0u; j < M; ++j) +	{ +		for(auto i = 0u; i < N; ++i) +		{ +			s << m.m(i,j) << ' '; +		} +		s << '\n'; +	} +	return s; +} + +std::ostream& operator<<(std::ostream& s, const Imatrix& m) +{ +	for(auto j = 0u; j < m.h; ++j) +	{ +		for(auto i = 0u; i < m.w; ++i) +		{ +			s << m.m(i,j) << ' '; +		} +		s << '\n'; +	} +	return s; +} +  struct Chess_piece  { +	Chess_piece operator*(const Chess_piece&) const {return {};} +	Chess_piece operator/(const Chess_piece&) const {return {};} +	Chess_piece operator+(const Chess_piece&) const {return {};} +	Chess_piece operator-(const Chess_piece&) const {return {};} + +	Chess_piece& operator*=(const Chess_piece&){return *this;} +	Chess_piece& operator/=(const Chess_piece&){return *this;} +	Chess_piece& operator+=(const Chess_piece&){return *this;} +	Chess_piece& operator-=(const Chess_piece&){return *this;} + +	Chess_piece operator*(int) const {return {};} +	Chess_piece operator/(int) const {return {};} +	Chess_piece operator+(int) const {return {};} +	Chess_piece operator-(int) const {return {};} + +	Chess_piece& operator*=(int){return *this;} +	Chess_piece& operator/=(int){return *this;} +	Chess_piece& operator+=(int){return *this;} +	Chess_piece& operator-=(int){return *this;}  }; +std::ostream& operator<<(std::ostream& s, const Chess_piece&) +{ +	s << "Chess_piece"; +	return s; +} + +template<typename M1, typename M2> +void test(M1& m1, M2& m2) +{ +	std::cout << "m1=\n" << m1 << '\n'; +	std::cout << "m2=\n" << m2 << '\n'; + +	{ +		auto m3 = m1 * m2; +		std::cout << "m1*m2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 / m2; +		std::cout << "m1/m2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 + m1; +		std::cout << "m1+m1\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 - m1; +		std::cout << "m1-m1\n" << m3 << '\n'; +	} + +	if constexpr (supports_modulo<M1, M1>) +	{ +		auto m3 = m1 % m1; +		std::cout << "m1%m1\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 * 2; +		std::cout << "m1*2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 / 2; +		std::cout << "m1/2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 + 2; +		std::cout << "m1+2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1 - 2; +		std::cout << "m1-2\n" << m3 << '\n'; +	} + +	if constexpr (supports_modulo<M1, int>) +	{ +		auto m3 = m1 % 2; +		std::cout << "m1%2\n" << m3 << '\n'; +	} + +	{ +		auto m3 = m1; +		m3.Move({0,0}, {1,1}); +		std::cout << "m3.Move (copy assign)\n" << m3 << '\n'; +	} + +	{ +		auto m3(m1); +		auto r = m3.Row(0); +		std::cout << "m3.Row (copy ctor)\n"; +		for(auto i : r) std::cout << i << ' '; +		std::cout << "\n\n"; +	} + +	{ +		auto m3(std::move(m1)); +		auto c = m3.Column(0); +		std::cout << "m3.Column (move ctor)\n"; +		for(auto i : c) std::cout << i << ' '; +		std::cout << "\n\n"; +	} + +	{ +		auto m3 = std::move(m2); +		std::cout << "m3 (move assign)\n" << m3 << '\n'; +	} +} +  int main()  { -	Matrix<double, 3, 5> m1; -	m1 = m1 + 2; -	m1.m(2,2) = 42; -	try +	std::cout << "Imatrix:\n";  	{ -		m1.m(6,6) = 0; +		Imatrix m1(3,2); +		m1.m(0,0) = 1; m1.m(1,0) = 2; m1.m(2,0) = 3; +		m1.m(0,1) = 4; m1.m(1,1) = 5; m1.m(2,1) = 6; + +		Imatrix m2(2,3); +		m2.m(0,0) = 1; m2.m(1,0) = 2; +		m2.m(0,1) = 3; m2.m(1,1) = 4; +		m2.m(0,2) = 5; m2.m(1,2) = 6; + +		test(m1, m2);  	} -	catch(...) + +	std::cout << "ImatrixNM<..,..>:\n";  	{ -		// expected +		ImatrixNM<3, 2> m1; +		m1.m(0,0) = 1; m1.m(1,0) = 2; m1.m(2,0) = 3; +		m1.m(0,1) = 4; m1.m(1,1) = 5; m1.m(2,1) = 6; + +		ImatrixNM<2, 3> m2; +		m2.m(0,0) = 1; m2.m(1,0) = 2; +		m2.m(0,1) = 3; m2.m(1,1) = 4; +		m2.m(0,2) = 5; m2.m(1,2) = 6; + +		test(m1, m2);  	} -	Matrix<double, 5, 2> m2; -	auto m3 = m1 * m2; -	Matrix<Chess_piece, 2, 2> c1; -	auto c2 = c1 + 42; +	std::cout << "Matrix<int, .., ..>:\n"; +	{ +		Matrix<int, 3, 2> m1; +		m1.m(0,0) = 1; m1.m(1,0) = 2; m1.m(2,0) = 3; +		m1.m(0,1) = 4; m1.m(1,1) = 5; m1.m(2,1) = 6; + +		Matrix<int, 2, 3> m2; +		m2.m(0,0) = 1; m2.m(1,0) = 2; +		m2.m(0,1) = 3; m2.m(1,1) = 4; +		m2.m(0,2) = 5; m2.m(1,2) = 6; + +		test(m1, m2); +	} + +	std::cout << "Matrix<double, .., ..>:\n"; +	{ +		Matrix<double, 3, 2> m1; +		m1.m(0,0) = 1; m1.m(1,0) = 2; m1.m(2,0) = 3; +		m1.m(0,1) = 4; m1.m(1,1) = 5; m1.m(2,1) = 6; + +		Matrix<double, 2, 3> m2; +		m2.m(0,0) = 1; m2.m(1,0) = 2; +		m2.m(0,1) = 3; m2.m(1,1) = 4; +		m2.m(0,2) = 5; m2.m(1,2) = 6; + +		test(m1, m2); +	} + +	std::cout << "Matrix<Chess_piece, .., ..>:\n"; +	{ +		Matrix<Chess_piece, 3, 2> m1; +		Matrix<Chess_piece, 2, 3> m2; +		test(m1, m2); +	}  } diff --git a/a5/matrix.h b/a5/matrix.h index af83683..6d880e2 100644 --- a/a5/matrix.h +++ b/a5/matrix.h @@ -4,33 +4,71 @@  #include <utility>  #include <valarray>  #include <stdexcept> +#include <concepts> -template<typename T, std::size_t W, std::size_t H> +template<typename T1, typename T2> +concept supports_modulo = +	requires(T1 t1, T2 t2) +	{ +		t1 % t2; +	}; + +template<typename T> +concept supports_matrix = +	requires(T a, T b, int i) +	{ +		a * b; +		a / b; +		a + b; +		a - b; + +		a * i; +		a / i; +		a + i; +		a - i; + +		a *= b; +		a /= b; +		a += b; +		a -= b; + +		a *= i; +		a /= i; +		a += i; +		a -= i; + +		a = b; +		a = {}; +	}; + + +template<typename T, std::size_t N, std::size_t M> +	requires supports_matrix<T>  class Matrix  {  public:  	Matrix() -		: data(T{}, W * H) +		: data(T{}, N * M)  	{  	} -	Matrix(Matrix<T, W, H>&& other) +	Matrix(Matrix<T, N, M>&& other)  	{  		*this = std::move(other);  	} -	Matrix(const Matrix<T, W, H>& other) +	Matrix(const Matrix<T, N, M>& other)  	{  		*this = other;  	} -	Matrix& operator=(Matrix<T, W, H>&& other) +	Matrix& operator=(Matrix<T, N, M>&& other)  	{  		data = std::move(other.data);  		return *this;  	} -	Matrix& operator=(const Matrix<T, W, H>& other) +	Matrix& operator=(const Matrix<T, N, M>& other)  	{  		data = other.data;  		return *this; @@ -38,70 +76,74 @@ public:  	T& m(std::size_t x, std::size_t y)  	{ -		if(x >= W || y >= H) throw std::out_of_range("Subscript out of range."); -		return data[x + y * W]; +		if(x >= N) throw std::out_of_range("Subscript x out of range."); +		if(y >= M) throw std::out_of_range("Subscript y out of range."); +		return data[x + y * N];  	}  	const T& m(std::size_t x, std::size_t y) const  	{ -		if(x >= W || y >= H) throw std::out_of_range("Subscript out of range."); -		return data[x + y * W]; +		if(x >= N) throw std::out_of_range("Subscript x out of range."); +		if(y >= M) throw std::out_of_range("Subscript y out of range."); +		return data[x + y * N];  	} -	Matrix<T, W, H> operator+(const Matrix<T, W, H>& other) const +	Matrix<T, N, M> operator+(const Matrix<T, N, M>& other) const  	{  		Matrix m(*this);  		m.data += other.data;  		return m;  	} -	Matrix<T, W, H> operator+(T val) +	Matrix<T, N, M> operator+(int val)  	{ -		Matrix<T, W, H> m(*this); -		m.data += val; +		Matrix<T, N, M> m(*this); +		for(auto& d : m.data) d += val;  		return m;  	} -	Matrix<T, W, H> operator-(const Matrix<T, W, H>& other) const +	Matrix<T, N, M> operator-(const Matrix<T, N, M>& other) const  	{ -		Matrix<T, W, H> m(*this); +		Matrix<T, N, M> m(*this);  		m.data -= other.data;  		return m;  	} -	Matrix <T, W, H>operator-(T val) +	Matrix <T, N, M> operator-(int val)  	{ -		Matrix<T, W, H> m(*this); -		m.data -= val; +		Matrix<T, N, M> m(*this); +		for(auto& d : m.data) d -= val;  		return m;  	} -	Matrix<T, W, H> operator%(const Matrix<T, W, H>& other) const +	Matrix<T, N, M> operator%(const Matrix<T, N, M>& other) const +		requires(supports_modulo<T,T>)  	{ -		Matrix<T, W, H> m(*this); +		Matrix<T, N, M> m(*this);  		m.data %= other.data;  		return m;  	} -	Matrix<T, W, H> operator%(T val) +	Matrix<T, N, M> operator%(int val) +		requires(supports_modulo<T,int>)  	{  		Matrix m(*this); -		m.data %= val; +		for(auto& d : m.data) d %= val;  		return m;  	} -	template<std::size_t N> -	Matrix<T, W, N> operator*(const Matrix<T, H, N>& other) const +	template<std::size_t R> +	Matrix<T, N, R> operator*(const Matrix<T, M, R>& other) const  	{ -		Matrix<T, W, N> out; -		for(std::size_t i = 0; i < W; ++i) +		Matrix<T, N, R> out; +		for(std::size_t i = 0; i < N; ++i)  		{ -			for(std::size_t j = 0; j < N; ++j) +			for(std::size_t j = 0; j < R; ++j)  			{  				out.m(i,j) = {}; -				for(std::size_t k = 0; k < H; ++k) +				for(std::size_t k = 0; k < M; ++k)  				{ -					out.m(i,j) += m(i, j) * other.m(k, j); +					out.m(i,j) += m(i,k) * other.m(k,j);  				}  			}  		} @@ -109,25 +151,25 @@ public:  		return out;  	} -	Matrix<T, W, H> operator*(T val) +	Matrix<T, N, M> operator*(int val)  	{ -		Matrix<T, W, H> m(*this); -		m.data *= val; +		Matrix<T, N, M> m(*this); +		for(auto& d : m.data) d *= val;  		return m;  	} -	template<std::size_t N> -	Matrix<T, W, N> operator/(const Matrix<T, H, N>& other) const +	template<std::size_t R> +	Matrix<T, N, R> operator/(const Matrix<T, M, R>& other) const  	{ -		Matrix<T, W, N> out; -		for(std::size_t i = 0; i < W; ++i) +		Matrix<T, N, R> out; +		for(std::size_t i = 0; i < N; ++i)  		{ -			for(std::size_t j = 0; j < N; ++j) +			for(std::size_t j = 0; j < R; ++j)  			{  				out.m(i,j) = {}; -				for(std::size_t k = 0; k < H; ++k) +				for(std::size_t k = 0; k < M; ++k)  				{ -					out.m(i,j) += m(i, j) / other.m(k, j); +					out.m(i,j) += m(i,k) / other.m(k,j);  				}  			}  		} @@ -135,10 +177,10 @@ public:  		return out;  	} -	Matrix<T, W, H> operator/(T val) const +	Matrix<T, N, M> operator/(int val) const  	{ -		Matrix<T, W, H> m(*this); -		m.data /= val; +		Matrix<T, N, M> m(*this); +		for(auto& d : m.data) d /= val;  		return m;  	} @@ -156,9 +198,9 @@ public:  	std::vector<T> Row(std::size_t n) const  	{ -		if(n >= H) throw std::out_of_range("Subscript out of range."); +		if(n >= M) throw std::out_of_range("Subscript out of range.");  		std::vector<T> out; -		for(std::size_t x = 0; x < W; ++x) +		for(std::size_t x = 0; x < N; ++x)  		{  			out.push_back(m(x, n));  		} @@ -167,9 +209,9 @@ public:  	std::vector<T> Column(std::size_t n) const  	{ -		if(n >= W) throw std::out_of_range("Subscript out of range."); +		if(n >= N) throw std::out_of_range("Subscript out of range.");  		std::vector<T> out; -		for(std::size_t y = 0; y < H; ++y) +		for(std::size_t y = 0; y < M; ++y)  		{  			out.push_back(m(n, y));  		}  | 
