diff --git a/README.md b/README.md index 0ce27ea..4fa1a86 100644 --- a/README.md +++ b/README.md @@ -46,18 +46,6 @@ 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 : diff --git a/datetime/datetime.go b/datetime/datetime.go index 60ae892..ebb92bc 100644 --- a/datetime/datetime.go +++ b/datetime/datetime.go @@ -75,12 +75,8 @@ func (dt datetime) add(y, m, d, h, i int, e ...int) datetime { p, v = PrecisionMillisecond, v+e[1] } } - d += v / MillisecondPerDay - v %= MillisecondPerDay - if v < 0 { - d-- - v += MillisecondPerDay - } + dc := v / MillisecondPerDay + d, v = d+dc, v-dc*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) } diff --git a/go.mod b/go.mod index f0b8f27..184b15e 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module gitea.zaclys.com/bvaudour/gob -go 1.22 +go 1.21 -require golang.org/x/crypto v0.19.0 +require golang.org/x/crypto v0.13.0 diff --git a/go.sum b/go.sum index b3dc059..8886ba7 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= diff --git a/number/atom.go b/number/atom.go deleted file mode 100644 index 2b71510..0000000 --- a/number/atom.go +++ /dev/null @@ -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 -} diff --git a/number/const.go b/number/const.go deleted file mode 100644 index 9ad31da..0000000 --- a/number/const.go +++ /dev/null @@ -1,48 +0,0 @@ -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)) -) diff --git a/number/convert.go b/number/convert.go deleted file mode 100644 index 9b7c44d..0000000 --- a/number/convert.go +++ /dev/null @@ -1,486 +0,0 @@ -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>": - return option.Some(Inf()) - case "-∞", "<-inf>": - return option.Some(NegInf()) - case "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() -} diff --git a/number/number.go b/number/number.go deleted file mode 100644 index fde7a9b..0000000 --- a/number/number.go +++ /dev/null @@ -1,426 +0,0 @@ -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)) -} diff --git a/number/operations.go b/number/operations.go deleted file mode 100644 index fb7da44..0000000 --- a/number/operations.go +++ /dev/null @@ -1,179 +0,0 @@ -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 -} diff --git a/number/util.go b/number/util.go deleted file mode 100644 index 219f2b2..0000000 --- a/number/util.go +++ /dev/null @@ -1,36 +0,0 @@ -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 -}