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

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):
    vs = set()
    for eq in equations:
        eq = "".join(eq.split(" "))
        coeffs, _ = parse_equation(eq)
        vs.update(coeffs.keys())
    vs = sorted(vs)
    A = []
    b = []
    for eq in equations:
        coeffs, rhs = parse_equation(eq)
        row = [coeffs.get(v, 0) for v in vs]
        A.append(row)
        b.append(rhs)
    return np.array(A), np.array(b), vs

def round_to_precision(number, places):
    if number == 0:
        return 0
    an = abs(number)
    if an < 1:
        dec_places = math.floor(math.log(an, 10))
        ar = round(an, -dec_places+places-1)
    else:
        ar = round(an, places)
    rounded = ar*np.sign(number)
    return rounded

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)
end = time.time()
time_elapsed = end-start
ls = [float(fs) for fs in s]
if DEBUG_TEXT:
    print(A, b, v, ls, time_elapsed,sep="")
    print()
rs = [round_to_precision(s_i, SOLUTION_DECIMAL_PLACES) for s_i in ls]
for var,val in zip(v,rs):
    print(var,'=',val)
rounded_time = round_to_precision(time_elapsed, TIME_DECIMAL_PLACES)
print(f"\nTime: {rounded_time} seconds")