text: #pragma once
  #ifndef DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE
  #define DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE
  
  #include <algorithm>
  #include <numeric>
  #include <set>
  #include <utility>
  
  #include "../../../third-party/conduit/include/uitsl/algorithm/copy_if.hpp"
  #include "../../../third-party/conduit/include/uitsl/math/shift_mod.hpp"
  #include "../../../third-party/Empirical/include/emp/base/vector.hpp"
  #include "../../../third-party/signalgp-lite/include/sgpl/utility/ThreadLocalRandom.hpp"
  
  #include "../cell/cardinal_iterators/GenomeNodeOutputWrapper.hpp"
  #include "../cell/cardinal_iterators/ResourceStockpileWrapper.hpp"
  #include "../cell/cardinal_iterators/SpawnArrestWrapper.hpp"
  #include "../cell/cardinal_iterators/SpawnCountWrapper.hpp"
  #include "../cell/cardinal_iterators/SpawnRequestWrapper.hpp"
  #include "../config/cfg.hpp"
  #include "../debug/LogScope.hpp"
  #include "../runninglog/SpawnEvent.hpp"
  
  namespace dish2 {
  
  struct SpawnSendingService {
  
    static bool ShouldRun( const size_t update, const bool alive ) {
      const size_t freq = dish2::cfg.SPAWN_SENDING_SERVICE_FREQUENCY();
      return
        alive
        && freq > 0
        && uitsl::shift_mod( update, freq ) == 0;
    }
  
    template<typename Cell>
    static void DoService( Cell& cell ) {
  
      const dish2::LogScope guard{ "spawn sending service", "TODO", 3 };
  
      using spec_t = typename Cell::spec_t;
  
      // check resource stockpile consistency
      emp_assert((
        std::set< typename dish2::ResourceStockpileWrapper<spec_t>::value_type >(
          cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
          cell.template end<dish2::ResourceStockpileWrapper<spec_t>>()
        ).size() == 1
      ));
  
      // check spawn count consistency
      emp_assert((
        std::set< typename dish2::SpawnCountWrapper<spec_t>::value_type >(
          cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
          cell.template end<dish2::SpawnCountWrapper<spec_t>>()
        ).size() == 1
      ));
  
      float available_resource{
        *cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>()
      };
  
      if ( available_resource < 1 ) return;
  
      thread_local emp::vector< std::reference_wrapper<
        typename dish2::GenomeNodeOutputWrapper<spec_t>::value_type
      > > requested_outputs;
      requested_outputs.clear();
  
      uitsl::copy_if(
        cell.template begin<dish2::GenomeNodeOutputWrapper<spec_t>>(),
        cell.template end<dish2::GenomeNodeOutputWrapper<spec_t>>(),
        cell.template begin<dish2::SpawnArrestWrapper<spec_t>>(),
        cell.template begin<dish2::SpawnRequestWrapper<spec_t>>(),
        std::back_inserter( requested_outputs ),
        []( const auto arrest, const auto request ){ return request && !arrest; }
      );
  
      while ( available_resource >= 1 && requested_outputs.size() ) {
  
        // pick a random request
        const size_t idx = sgpl::tlrand.Get().GetUInt(
          requested_outputs.size()
        );
  
        // do the spawn send
        requested_outputs[ idx ].get().Put( *cell.genome );
        available_resource -= 1;
  
        // record spawn send in spawn count
        std::transform(
          cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
          cell.template end<dish2::SpawnCountWrapper<spec_t>>(),
          cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
          []( const auto& cur ){ emp_assert( cur + 1 > cur ); return cur + 1; }
        );
  
        // record spawn event in running log
        const auto& peripheral = cell.cardinals[idx].peripheral;
        const size_t replev = peripheral.readable_state.template Get<
          dish2::RepLevRequest<spec_t>
        >().GetRepLev();
        const size_t kin_id_commonality_parent_daughter = spec_t::NLEV - replev;
        const size_t kin_id_commonality_parent_eliminated =
          peripheral.readable_state.template Get<
            dish2::KinGroupIDView<spec_t>
          >().CountCommonality(
            std::as_const( peripheral.state_node_input ).Get().template Get<
              dish2::KinGroupIDView<spec_t>
            >()
          );
        const size_t kin_id_commonality_daughter_eliminated = std::min(
          kin_id_commonality_parent_daughter,
          kin_id_commonality_parent_eliminated
        );
  
        cell.running_logs.Record( dish2::SpawnEvent<spec_t>{
          kin_id_commonality_daughter_eliminated,
          kin_id_commonality_parent_daughter,
          kin_id_commonality_parent_eliminated,
          cell.cardinals.size(),
          cell.GetPeripherality(),
          replev
        } );
  
        // swap 'n pop request
        requested_outputs[ idx ] = requested_outputs.back();
        requested_outputs.pop_back();
  
      }
  
      std::fill(
        cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
        cell.template end<dish2::ResourceStockpileWrapper<spec_t>>(),
        available_resource
      );
  
      // check resource stockpile consistency
      emp_assert((
        std::set< typename dish2::ResourceStockpileWrapper<spec_t>::value_type >(
          cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
          cell.template end<dish2::ResourceStockpileWrapper<spec_t>>()
        ).size() == 1
      ));
  
      // check spawn count consistency
      emp_assert((
        std::set< typename dish2::SpawnCountWrapper<spec_t>::value_type >(
          cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
          cell.template end<dish2::SpawnCountWrapper<spec_t>>()
        ).size() == 1
      ));
  
    }
  
  };
  
  } // namespace dish2
  
  #endif // #ifndef DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE
  