from sympy.parsing.latex import parse_latex from sympy.printing import latex from sympy import solve from sympy.core.numbers import Float ################# Program Executor ######################## spec_token_list = ['frac', 'pi', 'sqrt'] spec_letter_list = ['f', 'r', 'a', 'c', 'p', 'i', 's', 'q', 'r', 't'] low_case_list = [chr(i) for i in range(97, 123)] fixed_order_ops = [ 'Get', 'Iso_Tri_Ang', 'Gsin', 'Gcos', 'Gtan', 'Geo_Mean', 'Ratio', 'TanSec_Ang', \ 'Chord2_Ang', 'Tria_BH_Area', 'Para_Area', 'Kite_Area', 'Circle_R_Circum', \ 'Circle_D_Circum', 'Circle_R_Area', 'Circle_D_Area', 'ArcSeg_Area', 'Ngon_Angsum', \ 'RNgon_B_Area', 'RNgon_L_Area', 'RNgon_H_Area'] alterable_order_ops = [ 'Sum', 'Multiple', 'Equal', 'Gougu', 'Cos_Law', 'Sin_Law', 'Median', 'Proportion', \ 'Tria_SAS_Area', 'PRK_Perim', 'Rect_Area', 'Rhom_Area', 'Trap_Area'] arith_op_list = fixed_order_ops + alterable_order_ops priority_list = ["V0", "V1", "V2", "V3", "V4", "V5", "V6", \ "N0", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", "N10", \ "C0.5", "C2", "C3", "C4", "C5", "C6", "C8", "C60", "C90", "C180", "C360"] V_NUM = 10 class Varible_Record(object): def __init__(self): self.varible_dict = dict() self.mid_varible_dict = dict() self.result = '' def get_priority(token): if token in priority_list: return priority_list.index(token) else: return -1 # arg def is_exist_operator(func, ANNO): if not func in arith_op_list: print("Can Not Find Operators!") raise Exception return func def choose_result(result_list): if len(result_list)==0: return None elif len(result_list)==1: return result_list[0] elif len(result_list)>1: t1 = result_list[0].evalf() t2 = result_list[1].evalf() if (t1>t2 and t2<=0) or (t10): return result_list[0] else: return result_list[1] def operand_update(operands, ANNO): for id in range(len(operands)): # Substitute variable if operands[id] in ANNO.mid_varible_dict: operands[id] = "("+ANNO.mid_varible_dict[operands[id]]+")" # pi if "\\pi" in operands[id]: operands[id]=operands[id].replace('\\pi','(3.141593)') # mixed number (improper fraction) if "\\frac" in operands[id]: loc = operands[id].index("\\frac") if loc>0 and operands[id][loc-1].isdigit(): operands[id] = operands[id][:loc]+'+'+operands[id][loc:] continue # Substitute process(intermediate) variable if operands[id] in ANNO.mid_varible_dict: operands[id] = "("+ANNO.mid_varible_dict[operands[id]]+")" continue # Substitute constant if operands[id][0] == 'C': operands[id] = operands[id][1:] return operands def mid_var_solve(expr_step, ANNO, visit_list, midvar2letter): # replace process(intermediate) variable for key, value in midvar2letter.items(): expr_step = expr_step.replace(key, value) # Convert the latex form expression to sympy solvable form expr_step = parse_latex(expr_step) # Solving argument for letter in visit_list: try: result = solve(expr_step, letter) result = choose_result(result) except: ANNO.mid_varible_dict[letter] = letter continue if not result is None: result = latex(result) is_update = True result_t = result[:] for item in spec_token_list: result_t = result_t.replace(item, '') # more than one unknown varibles for letter_t in visit_list: if letter_t in result_t and letter get numerical value of a """ if len(arg_list)!=1: print(" function has only 1 augment!") raise Exception if arg_list[0] in ANNO.mid_varible_dict: result = ANNO.mid_varible_dict[arg_list[0]] else: result_v = ANNO.varible_dict[arg_list[0]] result_t = result_v[:] for item in spec_token_list: result_t = result_t.replace(item, "@"*len(item)) # Lowercase letters are replaced with solved values result = '' for id in range(len(result_t)): if result_t[id]!="@" and result_t[id] in ANNO.mid_varible_dict: result += "("+ANNO.mid_varible_dict[result_t[id]]+')' else: result += result_v[id] ANNO.result = format(float(parse_latex(result).evalf()),'0.3f') def Sum(arg_list): """ Sum(a, b, c, d) -> a+b+c=d """ if len(arg_list)<3: print(" function has 3 augments at least!") raise Exception expr_step = arg_list[0] for item in arg_list[1:-1]: expr_step += "+" + item expr_step += "-" + arg_list[-1] return expr_step def Multiple(arg_list): """ Multiple(a, b, c, d, e) -> a*b*c*d=e """ if len(arg_list)<3: print(" function has 3 augments at least!") raise Exception expr_step = arg_list[0] for item in arg_list[1:-1]: expr_step += "*" + item expr_step += "-" + arg_list[-1] return expr_step def Equal(arg_list): """ Equal(a, b) -> a=b """ if len(arg_list)!=2: print(" function has 2 augments!") raise Exception expr_step = arg_list[0] + "-" + arg_list[-1] return expr_step def Iso_Tri_Ang(arg_list): """ Iso_Tri_Ang(a, b) -> a+2*b=180 """ if len(arg_list)!=2: print(" function has 2 augments!") raise Exception expr_step = arg_list[0] + "+2*" + arg_list[-1]+"-180" return expr_step def Gougu(arg_list): """ Gougu(a, b, c) -> a^2+b^2=c^2 """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'^{2}'+"+"+arg_list[1]+"^{2}"+'-'+arg_list[2]+"^{2}" return expr_step def Gsin(arg_list): """ Gsin(a, b, c) -> sin(c)=a/b """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'/'+arg_list[1]+'-'+'\\sin{'+arg_list[2]+'/180*3.141593}' return expr_step def Gcos(arg_list): """ Gcos(a, b, c) -> cos(c)=a/b """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'/'+arg_list[1]+'-'+'\\cos{'+arg_list[2]+'/180*3.141593}' return expr_step def Gtan(arg_list): """ Gtan(a, b, c) -> tan(c)=a/b """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'/'+arg_list[1]+'-'+'\\tan{'+arg_list[2]+'/180*3.141593}' return expr_step def Cos_Law(arg_list): """ Cos_Law(a, b, c, d) -> a^2=b^2+c^2-2*b*c """ if len(arg_list)!=4: print(" function has 4 augments!") raise Exception expr_step = arg_list[1]+'^{2}'+"+"+arg_list[2]+"^{2}"+'-'+arg_list[0]+"^{2}"+ \ '-'+"2*"+arg_list[1]+'*'+arg_list[2]+'*'+'\\cos{'+arg_list[3]+'/180*3.141593}' return expr_step def Sin_Law(arg_list): """ Sin_Law(a, b, c, d) -> sin(a)/b=sin(c)/d """ if len(arg_list)!=4: print(" function has 4 augments!") raise Exception expr_step = arg_list[3]+'*'+'\\sin{'+arg_list[0]+'/180*3.141593}'+'-'+ \ arg_list[1]+'*'+'\\sin{'+arg_list[2]+'/180*3.141593}' return expr_step def Median(arg_list): """ Median(a, b, c) -> a+c=2*b """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'-2*'+arg_list[1]+"+"+arg_list[2] return expr_step def Geo_Mean(arg_list): """ Geo_Mean(a, b, c) -> a*b=c^2 """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+"-"+arg_list[2]+'^{2}' return expr_step def Proportion(arg_list): """ Proportion(a, b, c, d) -> a/b=c/d Proportion(a, b, c, d, e) -> (a/b)^e=c/d """ if len(arg_list)<4: print(" function has 4 augments at least!") raise Exception if len(arg_list)==4: expr_step = arg_list[0]+'*'+arg_list[3]+"-"+arg_list[1]+'*'+arg_list[2] else: expr_step = arg_list[0]+'*'+arg_list[3]+'^{1/'+arg_list[4]+"}-"+arg_list[1]+'*'+arg_list[2]+'^{1/'+arg_list[4]+"}" return expr_step def Ratio(arg_list): """ Ratio(a, b, c) -> a/b=c Ratio(a, b, c, d) -> (a/b)^c=d """ if len(arg_list)<3 or len(arg_list)>4: print(" function has 3 or 4 augments!") raise Exception if len(arg_list)==3: expr_step = arg_list[0]+' / '+arg_list[1]+'-'+arg_list[2] else: expr_step = '('+arg_list[0]+' / '+arg_list[1]+')^{'+arg_list[2]+"}"+"-"+arg_list[3] return expr_step def Chord2_Ang(arg_list): """ Chord2_Ang(a, b, c) -> a=(b+c)/2 """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*2-'+arg_list[1]+'-'+arg_list[2] return expr_step def TanSec_Ang(arg_list): """ TanSec_Ang(a, b, c) -> a=(c-b)/2 """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*2+'+arg_list[1]+'-'+arg_list[2] return expr_step def Tria_BH_Area(arg_list): """ Tria_BH_Area(a, b, c) -> a*b/2=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'*0.5-'+arg_list[2] return expr_step def Tria_SAS_Area(arg_list): """ Tria_SAS_Area(a, b, c, d) -> a*c*sin(b)/2=d """ if len(arg_list)!=4: print(" function has 4 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[2]+'*0.5*\\sin{'+arg_list[1]+'/180*3.141593}-'+arg_list[3] return expr_step def PRK_Perim(arg_list): """ PRK_Perim(a, b, c) -> (a+b)*2=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*2+'+arg_list[1]+'*2-'+arg_list[2] return expr_step def Para_Area(arg_list): """ Para_Area(a, b, c) -> a*b=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'-'+arg_list[2] return expr_step def Rect_Area(arg_list): """ Rect_Area(a, b, c) -> a*b=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'-'+arg_list[2] return expr_step def Rhom_Area(arg_list): """ Rhom_Area(a, b, c) -> a*b*2=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'*2-'+arg_list[2] return expr_step def Kite_Area(arg_list): """ Kite_Area(a, b, c) -> a*b/2=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'*0.5-'+arg_list[2] return expr_step def Trap_Area(arg_list): """ Trap_Area(a, b, c, d) -> (a+b)*c/2=d """ if len(arg_list)!=4: print(" function has 4 augments!") raise Exception expr_step = '0.5*('+arg_list[0]+'+'+arg_list[1]+')*'+arg_list[2]+'-'+arg_list[3] return expr_step def Circle_R_Circum(arg_list): """ Circle_R_Circum(a, b) -> 2*pi*a=b Circle_R_Circum(a, b, c) -> 2*pi*a*b/360=c """ if len(arg_list)<2 or len(arg_list)>3: print(" function has 2 or 3 augments!") raise Exception if len(arg_list)==2: expr_step = '2*3.141593*'+arg_list[0]+'-'+arg_list[1] else: expr_step = '2*3.141593*'+arg_list[0]+'*'+arg_list[1]+'/360'+'-'+arg_list[2] return expr_step def Circle_D_Circum(arg_list): """ Circle_D_Circum(a, b) -> pi*a=b Circle_D_Circum(a, b, c) -> pi*a*b/360=c """ if len(arg_list)<2 or len(arg_list)>3: print(" function has 2 or 3 augments!") raise Exception if len(arg_list)==2: expr_step = '3.141593*'+arg_list[0]+'-'+arg_list[1] else: expr_step = '3.141593*'+arg_list[0]+'*'+arg_list[1]+'/360'+'-'+arg_list[2] return expr_step def Circle_R_Area(arg_list): """ Circle_R_Area(a, b) -> pi*a^2=b Circle_R_Area(a, b, c) -> pi*a^2*b/360=c """ if len(arg_list)<2 and len(arg_list)>3: print(" function has 2 or 3 augments!") raise Exception if len(arg_list)==2: expr_step = '3.141593*'+arg_list[0]+'^{2}-'+arg_list[1] else: expr_step = '3.141593*'+arg_list[0]+'^{2}*'+arg_list[1]+'/360'+'-'+arg_list[2] return expr_step def Circle_D_Area(arg_list): """ Circle_D_Area(a, b) -> pi*(a/2)^2=b Circle_D_Area(a, b, c) -> pi*(a/2)^2*b/360=c """ if len(arg_list)<2 and len(arg_list)>3: print(" function has 2 or 3 augments!") raise Exception if len(arg_list)==2: expr_step = '0.25*3.141593*'+arg_list[0]+'^{2}-'+arg_list[1] else: expr_step = '0.25*3.141593*'+arg_list[0]+'^{2}*'+arg_list[1]+'/360'+'-'+arg_list[2] return expr_step def ArcSeg_Area(arg_list): """ ArcSeg_Area(a, b, c) -> pi*a^2*b/360 - a^2*sin(b)/2 = c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = '3.141593*'+arg_list[0]+'^{2}*'+arg_list[1]+'/360-0.5*'+ \ arg_list[0]+'^{2}*\\sin{'+arg_list[1]+'/180*3.141593}-'+arg_list[2] return expr_step def Ngon_Angsum(arg_list): """ Ngon_Ang(a, b) -> (a-2)*180=b """ if len(arg_list)!=2: print(" function has 2 augments!") raise Exception expr_step = '('+arg_list[0]+'-2)*180-'+arg_list[1] return expr_step def RNgon_B_Area(arg_list): """ RNgon_B_Area(a, b, c) -> a*b^2/tan(180/a)/4=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'^{2}/4/\\tan{3.141593/'+arg_list[0]+'}-'+arg_list[2] return expr_step def RNgon_L_Area(arg_list): """ RNgon_L_Area(a, b, c) -> a*b^2*sin(360/a)/2=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'^{2}*0.5*\\sin{2*3.141593/'+arg_list[0]+'}-'+arg_list[2] return expr_step def RNgon_H_Area(arg_list): """ RNgon_H_Area(a, b, c) -> a*b^2*tan(180/a)=c """ if len(arg_list)!=3: print(" function has 3 augments!") raise Exception expr_step = arg_list[0]+'*'+arg_list[1]+'^{2}*\\tan{3.141593/'+arg_list[0]+'}-'+arg_list[2] return expr_step def result_compute(num_all_list, exp_tokens): ANNO = Varible_Record() # Obtain the mapping between lowercase letters to intermediate variables V_i visit_list = [] # arguments denoted by lowercase letters for num in num_all_list: for item in spec_token_list: num = num.replace(item, "@"*len(item)) for letter in num: if letter in low_case_list: visit_list.append(letter) for id, var in enumerate(num_all_list): ANNO.varible_dict["N"+str(id)] = var ANNO.mid_varible_dict["N"+str(id)] = var visit_list.sort() no_visit_list = list(set(low_case_list)-set(spec_letter_list)-set(visit_list)) no_visit_list.sort() # lowercase letters which have not used # mapping between letters to intermediate variables V_i midvar2letter = dict() midletter2var = dict() for id in range(V_NUM): midvar2letter['V'+str(id)] = no_visit_list[id] midletter2var[no_visit_list[id]] = 'V'+str(id) # step split step_list = [] last_op_id = 0 for id, token in enumerate(exp_tokens): if token in arith_op_list and id>0: step_list.append(exp_tokens[last_op_id:id]) last_op_id = id step_list.append(exp_tokens[last_op_id:]) # run step for id, step in enumerate(step_list): operator = is_exist_operator(step[0], ANNO) if operator!='Get': operands = operand_update(step[1:], ANNO) expr_step = eval(operator)(operands) mid_var_solve(expr_step, ANNO, visit_list, midvar2letter) mid_var_update(ANNO, visit_list, midvar2letter, midletter2var, True) mid_var_update(ANNO, visit_list, midvar2letter, midletter2var, False) else: Get(ANNO, step[1:]) return ANNO.result def normalize_exp(exp): # step split step_list = [] last_op_id = 0 for id, token in enumerate(exp): if token in arith_op_list and id>0: step_list.append(exp[last_op_id:id]) last_op_id = id step_list.append(exp[last_op_id:]) # normalize step new_exp = [] for step in step_list: if step[0] in alterable_order_ops: if step[0] in ['Sum', 'Multiple']: begin_id, end_id = 1, -1 step[begin_id: end_id] = sorted(step[begin_id: end_id], key=lambda token:get_priority(token)) if step[0] in ['Equal', 'Gougu', 'PRK_Perim', 'Rect_Area', 'Rhom_Area', 'Trap_Area']: begin_id, end_id = 1, 3 step[begin_id: end_id] = sorted(step[begin_id: end_id], key=lambda token:get_priority(token)) if step[0] == 'Cos_Law': begin_id, end_id = 2, 4 step[begin_id: end_id] = sorted(step[begin_id: end_id], key=lambda token:get_priority(token)) if step[0] in ['Sin_Law', 'Proportion']: if get_priority(step[1])>get_priority(step[3]) and len(step)==5: step[1:3], step[3:5] = step[3:5], step[1:3] if step[0] in ['Tria_SAS_Area', 'Median']: if get_priority(step[1])>get_priority(step[3]): step[1], step[3] = step[3], step[1] new_exp += step return new_exp