/* XMRig
 * Copyright (c) 2025 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * SSE to RISC-V Vector (RVV) optimized compatibility header
 * Provides both scalar fallback and vectorized implementations using RVV intrinsics
 */

#ifndef XMRIG_SSE2RVV_OPTIMIZED_H
#define XMRIG_SSE2RVV_OPTIMIZED_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <string.h>

/* Check if RVV is available */
#if defined(__riscv_vector)
#include <riscv_vector.h>
#define USE_RVV_INTRINSICS 1
#else
#define USE_RVV_INTRINSICS 0
#endif

/* 128-bit vector type */
typedef union {
    uint8_t  u8[16];
    uint16_t u16[8];
    uint32_t u32[4];
    uint64_t u64[2];
    int8_t   i8[16];
    int16_t  i16[8];
    int32_t  i32[4];
    int64_t  i64[2];
#if USE_RVV_INTRINSICS
    vuint64m1_t rvv_u64;
    vuint32m1_t rvv_u32;
    vuint8m1_t  rvv_u8;
#endif
} __m128i_union;

typedef __m128i_union __m128i;

/* Set operations */
static inline __m128i _mm_set_epi32(int e3, int e2, int e1, int e0)
{
    __m128i result;
    result.i32[0] = e0;
    result.i32[1] = e1;
    result.i32[2] = e2;
    result.i32[3] = e3;
    return result;
}

static inline __m128i _mm_set_epi64x(int64_t e1, int64_t e0)
{
    __m128i result;
    result.i64[0] = e0;
    result.i64[1] = e1;
    return result;
}

static inline __m128i _mm_setzero_si128(void)
{
    __m128i result;
    memset(&result, 0, sizeof(result));
    return result;
}

/* Extract/insert operations */
static inline int _mm_cvtsi128_si32(__m128i a)
{
    return a.i32[0];
}

static inline int64_t _mm_cvtsi128_si64(__m128i a)
{
    return a.i64[0];
}

static inline __m128i _mm_cvtsi32_si128(int a)
{
    __m128i result = _mm_setzero_si128();
    result.i32[0] = a;
    return result;
}

static inline __m128i _mm_cvtsi64_si128(int64_t a)
{
    __m128i result = _mm_setzero_si128();
    result.i64[0] = a;
    return result;
}

/* Shuffle operations */
static inline __m128i _mm_shuffle_epi32(__m128i a, int imm8)
{
    __m128i result;
    result.u32[0] = a.u32[(imm8 >> 0) & 0x3];
    result.u32[1] = a.u32[(imm8 >> 2) & 0x3];
    result.u32[2] = a.u32[(imm8 >> 4) & 0x3];
    result.u32[3] = a.u32[(imm8 >> 6) & 0x3];
    return result;
}

/* Logical operations - optimized with RVV when available */
static inline __m128i _mm_xor_si128(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vr = __riscv_vxor_vv_u64m1(va, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = a.u64[0] ^ b.u64[0];
    result.u64[1] = a.u64[1] ^ b.u64[1];
    return result;
#endif
}

static inline __m128i _mm_or_si128(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vr = __riscv_vor_vv_u64m1(va, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = a.u64[0] | b.u64[0];
    result.u64[1] = a.u64[1] | b.u64[1];
    return result;
#endif
}

static inline __m128i _mm_and_si128(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vr = __riscv_vand_vv_u64m1(va, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = a.u64[0] & b.u64[0];
    result.u64[1] = a.u64[1] & b.u64[1];
    return result;
#endif
}

static inline __m128i _mm_andnot_si128(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vnot_a = __riscv_vnot_v_u64m1(va, vl);
    vuint64m1_t vr = __riscv_vand_vv_u64m1(vnot_a, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = (~a.u64[0]) & b.u64[0];
    result.u64[1] = (~a.u64[1]) & b.u64[1];
    return result;
#endif
}

/* Shift operations */
static inline __m128i _mm_slli_si128(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result = _mm_setzero_si128();
    int count = imm8 & 0xFF;
    if (count > 15) return result;
    
    size_t vl = __riscv_vsetvl_e8m1(16);
    vuint8m1_t va = __riscv_vle8_v_u8m1(a.u8, vl);
    vuint8m1_t vr = __riscv_vslideup_vx_u8m1(__riscv_vmv_v_x_u8m1(0, vl), va, count, vl);
    __riscv_vse8_v_u8m1(result.u8, vr, vl);
    return result;
#else
    __m128i result = _mm_setzero_si128();
    int count = imm8 & 0xFF;
    if (count > 15) return result;
    
    for (int i = 0; i < 16 - count; i++) {
        result.u8[i + count] = a.u8[i];
    }
    return result;
#endif
}

static inline __m128i _mm_srli_si128(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result = _mm_setzero_si128();
    int count = imm8 & 0xFF;
    if (count > 15) return result;
    
    size_t vl = __riscv_vsetvl_e8m1(16);
    vuint8m1_t va = __riscv_vle8_v_u8m1(a.u8, vl);
    vuint8m1_t vr = __riscv_vslidedown_vx_u8m1(va, count, vl);
    __riscv_vse8_v_u8m1(result.u8, vr, vl);
    return result;
#else
    __m128i result = _mm_setzero_si128();
    int count = imm8 & 0xFF;
    if (count > 15) return result;
    
    for (int i = count; i < 16; i++) {
        result.u8[i - count] = a.u8[i];
    }
    return result;
#endif
}

static inline __m128i _mm_slli_epi64(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    if (imm8 > 63) {
        result.u64[0] = 0;
        result.u64[1] = 0;
    } else {
        size_t vl = __riscv_vsetvl_e64m1(2);
        vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
        vuint64m1_t vr = __riscv_vsll_vx_u64m1(va, imm8, vl);
        __riscv_vse64_v_u64m1(result.u64, vr, vl);
    }
    return result;
#else
    __m128i result;
    if (imm8 > 63) {
        result.u64[0] = 0;
        result.u64[1] = 0;
    } else {
        result.u64[0] = a.u64[0] << imm8;
        result.u64[1] = a.u64[1] << imm8;
    }
    return result;
#endif
}

static inline __m128i _mm_srli_epi64(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    if (imm8 > 63) {
        result.u64[0] = 0;
        result.u64[1] = 0;
    } else {
        size_t vl = __riscv_vsetvl_e64m1(2);
        vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
        vuint64m1_t vr = __riscv_vsrl_vx_u64m1(va, imm8, vl);
        __riscv_vse64_v_u64m1(result.u64, vr, vl);
    }
    return result;
#else
    __m128i result;
    if (imm8 > 63) {
        result.u64[0] = 0;
        result.u64[1] = 0;
    } else {
        result.u64[0] = a.u64[0] >> imm8;
        result.u64[1] = a.u64[1] >> imm8;
    }
    return result;
#endif
}

/* Load/store operations - optimized with RVV */
static inline __m128i _mm_load_si128(const __m128i* p)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t v = __riscv_vle64_v_u64m1((const uint64_t*)p, vl);
    __riscv_vse64_v_u64m1(result.u64, v, vl);
    return result;
#else
    __m128i result;
    memcpy(&result, p, sizeof(__m128i));
    return result;
#endif
}

static inline __m128i _mm_loadu_si128(const __m128i* p)
{
    __m128i result;
    memcpy(&result, p, sizeof(__m128i));
    return result;
}

static inline void _mm_store_si128(__m128i* p, __m128i a)
{
#if USE_RVV_INTRINSICS
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t v = __riscv_vle64_v_u64m1(a.u64, vl);
    __riscv_vse64_v_u64m1((uint64_t*)p, v, vl);
#else
    memcpy(p, &a, sizeof(__m128i));
#endif
}

static inline void _mm_storeu_si128(__m128i* p, __m128i a)
{
    memcpy(p, &a, sizeof(__m128i));
}

/* Arithmetic operations - optimized with RVV */
static inline __m128i _mm_add_epi64(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vr = __riscv_vadd_vv_u64m1(va, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = a.u64[0] + b.u64[0];
    result.u64[1] = a.u64[1] + b.u64[1];
    return result;
#endif
}

static inline __m128i _mm_add_epi32(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e32m1(4);
    vuint32m1_t va = __riscv_vle32_v_u32m1(a.u32, vl);
    vuint32m1_t vb = __riscv_vle32_v_u32m1(b.u32, vl);
    vuint32m1_t vr = __riscv_vadd_vv_u32m1(va, vb, vl);
    __riscv_vse32_v_u32m1(result.u32, vr, vl);
    return result;
#else
    __m128i result;
    for (int i = 0; i < 4; i++) {
        result.i32[i] = a.i32[i] + b.i32[i];
    }
    return result;
#endif
}

static inline __m128i _mm_sub_epi64(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va = __riscv_vle64_v_u64m1(a.u64, vl);
    vuint64m1_t vb = __riscv_vle64_v_u64m1(b.u64, vl);
    vuint64m1_t vr = __riscv_vsub_vv_u64m1(va, vb, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = a.u64[0] - b.u64[0];
    result.u64[1] = a.u64[1] - b.u64[1];
    return result;
#endif
}

static inline __m128i _mm_mul_epu32(__m128i a, __m128i b)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    size_t vl = __riscv_vsetvl_e64m1(2);
    vuint64m1_t va_lo = __riscv_vzext_vf2_u64m1(__riscv_vle32_v_u32mf2(&a.u32[0], 2), vl);
    vuint64m1_t vb_lo = __riscv_vzext_vf2_u64m1(__riscv_vle32_v_u32mf2(&b.u32[0], 2), vl);
    vuint64m1_t vr = __riscv_vmul_vv_u64m1(va_lo, vb_lo, vl);
    __riscv_vse64_v_u64m1(result.u64, vr, vl);
    return result;
#else
    __m128i result;
    result.u64[0] = (uint64_t)a.u32[0] * (uint64_t)b.u32[0];
    result.u64[1] = (uint64_t)a.u32[2] * (uint64_t)b.u32[2];
    return result;
#endif
}

/* Unpack operations */
static inline __m128i _mm_unpacklo_epi64(__m128i a, __m128i b)
{
    __m128i result;
    result.u64[0] = a.u64[0];
    result.u64[1] = b.u64[0];
    return result;
}

static inline __m128i _mm_unpackhi_epi64(__m128i a, __m128i b)
{
    __m128i result;
    result.u64[0] = a.u64[1];
    result.u64[1] = b.u64[1];
    return result;
}

/* Pause instruction for spin-wait loops */
static inline void _mm_pause(void)
{
    /* RISC-V pause hint if available (requires Zihintpause extension) */
#if defined(__riscv_zihintpause)
    __asm__ __volatile__("pause");
#else
    __asm__ __volatile__("nop");
#endif
}

/* Memory fence - optimized for RISC-V */
static inline void _mm_mfence(void)
{
    __asm__ __volatile__("fence rw,rw" ::: "memory");
}

static inline void _mm_lfence(void)
{
    __asm__ __volatile__("fence r,r" ::: "memory");
}

static inline void _mm_sfence(void)
{
    __asm__ __volatile__("fence w,w" ::: "memory");
}

/* Comparison operations */
static inline __m128i _mm_cmpeq_epi32(__m128i a, __m128i b)
{
    __m128i result;
    for (int i = 0; i < 4; i++) {
        result.u32[i] = (a.u32[i] == b.u32[i]) ? 0xFFFFFFFF : 0;
    }
    return result;
}

static inline __m128i _mm_cmpeq_epi64(__m128i a, __m128i b)
{
    __m128i result;
    for (int i = 0; i < 2; i++) {
        result.u64[i] = (a.u64[i] == b.u64[i]) ? 0xFFFFFFFFFFFFFFFFULL : 0;
    }
    return result;
}

/* Additional shift operations */
static inline __m128i _mm_slli_epi32(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    if (imm8 > 31) {
        memset(&result, 0, sizeof(result));
    } else {
        size_t vl = __riscv_vsetvl_e32m1(4);
        vuint32m1_t va = __riscv_vle32_v_u32m1(a.u32, vl);
        vuint32m1_t vr = __riscv_vsll_vx_u32m1(va, imm8, vl);
        __riscv_vse32_v_u32m1(result.u32, vr, vl);
    }
    return result;
#else
    __m128i result;
    if (imm8 > 31) {
        for (int i = 0; i < 4; i++) result.u32[i] = 0;
    } else {
        for (int i = 0; i < 4; i++) {
            result.u32[i] = a.u32[i] << imm8;
        }
    }
    return result;
#endif
}

static inline __m128i _mm_srli_epi32(__m128i a, int imm8)
{
#if USE_RVV_INTRINSICS
    __m128i result;
    if (imm8 > 31) {
        memset(&result, 0, sizeof(result));
    } else {
        size_t vl = __riscv_vsetvl_e32m1(4);
        vuint32m1_t va = __riscv_vle32_v_u32m1(a.u32, vl);
        vuint32m1_t vr = __riscv_vsrl_vx_u32m1(va, imm8, vl);
        __riscv_vse32_v_u32m1(result.u32, vr, vl);
    }
    return result;
#else
    __m128i result;
    if (imm8 > 31) {
        for (int i = 0; i < 4; i++) result.u32[i] = 0;
    } else {
        for (int i = 0; i < 4; i++) {
            result.u32[i] = a.u32[i] >> imm8;
        }
    }
    return result;
#endif
}

/* 64-bit integer operations */
static inline __m128i _mm_set1_epi64x(int64_t a)
{
    __m128i result;
    result.i64[0] = a;
    result.i64[1] = a;
    return result;
}

/* Float type for compatibility */
typedef __m128i __m128;

/* Float operations - simplified scalar implementations */
static inline __m128 _mm_set1_ps(float a)
{
    __m128 result;
    uint32_t val;
    memcpy(&val, &a, sizeof(float));
    for (int i = 0; i < 4; i++) {
        result.u32[i] = val;
    }
    return result;
}

static inline __m128 _mm_setzero_ps(void)
{
    __m128 result;
    memset(&result, 0, sizeof(result));
    return result;
}

static inline __m128 _mm_add_ps(__m128 a, __m128 b)
{
    __m128 result;
    float fa[4], fb[4], fr[4];
    memcpy(fa, &a, sizeof(__m128));
    memcpy(fb, &b, sizeof(__m128));
    for (int i = 0; i < 4; i++) {
        fr[i] = fa[i] + fb[i];
    }
    memcpy(&result, fr, sizeof(__m128));
    return result;
}

static inline __m128 _mm_mul_ps(__m128 a, __m128 b)
{
    __m128 result;
    float fa[4], fb[4], fr[4];
    memcpy(fa, &a, sizeof(__m128));
    memcpy(fb, &b, sizeof(__m128));
    for (int i = 0; i < 4; i++) {
        fr[i] = fa[i] * fb[i];
    }
    memcpy(&result, fr, sizeof(__m128));
    return result;
}

static inline __m128 _mm_and_ps(__m128 a, __m128 b)
{
    __m128 result;
    result.u64[0] = a.u64[0] & b.u64[0];
    result.u64[1] = a.u64[1] & b.u64[1];
    return result;
}

static inline __m128 _mm_or_ps(__m128 a, __m128 b)
{
    __m128 result;
    result.u64[0] = a.u64[0] | b.u64[0];
    result.u64[1] = a.u64[1] | b.u64[1];
    return result;
}

static inline __m128 _mm_cvtepi32_ps(__m128i a)
{
    __m128 result;
    float fr[4];
    for (int i = 0; i < 4; i++) {
        fr[i] = (float)a.i32[i];
    }
    memcpy(&result, fr, sizeof(__m128));
    return result;
}

static inline __m128i _mm_cvttps_epi32(__m128 a)
{
    __m128i result;
    float fa[4];
    memcpy(fa, &a, sizeof(__m128));
    for (int i = 0; i < 4; i++) {
        result.i32[i] = (int32_t)fa[i];
    }
    return result;
}

/* Casting operations */
static inline __m128 _mm_castsi128_ps(__m128i a)
{
    __m128 result;
    memcpy(&result, &a, sizeof(__m128));
    return result;
}

static inline __m128i _mm_castps_si128(__m128 a)
{
    __m128i result;
    memcpy(&result, &a, sizeof(__m128));
    return result;
}

/* Additional set operations */
static inline __m128i _mm_set1_epi32(int a)
{
    __m128i result;
    for (int i = 0; i < 4; i++) {
        result.i32[i] = a;
    }
    return result;
}

/* AES instructions - placeholders for soft_aes compatibility */
static inline __m128i _mm_aesenc_si128(__m128i a, __m128i roundkey)
{
    return _mm_xor_si128(a, roundkey);
}

static inline __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon)
{
    return a;
}

/* Rotate right operation for soft_aes.h */
static inline uint32_t _rotr(uint32_t value, unsigned int count)
{
    const unsigned int mask = 31;
    count &= mask;
    return (value >> count) | (value << ((-count) & mask));
}

/* ARM NEON compatibility types and intrinsics for RISC-V */
typedef __m128i_union uint64x2_t;
typedef __m128i_union uint8x16_t;
typedef __m128i_union int64x2_t;
typedef __m128i_union int32x4_t;

static inline uint64x2_t vld1q_u64(const uint64_t *ptr)
{
    uint64x2_t result;
    result.u64[0] = ptr[0];
    result.u64[1] = ptr[1];
    return result;
}

static inline int64x2_t vld1q_s64(const int64_t *ptr)
{
    int64x2_t result;
    result.i64[0] = ptr[0];
    result.i64[1] = ptr[1];
    return result;
}

static inline void vst1q_u64(uint64_t *ptr, uint64x2_t val)
{
    ptr[0] = val.u64[0];
    ptr[1] = val.u64[1];
}

static inline uint64x2_t veorq_u64(uint64x2_t a, uint64x2_t b)
{
    return _mm_xor_si128(a, b);
}

static inline uint64x2_t vaddq_u64(uint64x2_t a, uint64x2_t b)
{
    return _mm_add_epi64(a, b);
}

static inline uint64x2_t vreinterpretq_u64_u8(uint8x16_t a)
{
    uint64x2_t result;
    memcpy(&result, &a, sizeof(uint64x2_t));
    return result;
}

static inline uint64_t vgetq_lane_u64(uint64x2_t v, int lane)
{
    return v.u64[lane];
}

static inline int64_t vgetq_lane_s64(int64x2_t v, int lane)
{
    return v.i64[lane];
}

static inline int32_t vgetq_lane_s32(int32x4_t v, int lane)
{
    return v.i32[lane];
}

typedef struct { uint64_t val[1]; } uint64x1_t;

static inline uint64x1_t vcreate_u64(uint64_t a)
{
    uint64x1_t result;
    result.val[0] = a;
    return result;
}

static inline uint64x2_t vcombine_u64(uint64x1_t low, uint64x1_t high)
{
    uint64x2_t result;
    result.u64[0] = low.val[0];
    result.u64[1] = high.val[0];
    return result;
}

#ifdef __cplusplus
}
#endif

#endif /* XMRIG_SSE2RVV_OPTIMIZED_H */
