// Classes for storing lists with tail-sharing.
//
// Copyright 2001, KUN.
//
// 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 2 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 Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

// $Id: insout.h,v 1.10 2005/05/04 15:25:33 andres Exp $

#ifndef INSOUT_H
#define INSOUT_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <set>


//------------------------------------------------------------------------------
// Class:
//      InsOutNode
//
// Description:
//      Stores a list element. Needed in InsOutLT.
//------------------------------------------------------------------------------

template<class T>
struct InsOutNode
{
    InsOutNode(const T& elem, const InsOutNode<T>* next);

    T elem;
    const InsOutNode<T>* next;
    unsigned length() const;
};

template<class T> inline
InsOutNode<T>::InsOutNode(const T& el, const InsOutNode<T>* p_node)
{
    elem = el;
    next = p_node;
}

template<class T> inline unsigned
InsOutNode<T>::length() const
{
    if (!next) return 1;
    return 1 + next->length();
}

template<class T> inline bool
operator ==(const InsOutNode<T> &node1, const InsOutNode<T> &node2)
{
#if 0
    return (node1.elem == node2.elem) && 
           ((node1.next == node2.next) ? true :
            (((!node1.next) || (!node2.next)) ? false :
             (*(node1.next) == *(node2.next))));
#else
    // Isn't this a lot more readable than the spaghetti above?
    return node1.elem == node2.elem && 
           (node1.next == node2.next ||
            (node1.next && node2.next && *node1.next == *node2.next));
#endif
}


//------------------------------------------------------------------------------
// Class:
//      InsOutNodeLT
//
// Description:
//      structure defining the ordering operator for the class InsOutTable.
//------------------------------------------------------------------------------

template<class T> inline bool
operator <(const InsOutNode<T>& node1, const InsOutNode<T>& node2)
{
    if (node1.elem < node2.elem) {
        return true;
    } else if (node2.elem < node1.elem) {
        return false;
    } else {
	// FIXME should this not be something involving dereference:
	// *node1.next < *node2.next, just like operator == above?
	// (It should also handle NULL pointers of course)
	// Unfortunately, without knowing how it is used I dare not
	// change it, but it seems to me that the stated purpose of
	// having only one copy of each tail is not met with this
	// implementation, unless InsOutTable::enter(elem, next) gets
	// offered a next pointer value that is already present in
	// the set.
        return node1.next < node2.next;
    }
}

template<class T>
struct InsOutNodeLT
{
    bool operator ()(const InsOutNode<T>& node1,
                     const InsOutNode<T>& node2) const;
};

template<class T> inline bool
InsOutNodeLT<T>::operator ()(const InsOutNode<T>& node1,
                             const InsOutNode<T>& node2) const
{
    return node1 < node2;
}


//------------------------------------------------------------------------------
// Class:
//      InsOutTable
//
// Description:
//      Stores a list of elements inside-out: share common list tails.
//------------------------------------------------------------------------------

template<class T, class Iterator>
class InsOutTable: public set<InsOutNode<T>, InsOutNodeLT<T> >
{
    public:
        InsOutTable();

        const InsOutNode<T>* enter(const T& elem, const InsOutNode<T>* next =0);
        const InsOutNode<T>* enter(Iterator first, Iterator last);

        // Nice functions for statistics:
        unsigned requested() const;
        unsigned allocated() const;

    private:
        unsigned nr_requested;
};

template<class T, class Iterator> inline
InsOutTable<T, Iterator>::InsOutTable()
{
    nr_requested = 0; 
}

template<class T, class Iterator> inline const InsOutNode<T>*
InsOutTable<T, Iterator>::enter(const T& elem, const InsOutNode<T>* next)
{
    pair<typename InsOutTable::iterator, bool> p = insert(InsOutNode<T>(elem, next));

    nr_requested++;

    return &*p.first;
}

template<class T, class Iterator> inline const InsOutNode<T>*
InsOutTable<T, Iterator>::enter(Iterator first, Iterator last)
{
    const InsOutNode<T>* next = 0;

    while (first < last) {
        pair<typename InsOutTable::iterator, bool> p = insert(InsOutNode<T>(*--last, next));

        nr_requested++;

        next = &*p.first;
    }

    return next;
}

template<class T, class Iterator> inline unsigned
InsOutTable<T, Iterator>::requested() const
{
    return nr_requested; 
}

template<class T, class Iterator> inline unsigned
InsOutTable<T, Iterator>::allocated() const
{
    return this->size(); 
}
  
#endif // INSOUT_H
