import numpy as np
import math
import matplotlib.pyplot as plt

# Define the range of n values
nfact = np.arange(1, 21, 1)  # Limited range due to factorial growth
nexp = np.arange(1, 60, 1)
ncubed = np.arange(1, 1_000_000, 10)
n = np.arange(1, 10_000_000, 10)

# Calculate the functions
log_n = np.log(n+1)
n_values = n
n_log_n = n * np.log(n+1)
n_squared = n**2
n_cubed = ncubed**3
n_quasi = n**(np.log(n+1)**(1/3))
exp_n = 2**nexp
factorial_n = np.array([math.factorial(i) for i in nfact])

# Plotting
plt.figure(figsize=(10, 6))
plt.loglog(n, log_n, label='$\log(n)$')
plt.loglog(n, n_values, label='$n$')
plt.loglog(n, n_log_n, label='$n\log(n)$')
plt.loglog(n, n_squared, label='$n^2$')
plt.loglog(ncubed, n_cubed, label='$n^3$')
plt.loglog(n, n_quasi, label='$n^{(\log n)^c}$')
plt.loglog(nexp, exp_n, label='$2^n$')
plt.loglog(nfact, factorial_n, label='$n!$')

plt.xlabel('$n$')
plt.ylabel('$f(n)$')
plt.legend()
plt.grid(True, which="both", linestyle='--', linewidth=0.5)
plt.tight_layout()

if True:
    plt.savefig("time-complexity-classical.png", dpi=300)

plt.show()
