// Copyright 2026 Siwei Xu, Cangejie porting.
// Copyright 2018 Ulf Adams, original C library.
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.

// Decimal string -> Float64 / Float32 parsing using a Ryu-like algorithm.
// Equivalent to s2d.c and s2f.c. Returns a (Status, value) tuple instead of
// using an output parameter. The input is treated as ASCII bytes.
package ryu

// Parse status codes, mirroring ryu_parse.h's enum Status.
public enum Status {
    SUCCESS
    | INPUT_TOO_SHORT // cjlint-ignore !G.NAM.03 upstream C type name
    | INPUT_TOO_LONG // cjlint-ignore !G.NAM.03 upstream C type name
    | MALFORMED_INPUT // cjlint-ignore !G.NAM.03 upstream C type name
}

let DOUBLE_EXPONENT_BIAS_PARSE: Int32 = 1023 // cjlint-ignore !G.VAR.02
let FLOAT_EXPONENT_BIAS_PARSE: Int32 = 127 // cjlint-ignore !G.VAR.02

// floor(log2(value)) for value >= 1, via binary search (no compiler builtin
// dependency). Returns 64/32 for value == 0 (matches the C fallback), though
// callers never pass 0.
// cjlint-ignore -start !G.NAM.04 upstream C function name
func floor_log2_64(value: UInt64): UInt32 {
    var v = value
    var r: UInt32 = 0
    if (v == 0) {
        return 64
    }
    if (v >= (1u64 << 32)) {
        r = r + 32
        v = v >> 32
    }
    if (v >= (1u64 << 16)) {
        r = r + 16
        v = v >> 16
    }
    if (v >= (1u64 << 8)) {
        r = r + 8
        v = v >> 8
    }
    if (v >= (1u64 << 4)) {
        r = r + 4
        v = v >> 4
    }
    if (v >= (1u64 << 2)) {
        r = r + 2
        v = v >> 2
    }
    if (v >= (1u64 << 1)) {
        r = r + 1
    }
    return r
}

func floor_log2_32(value: UInt32): UInt32 {
    var v = value
    var r: UInt32 = 0
    if (v == 0) {
        return 32
    }
    if (v >= (1u32 << 16)) {
        r = r + 16
        v = v >> 16
    }
    if (v >= (1u32 << 8)) {
        r = r + 8
        v = v >> 8
    }
    if (v >= (1u32 << 4)) {
        r = r + 4
        v = v >> 4
    }
    if (v >= (1u32 << 2)) {
        r = r + 2
        v = v >> 2
    }
    if (v >= (1u32 << 1)) {
        r = r + 1
    }
    return r
}

func max32(a: Int32, b: Int32): Int32 {
    return if (a < b) {
        b
    } else {
        a
    }
}

func double_sign_bit(signedM: Bool): UInt64 {
    return (if (signedM) {
        1u64
    } else {
        0u64
    }) << UInt32(DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)
}

func float_sign_bit(signedM: Bool): UInt32 {
    return (if (signedM) {
        1u32
    } else {
        0u32
    }) << UInt32(FLOAT_EXPONENT_BITS + FLOAT_MANTISSA_BITS)
}

// Parses buffer[0..len) as a Float64. Equivalent to s2d_n().
// cjlint-ignore -start !G.FUN.01 core algorithm function
public func s2d_n(buffer: String, len: Int64): (Status, Float64) {
    if (len == 0) {
        return (Status.INPUT_TOO_SHORT, 0.0)
    }
    var m10digits: Int64 = 0
    var e10digits: Int64 = 0 // cjlint-ignore !G.VAR.02
    var dotIndex = len
    var eIndex = len
    var m10: UInt64 = 0
    var e10: Int32 = 0
    var signedM = false
    var signedE = false
    var i: Int64 = 0
    if (buffer[i] == 45u8) { // '-'
        signedM = true
        i = i + 1
    }
    while (i < len) {
        let c = buffer[i]
        if (c == 46u8) { // '.'
            if (dotIndex != len) {
                return (Status.MALFORMED_INPUT, 0.0)
            }
            dotIndex = i
            i = i + 1
        } else if (c < 48u8 || c > 57u8) {
            break
        } else {
            if (m10digits >= 17) {
                return (Status.INPUT_TOO_LONG, 0.0)
            }
            m10 = 10u64 * m10 + UInt64(c - 48u8)
            if (m10 != 0) {
                m10digits = m10digits + 1
            }
            i = i + 1
        }
    }
    if (i < len && (buffer[i] == 101u8 || buffer[i] == 69u8)) { // 'e' or 'E'
        eIndex = i
        i = i + 1
        if (i < len && (buffer[i] == 45u8 || buffer[i] == 43u8)) { // '-' or '+'
            signedE = (buffer[i] == 45u8)
            i = i + 1
        }
        while (i < len) {
            let c = buffer[i]
            if (c < 48u8 || c > 57u8) {
                return (Status.MALFORMED_INPUT, 0.0)
            }
            if (e10digits > 3) {
                return (Status.INPUT_TOO_LONG, 0.0)
            }
            e10 = 10 * e10 + Int32(c - 48u8)
            if (e10 != 0) {
                e10digits = e10digits + 1
            }
            i = i + 1
        }
    }
    if (i < len) {
        return (Status.MALFORMED_INPUT, 0.0)
    }
    if (signedE) {
        e10 = -e10
    }
    e10 = e10 - Int32(if (dotIndex < eIndex) {
        eIndex - dotIndex - 1
    } else {
        0
    })
    if (m10 == 0) {
        return (Status.SUCCESS, if (signedM) {
                -0.0
            } else {
                0.0
            })
    }

    if (m10digits + Int64(e10) <= -324) {
        return (Status.SUCCESS, bits_to_float64(double_sign_bit(signedM)))
    }
    if (m10digits + Int64(e10) >= 310) {
        return (Status.SUCCESS, bits_to_float64(double_sign_bit(signedM) | (0x7ffu64 << UInt32(DOUBLE_MANTISSA_BITS))))
    }

    var e2: Int32
    var m2: UInt64
    var trailingZeros: Bool
    if (e10 >= 0) {
        e2 = Int32(floor_log2_64(m10)) + e10 + Int32(log2pow5(e10)) - (DOUBLE_MANTISSA_BITS + 1)
        let j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT
        m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[Int64(e10)], j)
        trailingZeros = e2 < e10 || (e2 - e10 < 64 && multipleOfPowerOf2(m10, UInt32(e2 - e10)))
    } else {
        e2 = Int32(floor_log2_64(m10)) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1)
        let j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT
        m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[Int64(-e10)], j)
        trailingZeros = multipleOfPowerOf5(m10, UInt32(-e10))
    }

    var ieeeE2 = UInt32(max32(0, e2 + DOUBLE_EXPONENT_BIAS_PARSE + Int32(floor_log2_64(m2))))
    if (ieeeE2 > 0x7fe) {
        return (Status.SUCCESS, bits_to_float64(double_sign_bit(signedM) | (0x7ffu64 << UInt32(DOUBLE_MANTISSA_BITS))))
    }
    let shift = (if (ieeeE2 == 0) {
        1
    } else {
        Int32(ieeeE2)
    }) - e2 - DOUBLE_EXPONENT_BIAS_PARSE - DOUBLE_MANTISSA_BITS
    trailingZeros = trailingZeros && ((m2 & ((1u64 << UInt32(shift - 1)) - 1u64)) == 0)
    let lastRemovedBit = (m2 >> UInt32(shift - 1)) & 1
    let roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> UInt32(shift)) & 1) != 0))
    var ieeeM2 = (m2 >> UInt32(shift)) + (if (roundUp) {
        1u64
    } else {
        0u64
    })
    ieeeM2 = ieeeM2 & ((1u64 << UInt32(DOUBLE_MANTISSA_BITS)) - 1)
    if (ieeeM2 == 0 && roundUp) {
        ieeeE2 = ieeeE2 + 1
    }
    let signPart = (if (signedM) {
        1u64
    } else {
        0u64
    }) << UInt32(DOUBLE_EXPONENT_BITS)
    let ieee = ((signPart | UInt64(ieeeE2)) << UInt32(DOUBLE_MANTISSA_BITS)) | ieeeM2
    return (Status.SUCCESS, bits_to_float64(ieee))
}

public func s2d(buffer: String): (Status, Float64) {
    return s2d_n(buffer, buffer.size)
}

// Parses buffer[0..len) as a Float32. Equivalent to s2f_n().
public func s2f_n(buffer: String, len: Int64): (Status, Float32) {
    // cjlint-ignore -end upstream C function name
    // cjlint-ignore -end core algorithm function
    if (len == 0) {
        // cjlint-ignore -start !G.EXP.02 float literal for conversion test
        return (Status.INPUT_TOO_SHORT, 0.0f32)
    }
    var m10digits: Int64 = 0
    var e10digits: Int64 = 0 // cjlint-ignore !G.VAR.02

    // cjlint-ignore -end float literal for conversion test
    var dotIndex = len
    var eIndex = len
    var m10: UInt32 = 0
    var e10: Int32 = 0
    var signedM = false
    var signedE = false
    var i: Int64 = 0
    if (buffer[i] == 45u8) { // '-'
        signedM = true
        i = i + 1
    }
    while (i < len) {
        let c = buffer[i]
        if (c == 46u8) { // '.'
            if (dotIndex != len) {
                // cjlint-ignore -start !G.EXP.02 float literal for conversion test
                return (Status.MALFORMED_INPUT, 0.0f32)
            }
            dotIndex = i
            i = i + 1
        } else if (c < 48u8 || c > 57u8) {
            break
        } else {
            if (m10digits >= 9) {
                return (Status.INPUT_TOO_LONG, 0.0f32)
            }
            m10 = 10u32 * m10 + UInt32(c - 48u8)
            if (m10 != 0) {
                m10digits = m10digits + 1
            }
            i = i + 1
        }
    }
    if (i < len && (buffer[i] == 101u8 || buffer[i] == 69u8)) { // 'e' or 'E'
        eIndex = i
        i = i + 1
        if (i < len && (buffer[i] == 45u8 || buffer[i] == 43u8)) { // '-' or '+'
            signedE = (buffer[i] == 45u8)
            i = i + 1
        }
        while (i < len) {
            let c = buffer[i]
            if (c < 48u8 || c > 57u8) {
                return (Status.MALFORMED_INPUT, 0.0f32)
            }
            if (e10digits > 3) {
                return (Status.INPUT_TOO_LONG, 0.0f32)
            }
            e10 = 10 * e10 + Int32(c - 48u8)
            if (e10 != 0) {
                e10digits = e10digits + 1
            }
            i = i + 1
        }
    }
    if (i < len) {
        return (Status.MALFORMED_INPUT, 0.0f32)
    }
    if (signedE) {
        e10 = -e10
    }
    e10 = e10 - Int32(if (dotIndex < eIndex) {
        eIndex - dotIndex - 1
    } else {
        0
    })
    if (m10 == 0) {
        return (Status.SUCCESS, if (signedM) {
                -0.0f32
            } else {
                0.0f32
            })
        // cjlint-ignore -end float literal for conversion test
    }

    if (m10digits + Int64(e10) <= -46) {
        return (Status.SUCCESS, bits_to_float32(float_sign_bit(signedM)))
    }
    if (m10digits + Int64(e10) >= 40) {
        return (Status.SUCCESS, bits_to_float32(float_sign_bit(signedM) | (0xffu32 << UInt32(FLOAT_MANTISSA_BITS))))
    }

    var e2: Int32
    var m2: UInt32
    var trailingZeros: Bool
    if (e10 >= 0) {
        e2 = Int32(floor_log2_32(m10)) + e10 + Int32(log2pow5(e10)) - (FLOAT_MANTISSA_BITS + 1)
        let j = e2 - e10 - ceil_log2pow5(e10) + FLOAT_POW5_BITCOUNT
        m2 = mulPow5divPow2(m10, UInt32(e10), j)
        trailingZeros = e2 < e10 || (e2 - e10 < 32 && multipleOfPowerOf2_32(m10, UInt32(e2 - e10)))
    } else {
        e2 = Int32(floor_log2_32(m10)) + e10 - ceil_log2pow5(-e10) - (FLOAT_MANTISSA_BITS + 1)
        let j = e2 - e10 + ceil_log2pow5(-e10) - 1 + FLOAT_POW5_INV_BITCOUNT
        m2 = mulPow5InvDivPow2(m10, UInt32(-e10), j)
        trailingZeros = (e2 < e10 || (e2 - e10 < 32 && multipleOfPowerOf2_32(m10, UInt32(e2 - e10)))) &&
            multipleOfPowerOf5_32(m10, UInt32(-e10))
    }

    var ieeeE2 = UInt32(max32(0, e2 + FLOAT_EXPONENT_BIAS_PARSE + Int32(floor_log2_32(m2))))
    if (ieeeE2 > 0xfe) {
        return (Status.SUCCESS, bits_to_float32(float_sign_bit(signedM) | (0xffu32 << UInt32(FLOAT_MANTISSA_BITS))))
    }
    let shift = (if (ieeeE2 == 0) {
        1
    } else {
        Int32(ieeeE2)
    }) - e2 - FLOAT_EXPONENT_BIAS_PARSE - FLOAT_MANTISSA_BITS
    trailingZeros = trailingZeros && ((m2 & ((1u32 << UInt32(shift - 1)) - 1u32)) == 0)
    let lastRemovedBit = (m2 >> UInt32(shift - 1)) & 1
    let roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> UInt32(shift)) & 1) != 0))
    var ieeeM2 = (m2 >> UInt32(shift)) + (if (roundUp) {
        1u32
    } else {
        0u32
    })
    ieeeM2 = ieeeM2 & ((1u32 << UInt32(FLOAT_MANTISSA_BITS)) - 1)
    if (ieeeM2 == 0 && roundUp) {
        ieeeE2 = ieeeE2 + 1
    }
    let signPart = (if (signedM) {
        1u32
    } else {
        0u32
    }) << UInt32(FLOAT_EXPONENT_BITS)
    let ieee = ((signPart | ieeeE2) << UInt32(FLOAT_MANTISSA_BITS)) | ieeeM2
    return (Status.SUCCESS, bits_to_float32(ieee))
}

public func s2f(buffer: String): (Status, Float32) {
    return s2f_n(buffer, buffer.size)
}