# Path: distributions/dbg/random.py
# def score_student_t(x, nu, mu, sigma):
#     """
#     multivariate score_student_t
# 
#     \cite{murphy2007conjugate}, Eq. 313
#     """
#     p = len(mu)
#     z = x - mu
#     S = inner(inner(z, inv(sigma)), z)
#     score = (
#         gammaln(0.5 * (nu + p))
#         - gammaln(0.5 * nu)
#         - 0.5 * (
#             p * log(nu * pi)
#             + log(det(sigma))
#             + (nu + p) * log(1 + S / nu)
#         )
#     )
#     return score
# 
# def sample_normal_inverse_wishart(mu0, lambda0, psi0, nu0):
#     D, = mu0.shape
#     assert psi0.shape == (D, D)
#     assert lambda0 > 0.0
#     assert nu0 > D - 1
#     cov = sample_inverse_wishart(nu0, psi0)
#     mu = np.random.multivariate_normal(mean=mu0, cov=(1. / lambda0) * cov)
#     return mu, cov


import numpy as np
import math
from scipy.special import multigammaln
from distributions.dbg.random import (
    score_student_t,
    sample_normal_inverse_wishart,
)
from distributions.mixins import SharedMixin, GroupIoMixin, SharedIoMixin
        self.sum_xxT = np.zeros((shared.dim(), shared.dim()))

    def add_value(self, shared, value):
        self.count += 1
        self.sum_x += value
        self.sum_xxT += np.outer(value, value)

    def add_repeated_value(self, shared, value, count):
        self.count += count
        self.sum_x += (count * value)
        self.sum_xxT += (count * np.outer(value, value))

    def remove_value(self, shared, value):
        self.count -= 1
        self.sum_x -= value
        self.sum_xxT -= np.outer(value, value)

    def merge(self, shared, source):
        self.count += source.count
        self.sum_x += source.sum_x
        self.sum_xxT += source.sum_xxT

    def score_value(self, shared, value):
        """
        \cite{murphy2007conjugate}, Eq. 258
        """
        post = shared.plus_group(self)
        mu_n, kappa_n, psi_n, nu_n = post.mu, post.kappa, post.psi, post.nu
        dof = nu_n - shared.dim() + 1.
        sigma_n = psi_n * (kappa_n + 1.) / (kappa_n * dof)

# Path: distributions/dbg/random.py
# def score_student_t(x, nu, mu, sigma):
#     """
#     multivariate score_student_t
# 
#     \cite{murphy2007conjugate}, Eq. 313
#     """
#     p = len(mu)
#     z = x - mu
#     S = inner(inner(z, inv(sigma)), z)
#     score = (
#         gammaln(0.5 * (nu + p))
#         - gammaln(0.5 * nu)
#         - 0.5 * (
#             p * log(nu * pi)
#             + log(det(sigma))
#             + (nu + p) * log(1 + S / nu)
#         )
#     )
#     return score
# 
# def sample_normal_inverse_wishart(mu0, lambda0, psi0, nu0):
#     D, = mu0.shape
#     assert psi0.shape == (D, D)
#     assert lambda0 > 0.0
#     assert nu0 > D - 1
#     cov = sample_inverse_wishart(nu0, psi0)
#     mu = np.random.multivariate_normal(mean=mu0, cov=(1. / lambda0) * cov)
#     return mu, cov


# Path: distributions/dbg/models/niw.py
import numpy as np
import math
from scipy.special import multigammaln
from distributions.dbg.random import (
    score_student_t,
    sample_normal_inverse_wishart,
)
from distributions.mixins import SharedMixin, GroupIoMixin, SharedIoMixin

        self.sum_xxT = np.zeros((shared.dim(), shared.dim()))

    def add_value(self, shared, value):
        self.count += 1
        self.sum_x += value
        self.sum_xxT += np.outer(value, value)

    def add_repeated_value(self, shared, value, count):
        self.count += count
        self.sum_x += (count * value)
        self.sum_xxT += (count * np.outer(value, value))

    def remove_value(self, shared, value):
        self.count -= 1
        self.sum_x -= value
        self.sum_xxT -= np.outer(value, value)

    def merge(self, shared, source):
        self.count += source.count
        self.sum_x += source.sum_x
        self.sum_xxT += source.sum_xxT

    def score_value(self, shared, value):
        """
        \cite{murphy2007conjugate}, Eq. 258
        """
        post = shared.plus_group(self)
        mu_n, kappa_n, psi_n, nu_n = post.mu, post.kappa, post.psi, post.nu
        dof = nu_n - shared.dim() + 1.
        sigma_n = psi_n * (kappa_n + 1.) / (kappa_n * dof)

        return score_student_t(value, dof, mu_n, sigma_n)