gob/number/convert.go

266 lines
6.0 KiB
Go
Raw Permalink 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"
)
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 dun 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 dun 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 dun 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 dun 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 dun numérateur et dun 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 dun numérateur et dun 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 dune 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 nest ni NaN ni 0.
func ToBool(n Number) bool { return n.Sign() != 0 }