import re
import numpy as np
import time
import math
DEBUG_TEXT = False
TIME_DECIMAL_PLACES = 3

def parse_equation(equation):
    pattern = r'([+-]?\d*)\s*([a-zA-Z]?)'
    lhs, rhs = equation.split('=')
    rhs = int(rhs.strip())
    terms = re.findall(pattern, lhs)
    if DEBUG_TEXT: print(terms)
    coefficients = {}
    for coeff, var in terms:
        if not (coeff or var):
            continue
        if DEBUG_TEXT: print(coeff,var,sep=',',end='!')
        if coeff in {'','+'}:
            coefficients[var] = 1
        elif coeff == '-':
            coefficients[var] = -1
        else:
            coefficients[var] = int(coeff)
        if DEBUG_TEXT: print(coeff, coefficients[var],sep=',',end="$")
    return coefficients, rhs

def convert_to_matrix(equations):
    variables = set()
    for eq in equations:
        eq = "".join(eq.split(" "))
        coefficients, _ = parse_equation(eq)
        variables.update(coefficients.keys())
    variables = sorted(variables)
    A = []
    b = []
    for eq in equations:
        coefficients, rhs = parse_equation(eq)
        row = [coefficients.get(var, 0) for var in variables]
        A.append(row)
        b.append(rhs)
    return np.array(A), np.array(b), variables

equations = """2a - b + 3c + d - e + 2f - g + h + j = 5
-a + 4b - c + 2d + e - f + g + i - 2j = 3
3a + b - 2c + d + 4e - f + 2g + h - i + j = 10
a - b + c + 3d - e + f - 2g + h + i + 2j = 0
-2a + 3b + c - d + e + f + g - 3h + i - j = -4
a + b - c + d + e + f - g + h - i + j = 7
4a - b + c + d + e - f + g + 2h - i + j = 12
-a + b + c - 2d + e + f + 3g - h + i - j = 1
a + 2b - 3c + d + e - f + g + h - 2i + j = 6
a - b + c + d - e + f - g + h + i + j = 2"""
list_eq = equations.split("\n")
print("\n".join(list_eq))
print()
start = time.time()
A, b, v = convert_to_matrix(list_eq)
s = np.linalg.solve(A,b)
if DEBUG_TEXT:
    print(A, b, v, s,sep="")
    print()
end = time.time()
for var,val in zip(v,s):
    print(var,'=',val)
time_elapsed = end-start
if time_elapsed < 1:
    dec_places = math.floor(math.log(time_elapsed, 10))
    rounded_time = round(time_elapsed, -dec_places-np.sign(dec_places)*(TIME_DECIMAL_PLACES-1))
else:
    rounded_time = round(time_elapsed, TIME_DECIMAL_PLACES)
print(f"\nTime: {rounded_time} seconds")