// That code is fully based on sqlite-src-3450100/ext/misc/ieee754.c.
// Excerpt from its source code:
//
//     2013-04-17
//
//     The author disclaims copyright to this source code.  In place of
//     a legal notice, here is a blessing:
//
//        May you do good and not evil.
//        May you find forgiveness for yourself and forgive others.
//        May you share freely, never taking more than you give.

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

#include "fp.h"

bool
keksMEToDouble(double *d, int64_t m, int64_t e)
{
    if ((m == 0) && (e == 0)) {
        (*d) = 0.0;
        return true;
    }
    if ((e > 1023) || (e < -1022)) {
        return false;
    }
    bool neg = false;
    uint64_t um = 0;
    if (m < 0) {
        neg = true;
        um = (uint64_t)(-m);
    } else {
        um = (uint64_t)m;
    }
    // if (um == 0 && e > -1000 && e < 1000) {
    //     (*d) = 0.0;
    //     return true;
    // }
    if (um >= 9007199254740992) {
        return false;
    }
    while ((um >> 32) & 0xFFE00000) {
        um >>= 1;
        e++;
    }
    while (um != 0 && ((um >> 32) & 0xFFF00000) == 0) {
        um <<= 1;
        e--;
    }
    e += 1075;
    if (e <= 0) {
        if (1 - e >= 64) { // subnormal
            um = 0;
        } else {
            um >>= (uint64_t)(1 - e);
        }
        e = 0;
    } else if (e > 0x7FF) {
        e = 0x07FF;
    }
    uint64_t a = um & ((((uint64_t)1) << 52) - 1);
    a |= ((uint64_t)e) << 52;
    if (neg) {
        a |= ((uint64_t)1) << 63;
    }
    memcpy(d, &a, sizeof(double));
    return true;
}

bool
keksDoubleToME(int64_t *m, int *e, double d)
{
    switch (fpclassify(d)) {
    case FP_NORMAL:
        break;
    case FP_ZERO:
        (*m) = 0;
        (*e) = 0;
        return true;
    case FP_INFINITE:
    case FP_NAN:
    case FP_SUBNORMAL:
        return false;
    default:
        assert(false);
    }
    bool neg = false;
    if (d < 0.0) {
        neg = true;
        d = -d;
    }
    uint64_t a = 0;
    memcpy(&a, &d, sizeof(a));
    uint64_t _m = 0;
    int _e = 0;
    if (a == 0) {
        _e = 0;
        _m = 0;
    } else {
        _e = a >> 52;
        _m = a & ((((uint64_t)1) << 52) - 1);
        if (_e == 0) {
            _m <<= 1;
        } else {
            _m |= ((uint64_t)1) << 52;
        }
        while (_e < 1075 && _m > 0 && (_m & 1) == 0) {
            _m >>= 1;
            _e++;
        }
    }
    (*m) = neg ? -((int64_t)_m) : (int64_t)_m;
    (*e) = _e - 1075;
    return true;
}
