# -*- coding: utf-8 -*-

import numpy as np


class ComplexWishart(object):
    def __init__(self):
        self._C = None

    @property
    def C(self):
        return self._C

    @C.setter
    def C(self, value):
        self._C = value
        self.recompute_constants()

    def recompute_constants(self):
        self._inv_C = np.linalg.inv(self._C)
        self._log_det_C = np.log(np.linalg.det(self._C))
        # here, we could store inv(C.conj().T), i.e. the inverse of the
        # conjugate transposed, and use the identity trace(A.T*B) = <vec(A),
        # vec(B)>, where vec(x) is the vectorized version of the matrix x

    def likelihood(self, Z):
        return np.exp(self._log_likelihood(Z))

    def log_likelihood(self, Z):
        _, log_det_Z = np.linalg.slogdet(Z)
        return (self._n - self._d) * log_det_Z - np.trace(np.dot(self._inv_C, Z)) - self._log_K

    def score(self, Z):
        sc = self._log_det_C + np.trace(np.dot(self._inv_C, Z))
        # if 1.0 - np.abs(np.real(sc)) / np.absolute(sc) > 0.001:
        #     import ipdb; ipdb.set_trace()
        return max(-1e4, -np.real(sc))

    def _log_R(self):
        logR = (self._d * (self._d - 1) / 2.) * np.log(np.pi)
        for j in range(1, self._d + 1):
            logR += gamma(self._n - j + 1)
        return logR

    def fit(self, X):
        self.C = np.sum(X, axis=0) / float(len(X))
        return self
