def gradient(A, b, x0, maxiter=10**3, tol=1e-6):
    x = x0.copy()
    g = A @ x - b
    d = -g
    k = 0

    while True:
        alpha = -d @ g / (d @ (A @ d))
        x = x + alpha*d
        g = A @ x - b
        d = -g
        k += 1

        if np.linalg.norm(d) < tol or k > maxiter:
            break

    return x, k



def gradientconjugue(A, b, x0, maxiter=10**3, tol=1e-6):
    x = x0.copy()

    g = A @ x - b
    d = -g
    k = 0

    while True:
        alpha = -d @ g / (d @ (A @ d))
        x = x + alpha*d
        gnew = A @ x - b
        beta = (np.linalg.norm(gnew) / np.linalg.norm(g))**2
        d = -gnew + beta*d
        g = gnew.copy()
        k += 1

        if np.linalg.norm(d) < tol or k > maxiter:
            break

    return x, k