"""linear ドメイン正規化モジュール．

シミュの生 luminance 配列に対して，mean を指定値（target_mean）に揃える
線形ゲインを適用する．

線形ゲインは形状指標（CoV / 勾配 / C/P 比 / max-min 比）に厳密に不変であり，
絶対値スケールだけを合わせる操作である．これにより複数設計のシミュ結果を
linear ドメインで公平比較できる．

OETF 順方向適用（oetf.py の露出アンカー）と同じ思想を linear モードに
対称に展開したものであり，OETF との併用は禁止する（EvaluationConfig の
model_validator で検証する）．
"""

from __future__ import annotations

import numpy


def apply_mean_anchor(
    values: numpy.ndarray,
    target_mean: float,
) -> tuple[numpy.ndarray, float]:
    """入力配列の mean を target_mean に揃える線形ゲインを適用する．

    $\\alpha = \\text{target\\_mean} / \\text{values.mean()}$
    として，``values * alpha`` を返す．

    CoV = std / mean，max-min 比，勾配，C/P 比などの形状指標はスカラー倍で
    不変なので，絶対値スケールだけが揃う（線形ゲインの性質）．

    Args:
        values: 入力 ROI 配列（任意形状・任意次元，float64 推奨）．
        target_mean: 目標 mean 値（uint8 スケール [0, 255] 想定だが任意 float 可）．

    Returns:
        (normalized, alpha) のタプル．
        normalized: ``values * alpha`` の配列（dtype は values と同じ）．
        alpha: 適用した線形ゲイン（target_mean / values.mean()）．

    Raises:
        ValueError: values.mean() が 0 の場合（ゼロ除算を防ぐ）．
    """
    src_mean = float(numpy.mean(values))
    if src_mean == 0.0:
        msg = "values の mean が 0 のため，mean anchor 正規化を適用できません．"
        raise ValueError(msg)
    alpha = target_mean / src_mean
    return values * alpha, alpha
