/*                                      
 * Search.java 
 *
 * Version: 
 *     $Id: Search.java,v 1.2 2002/04/15 12:08:43 jmg Exp $ 
 * 
 * Revisions: 
 *     $Log: Search.java,v $
 *     Revision 1.2  2002/04/15 12:08:43  jmg
 *     fixed bug
 *
 */

import java.util.*;

/**
 * A class for running searches.
 *
 * @author Joe Geigel
 */
public class Search {

    /**
     * current number of compares made
     */
    private int n_comp;

    /**
     *  Constructor for a search object
     */
    public Search ()
    {
	n_comp = 0;
    }

    /**
     * Builds and returns an array of random Integer objects.
     * Will assure that the argument value in will be in the 
     * array and that out will not be in the array
     *
     * @param n size of array
     * @param in value guaranteed to be in the array
     * @param out value guaranteednot to be in the array
     * @returns an array of random Integer objects
     */
    public static Integer[] buildTestArray(int n, int in, int out)
    {
	Integer ret[] = new Integer[n];
	
	// We'll need a random number generator
	Random R = new Random();
	
	// index to place in
	int inidx = (int)(R.nextDouble() * n);

	for (int i=0; i < n; i++) {
	    int v = R.nextInt();
	    while ((v == in) || (v == out)) v = R.nextInt();
	    ret[i] = new Integer(v);
	}
	ret[inidx] = new Integer (in);

	return ret;
    }

    /**
     * Compares two elements an returns a flag indicating if they
     * are equal. Will also update the count of comparisons as a
     * side effect.
     *
     * @param o1 First object to compare
     * @param o2 Second object to compare
     *
     * @return flag indicating if the two objects are equal
     */
    private boolean doCompare (Object o1, Object o2)
    {
	n_comp++;
	return o1.equals (o2);
    }


    /**
     * Compares two elements an returns an int indicating if 01 is less
     * than o2 (-1 returned), greater than o2 (1 returned), or equal to
     * o2 (0 returned).  Updates the comparison count as a 
     * side effect.
     *
     * @param o1 First object to compare
     * @param o2 Second object to compare
     *
     * @return int indicating if the two objects are equal, gt, or lt
     */
    private int doCompareI (Comparable o1, Comparable o2)
    {
	n_comp++;
	return o1.compareTo (o2);
    }

    /**
     * public call to perform a binary search on an array of objects
     *
     * @param x array to search
     * @param key the object to be searched for
     * @return flag indicating if key was found in array x
     */
    public boolean binarySearch (Comparable x[], Comparable key) 
    {
	// clear number of compares
	n_comp = 0;

	// start a recursive binary search
	return binarySearch (x, key, 0, x.length - 1);
    }

    /**
     * private call to perform a recursive binary search on an array of 
     * objects
     *
     * @param x array to search
     * @param key the object to be searched for
     * @param low The start index in the array from where to search
     * @param high The end index in the array to where to search
     * @return flag indicating if key was found in array x
     */
    private boolean binarySearch (Comparable x[], Comparable key, int low,
				 int high) 
    {
	if (high == low) 
	    return (doCompare(x[high],key));

	int mid = ( low + high ) / 2; 
	if ( doCompare(x[mid],key) ) return true;
	else if ((doCompareI(x[mid],key)) < 0 ) 
	    return binarySearch (x, key, mid+1, high);
	else
	    return binarySearch (x, key, low, high-1);
    }


    /**
     * Returns the current count of comparsions
     *
     * @return The current count of comparions
     */
    public int getNComp ()
    {
	return n_comp;
    }

    /**
     * Perform a linear search on an array of objects
     *
     * @param x array to search
     * @param key the object to be searched for
     * @return flag indicating if key was found in array x
     */
    public boolean linearSearch (Object x[], Object key) 
    {
	// clear number of compares
	n_comp = 0;

	for (int i=0; i < x.length; i++){
            if (doCompare(x[i],key)) return true;
	}
	return false;
    }

    /**
     * Test program for search class
     * usage: java Search [n] where n is the size of the array on which 
     * to test.  If left off, n = 100
     *
     */
    public static void main(String args[])
    {
	// commandline argument
	int n = 100;
	if (args.length > 0) {
	    try {
		 n = Integer.parseInt (args[0]);
	    }
	    catch (NumberFormatException e) {
		System.err.println ("Bad integer: " + args[0] + 
				    " using default of 100");
	    }
	}

	// Create some arrays
	int in = 7;
	int out = 12;
	Integer x[] = buildTestArray (n, in, out);

	// Create a new search object
	Search S = new Search();

	// Run a linear search
	System.out.println ("Linear Search");
	S.linearSearch (x, new Integer(in));
	System.out.println ("Found " + in + " using " + S.getNComp() + 
			    " comparisons");
	S.linearSearch (x, new Integer(out));
	System.out.println ("Did not find " + out + " using " + S.getNComp() +
			    " comparisons");

	// Run a binary search
	System.out.println ();
	System.out.println ("Binary Search");
	S.binarySearch (x, new Integer(in));
	System.out.println ("Found " + in + " using " + S.getNComp() + 
			    " comparisons");
	S.binarySearch (x, new Integer(out));
	System.out.println ("Did not find " + out + " using " + S.getNComp() +
			    " comparisons");

    }
}

