# OneDMapClasses.py

# import in a try-except statement in case a module is not installed
try:	
	import numpy as np
	import matplotlib.pyplot as plt
	
except ImportError, msg:	# catch any import exceptions, print error message
	print 'Error importing modules:', msg


class OneDimMap():
	"""
	Base class for all map classes. This provides 
	methods common to all map classes.
	"""
	
	def __init__(self):
		"""Constructor for the OneDMap base class."""
		
		# list of defaults in this order: [ ic, r low, r high ]
		self.defaults = [0.0, 0.0, 1.0]
		
		self.resetVals()	# set map values to defaults
		
		self.mapStr = 'One Dimensional Map f(x) = x'	# place holder for map string
		
	def __repr__(self):
		"""String representation of map."""
		
		return self.mapStr	# defined in __init__
		
	def __call__(self, num_its):
		"""Iterates the map num_its times and saves the state."""
		
		for n in xrange(num_its):
			self.func()	# compute the maps's function
		# state of last iteration is saved in self.state
		
	def func(self):
		"""Computes f(x). This is a place holder for all map classes."""
		
		self.state = 0.0	# in this case, f(x) = 0.0
		
	def resetVals(self):
		"""Set map values to their respective defaults."""
		
		# set default values for state and parameters/limits
		self.ic = self.defaults[0]
		self.state = self.defaults[0]
		self.r = self.defaults[1]
		self.rLow = self.defaults[1]
		self.rHi = self.defaults[2]
		
## end class OneDMap ##


class LogisticMap(OneDimMap):
	"""The logistic map class."""
	
	def __init__(self):
		"""Constructor for a LogisticMap object."""
		
		OneDimMap.__init__(self)	# invoke constructor of base class
		
		# list of defaults in this order: [ ic, r low, r high ]
		self.defaults = [0.3, 0.0, 4.0]
		
		self.resetVals()
		
		# descriptive string of map
		self.mapStr = 'Logistic Map: f(x) = rx(1-x)'
		
	def func(self):
		"""Defines the logistic map function."""
		
		self.state = self.r * self.state * (1.0 - self.state)
		
## end class LogisticMap ##


class CosineMap(OneDimMap):
	"""The cosine map class."""
	
	def __init__(self):
		"""Constructor for a CosineMap object."""
		
		OneDimMap.__init__(self)	# invoke constructor of base class
		
		# list of defaults in this order: [ ic, r low, r high ]
		self.defaults = [0.3, -5.0, 5.0]
		
		self.resetVals()
		
		# descriptive string of map
		self.mapStr = 'Cosine Map: f(x)=rcos(x)'
		
	def func(self):
		"""Defines the cosine map function."""
		
		self.state = self.r * np.cos(self.state)
		
## end class CosineMap ##


class TentMap(OneDimMap):
	"""The tent map class."""
	def __init__(self):
		"""Constructor for a TentMap object."""
		
		OneDimMap.__init__(self)	# invoke constructor of base class
		
		# list of defaults in this order: [ ic, r low, r high ]
		self.defaults = [0.3, 0.0, 2.0]
		
		self.resetVals()
		
		# descriptive string of map
		self.mapStr = 'Tent Map: f(x)=rx (x<0.5), f(x)=r(1-x) (x>=0.5)'
		
	def func(self):
		"""Defines the tent map function."""
		
		if self.state < 0.5:
			self.state = self.r * self.state
		else:
			self.state = self.r * (1.0 - self.state)
			
			
# Create a dictionary of map choices with pointers to their classes.
# Note that only references to the classes are stored below and no objects
# are actually created until the main loop.
maps = {
	'1' : ['Logistic Map: f(x)=rx(1-x)', LogisticMap],
	'2' : ['Cosine Map: f(x)=rcos(x)', CosineMap],
	'3' : ['Tent Map: f(x)=rx (x<0.5), f(x)=r(1-x) (x>=0.5)', TentMap]
}
