package number import ( "fmt" "math/big" "strconv" "strings" "gitea.zaclys.com/bvaudour/gob/option" ) // Undefined retourne un nombre indéfini, signé : // - si sign < 0, retourne -∞ // - si sign > 0, retourne +∞ // - sinon, retourne NaN func Undefined[N integer | float](sign N) Number { return Number{ base: 10, tpe: Integer, sign: signOf(sign), } } // Nan retourne NaN. func Nan() Number { return Undefined(0) } // Inf retourne +∞. func Inf() Number { return Undefined(1) } // NegInf retourne -∞. func NegInf() Number { return Undefined(-1) } // IntOf0 retourne un nombre entier à partir d’un entier, et éventuellement une base. func IntOf0(i *big.Int, base ...uint) Number { if i == nil { return Nan() } n := Number{ number: option.Some(new(big.Rat).SetInt(i)), sign: 1, base: formatBase(base...), tpe: Integer, } return n.format() } // IntOf retourne un nombre entier à partir d’un entier, et éventuellement une base. func IntOf[N integer | float](i N, base ...uint) Number { return IntOf0(new(big.Int).SetInt64(int64(i)), base...) } func pow[N integer | float, M integer | float](b N, p ...M) Number { if len(p) > 0 { return IntOf(b).pow(int64(p[0])) } return IntOf(b).pow(int64(FloatingPrecision)) } // Zero retourne le nombre 0. func Zero(base ...uint) Number { return IntOf(0, base...) } // One retourne le nombre 1. func One(base ...uint) Number { return IntOf(1, base...) } // Two retourne le nombre 2. func Two(base ...uint) Number { return IntOf(2, base...) } // DecOf0 retourne un nombre décimal à partir d’un rationnel. func DecOf0(f *big.Rat, base ...uint) Number { if f == nil { return Nan() } n := Number{ number: option.Some(new(big.Rat).Set(f)), sign: 1, base: formatBase(base...), tpe: Decimal, } return n.format() } // DecOf retourne un nombre décimal à partir d’un flottant. func DecOf[N integer | float](f float64, base ...uint) Number { return DecOf0(new(big.Rat).SetFloat64(float64(f))) } // FracOf0 retourne une fraction à partir d’un numérateur et d’un dénominateur entiers. func FracOf0(num, denom *big.Int, base ...uint) Number { if num == nil || denom == nil { return Nan() } n := Number{ number: option.Some(new(big.Rat).SetFrac(num, denom)), sign: 1, base: formatBase(base...), tpe: Fraction, } return n.format() } // FracOf retourne une fraction à partir d’un numérateur et d’un dénominateur entiers. func FracOf[N integer | float](num, denom N, base ...uint) Number { return FracOf0(new(big.Int).SetInt64(int64(num)), new(big.Int).SetInt64(int64(denom)), base...) } func parseBaseN(str string) (next string, base option.Option[uint]) { begin := strings.Index(str, "(") end := strings.Index(str, ")") 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 parseBaseB(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 parseSignN(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 parseSignB(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 { if sign < 0 { nb.Neg(nb) } n = option.Some(IntOf0(nb, base)) } return } func parseInt10(str string) (n option.Option[Number]) { next, sign := parseSignN(str) return setInt(next, 10, sign) } func parseIntB(str string) (n option.Option[Number]) { next, sign := parseSignB(str) next, base := parseBaseB(next) return setInt(next, base, sign) } func parseIntN(str string) (n option.Option[Number]) { next, base := parseBaseN(str) if b, ok := base.Get(); ok { next, sign := parseSignN(next) n = setInt(next, b, sign) } return } // Parse retourne un nombre à partir d’une chaîne de caractères. func Parse(str string) (out 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 regInt10.MatchString(str): return parseInt10(str) case regIntB.MatchString(str): return parseIntB(str) case regIntN.MatchString(str): return parseIntN(str) case regDec.MatchString(str): l := len(str) i := strings.Index(str, ".") dot := l - 1 - i str = fmt.Sprintf("%s%s", str[:i], str[i+1:]) if n, ok := Parse(str).Get(); ok { out = option.Some(n.Div(pow(n.Base(), dot)).ToType(Decimal)) } case regExp10.MatchString(str): i := strings.Index(strings.ToLower(str), "e") if exponent, err := strconv.Atoi(str[i+1:]); err == nil { if n, ok := Parse(str[:i]).Get(); ok { out = option.Some(n.Mul(pow(10, exponent)).ToType(Scientific)) } } case regExpN.MatchString(str): i, j := strings.Index(str, "×"), strings.Index(str, "^") if expBase, err := strconv.ParseUint(str[i+2:j], 10, 64); err == nil && isBaseValid(expBase) { if exponent, err := strconv.Atoi(str[j+1:]); err == nil { if n, ok := Parse(str[:i]).Get(); ok { out = option.Some(n.Mul(pow(expBase, exponent)).ToType(Scientific)) } } } case regFrac.MatchString(str): i := strings.Index(str, "/") num, denom := Parse(str[:i]), Parse(str[i+1:]) if n, ok := num.Get(); ok { if d, ok := denom.Get(); ok { out = option.Some(n.Div(d).ToType(Fraction)) } } } return } // ParseBool retourne 1 si vrai, et 0 sinon. func ParseBool(b bool) Number { if b { return One() } return Zero() } // ToBool retourne vrai si le nombre n’est ni NaN ni 0. func ToBool(n Number) bool { return n.Sign() != 0 }