/**
 * 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 main.cpp
 * \brief Vector Function Add Example (RegBase / VF)
 *
 * 使用 RegBase(Register Based)编程模型在 Vector Core 上实现向量加法 z = x + y,
 * 与 Samples/0_Introduction/vector_add(TQue / MemBase 编程模型)实现相同功能,便于对比。
 * 计算在 Vector Function(VF)中完成:数据 Load 进 Vector 寄存器,在寄存器之间完成 Add,
 * 结果 Store 回 UB,中间结果不往返 UB。尾块由 Mask 自动处理。
 */

#include <algorithm>
#include <cmath>
#include <iostream>
#include <memory>
#include <random>
#include <tuple>
#include <vector>
#include "acl/acl.h"
#include "kernel_operator.h"
#include "platform/platform_ascendc.h"

// ACL 错误检查宏
#define CHECK_ACL(call)                                              \
    do {                                                             \
        aclError err = (call);                                       \
        if (err != ACL_SUCCESS) {                                    \
            std::cerr << "ACL error: " << err << " at " << __FILE__ \
                      << ":" << __LINE__ << std::endl;              \
            return 1;                                                \
        }                                                            \
    } while (0)

// 自定义删除器,安全处理空指针
struct AclrtFreeDeleter {
    void operator()(void* ptr) const {
        if (ptr != nullptr) {
            aclrtFree(ptr);
        }
    }
};

// ----------------------------------------------------------------------------
// 1. Vector Function(VF):RegBase 编程模型的编程载体
// ----------------------------------------------------------------------------
// VF 用 __simd_vf__ 标记,运行在 Vector Core 上。它直接控制 Vector 寄存器:
// 在函数体内用 AscendC::Reg::* API 完成 Load -> 运算 -> Store,中间结果留在寄存器里。
// 一条指令作用在一整批元素(一个寄存器宽度)上,粒度是 AscendC::VECTOR_REG_WIDTH 字节。
//
// 尾块自适应:total 不一定是寄存器宽度的整数倍。UpdateMask 根据剩余元素数生成掩码,
// 并使剩余数原地递减一个寄存器宽度;最后一轮剩余数不足一个寄存器宽度时,掩码只让
// 前 remain 个元素参与运算,其余 lane 被屏蔽,无需单独处理尾块。
template <typename T>
__simd_vf__ inline void VectorFunctionAdd(
    __ubuf__ T* xAddr, __ubuf__ T* yAddr, __ubuf__ T* zAddr, uint32_t total, uint16_t loopNum)
{
    constexpr uint32_t vectorLength = AscendC::VECTOR_REG_WIDTH / sizeof(T);
    AscendC::Reg::RegTensor<T> xReg, yReg, zReg;
    AscendC::Reg::MaskReg mask;
    uint32_t remain = total;

    for (uint16_t i = 0; i < loopNum; ++i) {
        mask = AscendC::Reg::UpdateMask<T>(remain);
        AscendC::Reg::LoadAlign<T, AscendC::Reg::LoadDist::DIST_NORM>(
            xReg, xAddr + i * vectorLength);
        AscendC::Reg::LoadAlign<T, AscendC::Reg::LoadDist::DIST_NORM>(
            yReg, yAddr + i * vectorLength);
        AscendC::Reg::Add(zReg, xReg, yReg, mask);
        AscendC::Reg::StoreAlign<T, AscendC::Reg::StoreDist::DIST_NORM>(
            zAddr + i * vectorLength, zReg, mask);
    }
}

// ----------------------------------------------------------------------------
// 2. Kernel:GM -> UB -> VF -> UB -> GM
// ----------------------------------------------------------------------------
// 数据从 Global Memory 拷入 UB,由 asc_vf_call 调用 VF 在寄存器上完成计算,
// 结果再从 UB 拷回 Global Memory。多个 block 按 blockLength 切分,每个 block
// 内部按 tileSize 分 tile 处理。
template <typename T>
__global__ __vector__ void vector_function_add_kernel(
    __gm__ uint8_t* x, __gm__ uint8_t* y, __gm__ uint8_t* z,
    int64_t totalLength, int64_t blockLength, uint32_t tileSize)
{
    AscendC::TPipe pipe;
    AscendC::TBuf<AscendC::TPosition::VECCALC> xBuf, yBuf, zBuf;
    pipe.InitBuffer(xBuf, tileSize);
    pipe.InitBuffer(yBuf, tileSize);
    pipe.InitBuffer(zBuf, tileSize);
    AscendC::LocalTensor<T> xLocal = xBuf.Get<T>();
    AscendC::LocalTensor<T> yLocal = yBuf.Get<T>();
    AscendC::LocalTensor<T> zLocal = zBuf.Get<T>();

    uint32_t blockIdx = AscendC::GetBlockIdx();
    AscendC::GlobalTensor<T> xGm, yGm, zGm;
    xGm.SetGlobalBuffer((__gm__ T*)x + blockLength * blockIdx);
    yGm.SetGlobalBuffer((__gm__ T*)y + blockLength * blockIdx);
    zGm.SetGlobalBuffer((__gm__ T*)z + blockLength * blockIdx);

    int64_t currentBlockLength = totalLength - blockIdx * blockLength;
    currentBlockLength = currentBlockLength > blockLength ? blockLength : currentBlockLength;
    if (currentBlockLength <= 0) {
        return;
    }

    constexpr uint32_t vectorLength = AscendC::VECTOR_REG_WIDTH / sizeof(T);
    uint32_t elementNumPerTile = tileSize / sizeof(T);
    uint32_t tiles = (currentBlockLength + elementNumPerTile - 1) / elementNumPerTile;

    for (uint32_t t = 0; t < tiles; ++t) {
        int64_t offset = t * elementNumPerTile;
        int64_t elems = currentBlockLength - offset;
        if (elems > elementNumPerTile) {
            elems = elementNumPerTile;
        }
        uint32_t bytes = static_cast<uint32_t>(elems * sizeof(T));

        // GM -> UB:把当前 tile 的 x、y 拷入 UB。尾块不是 32B 整数倍时由 DataCopyPad 补全。
        AscendC::DataCopyExtParams copyParams{1, bytes, 0, 0, 0};
        AscendC::DataCopyPadExtParams<T> padParams{false, 0, 0, 0};
        AscendC::DataCopyPad(xLocal, xGm[offset], copyParams, padParams);
        AscendC::DataCopyPad(yLocal, yGm[offset], copyParams, padParams);
        AscendC::PipeBarrier<PIPE_ALL>();

        // Compute:调用 VF,Load -> Add -> Store 都在寄存器上完成。
        uint16_t loopNum = static_cast<uint16_t>((elems + vectorLength - 1) / vectorLength);
        VectorFunctionAdd<T>((__ubuf__ T*)xLocal.GetPhyAddr(), (__ubuf__ T*)yLocal.GetPhyAddr(),
                             (__ubuf__ T*)zLocal.GetPhyAddr(), static_cast<uint32_t>(elems), loopNum);
        AscendC::PipeBarrier<PIPE_ALL>();

        // UB -> GM:把计算结果拷回 Global Memory。
        AscendC::DataCopyPad(zGm[offset], zLocal, copyParams);
        AscendC::PipeBarrier<PIPE_ALL>();
    }
}

// ----------------------------------------------------------------------------
// 3. Host 侧:tiling 参数计算
// ----------------------------------------------------------------------------
std::tuple<int64_t, int64_t, int64_t> calc_tiling_params(int64_t totalLength)
{
    constexpr static int64_t MIN_ELEMS_PER_CORE = 1024;
    auto ascendcPlatform = platform_ascendc::PlatformAscendCManager::GetInstance();
    uint64_t ubSize;
    ascendcPlatform->GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize);
    int64_t coreNum = ascendcPlatform->GetCoreNumAiv();
    int64_t numBlocks = std::min(coreNum, (totalLength + MIN_ELEMS_PER_CORE - 1) / MIN_ELEMS_PER_CORE);
    numBlocks = std::max(numBlocks, static_cast<int64_t>(1));
    int64_t blockLength = (totalLength + numBlocks - 1) / numBlocks;
    // x/y/z 三个单缓冲各占一份 UB,tile 大小取 UB 的 1/4,并保证单个 tile 的
    // blockLen(uint16)不超过 64KB。
    int64_t tileSize = std::min<int64_t>(ubSize / 4, 48 * 1024);
    constexpr int64_t ALIGN_SIZE = 32;
    tileSize = (tileSize / ALIGN_SIZE) * ALIGN_SIZE;
    return std::make_tuple(numBlocks, blockLength, tileSize);
}

int run_vector_add(aclrtStream stream, int64_t numElements)
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> dist(0.0f, 10.0f);

    size_t size = static_cast<size_t>(numElements) * sizeof(float);

    // Host 内存
    std::vector<float> h_A(numElements);
    std::vector<float> h_B(numElements);
    std::vector<float> h_C(numElements);

    for (int64_t i = 0; i < numElements; ++i) {
        h_A[i] = dist(gen);
        h_B[i] = dist(gen);
        h_C[i] = 0.0f;
    }

    // Device 内存 - 使用智能指针管理
    uint8_t* d_A = nullptr;
    uint8_t* d_B = nullptr;
    uint8_t* d_C = nullptr;
    CHECK_ACL(aclrtMalloc((void**)&d_A, size, ACL_MEM_MALLOC_HUGE_FIRST));
    CHECK_ACL(aclrtMalloc((void**)&d_B, size, ACL_MEM_MALLOC_HUGE_FIRST));
    CHECK_ACL(aclrtMalloc((void**)&d_C, size, ACL_MEM_MALLOC_HUGE_FIRST));
    std::unique_ptr<void, AclrtFreeDeleter> d_A_guard(d_A);
    std::unique_ptr<void, AclrtFreeDeleter> d_B_guard(d_B);
    std::unique_ptr<void, AclrtFreeDeleter> d_C_guard(d_C);

    CHECK_ACL(aclrtMemcpy(d_A, size, h_A.data(), size, ACL_MEMCPY_HOST_TO_DEVICE));
    CHECK_ACL(aclrtMemcpy(d_B, size, h_B.data(), size, ACL_MEMCPY_HOST_TO_DEVICE));

    // Kernel Call
    int64_t numBlocks, blockLength, tileSize;
    std::tie(numBlocks, blockLength, tileSize) = calc_tiling_params(numElements);
    vector_function_add_kernel<float><<<numBlocks, nullptr, stream>>>(
        d_A, d_B, d_C, numElements, blockLength, tileSize);
    CHECK_ACL(aclrtSynchronizeStream(stream));

    CHECK_ACL(aclrtMemcpy(h_C.data(), size, d_C, size, ACL_MEMCPY_DEVICE_TO_HOST));
    CHECK_ACL(aclrtSynchronizeStream(stream));

    // 验证结果:浮点加法是精确运算,device 与 host 均按 IEEE 就近舍入,结果逐位一致
    bool success = true;
    int64_t firstErrorIndex = -1;
    for (int64_t i = 0; i < numElements; ++i) {
        if (h_C[i] != h_A[i] + h_B[i]) {
            success = false;
            firstErrorIndex = i;
            break;
        }
    }

    if (success) {
        std::cout << "Vector function add completed successfully!" << std::endl;
    } else {
        std::cout << "Vector function add failed at index " << firstErrorIndex << "!" << std::endl;
    }

    return success ? 0 : 1;
}

int main()
{
    CHECK_ACL(aclInit(nullptr));
    int32_t deviceId = 0;
    CHECK_ACL(aclrtSetDevice(deviceId));
    aclrtStream stream = nullptr;
    CHECK_ACL(aclrtCreateStream(&stream));

    // 409603 不是寄存器宽度(VECTOR_REG_WIDTH / sizeof(float))的整数倍,
    // 用于演示 VF 内部 Mask 对尾块的自适应处理。
    int result = run_vector_add(stream, 409603);

    CHECK_ACL(aclrtDestroyStream(stream));
    CHECK_ACL(aclrtResetDevice(deviceId));
    CHECK_ACL(aclFinalize());

    return result;
}