import numpy as np
import random

# here we define a DiscretizedMap object, and a number of specific discretized maps

class DiscretizedMap:
	"""
	Defined here is a discretized one dimensional map object. It contains an iterating function,
	a title string, a current state, and an iterator method.
	"""
	def __init__(self,function,titlestring):
		self.function=function
		self.titlestring=titlestring
	
	# ic is an initial condition, r is a control parameter, N is a grid scaling factor (if N is an integer, then N is the number of grid points in the interval)
	# the iterator will iterate  'iterations' times. icreset chooses whether to hold the current state when iterating, or to reset to the ic value.
	def iterate(self,ic,r,N,iterations,icreset='reset'):
		
		# sets ic to the appropriate value if icreset is not set to 'hold'
		if icreset == 'reset':
			self.state=ic
		# this tests for incorrect icreset values
		elif (icreset != 'reset') and (icreset != 'hold'):
			raise ValueError, 'Allowed values for icreset are \'hold\' and \'reset\'.' 		
		#the map is iterated
		for i in xrange(iterations):
			self.state=self.function(self.state,r,N)
		return self.state
	
	
# In all of the following maps, the discretization consists of scaling the map by some factor N, rounding down to the nearest integer, and then rescaling by 1/N

def logistic(x,r,N): 
	return np.floor( r*x*(1-x)*float(N) )/float(N)
Logistic=DiscretizedMap(logistic,'Discretized Logistic Map')

def tent(x,r,N):
	if x < 0.5:
		return np.floor( r*x*float(N) )/float(N)
	else:
		return np.floor( (r-r*x)*float(N) )/float(N)
Tent=DiscretizedMap(tent,'Discretized Tent Map')

# These are the same maps as above, but with the least significant 'bit' randomized. If N is a power of 2, 'bit' does truly mean bit.
# Otherwise, we can interpret this as adding zero mean noise with variance 1/N

def logisticrand(x,r,N): 
	return np.floor( r*x*(1-x)*float(N) )/float(N) + round(random.random())/float(N)
LogisticRand=DiscretizedMap(logisticrand,'Discretized Logistic Map + noise')

def tentrand(x,r,N):
	if x < 0.5:
		return np.floor( r*x*float(N) )/float(N) + round(random.random())/float(N)
	else:
		return np.floor( (r-r*x)*float(N) )/float(N) + round(random.random())/float(N)
TentRand=DiscretizedMap(tentrand,'Discretized Tent Map + noise')