#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/operators.h>
#include <pybind11/functional.h>

#include <adani/adani.h>

namespace py = pybind11;

PYBIND11_MODULE(_core, m) {

    // Documentation

    m.doc() = "Python wrapper of libadani";

    // ApproximateCoefficientFunctions

    m.def("C2_g3_approximation", &C2_g3_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0, py::arg("method_flag")=default_method);
    m.def("CL_g3_approximation", &CL_g3_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0, py::arg("method_flag")=default_method);
    m.def("C2_ps3_approximation", &C2_ps3_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0);
    m.def("CL_ps3_approximation", &CL_ps3_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0);

    m.def("C2_g2_approximation", &C2_g2_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("v")=0) ;
    m.def("CL_g2_approximation", &CL_g2_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("v")=0) ;
    m.def("C2_ps2_approximation", &C2_ps2_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("v")=0) ;
    m.def("CL_ps2_approximation", &CL_ps2_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("v")=0) ;

    m.def("C2_g2_approximationA_klmv", &C2_g2_approximationA_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_g2_approximationB_klmv", &C2_g2_approximationB_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    m.def("C2_g3_approximationA_klmv", &C2_g3_approximationA_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("method_flag")=default_method);
    m.def("C2_g3_approximationB_klmv", &C2_g3_approximationB_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("method_flag")=default_method);
    m.def("C2_g3_approximationB_klmv_paper", &C2_g3_approximationB_klmv_paper, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("method_flag")=default_method);
    m.def("C2_g3_approximationBlowxi_klmv", &C2_g3_approximationBlowxi_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("method_flag")=default_method);

    m.def("C2_ps3_approximationA_klmv", &C2_ps3_approximationA_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("C2_ps3_approximationB_klmv", &C2_ps3_approximationB_klmv, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("C2_ps3_approximationA_klmv_paper", &C2_ps3_approximationA_klmv_paper, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("C2_ps3_approximationB_klmv_paper", &C2_ps3_approximationB_klmv_paper, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));

    m.def("C2_g20_approximation", &C2_g20_approximation, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g20_approximation", &CL_g20_approximation, py::arg("x"), py::arg("m2Q2"));
    m.def("C2_ps20_approximation", &C2_ps20_approximation, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_ps20_approximation", &CL_ps20_approximation, py::arg("x"), py::arg("m2Q2"));

    m.def("C2_g30_approximation", &C2_g30_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g30_approximation", &CL_g30_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_ps30_approximation", &C2_ps30_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_ps30_approximation", &CL_ps30_approximation, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g30_approximation_implicit", &CL_g30_approximation_implicit, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("A"), py::arg("B"), py::arg("C"), py::arg("D"), py::arg("a"), py::arg("b"), py::arg("v"));

    m.def("C2_g20_approximation_BAND", &C2_g20_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("CL_g20_approximation_BAND", &CL_g20_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("C2_ps20_approximation_BAND", &C2_ps20_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("CL_ps20_approximation_BAND", &CL_ps20_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("v"), py::arg("var"), py::arg("fact"));

    m.def("C2_g30_approximation_BAND", &C2_g30_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("CL_g30_approximation_BAND", &CL_g30_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("C2_ps30_approximation_BAND", &C2_ps30_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("v"), py::arg("var"), py::arg("fact"));
    m.def("CL_ps30_approximation_BAND", &CL_ps30_approximation_BAND, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("v"), py::arg("var"), py::arg("fact"));

    // AsymptoticCoefficientFunctions

    m.def("C2_g3_asymptotic", &C2_g3_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v1")=0, py::arg("v2")=0);
    m.def("CL_g3_asymptotic", &CL_g3_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("CL_g2_asymptotic", &CL_g2_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_ps2_asymptotic", &C2_ps2_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_ps2_asymptotic", &CL_ps2_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_ps3_asymptotic", &C2_ps3_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("CL_ps3_asymptotic", &CL_ps3_asymptotic, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));

    // HighscaleCoefficientFunctions

    m.def("CL_g3_highscale", &CL_g3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("DL_g3_highscale", &DL_g3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("C2_g3_highscale", &C2_g3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0);
    m.def("CL_ps3_highscale", &CL_ps3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("C2_ps3_highscale", &C2_ps3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("D2_ps3_highscale", &D2_ps3_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v")=0);

    m.def("C2_g2_highscale", &C2_g2_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_g2_highscale", &CL_g2_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_ps2_highscale", &C2_ps2_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_ps2_highscale", &CL_ps2_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("D2_g2_highscale", &D2_g2_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    m.def("C2_g1_highscale", &C2_g1_highscale, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g1_highscale", &CL_g1_highscale, py::arg("x"));

    // ExactCoefficientFunctions

    m.def("C2_g1", &C2_g1, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g1", &CL_g1, py::arg("x"), py::arg("m2Q2"));

    m.def("C2_g2", &C2_g2, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_ps2", &C2_ps2, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_g2", &CL_g2, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_ps2", &CL_ps2, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    m.def("CL_g20", &CL_g20, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_ps20", &CL_ps20, py::arg("x"), py::arg("m2Q2"));

    m.def("C2_g21", &C2_g21, py::arg("x"), py::arg("m2Q2"));
    m.def("C2_ps21", &C2_ps21, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g21", &CL_g21, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_ps21", &CL_ps21, py::arg("x"), py::arg("m2Q2"));

    m.def("C2_g31", &C2_g31, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g32", &CL_g32, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("method_flag")=default_method);
    m.def("C2_g32", &C2_g32, py::arg("x"), py::arg("m2Q2"), py::arg("nf"), py::arg("method_flag")=default_method);

    // MasslessCoefficientFunctions

    m.def("CL_g3_massless", &CL_g3_massless, py::arg("x"), py::arg("nf"));

    // SplittingFunctions

    m.def("Pgg1reg", &Pgg1reg, py::arg("x"), py::arg("nf"));
    m.def("Pgg1sing", &Pgg1sing, py::arg("x"), py::arg("nf"));
    m.def("Pgg1loc", &Pgg1loc, py::arg("nf"));

    // ThresholdCoefficientFunctions

    m.def("C2_g1_threshold", &C2_g1_threshold, py::arg("x"), py::arg("m2Q2"));

    m.def("C2_g2_threshold", &C2_g2_threshold, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_g2_threshold", &CL_g2_threshold, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    m.def("C2_g3_threshold", &C2_g3_threshold, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));
    m.def("CL_g3_threshold", &CL_g3_threshold, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"));

    // HighEnergyCoefficientFunctions

    m.def("C2_g3_power_terms", &C2_g3_power_terms, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("CL_g2_highenergy", &CL_g2_highenergy, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    m.def("C2_g3_highenergy", &C2_g3_highenergy, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("CL_g3_highenergy", &CL_g3_highenergy, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("C2_g3_highenergyLL", &C2_g3_highenergyLL, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_g3_highenergyLL", &CL_g3_highenergyLL, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("C2_g3_highenergy_highscale", &C2_g3_highenergy_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("CL_g3_highenergy_highscale", &CL_g3_highenergy_highscale, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"), py::arg("nf"), py::arg("v"));
    m.def("C2_g3_highenergy_highscaleLL", &C2_g3_highenergy_highscaleLL, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));
    m.def("CL_g3_highenergy_highscaleLL", &CL_g3_highenergy_highscaleLL, py::arg("x"), py::arg("m2Q2"), py::arg("m2mu2"));

    // Convolutions
    m.def("C2_g1_x_Pgq0", &C2_g1_x_Pgq0, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g1_x_Pgq0", &CL_g1_x_Pgq0, py::arg("x"), py::arg("m2Q2"));
    m.def("C2_g1_x_Pgg0", &C2_g1_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0", &CL_g1_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pgq1", &C2_g1_x_Pgq1, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgq1", &CL_g1_x_Pgq1, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g20_x_Pgq0", &C2_g20_x_Pgq0, py::arg("x"), py::arg("m2Q2"));
    m.def("CL_g20_x_Pgq0", &CL_g20_x_Pgq0, py::arg("x"), py::arg("m2Q2"));
    m.def("C2_ps20_x_Pqq0", &C2_ps20_x_Pqq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_ps20_x_Pqq0", &CL_ps20_x_Pqq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pgg0_x_Pgq0", &C2_g1_x_Pgg0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0_x_Pgq0", &CL_g1_x_Pgg0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pqq0_x_Pgq0", &C2_g1_x_Pqq0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pqq0_x_Pgq0", &CL_g1_x_Pqq0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pgg1", &C2_g1_x_Pgg1, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg1", &CL_g1_x_Pgg1, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_ps20_x_Pqg0", &C2_ps20_x_Pqg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_ps20_x_Pqg0", &CL_ps20_x_Pqg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g20_x_Pgg0", &C2_g20_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g20_x_Pgg0", &CL_g20_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("Pqg0_x_Pgq0", &Pqg0_x_Pgq0, py::arg("x"), py::arg("nf"));
    m.def("C2_g1_x_Pqg0_x_Pgq0", &C2_g1_x_Pqg0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pqg0_x_Pgq0", &CL_g1_x_Pqg0_x_Pgq0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pgg0_x_Pgg0", &C2_g1_x_Pgg0_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0_x_Pgg0", &CL_g1_x_Pgg0_x_Pgg0, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));

    m.def("C2_g1_x_Pgg0_x_Pgg0_reg", &C2_g1_x_Pgg0_x_Pgg0_reg, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0_x_Pgg0_reg", &CL_g1_x_Pgg0_x_Pgg0_reg, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("C2_g1_x_Pgg0_x_Pgg0_sing", &C2_g1_x_Pgg0_x_Pgg0_sing, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0_x_Pgg0_sing", &CL_g1_x_Pgg0_x_Pgg0_sing, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));

    m.def("C2_g1_x_Pgg0_x_Pgg0_MC", &C2_g1_x_Pgg0_x_Pgg0_MC, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));
    m.def("CL_g1_x_Pgg0_x_Pgg0_MC", &CL_g1_x_Pgg0_x_Pgg0_MC, py::arg("x"), py::arg("m2Q2"), py::arg("nf"));

}
