#	PythonicaCore.py#import mathfrom types import *from tostr import *gSymTable = {}gInList = []gOutList = []def DataEval(data):	# return an evaluated copy of the data	return map(lambda d:d.Eval(), data)def StoreInOut( indata, outdata ):	global gInList, gOutList	gInList.append(indata)	gOutList.append(outdata)#----------------------------------------------------------------------#----------------------------------------------------------------------class Expr:	def __init__(self,data=None,head=None):		t = type(data)		if not data: self.data = []		elif t == TupleType: self.data = list(data)		elif t == ListType: self.data = data		else: self.data = [data]		if head: self.head = head		for d in self.data:			if type(d) != InstanceType:				# hmm... if it's a number, maybe we should just				# replace it with the appropriate ExprNumber?				raise "Bug", "Invalid data passed to Expr()!"			def __str__(self):		return str(self.Head()) + '[' + \			string.join(map(lambda x:str(x),self.data), ',') + ']'		def Head(self):		try: return self.head		except: return None		def __cmp__(self,other):		if self.Head() == other.Head() and self.data == other.data:			return 0		else: return 1			def Eval(self):		return self		def Simplify(self):		return self#----------------------------------------------------------------------class ExprNumber(Expr):	def __init__(self,data=0):		self.data = data			def __str__(self):		if self.data == int(self.data): return str(int(self.data))		return str(self.data)	def Head(self):		return 'Number'		def Eval(self):		return self#----------------------------------------------------------------------class ExprSymbol(Expr):	def __init__(self,data=''):		self.data = data			def __str__(self):			return str(self.data)		def Head(self):		return 'Symbol'	def Eval(self):		global gSymTable		if self.data in gSymTable.keys():			# warning: this could cause recursion!#			print self.data, "defined as", str(gSymTable[self.data]),			out = gSymTable[self.data].Eval()#			print "-->", repr(out)			return out		return self# global predefined symbolsTrue = ExprSymbol('True')False = ExprSymbol('False')#----------------------------------------------------------------------class ExprRule(Expr):	def Head(self):		return 'Rule'#----------------------------------------------------------------------class ExprReplaceAll(Expr):	def Head(self):		return 'ReplaceAll'	def Eval(self):		global gSymTable		if len(self.data) != 2:			raise "ReplaceAll::argrx", "ReplaceAll called with " + str(len(self.data)) \					+ " arguments; 2 arguments are expected."		# to do the substitution, temporarily replace		# the given symbol with the given value		if self.data[1].__class__ != ExprRule:			raise "ReplaceAll::argrx", "Replace all called with " + \					str(self.data[1].__class__) + "; ExprRule expected."		symname = self.data[1].data[0].data		if symname in gSymTable.keys(): oldval = gSymTable[symname]		else: oldval = None		gSymTable[symname] = self.data[1].data[1]		out = self.data[0].Eval()		if oldval: gSymTable[symname] = oldval		else: del gSymTable[symname]		return out#----------------------------------------------------------------------class ExprPlus(Expr):	def Head(self):	return 'Plus'		def Eval(self):		data = DataEval(self.data)		num = 0		extras = []		for d in data:			if d.Head() == 'Number':				num = num + d.data			else:				extras.append(d)		if extras:			# we partially reduced it...			if num: extras.append(ExprNumber(num))			elif len(extras) == 1:				return extras[0].Eval()			return ExprPlus(extras)		else:			# we completely reduced this to a Number			return ExprNumber(num)	def Simplify(self):		# if any of our operands are the same, use a Times		coeff = map(lambda x:1, self.data)		data = []		data[:] = self.data		# convert expressions of the form '3x'		for i in range(0,len(self.data)):			data[i] = data[i].Simplify()			if data[i].__class__ == ExprTimes and \					data[i].data[0].__class__ == ExprNumber:				coeff[i] = data[i].data[0].data				if len(data[i].data) == 2:					data[i] = data[i].data[1]				else:					data[i] = ExprTimes(data[i].data[1:])		# now, combine terms additively		for i in range(0,len(data)):			if coeff[i]:				for j in range(i+1,len(data)):					if data[i] == data[j]:						coeff[i] = coeff[i]+coeff[j]						coeff[j] = 0		# now we have coefficients, make the new list		repl = []		for i in range(0,len(coeff)):			if coeff[i] == 1:				repl.append( data[i] )			elif coeff[i]:				repl.append( ExprTimes([ExprNumber(coeff[i]),data[i]]) )		if len(repl) == 1:			return repl[0]		elif len(repl) == 0: return ExprNumber(0)		else:			return ExprPlus(repl)		#----------------------------------------------------------------------class ExprTimes(Expr):	def Head(self): return 'Times'		def Eval(self):		data = DataEval(self.data)		num = 1		extras = []		for d in data:			if d.Head() == 'Number':				num = num * d.data			else:				extras.append(d)		if extras and num != 0:			# we partially reduced it...			if num != 1: extras = [ExprNumber(num)] + extras			elif len(extras) == 1:				return extras[1].Eval()			return ExprTimes(extras)		else:			# we completely reduced this to a Number			return ExprNumber(num)	def Simplify(self):		data = []		data[:] = self.data				# first, try flattening -- if an argument is a Times,		# absorb its arguments into our own		for i in range(0,len(data)):			if data[i].Head() == 'Times':				data[i:i+1] = data[i].data						# if any of our operands are the same, use a Power		# build a list of terms, each stored as [coeff, base, power]		terms = map(lambda x:[1,x.Simplify(),1], data)		coeff = 1		for i in range(0,len(terms)):			# if it's a ExprNumber, collect separately			if terms[i][1].__class__ == ExprNumber:				coeff = coeff * terms[i][1].data				terms[i][0] = 0			# if it's a ExprTimes, grab the coefficient			if terms[i][1].__class__ == ExprTimes and \					terms[i][1].data[0].__class__ == ExprNumber:				terms[i][0] = terms[i][1].data[0].data				if len(terms[i][1].data) == 2:					terms[i][1] = terms[i][1].data[1]				else:					terms[i][1] = ExprTimes(terms[i][1].data[1:])			# if it's an ExprPower, grab the power			if terms[i][1].__class__ == ExprPower and \					terms[i][1].data[1].__class__ == ExprNumber:				terms[i][2] = terms[i][1].data[1].data				terms[i][1] = terms[i][1].data[0]		# now, combine terms additively		for i in range(0,len(terms)):			if terms[i][0]:				for j in range(i+1,len(terms)):					if terms[i][1] == terms[j][1]:						terms[i][0] = terms[i][0] * terms[j][0]						terms[i][2] = terms[i][2] + terms[j][2]						terms[j][0] = 0		# now we have powers, make the new list		repl = []		for i in range(0,len(terms)):			if terms[i][2] != 0:				if terms[i][2] != 1:					e = ExprPower( [terms[i][1], ExprNumber(terms[i][2])] )				else: e = terms[i][1]				if terms[i][0] == 1:					repl.append( e )				elif terms[i][0]:					repl.append( ExprTimes([ExprNumber(terms[i][0]),e]) )		# put the global coefficient on the front		if coeff != 1:			repl = [ExprNumber(coeff)] + repl		if len(repl) == 1:			return repl[0]		elif not repl: return ExprNumber(1)		return ExprTimes(repl)#----------------------------------------------------------------------class ExprMinus(Expr):	# NOTE: this differs from Mathematica's Minus[] function,	# which takes only one argument and returns its negative.	# This takes one or two arguments, which makes parsing	# much easier.	def Head(self): return 'Minus'		def Eval(self):		data = DataEval(self.data)		if len(data) < 2:			if data[0].Head() != "Number":				return ExprTimes( [ExprNumber(-1),self.data[0]] )			return ExprNumber( -self.data[0].data )		elif len(data) == 2:			if data[1].Head() == "Number":				e = ExprNumber( -self.data[1].data )			else: e = ExprTimes( [ExprNumber(-1),self.data[1]] )			e2 = ExprPlus( [self.data[0],e] )			return e2.Eval()		else:			raise "Minus::argrx", "Minus called with " + str(len(data)) \					+ " arguments; 1 or 2 arguments are expected."		#----------------------------------------------------------------------class ExprDivide(Expr):	def Head(self): return 'Divide'		def Eval(self):		data = DataEval(self.data)		if len(data) != 2:			raise "Divide::argrx", "Divide called with " + str(len(data)) \					+ " arguments; 2 arguments are expected."		if data[0].__class__ == ExprNumber and \			data[1].__class__ == ExprNumber:			return ExprNumber( data[0].data / data[1].data )		else: return self	def Simplify(self):		# replace Divide with Power, where possible		if self.data[1].Head() == 'Number':			return ExprTimes( [ExprNumber(1/self.data[1].data),self.data[0]])		e = ExprPower( [self.data[1],ExprNumber(-1)] )		if self.data[0].Head() == 'Number' and self.data[0].data == 1:			return e		return ExprTimes( [self.data[0],e] )		#----------------------------------------------------------------------class ExprSet(Expr):	def Head(self): return 'Set'	def Eval(self):		global gSymTable		if len(self.data) < 2:			raise "Set::argrx", "Set called with " + str(len(data)) \					+ " arguments; at least 2 arguments are expected."		data = [self.data[0]] + DataEval(self.data[1:])		for d in data[:-1]:			if d.Head() != "Symbol":				raise "Set::ParamError", \						"First parameters of Set must be a Symbol, not " \						+ d.Head()			gSymTable[d.data] = data[-1]		return data[-1]#----------------------------------------------------------------------class ExprUnset(Expr):	def Head(self): return 'Unset'	def Eval(self):		global gSymTable		if len(self.data) < 1:			raise "Unset::argrx", "Unset called with 0" \					+ " arguments; at least 1 argument is expected."		for d in self.data:			if d.Head() != "Symbol":				raise "Set::ParamError", \						"Parameters of Unset must be Symbol, not " \						+ d.Head()			if d.data in gSymTable.keys():				del gSymTable[d.data]		return ExprSymbol("OK")#----------------------------------------------------------------------class ExprPower(Expr):	def Head(self): return 'Power'	def Eval(self):		data = DataEval(self.data)		if len(data) != 2:			raise "Power::argrx", "Power called with " + str(len(data)) \					+ " arguments; 2 arguments are expected."		if data[0].__class__ != ExprNumber or data[1].__class__ != ExprNumber:			return self		return ExprNumber( math.pow(data[0].data, data[1].data) )#----------------------------------------------------------------------class ExprEqual(Expr):	def Head(self): return 'Equal'	def Eval(self):		if len (self.data) < 2: return True		data = DataEval(self.data)		if data[0].__class__ != ExprNumber: return self		val = data[0].data		for d in data[1:]:			if d.__class__ != ExprNumber: return self			if d.data != val: return False		return True#----------------------------------------------------------------------class ExprIn(Expr):	def Head(self): return 'In'	def Eval(self):		data = DataEval(self.data)		if len(data) != 1:			raise "In::argrx", "In called with " + str(len(data)) \					+ " arguments; 1 argument is expected."		if data[0].__class__ != ExprNumber:			return self		idx = int(data[0].data)		# make positive numbers 1-indexed, to match Mathematica		if idx > 0: idx = idx - 1		try: return gInList[idx]		except: return self#----------------------------------------------------------------------class ExprOut(Expr):	def Head(self): return 'Out'	def Eval(self):		data = DataEval(self.data)		if len(data) != 1:			raise "Out::argrx", "Out called with " + str(len(data)) \					+ " arguments; 1 argument is expected."		if data[0].__class__ != ExprNumber:			return self		idx = int(data[0].data)		# make positive numbers 1-indexed, to match Mathematica		if idx > 0: idx = idx - 1		try: return gOutList[idx].Eval()		except: return self		#----------------------------------------------------------------------# end of PythonicaCore.py#----------------------------------------------------------------------