
#include <iostream>                               // MPI
#include <mpi.h>
#include <vector>
using namespace std;


void DebugVector(vector<double> const& xin, MPI_Comm const& icomm)
{
  int myrank, numprocs;
   
   MPI_Comm_rank(icomm, &myrank);                          
   MPI_Comm_size(icomm, &numprocs);
   int selectedProcess = 0;
    while(selectedProcess >= 0 && selectedProcess < numprocs)
    {
      if(myrank == 0)
      {
        cout << "Enter process number: " << endl;
        cin >> selectedProcess;

      }

      MPI_Bcast(&selectedProcess, 1, MPI_INT, 0, icomm);
      MPI_Barrier(icomm);
      if(myrank == selectedProcess )
      {
        for(double x : xin)
        {
          cout << x << " ";
        }
        cout << endl;
      }
      MPI_Barrier(icomm);

    }
}

double par_scalar(const vector<double>& x, const vector<double>& y, MPI_Comm icomm) {
 
    double local = 0.0;
    for (unsigned int i = 0; i < x.size(); ++i) {
        local += x[i] * y[i];
    }

    double sum = 0.0;
    MPI_Allreduce(&local, &sum, 1, MPI_DOUBLE, MPI_SUM, icomm);

    return sum;
}

void E7(const vector<double>& x, MPI_Comm comm,
                             double& min, double& max)
{
    double local_min = x[0];
    double local_max = x[0];

    for (double x_i : x) {
        if (x_i < local_min)
        {
          local_min = x_i;
        } 
        if (x_i> local_max) 
        {
          local_max = x_i;
        }

    }

    MPI_Allreduce(&local_min, &min, 1, MPI_DOUBLE, MPI_MIN, comm);
    MPI_Allreduce(&local_max, &max, 1, MPI_DOUBLE, MPI_MAX, comm);
}


int main(int argc, char *argv[])
{
    MPI_Comm icomm = MPI_COMM_WORLD;
    MPI_Init(&argc, &argv);
    int myrank, numprocs;
    //numprocs = 1;    // delete this line when uncommenting the next line
   MPI_Comm_rank(icomm, &myrank);                          // my MPI-rank
   MPI_Comm_size(icomm, &numprocs);

   if (0==myrank) {
     cout << "\n There are " << numprocs << " processes running.\n \n";
   }


    if (0==myrank) cout << endl;

    unsigned int n=20;
    vector<double> x(n);
    vector<double> y(n);
    double x_i;
    
    for(unsigned int i=0; i<n; i++)
    {
      x_i = myrank*100+(i%5)*10+i;
      x[i] = x_i;

      if(myrank == 0 && i == 0)
      {
        y[i] = 0.0;
      }
      else{
        y[i] = 1.0/x_i;
      }
      
    }

    DebugVector(x,icomm);

    double result = par_scalar(x,y,icomm);

    if(myrank == 0)
    {
       cout << "result scalar: " << result << endl;
    }
   
    double min, max;
    E7(x,icomm,min,max);
    if(myrank == 0)
    {
       cout << "min: " << min << ", max: "<< max << endl;
    }


    
    MPI_Alltoall(MPI_IN_PLACE, 0, MPI_DOUBLE, x.data(), 5, MPI_DOUBLE, icomm);
    DebugVector(x,icomm);

    MPI_Finalize();
    return 0;
}


