gbsearch.h

/*  gbsearch.h
    Oct 3 1998
    Chris Lacher

    Generic binary search algorithms for random access iterators

    10/3/98
    10/01/00
    11/11/01
    12/13/02
    12/30/02

    These algrithms take parameters of type I, T, and optionally a third
    parameter of type P. 

    about I
    =======

    I is a random access iterator type; this means that the bracket operator

      T& operator [] (unsigned int);

    is defined and the usual "pointer" arithmetic operations are defined:

      I&    operator += (long n);
      I&    operator -= (long n);
      I     operator +  (long n) const;
      long  operator -  (const I& I2) const;

    Examples include: ordinary array iterators (pointers);
                      TVector<T>::Iterator;
                      TDeQueue<T>::Iterator;

    about T
    =======

    T is the value type of the iterator I

    about P 
    =======

    P is a predicate class used for order; an object cmp of class P
    has the function evaluation operator overloaded with the prototype

      bool operator () (const T&, const T&);

    Note that P must be used to order the elements so that P can work
    as a search condition: "a < b" iff "true = P(a,b)".

    g_lower_bound() and g_upper_bound() implement binary search for random
    access iterators.  It is assumed that the elements of iteration are in
    nondecreasing order (using operator < or an optional predicate object).

    g_lower_bound returns an iterator to the first location containing the
    search value if found, and the location where it would be otherwise. 
    g_upper_bound returns an iterator to the first location whose element is
    greater than the search value if such exists or the end iterator
    otherwise. I.e. (assuming L = lower bound and U = upper bound):

    if t is in the collection: t == *L, t < *U, and L and U are the
    smallest iterators in the sequence with these properties; thus,
    low = L and hih = U mark the range of t in the collection;

    if t is not in the collection: L == U, and these iterators point to the
    location where t could be inserted and maintain nondecreasing order.

    bool binary_search() uses the same algorithm, returning true iff found.

    All these versions of binary search run in time O(log size).

    Copyright 1998 - 2007, R.C. Lacher
*/

#ifndef _GBSEARCH_H
#define _GBSEARCH_H

#include <tcompare.h>

namespace fsu
{
template <class I, typename T, class P>
I g_lower_bound (I low, I hih, const T& val, const P& cmp)
// pre:    I is a random access iterator (operator [] and "pointer" arithmetic)
//         I has value_type T
//         low + n = hih for some n >= 0
//         low[0] ... low[n-1] are in non-decreasing order using cmp
// post:   no state is changed
// return: itr = lower bound location in range
//         itr = low + i, where low[i-1] < val <= low[i]; or
//         itr = hih if no such i exists
{
  I mid;
  while (low != hih)
  {
    mid =  low + ((hih - low) >> 1);    // low <= mid < hih
    if (cmp(*mid , val))                // *mid < val
      low = ++mid;                        // *mid <= val
    else                                // val <= *mid
      hih = mid;                          // val <= *hih
  }  
  return low;
}

template <class I, typename T, class P>
I g_upper_bound (I low, I hih, const T& val, const P& cmp)
// pre:    I is a random access iterator (operator [] and "pointer" arithmetic)
//         I has value_type T
//         low + n = hih for some n >= 0
//         low[0] ... low[n-1] are in non-decreasing order using cmp
// post:   no state is changed
// return: itr =  low + i, where low[i-1] <= val < low[i]; or
//         itr = hih if no such i exists
{
  I mid;
  while (low != hih)
  {
    mid =  low + ((hih - low) >> 1);    // low <= mid < hih
    if (cmp(val, *mid))                 // val < low[mid]
      hih = mid;                          // val < low[hih]
    else                                // low[mid] <= val
      low = ++mid;                        // low[low - 1] <= val
  }  
  return low;
}

template <class I, typename T, class P>
int g_binary_search (I low, I hih, const T& val, const P& cmp)
// pre:    I is a random access iterator (operator [] and "pointer" arithmetic)
//         I has value_type T
//         low + n = hih for some n >= 0
//         low[0] ... low[n-1] are in non-decreasing order using cmp
// post:   no state is changed
// return: true if found, false if not found
{
  I lb = g_lower_bound(low, hih, val, cmp);
  if (lb != hih)
  {
    if (val == *lb)
    {
      return 1;
    }
  }
  return 0;
}

template <class I, typename T>
I g_lower_bound (I low, I hih, const T& val)
{
  TLessThan<T> cmp;
  return g_lower_bound(low, hih, val, cmp);
}

template <class I, typename T>
I g_upper_bound (I low, I hih, const T& val)
{
  TLessThan<T> cmp;
  return g_upper_bound(low, hih, val, cmp);
}

template <class I, typename T>
int g_binary_search (I low, I hih, const T& val)
{
  TLessThan<T> cmp;
  return g_binary_search(low, hih, val, cmp);
}

/* Proof (g_lower_bound)
   -----

Before entering loop, define beg = low, top = hih - 1

loop invariants:
                      beg[0..low-1] are < val
                      beg[hih..top] are >= val
                      0 <= low <= mid <= hih <= top

Proof of Termination: (hih - low) collides with 0

Return Value: after loop terminates, the following are true:
              low == hih 
              0 <= low <= top
              beg[low] == beg + low is where val SHOULD be, i.e.:
                  val <= beg[0]                    if low == 0
                  beg[low - 1] < val <= beg[low]   if 0 < low < top
                  beg[top - 1] < val               if low == top
*/

} // namespace fsu
#endif