/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef BOOST_TEST_DYN_LINK
#define BOOST_TEST_DYN_LINK
#endif

#define BOOST_TEST_MAIN
#define BOOST_TEST_ALTERNATIVE_INIT_API
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <mia/core/gsl_matrix.hh>

#include <stdexcept>
#include <sstream>

using namespace gsl; 
using namespace ::boost;
using namespace ::boost::unit_test;

using std::ostringstream;
using std::string; 

BOOST_AUTO_TEST_CASE( test_matrix_alloc_and_free ) 
{
	Matrix m(2,3, true); 
	BOOST_CHECK_EQUAL(m.rows(), 2u); 
	BOOST_CHECK_EQUAL(m.cols(), 3u); 

	for(size_t i = 0; i < 2;++i) 
		for(size_t j = 0; j < 2;++j) 
			BOOST_CHECK_EQUAL(m(i,j), 0.0); 

	m.set(1,2, 1.0); 
	BOOST_CHECK_EQUAL(m(1,2), 1.0); 
	
	Matrix k(m); 
	BOOST_CHECK_EQUAL(k.rows(), 2u); 
	BOOST_CHECK_EQUAL(k.cols(), 3u); 
	BOOST_CHECK_EQUAL(k(1,2), 1.0); 

	k.set(0,2, 2.0); 
	BOOST_CHECK_EQUAL(k(0,2), 2.0); 

	m = k; 
	BOOST_CHECK_EQUAL(k(0,2), 2.0); 

}


BOOST_AUTO_TEST_CASE( test_with_init ) 
{
	const double input[6]  = { 
		1,2,3,4,5,6
	}; 
	
	Matrix m(2,3, input); 

	BOOST_CHECK_EQUAL(m(0,0), 1); 
	BOOST_CHECK_EQUAL(m(0,1), 2); 
	BOOST_CHECK_EQUAL(m(0,2), 3); 
	BOOST_CHECK_EQUAL(m(1,0), 4); 
	BOOST_CHECK_EQUAL(m(1,1), 5); 
	BOOST_CHECK_EQUAL(m(1,2), 6); 
	
}

BOOST_AUTO_TEST_CASE( test_copy_ops ) 
{
	Matrix m(2,3, 2.0); 

	BOOST_CHECK_EQUAL(m(0,0), 2.0); 
	BOOST_CHECK_EQUAL(m(0,1), 2.0); 
	BOOST_CHECK_EQUAL(m(0,2), 2.0); 
	BOOST_CHECK_EQUAL(m(1,0), 2.0); 
	BOOST_CHECK_EQUAL(m(1,1), 2.0); 
	BOOST_CHECK_EQUAL(m(1,2), 2.0);

	Matrix m2(2,3, 1.0);

	BOOST_CHECK_EQUAL(m2.rows(), 2u); 
	BOOST_CHECK_EQUAL(m2.cols(), 3u); 
			  
	m2.reset(3, 2, 3.0);

	BOOST_CHECK_EQUAL(m2.rows(), 3u); 
	BOOST_CHECK_EQUAL(m2.cols(), 2u);
	
	BOOST_CHECK_EQUAL(m2(0,0), 3.0); 
	BOOST_CHECK_EQUAL(m2(0,1), 3.0); 
	BOOST_CHECK_EQUAL(m2(1,0), 3.0); 
	BOOST_CHECK_EQUAL(m2(1,1), 3.0); 
	BOOST_CHECK_EQUAL(m2(2,0), 3.0); 
	BOOST_CHECK_EQUAL(m2(2,1), 3.0);

	Matrix *m3 = &m2;
	m3->reset(2, 3, 4.0); 
	
	m2 = *m3;

	BOOST_CHECK_EQUAL(m2.rows(), 2u); 
	BOOST_CHECK_EQUAL(m2.cols(), 3u);
	
	BOOST_CHECK_EQUAL(m2(0,0), 4.0); 
	BOOST_CHECK_EQUAL(m2(0,1), 4.0); 
	BOOST_CHECK_EQUAL(m2(0,2), 4.0); 
	BOOST_CHECK_EQUAL(m2(1,0), 4.0); 
	BOOST_CHECK_EQUAL(m2(1,1), 4.0); 
	BOOST_CHECK_EQUAL(m2(1,2), 4.0);

	m2 = m;

	BOOST_CHECK_EQUAL(m2.rows(), 2u); 
	BOOST_CHECK_EQUAL(m2.cols(), 3u); 
	
	BOOST_CHECK_EQUAL(m2(0,0), 2.0); 
	BOOST_CHECK_EQUAL(m2(0,1), 2.0); 
	BOOST_CHECK_EQUAL(m2(0,2), 2.0); 
	BOOST_CHECK_EQUAL(m2(1,0), 2.0); 
	BOOST_CHECK_EQUAL(m2(1,1), 2.0); 
	BOOST_CHECK_EQUAL(m2(1,2), 2.0);


	const double in_values[6] = {5, 6, 7, 8, 9, 2};
	const double *iv = in_values;

	for (auto im = m.begin(); im != m.end(); ++im, ++iv){
		*im = *iv;
	}
	
	BOOST_CHECK_EQUAL(m(0,0), 5.0); 
	BOOST_CHECK_EQUAL(m(0,1), 6.0); 
	BOOST_CHECK_EQUAL(m(0,2), 7.0); 
	BOOST_CHECK_EQUAL(m(1,0), 8.0); 
	BOOST_CHECK_EQUAL(m(1,1), 9.0); 
	BOOST_CHECK_EQUAL(m(1,2), 2.0);

	auto im = m.begin(); 
	BOOST_CHECK(im == m.begin()); 
}

BOOST_AUTO_TEST_CASE( test_move_ops ) 
{
	Matrix m(2,3, 2.0); 

	BOOST_CHECK_EQUAL(m.rows(), 2u); 
	BOOST_CHECK_EQUAL(m.cols(), 3u);
	BOOST_CHECK_EQUAL(m(0,0), 2.0); 

	Matrix m2(1,1, 1.0);

	BOOST_CHECK_EQUAL(m2.rows(), 1u); 
	BOOST_CHECK_EQUAL(m2.cols(), 1u);
	BOOST_CHECK_EQUAL(m2(0,0), 1.0);

	
	Matrix mm(std::move(m));
	BOOST_REQUIRE(mm.is_valid());
	BOOST_CHECK(!m.is_valid());
	
	BOOST_CHECK_EQUAL(mm.rows(), 2u); 
	BOOST_CHECK_EQUAL(mm.cols(), 3u);
	BOOST_CHECK_EQUAL(mm(0,0), 2.0);

	mm = std::move(m2); 
	BOOST_REQUIRE(mm.is_valid());
	BOOST_CHECK(!m2.is_valid());

	BOOST_CHECK_EQUAL(mm.rows(), 1u); 
	BOOST_CHECK_EQUAL(mm.cols(), 1u);
	BOOST_CHECK_EQUAL(mm(0,0), 1.0);
	

	
}


BOOST_AUTO_TEST_CASE( test_print )
{
	Matrix m;
	ostringstream s_empty;
	s_empty << m;

	BOOST_CHECK_EQUAL(s_empty.str(), string("[(null)]"));

	double v[] = {1, 2}; 
	
	Matrix m12(1, 2, v);
	ostringstream s_simple12;
	s_simple12 << m12;
	BOOST_CHECK_EQUAL(s_simple12.str(), string("[\n  1, 2, \n]"));
	
	const gsl_matrix *gslm = m12;
	Matrix cm12(gslm);
	ostringstream s_csimple12;
	s_csimple12 << cm12;
	BOOST_CHECK_EQUAL(s_csimple12.str(), string("[(const)\n  1, 2, \n]"));
	
	
	
}

BOOST_AUTO_TEST_CASE( test_const_iterator )
{
	const double input[6]  = { 
		1,2,3,4,5,6
	}; 
	
	Matrix m(2,3, input);

	int v = 1; 
	for ( auto i = m.begin(); i != m.end(); ++i, ++v) {
		BOOST_CHECK_EQUAL(*i, v); 
	}

	auto i = m.begin();
	BOOST_CHECK(i == m.begin()); 
}

BOOST_AUTO_TEST_CASE( test_sum )
{
	const double input1[6]  = { 
		1,2,3,4,5,6
	};
	
	const double input2[6]  = { 
		5,4,3,2,1,0
	}; 

	
	Matrix m1(2, 3, input1);
	Matrix m2(2, 3, input2);

	Matrix m3 = m1 + m2;

	BOOST_CHECK_EQUAL(m3.rows(), 2u); 
	BOOST_CHECK_EQUAL(m3.cols(), 3u); 
	
	BOOST_CHECK_EQUAL(m3(0,0), 6.0); 
	BOOST_CHECK_EQUAL(m3(0,1), 6.0); 
	BOOST_CHECK_EQUAL(m3(0,2), 6.0); 
	BOOST_CHECK_EQUAL(m3(1,0), 6.0); 
	BOOST_CHECK_EQUAL(m3(1,1), 6.0); 
	BOOST_CHECK_EQUAL(m3(1,2), 6.0);
	
}

BOOST_AUTO_TEST_CASE( test_sum_fail )
{
	const double input1[6]  = { 
		1,2,3,4,5,6
	};
	
	const double input2[6]  = { 
		5,4,3,2,1,0
	}; 

	
	Matrix m1(2, 3, input1);
	Matrix m2(3, 2, input2);

	// this must fail 
	BOOST_CHECK_THROW(m1 + m2, std::logic_error); 

	
}



BOOST_AUTO_TEST_CASE( test_transpose ) 
{
	const double input[6]  = { 
		1,2,3,4,5,6
	}; 
	
	Matrix m(2,3, input); 
	
	auto t = m.transposed(); 

	BOOST_CHECK_EQUAL(t.rows(), 3); 
	BOOST_CHECK_EQUAL(t.cols(), 2); 
	
	BOOST_CHECK_EQUAL(t(0,0), 1); 
	BOOST_CHECK_EQUAL(t(1,0), 2); 
	BOOST_CHECK_EQUAL(t(2,0), 3); 
	BOOST_CHECK_EQUAL(t(0,1), 4); 
	BOOST_CHECK_EQUAL(t(1,1), 5); 
	BOOST_CHECK_EQUAL(t(2,1), 6); 
	
}


BOOST_AUTO_TEST_CASE( test_matrix_column_covariance ) 
{
	const double input[50]  = { 
		1, 2, 3, 2, 1, 
		2, 1, 2, 5, 2, 
		3, 2, 5, 2, 5, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 

	Matrix m(10, 5, input); 
	
	auto cov = m.column_covariance(); 
	

	BOOST_CHECK_EQUAL(cov.rows(), 5); 
	BOOST_CHECK_EQUAL(cov.cols(), 5); 

	
	const double test[25]  = {  
		1.82222,  -0.11111,  -0.06667,   0.13333,   0.02222,
		-0.11111,   2.72222,   0.00000,  -0.94444,   2.11111,
		-0.06667,   0.00000,   4.26667,  -2.31111,  -0.86667,
		0.13333,  -0.94444,  -2.31111,   4.45556,   2.28889,
		0.02222,   2.11111,  -0.86667,   2.28889,   5.95556
	}; 
	
	auto t = test; 
	for (unsigned  r = 0; r < cov.rows(); ++r) 
		for (unsigned  c = 0; c < cov.cols(); ++c, ++t) {
			if (*t == 0.0) 
				BOOST_CHECK_SMALL(cov(r,c), 1e-10); 
			else 
				BOOST_CHECK_CLOSE(cov(r,c), *t, 0.1); 
		}
}

BOOST_AUTO_TEST_CASE( test_matrix_row_covariance ) 
{
	const double input[50]  = { 
		1, 2, 3, 2, 1, 
		2, 1, 2, 5, 2, 
		3, 2, 5, 2, 5, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 

	Matrix m(10, 5, input); 
	
	auto cov = m.transposed().row_covariance(); 
	

	BOOST_CHECK_EQUAL(cov.rows(), 5); 
	BOOST_CHECK_EQUAL(cov.cols(), 5); 

	
	const double test[25]  = {  
		1.82222,  -0.11111,  -0.06667,   0.13333,   0.02222,
		-0.11111,   2.72222,   0.00000,  -0.94444,   2.11111,
		-0.06667,   0.00000,   4.26667,  -2.31111,  -0.86667,
		0.13333,  -0.94444,  -2.31111,   4.45556,   2.28889,
		0.02222,   2.11111,  -0.86667,   2.28889,   5.95556
	}; 
	
	auto t = test; 
	for (unsigned  r = 0; r < cov.rows(); ++r) 
		for (unsigned  c = 0; c < cov.cols(); ++c, ++t) {
			if (*t == 0.0) 
				BOOST_CHECK_SMALL(cov(r,c), 1e-10); 
			else 
				BOOST_CHECK_CLOSE(cov(r,c), *t, 0.1); 
		}
}

BOOST_AUTO_TEST_CASE( test_matrix_iterator ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 

	const double test_submatrix[21]  = { 
	 	12, 13, 14,
		6, 2, 3, 
		1, 6, 2,
		1, 2, 5,
		2, 3, 8,
		4, 8, 1,
		2, 3, 2
	}; 

	Matrix m(10, 5, input); 

	auto im = m.begin(); 
	for (int i = 0; i < 50; ++i, ++im) {
		BOOST_CHECK_EQUAL(*im, input[i]); 
	}

	// look at a view 

	gsl_matrix_view mv = gsl_matrix_submatrix (m, 2, 1, 7, 3); 
	
	Matrix mvm(&mv.matrix); 
	
	auto imvm = mvm.begin(); 
	for (int i = 0; i < 21; ++i, ++imvm) {
		BOOST_CHECK_EQUAL(*imvm, test_submatrix[i]); 
	}
	
	for(auto im2 = m.begin(); im2 != m.end(); ++im2) 
		*im2 = 1; 

	int k = 0; 
	for (auto imvm2 = mvm.begin(); imvm2 != mvm.end(); ++imvm2, ++k) {
		BOOST_CHECK_EQUAL(*imvm, 1); 
	}
	BOOST_CHECK_EQUAL(k, 21); 

	

}

BOOST_AUTO_TEST_CASE( test_const_matrix_iterator ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 

	const double test_submatrix[21]  = { 
	 	12, 13, 14,
		6, 2, 3, 
		1, 6, 2,
		1, 2, 5,
		2, 3, 8,
		4, 8, 1,
		2, 3, 2
	}; 

	const Matrix m(10, 5, input); 

	auto im = m.begin(); 
	for (int i = 0; i < 50; ++i, ++im) {
		BOOST_CHECK_EQUAL(*im, input[i]); 
	}

	// look at a view 

	gsl_matrix_const_view mv = gsl_matrix_const_submatrix (m, 2, 1, 7, 3); 
	
	const Matrix mvm(&mv.matrix); 
	
	auto imvm = mvm.begin(); 
	for (int i = 0; i < 21; ++i, ++imvm) {
		BOOST_CHECK_EQUAL(*imvm, test_submatrix[i]); 
	}
	

}

BOOST_AUTO_TEST_CASE( test_row_ops ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 
	
	Matrix m(10, 5, input); 

	auto mr = m.get_row(2); 
	BOOST_CHECK_EQUAL(mr.size(), 5); 

	BOOST_CHECK_EQUAL(mr[0], 11); 
	BOOST_CHECK_EQUAL(mr[1], 12); 
	BOOST_CHECK_EQUAL(mr[2], 13); 
	BOOST_CHECK_EQUAL(mr[3], 14); 
	BOOST_CHECK_EQUAL(mr[4], 15); 
	
	auto b = mr.begin(); 

	BOOST_CHECK_EQUAL(*b, 11); ++b; 
	BOOST_CHECK_EQUAL(*b, 12); ++b; 
	BOOST_CHECK_EQUAL(*b, 13); ++b; 
	BOOST_CHECK_EQUAL(*b, 14); ++b; 
	BOOST_CHECK_EQUAL(*b, 15); ++b; 
	
	BOOST_CHECK(b == mr.end()); 

	m.set_row(3, mr); 
	auto mr3 = m.get_row(3); 
	BOOST_CHECK_EQUAL(mr3.size(), 5); 

	BOOST_CHECK_EQUAL(mr3[0], 11); 
	BOOST_CHECK_EQUAL(mr3[1], 12); 
	BOOST_CHECK_EQUAL(mr3[2], 13); 
	BOOST_CHECK_EQUAL(mr3[3], 14); 
	BOOST_CHECK_EQUAL(mr3[4], 15); 
	

}

BOOST_AUTO_TEST_CASE( test_const_row_ops ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 
	
	const Matrix m(10, 5, input); 

	auto mr = m.get_row(2); 
	BOOST_CHECK_EQUAL(mr.size(), 5); 

	BOOST_CHECK_EQUAL(mr[0], 11); 
	BOOST_CHECK_EQUAL(mr[1], 12); 
	BOOST_CHECK_EQUAL(mr[2], 13); 
	BOOST_CHECK_EQUAL(mr[3], 14); 
	BOOST_CHECK_EQUAL(mr[4], 15); 
	
	auto b = mr.begin(); 

	BOOST_CHECK_EQUAL(*b, 11); ++b; 
	BOOST_CHECK_EQUAL(*b, 12); ++b; 
	BOOST_CHECK_EQUAL(*b, 13); ++b; 
	BOOST_CHECK_EQUAL(*b, 14); ++b; 
	BOOST_CHECK_EQUAL(*b, 15); ++b; 
	
	BOOST_CHECK(b == mr.end()); 

	double v_init[5] = {1, 2, 3, 4, 5}; 
	
	Vector v(5, v_init);

	BOOST_CHECK_EQUAL(m.dot_row(9, v), 46.0); 
}


BOOST_AUTO_TEST_CASE( test_const_col_ops ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 
	
	const Matrix m(10, 5, input); 

	auto mr = m.get_column(2); 
	BOOST_CHECK_EQUAL(mr.size(), 10); 

	BOOST_CHECK_EQUAL(mr[0], 3); 
	BOOST_CHECK_EQUAL(mr[1], 8); 
	BOOST_CHECK_EQUAL(mr[2], 13); 
	BOOST_CHECK_EQUAL(mr[3], 2); 
	BOOST_CHECK_EQUAL(mr[4], 6); 

	BOOST_CHECK_EQUAL(mr[5], 2); 
	BOOST_CHECK_EQUAL(mr[6], 3); 
	BOOST_CHECK_EQUAL(mr[7], 8); 
	BOOST_CHECK_EQUAL(mr[8], 3); 
	BOOST_CHECK_EQUAL(mr[9], 2); 	

	
	auto b = mr.begin(); 

	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 8); ++b; 
	BOOST_CHECK_EQUAL(*b, 13); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 
	BOOST_CHECK_EQUAL(*b, 6); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 
	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 8); ++b; 
	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 

	BOOST_CHECK(b == mr.end()); 

}


BOOST_AUTO_TEST_CASE( test_matrix_inv_sqrt ) 
{
	const double input[16]  = {
		0.25, 0.35, 0.1, 0.3, 
		0.35, 0.25, 0.1, 0.3, 
		0.1,  0.3, 0.25, 0.35,  
		0.15, 0.45, 0.2, 0.7
	}; 

	Matrix m(4,4,input); 
	matrix_inv_sqrt(m); 

	       
	BOOST_CHECK_CLOSE(m(0,0), 0.272403, 0.1); 
	BOOST_CHECK_CLOSE(m(0,1), 0.919640, 0.1);
	BOOST_CHECK_CLOSE(m(0,2),-0.228760, 0.1);
	BOOST_CHECK_CLOSE(m(0,3),-0.166518, 0.1);

	BOOST_CHECK_CLOSE(m(1,0), 0.937965, 0.1);
	BOOST_CHECK_CLOSE(m(1,1),-0.201772, 0.1);
	BOOST_CHECK_CLOSE(m(1,2), 0.120041, 0.1);
	BOOST_CHECK_CLOSE(m(1,3), 0.255148, 0.1);

	BOOST_CHECK_CLOSE(m(2,0), -0.079173, 0.1);
	BOOST_CHECK_CLOSE(m(2,1), 0.271666, 0.1);
	BOOST_CHECK_CLOSE(m(2,2), 0.957528, 0.1);
	BOOST_CHECK_CLOSE(m(2,3), 0.055393, 0.1);

	BOOST_CHECK_CLOSE(m(3,0), -0.199374, 0.1);
	BOOST_CHECK_CLOSE(m(3,1), 0.199370, 0.1);
	BOOST_CHECK_CLOSE(m(3,2), -0.128056, 0.1);
	BOOST_CHECK_CLOSE(m(3,3), 0.950843, 0.1);

	
}

BOOST_AUTO_TEST_CASE( test_col_ops ) 
{
	const double input[50]  = { 
		1, 2, 3, 4, 5, 
		6, 7, 8, 9,10, 
	 	11, 12, 13, 14, 15, 
		2, 6, 2, 3, 8, 
		4, 1, 6, 2, 1, 
		4, 1, 2, 5, 5, 
		2, 2, 3, 8, 7, 
		2, 4, 8, 1, 4, 
		1, 2, 3, 2, 2, 
		5, 4, 2, 3, 3
	}; 
	
	Matrix m(10, 5, input); 

	auto mr = m.get_column(2); 
	BOOST_CHECK_EQUAL(mr.size(), 10); 

	BOOST_CHECK_EQUAL(mr[0], 3); 
	BOOST_CHECK_EQUAL(mr[1], 8); 
	BOOST_CHECK_EQUAL(mr[2], 13); 
	BOOST_CHECK_EQUAL(mr[3], 2); 
	BOOST_CHECK_EQUAL(mr[4], 6); 

	BOOST_CHECK_EQUAL(mr[5], 2); 
	BOOST_CHECK_EQUAL(mr[6], 3); 
	BOOST_CHECK_EQUAL(mr[7], 8); 
	BOOST_CHECK_EQUAL(mr[8], 3); 
	BOOST_CHECK_EQUAL(mr[9], 2); 	

	
	auto b = mr.begin(); 

	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 8); ++b; 
	BOOST_CHECK_EQUAL(*b, 13); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 
	BOOST_CHECK_EQUAL(*b, 6); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 
	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 8); ++b; 
	BOOST_CHECK_EQUAL(*b, 3); ++b; 
	BOOST_CHECK_EQUAL(*b, 2); ++b; 

	BOOST_CHECK(b == mr.end()); 

	m.set_column(4, mr); 


	auto mr3 = m.get_column(4); 
	BOOST_CHECK_EQUAL(mr3.size(), 10); 

	BOOST_CHECK_EQUAL(mr3[0], 3); 
	BOOST_CHECK_EQUAL(mr3[1], 8); 
	BOOST_CHECK_EQUAL(mr3[2], 13); 
	BOOST_CHECK_EQUAL(mr3[3], 2); 
	BOOST_CHECK_EQUAL(mr3[4], 6); 

	BOOST_CHECK_EQUAL(mr3[5], 2); 
	BOOST_CHECK_EQUAL(mr3[6], 3); 
	BOOST_CHECK_EQUAL(mr3[7], 8); 
	BOOST_CHECK_EQUAL(mr3[8], 3); 
	BOOST_CHECK_EQUAL(mr3[9], 2); 	
}


