text: // MIT License
  //
  // Copyright (c) 2020, The Regents of the University of California,
  // through Lawrence Berkeley National Laboratory (subject to receipt of any
  // required approvals from the U.S. Dept. of Energy).  All rights reserved.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in all
  // copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  // SOFTWARE.
  
  #ifndef TIMEMORY_VARIADIC_BUNDLE_CPP_
  #define TIMEMORY_VARIADIC_BUNDLE_CPP_
  
  #include "timemory/variadic/bundle.hpp"
  #include "timemory/backends/dmp.hpp"
  #include "timemory/mpl/filters.hpp"
  #include "timemory/operations/types/set.hpp"
  #include "timemory/settings/declaration.hpp"
  #include "timemory/tpls/cereal/cereal.hpp"
  #include "timemory/utility/macros.hpp"
  #include "timemory/variadic/bundle_execute.hpp"
  #include "timemory/variadic/functional.hpp"
  #include "timemory/variadic/types.hpp"
  
  namespace tim
  {
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::initializer_type&
  bundle<Tag, BundleT, TupleT>::get_initializer()
  {
      static initializer_type _instance = [](this_type&) {};
      return _instance;
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle()
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, get_initializer());
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T>
  bundle<Tag, BundleT, TupleT>::bundle(const string_t& _key, quirk::config<T...> _config,
                                       transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _key, true_type{}, _config))
  , m_data(invoke::construct<data_type, Tag>(_key, _config))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle, T...>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func),
                        _config);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T>
  bundle<Tag, BundleT, TupleT>::bundle(hash_value_t _hash, quirk::config<T...> _config,
                                       transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _hash, true_type{}, _config))
  , m_data(invoke::construct<data_type, Tag>(_hash, _config))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle, T...>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func),
                        _config);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T>
  bundle<Tag, BundleT, TupleT>::bundle(const captured_location_t& _loc,
                                       quirk::config<T...>        _config,
                                       transient_func_t           _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _loc, true_type{}, _config))
  , m_data(invoke::construct<data_type, Tag>(_loc, _config))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle, T...>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func),
                        _config);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T>
  bundle<Tag, BundleT, TupleT>::bundle(const string_t& _key, bool _store,
                                       quirk::config<T...> _config,
                                       transient_func_t    _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _key, _store, _config))
  , m_data(invoke::construct<data_type, Tag>(_key, _config))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle, T...>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func),
                        _config);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T>
  bundle<Tag, BundleT, TupleT>::bundle(const captured_location_t& _loc, bool _store,
                                       quirk::config<T...> _config,
                                       transient_func_t    _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _loc, _store, _config))
  , m_data(invoke::construct<data_type, Tag>(_loc, _config))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle, T...>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func),
                        _config);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(hash_value_t _hash, bool _store,
                                       scope::config _scope, transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _hash, _store, _scope))
  , m_data(invoke::construct<data_type, Tag>(_hash, m_scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(const string_t& _key, bool _store,
                                       scope::config _scope, transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _key, _store, _scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(const captured_location_t& _loc, bool _store,
                                       scope::config _scope, transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _loc, _store, _scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(hash_value_t _hash, scope::config _scope,
                                       transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _hash, true_type{}, _scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(const string_t& _key, scope::config _scope,
                                       transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _key, true_type{}, _scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(const captured_location_t& _loc,
                                       scope::config _scope, transient_func_t _init_func)
  : bundle_type(bundle_type::handle(type_list_type{}, _loc, true_type{}, _scope))
  {
      update_last_instance(&get_this_type(), get_last_instance(),
                           quirk_config<quirk::stop_last_bundle>::value);
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      bundle_type::init(type_list_type{}, get_this_type(), m_data, std::move(_init_func));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::~bundle()
  {
      if(get_last_instance() == &get_this_type())
          update_last_instance(nullptr, get_last_instance(), false);
  
      IF_CONSTEXPR(!quirk_config<quirk::explicit_stop>::value)
      {
          if(m_is_active())
              stop();
      }
  
      IF_CONSTEXPR(optional_count() > 0)
      {
  #if defined(DEBUG) && !defined(NDEBUG)
          if(tim::settings::debug() && tim::settings::verbose() > 4)
          {
              PRINT_HERE("%s", "deleting components");
          }
  #endif
          invoke::destroy<Tag>(m_data);
      }
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>::bundle(const bundle& rhs)
  : bundle_type(rhs)
  {
      using copy_oper_t = convert_each_t<operation::copy, remove_pointers_t<data_type>>;
      IF_CONSTEXPR(optional_count() > 0) { apply_v::set_value(m_data, nullptr); }
      apply_v::access2<copy_oper_t>(m_data, rhs.m_data);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>&
  bundle<Tag, BundleT, TupleT>::operator=(const bundle& rhs)
  {
      if(this != &rhs)
      {
          bundle_type::operator=(rhs);
          invoke::destroy<Tag>(m_data);
          invoke::invoke_impl::invoke_data<operation::copy, Tag>(m_data, rhs.m_data);
          // apply_v::access<operation_t<operation::copy>>(m_data);
      }
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // this_type operators
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::operator-=(const this_type& rhs)
  {
      bundle_type::operator-=(static_cast<const bundle_type&>(rhs));
      invoke::invoke_impl::invoke_data<operation::minus, Tag>(m_data, rhs.m_data);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::operator+=(const this_type& rhs)
  {
      bundle_type::operator+=(static_cast<const bundle_type&>(rhs));
      invoke::invoke_impl::invoke_data<operation::plus, Tag>(m_data, rhs.m_data);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  bundle<Tag, BundleT, TupleT>
  bundle<Tag, BundleT, TupleT>::clone(bool _store, scope::config _scope)
  {
      bundle tmp(*this);
      tmp.m_store(_store);
      tmp.m_scope = _scope;
      return tmp;
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  void
  bundle<Tag, BundleT, TupleT>::init_storage()
  {
      static thread_local bool _once = []() {
          apply_v::type_access<operation::init_storage, mpl::non_quirk_t<reference_type>>();
          return true;
      }();
      consume_parameters(_once);
  }
  
  //--------------------------------------------------------------------------------------//
  // insert into graph
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::push()
  {
      if(!m_enabled())
          return get_this_type();
  
      if(!m_is_pushed())
      {
          // reset the data
          invoke::reset<Tag>(m_data);
          // avoid pushing/popping when already pushed/popped
          m_is_pushed(true);
          // insert node or find existing node
          invoke::push<Tag>(m_data, m_scope, m_hash);
      }
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // insert into graph
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tp>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::push(mpl::piecewise_select<Tp...>)
  {
      if(!m_enabled())
          return get_this_type();
  
      using pw_type = convert_t<mpl::implemented_t<Tp...>, mpl::piecewise_select<>>;
      // reset the data
      invoke::invoke<operation::reset, Tag>(pw_type{}, m_data);
      // insert node or find existing node
      invoke::invoke<operation::push_node, Tag>(pw_type{}, m_data, m_scope, m_hash);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // insert into graph
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tp>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::push(mpl::piecewise_select<Tp...>, scope::config _scope)
  {
      if(!m_enabled())
          return get_this_type();
  
      using pw_type = convert_t<mpl::implemented_t<Tp...>, mpl::piecewise_select<>>;
      // reset the data
      invoke::invoke<operation::reset, Tag>(pw_type{}, m_data);
      // insert node or find existing node
      invoke::invoke<operation::push_node, Tag>(pw_type{}, m_data, _scope, m_hash);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // pop out of graph
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::pop()
  {
      if(!m_enabled())
          return get_this_type();
  
      if(m_is_pushed())
      {
          // set the current node to the parent node
          invoke::pop<Tag>(m_data);
          // avoid pushing/popping when already pushed/popped
          m_is_pushed(false);
      }
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // pop out of graph
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tp>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::pop(mpl::piecewise_select<Tp...>)
  {
      if(!m_enabled())
          return get_this_type();
  
      using pw_type = convert_t<mpl::implemented_t<Tp...>, mpl::piecewise_select<>>;
      // set the current node to the parent node
      invoke::invoke<operation::pop_node, Tag>(pw_type{}, m_data);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // measure functions
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::measure(Args&&... args)
  {
      return invoke<operation::measure>(std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  // sample functions
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::sample(Args&&... args)
  {
      return invoke<operation::sample>(std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  // start/stop functions with no push/pop or assemble/derive
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::start(mpl::lightweight, Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      assemble(*this);
      invoke::start<Tag>(m_data, std::forward<Args>(args)...);
      m_is_active(true);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::stop(mpl::lightweight, Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      invoke::stop<Tag>(m_data, std::forward<Args>(args)...);
      if(m_is_active())
          ++m_laps;
      derive(*this);
      m_is_active(false);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // start/stop functions with no push/pop or assemble/derive
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tp, typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::start(mpl::piecewise_select<Tp...>, Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      using select_tuple_t = mpl::sort<trait::start_priority, std::tuple<Tp...>>;
  
      TIMEMORY_FOLD_EXPRESSION(
          operation::reset<Tp>(std::get<index_of<Tp, data_type>::value>(m_data)));
      IF_CONSTEXPR(!quirk_config<quirk::explicit_push>::value &&
                   !quirk_config<quirk::no_store>::value)
      {
          if(m_store() && !bundle_type::m_explicit_push())
          {
              TIMEMORY_FOLD_EXPRESSION(operation::push_node<Tp>(
                  std::get<index_of<Tp, data_type>::value>(m_data), m_scope, m_hash));
          }
      }
  
      // start components
      auto&& _data = mpl::get_reference_tuple<select_tuple_t>(m_data);
      invoke::invoke<operation::standard_start, Tag>(_data, std::forward<Args>(args)...);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tp, typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::stop(mpl::piecewise_select<Tp...>, Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      using select_tuple_t = mpl::sort<trait::stop_priority, std::tuple<Tp...>>;
  
      // stop components
      auto&& _data = mpl::get_reference_tuple<select_tuple_t>(m_data);
      invoke::invoke<operation::standard_start, Tag>(_data, std::forward<Args>(args)...);
  
      IF_CONSTEXPR(!quirk_config<quirk::explicit_pop>::value &&
                   !quirk_config<quirk::no_store>::value)
      {
          if(m_store() && !bundle_type::m_explicit_pop())
          {
              TIMEMORY_FOLD_EXPRESSION(operation::pop_node<Tp>(
                  std::get<index_of<Tp, data_type>::value>(m_data)));
          }
      }
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // start/stop functions
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::start(Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      // push components into the call-stack
      IF_CONSTEXPR(!quirk_config<quirk::explicit_push>::value &&
                   !quirk_config<quirk::no_store>::value)
      {
          if(m_store() && !bundle_type::m_explicit_push())
              push();
      }
  
      // start components
      start(mpl::lightweight{}, std::forward<Args>(args)...);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::stop(Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      // stop components
      stop(mpl::lightweight{}, std::forward<Args>(args)...);
  
      // pop components off of the call-stack stack
      IF_CONSTEXPR(!quirk_config<quirk::explicit_pop>::value &&
                   !quirk_config<quirk::no_store>::value)
      {
          if(m_store() && !bundle_type::m_explicit_pop())
              pop();
      }
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // recording
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::record(Args&&... args)
  {
      if(!m_enabled())
          return get_this_type();
  
      ++m_laps;
      return invoke<operation::record>(std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  // reset data
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::reset(Args&&... args)
  {
      m_laps = 0;
      return invoke<operation::reset>(std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  uint64_t
  bundle<Tag, BundleT, TupleT>::count()
  {
      uint64_t _count = 0;
      invoke::invoke<operation::generic_counter>(m_data, std::ref(_count));
      return _count;
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::construct(Args&&... _args)
  {
      // using construct_t = operation_t<operation::construct>;
      // apply_v::access<construct_t>(m_data, std::forward<Args>(_args)...);
      return invoke<operation::construct>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::assemble(Args&&... _args)
  {
      return invoke<operation::assemble>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::derive(Args&&... _args)
  {
      return invoke<operation::derive>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::mark(Args&&... _args)
  {
      return invoke<operation::mark>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::mark_begin(Args&&... _args)
  {
      return invoke<operation::mark_begin>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::mark_end(Args&&... _args)
  {
      return invoke<operation::mark_end>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::store(Args&&... _args)
  {
      if(!m_enabled())
          return get_this_type();
  
      m_is_active(true);
      invoke<operation::store>(std::forward<Args>(_args)...);
      m_is_active(false);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::audit(Args&&... _args)
  {
      return invoke<operation::audit>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::add_secondary(Args&&... _args)
  {
      return invoke<operation::add_secondary>(std::forward<Args>(_args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <template <typename> class OpT, typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::invoke(Args&&... _args)
  {
      if(!m_enabled())
          return get_this_type();
  
      invoke::invoke<OpT, Tag>(m_data, std::forward<Args>(_args)...);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <template <typename> class OpT, typename... Tp, typename... Args>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::invoke(mpl::piecewise_select<Tp...>, Args&&... _args)
  {
      if(!m_enabled())
          return get_this_type();
  
      TIMEMORY_FOLD_EXPRESSION(operation::generic_operator<Tp, OpT<Tp>, Tag>(
          this->get<Tp>(), std::forward<Args>(_args)...));
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  // get data
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  auto
  bundle<Tag, BundleT, TupleT>::get(Args&&... args) const
  {
      return invoke::get<Tag>(m_data, std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  // get labeled data
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Args>
  auto
  bundle<Tag, BundleT, TupleT>::get_labeled(Args&&... args) const
  {
      return invoke::get_labeled<Tag>(m_data, std::forward<Args>(args)...);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::data_type&
  bundle<Tag, BundleT, TupleT>::data()
  {
      return m_data;
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  const typename bundle<Tag, BundleT, TupleT>::data_type&
  bundle<Tag, BundleT, TupleT>::data() const
  {
      return m_data;
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::get(void*& ptr, size_t _hash) const
  {
      if(!m_enabled())
          return get_this_type();
  
      tim::variadic::impl::get<Tag>(m_data, ptr, _hash);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... Tail>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::disable()
  {
      TIMEMORY_FOLD_EXPRESSION(operation::generic_deleter<remove_pointer_t<Tail>>{
          this->get_reference<Tail>() });
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename... T, typename... Args>
  std::array<bool, sizeof...(T)>
  bundle<Tag, BundleT, TupleT>::initialize(Args&&... args)
  {
      if(!m_enabled())
          return std::array<bool, sizeof...(T)>{};
  
      constexpr auto N = sizeof...(T);
      return TIMEMORY_FOLD_EXPANSION(bool, N, init<T>(std::forward<Args>(args)...));
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename T, typename Func, typename... Args,
            enable_if_t<trait::is_available<T>::value, int>>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::type_apply(Func&& _func, Args&&... _args)
  {
      if(!m_enabled())
          return get_this_type();
  
      auto* _obj = get<T>();
      if(_obj)
          ((*_obj).*(_func))(std::forward<Args>(_args)...);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename T, typename Func, typename... Args,
            enable_if_t<!trait::is_available<T>::value, int>>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::type_apply(Func&&, Args&&...)
  {
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  void
  bundle<Tag, BundleT, TupleT>::set_prefix(const string_t& _key) const
  {
      if(!m_enabled())
          return;
  
      invoke::set_prefix<Tag>(m_data, m_hash, _key);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::set_prefix(hash_value_t _hash) const
  {
      if(!m_enabled())
          return get_this_type();
  
      auto itr = get_hash_ids()->find(_hash);
      if(itr != get_hash_ids()->end())
          invoke::set_prefix<Tag>(m_data, _hash, itr->second);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::set_prefix(captured_location_t _loc) const
  {
      return set_prefix(_loc.get_hash());
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::set_scope(scope::config val)
  {
      if(!m_enabled())
          return get_this_type();
  
      m_scope = val;
      invoke::set_scope<Tag>(m_data, m_scope);
      return get_this_type();
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  scope::transient_destructor
  bundle<Tag, BundleT, TupleT>::get_scope_destructor()
  {
      return scope::transient_destructor{ [&]() { this->stop(); } };
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  scope::transient_destructor
  bundle<Tag, BundleT, TupleT>::get_scope_destructor(
      utility::transient_function<void(this_type&)> _func)
  {
      return scope::transient_destructor{ [&, _func]() { _func(get_this_type()); } };
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename T>
  void
  bundle<Tag, BundleT, TupleT>::set_prefix(T* obj, internal_tag) const
  {
      if(!m_enabled())
          return;
  
      using PrefixOpT = operation::generic_operator<T, operation::set_prefix<T>, Tag>;
      auto _key       = get_hash_identifier_fast(m_hash);
      PrefixOpT(obj, m_hash, _key);
  }
  
  //--------------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename T>
  void
  bundle<Tag, BundleT, TupleT>::set_scope(T* obj, internal_tag) const
  {
      if(!m_enabled())
          return;
  
      using PrefixOpT = operation::generic_operator<T, operation::set_scope<T>, Tag>;
      PrefixOpT(obj, m_scope);
  }
  
  //----------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <bool PrintPrefix, bool PrintLaps>
  typename bundle<Tag, BundleT, TupleT>::this_type&
  bundle<Tag, BundleT, TupleT>::print(std::ostream& os, bool _endl) const
  {
      using printer_t = typename bundle_type::print_type;
      if(size() == 0 || m_hash == 0)
          return get_this_type();
      std::stringstream ss_data;
      apply_v::access_with_indices<printer_t>(m_data, std::ref(ss_data), false);
      if(PrintPrefix)
      {
          bundle_type::update_width();
          std::stringstream ss_prefix;
          std::stringstream ss_id;
          ss_id << get_prefix() << " " << std::left << key();
          ss_prefix << std::setw(bundle_type::output_width()) << std::left << ss_id.str()
                    << " : ";
          os << ss_prefix.str();
      }
      os << ss_data.str();
      if(m_laps > 0 && PrintLaps)
          os << " [laps: " << m_laps << "]";
      if(_endl)
          os << '\n';
      return get_this_type();
  }
  
  //----------------------------------------------------------------------------------//
  //
  template <typename Tag, typename BundleT, typename TupleT>
  template <typename Archive>
  void
  bundle<Tag, BundleT, TupleT>::serialize(Archive& ar, const unsigned int)
  {
      std::string _key   = {};
      auto        keyitr = get_hash_ids()->find(m_hash);
      if(keyitr != get_hash_ids()->end())
          _key = keyitr->second;
  
      ar(cereal::make_nvp("hash", m_hash), cereal::make_nvp("key", _key),
         cereal::make_nvp("laps", m_laps));
  
      if(keyitr == get_hash_ids()->end())
      {
          auto _hash = add_hash_id(_key);
          if(_hash != m_hash)
          {
              PRINT_HERE("Warning! Hash for '%s' (%llu) != %llu", _key.c_str(),
                         (unsigned long long) _hash, (unsigned long long) m_hash);
          }
      }
  
      ar.setNextName("data");
      ar.startNode();
      invoke::serialize(ar, m_data);
      ar.finishNode();
      // ar(cereal::make_nvp("data", m_data));
  }
  
  //--------------------------------------------------------------------------------------//
  
  }  // namespace tim
  
  #endif
  