gob/number/convert.go

487 lines
9.4 KiB
Go
Raw Normal View History

2024-02-17 18:13:24 +00:00
package number
import (
"fmt"
"math/big"
"strconv"
"strings"
"gitea.zaclys.com/bvaudour/gob/option"
)
func undefinedString(sign int) string {
switch sign {
case -1:
return "-∞"
case +1:
return "+∞"
default:
return "NaN"
}
}
func integerString(n *big.Int, base uint, sign int) string {
out := n.Text(int(base))
if sign < 0 {
out = fmt.Sprintf("-%s", out)
}
if base != 10 {
out = fmt.Sprintf("(%d)%s", base, out)
}
return out
}
func fractionString(n, d *big.Int, base uint, sign int) string {
out := fmt.Sprintf("%s/%s", n.Text(int(base)), d.Text(int(base)))
if sign < 0 {
out = fmt.Sprintf("-%s", out)
}
if base != 10 {
out = fmt.Sprintf("(%d)%s", base, out)
}
return out
}
func decimalString(n Number) string {
base := n.Base()
num, denom := n.num(), n.denom()
p := pow(base, FloatingPrecision)
num = num.mul(p)
q := num.quo(denom)
out := "0"
if num.cmp(denom) >= 0 {
if qn, ok := q.get(); ok {
out = qn.Num().Text(int(base))
} else {
return ""
}
}
precision := int(FloatingPrecision)
if len(out) < precision {
out = fmt.Sprintf("%s%s", strings.Repeat("0", precision), out)
}
deltaPrecision := len(out) - precision
out = fmt.Sprintf("%s.%s", out[:deltaPrecision], out[deltaPrecision:])
if !FixedPrecision {
l := len(out) - 1
for out[l] == '0' {
out, l = out[:l], l-1
}
if out == "." {
out = "0."
}
}
if n.IsNeg() {
out = fmt.Sprintf("-%s", out)
} else if q.IsZero() && !n.IsZero() {
out = fmt.Sprintf("+%s", out)
}
if base != 10 {
out = fmt.Sprintf("(%d)%s", base, out)
}
return out
}
func scientificStsring(n Number) string {
base := n.Base()
var exponent int
tmp := n
if n.IsZero() {
num, denom := n.Abs().NumDenom()
nl, dl := num.Len(), denom.Len()
exponent := nl - dl
if exponent > 0 {
denom = denom.Mul(Number{
base: base,
tpe: Integer,
atom: pow(base, exponent),
})
} else if exponent < 0 {
num = num.Mul(Number{
base: base,
tpe: Integer,
atom: pow(base, -exponent),
})
}
if num.Lt(denom) {
exponent--
num.Mul(Int(base, base))
}
tmp = num.Div(denom)
}
out := decimalString(tmp)
signExponent := ""
if exponent > 0 {
signExponent = "+"
} else if exponent < 0 {
signExponent = "-"
}
if base == 10 {
return fmt.Sprintf("%sE%s%d", out, signExponent, exponent)
}
return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent)
}
// String retourne la représentation du nombre sous forme de chaîne de caractères.
func (n Number) String() string {
if nb, ok := n.get(); ok {
switch n.Type() {
case Integer:
return integerString(nb.Num(), n.Base(), n.Sign())
case Fraction:
return fractionString(nb.Num(), nb.Denom(), n.base, n.Sign())
case Scientific:
return scientificStsring(n)
default:
return decimalString(n)
}
}
return undefinedString(n.Sign())
}
func parseBase(str string) (next string, base option.Option[uint]) {
begin := strings.Index(str, "(")
end := strings.Index(str, ")")
if begin > end || begin < 0 || end < 0 {
return
}
next = str[end+1:]
if b, err := strconv.ParseUint(str[begin+1:end], 10, 64); err == nil && isBaseValid(b) {
base = option.Some(uint(b))
}
return
}
func parseBaseN(str string) (next string, base uint) {
next = str[1:]
switch str[0] {
case 'B', 'b':
base = 2
case 'O', 'o':
base = 8
default:
base = 16
}
return
}
func parseSign(str string) (next string, sign int) {
switch str[0] {
case '-':
next, sign = str[1:], -1
case '+':
next, sign = str[1:], 1
default:
next, sign = str, 1
}
return
}
func parseSignN(str string) (next string, sign int) {
next, sign = str[1:], 1
if str[0] == '1' {
sign = -1
}
return
}
func parseNumber(str string, base uint) (n option.Option[*big.Int]) {
if e, ok := new(big.Int).SetString(str, int(base)); ok {
n = option.Some(e)
}
return
}
func setInt(str string, base uint, sign int) (n option.Option[Number]) {
if nb, ok := parseNumber(str, base).Get(); ok {
nn := Number{
base: base,
tpe: Integer,
atom: entire0(nb, sign),
}
n = option.Some(nn.format())
}
return
}
func parseInt(str string) (n option.Option[Number]) {
next, base := parseBase(str)
if b, ok := base.Get(); ok {
next, sign := parseSign(next)
n = setInt(next, b, sign)
}
return
}
func parseInt10(str string) (n option.Option[Number]) {
next, sign := parseSign(str)
return setInt(next, 10, sign)
}
func parseIntN(str string) (n option.Option[Number]) {
next, sign := parseSignN(str)
next, base := parseBaseN(next)
return setInt(next, base, sign)
}
func setFrac(str string, base uint, sign int) (n option.Option[Number]) {
i := strings.Index(str, "/")
if i < 0 {
return
}
sNum, sDenom := str[:i], str[i:]
if num, ok := parseNumber(sNum, base).Get(); ok {
if denom, ok := parseNumber(sDenom, base).Get(); ok {
nn := Number{
base: base,
tpe: Fraction,
atom: frac0(num, denom, sign),
}
n = option.Some(nn.format())
}
}
return
}
func parseFrac(str string) (n option.Option[Number]) {
next, base := parseBase(str)
if b, ok := base.Get(); ok {
next, sign := parseSign(next)
n = setFrac(next, b, sign)
}
return
}
func parseFrac10(str string) (n option.Option[Number]) {
next, sign := parseSign(str)
return setFrac(next, 10, sign)
}
func parseDot(str string) (next string, dot int) {
i := strings.Index(str, ".")
if i >= 0 {
next = str[:i] + str[:i]
dot = len(next) - i
}
return
}
func setDec(str string, base uint, sign int) (n option.Option[Number]) {
next, dot := parseDot(str)
if num, ok := parseNumber(next, base).Get(); ok {
denom := pow(base, dot)
nn := Number{
base: base,
tpe: Decimal,
atom: entire0(num, sign).div(denom),
}
n = option.Some(nn.format())
}
return
}
func parseDec(str string) (n option.Option[Number]) {
next, base := parseBase(str)
if b, ok := base.Get(); ok {
next, sign := parseSign(next)
n = setDec(next, b, sign)
}
return
}
func parseDec10(str string) (n option.Option[Number]) {
next, sign := parseSign(str)
return setDec(next, 10, sign)
}
func parseDecN(str string) (n option.Option[Number]) {
next, sign := parseSignN(str)
next, base := parseBaseN(next)
return setDec(next, base, sign)
}
func parseExp(str string) (next string, baseExponent option.Option[int], exponent option.Option[int]) {
begin := strings.Index(str, "x")
end := strings.Index(str, "^")
if begin > end || begin < 0 || end < 0 {
return
}
sBase, sExponent := str[begin+1:end], str[end+1:]
if b, err := strconv.Atoi(sBase); err == nil && isBaseValid(b) {
if e, err := strconv.Atoi(sExponent); err == nil {
next, baseExponent, exponent = str[:begin], option.Some(b), option.Some(e)
}
}
return
}
func parseExp10(str string) (next string, exponent option.Option[int]) {
i := strings.Index(strings.ToLower(str), "e")
if i < 0 {
return
}
sExponent := str[i+1:]
if e, err := strconv.Atoi(sExponent); err == nil {
next, exponent = str[:i], option.Some(e)
}
return
}
func setSci(str string, base uint, sign, baseExponent, exponent int) (n option.Option[Number]) {
next, dot := parseDot(str)
if num, ok := parseNumber(next, base).Get(); ok {
denom := pow(base, dot)
nn := Number{
base: base,
tpe: Scientific,
atom: entire0(num, sign).div(denom),
}
if exponent > 0 {
nn.atom = nn.atom.mul(pow(baseExponent, exponent))
} else if exponent < 0 {
nn.atom = nn.atom.div(pow(baseExponent, -exponent))
}
n = option.Some(nn.format())
}
return
}
func parseSci(str string) (n option.Option[Number]) {
next, base := parseBase(str)
if b, ok := base.Get(); ok {
next, sign := parseSign(next)
next, be, e := parseExp(next)
if baseExponent, ok := be.Get(); ok {
if exponent, ok := e.Get(); ok {
n = setSci(next, b, sign, baseExponent, exponent)
}
}
}
return
}
func parseSci10(str string) (n option.Option[Number]) {
next, sign := parseSign(str)
next, be, e := parseExp(next)
if baseExponent, ok := be.Get(); ok {
if exponent, ok := e.Get(); ok {
n = setSci(next, 10, sign, baseExponent, exponent)
}
}
return
}
func parseSci10Simple(str string) (n option.Option[Number]) {
next, sign := parseSign(str)
next, e := parseExp10(next)
if exponent, ok := e.Get(); ok {
n = setSci(next, 10, sign, 10, exponent)
}
return
}
// Parse retourne un nombre à partir dune chaîne de caractères.
func Parse(str string) option.Option[Number] {
switch str {
case "+∞", "<inf>", "<+inf>":
return option.Some(Inf())
case "-∞", "<-inf>":
return option.Some(NegInf())
case "NaN", "<NaN>":
return option.Some(Nan())
}
switch {
case regInt.MatchString(str):
return parseInt(str)
case regInt10.MatchString(str):
return parseInt10(str)
case regInt2.MatchString(str), regInt8.MatchString(str), regInt16.MatchString(str):
return parseIntN(str)
case regFrac.MatchString(str):
return parseFrac(str)
case regFrac10.MatchString(str):
return parseFrac10(str)
case regDec.MatchString(str):
return parseInt(str)
case regDec10.MatchString(str):
return parseInt10(str)
case regDec2.MatchString(str), regDec8.MatchString(str), regDec16.MatchString(str):
return parseIntN(str)
case regSci.MatchString(str):
return parseSci(str)
case regSci10.MatchString(str):
return parseSci10(str)
case regSci10Simple.MatchString(str):
return parseSci10Simple(str)
}
return option.None[Number]()
}
func ParseBool(b bool) Number {
if b {
return One()
}
return Zero()
}
func ToBool(n Number) bool {
return !n.IsZero() && !n.IsNan()
}