Réécrituer de number

This commit is contained in:
Benjamin VAUDOUR 2024-02-21 10:12:59 +01:00
parent 2177ea9449
commit 5b437644bd
6 changed files with 885 additions and 1186 deletions

View File

@ -1,502 +0,0 @@
package number
import (
"math/big"
"gitea.zaclys.com/bvaudour/gob/option"
)
type atom struct {
number option.Option[*big.Rat]
sign int
}
func undefined[S integer | float](sign S) atom {
out := atom{
sign: signOf(sign),
}
return out.format()
}
func rational[S integer | float](rat *big.Rat, sign ...S) atom {
s := 1
if len(sign) > 0 {
s = signOf(sign[0])
}
out := atom{
number: option.Some(rat),
sign: s,
}
return out.format()
}
func frac0[S integer | float](num, denom *big.Int, sign ...S) atom {
if denom.IsInt64() && denom.Int64() == 0 {
s := num.Sign()
if len(sign) > 0 {
s *= signOf(sign[0])
}
return undefined(s)
}
rat := new(big.Rat).SetFrac(num, denom)
return rational(rat, sign...)
}
func frac[N integer | float, S integer | float](num, denom N, sign ...S) atom {
if denom == 0 {
s := signOf(num)
if len(sign) > 0 {
s *= signOf(sign[0])
}
return undefined(s)
}
rat := new(big.Rat).SetFrac64(int64(num), int64(denom))
return rational(rat, sign...)
}
func entire0[S integer | float](n *big.Int, sign ...S) atom {
rat := new(big.Rat).SetInt(n)
return rational(rat, sign...)
}
func entire[N integer | float, S integer | float](n N, sign ...S) atom {
rat := new(big.Rat).SetInt64(int64(n))
return rational(rat, sign...)
}
func decimal[N integer | float, S integer | float](n N, sign ...S) atom {
rat := new(big.Rat).SetFloat64(float64(n))
return rational(rat, sign...)
}
func pow[B integer | float, P integer | float](base B, precision ...P) atom {
p := FloatingPrecision
if len(precision) > 0 {
p = uint64(precision[0])
}
a := entire(formatBase(base), 1)
return a.pow(p)
}
func (a atom) get() (*big.Rat, bool) {
return a.number.Get()
}
func (a *atom) format() atom {
if nb, ok := a.get(); ok {
a.sign *= nb.Sign()
nb.Abs(nb)
}
a.sign = signOf(a.sign)
return *a
}
func (a atom) clone(format ...bool) atom {
out := atom{
sign: a.sign,
}
if nb, ok := a.get(); ok {
out.number = option.Some(new(big.Rat).Set(nb))
}
if len(format) > 0 && format[0] {
return out.format()
}
return out
}
func (a atom) rat() (nb *big.Rat, ok bool) {
if nb, ok = a.get(); ok {
nb = new(big.Rat).Set(nb)
if a.sign < 0 {
nb.Neg(nb)
} else if a.sign == 0 {
nb.SetInt64(0)
}
}
return
}
func (a atom) Sign() int {
return a.sign
}
func (a atom) IsNeg() bool {
return a.Sign() < 0
}
func (a atom) IsPos() bool {
return !a.IsNeg()
}
func (a atom) IsZero() bool {
return a.Is(0)
}
func (a atom) neg() atom {
out := a.clone()
out.sign = -out.sign
return out.format()
}
func (a atom) abs() atom {
out := a.clone()
if out.sign < 0 {
out.sign = -out.sign
}
return out.format()
}
func (a atom) IsDefined() bool {
return a.number.IsDefined()
}
func (a atom) IsInf() bool {
return !a.IsDefined() && a.Sign() > 0
}
func (a atom) IsNegInf() bool {
return !a.IsDefined() && a.Sign() < 0
}
func (a atom) IsNan() bool {
return !a.IsDefined() && a.Sign() == 0
}
func (a atom) Is(i int64) bool {
if nb, ok := a.get(); ok {
return nb.IsInt() && nb.Num().Int64()*int64(a.Sign()) == i
}
return false
}
func (a atom) IsFloat(f float64) bool {
if nb, ok := a.get(); ok {
e, ok := nb.Float64()
return ok && e*float64(a.Sign()) == f
}
return false
}
func (a atom) isInt() bool {
if nb, ok := a.get(); ok {
return nb.IsInt()
}
return false
}
func (a atom) isInt64() bool {
if nb, ok := a.get(); ok {
return nb.IsInt() && nb.Num().IsInt64()
}
return false
}
func (a atom) toInt64() (n int64, ok bool) {
var nb *big.Rat
if nb, ok = a.get(); ok {
if nb.IsInt() {
if num := nb.Num(); num.IsInt64() {
n = num.Int64()
}
}
}
return
}
func (a *atom) setInt() atom {
if nb, ok := a.get(); ok {
nb.SetInt(new(big.Int).Quo(nb.Num(), nb.Denom()))
}
return a.format()
}
func (a atom) num() atom {
if a.Sign() == 0 {
return entire(0, 0)
} else if nb, ok := a.get(); ok {
return entire0(nb.Num(), a.Sign())
}
return entire(a.Sign(), 1)
}
func (a atom) denom() atom {
if nb, ok := a.get(); ok {
return entire0(nb.Denom(), 1)
}
return entire(0, 0)
}
func (a1 atom) cmp(a2 atom) int {
if a1.IsNan() {
if a2.IsNan() {
return 0
}
return -2
} else if a2.IsNan() {
return 2
} else if nb1, ok := a1.rat(); ok {
if nb2, ok := a2.rat(); ok {
return nb1.Cmp(nb2)
}
return -a2.Sign()
} else if a2.IsDefined() {
return a1.Sign()
}
return compare(a1.Sign(), a2.Sign())
}
func (a1 atom) add(a2 atom) atom {
if r1, ok := a1.rat(); ok {
if r2, ok := a2.rat(); ok {
return rational(new(big.Rat).Add(r1, r2), 1)
}
return a2.clone()
} else if a2.IsDefined() || a1.Sign() == a2.Sign() {
return undefined(a1.Sign())
}
return undefined(0)
}
func (a1 atom) sub(a2 atom) atom {
return a1.add(a2.neg())
}
func (a atom) inc() atom {
return a.add(entire(1, 1))
}
func (a atom) dec() atom {
return a.sub(entire(1, 1))
}
func (a1 atom) mul(a2 atom) atom {
s := a1.Sign() * a2.Sign()
if nb1, ok := a1.get(); ok {
if nb2, ok := a2.get(); ok {
return rational(new(big.Rat).Mul(nb1, nb2), s)
}
}
return undefined(s)
}
func (a1 atom) div(a2 atom) atom {
if nb1, ok := a1.get(); ok {
if nb2, ok := a2.get(); ok {
if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
return undefined(0)
}
return rational(new(big.Rat).Quo(nb1, nb2), a1.Sign()*a2.Sign())
}
return undefined(a1.Sign() * a2.Sign())
} else if a2.IsDefined() {
return undefined(a1.Sign() * a2.Sign())
}
return undefined(0)
}
func (a1 atom) quo(a2 atom) atom {
if nb1, ok := a1.get(); ok {
if nb2, ok := a2.get(); ok {
if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
return undefined(0)
}
n := new(big.Int).Mul(nb1.Num(), nb2.Num())
d := new(big.Int).Mul(nb1.Denom(), nb2.Denom())
return entire0(new(big.Int).Quo(n, d), a1.Sign()*a2.Sign())
}
return undefined(a1.Sign() * a2.Sign())
} else if a2.IsDefined() {
return undefined(a1.Sign() * a2.Sign())
}
return undefined(0)
}
func (a1 atom) quoRem(a2 atom) (q, r atom) {
q = a1.quo(a2)
r = a1.sub(a2.mul(q))
return
}
func (a1 atom) rem(a2 atom) atom {
_, r := a1.quoRem(a2)
return r
}
func (a atom) inv() atom {
if nb, ok := a.get(); ok {
n, d := nb.Num(), nb.Denom()
return frac0(d, n, a.Sign())
}
if a.Sign() == 0 {
return undefined(0)
}
return entire(0, 0)
}
func (a atom) pow(p uint64) atom {
switch {
case p == 0:
if a.IsNan() {
return undefined(0)
}
return entire(0, 0)
case p == 1:
return a.clone(true)
case p == 2:
return a.mul(a)
case p&1 == 0:
return a.mul(a).pow(p >> 1)
default:
return a.mul(a).pow(p >> 1).mul(a)
}
}
func (a atom) optimize(precision atom) atom {
if a.isInt() || !a.IsDefined() {
return a
}
n1, d1 := a.num().abs(), a.denom()
t := n1.mul(precision).quo(d1)
an := t.div(precision)
n2, d2 := an.num(), an.denom()
if n2.cmp(n1) < 0 || d2.cmp(d1) < 0 {
t.sign = a.sign
return t.format()
}
return a
}
func (a atom) heron(n uint64, tmp atom) atom {
n0 := entire(int64(n), 1)
n1 := n0.dec()
return ((tmp.mul(n1)).add(a.div(tmp.pow(n - 1)))).div(n0)
}
func (a atom) sqrtn(n uint64, base uint) atom {
switch {
case a.IsNan() || n < 2 || (a.IsNeg() && n&1 == 0):
return undefined(0)
case a.IsZero() || a.Is(1) || !a.IsDefined():
return a.clone(true)
default:
precision := pow(base, FloatingPrecision)
pi := precision.inv()
out := a.heron(n, a).optimize(precision)
for {
t := a.heron(n, out).optimize(precision)
if out.cmp(t) == 0 {
break
} else if out.sub(t).abs().cmp(pi) < 0 {
out = t
break
}
}
return out
}
}
func (a atom) IsEven() bool {
if nb, ok := a.get(); ok {
return nb.IsInt() && nb.Num().Bit(0) == 0
}
return false
}
func (a atom) IsOdd() bool {
if nb, ok := a.get(); ok {
return nb.IsInt() && nb.Num().Bit(0) != 0
}
return false
}
func (a atom) lsh(n uint) atom {
if nb, ok := a.get(); ok {
if nb.IsInt() {
num, denom := new(big.Int).Lsh(nb.Num(), n), nb.Denom()
return frac0(num, denom, a.Sign())
}
return a.mul(pow(2, n))
}
return undefined(a.Sign())
}
func (a atom) rsh(n uint) atom {
if nb, ok := a.get(); ok {
if nb.IsInt() {
num, denom := new(big.Int).Rsh(nb.Num(), n), nb.Denom()
return frac0(num, denom, a.Sign())
}
return a.div(pow(2, n))
}
return undefined(a.Sign())
}
func (a atom) len(base uint) int {
if nb, ok := a.get(); ok {
if !nb.IsInt() {
return -1
}
num := nb.Num()
if num.IsInt64() && num.Int64() == 0 {
return 1
}
s := num.Text(int(base))
return len(s)
}
return -1
}
func (a atom) bit(n uint64, base uint) int {
if !a.isInt() {
return -1
}
out := a
if n > 0 {
out = a.quo(pow(base, n))
}
out = out.rem(entire(base, 1))
if nb, ok := out.get(); ok {
return int(nb.Num().Int64())
}
return -1
}

View File

@ -7,42 +7,56 @@ import (
var ( var (
FloatingPrecision uint64 = 10 // Nombre de chiffres après la virgule pour un nombre décimal ou un nombre scientifique FloatingPrecision uint64 = 10 // Nombre de chiffres après la virgule pour un nombre décimal ou un nombre scientifique
FixedPrecision = false // Si vrai le nombre chiffre après la virgule est fixe. FixedPrecision = false // Si vrai le nombre de chiffres après la virgule est fixe.
) )
const ( const (
regSign = `\+|-` rSign = `(\+|-)`
regBSign = `0|1` rBSign = `(0|1)`
regBase = `\(\d+\)` rNb2 = `(0|1)`
regBase2 = `B|b` rNb8 = `[0-7]`
regBase8 = `O|o` rNb10 = `\d`
regBase16 = `X|x` rNb16 = `[0-9a-fA-F]`
regNb = `[0-9a-zA-Z]` rNbN = `[0-9a-zA-Z]`
regNb2 = `0|1` rBase2 = `(B|b)`
regNb8 = `[0-7]` rBase8 = `(O|o)`
regNb10 = `\d` rBase16 = `(X|x)`
regNb16 = `[0-9a-fA-F]` rExp10 = `E|e`
regExp = `×\d+\^` rExpN = `×\d+\^`
regExp10 = `E|e`
) )
var ( var (
regInt = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+`, regBase, regSign, regNb)) rInt2 = fmt.Sprintf(`(%s)%s+`, rBase2, rNb2)
regInt2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase2, regNb2)) rInt8 = fmt.Sprintf(`(%s)%s+`, rBase8, rNb8)
regInt8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase8, regNb8)) rInt10 = fmt.Sprintf(`(%s)%s+`, rSign, rNb10)
regInt10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+`, regSign, regNb10)) rInt16 = fmt.Sprintf(`(%s)%s+`, rBase8, rNb8)
regInt16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase16, regNb16)) rIntN = fmt.Sprintf(`\(%s+\)%s?%s+`, rNb10, rSign, rNbN)
rIntB = fmt.Sprintf(`%s(%s|%s|%s)`, rBSign, rInt2, rInt8, rInt16)
rInt = fmt.Sprintf(`(%s|%s|%s)`, rInt10, rIntN, rIntB)
regDec = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.)`, regBase, regSign, regNb, regNb, regNb)) rDec2 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase2, rNb2, rNb2, rNb2)
regDec2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase2, regNb2, regNb2, regNb2)) rDec8 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase8, rNb8, rNb8, rNb8)
regDec8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase8, regNb8, regNb8, regNb8)) rDec10 = fmt.Sprintf(`%s?(%s+\.%s*|\.%s+)`, rSign, rNb10, rNb10, rNb10)
regDec10 = regexp.MustCompile(fmt.Sprintf(`(%s)?(%s*\.%s+|%s+\.)`, regSign, regNb10, regNb10, regNb10)) rDec16 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase16, rNb16, rNb16, rNb16)
regDec16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase16, regNb16, regNb16, regNb16)) rDecN = fmt.Sprintf(`\(%s+\)%s?(%s+\.%s*|\.%s+)`, rNb10, rSign, rNbN, rNbN, rNbN)
rDecB = fmt.Sprintf(`%s(%s|%s|%s)`, rBSign, rDec2, rDec8, rDec16)
regFrac = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+/(%s)?%s+`, regBase, regSign, regNb, regSign, regNb)) rDec = fmt.Sprintf(`(%s|%s|%s)`, rDec10, rDecN, rDecB)
regFrac10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+/(%s)?%s+`, regSign, regNb10, regSign, regNb10)) rExponent10 = fmt.Sprintf(`(%s|%s)(%s)%s?%s+`, rInt10, rDec10, rExp10, rSign, rNb10)
rExponentN = fmt.Sprintf(`(%s|%s)%s%s?%s+`, rInt, rDec, rExpN, rSign, rNb10)
regSci = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)%s(%s)?(%s)+`, regBase, regSign, regNb, regNb, regNb, regExp, regSign, regNb10)) rAll10 = fmt.Sprintf(`(%s|%s)((%s)%s?%s+)?`, rInt10, rDec10, regExp10, rSign, rNb10)
regSci10Simple = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp, regSign, regNb10)) rAllN = fmt.Sprintf(`(%s|%s)(%s%s?%s+)?`, rInt, rDec, rExpN, rSign, rNb10)
regSci10 = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp10, regSign, regNb10)) rAll = fmt.Sprintf(`(%s|%s)`, rAll10, rAllN)
)
var (
regInt10 = regexp.MustCompile(fmt.Sprintf(`^%s$`, rInt10))
regIntN = regexp.MustCompile(fmt.Sprintf(`^%s$`, rIntN))
regIntB = regexp.MustCompile(fmt.Sprintf(`^%s$`, rIntB))
regDec = regexp.MustCompile(fmt.Sprintf(`^%s$`, rDec))
regExp10 = regexp.MustCompile(fmt.Sprintf(`^%s$`, rExponent10))
regExpN = regexp.MustCompile(fmt.Sprintf(`^%s$`, rExpN))
regFrac = regexp.MustCompile(fmt.Sprintf(`^%s/%s$`, rAll, rAll))
) )

View File

@ -9,160 +9,109 @@ import (
"gitea.zaclys.com/bvaudour/gob/option" "gitea.zaclys.com/bvaudour/gob/option"
) )
func undefinedString(sign int) string { // Undefined retourne un nombre indéfini, signé :
switch sign { // - si sign < 0, retourne -∞
case -1: // - si sign > 0, retourne +∞
return "-∞" // - sinon, retourne NaN
case +1: func Undefined[N integer | float](sign N) Number {
return "+∞" return Number{
default: base: 10,
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, tpe: Integer,
atom: pow(base, exponent), sign: signOf(sign),
}) }
} else if exponent < 0 { }
num = num.Mul(Number{
base: base, // 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 dun 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, tpe: Integer,
atom: pow(base, -exponent),
})
} }
if num.Lt(denom) { return n.format()
exponent--
num.Mul(Int(base, base))
}
tmp = num.Div(denom)
} }
out := decimalString(tmp) // IntOf retourne un nombre entier à partir dun entier, et éventuellement une base.
func IntOf[N integer | float](i N, base ...uint) Number {
signExponent := "" return IntOf0(new(big.Int).SetInt64(int64(i)), base...)
if exponent > 0 {
signExponent = "+"
} else if exponent < 0 {
signExponent = "-"
} }
if base == 10 { func pow[N integer | float, M integer | float](b N, p ...M) Number {
return fmt.Sprintf("%sE%s%d", out, signExponent, exponent) if len(p) > 0 {
return IntOf(b).pow(int64(p[0]))
} }
return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent) return IntOf(b).pow(int64(FloatingPrecision))
} }
// String retourne la représentation du nombre sous forme de chaîne de caractères. // Zero retourne le nombre 0.
func (n Number) String() string { func Zero(base ...uint) Number { return IntOf(0, base...) }
if nb, ok := n.get(); ok {
switch n.Type() { // One retourne le nombre 1.
case Integer: func One(base ...uint) Number { return IntOf(1, base...) }
return integerString(nb.Num(), n.Base(), n.Sign())
case Fraction: // Two retourne le nombre 2.
return fractionString(nb.Num(), nb.Denom(), n.base, n.Sign()) func Two(base ...uint) Number { return IntOf(2, base...) }
case Scientific:
return scientificStsring(n) // DecOf0 retourne un nombre décimal à partir dun rationnel.
default: func DecOf0(f *big.Rat, base ...uint) Number {
return decimalString(n) if f == nil {
} return Nan()
} }
return undefinedString(n.Sign()) n := Number{
number: option.Some(new(big.Rat).Set(f)),
sign: 1,
base: formatBase(base...),
tpe: Decimal,
} }
func parseBase(str string) (next string, base option.Option[uint]) { return n.format()
}
// 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)))
}
// 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()
}
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 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...)
}
func parseBaseN(str string) (next string, base option.Option[uint]) {
begin := strings.Index(str, "(") begin := strings.Index(str, "(")
end := strings.Index(str, ")") end := strings.Index(str, ")")
if begin > end || begin < 0 || end < 0 {
return
}
next = str[end+1:] next = str[end+1:]
if b, err := strconv.ParseUint(str[begin+1:end], 10, 64); err == nil && isBaseValid(b) { if b, err := strconv.ParseUint(str[begin+1:end], 10, 64); err == nil && isBaseValid(b) {
@ -171,7 +120,7 @@ func parseBase(str string) (next string, base option.Option[uint]) {
return return
} }
func parseBaseN(str string) (next string, base uint) { func parseBaseB(str string) (next string, base uint) {
next = str[1:] next = str[1:]
switch str[0] { switch str[0] {
case 'B', 'b': case 'B', 'b':
@ -185,7 +134,7 @@ func parseBaseN(str string) (next string, base uint) {
return return
} }
func parseSign(str string) (next string, sign int) { func parseSignN(str string) (next string, sign int) {
switch str[0] { switch str[0] {
case '-': case '-':
next, sign = str[1:], -1 next, sign = str[1:], -1
@ -198,7 +147,7 @@ func parseSign(str string) (next string, sign int) {
return return
} }
func parseSignN(str string) (next string, sign int) { func parseSignB(str string) (next string, sign int) {
next, sign = str[1:], 1 next, sign = str[1:], 1
if str[0] == '1' { if str[0] == '1' {
sign = -1 sign = -1
@ -217,226 +166,40 @@ func parseNumber(str string, base uint) (n option.Option[*big.Int]) {
func setInt(str string, base uint, sign int) (n option.Option[Number]) { func setInt(str string, base uint, sign int) (n option.Option[Number]) {
if nb, ok := parseNumber(str, base).Get(); ok { if nb, ok := parseNumber(str, base).Get(); ok {
nn := Number{ if sign < 0 {
base: base, nb.Neg(nb)
tpe: Integer,
atom: entire0(nb, sign),
} }
n = option.Some(nn.format()) n = option.Some(IntOf0(nb, base))
}
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 return
} }
func parseInt10(str string) (n option.Option[Number]) { func parseInt10(str string) (n option.Option[Number]) {
next, sign := parseSign(str) next, sign := parseSignN(str)
return setInt(next, 10, sign) return setInt(next, 10, sign)
} }
func parseIntN(str string) (n option.Option[Number]) { func parseIntB(str string) (n option.Option[Number]) {
next, sign := parseSignN(str) next, sign := parseSignB(str)
next, base := parseBaseN(next) next, base := parseBaseB(next)
return setInt(next, base, sign) return setInt(next, base, sign)
} }
func setFrac(str string, base uint, sign int) (n option.Option[Number]) { func parseIntN(str string) (n option.Option[Number]) {
i := strings.Index(str, "/") next, base := parseBaseN(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 { if b, ok := base.Get(); ok {
next, sign := parseSign(next) next, sign := parseSignN(next)
n = setFrac(next, b, sign) n = setInt(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 return
} }
// Parse retourne un nombre à partir dune chaîne de caractères. // Parse retourne un nombre à partir dune chaîne de caractères.
func Parse(str string) option.Option[Number] { func Parse(str string) (out option.Option[Number]) {
switch str { switch str {
case "+∞", "<inf>", "<+inf>": case "+∞", "<inf>", "<+inf>":
return option.Some(Inf()) return option.Some(Inf())
@ -447,33 +210,50 @@ func Parse(str string) option.Option[Number] {
} }
switch { switch {
case regInt.MatchString(str):
return parseInt(str)
case regInt10.MatchString(str): case regInt10.MatchString(str):
return parseInt10(str) return parseInt10(str)
case regInt2.MatchString(str), regInt8.MatchString(str), regInt16.MatchString(str): case regIntB.MatchString(str):
return parseIntB(str)
case regIntN.MatchString(str):
return parseIntN(str) return parseIntN(str)
case regFrac.MatchString(str):
return parseFrac(str)
case regFrac10.MatchString(str):
return parseFrac10(str)
case regDec.MatchString(str): case regDec.MatchString(str):
return parseInt(str) l := len(str)
case regDec10.MatchString(str): i := strings.Index(str, ".")
return parseInt10(str) dot := l - 1 - i
case regDec2.MatchString(str), regDec8.MatchString(str), regDec16.MatchString(str): str = fmt.Sprintf("%s%s", str[:i], str[i+1:])
return parseIntN(str) if n, ok := Parse(str).Get(); ok {
case regSci.MatchString(str): out = option.Some(n.Div(pow(n.Base(), dot)).ToType(Decimal))
return parseSci(str) }
case regSci10.MatchString(str): case regExp10.MatchString(str):
return parseSci10(str) i := strings.Index(strings.ToLower(str), "e")
case regSci10Simple.MatchString(str): if exponent, err := strconv.Atoi(str[i+1:]); err == nil {
return parseSci10Simple(str) 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+1: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:])
if n, ok := num.Get(); ok {
if d, ok := denom.Get(); ok {
out = option.Some(n.Div(d).ToType(Fraction))
}
}
} }
return option.None[Number]() return
} }
// ParseBool retourne 1 si vrai, et 0 sinon.
func ParseBool(b bool) Number { func ParseBool(b bool) Number {
if b { if b {
return One() return One()
@ -481,6 +261,5 @@ func ParseBool(b bool) Number {
return Zero() return Zero()
} }
func ToBool(n Number) bool { // ToBool retourne vrai si le nombre nest ni NaN ni 0.
return !n.IsZero() && !n.IsNan() func ToBool(n Number) bool { return n.Sign() != 0 }
}

View File

@ -1,5 +1,13 @@
package number package number
import (
"fmt"
"math/big"
"strings"
"gitea.zaclys.com/bvaudour/gob/option"
)
// NumberType représente le type dun nombre. // NumberType représente le type dun nombre.
type NumberType uint type NumberType uint
@ -12,299 +20,585 @@ const (
// Number représente un nombre. // Number représente un nombre.
type Number struct { type Number struct {
atom number rat
sign int
tpe NumberType tpe NumberType
base uint base uint
} }
// Undefined retourne un nombre indéfini, signé : func (n *Number) get() (r *big.Rat, ok bool) {
// - si sign < 0, retourne -∞ return n.number.Get()
// - si sign > 0, retourne +∞ }
// - sinon, retourne NaN
func Undefined(sign int) Number { func (n *Number) rat() (r *big.Rat, ok bool) {
return Number{ if r, ok := n.get(); ok {
base: 10, r = new(big.Rat).Set(r)
tpe: Integer, if n.Sign() < 0 {
atom: undefined(sign), r = r.Neg(r)
} }
} }
// Nan retourne NaN. return
func Nan() Number { return Undefined(0) }
// Inf retourne +∞.
func Inf() Number { return Undefined(1) }
// NegInf retourne -∞.
func NegInf() Number { return Undefined(-1) }
// Int retourne un nombre à partir dun entier, et éventuellement une base.
func Int[N integer](n N, base ...uint) Number {
return Number{
base: formatBase(base...),
tpe: Integer,
atom: entire(n, 1),
}
} }
// Zero retourne le nombre 0. func (n *Number) format() Number {
func Zero(base ...uint) Number { return Int(0, base...) } n.base, n.sign, n.tpe = formatBase(n.base), signOf(n.sign), min(n.tpe, Fraction)
// One retourne le nombre 1. if nb, ok := n.get(); ok {
func One(base ...uint) Number { return Int(1, base...) } n.sign *= nb.Sign()
if n.tpe == Integer && !nb.IsInt() {
// Two retourne le nombre 2. num, denom := nb.Num(), nb.Denom()
func Two(base ...uint) Number { return Int(2, base...) } nb.SetInt(num.Quo(num, denom))
// Float retourne un nombre à partir dun flottant, et éventuellement, une base.
func Float[N ~float32 | ~float64](n N, base ...uint) Number {
return Number{
base: formatBase(base...),
tpe: Decimal,
atom: decimal(n, 1),
} }
} nb.Abs(nb)
} else {
// Frac retourne un nombre à partir du numérateur, du dénominateur, et éventuellement, une base.
func Frac[N integer](num N, denom N, base ...uint) Number {
return Number{
base: formatBase(base...),
tpe: Fraction,
atom: frac(num, denom, 1),
}
}
func (n Number) set(a atom, format ...bool) Number {
out := Number{
atom: a,
tpe: n.tpe,
base: n.base,
}
return out.format(format...)
}
func (n *Number) format(formatAtom ...bool) Number {
if len(formatAtom) > 0 && formatAtom[0] {
n.atom.format()
}
n.base = formatBase(n.base)
if !n.IsDefined() {
n.base = 10
n.tpe = Integer n.tpe = Integer
} else if n.tpe == Integer && !n.isInt() {
n.setInt()
} }
return *n return *n
} }
func (n Number) clone(format ...bool) Number { func (n Number) set(r *big.Rat, tpe NumberType, sign ...int) Number {
s := 1
if len(sign) > 0 {
s = sign[0]
}
out := Number{ out := Number{
atom: n.atom.clone(format...), base: n.Base(),
tpe: tpe,
sign: s,
number: option.Some(r),
}
return out.format()
}
// Clone crée une copie profonde du nombre.
func (n Number) Clone() Number {
out := Number{
sign: n.sign,
tpe: n.tpe, tpe: n.tpe,
base: n.base, base: n.base,
} }
if len(format) > 0 && format[0] { if nb, ok := n.get(); ok {
return out.format() out.number = option.Some(new(big.Rat).Set(nb))
} }
return out return out
} }
// Type retourne le type de nombre. // Base retourne la base utilisée pour laffichage du nombre.
func (n Number) Type() NumberType { func (n Number) Base() uint { return n.base }
return n.tpe
}
// ToType convertit le nombre au type donné. // Type retourne le type de nombre (entier, décimal, fraction ou scientifique).
func (n Number) ToType(t NumberType) Number { func (n Number) Type() NumberType { return n.tpe }
out := n.clone()
switch t {
case Integer, Decimal, Fraction, Scientific:
out.tpe = t
default:
out.tpe = Decimal
}
return out.format(true) // Sign retourne :
} // - 0 si n = 0 ou NaN
// - 1 si n > 0
// Base retourne la base du nombre. // - -1 si n < 0
func (n Number) Base() uint { func (n Number) Sign() int { return n.sign }
return n.base
}
// ToBase convertit le nombre dans la base donnée. // ToBase convertit le nombre dans la base donnée.
func (n Number) ToBase(base uint) Number { func (n Number) ToBase(base uint) Number {
out := n.clone(true) out := n.Clone()
out.base = formatBase(base) out.base = formatBase(base)
return out return out
} }
// Neg retourne -n. // ToType convertit le nombre dans le type donné. La base est conservée, sauf si une base est fournie.
func (n Number) Neg() Number { func (n Number) ToType(tpe NumberType, base ...uint) Number {
return n.set(n.neg()) out := n.Clone()
out.tpe = tpe
out = out.format()
if len(base) > 0 {
return out.ToBase(base[0])
}
return out
} }
// Abs retourne |n|. // IsDefined retourne vrai si le nombre est défini.
func (n Number) Abs() Number { func (n Number) IsDefined() bool { return n.number.IsDefined() }
return n.set(n.abs())
}
// IsInt retourne vrai si le nombre est entier. // IsInt retourne vrai si le nombre est entier.
func (n Number) IsInt() bool { func (n Number) IsInt() bool {
return n.isInt() nb, ok := n.get()
return ok && nb.IsInt()
}
// IsInt64 retourne vrai si le nombre est convertible en int64.
func (n Number) IsInt64() bool {
nb, ok := n.get()
return ok && nb.IsInt() && nb.Num().IsInt64()
}
// IsUint64 retourne vrai si le nombre est convertible en uint64.
func (n Number) IsUint64() bool {
nb, ok := n.get()
return ok && nb.IsInt() && nb.Num().IsUint64()
}
// Is retourne vrai si n = i.
func (n Number) Is(i int64) bool {
if nb, ok := n.get(); ok && nb.IsInt() {
num := nb.Num()
return num.IsInt64() && num.Int64() == i
}
return false
}
// IsUint retourne vrai si n = u.
func (n Number) IsUint(u uint64) bool {
if nb, ok := n.get(); ok && nb.IsInt() {
num := nb.Num()
return num.IsUint64() && num.Uint64() == u
}
return false
}
// IsFloat retourne vrai si n = f.
func (n Number) IsFloat(f float64) bool {
if nb, ok := n.get(); ok {
nf, ok := nb.Float64()
return nf == f && ok
}
return false
}
// IsNeg retourne vrai si n < 0.
func (n Number) IsNeg() bool { return n.Sign() < 0 }
// IsPos retourne vrai si n > 0.
func (n Number) IsPos() bool { return !n.IsNeg() }
// IsZero retourne vrai si n = 0.
func (n Number) IsZero() bool { return n.IsDefined() && n.Sign() == 0 }
// IsInf retourne vrai si n = +∞.
func (n Number) IsInf() bool { return !n.IsDefined() && n.Sign() > 0 }
// IsNegInf retourne vrai si n = -∞.
func (n Number) IsNegInf() bool { return !n.IsDefined() && n.Sign() < 0 }
// IsNan retourne vrai si n = NaN.
func (n Number) IsNan() bool { return !n.IsDefined() && n.Sign() == 0 }
// ToInt64 retourne le nombre converti en int64, et vrai si la conversion a réussi.
func (n Number) ToInt64() (i int64, ok bool) {
var nb *big.Rat
if nb, ok = n.get(); ok {
if ok = nb.IsInt(); ok {
num := nb.Num()
if ok = num.IsInt64(); ok {
i = num.Int64()
}
}
}
return
}
// ToUint64 retourne le nombre converti en uint64, et vrai si la conversion a réussi.
func (n Number) ToUint64() (u uint64, ok bool) {
var nb *big.Rat
if nb, ok = n.get(); ok {
if ok = nb.IsInt(); ok {
num := nb.Num()
if ok = num.IsUint64(); ok {
u = num.Uint64()
}
}
}
return
}
// IsEven retourne vrai si n est entier et pair.
func (n Number) IsEven() bool {
if nb, ok := n.get(); ok {
return nb.IsInt() && nb.Num().Bit(0) == 0
}
return false
}
// IsOdd retourne vrai si n est entier et impair.
func (n Number) IsOdd() bool {
if nb, ok := n.get(); ok {
return nb.IsInt() && nb.Num().Bit(0) != 0
}
return false
} }
// Num retourne le numérateur. // Num retourne le numérateur.
func (n Number) Num() Number { func (n Number) Num() Number {
out := Number{ if n.Sign() == 0 {
atom: n.num(), return IntOf(0, n.Base())
base: n.base, } else if nb, ok := n.rat(); ok {
tpe: Integer, return IntOf0(nb.Num(), n.Base())
} }
return out.format() return IntOf(n.Sign(), n.Base())
} }
// Denom retourne le dénominateur. // Denom retourne le dénominateur.
func (n Number) Denom() Number { func (n Number) Denom() Number {
out := Number{ if nb, ok := n.get(); ok {
atom: n.denom(), return IntOf0(nb.Denom(), n.Base())
base: n.base,
tpe: Integer,
} }
return out.format() return IntOf(0, n.Base())
} }
// Num retourne le numérateur et le dénominateur. // NumDenom retourne le numérateur et le dénominateur.
func (n Number) NumDenom() (Number, Number) { func (n Number) NumDenom() (Number, Number) { return n.Num(), n.Denom() }
return n.Num(), n.Denom()
// Neg retourne -n.
func (n Number) Neg() Number {
out := n.Clone()
out.sign = -out.sign
return out
}
// Abs retourne |n|.
func (n Number) Abs() Number {
out := n.Clone()
out.sign = abs(out.sign)
return out
} }
// Cmp retourne : // Cmp retourne :
// - 1 si n1 > n2
// - -1 si n1 < n2 // - -1 si n1 < n2
// - +1 si n1 > n2 // - 0 si n1 = n2
// - 0 sinon. // - 2 ou -2 si les nombres ne sont pas comparables (ie. n1 = NaN ou n2 = NaN)
func (n1 Number) Cmp(n2 Number) int { func (n1 Number) Cmp(n2 Number) int {
return n1.cmp(n2.atom) if n1.IsNan() {
if n2.IsNan() {
return 0
}
return -2
} else if n2.IsNan() {
return 2
} else if nb1, ok := n1.rat(); ok {
if nb2, ok := n2.rat(); ok {
return nb1.Cmp(nb2)
}
return -n2.Sign()
} else if n2.IsDefined() {
return n1.Sign()
} }
return compare(n1.Sign(), n2.Sign())
}
// Eq retourne vrai si n1 = n2.
func (n1 Number) Eq(n2 Number) bool { return n1.Cmp(n2) == 0 } func (n1 Number) Eq(n2 Number) bool { return n1.Cmp(n2) == 0 }
// Ne retourne vrai si n1 ≠ n2.
func (n1 Number) Ne(n2 Number) bool { return n1.Cmp(n2) != 0 } func (n1 Number) Ne(n2 Number) bool { return n1.Cmp(n2) != 0 }
// Gt retourne vrai si n1 > n2.
func (n1 Number) Gt(n2 Number) bool { return n1.Cmp(n2) > 0 } func (n1 Number) Gt(n2 Number) bool { return n1.Cmp(n2) > 0 }
// Lt retourne vrai si n1 < n2.
func (n1 Number) Lt(n2 Number) bool { return n1.Cmp(n2) < 0 } func (n1 Number) Lt(n2 Number) bool { return n1.Cmp(n2) < 0 }
// Ge retourne vrai si n1 ≥ n2.
func (n1 Number) Ge(n2 Number) bool { return n1.Cmp(n2) >= 0 } func (n1 Number) Ge(n2 Number) bool { return n1.Cmp(n2) >= 0 }
// Le retourne vrai si n1 ≤ n2.
func (n1 Number) Le(n2 Number) bool { return n1.Cmp(n2) <= 0 } func (n1 Number) Le(n2 Number) bool { return n1.Cmp(n2) <= 0 }
// Add retourne n1 + n2. // Add retourne n1 + n2.
func (n1 Number) Add(n2 Number) Number { func (n1 Number) Add(n2 Number) Number {
out := Number{ if r1, ok := n1.rat(); ok {
base: n1.base, if r2, ok := n2.rat(); ok {
tpe: max(n1.tpe, n2.tpe), return n1.set(new(big.Rat).Add(r1, r2), max(n1.Type(), n2.Type()))
atom: n1.add(n2.atom), }
return n2.Clone()
} else if n2.IsDefined() || n1.Sign() == n2.Sign() {
return Undefined(n1.Sign())
} }
return out.format() return Nan()
} }
// Sub retourne n1 - n2. // Sub retourne n1 n2.
func (n1 Number) Sub(n2 Number) Number { func (n1 Number) Sub(n2 Number) Number { return n1.Add(n2.Neg()) }
out := Number{
base: n1.base,
tpe: max(n1.tpe, n2.tpe),
atom: n1.sub(n2.atom),
}
return out.format()
}
// Inc retourne n + 1. // Inc retourne n + 1.
func (n Number) Inc() Number { func (n Number) Inc() Number { return n.Add(One()) }
return n.Add(One())
}
// Dec retourne n 1. // Dec retourne n 1.
func (n Number) Dec() Number { func (n Number) Dec() Number { return n.Sub(One()) }
return n.Sub(One())
}
// Mul retourne n1 × n2. // Mul retourne n1 × n2.
func (n1 Number) Mul(n2 Number) Number { func (n1 Number) Mul(n2 Number) Number {
out := Number{ s := n1.Sign() * n2.Sign()
base: n1.base, if nb1, ok := n1.get(); ok {
tpe: max(n1.tpe, n2.tpe), if nb2, ok := n2.get(); ok {
atom: n1.mul(n2.atom), return n1.set(new(big.Rat).Mul(nb1, nb2), max(n1.Type(), n2.Type()), s)
}
} }
return out.format() return Undefined(s)
} }
// Div retourne n1 ÷ n2 (division exacte). // Div retourne n1 / n2 (division exacte).
func (n1 Number) Div(n2 Number) Number { func (n1 Number) Div(n2 Number) Number {
out := Number{ s := n1.Sign() * n2.Sign()
base: n1.base, if nb1, ok := n1.get(); ok {
tpe: max(n1.tpe, n2.tpe), if nb2, ok := n2.get(); ok {
atom: n1.div(n2.atom), if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
return Nan()
} }
return out.format() t := max(n1.Type(), n2.Type())
result := new(big.Rat).Quo(nb1, nb2)
if t == Integer && !result.IsInt() {
t = Decimal
} }
return n1.set(result, t, s)
}
return Undefined(s)
} else if n2.IsDefined() {
return Undefined(s)
}
return Nan()
}
// inv retourne 1/n.
func (n Number) Inv() Number { return IntOf(1, n.Base()).Div(n) }
// Quo retourne n1 ÷ n2 (division entière). // Quo retourne n1 ÷ n2 (division entière).
func (n1 Number) Quo(n2 Number) Number { func (n1 Number) Quo(n2 Number) Number {
out := Number{ if nb1, ok := n1.rat(); ok {
base: n1.base, if nb2, ok := n2.rat(); ok {
tpe: Integer, if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
atom: n1.quo(n2.atom), return Nan()
} }
return out.format() q := new(big.Rat).Quo(nb1, nb2)
return IntOf0(new(big.Int).Quo(q.Num(), q.Denom()), n1.Base())
}
return Undefined(n1.Sign() * n2.Sign())
} else if n2.IsDefined() {
return Undefined(n1.Sign() * n2.Sign())
}
return Nan()
}
// QuoRem retourne q et r tels que n1 = q×n2 + r où q est entier.
func (n1 Number) QuoRem(n2 Number) (q, r Number) {
q = n1.Quo(n2)
r = n1.Sub(n2.Mul(q))
return
} }
// Rem retourne n1 % n2. // Rem retourne n1 % n2.
func (n1 Number) Rem(n2 Number) Number { func (n1 Number) Rem(n2 Number) Number {
out := Number{ _, r := n1.QuoRem(n2)
base: n1.base,
tpe: max(n1.tpe, n2.tpe), return r
atom: n1.rem(n2.atom),
} }
return out.format() func (n Number) pow(p int64) (out Number) {
inv := p < 0
p = abs(p)
switch {
case p == 0:
if n.IsNan() {
return Nan()
}
return IntOf(1, n.Base())
case p == 1:
out = n.Clone()
case p == 2:
out = n.Square()
case p&1 == 0:
out = n.Square().pow(p >> 1)
default:
out = n.Square().pow(p >> 1).Mul(n)
} }
// QuoRem retourne la division entière et le reste. if inv {
func (n1 Number) QuoRem(n2 Number) (q, r Number) { out = out.Inv()
q.base, r.base = n1.base, n1.base }
q.tpe, r.tpe = Integer, max(n1.tpe, n2.tpe) return
q.atom, r.atom = n1.quoRem(n2.atom) }
func (n Number) optimize(precision Number) (out Number) {
return q.format(), r.format() out = n
if n.Denom().Gt(precision) {
out = n.Num().Mul(precision).Quo(n.Denom()).Div(precision).ToType(n.Type())
} }
// Inv retourne 1 ÷ n. return
func (n Number) Inv() Number { }
t := n.tpe func (n Number) sqrt(s uint64) (out Number) {
if n.IsNan() || s < 2 || (s&1 == 0 && n.IsNeg()) {
return Nan()
} else if !n.IsDefined() {
return n.Clone()
}
s0 := IntOf(s)
s1 := s0.Dec()
precision := pow[uint, uint](n.Base())
deltaMax := precision.Inv()
heron := func(partial Number) Number {
return ((partial.Mul(s1)).Add(n.Div(partial.pow(int64(s - 1))))).Div(s0).optimize(precision)
}
out = n
for {
tmp := heron(out)
if out.Eq(tmp) {
break
} else if (tmp.Sub(out)).Abs().Lt(deltaMax) {
out = tmp
break
}
out = tmp
}
if n.Type() == Integer && out.IsInt() {
out = out.ToType(Integer)
}
return
}
// Pow retourne n1 ^ n2.
func (n1 Number) Pow(n2 Number) Number {
if !n2.IsDefined() {
return Nan()
}
p2, s2 := n2.NumDenom()
p, pok := p2.ToInt64()
s, sok := s2.ToUint64()
if !pok || !sok {
return Nan()
}
out := n1.pow(p)
if s > 1 {
out = out.sqrt(s)
}
return out
}
// Square retourne n².
func (n Number) Square() Number { return n.Mul(n) }
// Sqrtn retourne la racine n2ième de n1.
func (n1 Number) Sqrtn(n2 Number) Number {
s, ok := n2.ToUint64()
if !ok {
return Nan()
}
return n1.sqrt(s)
}
// Sqrt retourne la racine carrée de n.
func (n Number) Sqrt() Number { return n.Sqrtn(Two()) }
func (n Number) lsh(s uint64) Number {
if nb, ok := n.rat(); ok {
num, denom := nb.Num(), nb.Denom()
return FracOf0(new(big.Int).Lsh(num, uint(s)), denom, n.Base()).ToType(n.Type())
}
return n.Clone()
}
func (n Number) rsh(s uint64) Number {
if nb, ok := n.rat(); ok {
t := n.Type()
if t == Integer { if t == Integer {
t = Fraction return IntOf0(new(big.Int).Rsh(nb.Num(), uint(s)), n.Base())
}
return FracOf0(nb.Num(), new(big.Int).Lsh(nb.Denom(), uint(s)), n.Base()).ToType(t)
} }
out := Number{ return n.Clone()
tpe: t,
base: n.base,
atom: n.inv(),
} }
if n.tpe == Integer && out.IsInt() { // Lsh retourne n1 << n2.
out.tpe = Integer func (n1 Number) Lsh(n2 Number) Number {
if s, ok := n2.ToUint64(); ok {
return n1.lsh(s)
} }
return out.format() return Nan()
}
// Rsh retourne n1 >> n2.
func (n1 Number) Rsh(n2 Number) Number {
if s, ok := n2.ToUint64(); ok {
return n1.rsh(s)
}
return Nan()
}
// Len retourne le nombre de chiffre de n si n est entier ou -1 sinon.
func (n Number) Len() int {
if nb, ok := n.get(); ok {
if !nb.IsInt() {
return -1
}
num := nb.Num()
if num.IsInt64() && num.Int64() == 0 {
return 1
}
s := num.Text(int(n.Base()))
return len(s)
}
return -1
}
// Bit retourne le n2ième chiffre de n1 si n est entier, ou -1 sinon.
// Le n° de bit commence à 0 à partir de la droite.
func (n1 Number) Bit(n2 Number) int {
b, ok := n2.ToUint64()
if !ok || !n1.IsInt() {
return -1
}
out := n1
if b > 0 {
out = n1.Quo(pow(n1.Base(), b))
}
out = out.Rem(IntOf(n1.Base()))
if result, ok := out.ToInt64(); ok {
return int(result)
}
return -1
} }
// Fact retourne n!. // Fact retourne n!.
@ -315,7 +609,7 @@ func (n Number) Fact() Number {
case !n.IsInt() || n.IsNeg(): case !n.IsInt() || n.IsNeg():
return Nan() return Nan()
default: default:
out := One(n.base) out := One(n.Base())
for e := Two(); e.Le(n); e = e.Inc() { for e := Two(); e.Le(n); e = e.Inc() {
out.Mul(e) out.Mul(e)
} }
@ -323,104 +617,135 @@ func (n Number) Fact() Number {
} }
} }
// Lsh retourne n1 << n2. func (n Number) text() string {
func (n1 Number) Lsh(n2 Number) Number { if nb, ok := n.get(); ok {
if n2.IsNeg() { return nb.Num().Text(int(n.Base()))
return n1.Rsh(n2.Abs()) }
return ""
}
func (n Number) undefinedString() string {
switch n.Sign() {
case -1:
return "-∞"
case +1:
return "+∞"
default:
return "NaN"
}
}
func (n Number) intString() (out string) {
out = n.text()
if n.Sign() < 0 {
out = fmt.Sprintf("-%s", out)
} }
if n, ok := n2.toInt64(); ok && n >= 0 { if base := n.Base(); base != 10 {
return n1.set(n1.lsh(uint(n)), true) out = fmt.Sprintf("(%d)%s", base, out)
}
return Nan()
} }
// Rsh retourne n1 << n2. return
func (n1 Number) Rsh(n2 Number) Number { }
if n2.IsNeg() { func (n Number) fracString() string {
return n1.Lsh(n2.Abs()) num, denom := n.NumDenom()
return fmt.Sprintf("%s/%s", num.intString(), denom.intString())
}
func (n Number) decString() (out string) {
base, sign := n.Base(), n.Sign()
num, denom := n.NumDenom()
precision := pow[uint, uint](base)
num = num.Abs().Mul(precision)
q := num.Quo(denom)
out = "0"
if num.Ge(denom) {
out = q.text()
} }
if n, ok := n2.toInt64(); ok && n >= 0 { p := int(FloatingPrecision)
return n1.set(n1.rsh(uint(n)), true) if len(out) < p {
} out = fmt.Sprintf("%s%s", strings.Repeat("0", p), out)
return Nan()
} }
// Len retourne le nombre de chiffre dun nombre entier (ou -1 sil nest pas entier) dot := len(out) - p
func (n Number) Len() int { out = fmt.Sprintf("%s.%s", out[:dot], out[dot:])
return n.len(n.base)
}
// Bit retourne le chiffre à la position n2 (en partant de 0) if !FixedPrecision {
// dun nombre entier (ou -1 si n1 ou n2 nest pas entier). l := len(out) - 1
func (n1 Number) Bit(n2 Number) int { for out[l] == '0' {
if n, ok := n2.toInt64(); ok && n >= 0 { out, l = out[:l], l-1
return n1.bit(uint64(n), n1.base)
} }
if out == "." {
return -1 out = "0."
}
// Pow retourne n1 ^ n2.
func (n1 Number) Pow(n2 Number) Number {
if !n2.IsDefined() {
return Nan()
}
p, s := n2.NumDenom()
if !p.isInt64() || !s.isInt64() {
n2 = n2.set(n2.optimize(pow(n2.base, FloatingPrecision)))
p, s = n2.NumDenom()
if !p.isInt64() || !s.isInt64() {
return Nan()
} }
} }
if p0, ok := p.toInt64(); ok { if sign < 0 {
if s0, ok := s.toInt64(); ok && s0 > 0 { out = fmt.Sprintf("-%s", out)
inv := p0 < 0 } else if q.IsZero() && sign > 0 {
if inv { out = fmt.Sprintf("+%s", out)
p0 = -p0
}
out := Number{
tpe: n1.tpe,
base: n1.base,
atom: n1.pow(uint64(p0)).sqrtn(uint64(s0), n1.base),
}
if inv {
out.atom = out.atom.inv()
}
if n1.tpe == Integer && !out.isInt() {
out.tpe = Decimal
} }
return out.format() if base != 10 {
} out = fmt.Sprintf("(%d)%s", base, out)
} }
return Nan() return
}
func (n Number) sciString() (out string) {
base, sign := n.Base(), n.Sign()
var exponent int
tmp := n
if !n.IsZero() {
num, denom := n.Abs().NumDenom()
nl, dl := num.Len(), denom.Len()
exponent = nl - dl
nBase := IntOf(base)
if exponent > 0 {
denom = denom.Mul(nBase.pow(int64(exponent)))
} else if exponent < 0 {
num = num.Mul(nBase.pow(int64(-exponent)))
} }
// Sqrtn retourne la racine n2ième de n1. if num.Lt(denom) {
func (n1 Number) Sqrtn(n2 Number) Number { exponent--
if n, ok := n2.toInt64(); ok && n > 0 { num = num.Mul(nBase)
out := Number{
tpe: n1.tpe,
base: n1.base,
atom: n1.sqrtn(uint64(n), n1.base),
} }
if n1.tpe == Integer && !out.isInt() { tmp = num.Div(denom)
out.tpe = Decimal
} }
return out.format() if sign < 0 {
tmp = tmp.Neg()
} }
return Nan() signExponent := ""
if exponent > 0 {
signExponent = "+"
} }
// Sqrt retourne la racine carrée de n. out = tmp.decString()
func (n Number) Sqrt() Number { if base == 10 {
return n.Sqrtn(Int(2)) 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 !n.IsDefined() {
return n.undefinedString()
}
switch n.Type() {
case Integer:
return n.intString()
case Fraction:
return n.fracString()
case Scientific:
return n.sciString()
default:
return n.decString()
}
} }

View File

@ -11,6 +11,7 @@ type Op2To2Func func(Number, Number) (Number, Number)
type ReduceFunc func(...Number) Number type ReduceFunc func(...Number) Number
type MapFunc func(...Number) []Number type MapFunc func(...Number) []Number
// ToBase convertit n selon la base donnée.
func ToBase[N integer](n Number, base N) Number { func ToBase[N integer](n Number, base N) Number {
return n.ToBase(formatBase(base)) return n.ToBase(formatBase(base))
} }
@ -24,21 +25,17 @@ func toType[N integer](n Number, t NumberType, base ...N) Number {
return n return n
} }
func ToInteger[N integer](n Number, base ...N) Number { // ToInteger convertit n en entier.
return toType(n, Integer, base...) func ToInteger[N integer](n Number, base ...N) Number { return toType(n, Integer, base...) }
}
func ToDecimal[N integer](n Number, base ...N) Number { // ToDecimal convertit n en décimal.
return toType(n, Decimal, base...) func ToDecimal[N integer](n Number, base ...N) Number { return toType(n, Decimal, base...) }
}
func ToFraction[N integer](n Number, base ...N) Number { // ToFraction convertit n en fraction.
return toType(n, Fraction, base...) func ToFraction[N integer](n Number, base ...N) Number { return toType(n, Fraction, base...) }
}
func ToScientific[N integer](n Number, base ...N) Number { // ToScientific convertit n en nombre scientifique.
return toType(n, Scientific, base...) func ToScientific[N integer](n Number, base ...N) Number { return toType(n, Scientific, base...) }
}
// Fonctions de type f(n) → n // Fonctions de type f(n) → n
func Neg(n Number) Number { return n.Neg() } func Neg(n Number) Number { return n.Neg() }
@ -49,11 +46,12 @@ func Inc(n Number) Number { return n.Inc() }
func Dec(n Number) Number { return n.Dec() } func Dec(n Number) Number { return n.Dec() }
func Inv(n Number) Number { return n.Inv() } func Inv(n Number) Number { return n.Inv() }
func Fact(n Number) Number { return n.Fact() } func Fact(n Number) Number { return n.Fact() }
func Len(n Number) Number { return Int(n.Len()) } func Len(n Number) Number { return IntOf(n.Len()) }
func Sqrt(n Number) Number { return n.Sqrt() } func Sqrt(n Number) Number { return n.Sqrt() }
func Square(n Number) Number { return n.Square() }
// Fonctions de type f(n, n) → n // Fonctions de type f(n, n) → n
func Cmp(n1, n2 Number) Number { return Int(n1.Cmp(n2)) } func Cmp(n1, n2 Number) Number { return IntOf(n1.Cmp(n2)) }
func Eq(n1, n2 Number) Number { return ParseBool(n1.Eq(n2)) } func Eq(n1, n2 Number) Number { return ParseBool(n1.Eq(n2)) }
func Ne(n1, n2 Number) Number { return ParseBool(n1.Ne(n2)) } func Ne(n1, n2 Number) Number { return ParseBool(n1.Ne(n2)) }
func Gt(n1, n2 Number) Number { return ParseBool(n1.Gt(n2)) } func Gt(n1, n2 Number) Number { return ParseBool(n1.Gt(n2)) }
@ -68,7 +66,7 @@ func Quo(n1, n2 Number) Number { return n1.Quo(n2) }
func Rem(n1, n2 Number) Number { return n1.Rem(n2) } func Rem(n1, n2 Number) Number { return n1.Rem(n2) }
func Lsh(n1, n2 Number) Number { return n1.Lsh(n2) } func Lsh(n1, n2 Number) Number { return n1.Lsh(n2) }
func Rsh(n1, n2 Number) Number { return n1.Rsh(n2) } func Rsh(n1, n2 Number) Number { return n1.Rsh(n2) }
func Bit(n1, n2 Number) Number { return Int(n1.Bit(n2)) } func Bit(n1, n2 Number) Number { return IntOf(n1.Bit(n2)) }
func Pow(n1, n2 Number) Number { return n1.Pow(n2) } func Pow(n1, n2 Number) Number { return n1.Pow(n2) }
func Sqrtn(n1, n2 Number) Number { return n1.Sqrtn(n2) } func Sqrtn(n1, n2 Number) Number { return n1.Sqrtn(n2) }
@ -105,6 +103,7 @@ func Reduce(callback Op2Func) ReduceFunc {
} }
} }
// Max retourne le nombre le plus grand de la liste.
func Max(numbers ...Number) (n Number) { func Max(numbers ...Number) (n Number) {
return Reduce(func(n1, n2 Number) Number { return Reduce(func(n1, n2 Number) Number {
if n2.Gt(n1) { if n2.Gt(n1) {
@ -114,6 +113,7 @@ func Max(numbers ...Number) (n Number) {
})(numbers...) })(numbers...)
} }
// Min retourne le nombre le plus petit de la liste.
func Min(numbers ...Number) (n Number) { func Min(numbers ...Number) (n Number) {
return Reduce(func(n1, n2 Number) Number { return Reduce(func(n1, n2 Number) Number {
if n2.Lt(n1) { if n2.Lt(n1) {
@ -123,35 +123,100 @@ func Min(numbers ...Number) (n Number) {
})(numbers...) })(numbers...)
} }
func Sum(numbers ...Number) (n Number) { // Sum retourne la somme des nombres.
return Reduce(Add)(numbers...) func Sum(numbers ...Number) (n Number) { return Reduce(Add)(numbers...) }
}
// Mean retourne la moyenne des nombres.
func Mean(numbers ...Number) (n Number) { func Mean(numbers ...Number) (n Number) {
l := len(numbers) l := len(numbers)
if l == 0 { if l == 0 {
return Nan() return Nan()
} }
return Sum(numbers...).Div(Int(l)) return Sum(numbers...).Div(IntOf(l))
} }
// Median retourne la médiane des nombres.
func Median(numbers ...Number) Number {
l := len(numbers)
if l == 0 {
return Nan()
}
numbers = Sort(numbers...)
if l&1 == 0 {
i := l >> 1
return numbers[i].Add(numbers[i-1]).Div(Two())
}
return numbers[l>>1]
}
// Mode retourne retourne le mode des nombres (ie. le nombre le plus fréquent).
func Mode(numbers ...Number) Number {
l := len(numbers)
if l == 0 {
return Nan()
}
m := make(map[Number]int)
loop:
for _, n := range numbers {
for k := range m {
if k.Eq(n) {
m[k]++
continue loop
}
}
m[n] = 1
}
i := 0
var n Number
for k, j := range m {
if j > i {
n, i = k, j
}
}
return n
}
// Variance retourne la variance des nombres.
func Variance(numbers ...Number) Number {
m := Mean(numbers...)
if m.IsNan() {
return m
}
numbers = Map(func(n Number) Number {
return n.Sub(m).Square()
})(numbers...)
return Mean(numbers...)
}
// StdDeviation retourne lécart-type des nombres.
func StdDeviation(numbers ...Number) Number { return Variance(numbers...).Sqrt() }
// Round arrondit le n selon la précision et la base données.
func Round(n Number, precision uint64, base ...uint) Number { func Round(n Number, precision uint64, base ...uint) Number {
if !n.IsDefined() || n.Type() == Integer { if !n.IsDefined() || n.Type() == Integer {
return n return n
} }
p := Number{ b := n.Base()
base: n.Base(), if len(base) > 0 {
tpe: n.Type(), b = formatBase(base...)
atom: pow(formatBase(base...), precision),
} }
p.format()
p := pow(b, precision)
num, denom := n.Num().Mul(p), n.Denom() num, denom := n.Num().Mul(p), n.Denom()
return num.Quo(denom).Div(p) return num.Quo(denom).Div(p).ToType(n.Type())
} }
// Reverse inverse lordre de la liste des nombres.
func Reverse(numbers ...Number) []Number { func Reverse(numbers ...Number) []Number {
l := len(numbers) l := len(numbers)
for i := 0; i < l>>1; i++ { for i := 0; i < l>>1; i++ {
@ -162,6 +227,7 @@ func Reverse(numbers ...Number) []Number {
return numbers return numbers
} }
// Sort trie les nombres par ordre croissant.
func Sort(numbers ...Number) []Number { func Sort(numbers ...Number) []Number {
sort.Slice(numbers, func(i, j int) bool { sort.Slice(numbers, func(i, j int) bool {
return numbers[i].Lt(numbers[j]) return numbers[i].Lt(numbers[j])
@ -170,6 +236,7 @@ func Sort(numbers ...Number) []Number {
return numbers return numbers
} }
// SortDesc trie les nombres par ordre décroissant.
func SortDesc(numbers ...Number) []Number { func SortDesc(numbers ...Number) []Number {
sort.Slice(numbers, func(i, j int) bool { sort.Slice(numbers, func(i, j int) bool {
return numbers[i].Gt(numbers[j]) return numbers[i].Gt(numbers[j])

View File

@ -1,5 +1,11 @@
package number package number
import (
"math/big"
"gitea.zaclys.com/bvaudour/gob/option"
)
type integer interface { type integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
} }
@ -8,6 +14,8 @@ type float interface {
~float32 | ~float64 ~float32 | ~float64
} }
type rat = option.Option[*big.Rat]
func signOf[N integer | float](n N) int { func signOf[N integer | float](n N) int {
switch { switch {
case n < 0: case n < 0:
@ -19,6 +27,14 @@ func signOf[N integer | float](n N) int {
} }
} }
func abs[N integer | float](n N) N {
if n < 0 {
return -n
}
return n
}
func compare[N integer | float](n1, n2 N) int { func compare[N integer | float](n1, n2 N) int {
return signOf(n1 - n2) return signOf(n1 - n2)
} }