%PDF- %PDF-
| Direktori : /proc/thread-self/root/usr/lib/python3/dist-packages/sympy/integrals/rubi/parsetools/ |
| Current File : //proc/thread-self/root/usr/lib/python3/dist-packages/sympy/integrals/rubi/parsetools/parse.py |
"""
Parser for FullForm[Downvalues[]] of Mathematica rules.
This parser is customised to parse the output in MatchPy rules format. Multiple
`Constraints` are divided into individual `Constraints` because it helps the
MatchPy's `ManyToOneReplacer` to backtrack earlier and improve the speed.
Parsed output is formatted into readable format by using `sympify` and print the
expression using `sstr`. This replaces `And`, `Mul`, 'Pow' by their respective
symbols.
Mathematica
===========
To get the full form from Wolfram Mathematica, type:
```
ShowSteps = False
Import["RubiLoader.m"]
Export["output.txt", ToString@FullForm@DownValues@Int]
```
The file ``output.txt`` will then contain the rules in parseable format.
References
==========
[1] http://reference.wolfram.com/language/ref/FullForm.html
[2] http://reference.wolfram.com/language/ref/DownValues.html
[3] https://gist.github.com/Upabjojr/bc07c49262944f9c1eb0
"""
import re
import os
import inspect
from sympy import sympify, Function, Set, Symbol
from sympy.printing import StrPrinter
from sympy.utilities.misc import debug
class RubiStrPrinter(StrPrinter):
def _print_Not(self, expr):
return "Not(%s)" % self._print(expr.args[0])
def rubi_printer(expr, **settings):
return RubiStrPrinter(settings).doprint(expr)
replacements = dict( # Mathematica equivalent functions in SymPy
Times="Mul",
Plus="Add",
Power="Pow",
Log='log',
Exp='exp',
Sqrt='sqrt',
Cos='cos',
Sin='sin',
Tan='tan',
Cot='1/tan',
cot='1/tan',
Sec='1/cos',
sec='1/cos',
Csc='1/sin',
csc='1/sin',
ArcSin='asin',
ArcCos='acos',
# ArcTan='atan',
ArcCot='acot',
ArcSec='asec',
ArcCsc='acsc',
Sinh='sinh',
Cosh='cosh',
Tanh='tanh',
Coth='1/tanh',
coth='1/tanh',
Sech='1/cosh',
sech='1/cosh',
Csch='1/sinh',
csch='1/sinh',
ArcSinh='asinh',
ArcCosh='acosh',
ArcTanh='atanh',
ArcCoth='acoth',
ArcSech='asech',
ArcCsch='acsch',
Expand='expand',
Im='im',
Re='re',
Flatten='flatten',
Polylog='polylog',
Cancel='cancel',
#Gamma='gamma',
TrigExpand='expand_trig',
Sign='sign',
Simplify='simplify',
Defer='UnevaluatedExpr',
Identity = 'S',
Sum = 'Sum_doit',
Module = 'With',
Block = 'With',
Null = 'None'
)
temporary_variable_replacement = { # Temporarily rename because it can raise errors while sympifying
'gcd' : "_gcd",
'jn' : "_jn",
}
permanent_variable_replacement = { # Permamenely rename these variables
r"\[ImaginaryI]" : 'ImaginaryI',
"$UseGamma": '_UseGamma',
}
# These functions have different return type in different cases. So better to use a try and except in the constraints, when any of these appear
f_diff_return_type = ['BinomialParts', 'BinomialDegree', 'TrinomialParts', 'GeneralizedBinomialParts', 'GeneralizedTrinomialParts', 'PseudoBinomialParts', 'PerfectPowerTest',
'SquareFreeFactorTest', 'SubstForFractionalPowerOfQuotientOfLinears', 'FractionalPowerOfQuotientOfLinears', 'InverseFunctionOfQuotientOfLinears',
'FractionalPowerOfSquareQ', 'FunctionOfLinear', 'FunctionOfInverseLinear', 'FunctionOfTrig', 'FindTrigFactor', 'FunctionOfLog',
'PowerVariableExpn', 'FunctionOfSquareRootOfQuadratic', 'SubstForFractionalPowerOfLinear', 'FractionalPowerOfLinear', 'InverseFunctionOfLinear',
'Divides', 'DerivativeDivides', 'TrigSquare', 'SplitProduct', 'SubstForFractionalPowerOfQuotientOfLinears', 'InverseFunctionOfQuotientOfLinears',
'FunctionOfHyperbolic', 'SplitSum']
def contains_diff_return_type(a):
"""
This function returns whether an expression contains functions which have different return types in
diiferent cases.
"""
if isinstance(a, list):
for i in a:
if contains_diff_return_type(i):
return True
elif type(a) == Function('With') or type(a) == Function('Module'):
for i in f_diff_return_type:
if a.has(Function(i)):
return True
else:
if a in f_diff_return_type:
return True
return False
def parse_full_form(wmexpr):
"""
Parses FullForm[Downvalues[]] generated by Mathematica
"""
out = []
stack = [out]
generator = re.finditer(r'[\[\],]', wmexpr)
last_pos = 0
for match in generator:
if match is None:
break
position = match.start()
last_expr = wmexpr[last_pos:position].replace(',', '').replace(']', '').replace('[', '').strip()
if match.group() == ',':
if last_expr != '':
stack[-1].append(last_expr)
elif match.group() == ']':
if last_expr != '':
stack[-1].append(last_expr)
stack.pop()
elif match.group() == '[':
stack[-1].append([last_expr])
stack.append(stack[-1][-1])
last_pos = match.end()
return out[0]
def get_default_values(parsed, default_values={}):
"""
Returns Optional variables and their values in the pattern
"""
if not isinstance(parsed, list):
return default_values
if parsed[0] == "Times": # find Default arguments for "Times"
for i in parsed[1:]:
if i[0] == "Optional":
default_values[(i[1][1])] = 1
if parsed[0] == "Plus": # find Default arguments for "Plus"
for i in parsed[1:]:
if i[0] == "Optional":
default_values[(i[1][1])] = 0
if parsed[0] == "Power": # find Default arguments for "Power"
for i in parsed[1:]:
if i[0] == "Optional":
default_values[(i[1][1])] = 1
if len(parsed) == 1:
return default_values
for i in parsed:
default_values = get_default_values(i, default_values)
return default_values
def add_wildcards(string, optional={}):
"""
Replaces `Pattern(variable)` by `variable` in `string`.
Returns the free symbols present in the string.
"""
symbols = [] # stores symbols present in the expression
p = r'(Optional\(Pattern\((\w+), Blank\)\))'
matches = re.findall(p, string)
for i in matches:
string = string.replace(i[0], "WC('{}', S({}))".format(i[1], optional[i[1]]))
symbols.append(i[1])
p = r'(Pattern\((\w+), Blank\))'
matches = re.findall(p, string)
for i in matches:
string = string.replace(i[0], i[1] + '_')
symbols.append(i[1])
p = r'(Pattern\((\w+), Blank\(Symbol\)\))'
matches = re.findall(p, string)
for i in matches:
string = string.replace(i[0], i[1] + '_')
symbols.append(i[1])
return string, symbols
def seperate_freeq(s, variables=[], x=None):
"""
Returns list of symbols in FreeQ.
"""
if s[0] == 'FreeQ':
if len(s[1]) == 1:
variables = [s[1]]
else:
variables = s[1][1:]
x = s[2]
else:
for i in s[1:]:
variables, x = seperate_freeq(i, variables, x)
return variables, x
return variables, x
def parse_freeq(l, x, cons_index, cons_dict, cons_import, symbols=None):
"""
Converts FreeQ constraints into MatchPy constraint
"""
res = []
cons = ''
for i in l:
if isinstance(i, str):
r = ' return FreeQ({}, {})'.format(i, x)
# First it checks if a constraint is already present in `cons_dict`, If yes, use it else create a new one.
if r not in cons_dict.values():
cons_index += 1
c = '\n def cons_f{}({}, {}):\n'.format(cons_index, i, x)
c += r
c += '\n\n cons{} = CustomConstraint({})\n'.format(cons_index, 'cons_f{}'.format(cons_index))
cons_name = 'cons{}'.format(cons_index)
cons_dict[cons_name] = r
else:
c = ''
cons_name = next(key for key, value in sorted(cons_dict.items()) if value == r)
elif isinstance(i, list):
s = sorted(set(get_free_symbols(i, symbols)))
s = ', '.join(s)
r = ' return FreeQ({}, {})'.format(generate_sympy_from_parsed(i), x)
if r not in cons_dict.values():
cons_index += 1
c = '\n def cons_f{}({}):\n'.format(cons_index, s)
c += r
c += '\n\n cons{} = CustomConstraint({})\n'.format(cons_index, 'cons_f{}'.format(cons_index))
cons_name = 'cons{}'.format(cons_index)
cons_dict[cons_name] = r
else:
c = ''
cons_name = next(key for key, value in cons_dict.items() if value == r)
if cons_name not in cons_import:
cons_import.append(cons_name)
res.append(cons_name)
cons += c
if res != []:
return ', ' + ', '.join(res), cons, cons_index
return '', cons, cons_index
def generate_sympy_from_parsed(parsed, wild=False, symbols=[], replace_Int=False):
"""
Parses list into Python syntax.
Parameters
==========
wild : When set to True, the symbols are replaced as wild symbols.
symbols : Symbols already present in the pattern.
replace_Int: when set to True, `Int` is replaced by `Integral`(used to parse pattern).
"""
out = ""
if not isinstance(parsed, list):
try: # return S(number) if parsed is Number
float(parsed)
return "S({})".format(parsed)
except:
pass
if parsed in symbols:
if wild:
return parsed + '_'
return parsed
if parsed[0] == 'Rational':
return 'S({})/S({})'.format(generate_sympy_from_parsed(parsed[1], wild=wild, symbols=symbols, replace_Int=replace_Int), generate_sympy_from_parsed(parsed[2], wild=wild, symbols=symbols, replace_Int=replace_Int))
if parsed[0] in replacements:
out += replacements[parsed[0]]
elif parsed[0] == 'Int' and replace_Int:
out += 'Integral'
else:
out += parsed[0]
if len(parsed) == 1:
return out
result = [generate_sympy_from_parsed(i, wild=wild, symbols=symbols, replace_Int=replace_Int) for i in parsed[1:]]
if '' in result:
result.remove('')
out += "("
out += ", ".join(result)
out += ")"
return out
def get_free_symbols(s, symbols, free_symbols=None):
"""
Returns free_symbols present in `s`.
"""
free_symbols = free_symbols or []
if not isinstance(s, list):
if s in symbols:
free_symbols.append(s)
return free_symbols
for i in s:
free_symbols = get_free_symbols(i, symbols, free_symbols)
return free_symbols
def set_matchq_in_constraint(a, cons_index):
"""
Takes care of the case, when a pattern matching has to be done inside a constraint.
"""
lst = []
res = ''
if isinstance(a, list):
if a[0] == 'MatchQ':
s = a
optional = get_default_values(s, {})
r = generate_sympy_from_parsed(s, replace_Int=True)
r, free_symbols = add_wildcards(r, optional=optional)
free_symbols = sorted(set(free_symbols)) # remove common symbols
r = sympify(r, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not")})
pattern = r.args[1].args[0]
cons = r.args[1].args[1]
pattern = rubi_printer(pattern, sympy_integers=True)
pattern = setWC(pattern)
res = ' def _cons_f_{}({}):\n return {}\n'.format(cons_index, ', '.join(free_symbols), cons)
res += ' _cons_{} = CustomConstraint(_cons_f_{})\n'.format(cons_index, cons_index)
res += ' pat = Pattern(UtilityOperator({}, x), _cons_{})\n'.format(pattern, cons_index)
res += ' result_matchq = is_match(UtilityOperator({}, x), pat)'.format(r.args[0])
return "result_matchq", res
else:
for i in a:
if isinstance(i, list):
r = set_matchq_in_constraint(i, cons_index)
lst.append(r[0])
res = r[1]
else:
lst.append(i)
return lst, res
def _divide_constriant(s, symbols, cons_index, cons_dict, cons_import):
# Creates a CustomConstraint of the form `CustomConstraint(lambda a, x: FreeQ(a, x))`
lambda_symbols = sorted(set(get_free_symbols(s, symbols, [])))
r = generate_sympy_from_parsed(s)
r = sympify(r, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not")})
if r.has(Function('MatchQ')):
match_res = set_matchq_in_constraint(s, cons_index)
res = match_res[1]
res += '\n return {}'.format(rubi_printer(sympify(generate_sympy_from_parsed(match_res[0]), locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not")}), sympy_integers = True))
elif contains_diff_return_type(s):
res = ' try:\n return {}\n except (TypeError, AttributeError):\n return False'.format(rubi_printer(r, sympy_integers=True))
else:
res = ' return {}'.format(rubi_printer(r, sympy_integers=True))
# First it checks if a constraint is already present in `cons_dict`, If yes, use it else create a new one.
if not res in cons_dict.values():
cons_index += 1
cons = '\n def cons_f{}({}):\n'.format(cons_index, ', '.join(lambda_symbols))
if 'x' in lambda_symbols:
cons += ' if isinstance(x, (int, Integer, float, Float)):\n return False\n'
cons += res
cons += '\n\n cons{} = CustomConstraint({})\n'.format(cons_index, 'cons_f{}'.format(cons_index))
cons_name = 'cons{}'.format(cons_index)
cons_dict[cons_name] = res
else:
cons = ''
cons_name = next(key for key, value in cons_dict.items() if value == res)
if cons_name not in cons_import:
cons_import.append(cons_name)
return cons_name, cons, cons_index
def divide_constraint(s, symbols, cons_index, cons_dict, cons_import):
"""
Divides multiple constraints into smaller constraints.
Parameters
==========
s : constraint as list
symbols : all the symbols present in the expression
"""
result =[]
cons = ''
if s[0] == 'And':
for i in s[1:]:
if i[0]!= 'FreeQ':
a = _divide_constriant(i, symbols, cons_index, cons_dict, cons_import)
result.append(a[0])
cons += a[1]
cons_index = a[2]
else:
a = _divide_constriant(s, symbols, cons_index, cons_dict, cons_import)
result.append(a[0])
cons += a[1]
cons_index = a[2]
r = ['']
for i in result:
if i != '':
r.append(i)
return ', '.join(r),cons, cons_index
def setWC(string):
"""
Replaces `WC(a, b)` by `WC('a', S(b))`
"""
p = r'(WC\((\w+), S\(([-+]?\d)\)\))'
matches = re.findall(p, string)
for i in matches:
string = string.replace(i[0], "WC('{}', S({}))".format(i[1], i[2]))
return string
def process_return_type(a1, L):
"""
Functions like `Set`, `With` and `CompoundExpression` has to be taken special care.
"""
a = sympify(a1[1])
x = ''
processed = False
return_value = ''
if type(a) == Function('With') or type(a) == Function('Module'):
for i in a.args:
for s in i.args:
if isinstance(s, Set) and not s in L:
x += '\n {} = {}'.format(s.args[0], rubi_printer(s.args[1], sympy_integers=True))
if not type(i) in (Function('List'), Function('CompoundExpression')) and not i.has(Function('CompoundExpression')):
return_value = i
processed = True
elif type(i) == Function('CompoundExpression'):
return_value = i.args[-1]
processed = True
elif type(i.args[0]) == Function('CompoundExpression'):
C = i.args[0]
return_value = '{}({}, {})'.format(i.func, C.args[-1], i.args[1])
processed = True
return x, return_value, processed
def extract_set(s, L):
"""
this function extracts all `Set` functions
"""
lst = []
if isinstance(s, Set) and not s in L:
lst.append(s)
else:
try:
for i in s.args:
lst += extract_set(i, L)
except: # when s has no attribute args (like `bool`)
pass
return lst
def replaceWith(s, symbols, index):
"""
Replaces `With` and `Module by python functions`
"""
return_type = None
with_value = ''
if type(s) == Function('With') or type(s) == Function('Module'):
constraints = ' '
result = '\n\n\ndef With{}({}):'.format(index, ', '.join(symbols))
if type(s.args[0]) == Function('List'): # get all local variables of With and Module
L = list(s.args[0].args)
else:
L = [s.args[0]]
lst = []
for i in s.args[1:]:
lst += extract_set(i, L)
L += lst
for i in L: # define local variables
if isinstance(i, Set):
with_value += '\n {} = {}'.format(i.args[0], rubi_printer(i.args[1], sympy_integers=True))
elif isinstance(i, Symbol):
with_value += "\n {} = Symbol('{}')".format(i, i)
#result += with_value
if type(s.args[1]) == Function('CompoundExpression'): # Expand CompoundExpression
C = s.args[1]
result += with_value
if isinstance(C.args[0], Set):
result += '\n {} = {}'.format(C.args[0].args[0], C.args[0].args[1])
result += '\n return {}'.format(rubi_printer(C.args[1], sympy_integers=True))
return result, constraints, return_type
elif type(s.args[1]) == Function('Condition'):
C = s.args[1]
if len(C.args) == 2:
if all(j in symbols for j in [str(i) for i in C.free_symbols]):
result += with_value
#constraints += 'CustomConstraint(lambda {}: {})'.format(', '.join([str(i) for i in C.free_symbols]), sstr(C.args[1], sympy_integers=True))
result += '\n return {}'.format(rubi_printer(C.args[0], sympy_integers=True))
else:
if 'x' in symbols:
result += '\n if isinstance(x, (int, Integer, float, Float)):\n return False'
if contains_diff_return_type(s):
n_with_value = with_value.replace('\n', '\n ')
result += '\n try:{}\n res = {}'.format(n_with_value, rubi_printer(C.args[1], sympy_integers=True))
result += '\n except (TypeError, AttributeError):\n return False'
result += '\n if res:'
else:
result+=with_value
result += '\n if {}:'.format(rubi_printer(C.args[1], sympy_integers=True))
return_type = (with_value, rubi_printer(C.args[0], sympy_integers=True))
return_type1 = process_return_type(return_type, L)
if return_type1[2]:
return_type = (with_value+return_type1[0], rubi_printer(return_type1[1]))
result += '\n return True'
result += '\n return False'
constraints = ', CustomConstraint(With{})'.format(index)
return result, constraints, return_type
elif type(s.args[1]) == Function('Module') or type(s.args[1]) == Function('With'):
C = s.args[1]
result += with_value
return_type = (with_value, rubi_printer(C, sympy_integers=True))
return_type1 = process_return_type(return_type, L)
if return_type1[2]:
return_type = (with_value+return_type1[0], rubi_printer(return_type1[1]))
result += return_type1[0]
result += '\n return {}'.format(rubi_printer(return_type1[1]))
return result, constraints, None
elif s.args[1].has(Function("CompoundExpression")):
C = s.args[1].args[0]
result += with_value
if isinstance(C.args[0], Set):
result += '\n {} = {}'.format(C.args[0].args[0], C.args[0].args[1])
result += '\n return {}({}, {})'.format(s.args[1].func, C.args[-1], s.args[1].args[1])
return result, constraints, None
result += with_value
result += '\n return {}'.format(rubi_printer(s.args[1], sympy_integers=True))
return result, constraints, return_type
else:
return rubi_printer(s, sympy_integers=True), '', return_type
def downvalues_rules(r, header, cons_dict, cons_index, index):
"""
Function which generates parsed rules by substituting all possible
combinations of default values.
"""
rules = '['
parsed = '\n\n'
repl_funcs = '\n\n'
cons = ''
cons_import = [] # it contains name of constraints that need to be imported for rules.
for i in r:
debug('parsing rule {}'.format(r.index(i) + 1))
# Parse Pattern
if i[1][1][0] == 'Condition':
p = i[1][1][1].copy()
else:
p = i[1][1].copy()
optional = get_default_values(p, {})
pattern = generate_sympy_from_parsed(p.copy(), replace_Int=True)
pattern, free_symbols = add_wildcards(pattern, optional=optional)
free_symbols = sorted(set(free_symbols)) #remove common symbols
# Parse Transformed Expression and Constraints
if i[2][0] == 'Condition': # parse rules without constraints separately
constriant, constraint_def, cons_index = divide_constraint(i[2][2], free_symbols, cons_index, cons_dict, cons_import) # separate And constraints into individual constraints
FreeQ_vars, FreeQ_x = seperate_freeq(i[2][2].copy()) # separate FreeQ into individual constraints
transformed = generate_sympy_from_parsed(i[2][1].copy(), symbols=free_symbols)
else:
constriant = ''
constraint_def = ''
FreeQ_vars, FreeQ_x = [], []
transformed = generate_sympy_from_parsed(i[2].copy(), symbols=free_symbols)
FreeQ_constraint, free_cons_def, cons_index = parse_freeq(FreeQ_vars, FreeQ_x, cons_index, cons_dict, cons_import, free_symbols)
pattern = sympify(pattern, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not") })
pattern = rubi_printer(pattern, sympy_integers=True)
pattern = setWC(pattern)
transformed = sympify(transformed, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not") })
constraint_def = constraint_def + free_cons_def
cons += constraint_def
index += 1
# below are certain if - else condition depending on various situation that may be encountered
if type(transformed) == Function('With') or type(transformed) == Function('Module'): # define separate function when With appears
transformed, With_constraints, return_type = replaceWith(transformed, free_symbols, index)
if return_type is None:
repl_funcs += '{}'.format(transformed)
parsed += '\n pattern' + str(index) + ' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')'
parsed += '\n ' + 'rule' + str(index) + ' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', With{}'.format(index) + ')\n'
else:
repl_funcs += '{}'.format(transformed)
parsed += '\n pattern' + str(index) + ' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + With_constraints + ')'
repl_funcs += '\n\n\ndef replacement{}({}):\n'.format(
index, ', '.join(free_symbols)
) + return_type[0] + '\n return '.format(index) + return_type[1]
parsed += '\n ' + 'rule' + str(index) + ' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', replacement{}'.format(index) + ')\n'
else:
transformed = rubi_printer(transformed, sympy_integers=True)
parsed += '\n pattern' + str(index) + ' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')'
repl_funcs += '\n\n\ndef replacement{}({}):\n return '.format(index, ', '.join(free_symbols), index) + transformed
parsed += '\n ' + 'rule' + str(index) + ' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', replacement{}'.format(index) + ')\n'
rules += 'rule{}, '.format(index)
rules += ']'
parsed += ' return ' + rules +'\n'
header += ' from sympy.integrals.rubi.constraints import ' + ', '.join(word for word in cons_import)
parsed = header + parsed + repl_funcs
return parsed, cons_index, cons, index
def rubi_rule_parser(fullform, header=None, module_name='rubi_object'):
"""
Parses rules in MatchPy format.
Parameters
==========
fullform : FullForm of the rule as string.
header : Header imports for the file. Uses default imports if None.
module_name : name of RUBI module
References
==========
[1] http://reference.wolfram.com/language/ref/FullForm.html
[2] http://reference.wolfram.com/language/ref/DownValues.html
[3] https://gist.github.com/Upabjojr/bc07c49262944f9c1eb0
"""
if header is None: # use default header values
path_header = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
header = open(os.path.join(path_header, "header.py.txt")).read()
header = header.format(module_name)
cons_dict = {} # dict keeps track of constraints that has been encountered, thus avoids repetition of constraints.
cons_index = 0 # for index of a constraint
index = 0 # indicates the number of a rule.
cons = ''
# Temporarily rename these variables because it
# can raise errors while sympifying
for i in temporary_variable_replacement:
fullform = fullform.replace(i, temporary_variable_replacement[i])
# Permanently rename these variables
for i in permanent_variable_replacement:
fullform = fullform.replace(i, permanent_variable_replacement[i])
rules = []
for i in parse_full_form(fullform): # separate all rules
if i[0] == 'RuleDelayed':
rules.append(i)
parsed = downvalues_rules(rules, header, cons_dict, cons_index, index)
result = parsed[0].strip() + '\n'
cons += parsed[2]
# Replace temporary variables by actual values
for i in temporary_variable_replacement:
cons = cons.replace(temporary_variable_replacement[i], i)
result = result.replace(temporary_variable_replacement[i], i)
cons = "\n".join(header.split("\n")[:-2]) + '\n' + cons
return result, cons