When we attended middle school were asked to simplify mathematical expressions like "3x-yx+2xy-x" (or usually bigger), and that was easy-peasy ("2x+xy"). But tell that to your pc and we'll see! 

Write a function: `simplify`, that takes a string in input, representing a *multilinear non-constant polynomial in integers coefficients* (like `"3x-zx+2xy-x"`), and returns another string as output where the same expression has been simplified in the following way ( `->` means application of `simplify`):

- All possible sums and subtraction of equivalent monomials ("xy==yx") has been done, e.g.: `"cb+cba" -> "bc+abc"`, `"2xy-yx" -> "xy"`, `"-a+5ab+3a-c-2a" -> "-c+5ab"`

- All monomials appears in order of increasing number of variables, e.g.: `"-abc+3a+2ac" -> "3a+2ac-abc"`, `"xyz-xz" -> "-xz+xyz"`
 
- If two monomials have the same number of variables, they appears in lexicographic order, e.g.: `"a+ca-ab" -> "a-ab+ac"`, `"xzy+zby" ->"byz+xyz"`
  
- There is no leading `+` sign if the first coefficient is positive, e.g.: `"-y+x" -> "x-y"`, but no restrictions for `-`:  `"y-x" ->"-x+y"`

---

__N.B.__ to keep it simplest, the string in input is restricted to represent only *multilinear non-constant polynomials*, so you won't find something like `-3+yx^2'. **Multilinear** means in this context: **of degree 1 on each variable**.

**Warning**: the string in input can contain arbitrary variables represented by lowercase characters in the english alphabet.

__Good Work :)__

Difficulty: interview

Starter code:

def simplify(poly):
	

Solutions:
def simplify(poly):
    # I'm feeling verbose today
    
    # get 3 parts (even if non-existent) of each term: (+/-, coefficient, variables)
    import re
    matches = re.findall(r'([+\-]?)(\d*)([a-z]+)', poly)
    
    # get the int equivalent of coefficient (including sign) and the sorted variables (for later comparison)
    expanded = [[int(i[0] + (i[1] if i[1] != "" else "1")), ''.join(sorted(i[2]))] for i in matches]
    
    # get the unique variables from above list. Sort them first by length, then alphabetically
    variables = sorted(list(set(i[1] for i in expanded)), key=lambda x: (len(x), x))
    
    # get the sum of coefficients (located in expanded) for each variable
    coefficients = {v:sum(i[0] for i in expanded if i[1] == v) for v in variables}
    
    # clean-up: join them with + signs, remove '1' coefficients, and change '+-' to '-'
    return '+'.join(str(coefficients[v]) + v for v in variables if coefficients[v] != 0).replace('1','').replace('+-','-')

import re
def simplify(poly):
    terms = {}
    for sign, coef, vars in re.findall(r'([\-+]?)(\d*)([a-z]*)', poly):
        sign = (-1 if sign == '-' else 1)
        coef = sign * int(coef or 1)
        vars = ''.join(sorted(vars))
        terms[vars] = terms.get(vars, 0) + coef
    # sort by no. of variables
    terms = sorted(list(terms.items()), key=lambda v_c: (len(v_c[0]), v_c[0]))
    return ''.join(map(format_term, terms)).strip('+')

def format_term(xxx_todo_changeme):
    (vars, coef) = xxx_todo_changeme
    if coef == 0:
        return ''
    if coef == 1:
        return '+' + vars
    if coef == -1:
        return '-' + vars
    return '%+i%s' % (coef, vars)


def parse_terms(string):
    poly, add = [], ""
    for i in string:
        if i == "+" and len(add) != 0:
            poly.append(add)
            #poly.append("+")
            add = ""
        elif i == "-" and len(add) != 0:
            poly.append(add)
            #poly.append("+")
            add = "-"
        else: 
            add = add + i
    poly.append(add)
    return poly


def parse_coef(term):
    numbers = "1234567890"
    coef, end = "", 0
    
    if term == "+":
        return False, 0, term
    else:
        for i in range(len(term)):
            if term[i] in numbers+"-":
                coef = coef + term[i]
            else: 
                end = i
                break
        
    if coef == "":
        return True, 1, term
    elif coef == "-":
        return True, -1, term[1:]
    else:
        return True, int(coef), term[end:]
    
    
def simplify(poly):
    print(poly)
    if poly[0] == "+":
        poly = poly.replace("+", "", 1)
        print(poly)
        
    coeff = []
    poly = parse_terms(poly)
    print(poly)
    
    for i in range(len(poly)):
        if_term, coef, term = parse_coef(poly[i])
        if if_term:
            coeff.append(coef)
            poly[i] = "".join(sorted(term))
            

    for i in range(len(poly)-1):
        for j in range(i+1, len(poly)):
            if poly[i] == poly[j] and coeff[i] != 0:
                coeff[i] = coeff[i] + coeff[j]
                coeff[j] = 0
            
            
    poly = [[poly[i], coeff[i], len(poly[i])] for i in range(len(poly))]
    poly.sort(key= lambda poly: poly[0])    
    poly.sort(key= lambda poly: poly[2])
    print(poly)
    
    
    for i in range(len(poly)):
        if poly[i][1] == 1:
            pass
        elif poly[i][1] == -1:
            poly[i][0] = "-" + poly[i][0]
        elif poly[i][1] == 0:
            poly[i][0] = ""            
        else:
            poly[i][0] = str(poly[i][1]) + poly[i][0]
    
    output = [i[0] for i in poly if i[0] != ""] 
    send = ""
    for i in range(len(output) - 1):
        if output[i+1][0] == "-":
            send = send + output[i]
        else: 
            send = send + output[i] + "+"
    send = send + output[-1]
    
    print(send)
    return send
    


import re
def simplify(poly):
    p = {}
    for m in re.findall(r'([+-]?)(\d*)([a-z]+)', poly):
        var = ''.join(sorted(m[2]))
        p[var] = p.get(var,0)+(-1 if m[0]=='-' else 1)*(int(m[1]) if m[1]!='' else 1)
    poly = ''.join('+-'[p[k]<0]+str(abs(p[k]))+k for k in sorted(p, key=lambda x:(len(x),x)) if p[k])
    return re.sub('([+-])1(?=[a-z])',r'\1', poly)[poly[0]=='+':]

def simplify(poly):
  import re
  from collections import defaultdict
  
  terms = defaultdict(lambda: 0)
  m = re.finditer(r'([+-]?)\s*(\d?)(\w+)', poly)

  for x in m:
    terms[frozenset(x.group(3))] += int(x.group(1) + x.group(2)) if x.group(2) else -1 if x.group(1) == '-' else 1

  f = [('+' if sign == 1 else '-' if sign==-1 else '{:+d}'.format(sign), ''.join(sorted(term))) for term, sign in terms.items() if sign != 0]
  
  ff = sorted(f, key=lambda x: (len(x[1]), x[1]))
  
  first_term = (ff[0][0][1:] if ff[0][0][0] == '+' else ff[0][0]) + ff[0][1]
  
  return first_term + ''.join(p + t for p, t in ff[1:])

def simplify(poly):
    
    i = 0
    eq = {}
    length = len(poly)

    while i < length:
        if poly[i] in "+-":
            const = poly[i]
            i += 1
        else:
            const = "+"
        
        while poly[i].isnumeric():
            const += poly[i]
            i += 1
        if not const[-1].isnumeric():
            const += "1"
        
        var = ""
        while i < length and poly[i] not in "-+":
            var += poly[i]
            i += 1
        var = "".join(sorted(var))
        eq[var] = eq.get(var, 0) + int(const)
    
    
    output = ""
    for term in sorted(eq, key = lambda x: (len(x), x)):
        if eq[term] > 1:
            output += "+" + str(eq[term]) + term
        elif eq[term] == 1:
            output += "+" + term
        elif eq[term] == -1:
            output += "-" + term
        elif eq[term] < 0:
            output += str(eq[term]) + term
        
    if output.startswith("+"):
        return output[1:]
    else:
        return output

from collections import defaultdict
import re
def simplify(poly):
    terms = defaultdict(lambda: 0)
    for a, b, c in re.findall(r'([+-]?)(\d*?)([a-z]+)', poly):
        terms[''.join(sorted(c))] += eval(a + (b or '1'))
    return (
        re.sub
        (
            r'((?<=[-+])|\b)1(?=[a-z])|((?:[-+])|\b)0(?:[a-z]+)|\+(?=-)',
            '',
            '+'.join([f'{terms[x]}{x}' for x in sorted(list(terms.keys()), key = lambda x: (len(x), x))])
        )
    )


import re
def simplify(p):
    p,d = p.strip('+'),{}
    p = re.sub(r'([-+])', r' \1 ', p).split()
    start = 0 if p[0] != '-' else 1
    for i in range(start, len(p), 2):
        find = next(k for k in range(len(p[i])) if p[i][k].isalpha())
        digits = int(p[i][:find] or 1)
        digits = -digits if p[i - 1] == '-' else digits
        to_find = "".join(sorted(p[i][find:]))
        d[to_find] = d.get(to_find, 0) + digits
    li = [[i, j] for i, j in d.items() if j != 0]
    o= sorted(li, key=lambda x: (len(x[0]), x[0]))
    return re.sub(r'(-|\+)\1+', r'\1', "".join([['+', '-'][j < 0] + (str(j) if j not in [1, -1] else '') + i for i, j in o]).strip('+'))

import re
def simplify(p):
    if p[0] not in '-+':
        p = '+'+p
    p=p.replace('-',' -')
    p=p.replace('+',' +')
    p = p[1:].split(' ')
    d = {}
    for m in p:
        f = re.search(r'\d+',m)
        n = int(f.group(0)) if f else 1
        if m[0] == '-':
            n = -n
        v = re.search(r'[a-z]+',m).group(0)
        v = ''.join(sorted(v))
        try:
            d[v] += n
            if not d[v]:
                del d[v]
        except KeyError:
            d[v] = n
    res = ''.join(('+' if d[v]>0 else '-')+(str(abs(d[v])) if abs(d[v])>1 else '')+v for v in sorted(d.keys(),key=lambda x:(len(x),x)))
    if res[0]=='+':
        res = res[1:]
    return res