Index: XIOS/dev/branch_openmp/extern/src_ep_dev/ep_reduce.cpp
===================================================================
--- XIOS/dev/branch_openmp/extern/src_ep_dev/ep_reduce.cpp	(revision 1287)
+++ XIOS/dev/branch_openmp/extern/src_ep_dev/ep_reduce.cpp	(revision 1289)
@@ -9,5 +9,4 @@
 #include <mpi.h>
 #include "ep_declaration.hpp"
-#include "ep_mpi.hpp"
 
 using namespace std;
@@ -28,172 +27,417 @@
   }
 
-  template<typename T>
-  void reduce_max(const T * buffer, T* recvbuf, int count)
-  {
-    transform(buffer, buffer+count, recvbuf, recvbuf, max_op<T>);
-  }
-
-  template<typename T>
-  void reduce_min(const T * buffer, T* recvbuf, int count)
-  {
-    transform(buffer, buffer+count, recvbuf, recvbuf, min_op<T>);
-  }
-
-  template<typename T>
-  void reduce_sum(const T * buffer, T* recvbuf, int count)
-  {
-    transform(buffer, buffer+count, recvbuf, recvbuf, std::plus<T>());
-  }
-
-  int MPI_Reduce_local(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int local_root, MPI_Comm comm)
-  {
-    assert(valid_type(datatype));
-    assert(valid_op(op));
-
-    ::MPI_Aint datasize, lb;
-    ::MPI_Type_get_extent(to_mpi_type(datatype), &lb, &datasize);
-
-    int ep_rank_loc = comm.ep_comm_ptr->size_rank_info[1].first;
-    int num_ep = comm.ep_comm_ptr->size_rank_info[1].second;
-
-    #pragma omp critical (_reduce)
-    comm.my_buffer->void_buffer[ep_rank_loc] = const_cast< void* >(sendbuf);
-
-    MPI_Barrier_local(comm);
-
-    if(ep_rank_loc == local_root)
-    {
-      memcpy(recvbuf, comm.my_buffer->void_buffer[0], datasize * count);
-
-      if(op == MPI_MAX)
-      {
-        if(datasize == sizeof(int))
+
+  int MPI_Reduce_local2(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
+  {
+    if(datatype == MPI_INT)
+    {
+      Debug("datatype is INT\n");
+      return MPI_Reduce_local_int(sendbuf, recvbuf, count, op, comm);
+    }
+    else if(datatype == MPI_FLOAT)
+    {
+      Debug("datatype is FLOAT\n");
+      return MPI_Reduce_local_float(sendbuf, recvbuf, count, op, comm);
+    }
+    else if(datatype == MPI_DOUBLE)
+    {
+      Debug("datatype is DOUBLE\n");
+      return MPI_Reduce_local_double(sendbuf, recvbuf, count, op, comm);
+    }
+    else if(datatype == MPI_LONG)
+    {
+      Debug("datatype is DOUBLE\n");
+      return MPI_Reduce_local_long(sendbuf, recvbuf, count, op, comm);
+    }
+    else if(datatype == MPI_UNSIGNED_LONG)
+    {
+      Debug("datatype is DOUBLE\n");
+      return MPI_Reduce_local_ulong(sendbuf, recvbuf, count, op, comm);
+    }
+    else if(datatype == MPI_CHAR)
+    {
+      Debug("datatype is DOUBLE\n");
+      return MPI_Reduce_local_char(sendbuf, recvbuf, count, op, comm);
+    }
+    else
+    {
+      printf("MPI_Reduce Datatype not supported!\n");
+      exit(0);
+    }
+  }
+
+
+  int MPI_Reduce_local_int(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    int *buffer = comm.my_buffer->buf_int;
+    int *send_buf = static_cast<int*>(const_cast<void*>(sendbuf));
+    int *recv_buf = static_cast<int*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<int>(static_cast<int*>(comm.my_buffer->void_buffer[i]), static_cast<int*>(recvbuf), count);
+          #pragma omp flush
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<int>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<int>);
+          }
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<int>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else if(datasize == sizeof(float))
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
+  }
+
+
+  int MPI_Reduce_local_float(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    float *buffer = comm.my_buffer->buf_float;
+    float *send_buf = static_cast<float*>(const_cast<void*>(sendbuf));
+    float *recv_buf = static_cast<float*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<float>(static_cast<float*>(comm.my_buffer->void_buffer[i]), static_cast<float*>(recvbuf), count);
+          #pragma omp flush
+
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<float>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<float>);
+          }
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<float>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else if(datasize == sizeof(double))
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
+  }
+
+  int MPI_Reduce_local_double(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    double *buffer = comm.my_buffer->buf_double;
+    double *send_buf = static_cast<double*>(const_cast<void*>(sendbuf));
+    double *recv_buf = static_cast<double*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<double>(static_cast<double*>(comm.my_buffer->void_buffer[i]), static_cast<double*>(recvbuf), count);
+          #pragma omp flush
+
+
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<double>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<double>);
+          }
+
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<double>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else if(datasize == sizeof(char))
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
+  }
+
+  int MPI_Reduce_local_long(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    long *buffer = comm.my_buffer->buf_long;
+    long *send_buf = static_cast<long*>(const_cast<void*>(sendbuf));
+    long *recv_buf = static_cast<long*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<char>(static_cast<char*>(comm.my_buffer->void_buffer[i]), static_cast<char*>(recvbuf), count);
+          #pragma omp flush
+
+
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<long>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<long>);
+          }
+
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<long>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else if(datasize == sizeof(long))
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
+  }
+
+  int MPI_Reduce_local_ulong(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    unsigned long *buffer = comm.my_buffer->buf_ulong;
+    unsigned long *send_buf = static_cast<unsigned long*>(const_cast<void*>(sendbuf));
+    unsigned long *recv_buf = static_cast<unsigned long*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<long>(static_cast<long*>(comm.my_buffer->void_buffer[i]), static_cast<long*>(recvbuf), count);
+          #pragma omp flush
+
+
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<unsigned long>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<unsigned long>);
+          }
+
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<unsigned long>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else if(datasize == sizeof(unsigned long))
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
+  }
+
+  int MPI_Reduce_local_char(const void *sendbuf, void *recvbuf, int count, MPI_Op op, MPI_Comm comm)
+  {
+    int my_rank = comm.ep_comm_ptr->size_rank_info[1].first;
+    int num_ep  = comm.ep_comm_ptr->size_rank_info[1].second;
+
+    char *buffer = comm.my_buffer->buf_char;
+    char *send_buf = static_cast<char*>(const_cast<void*>(sendbuf));
+    char *recv_buf = static_cast<char*>(const_cast<void*>(recvbuf));
+
+    for(int j=0; j<count; j+=BUFFER_SIZE)
+    {
+      if( 0 == my_rank )
+      {
+        #pragma omp critical (write_to_buffer)
+        copy(send_buf+j, send_buf+j + min(BUFFER_SIZE, count-j), buffer);
+        #pragma omp flush
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank !=0 )
+      {
+        #pragma omp critical (write_to_buffer)
         {
-          for(int i=1; i<num_ep; i++)
-            reduce_max<unsigned long>(static_cast<unsigned long*>(comm.my_buffer->void_buffer[i]), static_cast<unsigned long*>(recvbuf), count);
+          #pragma omp flush
+
+
+          if(op == MPI_SUM)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, std::plus<char>());
+          }
+
+          else if (op == MPI_MAX)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, max_op<char>);
+          }
+
+
+          else if (op == MPI_MIN)
+          {
+            transform(buffer, buffer+min(BUFFER_SIZE, count-j), send_buf+j, buffer, min_op<char>);
+          }
+
+          else
+          {
+            printf("Supported operation: MPI_SUM, MPI_MAX, MPI_MIN\n");
+            exit(1);
+          }
+          #pragma omp flush
         }
-
-        else printf("datatype Error\n");
-
-      }
-
-      if(op == MPI_MIN)
-      {
-        if(datasize == sizeof(int))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<int>(static_cast<int*>(comm.my_buffer->void_buffer[i]), static_cast<int*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(float))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<float>(static_cast<float*>(comm.my_buffer->void_buffer[i]), static_cast<float*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(double))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<double>(static_cast<double*>(comm.my_buffer->void_buffer[i]), static_cast<double*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(char))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<char>(static_cast<char*>(comm.my_buffer->void_buffer[i]), static_cast<char*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(long))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<long>(static_cast<long*>(comm.my_buffer->void_buffer[i]), static_cast<long*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(unsigned long))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_min<unsigned long>(static_cast<unsigned long*>(comm.my_buffer->void_buffer[i]), static_cast<unsigned long*>(recvbuf), count);
-        }
-
-        else printf("datatype Error\n");
-
-      }
-
-
-      if(op == MPI_SUM)
-      {
-        if(datasize == sizeof(int))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<int>(static_cast<int*>(comm.my_buffer->void_buffer[i]), static_cast<int*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(float))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<float>(static_cast<float*>(comm.my_buffer->void_buffer[i]), static_cast<float*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(double))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<double>(static_cast<double*>(comm.my_buffer->void_buffer[i]), static_cast<double*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(char))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<char>(static_cast<char*>(comm.my_buffer->void_buffer[i]), static_cast<char*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(long))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<long>(static_cast<long*>(comm.my_buffer->void_buffer[i]), static_cast<long*>(recvbuf), count);
-        }
-
-        else if(datasize == sizeof(unsigned long))
-        {
-          for(int i=1; i<num_ep; i++)
-            reduce_sum<unsigned long>(static_cast<unsigned long*>(comm.my_buffer->void_buffer[i]), static_cast<unsigned long*>(recvbuf), count);
-        }
-
-        else printf("datatype Error\n");
-
-      }
-    }
-
-    MPI_Barrier_local(comm);
-
+      }
+
+      MPI_Barrier_local(comm);
+
+      if(my_rank == 0)
+      {
+        #pragma omp flush
+        copy(buffer, buffer+min(BUFFER_SIZE, count-j), recv_buf+j);
+      }
+      MPI_Barrier_local(comm);
+    }
   }
 
@@ -201,56 +445,226 @@
   int MPI_Reduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
   {
-
     if(!comm.is_ep && comm.mpi_comm)
     {
-      return ::MPI_Reduce(sendbuf, recvbuf, count, to_mpi_type(datatype), to_mpi_op(op), root, to_mpi_comm(comm.mpi_comm));
-    }
-
-
-
-    int ep_rank = comm.ep_comm_ptr->size_rank_info[0].first;
-    int ep_rank_loc = comm.ep_comm_ptr->size_rank_info[1].first;
-    int mpi_rank = comm.ep_comm_ptr->size_rank_info[2].first;
-    int ep_size = comm.ep_comm_ptr->size_rank_info[0].second;
-    int num_ep = comm.ep_comm_ptr->size_rank_info[1].second;
-    int mpi_size = comm.ep_comm_ptr->size_rank_info[2].second;
+      ::MPI_Reduce(sendbuf, recvbuf, count, static_cast< ::MPI_Datatype>(datatype), static_cast< ::MPI_Op>(op), root,
+                   static_cast< ::MPI_Comm>(comm.mpi_comm));
+      return 0;
+    }
+
+
+    if(!comm.mpi_comm) return 0;
 
     int root_mpi_rank = comm.rank_map->at(root).second;
     int root_ep_loc = comm.rank_map->at(root).first;
 
+    int ep_rank, ep_rank_loc, mpi_rank;
+    int ep_size, num_ep, mpi_size;
+
+    ep_rank = comm.ep_comm_ptr->size_rank_info[0].first;
+    ep_rank_loc = comm.ep_comm_ptr->size_rank_info[1].first;
+    mpi_rank = comm.ep_comm_ptr->size_rank_info[2].first;
+    ep_size = comm.ep_comm_ptr->size_rank_info[0].second;
+    num_ep = comm.ep_comm_ptr->size_rank_info[1].second;
+    mpi_size = comm.ep_comm_ptr->size_rank_info[2].second;
+
+
+    ::MPI_Aint recvsize, lb;
+
+    ::MPI_Type_get_extent(static_cast< ::MPI_Datatype>(datatype), &lb, &recvsize);
+
+    void *local_recvbuf;
+    if(ep_rank_loc==0)
+    {
+      local_recvbuf = new void*[recvsize*count];
+    }
+
+    MPI_Reduce_local2(sendbuf, local_recvbuf, count, datatype, op, comm);
+
+
+    if(ep_rank_loc==0)
+    {
+      ::MPI_Reduce(local_recvbuf, recvbuf, count, static_cast< ::MPI_Datatype>(datatype), static_cast< ::MPI_Op>(op), root_mpi_rank, static_cast< ::MPI_Comm>(comm.mpi_comm));
+    }
+
+    if(root_ep_loc != 0 && mpi_rank == root_mpi_rank) // root is not master, master send to root and root receive from master
+    {
+      innode_memcpy(0, recvbuf, root_ep_loc, recvbuf, count, datatype, comm);
+    }
+
+    if(ep_rank_loc==0)
+    {
+      if(datatype == MPI_INT) delete[] static_cast<int*>(local_recvbuf);
+      else if(datatype == MPI_FLOAT) delete[] static_cast<float*>(local_recvbuf);
+      else if(datatype == MPI_DOUBLE) delete[] static_cast<double*>(local_recvbuf);
+      else if(datatype == MPI_LONG) delete[] static_cast<long*>(local_recvbuf);
+      else if(datatype == MPI_UNSIGNED_LONG) delete[] static_cast<unsigned long*>(local_recvbuf);
+      else delete[] static_cast<char*>(local_recvbuf);
+    }
+
+    Message_Check(comm);
+
+    return 0;
+  }
+
+
+
+
+  int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
+  {
+    if(!comm.is_ep && comm.mpi_comm)
+    {
+      ::MPI_Allreduce(sendbuf, recvbuf, count, static_cast< ::MPI_Datatype>(datatype), static_cast< ::MPI_Op>(op),
+                      static_cast< ::MPI_Comm>(comm.mpi_comm));
+      return 0;
+    }
+
+    if(!comm.mpi_comm) return 0;
+
+
+    int ep_rank, ep_rank_loc, mpi_rank;
+    int ep_size, num_ep, mpi_size;
+
+    ep_rank = comm.ep_comm_ptr->size_rank_info[0].first;
+    ep_rank_loc = comm.ep_comm_ptr->size_rank_info[1].first;
+    mpi_rank = comm.ep_comm_ptr->size_rank_info[2].first;
+    ep_size = comm.ep_comm_ptr->size_rank_info[0].second;
+    num_ep = comm.ep_comm_ptr->size_rank_info[1].second;
+    mpi_size = comm.ep_comm_ptr->size_rank_info[2].second;
+
+
+    ::MPI_Aint recvsize, lb;
+
+    ::MPI_Type_get_extent(static_cast< ::MPI_Datatype>(datatype), &lb, &recvsize);
+
+    void *local_recvbuf;
+    if(ep_rank_loc==0)
+    {
+      local_recvbuf = new void*[recvsize*count];
+    }
+
+    MPI_Reduce_local2(sendbuf, local_recvbuf, count, datatype, op, comm);
+
+
+    if(ep_rank_loc==0)
+    {
+      ::MPI_Allreduce(local_recvbuf, recvbuf, count, static_cast< ::MPI_Datatype>(datatype), static_cast< ::MPI_Op>(op), static_cast< ::MPI_Comm>(comm.mpi_comm));
+    }
+
+    MPI_Bcast_local2(recvbuf, count, datatype, comm);
+
+    if(ep_rank_loc==0)
+    {
+      if(datatype == MPI_INT) delete[] static_cast<int*>(local_recvbuf);
+      else if(datatype == MPI_FLOAT) delete[] static_cast<float*>(local_recvbuf);
+      else if(datatype == MPI_DOUBLE) delete[] static_cast<double*>(local_recvbuf);
+      else if(datatype == MPI_LONG) delete[] static_cast<long*>(local_recvbuf);
+      else if(datatype == MPI_UNSIGNED_LONG) delete[] static_cast<unsigned long*>(local_recvbuf);
+      else delete[] static_cast<char*>(local_recvbuf);
+    }
+
+    Message_Check(comm);
+
+    return 0;
+  }
+
+
+  int MPI_Reduce_scatter(const void *sendbuf, void *recvbuf, const int recvcounts[], MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
+  {
+
+    if(!comm.is_ep && comm.mpi_comm)
+    {
+      ::MPI_Reduce_scatter(sendbuf, recvbuf, recvcounts, static_cast< ::MPI_Datatype>(datatype), static_cast< ::MPI_Op>(op),
+                           static_cast< ::MPI_Comm>(comm.mpi_comm));
+      return 0;
+    }
+
+    if(!comm.mpi_comm) return 0;
+
+    int ep_rank, ep_rank_loc, mpi_rank;
+    int ep_size, num_ep, mpi_size;
+
+    ep_rank = comm.ep_comm_ptr->size_rank_info[0].first;
+    ep_rank_loc = comm.ep_comm_ptr->size_rank_info[1].first;
+    mpi_rank = comm.ep_comm_ptr->size_rank_info[2].first;
+    ep_size = comm.ep_comm_ptr->size_rank_info[0].second;
+    num_ep = comm.ep_comm_ptr->size_rank_info[1].second;
+    mpi_size = comm.ep_comm_ptr->size_rank_info[2].second;
+
+    void* local_buf;
+    void* local_buf2;
+    int local_buf_size = accumulate(recvcounts, recvcounts+ep_size, 0);
+    int local_buf2_size = accumulate(recvcounts+ep_rank-ep_rank_loc, recvcounts+ep_rank-ep_rank_loc+num_ep, 0);
+
     ::MPI_Aint datasize, lb;
 
     ::MPI_Type_get_extent(static_cast< ::MPI_Datatype>(datatype), &lb, &datasize);
 
-    bool is_master = (ep_rank_loc==0 && mpi_rank != root_mpi_rank ) || ep_rank == root;
-    bool is_root = ep_rank == root;
-
-    void* local_recvbuf;
-
-    if(is_master)
-    {
-      local_recvbuf = new void*[datasize * count];
-    }
-
-    if(mpi_rank == root_mpi_rank) MPI_Reduce_local(sendbuf, local_recvbuf, count, datatype, op, root_ep_loc, comm);
-    else                          MPI_Reduce_local(sendbuf, local_recvbuf, count, datatype, op, 0, comm);
-
-
-
-    if(is_master)
-    {
-      ::MPI_Reduce(local_recvbuf, recvbuf, count, to_mpi_type(datatype), to_mpi_op(op), root_mpi_rank, to_mpi_comm(comm.mpi_comm));
-      
-    }
-
-    if(is_master)
-    {
-      delete[] local_recvbuf;
-    }
-
-    MPI_Barrier_local(comm);
-  }
-
-
+    if(ep_rank_loc == 0)
+    {
+      local_buf = new void*[local_buf_size*datasize];
+      local_buf2 = new void*[local_buf2_size*datasize];
+    }
+    MPI_Reduce_local2(sendbuf, local_buf, local_buf_size, MPI_INT, op, comm);
+
+
+    if(ep_rank_loc == 0)
+    {
+      int local_recvcnt[mpi_size];
+      for(int i=0; i<mpi_size; i++)
+      {
+        local_recvcnt[i] = accumulate(recvcounts+ep_rank, recvcounts+ep_rank+num_ep, 0);
+      }
+
+      ::MPI_Reduce_scatter(local_buf, local_buf2, local_recvcnt, static_cast< ::MPI_Datatype>(datatype),
+                         static_cast< ::MPI_Op>(op), static_cast< ::MPI_Comm>(comm.mpi_comm));
+    }
+
+
+    int displs[num_ep];
+    displs[0] = 0;
+    for(int i=1; i<num_ep; i++)
+    {
+      displs[i] = displs[i-1] + recvcounts[ep_rank-ep_rank_loc+i-1];
+    }
+
+    MPI_Scatterv_local2(local_buf2, recvcounts+ep_rank-ep_rank_loc, displs, datatype, recvbuf, comm);
+
+    if(ep_rank_loc == 0)
+    {
+      if(datatype == MPI_INT)
+      {
+        delete[] static_cast<int*>(local_buf);
+        delete[] static_cast<int*>(local_buf2);
+      }
+      else if(datatype == MPI_FLOAT)
+      {
+        delete[] static_cast<float*>(local_buf);
+        delete[] static_cast<float*>(local_buf2);
+      }
+      else if(datatype == MPI_DOUBLE)
+      {
+        delete[] static_cast<double*>(local_buf);
+        delete[] static_cast<double*>(local_buf2);
+      }
+      else if(datatype == MPI_LONG)
+      {
+        delete[] static_cast<long*>(local_buf);
+        delete[] static_cast<long*>(local_buf2);
+      }
+      else if(datatype == MPI_UNSIGNED_LONG)
+      {
+        delete[] static_cast<unsigned long*>(local_buf);
+        delete[] static_cast<unsigned long*>(local_buf2);
+      }
+      else // if(datatype == MPI_DOUBLE)
+      {
+        delete[] static_cast<char*>(local_buf);
+        delete[] static_cast<char*>(local_buf2);
+      }
+    }
+
+    Message_Check(comm);
+    return 0;
+  }
 }
 
