import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA, TruncatedSVD

print("="*70)
print("FULL REPORT: PCA vs SVD (ALL COMBINATIONS)")
print("="*70)

# ─────────────────────────────
# DATASET
# ─────────────────────────────
X = np.array([
    [2, 1],
    [3, 5],
    [4, 3],
    [5, 6],
    [6, 7],
    [7, 8]
])

print("\nOriginal Data:")
print(X)

# ─────────────────────────────
# 1️⃣ MANUAL PCA
# ─────────────────────────────
print("\n" + "="*50)
print("MANUAL PCA")
print("="*50)

mean = np.mean(X, axis=0)
X_centered = X - mean

cov = np.cov(X_centered.T)
eigvals_pca, eigvecs_pca = np.linalg.eig(cov)

idx = np.argsort(eigvals_pca)[::-1]
eigvals_pca = eigvals_pca[idx]
eigvecs_pca = eigvecs_pca[:, idx]

X_pca_manual = X_centered @ eigvecs_pca

var_ratio_pca_manual = eigvals_pca / np.sum(eigvals_pca)

print("\nEigenvalues (PCA):", eigvals_pca)
print("Variance Ratio (PCA):", var_ratio_pca_manual)

# ─────────────────────────────
# 2️⃣ MANUAL SVD
# ─────────────────────────────
print("\n" + "="*50)
print("MANUAL SVD")
print("="*50)

XtX = X.T @ X
eigvals_svd, V = np.linalg.eig(XtX)

singular_vals = np.sqrt(eigvals_svd)

idx = np.argsort(singular_vals)[::-1]
singular_vals = singular_vals[idx]
V = V[:, idx]

U = X @ V @ np.linalg.inv(np.diag(singular_vals))

X_svd_manual = U @ np.diag(singular_vals)

var_ratio_svd_manual = (singular_vals**2) / np.sum(singular_vals**2)

print("\nSingular Values:", singular_vals)
print("Variance Ratio (SVD):", var_ratio_svd_manual)

# ─────────────────────────────
# 3️⃣ SKLEARN PCA
# ─────────────────────────────
print("\n" + "="*50)
print("SKLEARN PCA")
print("="*50)

pca = PCA(n_components=2)
X_pca_sklearn = pca.fit_transform(X)

print("\nExplained Variance:", pca.explained_variance_)
print("Variance Ratio:", pca.explained_variance_ratio_)

# ─────────────────────────────
# 4️⃣ SKLEARN SVD
# ─────────────────────────────
print("\n" + "="*50)
print("SKLEARN SVD")
print("="*50)

svd = TruncatedSVD(n_components=2)
X_svd_sklearn = svd.fit_transform(X)

print("\nSingular Values:", svd.singular_values_)
print("Variance Ratio:", svd.explained_variance_ratio_)

# ─────────────────────────────
# 🔍 COMPARISONS
# ─────────────────────────────
print("\n" + "="*70)
print("COMPARISON RESULTS")
print("="*70)

# 1. Manual PCA vs Manual SVD
print("\n1️⃣ Manual PCA vs Manual SVD:")
print("Variance ratio close?",
      np.allclose(var_ratio_pca_manual, var_ratio_svd_manual))

# 2. Manual SVD vs Sklearn PCA
print("\n2️⃣ Manual SVD vs Sklearn PCA:")
print("Variance ratio close?",
      np.allclose(var_ratio_svd_manual, pca.explained_variance_ratio_))

# 3. Sklearn SVD vs Sklearn PCA
print("\n3️⃣ Sklearn SVD vs Sklearn PCA:")
print("Variance ratio close?",
      np.allclose(svd.explained_variance_ratio_,
                  pca.explained_variance_ratio_))

# 4. Manual PCA vs Sklearn PCA
print("\n4️⃣ Manual PCA vs Sklearn PCA:")
print("Variance ratio close?",
      np.allclose(var_ratio_pca_manual,
                  pca.explained_variance_ratio_))

# ─────────────────────────────
# 📊 VISUALIZATION
# ─────────────────────────────
plt.figure(figsize=(16,8))

# Original
plt.subplot(2,2,1)
plt.scatter(X[:,0], X[:,1])
plt.title("Original Data")
plt.xlabel("X")
plt.ylabel("Y")
plt.legend(["Original"])
plt.grid()

# Manual PCA
plt.subplot(2,2,2)
plt.scatter(X_pca_manual[:,0], X_pca_manual[:,1])
plt.title("Manual PCA")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.legend(["Manual"])
plt.grid()

# Sklearn PCA
plt.subplot(2,2,3)
plt.scatter(X_pca_sklearn[:,0], X_pca_sklearn[:,1])
plt.title("Sklearn PCA")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.legend(["Sklearn"])
plt.grid()

# Sklearn SVD
plt.subplot(2,2,4)
plt.scatter(X_svd_sklearn[:,0], X_svd_sklearn[:,1])
plt.title("Sklearn SVD")
plt.xlabel("Dim1")
plt.ylabel("Dim2")
plt.legend(["Sklearn"])
plt.grid()

plt.tight_layout()
plt.show()

# ─────────────────────────────
# 📊 VARIANCE COMPARISON
# ─────────────────────────────
plt.figure()

labels = ["Comp1", "Comp2"]
x = np.arange(len(labels))

plt.bar(x - 0.3, var_ratio_pca_manual, width=0.2)
plt.bar(x - 0.1, var_ratio_svd_manual, width=0.2)
plt.bar(x + 0.1, pca.explained_variance_ratio_, width=0.2)
plt.bar(x + 0.3, svd.explained_variance_ratio_, width=0.2)

plt.xticks(x, labels)
plt.title("Variance Ratio Comparison (All Methods)")
plt.xlabel("Components")
plt.ylabel("Variance Ratio")
plt.legend(["Manual PCA", "Manual SVD", "Sklearn PCA", "Sklearn SVD"])

plt.show()