# L-12 MCS 275 Mon 11 Feb 2008 : binarysearch.py
#
# Searches a number in a sorted list of integer numbers.
# The builtin "in" operator on list does not take
# into account that the list is sorted.

from random import randint

def LinearSearch(L,x):
   """
   Returns -1 if x belongs to L,
   else returns k for which L[k] == x.
   Items in the list L must be sorted
   in increasing order.
   """
   for i in range(0,len(L)):
      if L[i] == x:
         return i
      elif L[i] > x:
         return -1
   return -1

def BinaryIsIn(L,x):
   """
   Applies binary search to check
   whether x is in the sorted list L.
   Returns True if x is in L, 
   otherwise False is returned.
   """
   if len(L) == 0:
      return False
   elif len(L) == 1:
      return (L[0] == x)
   else:
      m = len(L)/2
      if L[m] == x:
         return True
      elif L[m] > x:
         return BinaryIsIn(L[0:m],x)
      else:
         return BinaryIsIn(L[m+1:len(L)],x) 

def BinaryIsInTrace(L,x,k):
   """
   Traces binary search to check
   whether x is in the sorted list L.
   Returns True if x is in L, 
   otherwise False is returned.
   """
   s = k*' ' + 'find ' + str(x)
   print s + ' in L = ' + str(L)
   if len(L) == 0:
      return False
   elif len(L) == 1:
      return (L[0] == x)
   else:
      m = len(L)/2
      if L[m] == x:
         return True
      elif L[m] > x:
         return BinaryIsInTrace(L[0:m],x,k+1)
      else:
         return BinaryIsInTrace(L[m+1:len(L)],x,k+1) 

def BinarySearch(L,x):
   """
   Applies binary search to find the
   position k of x in the sorted list L.
   Returns -1 if not x in L, or else
   returns k for which L[k] == x.
   """
   if len(L) == 0:
      return -1
   elif len(L) == 1:
      if L[0] == x:
         return 0
      else:
         return -1
   else:
      m = len(L)/2
      if L[m] == x:
         return m
      elif L[m] > x:
         return BinarySearch(L[0:m],x)
      else:
         k = BinarySearch(L[m+1:len(L)],x)
         if k == -1: return -1
         return k + m + 1

def main():
   """
   Illustration of linear and binary search 
   in a sorted list of numbers.  Prompts the
   user for the bounds of [a,b] and N because
   the list will contain N numbers uniformly
   distributed in [a,b].
   """
   a = input('Give lower bound : ')
   b = input('Give upper bound : ')
   N = input('How many numbers ? ')
   r = lambda i: randint(a,b)
   L = map(r,range(0,N))
   L.sort()
   print 'L =', L
   while True:
      x = input('Give number to search for : ')
      c = x in L  # sanity check
      if c: i = L.index(x)
      d = BinaryIsIn(L,x)
      e = BinaryIsInTrace(L,x,0)
      kl = LinearSearch(L,x)
      kb = BinarySearch(L,x)
      if kb == -1:
         print x, 'does not occur in L', not c, not d, kl == kb
      else:
         print 'L[%d] = %d = %d = %d' % (kb,L[kb],L[i],L[kl]), c, d
      ans = raw_input('Search for more ? (y/n) ')
      if ans != 'y': break

main()
