// SPDX-License-Identifier: MIT
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

#if defined(__x86_64__)
uint64_t _ucomiss_(float a, float b)
{
    uint64_t ret;
    asm volatile (
    "ucomiss %%xmm0, %%xmm1\n"
    "pushf\n"
    "pop %%rax"
    :"=a" (ret)::"xmm0","xmm1","cc");
    return ret;
}
uint64_t _ucomisd_(double a, double b)
{
    uint64_t ret;
    asm volatile (
    "ucomisd %%xmm0, %%xmm1\n"
    "pushf\n"
    "pop %%rax"
    :"=a" (ret)::"xmm0","xmm1","cc");
    return ret;
}
#define GO(n) \
uint64_t _cmpsd_##n##_(double a, double b) \
{ \
    uint64_t ret; \
    asm volatile ( \
    "cmpsd $" #n ", %%xmm1, %%xmm0\n" \
    "movq %%xmm0, %%rax" \
    :"=a" (ret)::"xmm0","xmm1","cc"); \
    return ret; \
}
GO(0)
GO(1)
GO(2)
GO(3)
GO(4)
GO(5)
GO(6)
GO(7)
#undef GO
uint64_t _cvtsd2si_(double a)
{
    uint64_t ret;
    asm volatile (
    "cvtsd2si %%xmm0, %%rax\n"
    :"=a" (ret)::"xmm0","cc");
    return ret;
}
uint32_t _stmxcsr_()
{
    uint32_t ret;
    asm volatile (
    "stmxcsr %[ret];"
    :[ret] "=m" (ret)::"cc");
    return ret;
}
void _ldmxcsr_(uint32_t a)
{
    volatile uint32_t ret = a;
    asm volatile (
    "ldmxcsr %[ret];"
    :[ret] "=m" (ret)::"cc");
}
#else
uint64_t _ucomiss_(float a, float b)
{
    uint32_t ret;
    asm volatile (
    "movss %1, %%xmm0\n"
    "movss %2, %%xmm1\n"
    "ucomiss %%xmm0, %%xmm1\n"
    "pushf\n"
    "pop %%eax"
    :"=a" (ret):"m"(a), "m"(b):"xmm0", "xmm1", "cc");
    return ret;
}
uint64_t _ucomisd_(double a, double b)
{
    uint32_t ret;
    asm volatile (
    "movsd %1, %%xmm0\n"
    "movsd %2, %%xmm1\n"
    "ucomisd %%xmm0, %%xmm1\n"
    "pushf\n"
    "pop %%eax"
    :"=a" (ret):"m"(a), "m"(b):"xmm0", "xmm1", "cc");
    return ret;
}
#endif

void test_ucomiss()
{
 float values[] = {1.0f, 2.0f, 0.0f, -0.0f, -1.0f, INFINITY, -INFINITY, NAN};
 float a, b;
 uint64_t flags;
 int n = sizeof(values)/sizeof(values[0]);
 for(int i=0; i<n; ++i)
    for(int j=0; j<n; ++j) {
        a = values[i];
        b = values[j];
        flags = _ucomiss_(a, b);
        printf("ucomiss %f, %f => 0x%lx\n", a, b, flags);
    }
}

void test_ucomisd()
{
 double values[] = {1.0, 2.0, 0.0, -0.0, -1.0, INFINITY, -INFINITY, NAN};
 double a, b;
 uint64_t flags;
 int n = sizeof(values)/sizeof(values[0]);
 for(int i=0; i<n; ++i)
    for(int j=0; j<n; ++j) {
        a = values[i];
        b = values[j];
        flags = _ucomisd_(a, b);
        printf("ucomisd %f, %f => 0x%lx\n", a, b, flags);
    }
}
#if defined(__x86_64__)
#define GO(n) \
void test_cmpsd_##n() \
{ \
 double a, b; \
 uint64_t flags; \
 a = 1.0; b = 2.0; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 flags = _cmpsd_##n##_(b, a); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", b, a, flags); \
 b = INFINITY; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 flags = _cmpsd_##n##_(b, a); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", b, a, flags); \
 b = -INFINITY; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 flags = _cmpsd_##n##_(b, a); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", b, a, flags); \
 b = NAN; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 flags = _cmpsd_##n##_(b, a); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", b, a, flags); \
 b = a; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 a = b = INFINITY; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
 a = b = NAN; \
 flags = _cmpsd_##n##_(a, b); \
 printf("cmpsd %f, %f, " #n " => 0x%lx\n", a, b, flags); \
}
GO(0)
GO(1)
GO(2)
GO(3)
GO(4)
GO(5)
GO(6)
GO(7)
#undef GO
void test_cvtsd2si()
{  
    uint64_t r;
    volatile double a;
    a = 1.0;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = 1.49;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = 1.50;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = 1.51;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -1.0;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -1.49;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -1.50;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -1.51;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = 1e300;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %g => 0x%lx\n", a, r);
    a = -1e300;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %g => 0x%lx\n", a, r);
    a = INFINITY;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -INFINITY;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = NAN;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
    a = -0.0;
    r = _cvtsd2si_(a);
    printf("cvtsd2si %f => 0x%lx\n", a, r);
}
#define GO(n) \
void test_cvtsd2si_##n() \
{ \
    uint32_t old_mx; \
    old_mx = _stmxcsr_(); \
    uint32_t new_mx = (old_mx & ~0x6000) | (n<<13); \
    _ldmxcsr_(new_mx), \
    printf("MMX RoundMode = %d\n", n); \
    test_cvtsd2si(); \
    _ldmxcsr_(old_mx); \
}
GO(0)
GO(1)
GO(2)
GO(3)
#undef GO
#endif
int main(int argc, const char** argv)
{
 test_ucomiss();
 test_ucomisd();
 #if defined(__x86_64__)
 test_cmpsd_0();
 test_cmpsd_1();
 test_cmpsd_2();
 test_cmpsd_3();
 test_cmpsd_4();
 test_cmpsd_5();
 test_cmpsd_6();
 test_cmpsd_7();
 test_cvtsd2si();
 test_cvtsd2si_0();
 test_cvtsd2si_1();
 test_cvtsd2si_2();
 test_cvtsd2si_3();
 #endif

 return 0;
}
