function root = root_bisect(lower_bound, upper_bound, epsilon, func)
//
// Find the root of the given function within the given interval,
// using the bisection method.
//
// Arguments:
//
//      lower_bound     (input)    lower bound of interval
//                                     on which to search for root
//
//      upper_bound     (input)    upper bound of interval
//
//      epsilon         (input)    when fractional change in root
//                                     reaches this limit, stop
//
//      func            (input)    name of function whose root
//                                     we seek -- should take
//                                     a single argument
//
//      root           (output)    argument at which the given
//                                     function has value zero
//
// MWR 3/25/2002
//

// set this to 1 to watch diagnostic messages as the function runs
verbose = 1;

// sanity checks
if (lower_bound >= upper_bound)
   error('lower_bound must be less than upper_bound');
end
if (epsilon <= 0)
   error('epsilon must be greater than zero');
end

// if there is no guaranteed root between the given bounds, 
// then quit with an error message
y_low = feval(lower_bound, func);
y_high = feval(upper_bound, func);
if (sign(y_low) == sign(y_high))
   error('function has same sign at both bounds -- quitting');
end

done = 0;
iterations = 0;
old_mid = lower_bound;
while (done == 0) 
   y_low = feval(lower_bound, func);
   y_high = feval(upper_bound, func);
   
   if (verbose > 0)
      mprintf('iter %5d   %11.7e %9.4e   %11.7e %9.4e ', ...
         iterations, lower_bound, y_low, upper_bound, y_high);
      pause;
   end
   
   // Pick the half of the interval in which the root must exist
   // 'mid' is going to be next root candidate
   mid = (upper_bound - lower_bound)/2.0;
   y_mid = feval(mid, func);
   if (sign(y_mid) == sign(y_low))
      // root is in upper half
      lower_bound = mid;
   else
      // root is in lower half
      upper_bound = mid;
   end
   
   // now, check to see if we are close enough
   if (mid == 0)
      if (verbose > 0) 
         mprintf('  abs change %9.4e ... ', abs(mid - old_mid));
      end
      // we can't calculate fractional change here, so use criterion
      // that absolute value of the change in candidates is less than epsilon
      if (abs(mid - old_mid) < epsilon)
         if (verbose > 0)
            mprintf(' stop \n');
         end
         done = 1;
      else
         if (verbose > 0)
            mprintf(' keep going \n');
         end
      end
   else
      frac_change = abs((mid - old_mid)/mid);
      if (verbose > 0) 
         mprintf('  %9.4e ', frac_change);
      end
      if (frac_change < epsilon)
         if (verbose > 0)
            mprintf(' stop \n');
         end
         done = 1;
      else
         if (verbose > 0)
            mprintf(' keep going \n');
         end
      end
   end
   
   // we need to save a copy of the current root candidate, so we
   // can calculate the fractional change since the previous one
   old_mid = mid;
   
   iterations = iterations + 1;
end

// when we get here, the latest root candidate is in 'mid'
root = mid;


endfunction
