"""Hurst exponent (R/S analysis) — faithful port of Rust ml-features/price_features.rs
`compute_hurst_exponent`. Close-only, so it fits fxhnt's PriceSeries directly.

H > 0.5 → persistent (trending); H < 0.5 → anti-persistent (mean-reverting); H ≈ 0.5 → random walk.
This is the principled persistence signal that distinguishes a trending regime from a ranging one — the
core of the foxhunt Hurst/ADX classifier (the ADX leg needs high/low bars, a future extension)."""
from __future__ import annotations

import math

import numpy as np


def hurst_exponent(close: np.ndarray, period: int = 20) -> float:
    """R/S Hurst over the last `period` closes: H = ln(R/S)/ln(n). Returns 0.5 (random walk) on degenerate
    input (too few points, period<10, or zero range/std). Clamped to [0, 1]."""
    c = np.asarray(close, float)
    if c.size < period or period < 10:
        return 0.5
    prices = c[-period:]
    rets = np.diff(np.log(prices))                 # log returns (len period-1)
    if rets.size == 0:
        return 0.5
    mean = float(rets.mean())
    cumulative = np.concatenate([[0.0], np.cumsum(rets - mean)])
    rng = float(cumulative.max() - cumulative.min())
    std = float(rets.std())                         # population std (ddof=0), matching the Rust
    if std < 1e-8 or rng < 1e-8:
        return 0.5
    n = float(rets.size)
    h = math.log(rng / std) / math.log(n) if n > 1.5 else 0.5
    return min(1.0, max(0.0, h))
