// bslalg_bidirectionallinklistutil.h                                 -*-C++-*-
#ifndef INCLUDED_BSLALG_BIDIRECTIONALLINKLISTUTIL
#define INCLUDED_BSLALG_BIDIRECTIONALLINKLISTUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide utilities to maintain bidirectional list data structures.
//
//@CLASSES:
//  bslalg::BidirectionalLinkListUtil: utilities to maintain linked lists
//
//@SEE_ALSO: bslalg_bidirectionallink, bslalg_hashtableimputil
//
//@DESCRIPTION: This component provides a namespace,
// 'bslalg::BidirectionalLinkListUtil', containing utility functions for
// operating on doubly linked lists with nodes of type
// 'bslalg::BidirectionalLink'.  The operations assume that the linked lists
// are either 0 terminated (on both ends) or terminate with sentinel (valid)
// nodes.  The main operations include insertion and removal of a node from a
// list of nodes, and the *splicing* of ranges from one list into another one.
// Splicing is the operation of moving a sub-list, or range, of elements from
// one linked list and into a second list, at a specified position.
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1: Creating and Using a List Template Class
///- - - - - - - - - - - - - - - - - - - - - - - - - -
// First, since 'Link' neither has a constructor nor is a POD (which would make
// aggregate initialization possible), we create a function 'makeLink' to
// assemble a link from two pointers:
//..
//  Link makeLink(Link *prev, Link *next)
//  {
//      Link result;
//      result.setPreviousLink(prev);
//      result.setNextLink(    next);
//
//      return result;
//  }
//..
// Then, we create a function that will, passed two links that are endpoints
// of a linked list from the specified 'first' to 'last' though the 'nextLink'
// pointers, count the number of nodes in the list including both endpoints.
//..
//  int length(Link *first, Link *last)
//  {
//      int result = 0;
//      Link *p = first;
//      while (p && ++result && last != p) {
//          p = p->nextLink();
//      }
//
//      return result;
//  }
//..
// Next, in our 'main', we declare a 'typedef' for the component name and a
// a constant 'invalid' garbage pointer we use when we want data to be garbage.
//..
//  typedef BidirectionalLinkListUtil Util;
//  Link * const invalid = (Link *) 0XBADDEED5;
//..
// Then, we create a linked list of links and use 'isWellFormed' to verify
// that it is well formed, and call the 'length' method we just created to
// verify its length.
//..
//  Link usageData[] = {
//      makeLink(0,             &usageData[1]),
//      makeLink(&usageData[0], &usageData[2]),
//      makeLink(&usageData[1], &usageData[3]),
//      makeLink(&usageData[2], 0            )  };
//
//  assert(Util::isWellFormed(      &usageData[0], &usageData[3]));
//  assert(4 == length(&usageData[0], &usageData[3]));
//..
// Next, we create two new links 'front' and 'back', and initialize them with
// garbage:
//..
//  Link front = makeLink(invalid, invalid);
//  Link back  = makeLink(invalid, invalid);
//..
// Then, we use our component's 'insertLinkBeforeTarget' and
// 'insertLinkAfterTarget' to concatenate 'front' to the front of the list and
// 'back' to its rear:
//..
//  Util::insertLinkBeforeTarget(&front, &usageData[0]);
//  Util::insertLinkAfterTarget( &back,  &usageData[3]);
//..
// Next, We examine the new list and verify we now have a well-formed list, 2
// longer than the old list:
//..
//  assert(0 == front.previousLink());
//  assert(0 == back .nextLink());
//  assert(Util::isWellFormed(          &front, &back));
//  assert(6 == length(&front, &back));
//..
// Then, we use our component's 'unlink' method to remove two nodes from our
// list.  Note that the state of the removed nodes is undefined:
//..
//  Util::unlink(&usageData[1]);
//  Util::unlink(&usageData[3]);
//..
// Next, we verify that the new list is well formed and 2 elements shorter than
// it was before we removed those two nodes:
//..
//  assert(Util::isWellFormed(&front, &back));
//  assert(4 == length(&front, &back));
//..
// Then, we weave the two discarded nodes into a new, second list of two nodes,
// and use 'isWellFormed' and 'length' to verify it is as we expect:
//..
//  usageData[1] = makeLink(0, &usageData[3]);
//  usageData[3] = makeLink(&usageData[1], 0);
//  assert(Util::isWellFormed(&usageData[1], &usageData[3]));
//  assert(2 ==        length(&usageData[1], &usageData[3]));
//..
// Next, we use our component's 'spliceListBeforeTarget' method to remove the
// middle nodes from the longer list and append them to the end of shorter
// list.  Note that the splicing function not only adds the sequence to the new
// list, it also splices the list the sequence is removed from so that both are
// well-formed lists:
//..
//  Util::spliceListBeforeTarget(&usageData[0],
//                               &usageData[2],
//                               &usageData[3]);
//..
// Then, we use 'isWellFormed' and 'length' to verify the state of our two
// lists:
//..
//  assert(Util::isWellFormed(&usageData[1], &usageData[3]));
//  assert(4 ==        length(&usageData[1], &usageData[3]));
//
//  assert(Util::isWellFormed(&front, &back));
//  assert(2 ==        length(&front, &back));
//..
// Next, we call 'spliceListBeforeTarget' again to join our two lists into one:
//..
//  Util::spliceListBeforeTarget(&usageData[1],
//                               &usageData[3],
//                               &back);
//..
// Now, we use 'isWellFormed' and 'length' to verify the state of our one
// remaining list:
//..
//  assert(Util::isWellFormed(&front, &back));
//  assert(6 ==        length(&front, &back));
//  assert(0 == front.previousLink());
//  assert(0 == back .nextLink());
//..
// Finally, we traverse our list in both directions, examining each node to
// verify all the links and that the sequence is as expected:
//..
//  Link *p = &front;
//  assert(0 == p->previousLink());
//  p = p->nextLink();
//  assert(&usageData[1] == p);
//  p = p->nextLink();
//  assert(&usageData[0] == p);
//  p = p->nextLink();
//  assert(&usageData[2] == p);
//  p = p->nextLink();
//  assert(&usageData[3] == p);
//  p = p->nextLink();
//  assert(&back         == p);
//  assert(0 == p->nextLink());
//
//  p = p->previousLink();
//  assert(&usageData[3] == p);
//  p = p->previousLink();
//  assert(&usageData[2] == p);
//  p = p->previousLink();
//  assert(&usageData[0] == p);
//  p = p->previousLink();
//  assert(&usageData[1] == p);
//  p = p->previousLink();
//  assert(&front        == p);
//  assert(0 == p->previousLink());
//..

#include <bslscm_version.h>

namespace BloombergLP {

namespace bslalg {

class BidirectionalLink;

                        // ========================================
                        // struct bslalg::BidirectionalLinkListUtil
                        // ========================================

struct BidirectionalLinkListUtil {
    // This 'struct' provides a namespace for utility functions that manipulate
    // linked lists based on 'bslalg::BidirectionalLink' nodes, including
    // insertion, removal, and *splicing*.

    // CLASS METHODS
    static
    void insertLinkBeforeTarget(BidirectionalLink *newNode,
                                BidirectionalLink *target);
        // Insert the specified 'newNode' before the specified 'target' node in
        // the linked list that contains 'target'.  If 'target' is 0, then the
        // value of the attributes 'nextLink' and 'previousLink' of 'newNode'
        // is set to 0.  After successful execution of this function the values
        // of the 'previousLink' and 'nextLink' attributes of all the links in
        // the list appropriately reflect the operation.  The behavior is
        // undefined unless '0 == target->previousLink()' is true or
        // 'isWellFormed(target->previousLink(), target)' is true.

    static
    void insertLinkAfterTarget(BidirectionalLink *newNode,
                               BidirectionalLink *target);
        // Insert the specified 'newNode' after the specified 'target' node in
        // the linked list that contains 'target'.  If the node following
        // 'target' is 0, then set the 'nextLink' attribute of 'newNode' to 0.
        // After successful execution of this function the values of the
        // 'previousLink' and 'nextLink' attributes of all the links in the
        // list appropriately reflect the operation.  The behavior is undefined
        // unless '0 != newNode' and '0 != target'.  The behavior is also
        // undefined unless '0 == target->nextLink()' is true or
        // 'isWellFormed(target, target->nextLink())' are true.

    static
    bool isWellFormed(BidirectionalLink *head, BidirectionalLink *tail);
        // Return true if the bidirectional list starting from the specified
        // 'head', and ending with the specified 'tail' is well formed.  A
        // bidirectional list is well formed if 'tail == head' (0 values are
        // allowed) or all of the following conditions are met (note that
        // 'head' is renamed to 'h' and 'tail' to 't' for brevity):
        //
        //: 1 'h' and 't' are valid addresses.
        //:
        //: 2 'h->nextLink()->previousLink() == h' is true.
        //:
        //: 3 '!h->previousLink() || h->previousLink()->nextLink() == h'
        //:    is true.
        //:
        //: 4 't->previousLink()->nextLink() == t' is true.
        //:
        //: 5 '!t->nextLink() || t->nextLink()->previousLink() == t'
        //:    is true.
        //:
        //: 6 For each 'link' in the list different than 'h' and 't' both
        //:   'link->nextLink()->previousLink() == link' and
        //:   'link->previousLink()->nextLink() == link' are true.
        //
        // The behavior is undefined unless 'tail' can be reached from 'head'
        // following the chain of 'nextLink' attributes of all the nodes in the
        // open range '[head, tail)'.

    static
    void spliceListBeforeTarget(BidirectionalLink *first,
                                BidirectionalLink *last,
                                BidirectionalLink *target);
        // Unlink and move (splice) the elements of a doubly-linked list
        // included in the closed range '[first, last]' out of their original
        // list and into another doubly-linked list before the specified
        // 'target' node.  If 'target' is 0, then the elements are extracted
        // and form a new list such that '0 == first->previousLink()' and
        // '0 == last->nextLink()'.  After successful execution of this
        // function the values of the 'previousLink' and 'nextLink' attributes
        // of all the links in the origin and destination lists appropriately
        // reflect the operation.  The behavior is undefined unless both
        // 'first' and 'last' are non-zero members of the same linked list;
        // 'first' precedes 'last' in the list, or 'first == last'; 'target' is
        // not a node contained in the closed range '[first, last]'; and
        // 'isWellFormed(first, last)' is true.

    static
    void unlink(BidirectionalLink *node);
        // Unlink the specified 'node' from the linked list of which it is a
        // member.  After successful execution of this function the values of
        // the 'previousLink' and 'nextLink' attributes of all the links in the
        // origin and destination lists appropriately reflect the operation
        // Note that this method does *not* change the value for the 'nextLink'
        // and 'previousLink' attributes of 'node'.  The behavior is
        // undefined unless '!node->previousLink()', '!node->nextLink()', or
        // 'isWellFormed(node->previousLink(), node->nextLink())' are true.
};

}  // close package namespace

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------