# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from .optim_complexity_k import _complexity_k_slope, complexity_k
[docs]
def fractal_higuchi(signal, k_max="default", show=False, **kwargs):
    """**Higuchi's Fractal Dimension (HFD)**
    The Higuchi's Fractal Dimension (HFD) is an approximate value for the box-counting dimension for
    time series. It is computed by reconstructing k-max number of new data sets. For each
    reconstructed data set, curve length is computed and plotted against its corresponding
    *k*-value on a log-log scale. HFD corresponds to the slope of the least-squares linear trend.
    Values should fall between 1 and 2. For more information about the *k* parameter selection, see
    the :func:`complexity_k` optimization function.
    Parameters
    ----------
    signal : Union[list, np.array, pd.Series]
        The signal (i.e., a time series) in the form of a vector of values.
    k_max : str or int
        Maximum number of interval times (should be greater than or equal to 2).
        If ``"default"``, the optimal k-max is estimated using :func:`complexity_k`, which is slow.
    show : bool
        Visualise the slope of the curve for the selected k_max value.
    **kwargs : optional
        Currently not used.
    Returns
    ----------
    HFD : float
        Higuchi's fractal dimension of the time series.
    info : dict
        A dictionary containing additional information regarding the parameters used
        to compute Higuchi's fractal dimension.
    See Also
    --------
    complexity_k
    Examples
    ----------
    .. ipython:: python
      import neurokit2 as nk
      signal = nk.signal_simulate(duration=1, sampling_rate=100, frequency=[3, 6], noise = 0.2)
      @savefig p_fractal_higuchi1.png scale=100%
      k_max, info =  nk.complexity_k(signal, k_max='default', show=True)
      @suppress
      plt.close()
      @savefig p_fractal_higuchi2.png scale=100%
      hfd, info = nk.fractal_higuchi(signal, k_max=k_max, show=True)
      @suppress
      plt.close()
    .. ipython:: python
      hfd
    References
    ----------
    * Higuchi, T. (1988). Approach to an irregular time series on the basis of the fractal theory.
      Physica D: Nonlinear Phenomena, 31(2), 277-283.
    * Vega, C. F., & Noel, J. (2015, June). Parameters analyzed of Higuchi's fractal dimension for
      EEG brain signals. In 2015 Signal Processing Symposium (SPSympo) (pp. 1-5). IEEE.
      https://ieeexplore.ieee.org/document/7168285
    """
    # Sanity checks
    if isinstance(signal, (np.ndarray, pd.DataFrame)) and signal.ndim > 1:
        raise ValueError(
            "Multidimensional inputs (e.g., matrices or multichannel data) are not supported yet."
        )
    # Get k_max
    if isinstance(k_max, (str, list, np.ndarray, pd.Series)):
        # Optimizing needed
        k_max, info = complexity_k(signal, k_max=k_max, show=False)
        idx = np.where(info["Values"] == k_max)[0][0]
        slope = info["Scores"][idx]
        intercept = info["Intercepts"][idx]
        average_values = info["Average_Values"][idx]
        k_values = np.arange(1, k_max + 1)
    else:
        # Compute Higuchi
        slope, intercept, info = _complexity_k_slope(k_max, signal)
        k_values = info["k_values"]
        average_values = info["average_values"]
    # Plot
    if show:
        _fractal_higuchi_plot(k_values, average_values, k_max, slope, intercept)
    return slope, {
        "k_max": k_max,
        "Values": k_values,
        "Scores": average_values,
        "Intercept": intercept,
    } 
# =============================================================================
# Utilities
# =============================================================================
def _fractal_higuchi_plot(k_values, average_values, kmax, slope, intercept, ax=None):
    if ax is None:
        fig, ax = plt.subplots()
        fig.suptitle("Higuchi Fractal Dimension (HFD)")
    else:
        fig = None
    ax.set_title(
        "Least-squares linear best-fit curve for $k_{max}$ = "
        + str(kmax)
        + ", slope = "
        + str(np.round(slope, 2))
    )
    ax.set_ylabel(r"$ln$(L(k))")
    ax.set_xlabel(r"$ln$(1/k)")
    colors = plt.cm.plasma(np.linspace(0, 1, len(k_values)))
    # Label all values unless len(k_values) > 10 then label only min and max k_max
    if len(k_values) < 10:
        for i in range(0, len(k_values)):
            ax.scatter(
                -np.log(k_values[i]),
                np.log(average_values[i]),
                color=colors[i],
                marker="o",
                zorder=2,
                label="k = {}".format(i + 1),
            )
    else:
        for i in range(0, len(k_values)):
            ax.scatter(
                -np.log(k_values[i]),
                np.log(average_values[i]),
                color=colors[i],
                marker="o",
                zorder=2,
                label="_no_legend_",
            )
        ax.plot([], label="k = {}".format(np.min(k_values)), c=colors[0])
        ax.plot([], label="k = {}".format(np.max(k_values)), c=colors[-1])
    fit_values = [slope * i + -intercept for i in -np.log(k_values)]
    ax.plot(-np.log(k_values), fit_values, color="#FF9800", zorder=1)
    ax.legend(loc="lower right")
    return fig