#include <cmath>
#include <mpi.h>

class mpi {
public:
    static void init(int argc, char** argv) {
        MPI_Init(&argc, &argv);

        MPI_Comm_rank(MPI_COMM_WORLD, &m_rank);
        MPI_Comm_size(MPI_COMM_WORLD, &m_size);
    }

    static void finalize() {
        MPI_Finalize();
    }

    static int rank() noexcept { return m_rank; }
    static int size() noexcept { return m_size; }

    static double min(double value) {
        double global_min = 0.0;
        MPI_Allreduce(&value, &global_min, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
        return global_min;
    }

    class master_stream  {
    public:
        template<class T>
        master_stream &operator<<(const T &x) {
            if (m_rank == 0)
                std::cout << x;
            return *this;
        }
    };

    static master_stream cout;
    static char endl;

private:
    static int m_rank;
    static int m_size;
};

int mpi::m_rank = 0;
int mpi::m_size = 1;
mpi::master_stream mpi::cout;
char mpi::endl = '\n';


int main(int argc, char** argv) {
    mpi::init(argc, argv);

    double kek = cos(mpi::rank() + 10.0);

    mpi::cout << "Hello, I'm master process" << mpi::endl;
    mpi::cout << "Min keks is " << mpi::min(kek) << mpi::endl;

    mpi::finalize();
    return 0;
}