#!/usr/bin/python

# globals
verbose = False # set to True if you want debug info printed
max_iter = 1000 # this stops infinate loops incase the code does not allow for some input

def solve(problem_str):
    """
    This task was to create a program that can process simple maths equations 
    without the use of any external libraries, lambdas, eval() etc. See Stack 
    Overflow question here for more details:
    http://stackoverflow.com/questions/21573436/solving-math-in-python-without-any-built-in-eval-functions-or-external-libraries/21580431#21580431
    """
    
    def multiply(arg_list):
        x = 1
        for i in arg_list:
            x *= i
        return x

    def find_innermost(x_str):
        a, b, c = [-1], [0], 0
        while True:
            start = a[-1]+1 # get position just after last opening bracket as start
            a.append(x_str.find('(', start)) # find next (
            b.append(x_str.find(',', start)) # find next ,
            c = x_str.find(')', start)       # find next )
            # if closing bracket is first or no more opening brackets left
            if (a[-1] > c) or (a[-1] == -1): 
                 # add to this to allow for more than 2 args by getting last comma before )?
                if (b[-2] > a[-3]) and (b[-2] < a[-2]): 
                    # if there is a comma between opening brackets 2 & 3 steps ago
                    return x_str[b[-2]+1:c+1] 
                else:
                    return x_str[a[-3]+1:c+1] 
            if len(a) > max_iter:
                raise Exception("Stuck in find_innermost loop")

    def do_sum(x_str):
        # get function arguements as integers from command string
        args = [int(x) for x in x_str[x_str.find('(')+1:x_str.find(')')].split(',')]
        task = x_str[:3].lower() # get function to be performed from command string
        if task == 'add':
            return sum(args)
        elif task == 'sub':
            return args[0] - sum(args[1:])
        elif task == 'abs':
            if len(args) > 1:
                raise Exception("Only one arguement can be supplied for abs() function")
            return abs(args.pop())
        elif task == 'mul':
            return multiply(args)
        else:
            print x_str + ': Task not recognised, please modify program or input'
            raise Exception("Invalid input")
    
    # solve function starts here...
    i = 0
    while True:
        i += 1
        if verbose: print 'debug: problem_str:', problem_str
        # find innermost nested problem if >1 opening bracket...
        if problem_str.count('(') > 1:
            x_str = find_innermost(problem_str)
        else:
            x_str = problem_str
        if verbose: print '.'*6, 'x_str:\t', x_str
        # process individual problem extracted from string...
        x = do_sum(x_str)
        if verbose: print '.'*6, 'x:\t', x, '\n'
        # replace individual problem with solution in full problem string...
        problem_str = problem_str.replace(x_str, str(x))
        # return result if no problems remaining in string...
        if problem_str.count('(') == 0:
            return int(problem_str)
        if i >= max_iter:
            raise Exception("Stuck in main loop")
        

# test solve function...
p1 = 'abs(add(add(9465,38),multiply(add(63303,146),46)))'
p2 = 'abs(add(multiply(95,multiply(-1,multiply(13,18875))),multiply(-1,add(18293,26))))'
p3 = 'abs(add(subtract(add(add(151,26875),122),254),subtract(237,multiply(-1,56497))))'
r1, r2, r3 = solve(p1), solve(p2), solve(p3)
print 'p1 evaluates to:', r1
print 'p2 evaluates to:', r2
print 'p3 evaluates to:', r3