487 lines
9.4 KiB
Go
487 lines
9.4 KiB
Go
|
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>", "<+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()
|
|||
|
}
|