#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[])
{
    MPFMatrix my_mpfmat_ans[10], my_mpfmat1[10], my_mpfmat2[10];
    MPFMatrix mpfmat_ans, mpfmat1, mpfmat2;
    mpf_t tmp;

    long int d_ddim[10];
    long int i, j, local_dim;
    int myrank, num_procs;
    double start_wtime, end_wtime;

    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_ddim, DIM, num_procs);

    _mpi_init_mpfmatrix(my_mpfmat_ans, d_ddim, DIM, MPI_COMM_WORLD);
    _mpi_init_mpfmatrix(my_mpfmat1, d_ddim, DIM, MPI_COMM_WORLD);
    _mpi_init_mpfmatrix(my_mpfmat2, d_ddim, DIM, MPI_COMM_WORLD);

    if(myrank == 0)
    {
        mpfmat_ans = init_mpfmatrix(local_dim * num_procs, local_dim * num_procs);
		mpfmat1 = init_mpfmatrix(local_dim * num_procs, local_dim * num_procs);
        mpfmat2 = init_mpfmatrix(local_dim * num_procs, local_dim * num_procs);
        for(i = 0; i < DIM; i++)
            for(j = 0; j < DIM; j++)
            {
                set_mpfmatrix_ij_ui(mpfmat1, i, j, (unsigned long)(i*DIM + j + 1));
                set_mpfmatrix_ij_ui(mpfmat2, i, j, (unsigned long)(DIM * DIM - (i*DIM + j)));
            }
    }

    _mpi_divide_mpfmatrix(my_mpfmat1, d_ddim, mpfmat1, MPI_COMM_WORLD);
    _mpi_divide_mpfmatrix(my_mpfmat2, d_ddim, mpfmat2, MPI_COMM_WORLD);

    if(myrank == 0) start_wtime = MPI_Wtime();
    _mpi_mul_mpfmatrix(my_mpfmat_ans, my_mpfmat1, my_mpfmat2, MPI_COMM_WORLD);
    if(myrank == 0) end_wtime = MPI_Wtime();

    _mpi_collect_mpfmatrix(mpfmat_ans, d_ddim, my_mpfmat_ans, MPI_COMM_WORLD);

    /* free */
    _mpi_free_mpfmatrix(my_mpfmat_ans, MPI_COMM_WORLD);
    _mpi_free_mpfmatrix(my_mpfmat1, MPI_COMM_WORLD);
    _mpi_free_mpfmatrix(my_mpfmat2, MPI_COMM_WORLD);

    if(myrank == 0)
    {
        printf("MPIBNC:\n"); print_mpfmatrix(mpfmat_ans);
        printf("MPI_MUL_TIME: %f\n", end_wtime - start_wtime);

		start_wtime = get_secv();
		mul_mpfmatrix(mpfmat_ans, mpfmat1, mpfmat2);
        end_wtime = get_secv();

        printf("BNC:\n"); print_mpfmatrix(mpfmat_ans);
        printf("BNC_MUL_TIME: %f\n", end_wtime - start_wtime);

        free_mpfmatrix(mpfmat_ans);
        free_mpfmatrix(mpfmat1);
        free_mpfmatrix(mpfmat2);
    }

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

	return EXIT_SUCCESS;
}

