"""This script generates msgspec/atof_consts.h"""

import math
import os
import textwrap


def gen_hpd_tables():
    log2log10 = math.log(2) / math.log(10)
    shifts = ["0x0000"]
    powers = []
    for i in range(1, 61):
        offset = len(powers)
        assert offset <= 0x07FF
        num_new_digits = int(log2log10 * float(i)) + 1
        assert num_new_digits <= 31
        code = (num_new_digits << 11) | offset
        p = str(5**i)
        powers.extend(p)
        shifts.append("0x%04X" % code)

    for i in range(61, 65):
        shifts.append("0x%04X" % len(powers))

    n_shifts = len(shifts)
    n_powers = len(powers)
    assert n_powers <= 0x07FF

    shifts_str = "\n".join(textwrap.wrap(", ".join(shifts), width=78))
    powers_str = "\n".join(textwrap.wrap(", ".join(powers), width=78))

    return n_shifts, shifts_str, n_powers, powers_str


def gen_row(e):
    z = 1 << 2048
    if e >= 0:
        exp = 10**e
        z = z * exp
    else:
        exp = 10 ** (-e)
        z = z // exp

    n = -2048

    while z >= (1 << 128):
        z = z >> 1
        n += 1

    h = hex(z)[2:]
    assert len(h) == 32

    approx_n = ((217706 * e) >> 16) + 1087
    biased_n = 1214 + n

    assert approx_n == biased_n

    return "{0x%s, 0x%s},  // 1e%-04d" % (h[16:], h[:16], e)


table_rows = [gen_row(e) for e in range(-307, 289)]

f64_powers = [f"1e{i}" for i in range(23)]

n_shifts, shifts, n_powers, powers = gen_hpd_tables()

text = """\
/* DO NOT EDIT - generated by scripts/generate_atof_consts.py */

#ifndef MSGSPEC_ATOF_CONSTS_H
#define MSGSPEC_ATOF_CONSTS_H

static const uint64_t ms_atof_powers_of_10[%d][2] = {
%s
};

static const double ms_atof_f64_powers_of_10[%d] = {
%s
};

static const uint16_t ms_atof_left_shift[%d] = {
%s
};

static const uint8_t ms_atof_powers_of_5[%d] = {
%s
};

#endif
""" % (
    len(table_rows),
    "\n".join(table_rows),
    len(f64_powers),
    "\n".join(textwrap.wrap(", ".join(f64_powers), width=78)),
    n_shifts,
    shifts,
    n_powers,
    powers,
)


if __name__ == "__main__":
    repo = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    path = os.path.join(repo, "src", "msgspec", "atof_consts.h")
    with open(path, "wb") as f:
        f.write(text.encode("utf-8"))
