def pad(lst,method="center"):
	'''
	Pads a list of strings according to the method chosen
	to be as long as the longest element of the list
	'''
	longest = max([len(l) for l in lst])
	if method == "center":
		lst = [l.center(longest) for l in lst]
	elif method == "left":
		lst = [l.ljust(longest) for l in lst]
	elif method == "right":
		lst = [l.rjust(longest) for l in lst]
	else:
		raise Exception("Bad Pad Method")
	return lst

def avgerator(dct,method="percent"):
	'''
	Takes key:value pairs where the value is some number
	and returns a key:value pair where the value is scaled 0->1
	'''
	outdict = {}

	if method == "percent": #Percent (default) scales to the sum of all values
		total = float(sum(dct.values()))
		for d in dct:
			outdict[d] = dct[d]/total
	elif method == "max": #max sumes to the max value
		total = float(max(dct.values()))
		for d in dct:
			outdict[d] = dct[d]/total
	elif method == "abspercent": #Same as percent, but counts negatives as positive for the sum
		total = float(sum(map(abs,dct.values())))
		for d in dct:
			outdict[d] = dct[d]/total
	else:
		raise Exception("Bad Averageator Method")
	return outdict

def bargraph(dct,width=80,sort=True):
	'''
	Takes a dictionary and outputs a horizontal bar graph with defined width
	'''
	if (max(dct.values()) > 1) or (min(dct.values()) < -1):
		raise Exception("Dict values must be between negative one and one")

	if (min(dct.values()) < 0):
		barwidth = (width - (3 + len(dct.keys()[0]) + 6))/2 # 3 for colons, 6 for value/percent sign, other for strings
		negative = True
	else:
		barwidth = width - (3 + len(dct.keys()[0]) + 6) # 3 for colons, 6 for value/percent sign, other for strings
		negative = False
	bars = []
	for d in dct:
		if dct[d] < 0:
			barln = int(barwidth*abs(dct[d]))
			spaceln = barwidth - barln 
			bar = " " * spaceln + "#" * barln + " " * (barwidth -1)
		else:
			if negative:
				barln = int(barwidth*dct[d]) -1
				spaceln = barwidth - barln -1
				bar = " " * barwidth + "#" * barln + " " * spaceln
			else:
				barln = int(barwidth*dct[d])
				spaceln = barwidth - barln
				bar = "#"*barln + " "*spaceln

		fmt = {
			"name":d,
			"value":dct[d],
			"bar":bar
			}
		if negative:
			string = "%(name)s:%(value)+.2f%%:%(bar)s:" % fmt
		else:
			string = "%(name)s:%(value).2f%%:%(bar)s:" % fmt

		bars.append((dct[d],string)) #output a tuple, so I can sort it
	if sort:
		bars.sort()
	bars,output = zip(*bars) #pull the output from the sorted list in the right order
	return output

def doitall(l1,l2):
	l1 = pad(l1)
	d = dict(zip(l1,l2))
	d2 = avgerator(d,method="abspercent")
	output = bargraph(d2)
	str = ""
	for o in output:
		str += o + "\n"
	return str


def gridinate(listx,listy,maxx,maxy,scale=1.0):

	grid = [] # Remember, the x,y orientation of grid is flipped because you reference the row then the column

	for dy in range(maxy+1):
			grid.append([" " for dx in range(maxx+1)])
	points = zip(listx,listy)
	negs = []
	for x,y in points:
		if x < maxx and y < maxy:
			if y < 0:
				negs.append((x,y))
			else:
				grid[y][x] = "X"

	grid.reverse() #make it so the bottom left is 0,0 instead of top left
	string = ""
	step = maxy
	for y in grid[:-1]: 
		'''
		skip the 0 point (x-axis), because it gets drawn at the time of the axis.
		It is the last element in the array, because it is drawn top down.
		'''
		position = "%+ 5.1f" % (step/scale) #this is the y-coordinate printout on the left
		string += position + "|" #border
		for x in y:
			string += x
		string += "\n"
		step -= 1
	#draw the axis:
	string += "%+ 5.1f|" % 0
	for i in grid[-1]:
		if i == " ":
			string += "-"
		else:
			string += i

	string += "\n"

	if negs: #create the negative side of the graph.
		negrid = []
		for dy in range(maxy+1):
			negrid.append([" " for dx in range(maxx+1)])
		for x,y in negs:
			if x < maxx and abs(y) < maxy:
				#these y's are negative, so by making them + we draw from the top
				negrid[-y][x] = "X"

		step = -1
		for y in negrid[1:]:
			position = "%+ 5.1f" % (step/scale) #this is the y-coordinate printout on the left
			string += position + "|" #border
			for x in y:
				string += x
			string += "\n"
			step -= 1
	return string

def coordstogrid(xlist, ylist,maxx=False,maxy=False,marks=5,scalef=1.0):
	if not maxx:
		maxx = max(xlist)
	if not maxy:
		maxy = max(ylist)
	string = gridinate(xlist,ylist,maxx,maxy)


	#Draw the x-axis
	scalestring  = "     |" #spaces just for alignment with y-axis numbering
	scalestring2 = "     |"
	every = max(maxx/marks,1)
	for i in range(1,maxx+1):
		if (i%every) == 0:
			num = "%.f" % ((i/scalef))
			lgth = len(num)
			if lgth > 1:
				test = scalestring2[(-lgth + 1):] .strip()
				scalestring2 = scalestring2[:(-lgth +1)] + num
				scalestring += "|"
			elif lgth == 1:
				scalestring2 += num
				scalestring += "|"
				
			else:
				scalestring += " "
				scalestring2 += " "
		else:
			scalestring += " "
			scalestring2 += " "

	string +=  scalestring + "\n"
	string += scalestring2 + "\n"
	return string



def functiontogrid(function,maxx,scale=1,maxy=False,marks=5):
	maxx = (maxx+1)*scale
	scalef = float(scale)

	#making a good 'ol fashioned x,y table for graphing just like you did in pre-algebra
	ys = []	
	xs = range(maxx)
	for x in xs:
		ys.append(int(function(x/scalef)*scale))
	if not maxy:
		maxy = min(maxx*scale,max(ys)+1)
	string = gridinate(xs,ys,maxx,maxy,scalef)


	#Draw the x-axis
	scalestring  = "     |" #spaces just for alignment with y-axis numbering
	scalestring2 = "     |"
	every = max(maxx/marks,1)
	for i in range(1,maxx+1):
		if (i%every) == 0:
			num = "%.1f" % ((i/scalef))
			lgth = len(num)
			if lgth > 1:
				test = scalestring2[(-lgth + 1):] .strip()
				if test == "":
					scalestring2 = scalestring2[:(-lgth +1)] + num
					scalestring += "|"
				else:
					scalestring += " "
					scalestring2 += " "
			elif lgth == 1:
				scalestring2 += num
				scalestring += "|"
				
			else:
				scalestring += " "
				scalestring2 += " "

		else:
			scalestring += " "
			scalestring2 += " "

	string +=  scalestring + "\n"
	string += scalestring2 + "\n"
	return string
