#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "mpi.h"

#define USE_GMP
#define USE_MPFR
#include "mpi_bnc.h"

#define DIM 5

int main(int argc, char *argv[])
{
	long int i, j, local_dim;
	MPFMatrix x;
	MPFMatrix local_x[10];
	MPFVector vc, vb;
	MPFVector local_vc, local_vb, local_vtmp;

	long int d_dim[10];
	int myrank, num_procs;

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
	MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

	_mpi_set_bnc_default_prec_decimal(50, MPI_COMM_WORLD);
	commit_mpf(&(MPI_MPF), ceil(50/log10(2.0)), MPI_COMM_WORLD);
	create_mpf_op(&(MPI_MPF_SUM), _mpi_mpf_add, MPI_COMM_WORLD);

	local_dim = _mpi_divide_dim(d_dim, DIM, num_procs);
	if(myrank == 0)
	{
		x = init_mpfmatrix(num_procs * local_dim, num_procs * local_dim);
	
		vb = init_mpfvector(num_procs * local_dim);
		vc = init_mpfvector(num_procs * local_dim);
		for(i = 0; i < DIM; i++)
		{
			set_mpfvector_i_d(vb, i, (double)(DIM - i));
			for(j = 0; j < DIM; j++)
			{
				set_mpfmatrix_ij_d(x, i, j, (double)(i * DIM + j + 1));
			}
		}
	}

	local_vtmp = init_mpfvector(num_procs * local_dim);

	_mpi_init_mpfmatrix(local_x, d_dim, DIM, MPI_COMM_WORLD);

	local_vb = _mpi_init_mpfvector(d_dim, DIM, MPI_COMM_WORLD);
	local_vc = _mpi_init_mpfvector(d_dim, DIM, MPI_COMM_WORLD);

	_mpi_divide_mpfmatrix(local_x, d_dim, x, MPI_COMM_WORLD);

	_mpi_divide_mpfvector(local_vc, d_dim, vc, MPI_COMM_WORLD);
	_mpi_divide_mpfvector(local_vb, d_dim, vb, MPI_COMM_WORLD);

	_mpi_mul_mpfmatrix_mpfvec(local_vc, local_x, local_vb, local_vtmp, MPI_COMM_WORLD);

	_mpi_collect_mpfvector(vc, d_dim, local_vc, MPI_COMM_WORLD);

	_mpi_free_mpfmatrix(local_x, MPI_COMM_WORLD);

	_mpi_free_mpfvector(local_vc);
	_mpi_free_mpfvector(local_vb);

	free_mpfvector(local_vtmp);
	
	if(myrank == 0)
	{
		print_mpfvector(vc);

		free_mpfmatrix(x);
		free_mpfvector(vb);
		free_mpfvector(vc);
	}

	free_mpf_op(&(MPI_MPF_SUM));
	free_mpf(&(MPI_MPF));

	MPI_Finalize();

	return EXIT_SUCCESS;
}

