// *****************************************************************************
/*!
  \file      src/Base/HashMapReducer.hpp
  \copyright 2012-2015 J. Bakosi,
             2016-2018 Los Alamos National Security, LLC.,
             2019-2021 Triad National Security, LLC.
             All rights reserved. See the LICENSE file for details.
  \brief     Custom Charm++ reducer for merging std::unordered_maps across PEs
  \details   Custom Charm++ reducer for merging std::unordered_maps across PEs.
*/
// *****************************************************************************
#ifndef HashMapReducer_h
#define HashMapReducer_h
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include "NoWarning/charm++.hpp"
#include "ContainerUtil.hpp"
namespace tk {
//! Serialize std::unordered_map to raw memory stream
//! \tparam Key Map key
//! \tparam T Map value
//! \tparam Hash Map hasher
//! \tparam Eq Map equality operator
//! \param[in] m Hash map to serialize
//! \return Pair of the length and the raw stream containing the serialized map
template< class Key,
          class T,
          class Hash = std::hash< Key >,
          class Eq = std::equal_to< Key > >
std::pair< int, std::unique_ptr<char[]> >
serialize( const std::unordered_map< Key, T, Hash, Eq >& m ) {
   // Prepare for serializing map to a raw binary stream, compute size
  PUP::sizer sizer;
  sizer | const_cast< std::unordered_map< Key, T, Hash, Eq >& >( m );
  // Create raw character stream to store the serialized map
  std::unique_ptr<char[]> flatData = std::make_unique<char[]>( sizer.size() );
  // Serialize map, each message will contain a map
  PUP::toMem packer( flatData.get() );
  packer | const_cast< std::unordered_map< Key, T, Hash, Eq >& >( m );
  // Return size of and raw stream
  return { sizer.size(), std::move(flatData) };
}
//! \brief Charm++ custom reducer for merging std::unordered_maps during
//!   reduction across PEs
//! \tparam Key Map key
//! \tparam T Map value
//! \tparam Hash Map hasher
//! \tparam Eq Map equality operator
//! \param[in] nmsg Number of messages in msgs
//! \param[in] msgs Charm++ reduction message containing the serialized maps
//! \return Aggregated std::unordered_maps built for further aggregation if
//!    needed
//! \details During aggregation the map keys are inserted, i.e., the keys remain
//!   unique and the mapped values, assuming containers defining begin() and
//!   end() iterators() are concatenated.
//! \note The mapped type must be a container, i.e., must provide iterators
//!   begin() and end().
template< class Key,
          class T,
          class Hash = std::hash< Key >,
          class Eq = std::equal_to< Key > >
CkReductionMsg*
mergeHashMap( int nmsg, CkReductionMsg **msgs ) {
  // Will store deserialized map
  std::unordered_map< Key, T, Hash, Eq > p;
  // Create PUP deserializer based on message passed in
  PUP::fromMem creator( msgs[0]->getData() );
  // Deserialize map from raw stream
  creator | p;
  for (int m=1; m<nmsg; ++m) {
    // Unpack map
    std::unordered_map< Key, T, Hash, Eq > u;
    PUP::fromMem curCreator( msgs[m]->getData() );
    curCreator | u;
    // Concatenate maps
    for (auto&& c : u) concat( std::move(c.second), p[c.first] );<--- Iterating over container 'u' that is always empty.
  }
  // Serialize concatenated maps to raw stream
  auto stream = tk::serialize( p );
  // Forward serialized hash map
  return CkReductionMsg::buildNew( stream.first, stream.second.get() );
}
} // tk::
#endif // HashMapReducer_h