"""Beweist, dass die bisect-Fassung des Pivot-Fensters BITGENAU dieselbe Menge
liefert wie die frühere lineare Filterung.

HINTERGRUND (2026-08-07): das Profil zeigte, dass **8,17 s von 10,07 s**
Gesamtlaufzeit in der Eigenzeit der Backtest-Schleife steckten — nicht in
`_build` (0,44 s). Ursache war

    [C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K]

je Bar über die KOMPLETTE Pivot-Liste, also O(Bars × Pivots). Bei 80k Bars sind
das hunderte Millionen Vergleiche — daher die 9–20-Minuten-Läufe.

⚠ Die Migrations-Regel des Projekts lautet: ein Backtest darf nur umgestellt
werden, wenn er danach DIESELBEN Zahlen liefert — sonst ist es keine
Refaktorierung, sondern eine unbemerkte Änderung der Messgrundlage. Weil ein
Vorher/Nachher-Vergleich zweier LÄUFE hier nicht taugt (`copy_rates_from_pos`
ankert am neuesten Bar, zwischenzeitlich eintreffende Live-Bars verschieben das
Fenster — real am 06.08. ein Trade Unterschied), wird die Gleichheit hier
DIREKT auf der Datenstruktur geprüft.
"""
import random
from bisect import bisect_left, bisect_right

from backtest_auto_signal_v3 import _PIV_K, _PIV_LOOK


def _alt(PH, C, i):
    """Die frühere, lineare Fassung."""
    return [C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K]


def _neu(PH, C, i):
    """Die bisect-Fassung, wie sie jetzt in `lauf()` steht."""
    a = bisect_left(PH, i - _PIV_LOOK)
    b = bisect_right(PH, i - _PIV_K)
    return [C[j] for j in PH[a:b]]


def test_fenster_ist_identisch():
    """Über eine realistisch geformte Pivot-Liste und viele Bar-Positionen."""
    rnd = random.Random(42)
    n = 4000
    C = [70 + rnd.gauss(0, 2) for _ in range(n)]
    # Pivots wie im Backtest: aufsteigend, unregelmäßig verteilt
    PH = sorted(rnd.sample(range(_PIV_K, n - _PIV_K), 400))

    for i in range(_PIV_LOOK, n, 7):
        assert _alt(PH, C, i) == _neu(PH, C, i), f"Abweichung bei i={i}"


def test_randfaelle():
    """Die Stellen, an denen eine Fensterlogik typischerweise bricht."""
    C = list(range(1000))
    PH = [10, 20, 30, 300, 305, 310, 700]

    for i in (0, _PIV_K, _PIV_LOOK, _PIV_LOOK + _PIV_K, 313, 999):
        assert _alt(PH, C, i) == _neu(PH, C, i), f"Abweichung bei i={i}"


def test_leere_pivotliste():
    C = list(range(100))
    assert _alt([], C, 50) == _neu([], C, 50) == []


def test_grenzen_sind_inklusiv():
    """`<=` auf BEIDEN Seiten — bisect_left/bisect_right bilden genau das ab.
    Ein `bisect_left` auf der oberen Grenze würde das Randelement verlieren."""
    C = list(range(1000))
    i = 500
    PH = [i - _PIV_LOOK, i - _PIV_LOOK + 1, i - _PIV_K - 1, i - _PIV_K]
    assert _neu(PH, C, i) == [C[j] for j in PH]      # alle vier drin
    assert _alt(PH, C, i) == _neu(PH, C, i)
