Compare commits
3 Commits
976ac750b1
...
c4029042e0
Author | SHA1 | Date |
---|---|---|
Benjamin VAUDOUR | c4029042e0 | |
Benjamin VAUDOUR | 79f946a116 | |
Benjamin VAUDOUR | c645fb3812 |
12
README.md
12
README.md
|
@ -46,6 +46,18 @@ Le paquet **datetime** implémente des structures pour gérer des temps :
|
|||
- datetime.DateTime : pour gérer une date avec indication de l’heure.
|
||||
- Range : pour gérer une période entre deux temps.
|
||||
|
||||
### number
|
||||
|
||||
Le paquet **number** implémente une structure pour gérer des opérations sur les nombres réels.
|
||||
Un nombre peut être présenté sous différentes formes (dans une base donnée) :
|
||||
|
||||
- Format entier (ex : 58)
|
||||
- Format décimal (ex : 36.25)
|
||||
- Format scientifique (ex : 42.5E-18)
|
||||
- Fraction (ex : 12/5)
|
||||
|
||||
Un nombre peut être également indérminé (NaN, ±∞)
|
||||
|
||||
### format
|
||||
|
||||
Le paquet **format** fournit le nécessaire pour formater la sortie terminal :
|
||||
|
|
|
@ -75,8 +75,12 @@ func (dt datetime) add(y, m, d, h, i int, e ...int) datetime {
|
|||
p, v = PrecisionMillisecond, v+e[1]
|
||||
}
|
||||
}
|
||||
dc := v / MillisecondPerDay
|
||||
d, v = d+dc, v-dc*MillisecondPerDay
|
||||
d += v / MillisecondPerDay
|
||||
v %= MillisecondPerDay
|
||||
if v < 0 {
|
||||
d--
|
||||
v += MillisecondPerDay
|
||||
}
|
||||
return newDT(dt.date.add(y, m, d), newC(uint(v), p))
|
||||
}
|
||||
func (dt datetime) addT(y, m, d int) datetime { return newDT(dt.date.add(y, m, d), dt.clock) }
|
||||
|
|
4
go.mod
4
go.mod
|
@ -1,5 +1,5 @@
|
|||
module gitea.zaclys.com/bvaudour/gob
|
||||
|
||||
go 1.21
|
||||
go 1.22
|
||||
|
||||
require golang.org/x/crypto v0.13.0
|
||||
require golang.org/x/crypto v0.19.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,2 @@
|
|||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
|
|
|
@ -0,0 +1,502 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package number
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
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.
|
||||
)
|
||||
|
||||
const (
|
||||
regSign = `\+|-`
|
||||
regBSign = `0|1`
|
||||
regBase = `\(\d+\)`
|
||||
regBase2 = `B|b`
|
||||
regBase8 = `O|o`
|
||||
regBase16 = `X|x`
|
||||
regNb = `[0-9a-zA-Z]`
|
||||
regNb2 = `0|1`
|
||||
regNb8 = `[0-7]`
|
||||
regNb10 = `\d`
|
||||
regNb16 = `[0-9a-fA-F]`
|
||||
regExp = `×\d+\^`
|
||||
regExp10 = `E|e`
|
||||
)
|
||||
|
||||
var (
|
||||
regInt = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+`, regBase, regSign, regNb))
|
||||
regInt2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase2, regNb2))
|
||||
regInt8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase8, regNb8))
|
||||
regInt10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+`, regSign, regNb10))
|
||||
regInt16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase16, regNb16))
|
||||
|
||||
regDec = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.)`, regBase, regSign, regNb, regNb, regNb))
|
||||
regDec2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase2, regNb2, regNb2, regNb2))
|
||||
regDec8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase8, regNb8, regNb8, regNb8))
|
||||
regDec10 = regexp.MustCompile(fmt.Sprintf(`(%s)?(%s*\.%s+|%s+\.)`, regSign, regNb10, regNb10, regNb10))
|
||||
regDec16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase16, regNb16, regNb16, regNb16))
|
||||
|
||||
regFrac = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+/(%s)?%s+`, regBase, regSign, regNb, regSign, regNb))
|
||||
regFrac10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+/(%s)?%s+`, regSign, regNb10, regSign, regNb10))
|
||||
|
||||
regSci = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)%s(%s)?(%s)+`, regBase, regSign, regNb, regNb, regNb, regExp, regSign, regNb10))
|
||||
regSci10Simple = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp, regSign, regNb10))
|
||||
regSci10 = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp10, regSign, regNb10))
|
||||
)
|
|
@ -0,0 +1,486 @@
|
|||
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()
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
package number
|
||||
|
||||
// NumberType représente le type d’un nombre.
|
||||
type NumberType uint
|
||||
|
||||
const (
|
||||
Integer NumberType = iota
|
||||
Scientific
|
||||
Decimal
|
||||
Fraction
|
||||
)
|
||||
|
||||
// Number représente un nombre.
|
||||
type Number struct {
|
||||
atom
|
||||
tpe NumberType
|
||||
base uint
|
||||
}
|
||||
|
||||
// Undefined retourne un nombre indéfini, signé :
|
||||
// - si sign < 0, retourne -∞
|
||||
// - si sign > 0, retourne +∞
|
||||
// - sinon, retourne NaN
|
||||
func Undefined(sign int) Number {
|
||||
return Number{
|
||||
base: 10,
|
||||
tpe: Integer,
|
||||
atom: undefined(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) }
|
||||
|
||||
// Int retourne un nombre à partir d’un 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 Zero(base ...uint) Number { return Int(0, base...) }
|
||||
|
||||
// One retourne le nombre 1.
|
||||
func One(base ...uint) Number { return Int(1, base...) }
|
||||
|
||||
// Two retourne le nombre 2.
|
||||
func Two(base ...uint) Number { return Int(2, base...) }
|
||||
|
||||
// Float retourne un nombre à partir d’un 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),
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
} else if n.tpe == Integer && !n.isInt() {
|
||||
n.setInt()
|
||||
}
|
||||
|
||||
return *n
|
||||
}
|
||||
|
||||
func (n Number) clone(format ...bool) Number {
|
||||
out := Number{
|
||||
atom: n.atom.clone(format...),
|
||||
tpe: n.tpe,
|
||||
base: n.base,
|
||||
}
|
||||
|
||||
if len(format) > 0 && format[0] {
|
||||
return out.format()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Type retourne le type de nombre.
|
||||
func (n Number) Type() NumberType {
|
||||
return n.tpe
|
||||
}
|
||||
|
||||
// ToType convertit le nombre au type donné.
|
||||
func (n Number) ToType(t NumberType) Number {
|
||||
out := n.clone()
|
||||
switch t {
|
||||
case Integer, Decimal, Fraction, Scientific:
|
||||
out.tpe = t
|
||||
default:
|
||||
out.tpe = Decimal
|
||||
}
|
||||
|
||||
return out.format(true)
|
||||
}
|
||||
|
||||
// Base retourne la base du nombre.
|
||||
func (n Number) Base() uint {
|
||||
return n.base
|
||||
}
|
||||
|
||||
// ToBase convertit le nombre dans la base donnée.
|
||||
func (n Number) ToBase(base uint) Number {
|
||||
out := n.clone(true)
|
||||
out.base = formatBase(base)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Neg retourne -n.
|
||||
func (n Number) Neg() Number {
|
||||
return n.set(n.neg())
|
||||
}
|
||||
|
||||
// Abs retourne |n|.
|
||||
func (n Number) Abs() Number {
|
||||
return n.set(n.abs())
|
||||
}
|
||||
|
||||
// IsInt retourne vrai si le nombre est entier.
|
||||
func (n Number) IsInt() bool {
|
||||
return n.isInt()
|
||||
}
|
||||
|
||||
// Num retourne le numérateur.
|
||||
func (n Number) Num() Number {
|
||||
out := Number{
|
||||
atom: n.num(),
|
||||
base: n.base,
|
||||
tpe: Integer,
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Denom retourne le dénominateur.
|
||||
func (n Number) Denom() Number {
|
||||
out := Number{
|
||||
atom: n.denom(),
|
||||
base: n.base,
|
||||
tpe: Integer,
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Num retourne le numérateur et le dénominateur.
|
||||
func (n Number) NumDenom() (Number, Number) {
|
||||
return n.Num(), n.Denom()
|
||||
}
|
||||
|
||||
// Cmp retourne :
|
||||
// - -1 si n1 < n2
|
||||
// - +1 si n1 > n2
|
||||
// - 0 sinon.
|
||||
func (n1 Number) Cmp(n2 Number) int {
|
||||
return n1.cmp(n2.atom)
|
||||
}
|
||||
|
||||
func (n1 Number) Eq(n2 Number) bool { return n1.Cmp(n2) == 0 }
|
||||
func (n1 Number) Ne(n2 Number) bool { return n1.Cmp(n2) != 0 }
|
||||
func (n1 Number) Gt(n2 Number) bool { return n1.Cmp(n2) > 0 }
|
||||
func (n1 Number) Lt(n2 Number) bool { return n1.Cmp(n2) < 0 }
|
||||
func (n1 Number) Ge(n2 Number) bool { return n1.Cmp(n2) >= 0 }
|
||||
func (n1 Number) Le(n2 Number) bool { return n1.Cmp(n2) <= 0 }
|
||||
|
||||
// Add retourne n1 + n2.
|
||||
func (n1 Number) Add(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: max(n1.tpe, n2.tpe),
|
||||
atom: n1.add(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Sub retourne n1 - n2.
|
||||
func (n1 Number) Sub(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: max(n1.tpe, n2.tpe),
|
||||
atom: n1.sub(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Inc retourne n + 1.
|
||||
func (n Number) Inc() Number {
|
||||
return n.Add(One())
|
||||
}
|
||||
|
||||
// Dec retourne n − 1.
|
||||
func (n Number) Dec() Number {
|
||||
return n.Sub(One())
|
||||
}
|
||||
|
||||
// Mul retourne n1 × n2.
|
||||
func (n1 Number) Mul(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: max(n1.tpe, n2.tpe),
|
||||
atom: n1.mul(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Div retourne n1 ÷ n2 (division exacte).
|
||||
func (n1 Number) Div(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: max(n1.tpe, n2.tpe),
|
||||
atom: n1.div(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Quo retourne n1 ÷ n2 (division entière).
|
||||
func (n1 Number) Quo(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: Integer,
|
||||
atom: n1.quo(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Rem retourne n1 % n2.
|
||||
func (n1 Number) Rem(n2 Number) Number {
|
||||
out := Number{
|
||||
base: n1.base,
|
||||
tpe: max(n1.tpe, n2.tpe),
|
||||
atom: n1.rem(n2.atom),
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// QuoRem retourne la division entière et le reste.
|
||||
func (n1 Number) QuoRem(n2 Number) (q, r Number) {
|
||||
q.base, r.base = n1.base, n1.base
|
||||
q.tpe, r.tpe = Integer, max(n1.tpe, n2.tpe)
|
||||
q.atom, r.atom = n1.quoRem(n2.atom)
|
||||
|
||||
return q.format(), r.format()
|
||||
}
|
||||
|
||||
// Inv retourne 1 ÷ n.
|
||||
func (n Number) Inv() Number {
|
||||
t := n.tpe
|
||||
if t == Integer {
|
||||
t = Fraction
|
||||
}
|
||||
|
||||
out := Number{
|
||||
tpe: t,
|
||||
base: n.base,
|
||||
atom: n.inv(),
|
||||
}
|
||||
|
||||
if n.tpe == Integer && out.IsInt() {
|
||||
out.tpe = Integer
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
// Fact retourne n!.
|
||||
func (n Number) Fact() Number {
|
||||
switch {
|
||||
case n.IsInf():
|
||||
return Inf()
|
||||
case !n.IsInt() || n.IsNeg():
|
||||
return Nan()
|
||||
default:
|
||||
out := One(n.base)
|
||||
for e := Two(); e.Le(n); e = e.Inc() {
|
||||
out.Mul(e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
// Lsh retourne n1 << n2.
|
||||
func (n1 Number) Lsh(n2 Number) Number {
|
||||
if n2.IsNeg() {
|
||||
return n1.Rsh(n2.Abs())
|
||||
}
|
||||
|
||||
if n, ok := n2.toInt64(); ok && n >= 0 {
|
||||
return n1.set(n1.lsh(uint(n)), true)
|
||||
}
|
||||
return Nan()
|
||||
}
|
||||
|
||||
// Rsh retourne n1 << n2.
|
||||
func (n1 Number) Rsh(n2 Number) Number {
|
||||
if n2.IsNeg() {
|
||||
return n1.Lsh(n2.Abs())
|
||||
}
|
||||
|
||||
if n, ok := n2.toInt64(); ok && n >= 0 {
|
||||
return n1.set(n1.rsh(uint(n)), true)
|
||||
}
|
||||
return Nan()
|
||||
}
|
||||
|
||||
// Len retourne le nombre de chiffre d’un nombre entier (ou -1 s’il n’est pas entier)
|
||||
func (n Number) Len() int {
|
||||
return n.len(n.base)
|
||||
}
|
||||
|
||||
// Bit retourne le chiffre à la position n2 (en partant de 0)
|
||||
// d’un nombre entier (ou -1 si n1 ou n2 n’est pas entier).
|
||||
func (n1 Number) Bit(n2 Number) int {
|
||||
if n, ok := n2.toInt64(); ok && n >= 0 {
|
||||
return n1.bit(uint64(n), n1.base)
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// 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 s0, ok := s.toInt64(); ok && s0 > 0 {
|
||||
inv := p0 < 0
|
||||
if inv {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
return Nan()
|
||||
}
|
||||
|
||||
// Sqrtn retourne la racine n2ième de n1.
|
||||
func (n1 Number) Sqrtn(n2 Number) Number {
|
||||
if n, ok := n2.toInt64(); ok && n > 0 {
|
||||
out := Number{
|
||||
tpe: n1.tpe,
|
||||
base: n1.base,
|
||||
atom: n1.sqrtn(uint64(n), n1.base),
|
||||
}
|
||||
if n1.tpe == Integer && !out.isInt() {
|
||||
out.tpe = Decimal
|
||||
}
|
||||
|
||||
return out.format()
|
||||
}
|
||||
|
||||
return Nan()
|
||||
}
|
||||
|
||||
// Sqrt retourne la racine carrée de n.
|
||||
func (n Number) Sqrt() Number {
|
||||
return n.Sqrtn(Int(2))
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package number
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Op1Func func(Number) Number
|
||||
type Op2Func func(Number, Number) Number
|
||||
type Op1To2Func func(Number) (Number, Number)
|
||||
type Op2To2Func func(Number, Number) (Number, Number)
|
||||
type ReduceFunc func(...Number) Number
|
||||
type MapFunc func(...Number) []Number
|
||||
|
||||
func ToBase[N integer](n Number, base N) Number {
|
||||
return n.ToBase(formatBase(base))
|
||||
}
|
||||
|
||||
func toType[N integer](n Number, t NumberType, base ...N) Number {
|
||||
n = n.ToType(t)
|
||||
if len(base) > 0 {
|
||||
n = ToBase(n, base[0])
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func ToInteger[N integer](n Number, base ...N) Number {
|
||||
return toType(n, Integer, base...)
|
||||
}
|
||||
|
||||
func ToDecimal[N integer](n Number, base ...N) Number {
|
||||
return toType(n, Decimal, base...)
|
||||
}
|
||||
|
||||
func ToFraction[N integer](n Number, base ...N) Number {
|
||||
return toType(n, Fraction, base...)
|
||||
}
|
||||
|
||||
func ToScientific[N integer](n Number, base ...N) Number {
|
||||
return toType(n, Scientific, base...)
|
||||
}
|
||||
|
||||
// Fonctions de type f(n) → n
|
||||
func Neg(n Number) Number { return n.Neg() }
|
||||
func Abs(n Number) Number { return n.Abs() }
|
||||
func Num(n Number) Number { return n.Num() }
|
||||
func Denom(n Number) Number { return n.Denom() }
|
||||
func Inc(n Number) Number { return n.Inc() }
|
||||
func Dec(n Number) Number { return n.Dec() }
|
||||
func Inv(n Number) Number { return n.Inv() }
|
||||
func Fact(n Number) Number { return n.Fact() }
|
||||
func Len(n Number) Number { return Int(n.Len()) }
|
||||
func Sqrt(n Number) Number { return n.Sqrt() }
|
||||
|
||||
// Fonctions de type f(n, n) → n
|
||||
func Cmp(n1, n2 Number) Number { return Int(n1.Cmp(n2)) }
|
||||
func Eq(n1, n2 Number) Number { return ParseBool(n1.Eq(n2)) }
|
||||
func Ne(n1, n2 Number) Number { return ParseBool(n1.Ne(n2)) }
|
||||
func Gt(n1, n2 Number) Number { return ParseBool(n1.Gt(n2)) }
|
||||
func Lt(n1, n2 Number) Number { return ParseBool(n1.Lt(n2)) }
|
||||
func Ge(n1, n2 Number) Number { return ParseBool(n1.Ge(n2)) }
|
||||
func Le(n1, n2 Number) Number { return ParseBool(n1.Le(n2)) }
|
||||
func Add(n1, n2 Number) Number { return n1.Add(n2) }
|
||||
func Sub(n1, n2 Number) Number { return n1.Sub(n2) }
|
||||
func Mul(n1, n2 Number) Number { return n1.Mul(n2) }
|
||||
func Div(n1, n2 Number) Number { return n1.Div(n2) }
|
||||
func Quo(n1, n2 Number) Number { return n1.Quo(n2) }
|
||||
func Rem(n1, n2 Number) Number { return n1.Rem(n2) }
|
||||
func Lsh(n1, n2 Number) Number { return n1.Lsh(n2) }
|
||||
func Rsh(n1, n2 Number) Number { return n1.Rsh(n2) }
|
||||
func Bit(n1, n2 Number) Number { return Int(n1.Bit(n2)) }
|
||||
func Pow(n1, n2 Number) Number { return n1.Pow(n2) }
|
||||
func Sqrtn(n1, n2 Number) Number { return n1.Sqrtn(n2) }
|
||||
|
||||
// Fonctions de type f(n) → (n, n)
|
||||
func NumDenom(n Number) (Number, Number) { return n.NumDenom() }
|
||||
|
||||
// Fonctions de type f(n, n) → (n, n)
|
||||
func QuoRem(n1, n2 Number) (Number, Number) { return n1.QuoRem(n2) }
|
||||
|
||||
// Autre
|
||||
func Map(callback Op1Func) MapFunc {
|
||||
return func(numbers ...Number) []Number {
|
||||
out := make([]Number, len(numbers))
|
||||
for i, n := range numbers {
|
||||
out[i] = callback(n)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
func Reduce(callback Op2Func) ReduceFunc {
|
||||
return func(numbers ...Number) Number {
|
||||
var acc Number
|
||||
for i, n := range numbers {
|
||||
if i == 0 {
|
||||
acc = n
|
||||
} else {
|
||||
acc = callback(acc, n)
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
}
|
||||
|
||||
func Max(numbers ...Number) (n Number) {
|
||||
return Reduce(func(n1, n2 Number) Number {
|
||||
if n2.Gt(n1) {
|
||||
return n2
|
||||
}
|
||||
return n1
|
||||
})(numbers...)
|
||||
}
|
||||
|
||||
func Min(numbers ...Number) (n Number) {
|
||||
return Reduce(func(n1, n2 Number) Number {
|
||||
if n2.Lt(n1) {
|
||||
return n2
|
||||
}
|
||||
return n1
|
||||
})(numbers...)
|
||||
}
|
||||
|
||||
func Sum(numbers ...Number) (n Number) {
|
||||
return Reduce(Add)(numbers...)
|
||||
}
|
||||
|
||||
func Mean(numbers ...Number) (n Number) {
|
||||
l := len(numbers)
|
||||
if l == 0 {
|
||||
return Nan()
|
||||
}
|
||||
|
||||
return Sum(numbers...).Div(Int(l))
|
||||
}
|
||||
|
||||
func Round(n Number, precision uint64, base ...uint) Number {
|
||||
if !n.IsDefined() || n.Type() == Integer {
|
||||
return n
|
||||
}
|
||||
|
||||
p := Number{
|
||||
base: n.Base(),
|
||||
tpe: n.Type(),
|
||||
atom: pow(formatBase(base...), precision),
|
||||
}
|
||||
p.format()
|
||||
num, denom := n.Num().Mul(p), n.Denom()
|
||||
|
||||
return num.Quo(denom).Div(p)
|
||||
}
|
||||
|
||||
func Reverse(numbers ...Number) []Number {
|
||||
l := len(numbers)
|
||||
for i := 0; i < l>>1; i++ {
|
||||
j := l - 1 - i
|
||||
numbers[i], numbers[j] = numbers[j], numbers[i]
|
||||
}
|
||||
|
||||
return numbers
|
||||
}
|
||||
|
||||
func Sort(numbers ...Number) []Number {
|
||||
sort.Slice(numbers, func(i, j int) bool {
|
||||
return numbers[i].Lt(numbers[j])
|
||||
})
|
||||
|
||||
return numbers
|
||||
}
|
||||
|
||||
func SortDesc(numbers ...Number) []Number {
|
||||
sort.Slice(numbers, func(i, j int) bool {
|
||||
return numbers[i].Gt(numbers[j])
|
||||
})
|
||||
|
||||
return numbers
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package number
|
||||
|
||||
type integer interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
||||
}
|
||||
|
||||
type float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
func signOf[N integer | float](n N) int {
|
||||
switch {
|
||||
case n < 0:
|
||||
return -1
|
||||
case n > 0:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func compare[N integer | float](n1, n2 N) int {
|
||||
return signOf(n1 - n2)
|
||||
}
|
||||
|
||||
func isBaseValid[N integer | float](base N) bool {
|
||||
return base >= 0 && base <= 36
|
||||
}
|
||||
|
||||
func formatBase[N integer | float](base ...N) uint {
|
||||
if len(base) > 0 && isBaseValid(base[0]) {
|
||||
return uint(base[0])
|
||||
}
|
||||
|
||||
return 10
|
||||
}
|
Loading…
Reference in New Issue