2024-02-17 18:13:24 +00:00
|
|
|
|
package number
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"math/big"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/option"
|
|
|
|
|
)
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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),
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// Nan retourne NaN.
|
|
|
|
|
func Nan() Number { return Undefined(0) }
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// Inf retourne +∞.
|
|
|
|
|
func Inf() Number { return Undefined(1) }
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// NegInf retourne -∞.
|
|
|
|
|
func NegInf() Number { return Undefined(-1) }
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
n := Number{
|
|
|
|
|
number: option.Some(new(big.Rat).SetInt(i)),
|
|
|
|
|
sign: 1,
|
|
|
|
|
base: formatBase(base...),
|
|
|
|
|
tpe: Integer,
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
return n.format()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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...)
|
|
|
|
|
}
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func pow[N integer | float, M integer | float](b N, p ...M) Number {
|
|
|
|
|
if len(p) > 0 {
|
|
|
|
|
return IntOf(b).pow(int64(p[0]))
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
2024-02-21 09:12:59 +00:00
|
|
|
|
return IntOf(b).pow(int64(FloatingPrecision))
|
|
|
|
|
}
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// Zero retourne le nombre 0.
|
|
|
|
|
func Zero(base ...uint) Number { return IntOf(0, base...) }
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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...) }
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// DecOf0 retourne un nombre décimal à partir d’un rationnel.
|
|
|
|
|
func DecOf0(f *big.Rat, base ...uint) Number {
|
|
|
|
|
if f == nil {
|
|
|
|
|
return Nan()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
n := Number{
|
|
|
|
|
number: option.Some(new(big.Rat).Set(f)),
|
|
|
|
|
sign: 1,
|
|
|
|
|
base: formatBase(base...),
|
|
|
|
|
tpe: Decimal,
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
return n.format()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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)))
|
|
|
|
|
}
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
n := Number{
|
|
|
|
|
number: option.Some(new(big.Rat).SetFrac(num, denom)),
|
|
|
|
|
sign: 1,
|
|
|
|
|
base: formatBase(base...),
|
|
|
|
|
tpe: Fraction,
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
return n.format()
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// 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...)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseBaseN(str string) (next string, base option.Option[uint]) {
|
2024-02-17 18:13:24 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseBaseB(str string) (next string, base uint) {
|
2024-02-17 18:13:24 +00:00
|
|
|
|
next = str[1:]
|
|
|
|
|
switch str[0] {
|
|
|
|
|
case 'B', 'b':
|
|
|
|
|
base = 2
|
|
|
|
|
case 'O', 'o':
|
|
|
|
|
base = 8
|
|
|
|
|
default:
|
|
|
|
|
base = 16
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseSignN(str string) (next string, sign int) {
|
2024-02-17 18:13:24 +00:00
|
|
|
|
switch str[0] {
|
|
|
|
|
case '-':
|
|
|
|
|
next, sign = str[1:], -1
|
|
|
|
|
case '+':
|
|
|
|
|
next, sign = str[1:], 1
|
|
|
|
|
default:
|
|
|
|
|
next, sign = str, 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseSignB(str string) (next string, sign int) {
|
2024-02-17 18:13:24 +00:00
|
|
|
|
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 {
|
2024-02-21 09:12:59 +00:00
|
|
|
|
if sign < 0 {
|
|
|
|
|
nb.Neg(nb)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
2024-02-21 09:12:59 +00:00
|
|
|
|
n = option.Some(IntOf0(nb, base))
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseInt10(str string) (n option.Option[Number]) {
|
2024-02-21 09:12:59 +00:00
|
|
|
|
next, sign := parseSignN(str)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
|
|
|
|
return setInt(next, 10, sign)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseIntB(str string) (n option.Option[Number]) {
|
|
|
|
|
next, sign := parseSignB(str)
|
|
|
|
|
next, base := parseBaseB(next)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
|
|
|
|
|
return setInt(next, base, sign)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func parseIntN(str string) (n option.Option[Number]) {
|
|
|
|
|
next, base := parseBaseN(str)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
if b, ok := base.Get(); ok {
|
2024-02-21 09:12:59 +00:00
|
|
|
|
next, sign := parseSignN(next)
|
|
|
|
|
n = setInt(next, b, sign)
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse retourne un nombre à partir d’une chaîne de caractères.
|
2024-02-21 09:12:59 +00:00
|
|
|
|
func Parse(str string) (out option.Option[Number]) {
|
2024-02-17 18:13:24 +00:00
|
|
|
|
switch str {
|
|
|
|
|
case "+∞", "<inf>", "<+inf>":
|
|
|
|
|
return option.Some(Inf())
|
|
|
|
|
case "-∞", "<-inf>":
|
|
|
|
|
return option.Some(NegInf())
|
|
|
|
|
case "NaN", "<NaN>":
|
|
|
|
|
return option.Some(Nan())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case regInt10.MatchString(str):
|
|
|
|
|
return parseInt10(str)
|
2024-02-21 09:12:59 +00:00
|
|
|
|
case regIntB.MatchString(str):
|
|
|
|
|
return parseIntB(str)
|
|
|
|
|
case regIntN.MatchString(str):
|
2024-02-17 18:13:24 +00:00
|
|
|
|
return parseIntN(str)
|
|
|
|
|
case regDec.MatchString(str):
|
2024-02-21 09:12:59 +00:00
|
|
|
|
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, "^")
|
2024-02-21 13:23:18 +00:00
|
|
|
|
if expBase, err := strconv.ParseUint(str[i+2:j], 10, 64); err == nil && isBaseValid(expBase) {
|
2024-02-21 09:12:59 +00:00
|
|
|
|
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, "/")
|
2024-02-21 13:23:18 +00:00
|
|
|
|
num, denom := Parse(str[:i]), Parse(str[i+1:])
|
2024-02-21 09:12:59 +00:00
|
|
|
|
if n, ok := num.Get(); ok {
|
|
|
|
|
if d, ok := denom.Get(); ok {
|
|
|
|
|
out = option.Some(n.Div(d).ToType(Fraction))
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
return
|
2024-02-17 18:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// ParseBool retourne 1 si vrai, et 0 sinon.
|
2024-02-17 18:13:24 +00:00
|
|
|
|
func ParseBool(b bool) Number {
|
|
|
|
|
if b {
|
|
|
|
|
return One()
|
|
|
|
|
}
|
|
|
|
|
return Zero()
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 09:12:59 +00:00
|
|
|
|
// ToBool retourne vrai si le nombre n’est ni NaN ni 0.
|
|
|
|
|
func ToBool(n Number) bool { return n.Sign() != 0 }
|