from symbolica.community.vakint import Vakint, VakintEvaluationMethod, VakintExpression, VakintNumericalResult
from symbolica import E, S

masses = {"muvsq": 2., "mursq": 3.}
external_momenta = {
    1: (0.1, 0.2, 0.3, 0.4),
    2: (0.5, 0.6, 0.7, 0.8)
}

# FIX: on my setup, the code below triggers an "out of bound" python crash
# eo = [ VakintEvaluationMethod.new_alphaloop_method() ]
# to_fix = vakint = Vakint(evaluation_order=eo)

# It is of course possible to use defaults only, with
# vakint = Vakint()
vakint = Vakint(
    integral_normalization_factor="MSbar",
    mu_r_sq_symbol=S("mursq"),
    # If you select 5 terms, then MATAD will be used, but for 4 and fewer, alphaLoop is will be used as
    # it is first in the evaluation_order supplied.
    number_of_terms_in_epsilon_expansion=4,
    evaluation_order=[
        VakintEvaluationMethod.new_alphaloop_method(),
        VakintEvaluationMethod.new_matad_method(),
        VakintEvaluationMethod.new_fmft_method(),
        # VakintEvaluationMethod.new_pysecdec_method(
        #     min_n_evals=10_000,
        #     max_n_evals=1000_000,
        #     numerical_masses=masses,
        #     numerical_external_momenta=external_momenta
        # ),
    ],
    form_exe_path="form",
    python_exe_path="python3",
)

integral = E("""
        ( 
            k(1,11)*k(2,11)*k(1,22)*k(2,22)
          + p(1,11)*k(3,11)*k(3,22)*p(2,22)
          + p(1,11)*p(2,11)*(k(2,22)+k(1,22))*k(2,22) 
        )*topo(
             prop(1,edge(1,2),k(1),muvsq,1)
            * prop(2,edge(2,3),k(2),muvsq,1)
            * prop(3,edge(3,1),k(3),muvsq,1)
            * prop(4,edge(1,4),k(3)-k(1),muvsq,1)
            * prop(5,edge(2,4),k(1)-k(2),muvsq,1)
            * prop(6,edge(3,4),k(2)-k(3),muvsq,1)
)""", default_namespace="vakint")
print(f"\nStarting integral:\n{VakintExpression(integral)}")

canonical_integral = vakint.to_canonical(integral, short_form=True)
print(f"\nCanonical integral:\n{VakintExpression(canonical_integral)}")

tensor_reduced_integral = vakint.tensor_reduce(canonical_integral)
print(f"\nTensor reduced integral:\n{
      VakintExpression(tensor_reduced_integral)}")

evaluated_integral = vakint.evaluate_integral(tensor_reduced_integral)
print(f"\nEvaluated integral:\n{evaluated_integral}")

# Direct evaluation all at once
direct_evaluation = vakint.evaluate(integral)

assert direct_evaluation == evaluated_integral

num_eval, num_error = vakint.numerical_evaluation(
    evaluated_integral, params=masses, externals=external_momenta)

print(f"\nNumerical evaluation:\n{num_eval}")

print(f"\nNumerical evaluation as list:\n{num_eval.to_list()}")

# FIX: on my setup, the code below triggers an "out of bound" python crash, similar to before for new Vakint() setup
print(f"\nNumerical evaluation, as expression:\n{vakint.numerical_result_to_expression(num_eval)}")  # nopep8

benchmark = VakintNumericalResult([
    (-3, (0.0, -11440.53140354612)),
    (-2, (0.0,  57169.95521898031)),
    (-1, (0.0, -178748.9838377694)),
    (-0, (0.0,  321554.1122184795)),
])

match_res, match_msg = benchmark.compare_to(
    num_eval, relative_threshold=1.0e-10
)

print(f"\nMatch result: {match_res}, {match_msg}")

assert match_res
