# -*- coding: utf-8 -*-
"""全Track共通の評価指標。BytePlus公表値と同一定義で比較可能にする。"""
import numpy as np

def grade(score):
    """A:80-100 / B:60-79 / C:21-59 / D:0-20 (BytePlus定義)"""
    s = np.asarray(score)
    g = np.full(s.shape, 'D', dtype='<U1')
    g[s >= 21] = 'C'; g[s >= 60] = 'B'; g[s >= 80] = 'A'
    return g

def spearman(y_true, y_pred):
    """スピアマン順位相関 ρ（BytePlus主指標）。SciPy非依存の実装。"""
    yt = _rank(np.asarray(y_true, float)); yp = _rank(np.asarray(y_pred, float))
    return float(np.corrcoef(yt, yp)[0, 1])

def _rank(x):
    order = x.argsort(); ranks = np.empty_like(order, float); ranks[order] = np.arange(len(x))
    # 同順位は平均ランクに
    _, inv, cnt = np.unique(x, return_inverse=True, return_counts=True)
    avg = np.zeros(len(cnt)); pos = 0
    srt = np.sort(x)
    for i, c in enumerate(cnt):
        avg[i] = np.arange(pos, pos + c).mean(); pos += c
    return avg[inv]

def evaluate(y_true, y_pred, anomaly_true=None, anomaly_pred=None):
    """主要指標をまとめて返す。"""
    yt = np.asarray(y_true, float); yp = np.asarray(y_pred, float)
    out = {
        "n": int(len(yt)),
        "rho": round(spearman(yt, yp), 3),
        "mae": round(float(np.abs(yt - yp).mean()), 2),
        "grade_match": round(float((grade(yt) == grade(yp)).mean()), 3),
    }
    # 異常検知recall: 真のC+D(=score<60)を拾えた率
    at = (yt < 60) if anomaly_true is None else np.asarray(anomaly_true, bool)
    ap = (yp < 60) if anomaly_pred is None else np.asarray(anomaly_pred, bool)
    if at.sum() > 0:
        out["anomaly_recall"] = round(float((ap & at).sum() / at.sum()), 3)
        out["anomaly_n"] = int(at.sum())
    return out

if __name__ == "__main__":
    import numpy as np
    yt = np.array([100, 80, 60, 40, 20, 90, 70])
    yp = np.array([ 92, 78, 55, 50, 25, 88, 60])
    print(evaluate(yt, yp))
