import numpy as np

### Testdaten ##################################################################

A1 = np.array([ [ 2, -1 ],
                [-1,  4 ]])

### Funktion von frueheren Aufgaben ############################################

def gramSchmidt(A):
    m, n = A.shape 
    q = np.zeros((m,n))
    r = np.zeros((n,n))
    a0 = A[:,0]
    r[0][0] = np.linalg.norm(a0)
    q[:,0] = a0 / r[0][0] 
    for j in range(1,m):
        aj = A[:,j]
        qj = aj.copy()
        for i in range(j):
            qi = q[:,i]
            r[i][j] = np.dot(qi, aj)
            qj = qj - r[i][j] * qi
        r[j][j] = np.linalg.norm(qj)
        qj = qj / r[j][j] 
        q[:,j] = qj
    return q, r

### Aufgabe 3 ##################################################################

def nonDiagSum(A): 
    return np.sum(np.abs(A)) - np.sum(np.abs(np.diag(A)))

def qrMethod(A, eps):
    n = len(A)
    QQ = np.eye(n)		# QQ  = E
    k = 0			# Iterationszaehler

    while nonDiagSum(A) > eps:
        Q, R = gramSchmidt(A)	# A_k = Q_k * R_k
        A = np.dot(R, Q)	# A_{k+1} = R * Q
        QQ = np.dot(QQ, Q)	# QQ = QQ * Q_k
        k = k + 1

    print(f'eps = {eps}, #Iterationen: {k}') 

    return np.diag(A), QQ

### kleiner Test ###############################################################

l, U = qrMethod(A1, 1e-12)
print(f'Eigenwerte: {l}')
print(f'Matrix der Eigenvektoren:\n {U}')
