From 7061f830c389ac1488df51f0107648b719b01d4f Mon Sep 17 00:00:00 2001
From: AAAlvesJr <aalvesju@gmail.com>
Date: Wed, 2 Dec 2020 14:45:50 +0100
Subject: [PATCH] [refactory-2020] stack implementations: more cleaning up.
 Moving implemententations to inl. Stack.hpp.

---
 .../detail/framework/stack/CombinedStack.inl  | 180 ++++--
 .../detail/framework/stack/SecondaryView.inl  | 536 ++++++++++++++++--
 corsika/detail/framework/stack/Stack.inl      | 368 ++++++++++++
 corsika/framework/stack/Stack.hpp             | 341 +++--------
 4 files changed, 1048 insertions(+), 377 deletions(-)
 create mode 100644 corsika/detail/framework/stack/Stack.inl

diff --git a/corsika/detail/framework/stack/CombinedStack.inl b/corsika/detail/framework/stack/CombinedStack.inl
index 48ff64e71..f109f67ce 100644
--- a/corsika/detail/framework/stack/CombinedStack.inl
+++ b/corsika/detail/framework/stack/CombinedStack.inl
@@ -1,7 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
  *
  * This software is distributed under the terms of the GNU General Public
  * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
@@ -10,110 +8,168 @@
 
 #pragma once
 
+
+#include <corsika/framework/logging/Logging.hpp>
 #include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/stack/Stack.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
 
-
 namespace corsika {
 
-	template <template <typename> typename ParticleInterfaceA,
-	            template <typename> typename ParticleInterfaceB, typename StackIterator>
-    template <typename... Args1>
-    void CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB,
-	                          StackIterator>::SetParticleData(const std::tuple<Args1...> vA) {
-      PI_A::SetParticleData(vA);
-      PI_B::SetParticleData();
-    }
+  /**
+   * CombinedParticleInterface can be used to combine the data of several StackData
+   * objects.
+   *
+   * You may combine two StackData object, see class CombinedStackImpl
+   * below, into one Stack, using a combined StackIterator (aka
+   * CombinedParticleInterface) interface class.
+   *
+   * This allows to add specific information to a given Stack, could
+   * be special information on a subset of entries
+   * (e.g. NuclearStackExtension) or also (multi) thinning weights for
+   * all particles.
+   *
+   * Many Stacks can be combined into more complex object.
+   *
+   * The two sub-stacks must both provide their independent
+   * ParticleInterface classes.
+   *
+   */
+  template <template <typename> typename TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  struct CombinedParticleInterface
+      : public TParticleInterfaceB<TParticleInterfaceA<TStackIterator>> {
+
+    typedef CombinedParticleInterface<TParticleInterfaceA, TParticleInterfaceB,
+                                      TStackIterator>
+        pi_c_type;
+    typedef TParticleInterfaceA<TStackIterator> pi_a_type;
+    typedef TParticleInterfaceB<TParticleInterfaceA<TStackIterator>> pi_b_type;
+
+  protected:
+    using pi_b_type::getIndex;     // choose B, A would also work
+    using pi_b_type::getStackData; // choose B, A would also work
+
+  public:
+    /**
+     * @name wrapper for user functions
+     * @{
+     *
+     * In this set of functions we call the user-provide
+     * TParticleInterface setParticleData(...) methods, either with
+     * parent particle reference, or w/o.
+     *
+     * There is one implicit assumption here: if only one data tuple
+     * is provided for setParticleData, the data is passed on to
+     * TParticleInterfaceA and the TParticleInterfaceB is
+     * default-initialized. There are many occasions where this is the
+     * desired behaviour, e.g. for thinning etc.
+     *
+     */
 
-	template <template <typename> typename ParticleInterfaceA,
-		            template <typename> typename ParticleInterfaceB, typename StackIterator>
-    template <typename... Args1, typename... Args2>
-    void CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB,
-    StackIterator>::SetParticleData(const std::tuple<Args1...> vA, const std::tuple<Args2...> vB) {
-      PI_A::SetParticleData(vA);
-      PI_B::SetParticleData(vB);
+    template <typename... TArgs1>
+    void setParticleData(std::tuple<TArgs1...> const vA) {
+      pi_a_type::setParticleData(vA);
+      pi_b_type::setParticleData();
+    }
+    template <typename... TArgs1, typename... TArgs2>
+    void setParticleData(std::tuple<TArgs1...> const vA, std::tuple<TArgs2...> const vB) {
+      pi_a_type::setParticleData(vA);
+      pi_b_type::setParticleData(vB);
     }
 
-	template <template <typename> typename ParticleInterfaceA,
-		            template <typename> typename ParticleInterfaceB, typename StackIterator>
-    template <typename... Args1>
-    void CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB,
-    StackIterator>::SetParticleData(PI_C& p, const std::tuple<Args1...> vA) {
+    template <typename... TArgs1>
+    void setParticleData(pi_a_type& p, std::tuple<TArgs1...> const vA) {
       // static_assert(MT<I>::has_not, "error");
-      PI_A::SetParticleData(static_cast<PI_A&>(p), vA); // original stack
-      PI_B::SetParticleData(static_cast<PI_B&>(p));     // addon stack
+      pi_a_type::setParticleData(static_cast<pi_a_type&>(p), vA); // original stack
+      pi_b_type::setParticleData(static_cast<pi_b_type&>(p));     // addon stack
     }
+    template <typename... TArgs1, typename... TArgs2>
+    void setParticleData(pi_c_type& p, std::tuple<TArgs1...> const vA,
+                         std::tuple<TArgs2...> const vB) {
 
-	template <template <typename> typename ParticleInterfaceA,
-		            template <typename> typename ParticleInterfaceB, typename StackIterator>
-    template <typename... Args1, typename... Args2>
-    void CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB,
-    StackIterator>::SetParticleData(PI_C& p, const std::tuple<Args1...> vA,
-                         const std::tuple<Args2...> vB) {
-      PI_A::SetParticleData(static_cast<PI_A&>(p), vA);
-      PI_B::SetParticleData(static_cast<PI_B&>(p), vB);
+      pi_a_type::setParticleData(static_cast<pi_a_type&>(p), vA);
+      pi_b_type::setParticleData(static_cast<pi_b_type&>(p), vB);
+    }
+    ///@}
+    std::string as_string() const {
+      return fmt::format("[[{}][{}]]", pi_a_type::as_string(), pi_b_type::as_string());
     }
 
-
-namespace detail {
-
+    protected:
+  };
+
+  /**
+   * @class CombinedStackImpl
+   *
+   * Memory implementation of a combined data stack.
+   *
+   * The two stack data user objects Stack1Impl and Stack2Impl are
+   * merged into one consistent Stack container object providing
+   * access to the combined number of data entries.
+   */
   template <typename Stack1Impl, typename Stack2Impl>
   class CombinedStackImpl : public Stack1Impl, public Stack2Impl {
 
   public:
-    void Init() {
-      Stack1Impl::Init();
-      Stack2Impl::Init();
-    }
-
-    void Clear() {
-      Stack1Impl::Clear();
-      Stack2Impl::Clear();
+    void clear() {
+      Stack1Impl::clear();
+      Stack2Impl::clear();
     }
 
-    unsigned int GetSize() const { return Stack1Impl::GetSize(); }
-    unsigned int GetCapacity() const { return Stack1Impl::GetCapacity(); }
+    unsigned int getSize() const { return Stack1Impl::getSize(); }
+    unsigned int getCapacity() const { return Stack1Impl::getCapacity(); }
 
     /**
      *   Function to copy particle at location i1 in stack to i2
      */
-    void Copy(const unsigned int i1, const unsigned int i2) {
-      if (i1 >= GetSize() || i2 >= GetSize()) {
+    void copy(const unsigned int i1, const unsigned int i2) {
+      if (i1 >= getSize() || i2 >= getSize()) {
         std::ostringstream err;
         err << "CombinedStack: trying to access data beyond size of stack!";
         throw std::runtime_error(err.str());
       }
-      Stack1Impl::Copy(i1, i2);
-      Stack2Impl::Copy(i1, i2);
+      Stack1Impl::copy(i1, i2);
+      Stack2Impl::copy(i1, i2);
     }
 
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    void Swap(const unsigned int i1, const unsigned int i2) {
-      if (i1 >= GetSize() || i2 >= GetSize()) {
+    void swap(const unsigned int i1, const unsigned int i2) {
+      if (i1 >= getSize() || i2 >= getSize()) {
         std::ostringstream err;
         err << "CombinedStack: trying to access data beyond size of stack!";
         throw std::runtime_error(err.str());
       }
-      Stack1Impl::Swap(i1, i2);
-      Stack2Impl::Swap(i1, i2);
+      Stack1Impl::swap(i1, i2);
+      Stack2Impl::swap(i1, i2);
     }
 
-    void IncrementSize() {
-      Stack1Impl::IncrementSize();
-      Stack2Impl::IncrementSize();
+    void incrementSize() {
+      Stack1Impl::incrementSize();
+      Stack2Impl::incrementSize();
     }
 
-    void DecrementSize() {
-      Stack1Impl::DecrementSize();
-      Stack2Impl::DecrementSize();
+    void decrementSize() {
+      Stack1Impl::decrementSize();
+      Stack2Impl::decrementSize();
     }
 
   }; // end class CombinedStackImpl
 
-}  // namespace detail
+  /**
+   * Helper template alias `CombinedStack` to construct new combined
+   * stack from two stack data objects and a particle readout interface.
+   *
+   * Note that the Stack2Impl provides only /additional/ data to
+   * Stack1Impl. This is important (see above) since tuple data for
+   * initialization are forwarded to Stack1Impl (first).
+   */
 
+  template <typename Stack1Impl, typename Stack2Impl, template <typename> typename _PI>
+  using CombinedStack = Stack<CombinedStackImpl<Stack1Impl, Stack2Impl>, _PI>;
 
 } // namespace corsika
 
+//#include <corsika/detail/framework/stack/CombinedStack.inl>
diff --git a/corsika/detail/framework/stack/SecondaryView.inl b/corsika/detail/framework/stack/SecondaryView.inl
index 13fa27de4..576f6406d 100644
--- a/corsika/detail/framework/stack/SecondaryView.inl
+++ b/corsika/detail/framework/stack/SecondaryView.inl
@@ -1,80 +1,534 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
+ * (c) copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
  *
  * This software is distributed under the terms of the GNU General Public
  * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
  * the license.
  */
-
 #pragma once
 
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/logging/Logging.hpp>
+
 #include <stdexcept>
 #include <vector>
-#include <corsika/framework/stack/Stack.hpp>
 
 namespace corsika {
 
+  // forward-decl:
+  template <class T1, template <class> class T2>
+  class DefaultSecondaryProducer;
+
+  /**
+   * @class SecondaryView
+   *
+   * SecondaryView can only be constructed by giving a valid
+   * Projectile particle, following calls to addSecondary will
+   * populate the original Stack, but will be directly accessible via
+   * the SecondaryView, e.g.
+
+     This allows to write code like
+     \verbatim
+     auto projectileInput = mainStack.getNextParticle();
+     const unsigned int nMain = mainStack.getSize();
+     SecondaryView<StackData, ParticleInterface> mainStackView(projectileInput);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     assert(mainStackView.getSize() == 4);
+     assert(mainStack.getSize() = nMain+4);
+     \endverbatim
+
+     All operations possible on a Stack object are also possible on a
+     SecondaryView object. This means you can add, delete, copy, swap,
+     iterate, etc.
+
+     *Further information about implementation (for developers):* All
+     data is stored in the original stack privided at construction
+     time. The secondary particle (view) indices are stored in an
+     extra std::vector of SecondaryView class 'indices_' referring to
+     the original stack slot indices. The index of the primary
+     projectle particle is also explicitly stored in
+     'projectile_index_'. StackIterator indices
+     'i = StackIterator::getIndex()' are referring to those numbers,
+     where 'i==0' refers to the 'projectile_index_', and
+     'StackIterator::getIndex()>0' to 'indices_[i-1]', see function
+     getIndexFromIterator.
+   */
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer =
+                DefaultSecondaryProducer>
+  class SecondaryView : public Stack<TStackDataType&, TParticleInterface>,
+                        public MSecondaryProducer<TStackDataType, TParticleInterface> {
+
+    typedef SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>
+        view_type;
+    /**
+     * Helper type for inside this class
+     */
+    typedef Stack<TStackDataType&, TParticleInterface> inner_stack_reference_type;
+
+    /**
+     * @name We need this "value" types with non-reference TStackData for
+     * the constructor of the SecondaryView class
+     * @{
+     */
+    typedef Stack<TStackDataType, TParticleInterface> inner_stack_value_type;
+
+  public:
+
+    typedef StackIteratorInterface<typename std::remove_reference<TStackDataType>::type,
+                                   TParticleInterface, inner_stack_value_type>
+        stack_value_iterator;
 
+    typedef ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        inner_stack_value_type>
+        const_stack_value_iterator;
+    /// @}
 
- // template <typename StackDataType, template <typename> typename ParticleInterface>
- // SecondaryView<StackDataType, ParticleInterface>:
+    typedef StackIteratorInterface<typename std::remove_reference<TStackDataType>::type,
+                                   TParticleInterface, view_type> stack_view_iterator;
 
+    typedef ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>
+        const_stack_view_iterator;
 
+    /**
+     * this is the full type of the declared TParticleInterface:
+     */
+    using ParticleType = stack_view_iterator;
+    using ParticleInterfaceType = typename stack_view_iterator::particle_interface_type;
 
-	template <typename StackDataType, template <typename> typename ParticleInterface>
+    /**
+     * This is not accessible, since we don't want to allow creating a
+     * new stack.
+     */
+
+    template <typename... TArgs>
+    SecondaryView(TArgs... args) = delete;
+    SecondaryView() = delete;
+
+    /**
+        SecondaryView can only be constructed passing it a valid
+        stack_view_iterator to another Stack object (here: lvalue)
+      **/
+    SecondaryView(stack_value_iterator& particle)
+        : Stack<TStackDataType&, TParticleInterface>(particle.getStackData())
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{particle}
+        , inner_stack_(particle.getStack())
+        , projectile_index_(particle.getIndex()) {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(particle&)");
+    }
+    /**
+       SecondaryView can only be constructed passing it a valid
+       stack_view_iterator to another Stack object (here: rvalue)
+     **/
+    SecondaryView(stack_value_iterator&& particle)
+        : Stack<TStackDataType&, TParticleInterface>(particle.getStackData())
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{particle}
+        , inner_stack_(particle.getStack())
+        , projectile_index_(particle.getIndex()) {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(particle&&)");
+    }
+    /**
+     * Also allow to create a new View from a Projectile (stack_view_iterator on View)
+     *
+     * Note, the view generated this way will be equivalent to the orignal view in
+     * terms of reference to the underlying data stack. It is not a "view to a view".
+     */
+    SecondaryView(view_type& view, stack_view_iterator& projectile)
+
+        : Stack<TStackDataType&, TParticleInterface>{view.getStackData()}
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{stack_value_iterator{
+              view.inner_stack_, view.getIndexFromIterator(projectile.getIndex())}}
+        , inner_stack_{view.inner_stack_}
+        , projectile_index_{view.getIndexFromIterator(projectile.getIndex())} {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(view, projectile)");
+    }
+
+    /**
+     * This returns the projectile/parent in the original Stack, where this
+     * SecondaryView is derived from. This projectile should not be
+     * used to modify the Stack!
+     */
+    stack_value_iterator parent()
+        const { // todo: check if this can't be Conststack_value_iterator
+      return stack_value_iterator(inner_stack_, projectile_index_);
+    }
+
+    /**
+     * This returns the projectile/parent in the original Stack, where this
+     * SecondaryView is derived from. This projectile should not be
+     * used to modify the Stack!
+     */
+    stack_value_iterator asNewParent() const {
+      return stack_value_iterator(inner_stack_, projectile_index_);
+    }
+
+    /**
+     * This return a projectile of this SecondaryView, which can be
+     * used to modify the SecondaryView
+     */
+    stack_view_iterator getProjectile() {
+      // NOTE: 0 is special marker here for PROJECTILE, see getIndexFromIterator
+      return stack_view_iterator(*this, 0);
+    }
+    /**
+     * Method to add a new secondary particle on this SecondaryView
+     */
     template <typename... Args>
-    auto SecondaryView<StackDataType, ParticleInterface>::AddSecondary(const Args... v) {
-      StackIterator proj = GetProjectile();
-      return AddSecondary(proj, v...);
+    stack_view_iterator addSecondary(const Args... v) {
+      CORSIKA_LOG_TRACE("SecondaryView::addSecondary(Args&&)");
+      stack_view_iterator proj = getProjectile(); // make this const
+      return addSecondary(proj, v...);
     }
+    /**
+     * overwrite Stack::getSize to return actual number of secondaries
+     */
+    unsigned int getSize() const { return indices_.size(); }
 
-	template <typename StackDataType, template <typename> typename ParticleInterface>
+    unsigned int getEntries() const {
+      return getSize() - inner_stack_reference_type::getErased();
+    }
+
+    bool isEmpty() const { return getEntries() == 0; }
+
+    /**
+     * @name These are functions required by std containers and std loops
+     * The Stack-versions must be overwritten, since here we need the correct
+     * SecondaryView::getSize
+     * @{
+     */
+    // NOTE: the "+1" is since "0" is special marker here for PROJECTILE, see
+    // getIndexFromIterator
+    stack_view_iterator begin() {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+
+        if (!isErased(i)) break;
+      }
+      return stack_view_iterator(*this, i + 1);
+    }
+
+    auto end() { return stack_view_iterator(*this, getSize() + 1); }
+
+    auto last() {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!isErased(getSize() - 1 - i)) break;
+      }
+      return stack_view_iterator(*this, getSize() - 1 - i + 1);
+    }
+
+    auto begin() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!isErased(i)) break;
+      }
+
+      return const_stack_view_iterator(*this, i + 1);
+    }
+
+    auto end() const { return const_stack_view_iterator(*this, getSize() + 1); }
+    auto last() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+
+        if (!isErased(getSize() - 1 - i)) break;
+      }
+      return const_stack_view_iterator(*this, getSize() - 1 - i + 1);
+    }
+
+    auto cbegin() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+
+        if (!isErased(i)) break;
+      }
+      return const_stack_view_iterator(*this, i + 1);
+    }
+
+
+    auto cend() const { return const_stack_view_iterator(*this, getSize()); }
+
+    auto clast() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+
+        if (!isErased(getSize() - 1 - i)) break;
+      }
+      return const_stack_view_iterator(*this, getSize() - 1 - i + 1);
+    }
+
+    stack_view_iterator at(unsigned int i) { return stack_view_iterator(*this, i); }
+
+    const_stack_view_iterator at(unsigned int i) const {
+      return const_stack_view_iterator(*this, i);
+    }
+
+    stack_view_iterator first() { return stack_view_iterator{*this, 0}; }
+
+    const_stack_view_iterator cfirst() const {
+      return const_stack_view_iterator{*this, 0};
+    }
+    /// @}
+
+    void swap(stack_view_iterator a, stack_view_iterator b) {
+
+      CORSIKA_LOG_TRACE("View::swap");
+      inner_stack_.swap(getIndexFromIterator(a.getIndex()),
+                        getIndexFromIterator(b.getIndex()));
+    }
+    void copy(stack_view_iterator a, stack_view_iterator b) {
+
+      CORSIKA_LOG_TRACE("View::copy");
+      inner_stack_.copy(getIndexFromIterator(a.getIndex()),
+                        getIndexFromIterator(b.getIndex()));
+    }
+    void copy(const_stack_view_iterator a, stack_view_iterator b) {
+
+      CORSIKA_LOG_TRACE("View::copy");
+      inner_stack_.copy(getIndexFromIterator(a.getIndex()),
+                        getIndexFromIterator(b.getIndex()));
+    }
+
+    /**
+     * need overwrite Stack::Delete, since we want to call
+     * SecondaryView::DeleteLast
+     *
+     * The particle is deleted on the underlying (internal) stack. The
+     * local references in SecondaryView in indices_ must be fixed,
+     * too.  The approach is to a) check if the particle 'p' is at the
+     * very end of the internal stack, b) if not: move it there by
+     * copying the last particle to the current particle location, c)
+     * remove the last particle.
+     *
+     */
+    void erase(stack_view_iterator p) {
+
+      CORSIKA_LOG_TRACE("SecondaryView::Delete");
+      if (isEmpty()) { /*error*/
+        throw std::runtime_error("Stack, cannot delete entry since size is zero");
+      }
+      if (isErased(p.getIndex() - 1)) { /*error*/
+        throw std::runtime_error("Stack, cannot delete entry since already deleted");
+      }
+      inner_stack_.erase(getIndexFromIterator(p.getIndex()));
+      inner_stack_reference_type::nDeleted_++; // also count in SecondaryView
+    }
+
+    /**
+     * return next particle from stack, need to overwrtie Stack::getNextParticle to get
+     * right reference
+     */
+    stack_view_iterator getNextParticle() {
+      while (purgeLastIfDeleted()) {}
+      return last();
+    }
+
+    /**
+     * check if this particle was already deleted
+     *
+     * need to re-implement for SecondaryView since stack_view_iterator types are a bit
+     * different
+     */
+    bool isErased(const stack_view_iterator& p) const {
+      return isErased(p.getIndex() - 1);
+    }
+
+    bool isErased(const const_stack_view_iterator& p) const {
+      return isErased(p.getIndex() - 1);
+    }
+    /**
+     * delete this particle
+     */
+    bool isErased(const ParticleInterfaceType& p) const {
+      return isErased(p.getIterator());
+    }
+
+    bool isDeleted(const const_stack_view_iterator& p) const {
+      return isDeleted(p.getIndex() - 1);
+    }
+    /**
+     * Function to ultimatively remove the last entry from the stack,
+     * if it was marked as deleted before. If this is not the case,
+     * the function will just return false and do nothing.
+     */
+    bool purgeLastIfDeleted() {
+      CORSIKA_LOG_TRACE("SecondaryView::purgeLastIfDeleted");
+      if (!isErased(getSize() - 1))
+        return false; // the last particle is not marked for deletion. Do nothing.
+      inner_stack_.purge(getIndexFromIterator(getSize()));
+      inner_stack_reference_type::nDeleted_--;
+      indices_.pop_back();
+      return true;
+    }
+
+    /**
+     * Function to ultimatively remove all entries from the stack
+     * marked as deleted.
+     *
+     * Careful: this will re-order the entries on the stack, since
+     * "gaps" in the stack are filled with entries from the back
+     * (copied).
+     */
+    void purge() {
+      unsigned int iStack = 0;
+      unsigned int size = getSize();
+      while (iStack < size) {
+
+        if (isErased(iStack)) {
+          inner_stack_.purge(iStack);
+          indices_.erase(indices_.begin() + iStack);
+        }
+        size = getSize();
+        iStack++;
+      }
+      inner_stack_reference_type::nDeleted_ = 0;
+    }
+
+    std::string as_string() const {
+      std::string str(fmt::format("size {}\n", getSize()));
+      // we make our own begin/end since we want ALL entries
+      std::string new_line = "     ";
+      for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
+        const_stack_view_iterator itPart(*this, iPart);
+        str += fmt::format(
+            "{}{}{}", new_line, itPart.as_string(),
+            (inner_stack_.deleted_[getIndexFromIterator(itPart.getIndex())] ? " [deleted]"
+                                                                            : ""));
+        new_line = "\n     ";
+      }
+      return str;
+    }
+
+  protected:
+
+    friend class StackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>;
+
+    friend class ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>;
+
+    friend class ParticleBase<stack_view_iterator>;
+
+    /**
+     * Overwrite of Stack::stack_view_iterator
+     *
+     * increase stack size, create new particle at end of stack,
+     * related to parent particle/projectile
+     *
+     * This should only get internally called from a
+     * stack_view_iterator::addSecondary via ParticleBase
+     */
     template <typename... Args>
-    auto SecondaryView<StackDataType, ParticleInterface>::AddSecondary(StackIterator& proj, const Args... v) {
+    stack_view_iterator addSecondary(stack_view_iterator& proj, const Args... v) {
+      CORSIKA_LOG_TRACE("SecondaryView::addSecondary(stack_view_iterator&, Args&&)");
       // make space on stack
-      InnerStackType::GetStackData().IncrementSize();
+      inner_stack_reference_type::getStackData().incrementSize();
+      inner_stack_.deleted_.push_back(false);
       // get current number of secondaries on stack
-      const unsigned int idSec = GetSize();
+      const unsigned int idSec = getSize();
       // determine index on (inner) stack where new particle will be located
-      const unsigned int index = InnerStackType::GetStackData().GetSize() - 1;
-      fIndices.push_back(index);
+      const unsigned int index = inner_stack_reference_type::getStackData().getSize() - 1;
+      indices_.push_back(index);
       // NOTE: "+1" is since "0" is special marker here for PROJECTILE, see
-      // GetIndexFromIterator
-      return StackIterator(*this, idSec + 1, proj, v...);
+      // getIndexFromIterator
+      auto sec = stack_view_iterator(*this, idSec + 1, proj, v...);
+      MSecondaryProducer<TStackDataType, TParticleInterface>::new_secondary(sec);
+      return sec;
     }
 
+    // forward to inner stack
+    // this also checks the allowed bounds of 'i'
+    bool isErased(unsigned int i) const {
+      if (i >= indices_.size()) return false;
+      return inner_stack_.isErased(getIndexFromIterator(i + 1));
+    }
 
-
-	template <typename StackDataType, template <typename> typename ParticleInterface>
-    void SecondaryView<StackDataType, ParticleInterface>::Delete(StackIterator p) {
-      if (IsEmpty()) { /* error */
-        throw std::runtime_error("Stack, cannot delete entry since size is zero");
-      }
-      const int innerSize = InnerStackType::GetSize();
-      const int innerIndex = GetIndexFromIterator(p.GetIndex());
-      if (innerIndex < innerSize - 1)
-        InnerStackType::GetStackData().Copy(innerSize - 1,
-                                            GetIndexFromIterator(p.GetIndex()));
-      DeleteLast();
+    /**
+     * We only want to 'see' secondaries indexed in indices_. In this
+     * function the conversion form iterator-index to stack-index is
+     * performed.
+     */
+    unsigned int getIndexFromIterator(const unsigned int vI) const {
+      // this is too much: CORSIKA_LOG_TRACE("SecondaryView::getIndexFromIterator({})={}",
+      // vI, (vI?indices_[vI-1]:projectile_index_));
+      if (vI == 0) return projectile_index_;
+      return indices_[vI - 1];
     }
 
-    template <typename StackDataType, template <typename> typename ParticleInterface>
-    void SecondaryView<StackDataType, ParticleInterface>::Delete(ParticleInterfaceType p) { Delete(p.GetIterator()); }
+  private:
+    inner_stack_value_type& inner_stack_;
+    unsigned int projectile_index_;
+    std::vector<unsigned int> indices_;
+  };
 
-   template <typename StackDataType, template <typename> typename ParticleInterface>
-    void SecondaryView<StackDataType, ParticleInterface>::DeleteLast() {
-      fIndices.pop_back();
-      InnerStackType::GetStackData().DecrementSize();
+
+  /**
+   * Class to handle the generation of new secondaries. Used as default mix-in for
+   * SecondaryView.
+   */
+  template <class T1, template <class> class T2>
+  class DefaultSecondaryProducer {
+    using View = SecondaryView<T1, T2, DefaultSecondaryProducer>;
+
+  public:
+    static bool constexpr has_event{false};
+
+    /**
+     * Method is called after a new secondary has been created on the
+     * SecondaryView. Extra logic can be introduced here.
+     *
+     * The input Particle is the new secondary that was produced and
+     * is of course a reference into the SecondaryView itself.
+     */
+    template <typename Particle>
+    auto new_secondary(Particle&&) const {
+      CORSIKA_LOG_TRACE("DefaultSecondaryProducer::new_secondary(Particle&&)");
     }
 
-   template <typename StackDataType, template <typename> typename ParticleInterface>
-   unsigned int SecondaryView<StackDataType, ParticleInterface>::GetIndexFromIterator(const unsigned int vI) const {
-      if (vI == 0) return fProjectileIndex;
-      return fIndices[vI - 1];
+    /**
+     * Method is called when a new SecondaryView is being created
+     * created. Extra logic can be introduced here.
+     *
+     * The input Particle is a reference object into the original
+     * parent stack! It is not a reference into the SecondaryView
+     * itself.
+     */
+    template <typename Particle>
+    DefaultSecondaryProducer(Particle const&) {
+
+      CORSIKA_LOG_TRACE("DefaultSecondaryProducer::DefaultSecondaryProducer(Particle&)");
     }
+  };
+
+  /*
+    See Issue 161
+
+    unfortunately clang does not support this in the same way (yet) as
+    gcc, so we have to distinguish here. If clang cataches up, we
+    could remove the #if here and elsewhere. The gcc code is much more
+    generic and universal.
+  */
+#if not defined(__clang__) && defined(__GNUC__) || defined(__GNUG__)
+  template <typename TStack,
 
+            template <class TStack_, template <class> class pi_type_>
+            class MSecondaryProducer = corsika::DefaultSecondaryProducer,
+            template <typename> typename pi_type_ = TStack::template pi_type>
+  struct MakeView {
+    using type = corsika::SecondaryView<typename TStack::stack_implementation_type,
+                                        pi_type_, MSecondaryProducer>;
+  };
+#endif
 
 } // namespace corsika
 
+//#include <corsika/detail/framework/stack/SecondaryView.inl>
diff --git a/corsika/detail/framework/stack/Stack.inl b/corsika/detail/framework/stack/Stack.inl
new file mode 100644
index 000000000..3c532426e
--- /dev/null
+++ b/corsika/detail/framework/stack/Stack.inl
@@ -0,0 +1,368 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+
+#include <corsika/framework/logging/Logging.hpp>
+#include <corsika/framework/stack/StackIteratorInterface.hpp>
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+#include <type_traits>
+
+
+namespace corsika {
+
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    template <typename... TArgs>
+    void Stack<StackData, MParticleInterface>::clear(TArgs... args) {
+      data_.clear(args...);
+      deleted_ = std::vector<bool>(data_.getSize(), false);
+      nDeleted_ = 0;
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::begin() {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[i]) break;
+      }
+      return stack_iterator_type(*this, i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::end() {
+    	return stack_iterator_type(*this, getSize());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::last() {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[getSize() - 1 - i]) break;
+      }
+      return stack_iterator_type(*this, getSize() - 1 - i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::begin() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[i]) break;
+      }
+      return const_stack_iterator_type(*this, i);
+    }
+
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::end() const {
+      return const_stack_iterator_type(*this, getSize());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::last() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[getSize() - 1 - i]) break;
+      }
+      return const_stack_iterator_type(*this, getSize() - 1 - i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::cbegin() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[i]) break;
+      }
+      return const_stack_iterator_type(*this, i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::cend() const {
+      return const_stack_iterator_type(*this, getSize());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::clast() const {
+      unsigned int i = 0;
+      for (; i < getSize(); ++i) {
+        if (!deleted_[getSize() - 1 - i]) break;
+      }
+
+      return const_stack_iterator_type(*this, getSize() - 1 - i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::at(unsigned int i) {
+    	return stack_iterator_type(*this, i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::at(unsigned int i) const {
+      return const_stack_iterator_type(*this, i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::first() {
+    	return stack_iterator_type{*this, 0};
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+	Stack<StackData, MParticleInterface>::cfirst() const {
+      return const_stack_iterator_type{*this, 0};
+    }
+    /// @}
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename  Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::getNextParticle() {
+      while (purgeLastIfDeleted()) {}
+      return last();
+    }
+
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    template <typename... TArgs>
+    typename Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::addParticle(const TArgs... v) {
+      CORSIKA_LOG_TRACE("Stack::AddParticle");
+      data_.incrementSize();
+      deleted_.push_back(false);
+      return stack_iterator_type(*this, getSize() - 1, v...);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::swap(stack_iterator_type a, stack_iterator_type b) {
+      CORSIKA_LOG_TRACE("Stack::Swap");
+      swap(a.getIndex(), b.getIndex());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::copy(stack_iterator_type a, stack_iterator_type b) {
+      CORSIKA_LOG_TRACE("Stack::Copy");
+      copy(a.getIndex(), b.getIndex());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::copy(const_stack_iterator_type a, stack_iterator_type b) {
+      CORSIKA_LOG_TRACE("Stack::Copy");
+      data_.copy(a.getIndex(), b.getIndex());
+      if (deleted_[b.getIndex()] && !deleted_[a.getIndex()]) nDeleted_--;
+      if (!deleted_[b.getIndex()] && deleted_[a.getIndex()]) nDeleted_++;
+      deleted_[b.getIndex()] = deleted_[a.getIndex()];
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::erase(stack_iterator_type p) {
+      CORSIKA_LOG_TRACE("Stack::Delete");
+      if (this->isEmpty()) { /*error*/
+        throw std::runtime_error("Stack, cannot delete entry since size is zero");
+      }
+      if (deleted_[p.getIndex()]) { /*error*/
+        throw std::runtime_error("Stack, cannot delete entry since already deleted");
+      }
+      this->erase(p.getIndex());
+    }
+    /**
+     * delete this particle
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::erase(particle_interface_type p) { this->erase(p.getIterator()); }
+
+    /**
+     * check if there are no further non-deleted particles on stack
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::isEmpty() { return getEntries() == 0; }
+
+    /**
+     * check if this particle was already deleted
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::isErased(const stack_iterator_type& p) const { return isErased(p.getIndex()); }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::isErased(const const_stack_iterator_type& p) const {
+      return isErased(p.getIndex());
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::isErased(const particle_interface_type& p) const {
+      return isErased(p.getIterator());
+    }
+
+    /**
+     * Function to ultimatively remove the last entry from the stack,
+     * if it was marked as deleted before. If this is not the case,
+     * the function will just return false and do nothing.
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::purgeLastIfDeleted() {
+      if (!deleted_.back())
+        return false; // the last particle is not marked for deletion. Do nothing.
+
+      CORSIKA_LOG_TRACE("Stack::purgeLastIfDeleted: yes");
+      data_.decrementSize();
+      nDeleted_--;
+      deleted_.pop_back();
+      return true;
+    }
+
+    /**
+     * Function to ultimatively remove all entries from the stack
+     * marked as deleted.
+     *
+     * Careful: this will re-order the entries on the stack, since
+     * "gaps" in the stack are filled with entries from the back
+     * (copied).
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::purge() {
+      unsigned int iStackFront = 0;
+      unsigned int iStackBack = getSize() - 1;
+
+      for (unsigned int iDeleted = 0; iDeleted < getErased(); ++iDeleted) {
+        // search first delete entry on stack
+        while (!deleted_[iStackFront]) { iStackFront++; }
+        // search for last non-deleted particle on stack
+        while (deleted_[iStackBack]) { iStackBack--; }
+        // copy entry from iStackBack to iStackFront
+        data_.copy(iStackBack, iStackFront);
+        data_.decrementSize();
+      }
+      deleted_.clear();
+      nDeleted_ = 0;
+    }
+
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    unsigned int Stack<StackData, MParticleInterface>::getSize() const { return data_.getSize(); }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    std::string Stack<StackData, MParticleInterface>::as_string() const {
+      std::string str(fmt::format("size {}, entries {}, deleted {} \n", getSize(),
+                                  getEntries(), getErased()));
+      // we make our own begin/end since we want ALL entries
+      std::string new_line = "     ";
+      for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
+        const_stack_iterator_type itPart(*this, iPart);
+        str += fmt::format("{}{}{}", new_line, itPart.as_string(),
+                           (deleted_[itPart.getIndex()] ? " [deleted]" : ""));
+        new_line = "\n     ";
+      }
+      return str;
+    }
+
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    template <typename... TArgs>
+    typename  Stack<StackData, MParticleInterface>::stack_iterator_type
+	Stack<StackData, MParticleInterface>::addSecondary(stack_iterator_type& parent, const TArgs... v) {
+      CORSIKA_LOG_TRACE("Stack::AddSecondary");
+      data_.incrementSize();
+      deleted_.push_back(false);
+      return stack_iterator_type(*this, getSize() - 1, parent, v...);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::swap(unsigned int const a, unsigned int const b) {
+      CORSIKA_LOG_TRACE("Stack::Swap(unsigned int)");
+      data_.swap(a, b);
+      std::swap(deleted_[a], deleted_[b]);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::copy(unsigned int const a, unsigned int const b) {
+      CORSIKA_LOG_TRACE("Stack::Copy");
+      data_.copy(a, b);
+      if (deleted_[b] && !deleted_[a]) nDeleted_--;
+      if (!deleted_[b] && deleted_[a]) nDeleted_++;
+      deleted_[b] = deleted_[a];
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    bool Stack<StackData, MParticleInterface>::isErased(unsigned int const i) const {
+      if (i >= deleted_.size()) return false;
+      return deleted_.at(i);
+    }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::erase(unsigned int const i) {
+      deleted_[i] = true;
+      nDeleted_++;
+    }
+
+    /**
+     * will remove from storage the element i. This is a helper
+     * function for SecondaryView.
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    void Stack<StackData, MParticleInterface>::purge(unsigned int i) {
+      unsigned int iStackBack = getSize() - 1;
+      // search for last non-deleted particle on stack
+      while (deleted_[iStackBack]) { iStackBack--; }
+      // copy entry from iStackBack to iStackFront
+      data_.copy(iStackBack, i);
+      if (deleted_[i]) nDeleted_--;
+      deleted_[i] = deleted_[iStackBack];
+      data_.decrementSize();
+      deleted_.pop_back();
+    }
+
+    /**
+     * Function to perform eventual transformation from
+     * StackIterator::getIndex() to index in data stored in
+     * StackData data_. By default (and in almost all cases) this
+     * should just be identiy. See class SecondaryView for an alternative implementation.
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    unsigned int Stack<StackData, MParticleInterface>::getIndexFromIterator(const unsigned int vI) const {
+      // this is too much: CORSIKA_LOG_TRACE("Stack::getIndexFromIterator({})={}", vI, vI);
+      return vI;
+    }
+
+    /**
+     * @name Return reference to StackData object data_ for data access
+     * @{
+     */
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+    typename Stack<StackData, MParticleInterface>::value_type&
+	Stack<StackData, MParticleInterface>::getStackData() { return data_; }
+
+    template <typename StackData, template <typename> typename MParticleInterface>
+     const typename Stack<StackData, MParticleInterface>::value_type&
+	Stack<StackData, MParticleInterface>::getStackData() const { return data_; }
+
+
+
+} // namespace corsika
diff --git a/corsika/framework/stack/Stack.hpp b/corsika/framework/stack/Stack.hpp
index a7ed1cdb0..4fc5b0a8c 100644
--- a/corsika/framework/stack/Stack.hpp
+++ b/corsika/framework/stack/Stack.hpp
@@ -83,8 +83,6 @@ namespace corsika {
      */
     typedef stack_iterator_type particle_type;
 
-    //========================
-
     Stack() = default;
 
     Stack(Stack&) = delete; ///< since Stack can be very big, we don't want to copy it
@@ -130,170 +128,82 @@ namespace corsika {
     unsigned int getEntries() const { return getSize() - getErased(); }
 
     template <typename... TArgs>
-    void clear(TArgs... args) {
-      data_.clear(args...);
-      deleted_ = std::vector<bool>(data_.getSize(), false);
-      nDeleted_ = 0;
-    }
+    void clear(TArgs... args);
     ///@}
 
     /**
      * @name These are functions required by std containers and std loops
      * @{
      */
-    stack_iterator_type begin() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return stack_iterator_type(*this, i);
-    }
-
-    stack_iterator_type end() { return stack_iterator_type(*this, getSize()); }
-
-    stack_iterator_type last() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return stack_iterator_type(*this, getSize() - 1 - i);
-    }
-
-    const_stack_iterator_type begin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return const_stack_iterator_type(*this, i);
-    }
-
-    const_stack_iterator_type end() const {
-      return const_stack_iterator_type(*this, getSize());
-    }
-
-    const_stack_iterator_type last() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return const_stack_iterator_type(*this, getSize() - 1 - i);
-    }
-
-    const_stack_iterator_type cbegin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return const_stack_iterator_type(*this, i);
-    }
-
-    const_stack_iterator_type cend() const {
-      return const_stack_iterator_type(*this, getSize());
-    }
-
-    const_stack_iterator_type clast() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return const_stack_iterator_type(*this, getSize() - 1 - i);
-    }
-
-    stack_iterator_type at(unsigned int i) { return stack_iterator_type(*this, i); }
-
-    const_stack_iterator_type at(unsigned int i) const {
-      return const_stack_iterator_type(*this, i);
-    }
-
-    stack_iterator_type first() { return stack_iterator_type{*this, 0}; }
-
-    const_stack_iterator_type cfirst() const {
-      return const_stack_iterator_type{*this, 0};
-    }
-    /// @}
-
-    stack_iterator_type getNextParticle() {
-      while (purgeLastIfDeleted()) {}
-      return last();
-    }
+    stack_iterator_type begin();
+
+    stack_iterator_type end() ;
+
+    stack_iterator_type last();
+
+    const_stack_iterator_type begin() const;
+
+    const_stack_iterator_type end() const ;
+
+    const_stack_iterator_type last() const ;
+
+    const_stack_iterator_type cbegin() const;
+
+    const_stack_iterator_type cend() const;
+
+    const_stack_iterator_type clast() const;
+
+    stack_iterator_type at(unsigned int i);
+
+    const_stack_iterator_type at(unsigned int i) const;
+
+    stack_iterator_type first();
+
+    const_stack_iterator_type cfirst() const;
+
+    stack_iterator_type getNextParticle();
 
     /**
      * increase stack size, create new particle at end of stack
      */
     template <typename... TArgs>
-    stack_iterator_type addParticle(const TArgs... v) {
-      // C8LOG_TRACE("Stack::AddParticle");
-      data_.incrementSize();
-      deleted_.push_back(false);
-      return stack_iterator_type(*this, getSize() - 1, v...);
-    }
-
-    void swap(stack_iterator_type a, stack_iterator_type b) {
-      // C8LOG_TRACE("Stack::Swap");
-      swap(a.getIndex(), b.getIndex());
-    }
-
-    void copy(stack_iterator_type a, stack_iterator_type b) {
-      // C8LOG_TRACE("Stack::Copy");
-      copy(a.getIndex(), b.getIndex());
-    }
-
-    void copy(const_stack_iterator_type a, stack_iterator_type b) {
-      // C8LOG_TRACE("Stack::Copy");
-      data_.copy(a.getIndex(), b.getIndex());
-      if (deleted_[b.getIndex()] && !deleted_[a.getIndex()]) nDeleted_--;
-      if (!deleted_[b.getIndex()] && deleted_[a.getIndex()]) nDeleted_++;
-      deleted_[b.getIndex()] = deleted_[a.getIndex()];
-    }
-
-    void erase(stack_iterator_type p) {
-      // C8LOG_TRACE("Stack::Delete");
-      if (this->isEmpty()) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since size is zero");
-      }
-      if (deleted_[p.getIndex()]) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since already deleted");
-      }
-      this->erase(p.getIndex());
-    }
+    stack_iterator_type addParticle(const TArgs... v) ;
+
+    void swap(stack_iterator_type a, stack_iterator_type b);
+
+    void copy(stack_iterator_type a, stack_iterator_type b);
+
+    void copy(const_stack_iterator_type a, stack_iterator_type b);
+
+    void erase(stack_iterator_type p);
     /**
      * delete this particle
      */
-    void erase(particle_interface_type p) { this->erase(p.getIterator()); }
+
+    void erase(particle_interface_type p);
 
     /**
      * check if there are no further non-deleted particles on stack
      */
-    bool isEmpty() { return getEntries() == 0; }
+
+    bool isEmpty();
 
     /**
      * check if this particle was already deleted
      */
-    bool isErased(const stack_iterator_type& p) const { return isErased(p.getIndex()); }
 
-    bool isErased(const const_stack_iterator_type& p) const {
-      return isErased(p.getIndex());
-    }
+    bool isErased(const stack_iterator_type& p) const;
 
-    bool isErased(const particle_interface_type& p) const {
-      return isErased(p.getIterator());
-    }
+    bool isErased(const const_stack_iterator_type& p) const;
+
+    bool isErased(const particle_interface_type& p) const;
 
     /**
      * Function to ultimatively remove the last entry from the stack,
      * if it was marked as deleted before. If this is not the case,
      * the function will just return false and do nothing.
      */
-    bool purgeLastIfDeleted() {
-      if (!deleted_.back())
-        return false; // the last particle is not marked for deletion. Do nothing.
-      // C8LOG_TRACE("Stack::purgeLastIfDeleted: yes");
-      data_.decrementSize();
-      nDeleted_--;
-      deleted_.pop_back();
-      return true;
-    }
-
+    bool purgeLastIfDeleted();
     /**
      * Function to ultimatively remove all entries from the stack
      * marked as deleted.
@@ -302,37 +212,12 @@ namespace corsika {
      * "gaps" in the stack are filled with entries from the back
      * (copied).
      */
-    void purge() {
-      unsigned int iStackFront = 0;
-      unsigned int iStackBack = getSize() - 1;
-      for (unsigned int iDeleted = 0; iDeleted < getErased(); ++iDeleted) {
-        // search first delete entry on stack
-        while (!deleted_[iStackFront]) { iStackFront++; }
-        // search for last non-deleted particle on stack
-        while (deleted_[iStackBack]) { iStackBack--; }
-        // copy entry from iStackBack to iStackFront
-        data_.copy(iStackBack, iStackFront);
-        data_.decrementSize();
-      }
-      deleted_.clear();
-      nDeleted_ = 0;
-    }
-
-    unsigned int getSize() const { return data_.getSize(); }
-
-    std::string as_string() const {
-      std::string str(fmt::format("size {}, entries {}, deleted {} \n", getSize(),
-                                  getEntries(), getErased()));
-      // we make our own begin/end since we want ALL entries
-      std::string new_line = "     ";
-      for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
-        const_stack_iterator_type itPart(*this, iPart);
-        str += fmt::format("{}{}{}", new_line, itPart.as_string(),
-                           (deleted_[itPart.getIndex()] ? " [deleted]" : ""));
-        new_line = "\n     ";
-      }
-      return str;
-    }
+    void purge();
+
+
+    unsigned int getSize() const;
+
+    std::string as_string() const;
 
   protected:
 
@@ -343,110 +228,22 @@ namespace corsika {
      * This should only get internally called from a
      * StackIterator::AddSecondary via ParticleBase
      */
-    /*
     template <typename... TArgs>
-    stack_iterator_type addSecondary(stack_iterator_type& parent, const TArgs... v) {
-      CORSIKA_LOG_TRACE("Stack::AddSecondary");
-      data_.incrementSize();
-      deleted_.push_back(false);
-      return stack_iterator_type(*this, getSize() - 1, parent, v...);
-    }
-
-    void swap(unsigned int a, unsigned int b) {
-      CORSIKA_LOG_TRACE("Stack::Swap(unsigned int)");
-      data_.swap(a, b);
-      std::swap(deleted_[a], deleted_[b]);
-    }
-    void copy(unsigned int a, unsigned int b) {
-      CORSIKA_LOG_TRACE("Stack::Copy");
-      data_.copy(a, b);
-      if (deleted_[b] && !deleted_[a]) nDeleted_--;
-      if (!deleted_[b] && deleted_[a]) nDeleted_++;
-      deleted_[b] = deleted_[a];
-    }
-
-    bool isDeleted(unsigned int i) const {
-      if (i >= deleted_.size()) return false;
-      return deleted_.at(i);
-    }
-
-    void erase(unsigned int i) {
-      deleted_[i] = true;
-      nDeleted_++;
-    }
-    */
-
-    /*
-     * will remove from storage the element i. This is a helper
-     * function for SecondaryView.
-     */
+    stack_iterator_type addSecondary(stack_iterator_type& parent, const TArgs... v) ;
 
-    /*
-    void purge(unsigned int i) {
-      unsigned int iStackBack = getSize() - 1;
-      // search for last non-deleted particle on stack
-      while (deleted_[iStackBack]) { iStackBack--; }
-      // copy entry from iStackBack to iStackFront
-      data_.copy(iStackBack, i);
-      if (deleted_[i]) nDeleted_--;
-      deleted_[i] = deleted_[iStackBack];
-      data_.decrementSize();
-      deleted_.pop_back();
-    }
-    */
-    /**
-     * increase stack size, create new particle at end of stack, related to parent
-     * particle/projectile
-     *
-     * This should only get internally called from a
-     * StackIterator::AddSecondary via ParticleBase
-     */
-    template <typename... TArgs>
-    stack_iterator_type addSecondary(stack_iterator_type& parent, const TArgs... v) {
-      // C8LOG_TRACE("Stack::AddSecondary");
-      data_.incrementSize();
-      deleted_.push_back(false);
-      return stack_iterator_type(*this, getSize() - 1, parent, v...);
-    }
-
-    void swap(unsigned int const a, unsigned int const b) {
-      // C8LOG_TRACE("Stack::Swap(unsigned int)");
-      data_.swap(a, b);
-      std::swap(deleted_[a], deleted_[b]);
-    }
-    void copy(unsigned int const a, unsigned int const b) {
-      // C8LOG_TRACE("Stack::Copy");
-      data_.copy(a, b);
-      if (deleted_[b] && !deleted_[a]) nDeleted_--;
-      if (!deleted_[b] && deleted_[a]) nDeleted_++;
-      deleted_[b] = deleted_[a];
-    }
-
-    bool isErased(unsigned int const i) const {
-      if (i >= deleted_.size()) return false;
-      return deleted_.at(i);
-    }
-
-    void erase(unsigned int const i) {
-      deleted_[i] = true;
-      nDeleted_++;
-    }
+    void swap(unsigned int const a, unsigned int const b);
+
+    void copy(unsigned int const a, unsigned int const b);
+
+    bool isErased(unsigned int const i) const;
+
+    void erase(unsigned int const i) ;
 
     /**
      * will remove from storage the element i. This is a helper
      * function for SecondaryView.
      */
-    void purge(unsigned int i) {
-      unsigned int iStackBack = getSize() - 1;
-      // search for last non-deleted particle on stack
-      while (deleted_[iStackBack]) { iStackBack--; }
-      // copy entry from iStackBack to iStackFront
-      data_.copy(iStackBack, i);
-      if (deleted_[i]) nDeleted_--;
-      deleted_[i] = deleted_[iStackBack];
-      data_.decrementSize();
-      deleted_.pop_back();
-    }
+    void purge(unsigned int i);
 
     /**
      * Function to perform eventual transformation from
@@ -454,22 +251,16 @@ namespace corsika {
      * StackData data_. By default (and in almost all cases) this
      * should just be identiy. See class SecondaryView for an alternative implementation.
      */
-    unsigned int getIndexFromIterator(const unsigned int vI) const {
-      // this is too much: //C8LOG_TRACE("Stack::getIndexFromIterator({})={}", vI, vI);
-      return vI;
-    }
-
+    unsigned int getIndexFromIterator(const unsigned int vI) const;
     /**
      * @name Return reference to StackData object data_ for data access
      * @{
      */
-    value_type& getStackData() { return data_; }
 
-    const value_type& getStackData() const { return data_; }
-    ///@}
-    ///
+    value_type& getStackData();
+
+    const value_type& getStackData() const;
 
-    ///
     friend class StackIteratorInterface<value_type, MParticleInterface, Stack>;
     friend class ConstStackIteratorInterface<value_type, MParticleInterface, Stack>;
     template <typename T1, //=StackData,
@@ -491,3 +282,5 @@ namespace corsika {
   };
 
 } // namespace corsika
+
+#include <corsika/detail/framework/stack/Stack.inl>
-- 
GitLab