# Path: sdcit/utils.py
# def p_value_of(val: float, data: typing.Iterable) -> float:
#     """The percentile of a value given a data"""
# 
#     data = np.sort(data)
#     return float(1 - np.searchsorted(data, val, side='right') / len(data))
# 
# def cythonize(*matrices):
#     return tuple(np.ascontiguousarray(matrix, dtype=np.float64) for matrix in matrices)
# 
# def random_seeds(n=None):
#     """Random seeds of given size or a random seed if n is None"""
#     if n is None:
#         return np.random.randint(np.iinfo(np.int32).max)
#     else:
#         return [np.random.randint(np.iinfo(np.int32).max) for _ in range(n)]
# 
# def centering(M: np.ndarray) -> Union[None, np.ndarray]:
#     """Matrix Centering"""
#     if M is None:
#         return None
#     n = len(M)
#     H = np.eye(n) - 1 / n
#     return H @ M @ H


import numpy as np
import scipy.stats
from typing import List, Tuple
from sdcit.cython_impl.cy_sdcit import cy_hsic
from sdcit.utils import p_value_of, cythonize, random_seeds, centering
    muY = 1 / m / (m - 1) * sum_except_diag(L)
    mHSIC = 1 / m * (1 + muX * muY - muX - muY)
    varHSIC = 72 * (m - 4) * (m - 5) / m / (m - 1) / (m - 2) / (m - 3) * (1 / m / (m - 1) * (sum_except_diag((1 / 6 * Kc * Lc) ** 2)))

    al = mHSIC ** 2 / varHSIC
    bet = varHSIC * m / mHSIC
    return scipy.stats.gamma.sf(test_stat, al, scale=bet)


def HSIC_stat(K: np.ndarray, L: np.ndarray) -> float:
    """HSIC statistic assuming given two centered kernel matrices.

    References
    ----------
    Gretton, A., Herbrich, R., Smola, A., Bousquet, O., & Schölkopf, B. (2005). Kernel Methods for Measuring Independence. Journal of Machine Learning Research, 6, 2075–2129.
    """
    m = len(K)
    return float(1 / m * np.sum(K * L))


def HSIC_boot(K: np.ndarray, L: np.ndarray, num_boot=1000, seed=None) -> Tuple[float, List[float]]:
    """A Hilbert-Schmidt Independence Criterion where null distribution is based on bootstrapping

    References
    ----------
    Gretton, A., Herbrich, R., Smola, A., Bousquet, O., & Schölkopf, B. (2005). Kernel Methods for Measuring Independence. Journal of Machine Learning Research, 6, 2075–2129.
    """
    if seed is not None:
        np.random.seed(seed)


# Path: sdcit/utils.py
# def p_value_of(val: float, data: typing.Iterable) -> float:
#     """The percentile of a value given a data"""
# 
#     data = np.sort(data)
#     return float(1 - np.searchsorted(data, val, side='right') / len(data))
# 
# def cythonize(*matrices):
#     return tuple(np.ascontiguousarray(matrix, dtype=np.float64) for matrix in matrices)
# 
# def random_seeds(n=None):
#     """Random seeds of given size or a random seed if n is None"""
#     if n is None:
#         return np.random.randint(np.iinfo(np.int32).max)
#     else:
#         return [np.random.randint(np.iinfo(np.int32).max) for _ in range(n)]
# 
# def centering(M: np.ndarray) -> Union[None, np.ndarray]:
#     """Matrix Centering"""
#     if M is None:
#         return None
#     n = len(M)
#     H = np.eye(n) - 1 / n
#     return H @ M @ H


# Path: sdcit/hsic.py
import numpy as np
import scipy.stats
from typing import List, Tuple
from sdcit.cython_impl.cy_sdcit import cy_hsic
from sdcit.utils import p_value_of, cythonize, random_seeds, centering

    muY = 1 / m / (m - 1) * sum_except_diag(L)
    mHSIC = 1 / m * (1 + muX * muY - muX - muY)
    varHSIC = 72 * (m - 4) * (m - 5) / m / (m - 1) / (m - 2) / (m - 3) * (1 / m / (m - 1) * (sum_except_diag((1 / 6 * Kc * Lc) ** 2)))

    al = mHSIC ** 2 / varHSIC
    bet = varHSIC * m / mHSIC
    return scipy.stats.gamma.sf(test_stat, al, scale=bet)


def HSIC_stat(K: np.ndarray, L: np.ndarray) -> float:
    """HSIC statistic assuming given two centered kernel matrices.

    References
    ----------
    Gretton, A., Herbrich, R., Smola, A., Bousquet, O., & Schölkopf, B. (2005). Kernel Methods for Measuring Independence. Journal of Machine Learning Research, 6, 2075–2129.
    """
    m = len(K)
    return float(1 / m * np.sum(K * L))


def HSIC_boot(K: np.ndarray, L: np.ndarray, num_boot=1000, seed=None) -> Tuple[float, List[float]]:
    """A Hilbert-Schmidt Independence Criterion where null distribution is based on bootstrapping

    References
    ----------
    Gretton, A., Herbrich, R., Smola, A., Bousquet, O., & Schölkopf, B. (2005). Kernel Methods for Measuring Independence. Journal of Machine Learning Research, 6, 2075–2129.
    """
    if seed is not None:
        np.random.seed(seed)


    Kc, Lc = centering(K), centering(L)