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

print("="*60)
print("SVD REPORT — MANUAL vs SKLEARN")
print("="*60)

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

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

# ─────────────────────────────
# STEP 1: COMPUTE XᵀX
# ─────────────────────────────
XtX = X.T @ X
print("\nStep 1: X^T X Matrix:")
print(XtX)

# ─────────────────────────────
# STEP 2: EIGEN DECOMPOSITION
# ─────────────────────────────
eigenvalues, V = np.linalg.eig(XtX)

print("\nStep 2: Eigenvalues of X^T X:")
print(eigenvalues)

print("\nStep 2: Right Singular Vectors (V):")
print(V)

# ─────────────────────────────
# STEP 3: SINGULAR VALUES
# ─────────────────────────────
singular_values = np.sqrt(eigenvalues)

print("\nStep 3: Singular Values (σ):")
print(singular_values)

# Sort descending
idx = np.argsort(singular_values)[::-1]
singular_values = singular_values[idx]
V = V[:, idx]

# ─────────────────────────────
# STEP 4: COMPUTE U
# ─────────────────────────────
U = X @ V

# Normalize U
U = U / singular_values

print("\nStep 4: Left Singular Vectors (U):")
print(U)

# ─────────────────────────────
# STEP 5: Σ MATRIX
# ─────────────────────────────
Sigma = np.diag(singular_values)

print("\nStep 5: Sigma Matrix:")
print(Sigma)

# ─────────────────────────────
# STEP 6: RECONSTRUCTION
# ─────────────────────────────
X_reconstructed = U @ Sigma @ V.T

print("\nStep 6: Reconstructed Matrix (UΣVᵀ):")
print(X_reconstructed)

# ─────────────────────────────
# VARIANCE EXPLAINED (MANUAL)
# ─────────────────────────────
var_explained = singular_values**2
var_ratio_manual = var_explained / np.sum(var_explained)

print("\nManual Variance Explained:")
print(var_explained)

print("\nManual Variance Ratio:")
print(var_ratio_manual)

# ─────────────────────────────
# SKLEARN SVD
# ─────────────────────────────
svd = TruncatedSVD(n_components=2)
X_svd_sklearn = svd.fit_transform(X)

print("\n" + "="*60)
print("SKLEARN SVD RESULTS")
print("="*60)

print("\nTransformed Data:")
print(X_svd_sklearn)

print("\nSingular Values:")
print(svd.singular_values_)

print("\nExplained Variance:")
print(svd.explained_variance_)

print("\nExplained Variance Ratio:")
print(svd.explained_variance_ratio_)

print("\nComponents (V^T):")
print(svd.components_)

# ─────────────────────────────
# COMPARISON CHECK
# ─────────────────────────────
print("\n" + "="*60)
print("COMPARISON CHECK")
print("="*60)

print("Manual vs Sklearn (singular values close?):")
print(np.allclose(singular_values, svd.singular_values_))

# ─────────────────────────────
# VISUALIZATION
# ─────────────────────────────
plt.figure(figsize=(15,5))

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

# Manual Projection (U * Σ)
X_manual_proj = U[:, :2] * singular_values[:2]

plt.subplot(1,3,2)
plt.scatter(X_manual_proj[:,0], X_manual_proj[:,1])
plt.title("Manual SVD Projection")
plt.xlabel("Dim1")
plt.ylabel("Dim2")
plt.grid()

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

plt.tight_layout()
plt.show()

# ─────────────────────────────
# VARIANCE BAR PLOT
# ─────────────────────────────
plt.figure()

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

plt.bar(x - 0.15, var_ratio_manual, width=0.3)
plt.bar(x + 0.15, svd.explained_variance_ratio_, width=0.3)

plt.xticks(x, labels)
plt.title("Variance Ratio (Manual vs Sklearn SVD)")
plt.xlabel("Components")
plt.ylabel("Variance Ratio")

plt.show()