from __future__ import annotations
from pyvelora.core import vector
from pyvelora.core.array_base import ArrayBase
from pyvelora.core.vector import Vector
import numpy as np

class Matrix(ArrayBase):
    def __init__(self, data):
        super().__init__(data)
    
    def transpose(self):
        """Return the transpose of the matrix."""
        return Matrix(self.data.T)
    
    def determinant(self):
        """Return the determinant of the matrix."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Determinant is only defined for square matrices")
        return np.linalg.det(self.data)
    
    def inverse(self):
        """Return the inverse of the matrix."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Inverse is only defined for square matrices")
        return Matrix(np.linalg.inv(self.data))
    
    def trace(self):
        """Return the trace of the matrix."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Trace is only defined for square matrices")
        return np.trace(self.data)
    
    def eigenvalues(self):
        """Return the eigenvalues of the matrix."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Eigenvalues are only defined for square matrices")
        return np.linalg.eigh(self.data)[0]
    
    def eigenvectors(self):
        """Return the eigenvectors of the matrix."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Eigenvectors are only defined for square matrices")
        vectors = np.linalg.eigh(self.data)[1]
        return Matrix(vectors)
    
    def solve(self, b):
        """Solve the linear system Ax = b for x."""
        if self.shape[0] != self.shape[1]:
            raise ValueError("Solving is only defined for square matrices")
        return Vector(np.linalg.solve(self.data, b.data))

if __name__ == "__main__":
    m = Matrix([[0, 1], [0.1, 3]])
    print("Matrix:\n", m.data)
    print("Determinant:", m.determinant())
    print("Inverse:\n", m.inverse().data)
    print("Trace:", m.trace())
    print("Eigenvalues:", m.eigenvalues())
    print("Eigenvectors:\n", m.eigenvectors().data)
