// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// compiler_intrinsics.hpp
// MSVC / GCC 間のコンパイラ intrinsic 互換レイヤー

#ifndef CALX_COMPILER_INTRINSICS_HPP
#define CALX_COMPILER_INTRINSICS_HPP

#include <cstdint>

#ifdef _MSC_VER
#include <intrin.h>
#else

// --- _umul128: 64x64 → 128 bit 乗算 ---
static inline uint64_t _umul128(uint64_t a, uint64_t b, uint64_t* hi) {
    __uint128_t r = (__uint128_t)a * b;
    *hi = (uint64_t)(r >> 64);
    return (uint64_t)r;
}

// --- _addcarry_u64: キャリー付き加算 ---
static inline unsigned char _addcarry_u64(unsigned char carry_in,
                                           uint64_t a, uint64_t b, uint64_t* out) {
    __uint128_t sum = (__uint128_t)a + b + carry_in;
    *out = (uint64_t)sum;
    return (unsigned char)(sum >> 64);
}

// --- _subborrow_u64: ボロー付き減算 ---
static inline unsigned char _subborrow_u64(unsigned char borrow_in,
                                            uint64_t a, uint64_t b, uint64_t* out) {
    __uint128_t diff = (__uint128_t)a - b - borrow_in;
    *out = (uint64_t)diff;
    return (unsigned char)(diff >> 127); // borrow = sign bit
}

// --- _BitScanReverse64: 最上位ビット位置 ---
static inline unsigned char _BitScanReverse64(unsigned long* index, uint64_t mask) {
    if (mask == 0) return 0;
    *index = 63 - __builtin_clzll(mask);
    return 1;
}

// --- _udiv128: 128/64 → 64 除算 ---
static inline uint64_t _udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_t* remainder) {
    __uint128_t dividend = ((__uint128_t)hi << 64) | lo;
    *remainder = (uint64_t)(dividend % divisor);
    return (uint64_t)(dividend / divisor);
}

// --- __shiftleft128: 128-bit 左シフト ---
static inline uint64_t __shiftleft128(uint64_t lo, uint64_t hi, unsigned char shift) {
    if (shift == 0) return hi;
    return (hi << shift) | (lo >> (64 - shift));
}

// --- __shiftright128: 128-bit 右シフト ---
static inline uint64_t __shiftright128(uint64_t lo, uint64_t hi, unsigned char shift) {
    if (shift == 0) return lo;
    return (lo >> shift) | (hi << (64 - shift));
}

// --- __lzcnt64: 先行ゼロカウント ---
static inline uint64_t __lzcnt64(uint64_t x) {
    return x ? __builtin_clzll(x) : 64;
}

// --- _mulx_u64: MULX (carry-less multiply) ---
static inline uint64_t _mulx_u64(uint64_t a, uint64_t b, uint64_t* hi) {
    return _umul128(a, b, hi);
}

// --- _BitScanForward64: 最下位ビット位置 ---
static inline unsigned char _BitScanForward64(unsigned long* index, uint64_t mask) {
    if (mask == 0) return 0;
    *index = __builtin_ctzll(mask);
    return 1;
}

// --- _tzcnt_u64: 末尾ゼロカウント ---
static inline uint64_t _tzcnt_u64(uint64_t x) {
    return x ? __builtin_ctzll(x) : 64;
}

// --- _alloca → alloca ---
#include <alloca.h>
#define _alloca alloca

#endif // _MSC_VER

#endif // CALX_COMPILER_INTRINSICS_HPP
