import random
import time
import sys

# oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
# The file demonstrates Python behaviour when presented with the problem
# of finding a particular item in a list.
#
#  The built-in method .index() and keyword *in*
#     always scan the list from left to right (from the lowest index)
#     and either stop when the item is found ( = search hit )
#     or report a search miss.
#
#  Both these approaches are NOT sensitive to the contents of the list.
#  Thus, they are general and can be applied to any list.
#
#  When a list contents is SORTED, a different approach may be applied.
#  Binary search operates significantly faster, however, it is not
#  general, it requires sorted data.
#
#
#  In the examples below, each time a list is created randomly
#  and it is searched using .index() and *in* approaches.
#  Then, the list gets sorted and it is searched again using both aproaches,
#  and this time also Binary Search is included.
#
#  For demonstration purposes, the list is searched
#  for each value in the given range [0..maxValue]
#  The total number of the search hits is counted.
#  That also gives the number of different values in the list.
# ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo


# ----------------------------------------------------------------
#   DEFINITIONS, INITIALIZATION, SUPPORT FUNCTIONS
# ----------------------------------------------------------------

aPGElist = []

def printf(format, *args):
    sys.stdout.write(format % args)

# --------------------------------------------------
# a list is printed only if it contains less than printlim values
# printlim = 12
printlim = 20

# --------------------------------------------------
# manipulate the problem size n and maximum list value to see varying results
listSize = 20000
maxValue = 20000

print( "List length =", listSize, ", maximum value =", maxValue )


# --------------------------------------------------
# Function creates a list filled with randomly chosen values
def generate( listSize, maxValue  ):
    random.seed(12321)
    t1 = time.time()
    aList = [random.randint(0, maxValue) for x in range(listSize)]
    t2 = time.time()
    #printf("time generating %f\n", t2-t1  )
    if listSize < printlim: print(aList)
    return aList


# oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
#
#               M A I N
#
# ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

# ----------------------------------------------------------------
#   SEARCH UNSORTED LIST
#
# 1. .index() method
# 2.  *in*  keyword
# ----------------------------------------------------------------

# 1.
# search in an unsorted list -- by *index* method
print("Search in an UNsorted list -- *index* ")
aPGElist = generate(listSize, maxValue)
t1 = time.time()
count = 0
for value in range(maxValue+1):
    try:
        indx = aPGElist.index(value)
        count += 1
    except ValueError:
        pass
t2 = time.time()
printf("Number of different values %d\n", count)
printf("        time %f\n\n", t2-t1 )

# 2.
# search in an unsorted list -- *in*
print("Search in an UNsorted list -- *in* ")
aPGElist = generate(listSize, maxValue)
t1 = time.time()
count = 0
for value in range(maxValue+1):
    if value in aPGElist:
        count += 1
t2 = time.time()
printf("Number of different values %d\n", count)
printf("        time %f\n\n", t2-t1 )

# ----------------------------------------------------------------
#    SEARCH  SORTED  LIST
#
# 3. .index() method
# 4.  *in*  keyword
# ----------------------------------------------------------------

# 3.
# search in a sorted list -- *index*
print("Search in a SORTED list -- *index* ")
aPGElist = generate(listSize, maxValue)
aPGElist.sort()
t1 = time.time()
count = 0
for value in range(maxValue+1):
    try:
        indx = aPGElist.index(value)
        count += 1
    except ValueError:
        pass
t2 = time.time()
printf("Number of different values %d\n", count)
printf("        time %f\n\n", t2-t1 )

# 4.
# search in an unordered list -- *in*
print("Search in a SORTED list -- *in* ")
taPGElist = generate(listSize, maxValue)
aPGElist.sort()
p1 = time.time()
count = 0
for value in range(maxValue+1):
    if value in aPGElist:
        count += 1
t2 = time.time()
printf("Number of different values %d\n", count)
printf("        time %f\n\n", t2-t1 )

# ----------------------------------------------------------------
#    SEARCH SORTED LIST WITH     BINARY SEARCH
#  5. binary search
# ----------------------------------------------------------------

#  --------------------------------------------------
# Binary search function
# It is widely used for its efficiency.
def binarySearch( arr, value ):
    low = 0; high = len(arr)-1
    while low < high:    # while segment length > 1
        mid = (low + high) // 2
        if arr[mid] < value: low  = mid+1
        else:                 high = mid
    if arr[low] == value: return low  # found or
    return -1 # not found

# 5.
# search in an ordered list -- *binary search*
print("Search in a SORTED list -- *binary search* ")
aPGElist = generate(listSize, maxValue)
aPGElist.sort()
t1 = time.time()
count = 0
for value in range(maxValue+1):
    if binarySearch(aPGElist, value) != -1:  # if found
        count += 1
t2 = time.time()
printf("Number of different values %d\n", count)
printf("        time %f\n\n", t2-t1 )








