// Copyright 2017-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
// This file is a Go translation and modification of MathJax 3.2.2 behavior.

// Package jscompat implements JavaScript numeric behavior that is observable // in MathJax 3.2.2's SVG output.
package jscompat import ( ) // NumberStep forces an intermediate float64 rounding point. It prevents the Go // compiler from fusing operations that JavaScript evaluates separately. // //go:noinline func ( float64) float64 { return } // Round implements ECMAScript Math.round, including negative zero for values // in [-0.5, 0). func ( float64) float64 { if math.IsNaN() || math.IsInf(, 0) || == 0 || math.Abs() >= 1<<52 { return } if >= -0.5 && < 0 { return math.Copysign(0, -1) } return math.Floor( + 0.5) } // TruthyNumber reports JavaScript truthiness for a Number. func ( float64) bool { return != 0 && !math.IsNaN() } // NumberString formats a float64 as ECMAScript String(Number) for the finite // and non-finite values observable in MathJax's serialization paths. func ( float64) string { switch { case math.IsNaN(): return "NaN" case math.IsInf(, 1): return "Infinity" case math.IsInf(, -1): return "-Infinity" case == 0: return "0" } := math.Abs() if >= 1e-6 && < 1e21 { return strconv.FormatFloat(, 'f', -1, 64) } := strconv.FormatFloat(, 'e', -1, 64) , , := strings.Cut(, "e") if ! { return } := "" if strings.HasPrefix(, "+") || strings.HasPrefix(, "-") { , = [:1], [1:] } = strings.TrimLeft(, "0") if == "" { = "0" } return + "e" + + } // ToFixed implements Number.prototype.toFixed for 0 through 100 fraction // digits. Unlike strconv's fixed formatting, ECMAScript resolves an exact tie // toward the larger integer after operating on the Number's exact binary // value. That difference is visible for values such as 2.5. func ( float64, int) string { if < 0 || > 100 { return "RangeError" } if math.IsNaN() || math.IsInf(, 0) { return NumberString() } if math.Abs() >= 1e21 { return NumberString() } := < 0 if { = - } := new(big.Rat).SetFloat64() := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64()), nil) := new(big.Int).Mul(.Num(), ) := .Denom() , := new(big.Int), new(big.Int) .QuoRem(, , ) if new(big.Int).Lsh(, 1).Cmp() >= 0 { .Add(, big.NewInt(1)) } := .String() if > 0 { if len() <= { = strings.Repeat("0", -len()+1) + } := len() - = [:] + "." + [:] } if { = "-" + } return } // Fixed reproduces CommonOutputJax.fixed(): values close to zero collapse to // "0", and trailing fractional zeroes are removed. func ( float64, int) string { if math.Abs() < 0.0006 { return "0" } return trimFixed(ToFixed(, )) } // TrimFixed removes the trailing zeroes and optional decimal point in a // toFixed result, as MathJax does with /\.?0+$/. It is exported for the SVG // output package's other fixed-precision helpers. func ( string) string { return trimFixed() } func trimFixed( string) string { if !strings.Contains(, ".") { return } = strings.TrimRight(, "0") return strings.TrimSuffix(, ".") }