# Erweiterte Anleitung: Hinzufügen einer neuen Testfunktion (mit Templates, Properties-Liste und strengen Regeln)

Diese Anleitung basiert auf der ursprünglichen Version und ist verbessert durch Integration von maschinenlesbaren Regeln (RFC-2119-Keywords: MUST, SHOULD, MAY), Tags für Suchbarkeit, thematische Gruppierung, Beispiele und Automatisierungs-Hinweise. Redundanz ist beabsichtigt für Klarheit. Die Regeln fördern Konsistenz, reduzieren Fehler und machen die Anleitung KI-freundlich. Wichtige Regeln werden wiederholt. Neu: Ergänzungen zu häufigen Syntax-Pitfalls in Meta-Funktionen und Tests, basierend auf realen Fehlern (z.B. Signatur-Mismatches bei :min_value und Vektor-Vergleichen). [NEW] Für skalierbare Funktionen: Integriere :default_n als Meta-Feld, um eine Standarddimension für Tests und Dokumentation zu definieren. [NEW] Noise-Handling: Für Funktionen mit "has_noise" (z.B. Quartic), passe Validierungen an, um stochastische Effekte zu berücksichtigen – exakte Gleichheit ist unmöglich; verwende Range-Checks statt atol. [NEW] [RULE_SOURCE_FORMULA_CONSISTENCY] MUST: Die Quellen für :properties MÜSSEN die exakt gleiche Funktionsformel verwenden wie die implementierte Version (z.B. gleiche Reihenfolge der trigonometrischen Funktionen, exakte Denominator-Struktur). Wenn keine exakte Übereinstimmung verfügbar, adaptiere Properties nur aus der nächstverwandten Quelle und notiere die Abweichung explizit in :description (z.B. "Properties adapted from [Quelle] for variant without absolute value"). Suche bei Bedarf weitere Quellen, die die präzise Formel abdecken, aber priorisiere Konsistenz über Vollständigkeit.

## Grundprinzipien
- Verwende RFC-2119-Keywords: MUST (verpflichtend), SHOULD (empfohlen), MAY (optional).
- Suche nach Tags wie [RULE_SIGNATURE] für schnelle Referenz.
- Automatisierte Checks: Führe vor jedem Commit CI-Skripte aus (z.B. Syntax-Check mit julia --project=. -e 'include("src/functions/<name>.jl")' + Namens-Check: julia --project=. -e 'tf = <UPPER>_FUNCTION; @test tf.meta[:name] == basename("src/functions/<name>.jl")[1:end-3]').
- Wiederhole kritische Regeln: z.B. Vermeide globale Konstanten [RULE_NO_CONST_ARRAYS].
- Assume gute Absicht: Keine moralisierenden Kommentare; fokussiere auf Fakten.
- [RULE_META_CONSISTENCY] MUST: Alle Meta-Funktionen (:start, :min_position, :min_value, :lb, :ub) müssen konsistente Signaturen haben. Für skalierbare Funktionen: Wenn der Wert von n unabhängig ist (z.B. konstantes Minimum), verwende (n::Int) -> value (ignoriere n intern). Wenn abhängig (z.B. Position sqrt.(1:n)), verwende (n::Int) -> ... . Teste im REPL: tf.meta[:min_value](2) – muss funktionieren; tf.meta[:min_value]() darf MethodError werfen, da runtests.jl für scalable n aufruft. [NEW] Für konstante Werte in skalierbaren Funktionen (z.B. min_value=0.0 unabhängig von n): Verwende (n::Int) -> value, um MethodError in runtests.jl zu vermeiden (Aufruf mit n bei ()-Signatur).
- [RULE_DEFAULT_N] MUST für skalierbare Funktionen: Setze :default_n => Integer im Meta-Dict. Der Wert MUST >= 2 sein und das kleinste mögliche n > 1 für die Funktion darstellen (z.B. 4 für Funktionen, die in Blöcken von 4 skaliert werden, wie PowellSingular2; 2 für allgemein skalierbare wie Ackley). Verwende es in Tests und Dokumentation (z.B. print_function_properties.jl) als Standard-n für Anzeigen und Validierungen. SHOULD: Validiere im Test, dass tf.f mit n=default_n type-stable ist (@code_warntype tf.f(rand(n))).
- [NEW RULE_NOISE_HANDLING] MUST: Für Funktionen mit "has_noise" in :properties (z.B. Quartic mit uniform [0,1)-Noise): In runtests.jl und funktionsspezifischen Tests Range-Checks verwenden (z.B. f(min_pos) >= 0 && <1) statt f(min_pos) ≈ 0 atol=1e-8. Dokumentiere Noise-Details in :description (z.B. "Additive uniform [0,1) noise"). Gradient bleibt deterministisch (nur f betroffen).

## Recherche und Validierung
Ziel: Sammle genaue Daten zu Minima, Werten, Schranken und Properties aus zuverlässigen Quellen. Vermeide Rundungen.

- [RULE_PROPERTIES_SOURCE] MUST: Notiere :source im Meta-Dict immer als die **direkte Quelle**, aus der die spezifischen Details (Formel, Minimum, Bounds, Properties) tatsächlich übernommen wurden (z.B. "Jamil & Yang (2013, p. 27)" für die Sargan-Funktion). Priorisiere umfassende Quellen wie Jamil & Yang ( https://arxiv.org/pdf/1308.4008 ) für Modality/Separability. Validiere Properties gegen diese Quelle; bei Abweichungen notiere in :description ("Adapted from [Quelle]"). [NEW] Gib immer eine spezifische Stelle an (z.B. "p. 27" oder "Section 3.2"), um die Übernahme nachvollziehbar zu machen. Vermeide mutmaßliche Ursprungsquellen (z.B. Dixon & Szegö (1978) für Sargan) als primäre :source – erwähne sie nur in :description als historischen Kontext. [NEW] [RULE_SOURCE_FORMULA_CONSISTENCY] Wiederholung: Properties-Quellen MÜSSEN die exakt gleiche Formel verwenden; bei Varianten (z.B. mit/ohne Absolutwert) notiere "Adapted for [spezifische Abweichung]" und suche ggf. alternative Quellen.
- SHOULD: Prüfe ill-conditioning mit cond(ForwardDiff.hessian(tf.f, min_pos)) < 1e6; bei hoher Kondition füge "ill-conditioned" zu Properties hinzu. [NEW] Für noisy Funktionen: Ignoriere Noise in Hessian-Check (z.B. setze rand()=0 temporär).
- MAY: Für komplexe Funktionen (z.B. Cola) erstelle Validierungs-Skripte (z.B. build_xy testen).
- [RULE_MIN_VALUE_INDEPENDENT] SHOULD: Für skalierbare Funktionen mit konstantem globalem Minimum (z.B. Qing: immer 0.0), setze :min_value = (n::Int) -> 0.0, auch wenn :min_position (n::Int) -> ... ist (ignoriere n). Beispiel: In Jamil & Yang ( https://arxiv.org/pdf/1308.4008 , p. 29) ist Qing multimodal, aber Minimum konstant – keine n-Abhängigkeit. [NEW] Für noisy konstante Minima (z.B. Quartic: deterministisch 0, plus Noise): Verwende (n::Int) -> 0.0 als deterministischen Wert; validiere in Tests mit Noise-Range.
- [NEW] Für :default_n: Bestimme basierend auf der Funktion (z.B. kleinste n>1, bei der die Funktion definiert ist und Properties gelten; >=2). Validiere: tf.meta[:min_position](default_n) muss funktionieren ohne Error. Für block-skalierbare (z.B. n%4==0): Setze default_n=4.

Quellen-Liste (erweitert):
- Jamil & Yang (2013): https://arxiv.org/abs/1308.4008 (primär für Properties; zitiere immer mit spezifischer Seite, z.B. "Jamil & Yang (2013, p. 27)").
- Al-Roomi (2015): https://www.al-roomi.org/benchmarks/unconstrained.
- Weitere: sfu.ca/~ssurjano, geatbx.com, MVF-Library (Adorio & Diliman, 2005).

Häufige Fehler (konsolidiert mit Beispielen):
- Ungenaue Minima: Berechne exakt via REPL (z.B. tf.f(min_pos) ≈ min_value atol=1e-8). [NEW] Für Noise: Verwende multiple Runs und Mittelwert; vermeide atol in runtests.jl.
- Namenskonflikte: Vermeide T als Konstante (kollidiert mit Typ T).
- Globale Constants: [RULE_NO_CONST_ARRAYS] MUST – Keine globalen const-Arrays; lokale let-Blöcke verwenden. Beispiel: Bei multi-include (devilliersglasser1/2) überschreiben sich Werte.
- [NEW] Meta-Signatur-Mismatch: Falsche Signatur für :min_value in skalierbaren Funktionen führt zu MethodError (z.B. Aufruf mit n bei ()-Signatur). Lösung: Überprüfe mit REPL: julia> using NonlinearOptimizationTestFunctions; tf = QING_FUNCTION; tf.meta[:min_value](2).
- [NEW] Test-Syntax-Fehler: all(vec .≈ 0, atol=1e-8) schlägt fehl, da all() kein atol akzeptiert. Verwende all(isapprox.(vec, 0, atol=1e-8)).
- [NEW] Noise-Validation-Fehler: In runtests.jl schlägt f(min_pos) ≈ 0 atol=1e-6 fehl durch rand() (z.B. Quartic). Lösung: Füge if "has_noise" in properties: @test f_val >=0 && f_val <1 (für [0,1)-Noise).
- [NEW] Statische Fehlermeldungen: Führt zu Namenskonflikten bei Refactoring. Lösung: Immer dynamisch via `basename(@__FILE__)[1:end-3]` in ArgumentError.
- [NEW] Redundante T()-Konvertierungen: `T(0.5)` in Formeln führt zu leichten Instabilitäten. Lösung: Entferne sie; Julia inferiert automatisch. Beispiel: In Schaffer1: `T(0.5) + numerator / denominator` → `0.5 + numerator / denominator`.
- [NEW] Inkonsistente Quellen: Properties aus Quelle mit abweichender Formel (z.B. sin^2(cos) vs. cos^2(sin)) – Lösung: Suche exakte Matches oder notiere Adaption in :description per [RULE_SOURCE_FORMULA_CONSISTENCY].

## Erlaubte Properties (VALID_PROPERTIES)
MUST: Nur aus dieser Liste verwenden. Erweitere Set und Readme bei Bedarf (PR mit Quelle).
SHOULD: Für separable: "separable" (z.B. Bohachevsky1). Ergänze :source.

Tabelle (mit Beschreibungen und Beispielen):

| Property                  | Beschreibung                                                                 | Beispiele (Funktionen)          |
|---------------------------|-----------------------------------------------------------------------------|---------------------------------|
| bounded                  | Hat definierte Schranken (:lb, :ub).                                        | Branin, Bohachevsky2            |
| continuous               | Kontinuierlich überall.                                                     | Ackley, Rosenbrock              |
| controversial            | Minimum umstritten (z. B. mehrere Quellen).                                 | Langermann                      |
| convex                   | Konvex (globales Minimum = einziges).                                       | Sphere                          |
| deceptive                | Täuscht lokale Optima vor.                                                  | -                               |
| differentiable           | Differenzierbar überall.                                                    | Rosenbrock                      |
| finite_at_inf            | Endlicher Wert bei ±∞.                                                      | -                               |
| fully non-separable      | Vollständig nicht-separabel.                                                | -                               |
| has_constraints          | Hat Nebenbedingungen.                                                       | -                               |
| has_noise                | Stochastisch/noisy.                                                         | Quartic                         |
| highly multimodal        | Stark multimodal (viele Local Minima).                                      | Rastrigin                       |
| multimodal               | Mehrere Local Minima.                                                       | Branin                          |
| non-convex               | Nicht-konvex.                                                               | Ackley                          |
| non-separable            | Nicht-separabel (Variablen gekoppelt).                                      | Rosenbrock                      |
| partially differentiable | Teilweise differenzierbar.                                                  | BartelsConn                     |
| partially separable      | Teilweise separabel.                                                        | -                               |
| quasi-convex             | Quasi-konvex.                                                               | -                               |
| scalable                 | Skalierbar (beliebige n).                                                   | Ackley, Rastrigin               |
| separable                | Separabel (unabhängige Variablen).                                          | Sphere, Bohachevsky1            |
| strongly convex          | Stark konvex.                                                               | Quadratic                       |
| unimodal                 | Unimodal (ein Global Minimum).                                              | Sphere                          |
| ill-conditioned          | Schlecht konditioniert (hohe Cond-Number).                                  | Rosenbrock                      |

## Umgang mit fehlenden Properties

[RULE_MISSING_PROPERTIES]: Wenn Jamil & Yang eine Property nennt, die nicht in VALID_PROPERTIES ist:
1. Verwende nur die Properties, die in VALID_PROPERTIES existieren
2. Dokumentiere fehlende Properties in :description (z.B. "Contains absolute value terms" statt "non-differentiable")
3. Erstelle KEINE neuen Properties ohne vorherige Erweiterung von VALID_PROPERTIES
4. [NEW] Stelle sicher, dass die Formel in der Quelle exakt passt [RULE_SOURCE_FORMULA_CONSISTENCY]; bei Varianten suche alternative Quellen.

Beispiel: Price Function 1 ist laut Jamil & Yang (2013, p. 32) "non-differentiable, separable, non-scalable"
→ Verwende: ["continuous", "separable", "multimodal"]
→ In :description erwähnen: "Contains absolute value terms"

## Code-Struktur und Implementierung
Ziel: Erstelle <name>.jl mit konsistenter Signatur, ohne externe Deps.

- [RULE_NO_MODULE] MUST: Kein module ... end in src/functions/*.jl. Wird via include geladen.
- [RULE_SIGNATURE] MUST: Funktionssignatur: function <name>(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}. Export: export <UPPER>_FUNCTION, <name>, <name>_gradient.
- [RULE_NAME_CONSISTENCY] MUST: Der gewählte, kleingeschriebene Funktionsname (`<name>`) MUST in allen folgenden Artefakten konsistent verwendet werden: Dateiname (`<name>.jl`), Funktionssignatur (`function <name>`), Gradientensignatur (`function <name>_gradient`), exportierte Konstante (`<UPPERCASE>_FUNCTION`), und im `:name`-Meta-Feld (exakt der Dateiname ohne Extension, klein, ohne Leerzeichen). | Z. B. Datei `schaffer1.jl` → `schaffer1`, `schaffer1_gradient`, `SCHAFFER1_FUNCTION`, `:name => "schaffer1"`. Automatisierbar: `:name => basename(@__FILE__)[1:end-3]`. Häufiger Fehler: Tippfehler wie "shaffer" vs. "schaffer" – prüfe mit `grep -r "<name>" src/` vor Commit.
- [RULE_NAME_CONSISTENCY] SHOULD: In Fehlermeldungen (z. B. ArgumentError) den exakten `<name>` verwenden, um Konsistenz zu wahren. Für lesbare Anzeigen (z. B. in Docs/Print-Funktionen) eine separate Formatierung nutzen (z. B. titlecase(`<name>`)). | Z. B. `throw(ArgumentError("$(basename(@__FILE__)[1:end-3]) requires exactly 2 dimensions"))` – dynamisch konsistent. In `print_function_properties.jl`: "Schaffer1" via String-Manipulation. Für skalierbare: Gleiche Regel (z. B. `:name => "ackley"`).
- [RULE_ERROR_TEXT_DYNAMIC] MUST: In allen Fehlermeldungen (z. B. ArgumentError für Dimension, NaN/Inf-Handling) den dynamischen Funktionsnamen verwenden: `func_name = basename(@__FILE__)[1:end-3]; throw(ArgumentError("$(func_name) requires exactly <DIM> dimensions"))`. Dies gewährleistet Konsistenz mit `:name` und vermeidet manuelle Tippfehler. Beispiel: Statischer Text `"schaffer1 requires exactly 2 dimensions"` → dynamisch `"$(func_name) requires exactly 2 dimensions"`. Automatisierbar: CI-Check mit Regex auf `basename(@__FILE__)` in Fehlern.
- [RULE_GRADTYPE] MUST: Gradient als Vector<T> (z.B. zeros(T, n)). Kein SVector.
- [RULE_NO_CONST_ARRAYS] MUST: Keine globalen const-Arrays. Lokale let-Blöcke für Caching. Wiederholung: Vermeide globale Konstanten.
- [RULE_ERROR_HANDLING] MUST: Prüfe length(x)==0 → ArgumentError. Handle NaN/Inf (return NaN/Inf).
- SHOULD: Verwende @inbounds für Loops; prüfe @code_warntype für Type-Stability.
- [RULE_TYPE_CONVERSION_MINIMAL] SHOULD: Vermeide redundante explizite Typ-Konvertierungen wie `T(0.5)` oder `T(1.0)` in Ausdrücken, da Julia die Inferenz korrekt durchführt (T ist Subtyp von Real). Verwende stattdessen `0.5` oder `1.0`. Dies verbessert Type-Stability und vermeidet unnötigen Overhead. Beispiel: `numerator = sin(sq_sum)^2 - T(0.5)` → `numerator = sin(sq_sum)^2 - 0.5`. Prüfe mit `@code_warntype tf.f([1.0, 1.0])` – sollte keine Type-Instabilitäten zeigen.
- MAY: Für Matrizen: A = Diagonal(fill(2.0, n)); prüfe eigvals(A) > 0.
- [RULE_META_CALL] SHOULD: In Meta-Dict: Für skalierbare, aber konstante Werte (z.B. :min_value), (n::Int) -> value verwenden (ignoriere n). Beispiel für Qing: :min_value => (n::Int) -> 0.0. [NEW] Für noisy konstante Minima: Deterministischer Teil als (n::Int) -> value; Noise in :description.
- [NEW] Für skalierbare Funktionen: Füge :default_n => <int> hinzu (siehe [RULE_DEFAULT_N]). Verwende es konsistent in Templates und Tests. MUST: Passe die Templates an, um [RULE_ERROR_TEXT_DYNAMIC] und [RULE_TYPE_CONVERSION_MINIMAL] einzubauen – siehe aktualisierte Templates unten.

Template für src/functions/<name>.jl (non-scalable, z.B. Biggs EXP3, n=3):

# src/functions/<name>.jl
# Purpose: Implementation of the <Full Name> test function.
# Global minimum: f(x*)=<value> at x*=<pos>.
# Bounds: <lb> ≤ x_i ≤ <ub>.

export <UPPERCASE>_FUNCTION, <name>, <name>_gradient

function <name>(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    func_name = basename(@__FILE__)[1:end-3]  # Dynamisch: "<name>" für Konsistenz [RULE_NAME_CONSISTENCY]
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n != <DIM> && throw(ArgumentError("$(func_name) requires exactly <DIM> dimensions"))  # Dynamischer Fehlertext [RULE_ERROR_TEXT_DYNAMIC]
    any(isnan.(x)) && return T(NaN)
    any(isinf.(x)) && return T(Inf)
    
    # Optimierte lokale Berechnung: Arrays direkt in Schleife generieren, um let-Block zu vermeiden [RULE_NO_CONST_ARRAYS]
    sum_sq = zero(T)
    @inbounds for i in 1:<LOOP_SIZE>
        t_i = 0.1 * i  # Dynamisch berechnen statt pre-allocated Array
        y_i = exp(-t_i) - 5 * exp(-10 * t_i)
        e_i = exp(-t_i * x[1]) - x[3] * exp(-t_i * x[2]) - y_i
        sum_sq += e_i^2
    end
    sum_sq
end

function <name>_gradient(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    func_name = basename(@__FILE__)[1:end-3]  # Dynamisch: "<name>" [RULE_NAME_CONSISTENCY]
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n != <DIM> && throw(ArgumentError("$(func_name) requires exactly <DIM> dimensions"))  # Dynamisch [RULE_ERROR_TEXT_DYNAMIC]
    any(isnan.(x)) && return fill(T(NaN), n)
    any(isinf.(x)) && return fill(T(Inf), n)
    
    grad = zeros(T, <DIM>)  # [RULE_GRADTYPE]
    # ... Gradient-Implementierung, z.B. ohne redundante T(1.0) [RULE_TYPE_CONVERSION_MINIMAL] ...
    @inbounds for i in 1:n
        # ...
    end
    grad
end

const <UPPERCASE>_FUNCTION = TestFunction(
    <name>,
    <name>_gradient,
    Dict(
        :name => basename(@__FILE__)[1:end-3],  # Dynamisch: "<name>" – exakt Dateiname [RULE_NAME_CONSISTENCY]
        :description => "<Beschreibung; Properties based on [Direkte Quelle, z.B. Jamil & Yang (2013, p. 15)]; ursprünglich aus [Mutmaßliche Ursprungsquelle, falls bekannt]>.",
        :math => raw"""f(\mathbf{x}) = <LaTeX>. """,
        :start => () -> [<start1>, ...],  # Für scalable: (n::Int) -> ...
        :min_position => () -> [<pos1>, ...],
        :min_value => () -> <value>,  # Für konstante Werte: Immer () -> value, auch bei scalable
        :properties => ["<prop1>", ...],
        :source => "<Direkte Quelle, z.B. Jamil & Yang (2013, p. 15)>",
        :lb => () -> [<lb1>, ...],
        :ub => () -> [<ub1>, ...],
    )
)

Für scalable: Ersetze () -> [...] durch (n::Int) -> begin n<1 && throw(...); fill(<val>, n) end. Füge "scalable" zu properties. [NEW] Beispiel-Anpassung für Qing: :min_value => (n::Int) -> 0.0 (konstant), :min_position => (n::Int) -> sqrt.(1:n). Füge :default_n => 2 hinzu.

Template für src/functions/<name>.jl (scalable, z.B. Qing):

# src/functions/<name>.jl
# Purpose: Implementation of the <Full Name> test function.
# Global minimum: f(x*)=<value> at x*=... (n-dependent).
# Bounds: <lb> ≤ x_i ≤ <ub>.

export <UPPERCASE>_FUNCTION, <name>, <name>_gradient

function <name>(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    func_name = basename(@__FILE__)[1:end-3]  # Dynamisch: "<name>" [RULE_NAME_CONSISTENCY]
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n < 1 && throw(ArgumentError("$(func_name) requires at least 1 dimension"))  # Dynamisch, angepasst für scalable [RULE_ERROR_TEXT_DYNAMIC]
    any(isnan.(x)) && return T(NaN)
    any(isinf.(x)) && return T(Inf)
    
    sum_sq = zero(T)
    @inbounds for i in 1:n
        diff = x[i]^2 - i
        sum_sq += diff^2
    end
    sum_sq
end

function <name>_gradient(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    func_name = basename(@__FILE__)[1:end-3]  # Dynamisch: "<name>"
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n < 1 && throw(ArgumentError("$(func_name) requires at least 1 dimension"))  # Dynamisch [RULE_ERROR_TEXT_DYNAMIC]
    any(isnan.(x)) && return fill(T(NaN), n)
    any(isinf.(x)) && return fill(T(Inf), n)
    
    grad = zeros(T, n)  # [RULE_GRADTYPE]
    @inbounds for i in 1:n
        diff = x[i]^2 - i
        grad[i] = 4 * x[i] * diff  # Vereinfachte Konstanten: 4 statt T(4) [RULE_TYPE_CONVERSION_MINIMAL]
    end
    grad
end

const <UPPERCASE>_FUNCTION = TestFunction(
    <name>,
    <name>_gradient,
    Dict(
        :name => basename(@__FILE__)[1:end-3],  # Dynamisch: "<name>" – exakt Dateiname [RULE_NAME_CONSISTENCY]
        :description => "<Beschreibung; Properties based on [Direkte Quelle, z.B. Jamil & Yang (2013, p. 29)]; Multiple global minima due to sign choices; ursprünglich aus [Mutmaßliche Ursprungsquelle, falls bekannt]>.",
        :math => raw"""f(\mathbf{x}) = \sum_{i=1}^D (x_i^2 - i)^2.""",
        :start => (n::Int) -> begin n < 1 && throw(ArgumentError("Dimension must be at least 1")); zeros(n) end,
        :min_position => (n::Int) -> begin n < 1 && throw(ArgumentError("Dimension must be at least 1")); sqrt.(1:n) end,
        :min_value => (n::Int) -> 0.0,  # Konstant: (n::Int) -> value [RULE_META_CONSISTENCY]
        :default_n => 2,  # Kleinste n >1; hier 2 für allgemein skalierbar [RULE_DEFAULT_N]
        :properties => ["continuous", "differentiable", "separable", "scalable", "multimodal"],
        :source => "<Direkte Quelle, z.B. Jamil & Yang (2013, p. 29)>",
        :lb => (n::Int) -> begin n < 1 && throw(ArgumentError("Dimension must be at least 1")); fill(<lb>, n) end,
        :ub => (n::Int) -> begin n < 1 && throw(ArgumentError("Dimension must be at least 1")); fill(<ub>, n) end,
    )
)

## Tests und Validierung
Ziel: Zentrale Tests + funktionsspezifisch in test/<name>_tests.jl.

- [RULE_ATOL] SHOULD: atol=1e-3 für Start/Extra, 1e-8 für Minima. [NEW] Für has_noise: Kein atol; Range-Check (z.B. 0 <= f <1).
- MUST: Einbinden via include("test/<name>_tests.jl") in include_testfiles.jl.
- SHOULD: Berechne Test-Werte exakt via REPL (z.B. tf.f(start_point)). [NEW] Für Noise: Multiple Evaluations (z.B. mean([tf.f(min_pos) for _ in 1:10]) ≈ deterministischer Wert).
- [RULE_TEST_SYNTAX] MUST: Für Vektor-Vergleiche: Verwende all(isapprox.(vec, target, atol=1e-8)) statt all(vec .≈ target, atol=1e-8) – letzteres wirft MethodError, da all() atol ignoriert. Für Skalare: x ≈ y atol=1e-8.
- [NEW] Meta-Aufruf im Test: Passe Aufruf an Signatur an: Wenn () -> ..., dann tf.meta[:min_value](); wenn (n::Int) -> ..., dann tf.meta[:min_value](n).
- [NEW] Für skalierbare: Verwende n = tf.meta[:default_n] in Tests für Standard-Validierungen (z.B. min_pos = tf.meta[:min_position](n)).
- [NEW RULE_NOISE_VALIDATION] MUST: In funktionsspezifischen Tests und runtests.jl: Für "has_noise": @test f_min >= min_value && f_min < min_value + noise_upper (z.B. <1 für [0,1)). 

Template für test/<name>_tests.jl (non-scalable):

# test/<name>_tests.jl

using Test, NonlinearOptimizationTestFunctions
@testset "<name>" begin
    tf = <UPPERCASE>_FUNCTION
    @test tf.meta[:name] == basename("src/functions/<name>.jl")[1:end-3]  # Dynamisch: "<name>"
    @test has_property(tf, "<prop1>")  # Für jede Property
    
    start_point = tf.meta[:start]()
    @test tf.f(start_point) ≈ <computed> atol=1e-3
    
    min_pos = tf.meta[:min_position]()
    @test tf.f(min_pos) ≈ tf.meta[:min_value]() atol=1e-8  # Passe an: () oder (n); für Noise: Range-Check
end

Template für test/<name>_tests.jl (scalable, z.B. Qing):

# test/<name>_tests.jl

using Test, NonlinearOptimizationTestFunctions
@testset "<name>" begin
    tf = <UPPERCASE>_FUNCTION
    @test tf.meta[:name] == basename("src/functions/<name>.jl")[1:end-3]  # Dynamisch: "<name>"
    @test has_property(tf, "<prop1>")  # Für jede Property
    
    n = tf.meta[:default_n]  # Verwende default_n für Standard-Tests
    @test n >= 2
    
    start_point = tf.meta[:start](n)
    @test length(start_point) == n
    @test all(start_point .== 0)  # Beispiel-Check
    
    min_pos = tf.meta[:min_position](n)
    @test tf.f(min_pos) ≈ tf.meta[:min_value](n) atol=1e-8  # min_value mit (n), min_position mit (n); für Noise: Range-Check
    
    # Extra: Check another minimum
    min_pos_neg = [-sqrt(1.0), sqrt(2.0)]
    @test tf.f(min_pos_neg) ≈ 0.0 atol=1e-8
end

[NEW] Für noisy Funktionen (z.B. Quartic): Erweitere Template um:
    f_min = tf.f(min_pos)
    @test f_min >= 0 && f_min < 1  # Für [0,1)-Noise

[NEW] Update für runtests.jl "Minimum Validation" Testset (füge nach min_value-Zeile ein):
    if "has_noise" in tf.meta[:properties]
        @test f_val >= min_value && f_val < min_value + 1.0  # Beispiel für [0,1); passe an Noise an
    else
        @test f_val ≈ min_value atol=1e-6
    end

## TestFunction-Struktur [RULE_TESTFUNCTION_FIELDS]

Die TestFunction-Struktur hat folgende Felder:
- f: Die Zielfunktion (erster Parameter im Konstruktor)
- grad: Die Gradientenfunktion (zweiter Parameter im Konstruktor)
- meta: Dictionary mit Metadaten

WICHTIG: In Tests verwendet man tf.grad(x), NICHT tf.gradient(x)!

Hinweis zur Namenskonvention: Der Funktionsname endet auf _gradient (z.B. price1_gradient), aber das Struct-Feld heißt grad. Dies ist eine Designentscheidung des Pakets - der längere Name verhindert Namenskonflikte, während das kurze Feld praktischer im Zugriff ist.

Beispiel:

# Funktion definieren
function myfunction_gradient(x) ... end

# TestFunction erstellen
const MYFUNCTION_FUNCTION = TestFunction(
    myfunction,
    myfunction_gradient,  # ← wird zu tf.grad
    Dict(...)
)

# In Tests verwenden
grad = tf.grad([1.0, 2.0])  # ✅ Korrekt
grad = tf.gradient([1.0, 2.0])  # ❌ Fehler!

## Schritte-Checkliste (mit Regeln)
1. Recherche: Validiere Properties [RULE_PROPERTIES_SOURCE] – notiere spezifische Stelle (z.B. Seite) in :source. Für scalable: Bestimme :default_n [RULE_DEFAULT_N]. [NEW] Für noisy: Definiere Noise-Range in :description.
2. Implementiere src/functions/<name>.jl [RULE_SIGNATURE, RULE_NO_CONST_ARRAYS]. Füge :default_n hinzu. [NEW] Für konstante min_value: (n::Int) -> value. Verwende direkte Quelle mit Stelle in :source.
3. Syntax-Check: julia --project=. -e 'include("src/functions/<name>.jl")' [RULE_SYNTAXCHECK].
4. Tests: Schreibe test/<name>_tests.jl, einbinden. [NEW] REPL-Validierung: Lade tf, teste tf.meta[:min_value](tf.meta[:default_n]) ≈ tf.f(tf.meta[:min_position](tf.meta[:default_n])) atol=1e-8; für Noise: Range-Check.
5. Gesamttests: julia --project=. test/runtests.jl. [NEW] Überprüche Noise-Adaptation in Minimum Validation.
6. Validierung: [ ] Properties aus Tabelle; [ ] Keine globalen const; [ ] Gradient als Vector<T>; [NEW] Keine MethodError in Meta-Aufrufen; [ ] Vektor-Tests mit isapprox.. [NEW] :default_n >=2 und verwendet in Tests; [NEW] Noise-Handling in Tests/runtests.jl; [NEW] :source mit spezifischer Stelle (z.B. "Jamil & Yang (2013, p. 27)").

Finale Validierung: Alle MUST-Regeln erfüllt? Benchmark mit @btime tf.f(rand(tf.meta[:default_n])) <1μs. [NEW] Führe manuell: julia> tf = <UPPER>_FUNCTION; n = tf.meta[:default_n]; min_pos = tf.meta[:min_position](n); für Noise: multiple tf.f(min_pos) in [0,1); sonst @test tf.f(min_pos) ≈ tf.meta[:min_value](n) atol=1e-8 – passe Signatur an, falls Error. Überprüche :source auf direkte Übernahme mit Stelle.