# OneDTool.py

# try importing modules, catch exceptions if a module isn't installed
try:
	import sys
	import matplotlib.pyplot as plt
	import numpy as np
	import OneDMapClasses as odm
	
except ImportError, msg:
	print 'Error importing modules:', msg
	sys.exit()
	

mapList = odm.maps.keys()	# convenient for looping in order
mapList.sort()	# dictionaries do not necessarily automatically sort, even if
				# items are entered in correct order, so we have to manually 
				# sort keys
mapChoice = None

# main loop...
while mapChoice != '0':
	
	
	# prompt user for map choice
	print '\nOne Dimensional Map Bifurcation Tool'
	print 'Maps available:'
	for item in mapList:
		print item, '-', odm.maps[item][0]
		
	# get choice from user		
	mapChoice = raw_input('Enter choice (%s-%s) or 0 to quit): ' % (mapList[0], mapList[-1]))
	
	# check if user wants to quit
	if mapChoice == '0': 
		print 'Exiting One-D bifurcation tool...'
		break	# break out of main loop
		
	# else check if valid choice
	elif mapChoice not in mapList: 
		print '\n-->Invalid choice! Please try again...'
		continue	# skip to beginning of main loop to try again
		
	# this is where we actually create a map object
	OneDMap = odm.maps[mapChoice][1]()
	
	
	# get initial condition from user
	while True:
		ic = raw_input('Enter initial condition (leave blank for default of %0.2f): ' \
			% OneDMap.defaults[0]).strip()
		
		if ic != '':	# something was entered
			try:
				OneDMap.ic = float(ic)	# try saving value to the map object
				break	# break out of loop if valid ic
				
			except ValueError:	# catch non-numerical input
				print 'Invalid number... try again'
		else:	# user wants default
			break	# default value is already set, so jump out of loop, go to next prompt
		
	
	# get lower parameter limit
	while True:
		rLow = raw_input('Enter lower limit of r (leave blank for default of %0.2f): ' \
			% OneDMap.defaults[1]).strip()
			
		if rLow != '':	# something was entered
			try:
				OneDMap.rLow = float(rLow)	# try saving value to the map object
				break	# break out of loop if valid ic
				
			except ValueError:	# catch non-numerical input
				print 'Invalid number... try again'
		else:	# user wants default
			break	# default value is already set, so jump out of loop, go to next prompt
		
	
	# get upper parameter limit
	while True:
		rHi = raw_input('Enter upper limit of r (leave blank for default of %0.2f): ' \
			% OneDMap.defaults[2]).strip()
			
		if rHi != '':	# something was entered
			try:
				OneDMap.rHi = float(rHi)	# try saving value to the map object
				break	# break out of loop if valid ic
				
			except ValueError:	# catch non-numerical input
				print 'Invalid number... try again'
		else:	# user wants default
			break	# default value is already set, so jump out of loop, go to next prompt
	
	# now compute and plot the bifurcation diagram
	plt.figure()
	# Title for plot
	plt.title(OneDMap.mapStr + '\nBifurcation diagram for r in [%g,%g]' \
		% (OneDMap.rLow,OneDMap.rHi))
	# Label axes
	plt.xlabel('Control parameter r')
	plt.ylabel('{X(n)}')
	
	# The iterates we'll throw away
	nTransients = 200
	# This sets how much the attractor is filled in
	nIterates = 250
	# This sets how dense the bifurcation diagram will be
	nSteps = 400
	# Sweep the control parameter over the desired range
	rInc = (OneDMap.rHi-OneDMap.rLow)/float(nSteps)
	
	for OneDMap.r in np.arange(OneDMap.rLow, OneDMap.rHi, rInc):
	
		# Set the initial condition to the reference value
		OneDMap.state = OneDMap.ic
		# Throw away the transient iterations
		OneDMap(nTransients)
		# Now store the next batch of iterates
		rsweep = np.repeat(OneDMap.r, nIterates) # array of parameter values
		x = [ ]                                  # The iterates
		for i in xrange(nIterates):
			OneDMap(1)	# iterate the map once
			x.append( OneDMap.state )	# save the new state for plotting
		plt.plot(rsweep, x, 'k,') # Plot the list of (r,x) pairs as pixels
		
	plt.show()
	
	# delete object before creating a new one
	del OneDMap 
	
## end main loop ##
