/**
 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

/* !
 * \file debug.asc
 * \brief 向量加法算子调试样例,演示 msdebug 工具的断点调试与变量查看功能
 */

#include <cstdint>
#include <vector>
#include <iostream>
#include "acl/acl.h"
#include "kernel_operator.h"

#define CHECK_ACL(call, msg) do { \
    aclError ret = (call); \
    if (ret != ACL_SUCCESS) { \
        std::cerr << "[ERROR] " << msg << " failed, ret=" << ret << std::endl; \
        return std::vector<float>(); \
    } \
} while(0)

template <uint32_t tileSize>
__vector__ __global__ void vec_add_demo(__gm__ float* a, __gm__ float* b, __gm__ float* c)
{
    AscendC::InitSocState();

    AscendC::GlobalTensor<float> gmA, gmB, gmC;
    gmA.SetGlobalBuffer(a + block_idx * tileSize, tileSize);
    gmB.SetGlobalBuffer(b + block_idx * tileSize, tileSize);
    gmC.SetGlobalBuffer(c + block_idx * tileSize, tileSize);

    AscendC::LocalMemAllocator<AscendC::Hardware::UB> ubAlloc;
    AscendC::LocalTensor<float> bufA = ubAlloc.Alloc<float, tileSize>();
    AscendC::LocalTensor<float> bufB = ubAlloc.Alloc<float, tileSize>();
    AscendC::LocalTensor<float> bufC = ubAlloc.Alloc<float, tileSize>();

    AscendC::DataCopy(bufA, gmA, tileSize);
    AscendC::DataCopy(bufB, gmB, tileSize);
    AscendC::PipeBarrier<PIPE_ALL>();

    AscendC::Add(bufC, bufA, bufB, tileSize);
    AscendC::PipeBarrier<PIPE_ALL>();

    AscendC::DataCopy(gmC, bufC, tileSize);
    AscendC::PipeBarrier<PIPE_ALL>();
}

std::vector<float> launch_add(std::vector<float>& a, std::vector<float>& b)
{
    constexpr uint32_t numBlocks = 8;
    constexpr uint32_t tileSize = 2048;
    uint32_t totalLen = a.size();
    size_t totalBytes = totalLen * sizeof(float);
    float* devA = nullptr;
    float* devB = nullptr;
    float* devC = nullptr;
    uint8_t* hostC = nullptr;

    int32_t deviceId = 0;
    CHECK_ACL(aclrtSetDevice(deviceId), "aclrtSetDevice");
    aclrtStream stream = nullptr;
    CHECK_ACL(aclrtCreateStream(&stream), "aclrtCreateStream");

    CHECK_ACL(aclrtMalloc((void**)&devA, totalBytes, ACL_MEM_MALLOC_HUGE_FIRST), "aclrtMalloc devA");
    CHECK_ACL(aclrtMalloc((void**)&devB, totalBytes, ACL_MEM_MALLOC_HUGE_FIRST), "aclrtMalloc devB");
    CHECK_ACL(aclrtMalloc((void**)&devC, totalBytes, ACL_MEM_MALLOC_HUGE_FIRST), "aclrtMalloc devC");
    CHECK_ACL(aclrtMallocHost((void**)&hostC, totalBytes), "aclrtMallocHost hostC");

    CHECK_ACL(aclrtMemcpy(devA, totalBytes, a.data(), totalBytes, ACL_MEMCPY_HOST_TO_DEVICE), "aclrtMemcpy H2D devA");
    CHECK_ACL(aclrtMemcpy(devB, totalBytes, b.data(), totalBytes, ACL_MEMCPY_HOST_TO_DEVICE), "aclrtMemcpy H2D devB");

    vec_add_demo<tileSize><<<numBlocks, nullptr, stream>>>(devA, devB, devC);
    CHECK_ACL(aclrtSynchronizeStream(stream), "aclrtSynchronizeStream");

    CHECK_ACL(aclrtMemcpy(hostC, totalBytes, devC, totalBytes, ACL_MEMCPY_DEVICE_TO_HOST), "aclrtMemcpy D2H");
    std::vector<float> c((float*)hostC, (float*)(hostC + totalBytes));

    aclrtFree(devA);
    aclrtFree(devB);
    aclrtFree(devC);
    aclrtFreeHost(hostC);
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);

    return c;
}

int32_t main(int32_t argc, char* argv[])
{
    aclInit(nullptr);

    constexpr uint32_t totalLen = 8 * 2048;
    std::vector<float> a(totalLen);
    std::vector<float> b(totalLen);

    for (uint32_t i = 0; i < totalLen; ++i) {
        a[i] = i * 0.1f;
        b[i] = i * 0.2f;
    }

    std::vector<float> result = launch_add(a, b);
    if (result.empty()) {
        std::cerr << "[ERROR] launch_add failed" << std::endl;
        aclFinalize();
        return 1;
    }

    std::vector<float> golden(totalLen);
    for (uint32_t i = 0; i < totalLen; ++i) {
        golden[i] = a[i] + b[i];
    }

    bool pass = std::equal(golden.begin(), golden.end(), result.begin());
    std::cout << (pass ? "test pass!" : "test failed!") << std::endl;

    aclFinalize();
    return pass ? 0 : 1;
}