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 d’une chaîne de caractères. func Parse(str string) option.Option[Number] { switch str { case "+∞", "", "<+inf>": return option.Some(Inf()) case "-∞", "<-inf>": return option.Some(NegInf()) case "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() }