#include "q_math.h"

#include "coduo_int32_bits.h"

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

#if defined(WINDOWS_BEHAVIOR) && defined(LINUX_BEHAVIOR)
#error "Select only one of WINDOWS_BEHAVIOR or LINUX_BEHAVIOR"
#elif !defined(WINDOWS_BEHAVIOR) && !defined(LINUX_BEHAVIOR)
#error "Select WINDOWS_BEHAVIOR or LINUX_BEHAVIOR"
#endif

#if defined(LINUX_BEHAVIOR)
#include "coduo_x87emu.h"
#endif

#define Q_FLOAT_SIGN_BIT UINT32_C(0x80000000)

/*
 * All six authoritative bodies arithmetic-shift before the first test and
 * count subsequent nonzero shifts:
 *
 *   CoDUOMP.exe                 0x004311b0
 *   uo_cgame_mp_x86.dll        0x30049310
 *   uo_ui_mp_x86.dll           0x400012e0
 *   uo_game_mp_x86.dll         0x20016360
 *   coduo_lnxded               0x0806610e
 *   game.mp.uo.i386.so         0x00039ade
 *
 * ORIGINAL_BINARY_BUG: every negative input eventually reaches -1, whose
 * arithmetic right shift remains -1, so the function does not terminate.
 * Preserve that behavior without relying on implementation-defined signed
 * right shift or undefined counter overflow.
 */
int32_t Q_log2(int32_t value)
{
    uint32_t shifted = coduo_int32_bits(value);
    uint32_t result = 0;

    while ((shifted = coduo_int32_sar_bits(shifted, 1U)) != 0U) {
        result += 1U;
    }

    return coduo_int32_from_bits(result);
}

/*
 * The clamp thresholds and lower-before-upper branch order agree in all six
 * authoritative bodies.  The four Windows bodies return ClampChar through AL
 * and ClampShort through AX.  Both Linux bodies happen to sign-extend the
 * result across EAX, but the i386 System V ABI leaves the upper EAX bits
 * unspecified for signed-char and signed-short returns; this is compiler
 * output variation, not a different source signature:
 *
 *   CoDUOMP.exe                 0x00431200, 0x00431210
 *   uo_cgame_mp_x86.dll        0x30049360, 0x30049370
 *   uo_ui_mp_x86.dll           0x40001330, 0x40001340
 *   uo_game_mp_x86.dll         0x200163b0, 0x200163c0
 *   coduo_lnxded               0x0806618e, 0x080661be
 *   game.mp.uo.i386.so         0x00039b6e, 0x00039b9e
 *
 * The supporting Mac client ClampChar at PEF file offset 0x000dd450 performs
 * the same comparisons and signed-byte in-range conversion.
 */
int8_t ClampChar(int32_t value)
{
    if (value < INT8_MIN) {
        return INT8_MIN;
    }
    if (value > INT8_MAX) {
        return INT8_MAX;
    }
    return (int8_t)value;
}

int16_t ClampShort(int32_t value)
{
    if (value < INT16_MIN) {
        return INT16_MIN;
    }
    if (value > INT16_MAX) {
        return INT16_MAX;
    }
    return (int16_t)value;
}

/*
 * The retained original bodies agree on lane order and operation width:
 *
 *   CoDUOMP.exe                 0x00431300, 0x00431320, 0x00431340
 *   uo_cgame_mp_x86.dll        0x30049460, 0x30049480, 0x300494a0
 *   uo_ui_mp_x86.dll           0x40001430, 0x40001450, 0x40001470
 *   uo_game_mp_x86.dll         0x200164b0, 0x200164d0, 0x200164f0
 *   coduo_lnxded               0x08066366, 0x080663aa, 0x080663ee
 *   game.mp.uo.i386.so         0x00039d97, 0x00039ddb, 0x00039e1f
 *
 * Add and subtract load two binary32 operands, perform one operation, then
 * immediately store binary32 for each lane.  The copy bodies move three raw
 * 32-bit words, in ascending lane order; copying through a local word preserves
 * both the bit payload and the original behavior when vectors alias by a lane.
 */
void _VectorSubtract(const vec3_t first, const vec3_t second, vec3_t result)
{
    result[0] = first[0] - second[0];
    result[1] = first[1] - second[1];
    result[2] = first[2] - second[2];
}

void _VectorAdd(const vec3_t first, const vec3_t second, vec3_t result)
{
    result[0] = first[0] + second[0];
    result[1] = first[1] + second[1];
    result[2] = first[2] + second[2];
}

void _VectorCopy(const vec3_t source, vec3_t destination)
{
    uint32_t component;

    memcpy(&component, &source[0], sizeof(component));
    memcpy(&destination[0], &component, sizeof(component));
    memcpy(&component, &source[1], sizeof(component));
    memcpy(&destination[1], &component, sizeof(component));
    memcpy(&component, &source[2], sizeof(component));
    memcpy(&destination[2], &component, sizeof(component));
}

/*
 * The retained original bodies perform three independent binary32 products
 * and stores in ascending lane order:
 *
 *   CoDUOMP.exe                 0x00431360
 *   uo_cgame_mp_x86.dll        0x300494c0
 *   uo_ui_mp_x86.dll           0x40001490
 *   uo_game_mp_x86.dll         0x20016510
 *   coduo_lnxded               0x0806641d
 *   game.mp.uo.i386.so         0x00039e4e
 *
 * Windows loads scale and multiplies the vector lane from memory.  Linux
 * loads the lane and multiplies scale from memory.  The distinct whole
 * functions preserve that operand order for NaN propagation and x87 status.
 */
#if defined(WINDOWS_BEHAVIOR)
void _VectorScale(const vec3_t vector, float scale, vec3_t result)
{
#if (defined(__i386__) || defined(__x86_64__)) && \
    (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[0])
                         : "m"(scale), "m"(vector[0])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[1])
                         : "m"(scale), "m"(vector[1])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[2])
                         : "m"(scale), "m"(vector[2])
                         : "st", "memory");
#else
    result[0] = (float)((double)scale * (double)vector[0]);
    result[1] = (float)((double)scale * (double)vector[1]);
    result[2] = (float)((double)scale * (double)vector[2]);
#endif
}
#else
void _VectorScale(const vec3_t vector, float scale, vec3_t result)
{
#if EMULATE_X87
    result[0] = x87f_store_f32(
        x87f_mul(x87f_load_f32(vector[0]), x87f_load_f32(scale)));
    result[1] = x87f_store_f32(
        x87f_mul(x87f_load_f32(vector[1]), x87f_load_f32(scale)));
    result[2] = x87f_store_f32(
        x87f_mul(x87f_load_f32(vector[2]), x87f_load_f32(scale)));
#elif (defined(__i386__) || defined(__x86_64__)) && \
      (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[0])
                         : "m"(vector[0]), "m"(scale)
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[1])
                         : "m"(vector[1]), "m"(scale)
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(result[2])
                         : "m"(vector[2]), "m"(scale)
                         : "st", "memory");
#else
    result[0] = vector[0] * scale;
    result[1] = vector[1] * scale;
    result[2] = vector[2] * scale;
#endif
}
#endif

/*
 * The corresponding multiply-add bodies use one unspilled multiply/add chain
 * per lane.  Windows emits FLD scale, FMUL direction, FADD start, FSTP result
 * under the process PC=53 policy.  Linux emits FLD scale, FMUL direction, FLD
 * start, FADDP, FSTP under the server PC=64 policy.  The addresses are the
 * `_VectorScale` addresses above plus 0x20 on Windows, and 0x38 in both Linux
 * binaries (0x08066455 and RVA 0x00039e86).
 */
#if defined(WINDOWS_BEHAVIOR)
void _VectorMA(const vec3_t start, float scale, const vec3_t direction,
               vec3_t result)
{
#if (defined(__i386__) || defined(__x86_64__)) && \
    (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fadds %3\n\t"
                         "fstps %0"
                         : "=m"(result[0])
                         : "m"(scale), "m"(direction[0]), "m"(start[0])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fadds %3\n\t"
                         "fstps %0"
                         : "=m"(result[1])
                         : "m"(scale), "m"(direction[1]), "m"(start[1])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fadds %3\n\t"
                         "fstps %0"
                         : "=m"(result[2])
                         : "m"(scale), "m"(direction[2]), "m"(start[2])
                         : "st", "memory");
#else
    volatile double product;

    product = (double)scale * (double)direction[0];
    result[0] = (float)(product + (double)start[0]);
    product = (double)scale * (double)direction[1];
    result[1] = (float)(product + (double)start[1]);
    product = (double)scale * (double)direction[2];
    result[2] = (float)(product + (double)start[2]);
#endif
}
#else
void _VectorMA(const vec3_t start, float scale, const vec3_t direction,
               vec3_t result)
{
#if EMULATE_X87
    result[0] = x87f_store_f32(x87f_add(
        x87f_mul(x87f_load_f32(scale), x87f_load_f32(direction[0])),
        x87f_load_f32(start[0])));
    result[1] = x87f_store_f32(x87f_add(
        x87f_mul(x87f_load_f32(scale), x87f_load_f32(direction[1])),
        x87f_load_f32(start[1])));
    result[2] = x87f_store_f32(x87f_add(
        x87f_mul(x87f_load_f32(scale), x87f_load_f32(direction[2])),
        x87f_load_f32(start[2])));
#elif (defined(__i386__) || defined(__x86_64__)) && \
      (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "flds %3\n\t"
                         "faddp %%st, %%st(1)\n\t"
                         "fstps %0"
                         : "=m"(result[0])
                         : "m"(scale), "m"(direction[0]), "m"(start[0])
                         : "st", "st(1)", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "flds %3\n\t"
                         "faddp %%st, %%st(1)\n\t"
                         "fstps %0"
                         : "=m"(result[1])
                         : "m"(scale), "m"(direction[1]), "m"(start[1])
                         : "st", "st(1)", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "flds %3\n\t"
                         "faddp %%st, %%st(1)\n\t"
                         "fstps %0"
                         : "=m"(result[2])
                         : "m"(scale), "m"(direction[2]), "m"(start[2])
                         : "st", "st(1)", "memory");
#else
    result[0] = start[0] + scale * direction[0];
    result[1] = start[1] + scale * direction[1];
    result[2] = start[2] + scale * direction[2];
#endif
}
#endif

/*
 * The original Windows bodies enter the x87 domain for each lane:
 *
 *   CoDUOMP.exe                 0x00431850
 *   uo_cgame_mp_x86.dll        0x300499b0
 *   uo_ui_mp_x86.dll           0x40001980
 *   uo_game_mp_x86.dll         0x20016a00
 *
 * Each lane is loaded with FLD, negated with FCHS, and stored with FSTP.  The
 * supporting Mac engine and game-module bodies likewise use LFS/FNEG/STFS.
 */
#if defined(WINDOWS_BEHAVIOR)
void VectorInverse(vec3_t vector)
{
    vector[0] = -vector[0];
    vector[1] = -vector[1];
    vector[2] = -vector[2];
}
#else
/*
 * The original Linux bodies instead toggle the three stored binary32 sign
 * bits with integer XOR, without loading the values into the x87 stack:
 * coduo_lnxded 0x08066a5e and game.mp.uo.i386.so RVA 0x0003a53a.
 */
void VectorInverse(vec3_t vector)
{
    for (int32_t lane = 0; lane < 3; ++lane) {
        uint32_t bits;

        memcpy(&bits, &vector[lane], sizeof(bits));
        bits ^= Q_FLOAT_SIGN_BIT;
        memcpy(&vector[lane], &bits, sizeof(bits));
    }
}
#endif

/*
 * All six authoritative bodies perform four separate binary32 multiplies and
 * stores in ascending lane order.  The x87 operand order differs: Windows
 * loads scale and multiplies each input lane from memory; Linux loads the lane
 * and multiplies scale from memory.  Retaining that source order also preserves
 * each platform's NaN propagation and floating-point status behavior.
 */
#if defined(WINDOWS_BEHAVIOR)
void Vector4Scale(const vec4_t input, float scale, vec4_t output)
{
#if (defined(__i386__) || defined(__x86_64__)) && \
    (defined(__GNUC__) || defined(__clang__))
    /* GCC commutes the plain product even at -O0.  Keep the original Windows
     * scale-first x87 operand order explicit at each independent store. */
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(output[0])
                         : "m"(scale), "m"(input[0])
                         : "st");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(output[1])
                         : "m"(scale), "m"(input[1])
                         : "st");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(output[2])
                         : "m"(scale), "m"(input[2])
                         : "st");
    __asm__ __volatile__("flds %1\n\t"
                         "fmuls %2\n\t"
                         "fstps %0"
                         : "=m"(output[3])
                         : "m"(scale), "m"(input[3])
                         : "st");
#else
    output[0] = scale * input[0];
    output[1] = scale * input[1];
    output[2] = scale * input[2];
    output[3] = scale * input[3];
#endif
}
#else
void Vector4Scale(const vec4_t input, float scale, vec4_t output)
{
    output[0] = input[0] * scale;
    output[1] = input[1] * scale;
    output[2] = input[2] * scale;
    output[3] = input[3] * scale;
}
#endif

/*
 * The authoritative bodies agree on both sentinel bit patterns and reverse
 * lane store order:
 *
 *   CoDUOMP.exe                 0x00433f30
 *   uo_cgame_mp_x86.dll        0x3004c090
 *   uo_ui_mp_x86.dll           0x400040a0
 *   uo_game_mp_x86.dll         0x200190e0
 *   coduo_lnxded               0x0806a164
 *   game.mp.uo.i386.so         0x0003df31
 *
 * The stores are raw dword moves in every authoritative body.  The supporting
 * Mac engine body at PEF file offset 0x000db9d0 loads and stores the same two
 * binary32 values in the same lane order.
 */
void ClearBounds(vec3_t mins, vec3_t maxs)
{
    const uint32_t clearMins = UINT32_C(0x48800000);
    const uint32_t clearMaxs = UINT32_C(0xc8800000);

    memcpy(&mins[2], &clearMins, sizeof(clearMins));
    memcpy(&mins[1], &clearMins, sizeof(clearMins));
    memcpy(&mins[0], &clearMins, sizeof(clearMins));
    memcpy(&maxs[2], &clearMaxs, sizeof(clearMaxs));
    memcpy(&maxs[1], &clearMaxs, sizeof(clearMaxs));
    memcpy(&maxs[0], &clearMaxs, sizeof(clearMaxs));
}

/*
 * All six authoritative AddPointToBounds bodies visit lanes 0, 1, 2, test the
 * minimum before the maximum, and copy an accepted component as a raw dword:
 *
 *   CoDUOMP.exe                 0x00433f50
 *   uo_cgame_mp_x86.dll        0x3004c0b0
 *   uo_ui_mp_x86.dll           0x400040c0
 *   uo_game_mp_x86.dll         0x20019100
 *   coduo_lnxded               0x0806a19f
 *   game.mp.uo.i386.so         0x0003df7b
 *
 * Windows uses signaling FCOMP while Linux uses quiet FUCOMPP.  Both classify
 * a NaN comparison as unordered and leave the bound unchanged; the opcode
 * distinction affects only x87 exception status.  Windows retains the
 * signaling relational operator; native Linux uses the C99 quiet isless
 * predicate, and Linux software-x87 uses the equivalent x87f_lt primitive.
 * Re-reading point for each comparison and copy preserves the original
 * behavior for overlapping arrays.
 */
void AddPointToBounds(const vec3_t point, vec3_t mins, vec3_t maxs)
{
    for (int32_t lane = 0; lane < 3; ++lane) {
        uint32_t component;

#if defined(LINUX_BEHAVIOR) && EMULATE_X87
        if (x87f_lt(x87f_load_f32(point[lane]),
                    x87f_load_f32(mins[lane]))) {
#elif defined(LINUX_BEHAVIOR)
        if (isless(point[lane], mins[lane])) {
#else
        if (point[lane] < mins[lane]) {
#endif
            memcpy(&component, &point[lane], sizeof(component));
            memcpy(&mins[lane], &component, sizeof(component));
        }
#if defined(LINUX_BEHAVIOR) && EMULATE_X87
        if (x87f_lt(x87f_load_f32(maxs[lane]),
                    x87f_load_f32(point[lane]))) {
#elif defined(LINUX_BEHAVIOR)
        if (isless(maxs[lane], point[lane])) {
#else
        if (maxs[lane] < point[lane]) {
#endif
            memcpy(&component, &point[lane], sizeof(component));
            memcpy(&maxs[lane], &component, sizeof(component));
        }
    }
}

/*
 * The four-vector body is ExpandBounds, with source bounds first and the
 * destination bounds second.  That identity and argument order are explicit
 * in both Linux ABIs and the supporting Mac engine symbol/body; the Windows
 * bodies are instruction-identical at:
 *
 *   CoDUOMP.exe                 0x00433fc0
 *   uo_cgame_mp_x86.dll        0x3004c120
 *   uo_ui_mp_x86.dll           0x40004130
 *   uo_game_mp_x86.dll         0x20019170
 *   coduo_lnxded               0x0806a288
 *   game.mp.uo.i386.so         0x0003e064
 *
 * Lanes, minimum-before-maximum order, raw accepted-value copies, overlap
 * behavior, and the Windows FCOMP/Linux FUCOMPP distinction match
 * AddPointToBounds above.
 */
void ExpandBounds(const vec3_t addMins, const vec3_t addMaxs,
                  vec3_t mins, vec3_t maxs)
{
    for (int32_t lane = 0; lane < 3; ++lane) {
        uint32_t component;

#if defined(LINUX_BEHAVIOR) && EMULATE_X87
        if (x87f_lt(x87f_load_f32(addMins[lane]),
                    x87f_load_f32(mins[lane]))) {
#elif defined(LINUX_BEHAVIOR)
        if (isless(addMins[lane], mins[lane])) {
#else
        if (addMins[lane] < mins[lane]) {
#endif
            memcpy(&component, &addMins[lane], sizeof(component));
            memcpy(&mins[lane], &component, sizeof(component));
        }
#if defined(LINUX_BEHAVIOR) && EMULATE_X87
        if (x87f_lt(x87f_load_f32(maxs[lane]),
                    x87f_load_f32(addMaxs[lane]))) {
#elif defined(LINUX_BEHAVIOR)
        if (isless(maxs[lane], addMaxs[lane])) {
#else
        if (maxs[lane] < addMaxs[lane]) {
#endif
            memcpy(&component, &addMaxs[lane], sizeof(component));
            memcpy(&maxs[lane], &component, sizeof(component));
        }
    }
}

/*
 * The retained original bodies agree on matrix layout and lane order:
 *
 *   CoDUOMP.exe                 0x00434030, 0x00434060
 *   uo_cgame_mp_x86.dll        0x3004c190, 0x3004c1c0
 *   uo_ui_mp_x86.dll           0x400041a0, 0x400041d0
 *   uo_game_mp_x86.dll         0x200191e0, 0x20019210
 *   coduo_lnxded               0x0806a371, 0x0806a3df
 *   game.mp.uo.i386.so         0x0003e14d, 0x0003e1bb
 *
 * AxisClear writes binary32 1.0 on the diagonal and positive zero elsewhere.
 * AxisCopy moves nine raw 32-bit words in ascending lane order.  The supporting
 * Mac cgame retains both functions (PEF file offsets 0x15020 and 0x14fc0); Mac
 * UI retains AxisClear (0x570), and the Mac engine retains AxisCopy (0xdb830).
 * Their PowerPC bodies use scalar/pair floating loads and stores but perform no
 * arithmetic.  They produce the same result for disjoint or identical matrices;
 * Linux and Windows establish the ascending raw-word behavior used here.
 */
void AxisClear(axis_t axis)
{
    for (int32_t row = 0; row < 3; ++row) {
        for (int32_t lane = 0; lane < 3; ++lane) {
            const uint32_t component =
                row == lane ? UINT32_C(0x3f800000) : UINT32_C(0);

            memcpy(&axis[row][lane], &component, sizeof(component));
        }
    }
}

void AxisCopy(const axis_t input, axis_t output)
{
    uint32_t component;

    for (int32_t row = 0; row < 3; ++row) {
        for (int32_t lane = 0; lane < 3; ++lane) {
            memcpy(&component, &input[row][lane], sizeof(component));
            memcpy(&output[row][lane], &component, sizeof(component));
        }
    }
}

/*
 * All six authoritative bodies transpose the same nine raw 32-bit words in
 * output row-major order:
 *
 *   CoDUOMP.exe                 0x00432b20
 *   uo_cgame_mp_x86.dll        0x3004ac80
 *   uo_ui_mp_x86.dll           0x40002c50
 *   uo_game_mp_x86.dll         0x20017cd0
 *   coduo_lnxded               0x0806881d
 *   game.mp.uo.i386.so         0x0003c429
 *
 * Each source word is loaded immediately before its destination store.  Keep
 * that progressive order: it preserves the original bit transport, including
 * NaN payloads, and the original overwrite behavior when the matrices alias.
 * The Linux game symbol and the supporting Mac module symbols retain the name
 * MatrixTranspose.
 */
void MatrixTranspose(const axis_t input, axis_t output)
{
    for (int32_t row = 0; row < 3; ++row) {
        for (int32_t lane = 0; lane < 3; ++lane) {
            uint32_t component;

            memcpy(&component, &input[lane][row], sizeof(component));
            memcpy(&output[row][lane], &component, sizeof(component));
        }
    }
}

/*
 * All six authoritative bodies negate the quaternion vector part in ascending
 * lane order and copy the scalar part as one raw binary32 word:
 *
 *   CoDUOMP.exe                 0x00433640
 *   uo_cgame_mp_x86.dll        0x3004b7a0
 *   uo_ui_mp_x86.dll           0x40003770
 *   uo_game_mp_x86.dll         0x200187f0
 *   coduo_lnxded               0x0806972e
 *   game.mp.uo.i386.so         0x0003d381
 *
 * The Linux game dynamic symbol directly preserves the name QuatInverse.
 * Base Quake 3 contains no QuatInverse or QuatConjugate routine that would
 * override that original-symbol evidence; the engine's recovered
 * QuatConjugate name was therefore normalized here.
 *
 * Windows enters the x87 domain separately for lanes 0..2 through
 * FLD/FCHS/FSTP. Linux toggles the stored binary32 sign bit with integer XOR.
 * Keep the whole platform bodies separate for signaling-NaN conversion, x87
 * status, and the original progressive behavior when the arrays overlap.
 */
#if defined(WINDOWS_BEHAVIOR)
void QuatInverse(const vec4_t input, vec4_t output)
{
#if (defined(__i386__) || defined(__x86_64__)) && \
    (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %1\n\t"
                         "fchs\n\t"
                         "fstps %0"
                         : "=m"(output[0])
                         : "m"(input[0])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fchs\n\t"
                         "fstps %0"
                         : "=m"(output[1])
                         : "m"(input[1])
                         : "st", "memory");
    __asm__ __volatile__("flds %1\n\t"
                         "fchs\n\t"
                         "fstps %0"
                         : "=m"(output[2])
                         : "m"(input[2])
                         : "st", "memory");
#else
    /* Supporting non-x86 clients retain their existing normal-value behavior;
     * exact x87 exceptions require the future client EMULATE_X87 wiring. */
    output[0] = -input[0];
    output[1] = -input[1];
    output[2] = -input[2];
#endif
    memcpy(&output[3], &input[3], sizeof(output[3]));
}
#else
void QuatInverse(const vec4_t input, vec4_t output)
{
    for (int32_t lane = 0; lane < 3; ++lane) {
        uint32_t component;

        memcpy(&component, &input[lane], sizeof(component));
        component ^= Q_FLOAT_SIGN_BIT;
        memcpy(&output[lane], &component, sizeof(component));
    }
    memcpy(&output[3], &input[3], sizeof(output[3]));
}
#endif
