import time

import numpy as np
from scipy.linalg import lu, qr, svd


def table4_12(n_values=None, nr_small=50, nr_large=15):
    if n_values is None:
        n = np.array([200, 400, 600, 800, 1000, 2000, 4000], dtype=int)
    else:
        n = np.asarray(n_values, dtype=int)

    # this is to let program use commands once before timing begins
    A = np.random.rand(3, 3)
    L, U = lu(A, permute_l=True)
    Q, R = qr(A)
    UU, S, Vh = svd(A, full_matrices=False)
    V = Vh.T

    tLU = np.inf
    tQR = np.inf
    tSVD = np.inf

    print("\n n  \t   LU (sec) \t  QR  \t SVD ")

    # iteration for matrix size
    IR = len(n)
    tlu = np.zeros(IR)
    tqr = np.zeros(IR)
    tsvd = np.zeros(IR)
    for i in range(IR):
        tLU = np.inf
        tQR = np.inf
        tSVD = np.inf

        # NR = number of trials run for timing
        if n[i] < 2000:
            NR = nr_small
        else:
            NR = nr_large

        # timing iteration
        for r in range(NR):
            A = np.random.rand(n[i], n[i])

            start = time.perf_counter()
            L, U = lu(A, permute_l=True)
            toc_value = time.perf_counter() - start
            # lut[i]=toc_value+lut[i]
            if toc_value < tLU:
                tLU = toc_value

            start = time.perf_counter()
            Q, R = qr(A)
            toc_value = time.perf_counter() - start
            # qrt[i]=toc_value+qrt[i]
            if toc_value < tQR:
                tQR = toc_value

            start = time.perf_counter()
            UU, S, Vh = svd(A, full_matrices=False)
            V = Vh.T
            toc_value = time.perf_counter() - start
            # svdt[i]=toc_value+svdt[i]
            if toc_value < tSVD:
                tSVD = toc_value

            del A, L, U, Q, R, UU, S, V, Vh

        # lut[i]=lut[i]/NR
        # qrt[i]=qrt[i]/(NR*lut[i])
        # svdt[i]=svdt[i]/(NR*lut[i])
        tlu[i] = tLU
        tqr[i] = tQR / tLU
        tsvd[i] = tSVD / tLU
        print(f" \\, {n[i]:d}  \\, & {tlu[i]:5.4f}  & {tqr[i]:5.1f} & {tsvd[i]:5.1f}   \\\\ \\hline ")

    print("\n")
    # print("\n\n", n[i], lut[i], qrt[i], svdt[i])

    # this is the data for Table 8.10
    qrt = np.zeros(IR)
    svdt = np.zeros(IR)
    for ii in range(IR):
        qrt[ii] = tlu[ii] * tqr[ii]
        svdt[ii] = tlu[ii] * tsvd[ii]
        print(
            f" \\, {n[ii]:d}  \\,  & {tlu[ii]:5.4f}  & {qrt[ii]:8.4f} & "
            f"{svdt[ii]:8.4f}   \\\\ \\hline  "
        )
    print("\n")


if __name__ == "__main__":
    table4_12()
