// pow10table writes the table of the powers of ten which the decoder multiplies a mantissa by
// ( internal/decoder/pow10_table.go ).
//
// The power 10^q of every q in [minExp10, maxExp10] is written as a 128-bit significand T and an exponent E,
// such that T * 2^E <= 10^q < (T+1) * 2^E and 2^127 <= T < 2^128: the significand is truncated, never rounded,
// which is what the decoder relies on to bound the product. They are computed by math/big, exactly.
package main

import (
	"bytes"
	"fmt"
	"go/format"
	"math/big"
	"os"
)

const (
	minExp10 = -348
	maxExp10 = 347
)

func main() {
	out := "internal/decoder/pow10_table.go"
	if len(os.Args) > 1 {
		out = os.Args[1]
	}
	var b bytes.Buffer
	fmt.Fprintln(&b, "// Code generated by internal/cmd/pow10table. DO NOT EDIT.")
	fmt.Fprintln(&b)
	fmt.Fprintln(&b, "package decoder")
	fmt.Fprintln(&b)
	fmt.Fprintf(&b, "const (\n\tminPow10Exp10 = %d\n\tmaxPow10Exp10 = %d\n)\n\n", minExp10, maxExp10)
	fmt.Fprintln(&b, "// pow10Table has 10^q as hi*2^64 + lo times 2^exp2, truncated, for q from minPow10Exp10.")
	fmt.Fprintln(&b, "var pow10Table = [...]pow10Entry{")
	mask := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 64), big.NewInt(1))
	for q := minExp10; q <= maxExp10; q++ {
		t, e := significand(q)
		hi := new(big.Int).Rsh(t, 64)
		lo := new(big.Int).And(t, mask)
		fmt.Fprintf(&b, "\t{hi: %#016x, lo: %#016x, exp2: %d}, // 1e%d\n", hi.Uint64(), lo.Uint64(), e, q)
	}
	fmt.Fprintln(&b, "}")
	src, err := format.Source(b.Bytes())
	if err != nil {
		panic(err)
	}
	if err := os.WriteFile(out, src, 0o644); err != nil {
		panic(err)
	}
}

// significand returns T and E such that T * 2^E <= 10^q < (T+1) * 2^E and 2^127 <= T < 2^128.
func significand(q int) (*big.Int, int) {
	ten := big.NewInt(10)
	if q >= 0 {
		p := new(big.Int).Exp(ten, big.NewInt(int64(q)), nil)
		n := p.BitLen()
		if n >= 128 {
			return new(big.Int).Rsh(p, uint(n-128)), n - 128
		}
		return new(big.Int).Lsh(p, uint(128-n)), n - 128
	}
	// 10^q = 1 / 10^-q: T = floor(2^k / 10^-q) with k such that T has 128 bits, and E = -k.
	d := new(big.Int).Exp(ten, big.NewInt(int64(-q)), nil)
	k := 128 + d.BitLen()
	for {
		t := new(big.Int).Quo(new(big.Int).Lsh(big.NewInt(1), uint(k)), d)
		switch n := t.BitLen(); {
		case n > 128:
			k--
		case n < 128:
			k++
		default:
			return t, -k
		}
	}
}
