/*
    GNU Octave level-set package.
    Copyright (C) 2013-2014  Daniel Kraft <d@domob.eu>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef HEAP_HPP
#define HEAP_HPP

/* Manual implementation of a binary heap, which is used by the fast marching
   algorithm.  Unfortunately I can't use the STL provided stuff directly,
   since we need "bubbling up" of values that were already in the heap and
   that changed to a lesser distance.  Hence I implement my own.

   Mapping from the linear array physically holding the elements to the
   tree structure and back is performed by the Entry class.  It also
   adds an additional layer of redirection so that we can store "permanent"
   references to heap elements (pointers to the Entry objects) which are
   not invalidated and keep pointing to the same element and also its physical
   position inside the heap even when the heap is bubbled in some way.  */

#include <cassert>
#include <stdexcept>
#include <vector>

namespace fastMarching
{

/* ************************************************************************** */
/* Heap class itself.  */

/**
 * This class represents the binary heap, with all the basic operations
 * necessary for it.  As underlying store we use a std::vector for simplicity.
 */
template<typename T, typename C>
  class Heap
{

  private:

    class EntryData;

    /**
     * Type of array holding physical data and the "master" pointers to
     * the memory allocated to entries.
     */
    typedef std::vector<EntryData*> entryArr;

    /** Type indexing a physical entry.  */
    typedef typename entryArr::size_type indexT;

    /** Hold the elements.  */
    entryArr data;

    /** Our comparator functor.  */
    const C compare;

  public:

    /** Pointer to an entry as it can be used from the outside.  */
    typedef EntryData* Entry;

    /* No copying for simplicity.  The default implementations should work,
       but it is not needed.  */
    Heap () = delete;
    Heap (const Heap& o) = delete;
    Heap& operator= (const Heap& o) = delete;

    /**
     * Construct an empty heap with the given comparator.
     * @param c Comparator object to use.
     */
    explicit inline Heap (const C& c)
      : data(), compare(c)
    {}

    /**
     * Destruct freeing all memory.
     */
    ~Heap ();

    /**
     * Get entry at the top of the heap.
     * @return Entry reference to top of heap.
     * @throws std::out_of_range if the heap is empty.
     */
    inline Entry
    top ()
    {
      if (empty ())
        throw std::out_of_range ("top() for empty heap");

      return data.front ();
    }

    /**
     * Pop off and remove the top.
     * @throws std::out_of_range if the heap is empty.
     */
    void pop ();

    /**
     * Push a new element and return its entry.
     * @param e The new element.
     * @return Entry reference to it after finding the correct position.
     */
    Entry push (const T& e);

    /**
     * Bubble an element pointed to by the entry up to the correct position.
     * @param e Entry to bubble up.
     * @throws std::invalid_argument if the element belongs not to this heap.
     */
    void bubbleUp (Entry e);

    /**
     * Bubble an element pointed to by the entry down to the correct position.
     * @param e Entry to bubble down.
     * @throws std::invalid_argument if the element belongs not to this heap.
     */
    void bubbleDown (Entry e);

    /**
     * Query whether the heap is empty or not.
     * @return True iff no elements are present.
     */
    inline bool
    empty () const
    {
      return data.empty ();
    }

};

/* ************************************************************************** */
/* Heap entry class.  */

/**
 * Represent an entry in the heap.  This holds both the actual data and
 * also the position in the heap vector.  It allows accessing parent nodes
 * and children in the binary tree.
 */
template<typename T, typename C>
  class Heap<T, C>::EntryData
{

  private:

    friend class Heap;

    /** Type of parent heap.  */
    typedef Heap<T, C> parentT;

    /** The heap we're part of.  */
    parentT& parent;

    /** Piece of data hold.  */
    const T data;

    /** Current index into the heap array.  */
    parentT::indexT index;

    /**
     * Construct it, given the parent heap, data entry and index.
     * @param h Parent heap.
     * @param d Data entry.
     * @param i Index.
     */
    inline EntryData (parentT& h, const T& d, parentT::indexT i)
      : parent(h), data(d), index(i)
    {}

    /**
     * Verify that the subtree belonging to this entry is in fact really
     * a heap satisfying the invariant.  An assertion failure is thrown if not.
     */
    void verifySubtree ();

    /**
     * Swap two entries with each other in the vector.  This is a utility
     * routine, which updates the vector holding the entries as well
     * as updating the indices.  Of course, they must belong to the same
     * parent heap.
     * @param a First entry.
     * @param b Second entry.
     */
    static void swap (EntryData& a, EntryData& b);

  public:

    /* No copying, users should use pointers to this one.  */
    EntryData () = delete;
    EntryData (const EntryData& o) = delete;
    EntryData& operator= (const EntryData& o) = delete;

    /* No destructor necessary.  */

    /**
     * Access data element.
     * @return Data element.
     */
    inline const T&
    operator* () const
    {
      return data;
    }

    /**
     * Get pointer to entry of parent in the tree.
     * @return Parent in the tree as Entry or NULL if we're on top.
     */
    Entry up () const;

    /**
     * Get pointer to one of the childs in the tree.
     * @param c Which child to get (0 or 1).
     * @return Selected child as Entry or NULL if there's no such child.
     * @throws std::invalid_argument if c is not 0 or 1.
     */
    Entry down (parentT::indexT c) const;

    /**
     * Bubble up this entry in the containing heap.
     */
    inline void
    bubbleUp ()
    {
      parent.bubbleUp (this);
    }

    /**
     * Bubble down this entry in the containing heap.
     */
    inline void
    bubbleDown ()
    {
      parent.bubbleDown (this);
    }

};

/* ************************************************************************** */

} // Namespace fastMarching.

/* Include template implementations.  */
#include "Heap.tpp"

#endif /* Header guard.  */
