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 :)__

Source: codewars | Difficulty: MEDIUM_HARD
Tags: String algorithms

Fundamentals

Mathematics

Starter code: def simplify(poly):
	

Solutions:
def simplify(poly):
	import re
	matches = re.findall('([+\\-]?)(\\d*)([a-z]+)', poly)
	expanded = [[int(i[0] + (i[1] if i[1] != '' else '1')), ''.join(sorted(i[2]))] for i in matches]
	variables = sorted(list(set((i[1] for i in expanded))), key=lambda x: (len(x), x))
	coefficients = {v: sum((i[0] for i in expanded if i[1] == v)) for v in variables}
	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('([\\-+]?)(\\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
	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)
			add = ''
		elif i == '-' and len(add) != 0:
			poly.append(add)
			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('([+-]?)(\\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])', '\\1', poly)[poly[0] == '+':]


def simplify(poly):
	import re
	from collections import defaultdict
	terms = defaultdict(lambda : 0)
	m = re.finditer('([+-]?)\\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('([+-]?)(\\d*?)([a-z]+)', poly):
		terms[''.join(sorted(c))] += eval(a + (b or '1'))
	return re.sub('((?<=[-+])|\\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('([-+])', ' \\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('(-|\\+)\\1+', '\\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('\\d+', m)
		n = int(f.group(0)) if f else 1
		if m[0] == '-':
			n = -n
		v = re.search('[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