def build_file_segments(directory):
    """
    Builds a dict mapping all filepaths in @param directory to Segments with a 1D list for its right col
    and left col

    @param directory: path to directory containing slices
@return: dict of filepath -> Segment
    """
    files_names = imageutils.files_in_directory(directory)

    files = {}

    for file_name in files_names:
        img_arr = imageutils.load_image(file_name)
        files[file_name] = Segment(tuple(img_arr[0]), tuple(img_arr[-1]))

    return files
					first_index = first_slice_index
					second_index = second_slice_index
					if left_overlap_score <= right_overlap_score:
						best_overlap_score = left_overlap_score
						first_on_left = True
					else:
						best_overlap_score = right_overlap_score
						first_on_left = False

		if first_on_left:
			slices[first_index] += slices[second_index]
			slices.pop(second_index)
		else:
			slices[second_index] += slices[first_index]
			slices.pop(first_index)
	return slices[0]

if __name__ == '__main__':
	"""
	Constructs and opens the image formed by the slices in DATA_LOCATION.
	"""
	DATA_LOCATION = 'shredded/destination'
	slices = []
	for file in imageutils.files_in_directory(DATA_LOCATION):
		image_slice = imageutils.load_image(file)
		slices.append(image_slice)
	final_image = reconstruct_slices(slices)
	imageutils.show_image(final_image)


		images.remove(curr_slice)
		min_sim = 2000000000
		index = 0
		for i in range(len(images)):
			next_slice = images[i]
			x = min(slice_similarity(curr_slice, next_slice), slice_similarity(next_slice, curr_slice))
			if x < min_sim:
				min_sim = x
				index = i
		next_slice = images[index]
		if slice_similarity(curr_slice, next_slice) < slice_similarity(next_slice, curr_slice):	
			images[index] = curr_slice + images[index]
		else:
			images[index] = images[index] + curr_slice



if __name__ == '__main__':
	"""
	Opens file and reads it using a list comprehension into a list called images.
	After reconstructing the image, this method then displays the image and saves
	it to the hard drive.

	
	"""
	IMGS_DIR = 'shredded/destination'
	images = [imageutils.load_image(filename) for filename in imageutils.files_in_directory(IMGS_DIR)]
	reconstruct_image(images)
	imageutils.show_all_images(*images)
	imageutils.save_image(images[0], 'final-destination')
                    if first_overlap_score <= second_overlap_score:
                        best_overlap_score = first_overlap_score
                        first_slice_on_left = True
                    else:
                        best_overlap_score = second_overlap_score
                        first_slice_on_left = False
                first_slice_index = first_slice_index
        if first_slice_on_left:
            slices[first_index] += slices[second_index]
            slices.pop(second_index)
        else:
            slices[second_index] += slices[first_index]
            slices.pop(first_index)

    return slices[0]
    
"""
TA Grading Comment:
Yeesh. A little messy. I get it was the last one and you wanted to knock it out.
"""

if __name__ == '__main__':
    DATA_FILES = 'shredded/destination'

    slices = list()
    for f in imageutils.files_in_directory(DATA_FILES):
        piece = imageutils.load_image(f)
        slices.append(piece)

    imageutils.show_image(reconstruct_slices(slices))
'''
***** beautiful. glad you took advantage of the fact that pixels are simply tuples
'''

def pixel_similarity(p1, p2): 
	"""
	Determine how similar two input pixels by summing the squares of the absolute difference for each color component
	@param: tuple of 4 representing 2 color components
	@return: float representing the sum
	"""
	return sum(map(lambda component: (component[0] - component[1])**2, zip(p1, p2)))

if __name__ == '__main__':
    DESTINATION = "grail4"
    img_name_list = imageutils.files_in_directory(DESTINATION) #get all the image file names in the destination folder
    complete_list = [] #list of all 2Darrays representing slices
    for img_name in img_name_list:
    	pixel_list = load_image(img_name) 
    	complete_list.append(pixel_list)
    
    current_piece = complete_list.pop(0) #take out the first data list 
    while len(complete_list)>0: #repeat the process there is no more image pieces in the list to look at 
    	min_diff = 1000000000000000 #some really large number
    	next_piece = current_piece #look for the next_piece that matches the current piece the best
    	for compared_piece in complete_list:
    			#check both cases when current_piece is on either the left or the right of the compared_piece
    			if min(slice_similarity(current_piece, compared_piece), slice_similarity(compared_piece, current_piece)) < min_diff:
    				next_piece = compared_piece

    	#determine the left-right order of current_piece and next_piece. Merge the two pieces together and update the current_piece
	""" Returns the image array for the given filename, using imageutils.
	"""
	return imageutils.load_image(filename)

def stitch_images(img1, img2):
	""" Appends the columns of img2 to the right of img1's existing columns.
	"""
	img1 += img2
	return img1

if __name__ == '__main__':
	DIRNAME = "./shredded/destination"
	all_image_slices = []

	print("Loading images...")
	for filename in imageutils.files_in_directory(DIRNAME):
		next_image = get_image_array(filename)
		if not next_image:
			print("Error while loading image!")
		all_image_slices.append(next_image)

	print("done!")

	INITIAL_SLICE_COUNT = len(all_image_slices)

	# Let's build a distance matrix for the slices
	distances = [[INFINITY for i in range(INITIAL_SLICE_COUNT)] for j in range(INITIAL_SLICE_COUNT)]
	for i in range(INITIAL_SLICE_COUNT):
		for j in range(i + 1, INITIAL_SLICE_COUNT):
			distances[i][j] = left_distance(all_image_slices[i], all_image_slices[j])
			distances[j][i] = left_distance(all_image_slices[j], all_image_slices[i])
# Computes the difference between all the components of two pixels.
# sqrts it to value matches with most very close and a few far away
# over those that are somewhat close across the board.
# Both args are imageutils pixels. Returns a float.
def pixDiff(pix1, pix2):
  return sqrt(sum([abs(com[0]-com[1]) for com in zip(pix1, pix2)]))
  
# Computes the sum of the differences amonst all the pixels in the
# rightmost edge of lhs and leftmost edge of rhs. Both args are 
# tuples of imageutils pixels. Returns a float.
@lru_cache(maxsize=None)
def colDiff(lhs, rhs):
  return sum([pixDiff(pixs[0], pixs[1]) for pixs in zip(lhs, rhs)])

if __name__ == '__main__':
  strips = [list(map(tuple, imageutils.load_image(file))) for file in imageutils.files_in_directory(dir)]
  """
  TA Grading Comment:
  file is a keyword watch out
  
  True = False
  """
  while len(strips) > 1:
    besti, bestj, bestDiff = 0, 0, -1
    for i in range(len(strips)):
      for j in range(len(strips)):
        if i == j: continue
        diff = colDiff(strips[i][-1], strips[j][0])
        if bestDiff == -1 or diff < bestDiff:
          bestDiff, besti, bestj = diff, i, j
    strips[besti] += strips[bestj]
	for p1, p2 in zip(col1, col2):
		sim_score += pixel_sim(p1, p2)
	return sim_score

def pixel_sim(pixel1, pixel2):
	"""
	Helper function that compares two Pixel objects by computing the sum
	of the square of each of the pixel differences, between red, green, blue,
	and alpha. This value is returned and used in column_sim.
	"""
	return sum([(pixel1.red - pixel2.red)**2, (pixel1.green - pixel2.green)**2, 
		(pixel1.blue - pixel2.blue)**2, (pixel1.alpha - pixel2.alpha)**2])

if __name__ == '__main__':
    pass
    dirname = "shredded/grail20/"
    #reads in all the vertical slice image files and put them into a list
    files_list = imageutils.files_in_directory(dirname)
    image_files = [imageutils.load_image(file) for file in files_list]

    #reassembles the list of slices and renders the image
    reassemble_slices(image_files)
    imageutils.show_all_images(*image_files, buffer_width=0)







def get_all_images():

    '''
    Gets all the images from the named file (relative path) and places them in a dict
    with an number key for each picture array. I'll use that key throughout to track the
    proper reordering of the slices. Returns dict.
    '''
    pic_map = {}
    pic_files = imageutils.files_in_directory("shredded/destination")

    pic_key = 0

    for pic in pic_files:

    pic_col_array = imageutils.load_image(pic)
pic_map[pic_key] = pic_col_array
pic_key += 1

return pic_map


def pixel_similarity(base_pixel, cmp_pixel):

    '''
    Basic helper that computes the similarity score between two pixels by finding the square
    of the absolute value of the difference between each Pixel field (RGBA) and suming those diffs
    The larger the return value, the less similar
    '''

    diffs = []
    diffs.append(abs(base_pixel.red - cmp_pixel.red)**2)
    diffs.append(abs(base_pixel.green - cmp_pixel.green)**2)
    diffs.append(abs(base_pixel.blue - cmp_pixel.blue)**2)
    diffs.append(abs(base_pixel.alpha - cmp_pixel.alpha)**2)
    return sum(diffs)


def column_similarity(rightmost_piece, leftmost_piece):

    '''
    Takes two pieces, which are one-column slices from the larger slice and computes their 
    similarity. Run a for loop over over each pixel down the two columns, passing each pixel 
    pair to pixel_similarity. Returns similarity score for the columns, which is equivalent to 
    simiarlity score for two slices, because the columns represent rightmost and leftmost column of 
    two slices, respectively.
    '''
    
    sim_score_list = []

    for n in range(len(rightmost_piece)):
    sim_score_list.append(pixel_similarity(rightmost_piece[n], leftmost_piece[n]))

    return sum(sim_score_list)





def slice_similarity(this_slice, this_key, slice_map, master_repository):

    '''
    Computes the similarity scores for one slice compared against every other slice from the picture. 

    Grabs the rightmost column from base slice (since that's where we would glue it to another slice) and 
    then grabs the leftmost column of every other slice, comparing each right-left pair for similarity. 
    Places the similarity score into a dict, with the key being a tuple indicating which two slices (which pair)
    that similarity score represents.
    '''
    #need to grab rightmost piece from this_slice and left_most from every other slice
    rightmost_piece = this_slice[-1]


    #compute similarity scores between this key and every other slice
    for key, other_slice in slice_map.items():

        if(key != this_key):
        leftmost_piece = other_slice[0]
    pair_score = column_similarity(rightmost_piece, leftmost_piece)

master_repository[tuple([this_key, key])] = pair_score



def piece_image_together(master_repository, slice_map):

    '''
    Takes the master_repo, which is the dictionary with all the similarity scores in it, and uses it to 
    figure out which slices ought to be glued together. Does this by: 

    - Sorting the repo such that the most similar slice pairing are first in the repo
    - Iterating through the sorted repo to grab the next-most-similar pair and gluing those two 
      together. Builds a list that contains the tuples in the order in which they should be combined
    - Passes that list of tuples to the helper "stich_list", which turns the tuples into one ordered list
      without any repeating key numbers. The result lists the slices in the order in which they should be combined
    - Then combines those slices from the slice_map into one 2D array and calls show_image
    '''
    already_combined_keys = list()
    final_order_list = []

    sorted_repo = sorted(master_repository, key=(lambda k: master_repository[k]))

    
    counter = 0
    for key in sorted_repo:
    #max_key = max(slice_map.keys(), key=(lambda k: len(slice_map[k])))

    if(counter == (len(slice_map) - 1)):
break

if(key in already_combined_keys):
    continue

final_order_list.append(key)
key1, key2 = key

already_combined_keys.append(tuple([key2, key1]))
key2_list = list(filter(lambda key: key[1] == key2, master_repository))

for two_key in key2_list:
    already_combined_keys.append(two_key)

counter += 1

init_start, init_end = final_order_list[0]
sub_list = []
sub_list.append(init_start)
sub_list.append(init_end)
final_order = stitch_list(sub_list, init_start, init_end, final_order_list)

build_final_pic_array(final_order, slice_map)


def build_final_pic_array(final_order, slice_map):
    '''
    Takes final_order array, which lists the slices in the order they should be combined (identifying
    each by its key number) and builds the final slice_map according. Passes that final slice array to 
    show_image to get the picture
    '''

    start = final_order[0]
    for n in final_order:
    if(n != start):
slice_map[start] += slice_map[n]

imageutils.show_image(slice_map[start])


def stitch_list(sub_list, start, end, final_order_list):

    '''
    Recursive function that takes the list of tuples which shows the slice pairings that ought 
    to be glued and turns all those pair tuples into one continuous list, from leftmost slice to 
    rightmost. Basically, any tuple (key1, key2) would be added to the end of a tuple (keyX, key1)
    and to the front of a tuple (key2, keyY). 

    Uses recursion to stitch the tuples together like this. Returns the final stitched list.
    '''
    start_is_key2 = [key for key in final_order_list if key[1] == start]
    end_is_key1 = [key for key in final_order_list if key[0] == end]

    if(start_is_key2 == [] and end_is_key1 == []):
    return sub_list

    new_sub_list = []
    if(start_is_key2 != []):
    new_start_tup = start_is_key2[0]
new_start = new_start_tup[0]
new_sub_list.append(new_start)
else:ether(master_repository, slice_map)




if __name__ == '__main__':

    slice_map = get_all_images()

    slice_similarity_control(slice_map)
You have a hand not only in the program implementation, but also
in the program design. As such, take a moment to plan out your approach
to this problem. How will you merge the image slices? You can import any
builtin libraries you need (i.e. itertools, operator, etc), but do not
use any 3rd party libraries (outside of Pillow, which we've provided
through the imageutils module)

Best of luck! Now go find that Holy Grail.
"""
import imageutils
import itertools


if __name__ == '__main__':
    images = []
    for filename in imageutils.files_in_directory('shredded/grail4'):
        images.append(imageutils.load_image(filename))
    imageutils.show_all_images(images)
    reassembled_image = reassemble(images)
    imageutils.show_image(reassembled_image)

'''
***** alternatively, for pixel_similarity1 you can use the fact that pixels are tuples:

return sum([(component[0] - component[1])**2 for component in zip(p1, p2)])

'''
def pixel_similarity1(pixel1, pixel2):
    return (pixel1.red - pixel2.red)**2 + (pixel1.green - pixel2.greem)**2 + (pixel1.blue - pixel2.blue)**2 + (pixel1.alpha - pixel2.alpha)**2

'''
            best_score = sim
            best_pair = (pair[0], pair[1])
    return best_pair


def stitch(imgs):
    """
    FUNCTION: stitch

    Stitches together all the shreds specified in imgs by searching for the
    pairs with highest similarity and iteratively reassembling the image.

    @params: imgs: dict mapping a unique key to a 2D array representing an
    image slice.
    @return: reassembled image represented via 2D array.
    """
    while len(imgs) > 1:
        pair = get_best_pair(imgs)
        stitched = imgs[pair[0]] + imgs[pair[1]]  # Combine the pair of slices.
        del imgs[pair[0]]  # Delete the slices we just used ...
        del imgs[pair[1]]
        # ...and create a unique key & store the new slice
        imgs["({})-({})".format(*pair)] = stitched
    return imgs.popitem()[1]

if __name__ == '__main__':
    # Associate each 2D array with a unique key
    imgs = {i: imageutils.load_image(filename) for i, filename in enumerate(
        imageutils.files_in_directory("./shredded/grail20"))}
    imageutils.show_image(stitch(imgs))
    return sum(similarities) / len(similarities)


def compare_shreds(right_col_shred, left_col_shred, column_compare_func):
    """
    Compares the right-most column of one shred to the left-most column of another
    using provided comparison function

    returns the similarity score for the two columns
    """
    return column_compare_func(right_col_shred[-1], left_col_shred[0], cosine_similarity)


if __name__ == "__main__":
    SHREDS_DIR = "shredded/destination/"
    shred_files = iu.files_in_directory(SHREDS_DIR)[1:]
    shreds = []
    for filename in shred_files:
        shreds.append(iu.load_image(filename))

        ##Note: in the future—make your code a bit more modular and decompose this main function a bit!

    # ordered_shreds will contain the shreds reassembled in the correct order
    ordered_shreds = [shreds.pop(0)]
    # while unordered shreds remain
    while len(shreds) > 0:
        max_right_to_left_sim = float("-Inf")
        candidate_insert_right_shred = None
        max_left_to_right_sim = float("-Inf")
        candidate_insert_left_shred = None
        # loop over all remaining shreds
		highestOne.append(column)


def mergeClosestSlices(slices):
	"""
	Gets the two closest slices and merges them together
	"""
	lowestSimilarity = 9999999999999
	for left in slices:
		for right in slices:
			if left != right:
				currSim = sliceSimilarity(left, right)
				if currSim < lowestSimilarity:
					lowestSimilarity = currSim
					leftHigh = left
					rightHigh = right
	mergeSlices(leftHigh, rightHigh)
	slices.remove(rightHigh)


if __name__ == '__main__':
	slices = []
	for image in imageutils.files_in_directory("shredded/destination"):
		slices.append(imageutils.load_image(image))
	while len(slices) > 1:
		mergeClosestSlices(slices)
	imageutils.show_image(slices[0])


		
    while(num_slices > 1):
        highest = 442.0
        for i_ind, i in enumerate(slices):
            for j_ind, j in enumerate(slices):
                if i != j:
                    sim = slice_similarity(i, j)
                    if sim < highest:
                        highest = sim 
                        highest_ind[0] = i_ind
                        highest_ind[1] = j_ind
        slices[highest_ind[0]] = slices[highest_ind[0]] + slices[highest_ind[1]]
        slices.remove(slices[highest_ind[1]])
        num_slices -= 1
    return slices[0]

if __name__ == '__main__':
    """
    Opens directory of image slices and translates into list pixel arrays, 
    then stiches together original image and opens it
    """
    file_names = imageutils.files_in_directory('shredded/destination')

    slices = []
    for file in file_names:
        pixels = imageutils.load_image(file)
        slices.append(pixels)
    imageutils.show_image(stitch(slices))


#great functional programming on this one!
				minSlice = slices.index((slices[s][0], slices[s][1]))
		answer.append(minSlice)
		remaining.remove(minSlice)
	return answer

if __name__ == '__main__':
    """
    Puts together the slices and store it as a file named 'map'
    Also prints the corresponding message on console.
    
    MESSAGES:
    1) HOLY
    2) YOU FOUND A MESSAGE!
    """
    IMAGE_DIRECTORY = 'shredded/destination'
    files = iu.files_in_directory(IMAGE_DIRECTORY)

    # Stores tuple (filename, pixels)
    slices = [(fn, iu.load_image(fn)) for fn in files]
    answer = glue_together(slices)
    tm = []
    kw = ""
    for a in answer:
    	kw += slices[a][0][-5]
    	tm += slices[a][1]
    iu.save_image(tm, 'map')
    print(kw)


#great decomposition on this one! loved the functional programming of this one
		# get closest slice match and combine
		best_combo = best_combos(slices)
		combined = best_combo[0] + best_combo[1]
		
		# also combine strings and update strings array
		i1 = slices.index(best_combo[0])
		i2 = slices.index(best_combo[1])
		result_string = combined_strings[i1] + combined_strings[i2]
		combined_strings.pop(i1)
		combined_strings.pop(i2 if i2 < i1 else i2 - 1)
		combined_strings.append(result_string)

		# remove individual slices and add combined
		slices.remove(best_combo[0])
		slices.remove(best_combo[1])
		slices.append(combined)

	# print the hidden message and return a single slice comtaining the whole image
	print (combined_strings[0])
	return slices[0]


def combine_images(image_list, filename):
	imageutils.save_image(combine_arrays([imageutils.load_image(i) for i in image_list]), filename)

if __name__ == '__main__':
    image_list = imageutils.files_in_directory('shredded/grail20/')
    combine_images(image_list, 'result')

#beautiful