123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
/*******************************************************************************

        copyright:      Copyright (c) 2008 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Apr 2008: Initial release

        authors:        Kris

        Since:          0.99.7

        Based upon Doug Lea's Java collection package

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

module tango.util.container.Clink;

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

        Clinks are links that are always arranged in circular lists.

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

struct Clink (V)
{
        alias Clink!(V)    Type;
        alias Type         *Ref;

        Ref     prev,           // pointer to prev
                next;           // pointer to next
        V       value;          // element value

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

                 Set to point to ourselves
                        
        ***********************************************************************/

        Ref set (V v)
        {
                return set (v, &this, &this);
        }

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

                 Set to point to n as next cell and p as the prior cell

                 param: n, the new next cell
                 param: p, the new prior cell
                        
        ***********************************************************************/

        Ref set (V v, Ref p, Ref n)
        {
                value = v;
                prev = p;
                next = n;
                return &this;
        }

        /**
         * Return true if current cell is the only one on the list
        **/

        @property const bool singleton()
        {
                return next is &this;
        }

        void linkNext (Ref p)
        {
                if (p)
                   {
                   next.prev = p;
                   p.next = next;
                   p.prev = &this;
                   next = p;
                   }
        }

        /**
         * Make a cell holding v and link it immediately after current cell
        **/

        void addNext (V v, scope Ref delegate() alloc)
        {
                auto p = alloc().set (v, &this, next);
                next.prev = p;
                next = p;
        }

        /**
         * make a node holding v, link it before the current cell, and return it
        **/

        Ref addPrev (V v, scope Ref delegate() alloc)
        {
                auto p = prev;
                auto c = alloc().set (v, p, &this);
                p.next = c;
                prev = c;
                return c;
        }

        /**
         * link p before current cell
        **/

        void linkPrev (Ref p)
        {
                if (p)
                   {
                   prev.next = p;
                   p.prev = prev;
                   p.next = &this;
                   prev = p;
                   }
        }

        /**
         * return the number of cells in the list
        **/

        @property const int size()
        {
                int c = 0;
                auto p = &this;
                do {
                   ++c;
                   p = p.next;
                   } while (p !is &this);
                return c;
        }

        /**
         * return the first cell holding element found in a circular traversal starting
         * at current cell, or null if no such
        **/

        Ref find (V element)
        {
                auto p = &this;
                do {
                   if (element == p.value)
                       return p;
                   p = p.next;
                   } while (p !is &this);
                return null;
        }

        /**
         * return the number of cells holding element found in a circular
         * traversal
        **/

        const int count (V element)
        {
                int c = 0;
                auto p = &this;
                do {
                   if (element == p.value)
                       ++c;
                   p = p.next;
                   } while (p !is &this);
                return c;
        }

        /**
         * return the nth cell traversed from here. It may wrap around.
        **/

        Ref nth (size_t n)
        {
                auto p = &this;
                for (int i = 0; i < n; ++i)
                     p = p.next;
                return p;
        }


        /**
         * Unlink the next cell.
         * This has no effect on the list if isSingleton()
        **/

        void unlinkNext ()
        {
                auto nn = next.next;
                nn.prev = &this;
                next = nn;
        }

        /**
         * Unlink the previous cell.
         * This has no effect on the list if isSingleton()
        **/

        void unlinkPrev ()
        {
                auto pp = prev.prev;
                pp.next = &this;
                prev = pp;
        }


        /**
         * Unlink self from list it is in.
         * Causes it to be a singleton
        **/

        void unlink ()
        {
                auto p = prev;
                auto n = next;
                p.next = n;
                n.prev = p;
                prev = &this;
                next = &this;
        }

        /**
         * Make a copy of the list and return new head. 
        **/

        const Ref copyList (scope Ref delegate() alloc)
        {
                auto hd = &this;

                auto newlist = alloc().set (hd.value, null, null);
                auto current = newlist;

                for (const(Type)* p = next; p !is hd; p = p.next)
                     {
                     current.next = alloc().set (p.value, current, null);
                     current = current.next;
                     }
                newlist.prev = current;
                current.next = newlist;
                return newlist;
        }
}