#include "ck/host/device_gemm_multiple_d.hpp"
#include <iostream>

bool test_Problem()
{
    auto problem = ck::host::device_gemm_multiple_d::Problem{
        256,
        256,
        256,
        false,
        true,
        false,
        {},
        ck::host::DataType::Half,
        ck::host::DataType::Half,
        ck::host::DataType::Half,
        {},
        "ck::tensor_operation::element_wise::Passthrough",
        "ck::tensor_operation::element_wise::Passthrough",
        "ck::tensor_operation::element_wise::Passthrough"};

    const auto include_header = problem.GetIncludeHeader();
    const auto solutions      = problem.GetSolutions("gfx90a");
    const auto& solution      = solutions.at(0);
    const auto template_str   = solution.template_str;
    const auto grid_size      = solution.grid_size;
    const auto block_size     = solution.block_size;

    bool pass = true;

    pass &= include_header ==
            "ck/tensor_operation/gpu/device/impl/device_gemm_multiple_d_xdl_cshuffle.hpp";
    pass &= solutions.size() == 42;
    pass &= template_str ==
            "ck::tensor_operation::device::DeviceGemmMultipleD_Xdl_CShuffle< "
            "ck::tensor_layout::gemm::RowMajor, ck::tensor_layout::gemm::ColumnMajor, ck::Tuple<>, "
            "ck::tensor_layout::gemm::RowMajor, ck::half_t, ck::half_t, float, float, ck::Tuple<>, "
            "ck::half_t, ck::tensor_operation::element_wise::Passthrough, "
            "ck::tensor_operation::element_wise::Passthrough, "
            "ck::tensor_operation::element_wise::Passthrough, "
            "ck::tensor_operation::device::GemmSpecialization::Default, 1, 256, 256, 128, 32, 8, "
            "8, 32, 32, 4, 2, ck::Sequence<4,64,1>, ck::Sequence<1,0,2>, ck::Sequence<1,0,2>, 2, "
            "8, 8, 1, ck::Sequence<4,64,1>, ck::Sequence<1,0,2>, ck::Sequence<1,0,2>, 2, 8, 8, 1, "
            "1, 1, ck::Sequence<1,32,1,8>, 8, ck::LoopScheduler::Default, ck::PipelineVersion::v1>";
    pass &= grid_size == 2;
    pass &= block_size == 256;

    return pass;
}

bool test_GetGemmSpec()
{
    bool pass = true;
    {
        // PadMNK
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            255,
            255,
            255,
            false,
            true,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;

        pass &= template_str.find("GemmSpecialization::MNKPadding") != std::string::npos;
    }
    {
        // Default
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            true,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;

        pass &= template_str.find("GemmSpecialization::Default") != std::string::npos;
    }

    return pass;
}

bool test_GetInstances()
{
    bool pass = true;
    {
        // Col Col Fp16
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            true,
            true,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 51;
    }
    {
        // Col Row Fp16
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            true,
            false,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 51;
    }
    {
        // Row Col Fp16
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            true,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 42;
    }
    {
        // Row Row Int8
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {},
            ck::host::DataType::Int8,
            ck::host::DataType::Int8,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 48;
    }
    {
        // Col Col Int8
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            true,
            true,
            false,
            {},
            ck::host::DataType::Int8,
            ck::host::DataType::Int8,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 48;
    }
    {
        // Col Row Int8
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            true,
            false,
            false,
            {},
            ck::host::DataType::Int8,
            ck::host::DataType::Int8,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 48;
    }
    {
        // Row Col Int8
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            true,
            false,
            {},
            ck::host::DataType::Int8,
            ck::host::DataType::Int8,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 39;
    }
    {
        // Row Row Int8
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {},
            ck::host::DataType::Int8,
            ck::host::DataType::Int8,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        pass &= problem.GetSolutions("gfx90a").size() == 48;
    }

    return pass;
}

bool test_MakeLayoutsTuple()
{
    bool pass = true;
    {
        // Empty Tuple
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {ck::host::DataType::Half},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;
        pass &= template_str.find("ck::Tuple<>") != std::string::npos;
    }
    {
        // RowColRow Tuple
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {false, true, false},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {ck::host::DataType::Half},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;
        pass &= template_str.find(
                    "ck::Tuple<ck::tensor_layout::gemm::RowMajor, "
                    "ck::tensor_layout::gemm::ColumnMajor, ck::tensor_layout::gemm::RowMajor>") !=
                std::string::npos;
    }

    return pass;
}

bool test_MakeTypeTuple()
{
    bool pass = true;
    {
        // Empty Tuple
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {true},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;
        pass &= template_str.find("ck::Tuple<>") != std::string::npos;
    }
    {
        // Half Int8 Tuple
        auto problem = ck::host::device_gemm_multiple_d::Problem{
            256,
            256,
            256,
            false,
            false,
            false,
            {},
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            ck::host::DataType::Half,
            {ck::host::DataType::Half, ck::host::DataType::Int8},
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough",
            "ck::tensor_operation::element_wise::Passthrough"};
        const auto solutions    = problem.GetSolutions("gfx90a");
        const auto& solution    = solutions.at(0);
        const auto template_str = solution.template_str;
        pass &= template_str.find("ck::Tuple<ck::half_t, int8_t>") != std::string::npos;
    }
    return pass;
}

int main()
{
    bool pass = true;
    pass &= test_Problem();
    pass &= test_GetGemmSpec();
    pass &= test_GetInstances();
    pass &= test_MakeLayoutsTuple();
    pass &= test_MakeTypeTuple();

    if(pass)
    {
        std::cout << "Test jit library: Pass" << std::endl;
        return 0;
    }
    else
    {
        std::cout << "Test jit library: Fail" << std::endl;
        return -1;
    }
}
