/*
   <p><i>chopsearch()</i> declaration.</p>

   <kbd>
   <b>environ - C programming support functions</b><br>
   Copyright (C) 2005  Martin C Gregorie
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Lesser GNU General Public License.
   
   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 Lesser 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.
   </kbd>
  
   <p>Contact Martin Gregorie at <i>martin@gregorie.org</i></p>
*/

#include "environ.h"

#ifdef OS9
#include <strings.h>
#else
#include <string.h>
#endif

/* #define CHOPTEST 10 */

/*
   <i>chopsearch()</i> is an enhanced binary chop array search for use
   when the array must grow during use. It is implemented using the
   binary chop algorithm from <i><b>Sedgwick : Algorithms</b></i>. 
   The array to be searched is a list of pointers to objects in heap
   storage. Each object must contain a key value and may also contain
   other values, hence the pointer ordering defines the key sequence
   within the array. If the search succeeds it returns a pointer to
   the object. A failed search is signalled by returning NULL.

   If the search fails <i>chopsearch()</i> returns information to allow
   <i>chopinsert()</i> to efficiently insert the missing entry in the
   correct position, thus maintaining the array in key sequence.

   <i>chopsearch()</i> is called with <u>Target</u> pointing to an object
   containing the key value to be found. <u>Table</u> points to the
   array to be searched. <u>Items</u> holds the number of items in the
   array. If the search fails <u>InsertHere</u> is used to return the
   subscript of the array element where the missing item should be added.
   If the search is successful it holds the subscript of the array
   containing the pointer that <i>chopsearch()</i> returned.
   <u>Compare</u> is a user-written function that compares <u>Target</u>
   with an array entry. The object pointed to by <u>Target</u> must be
   identical to the objects pointed to by items in the array.

   <u>Compare</u> must be declared as:

   <kbd>int function(void* target, void* elem);</kbd>

   where <u>target</u> is an object containing the search key and
   <u>elem</u> is an object pointed to by an item in the array being
   searched. <u>Compare()</u> should return a negative value if the
   first argument is less than the second argument, zero if they are
   the same and a positive value if the first argument is larger than
   the second. 

   <b>See also:</b> <i>chopinsert()</i>. 
*/
void *chopsearch(void *Target,
                 void **Table,
                 int Items,
                 int *InsertHere,
                 int (*Compare)(const void*, const void*))
{
    int base;
    int mid;
    int top;
    int cmp;
    void *reply;

#ifdef CHOPTEST
    int limit;
#endif

    base = 0;
    top = Items - 1;
    mid = top / 2;
    cmp = -1;

#ifdef CHOPTEST
    logmsg("Search for %s starting at %d", Target, mid);
    limit = 0;
#endif

    while (cmp && base <= top)
    {

#ifdef CHOPTEST
        if(++limit > CHOPTEST)
        {
            logmsg("Search loop?");
            exit(1);
        }
#endif

#ifdef CHOPTEST
        logmsg("Search: top=%d mid=%d base=%d", top, mid, base);
#endif
        mid = (base + top) / 2;
        cmp = Compare(Target, Table[mid]);
        if (cmp < 0)
            top  = mid - 1;
        else
            base = mid + 1;
    }

    if (cmp == 0)
    {
        *InsertHere = mid;
        reply = Table[mid];
    }
    else
    {
        reply = (void*)NULL;
        if (cmp < 0)
            *InsertHere = mid;
        else
            *InsertHere = mid + 1;
    }

#ifdef CHOPTEST
    if (reply == NULL)
        logmsg("Search miss: search ended at %d; insert %s at %d", 
               mid, Target, *InsertHere);
    else
        logmsg("Search found %s at %d", Target, mid);
#endif

    return(reply);
}
