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 index 9ad31da..3a1ee97 100644 --- a/number/const.go +++ b/number/const.go @@ -7,42 +7,56 @@ import ( 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. + FixedPrecision = false // Si vrai le nombre de chiffres 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` + rSign = `(\+|-)` + rBSign = `(0|1)` + rNb2 = `(0|1)` + rNb8 = `[0-7]` + rNb10 = `\d` + rNb16 = `[0-9a-fA-F]` + rNbN = `[0-9a-zA-Z]` + rBase2 = `(B|b)` + rBase8 = `(O|o)` + rBase16 = `(X|x)` + rExp10 = `E|e` + rExpN = `×\d+\^` ) 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)) + rInt2 = fmt.Sprintf(`(%s)%s+`, rBase2, rNb2) + rInt8 = fmt.Sprintf(`(%s)%s+`, rBase8, rNb8) + rInt10 = fmt.Sprintf(`(%s)%s+`, rSign, rNb10) + rInt16 = fmt.Sprintf(`(%s)%s+`, rBase8, rNb8) + 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)) - 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)) + rDec2 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase2, rNb2, rNb2, rNb2) + rDec8 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase8, rNb8, rNb8, rNb8) + rDec10 = fmt.Sprintf(`%s?(%s+\.%s*|\.%s+)`, rSign, rNb10, rNb10, rNb10) + rDec16 = fmt.Sprintf(`(%s)(%s+\.%s*|\.%s+)`, rBase16, rNb16, rNb16, rNb16) + rDecN = fmt.Sprintf(`\(%s+\)%s?(%s+\.%s*|\.%s+)`, rNb10, rSign, rNbN, rNbN, rNbN) + rDecB = fmt.Sprintf(`%s(%s|%s|%s)`, rBSign, rDec2, rDec8, rDec16) + rDec = fmt.Sprintf(`(%s|%s|%s)`, rDec10, rDecN, rDecB) + 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) + rAll10 = fmt.Sprintf(`(%s|%s)((%s)%s?%s+)?`, rInt10, rDec10, regExp10, rSign, rNb10) + rAllN = fmt.Sprintf(`(%s|%s)(%s%s?%s+)?`, rInt, rDec, rExpN, rSign, rNb10) + 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)) ) diff --git a/number/convert.go b/number/convert.go index 9b7c44d..39bcfa7 100644 --- a/number/convert.go +++ b/number/convert.go @@ -9,160 +9,109 @@ import ( "gitea.zaclys.com/bvaudour/gob/option" ) -func undefinedString(sign int) string { - switch sign { - case -1: - return "-∞" - case +1: - return "+∞" - default: - return "NaN" +// Undefined retourne un nombre indéfini, signé : +// - si sign < 0, retourne -∞ +// - si sign > 0, retourne +∞ +// - sinon, retourne NaN +func Undefined[N integer | float](sign N) Number { + return Number{ + base: 10, + tpe: Integer, + sign: signOf(sign), } } -func integerString(n *big.Int, base uint, sign int) string { - out := n.Text(int(base)) +// Nan retourne NaN. +func Nan() Number { return Undefined(0) } - if sign < 0 { - out = fmt.Sprintf("-%s", out) +// Inf retourne +∞. +func Inf() Number { return Undefined(1) } + +// NegInf retourne -∞. +func NegInf() Number { return Undefined(-1) } + +// IntOf0 retourne un nombre entier à partir d’un entier, et éventuellement une base. +func IntOf0(i *big.Int, base ...uint) Number { + if i == nil { + return Nan() } - if base != 10 { - out = fmt.Sprintf("(%d)%s", base, out) + n := Number{ + number: option.Some(new(big.Rat).SetInt(i)), + sign: 1, + base: formatBase(base...), + tpe: Integer, } - return out + return n.format() } -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 +// IntOf retourne un nombre entier à partir d’un entier, et éventuellement une base. +func IntOf[N integer | float](i N, base ...uint) Number { + return IntOf0(new(big.Int).SetInt64(int64(i)), base...) } -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 "" - } +func pow[N integer | float, M integer | float](b N, p ...M) Number { + if len(p) > 0 { + return IntOf(b).pow(int64(p[0])) } - - 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 + return IntOf(b).pow(int64(FloatingPrecision)) } -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), - }) - } +// Zero retourne le nombre 0. +func Zero(base ...uint) Number { return IntOf(0, base...) } - if num.Lt(denom) { - exponent-- - num.Mul(Int(base, base)) - } - tmp = num.Div(denom) +// One retourne le nombre 1. +func One(base ...uint) Number { return IntOf(1, base...) } + +// Two retourne le nombre 2. +func Two(base ...uint) Number { return IntOf(2, base...) } + +// DecOf0 retourne un nombre décimal à partir d’un rationnel. +func DecOf0(f *big.Rat, base ...uint) Number { + if f == nil { + return Nan() } - out := decimalString(tmp) - - signExponent := "" - if exponent > 0 { - signExponent = "+" - } else if exponent < 0 { - signExponent = "-" + n := Number{ + number: option.Some(new(big.Rat).Set(f)), + sign: 1, + base: formatBase(base...), + tpe: Decimal, } - if base == 10 { - return fmt.Sprintf("%sE%s%d", out, signExponent, exponent) - } - return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent) + return n.format() } -// 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()) +// DecOf retourne un nombre décimal à partir d’un flottant. +func DecOf[N integer | float](f float64, base ...uint) Number { + return DecOf0(new(big.Rat).SetFloat64(float64(f))) } -func parseBase(str string) (next string, base option.Option[uint]) { +// FracOf0 retourne une fraction à partir d’un numérateur et d’un dénominateur entiers. +func FracOf0(num, denom *big.Int, base ...uint) Number { + if num == nil || denom == nil { + return Nan() + } + + 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 d’un numérateur et d’un dénominateur entiers. +func FracOf[N integer | float](num, denom N, base ...uint) Number { + return FracOf0(new(big.Int).SetInt64(int64(num)), new(big.Int).SetInt64(int64(denom)), base...) +} + +func parseBaseN(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) { @@ -171,7 +120,7 @@ func parseBase(str string) (next string, base option.Option[uint]) { return } -func parseBaseN(str string) (next string, base uint) { +func parseBaseB(str string) (next string, base uint) { next = str[1:] switch str[0] { case 'B', 'b': @@ -185,7 +134,7 @@ func parseBaseN(str string) (next string, base uint) { return } -func parseSign(str string) (next string, sign int) { +func parseSignN(str string) (next string, sign int) { switch str[0] { case '-': next, sign = str[1:], -1 @@ -198,7 +147,7 @@ func parseSign(str string) (next string, sign int) { return } -func parseSignN(str string) (next string, sign int) { +func parseSignB(str string) (next string, sign int) { next, sign = str[1:], 1 if str[0] == '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]) { if nb, ok := parseNumber(str, base).Get(); ok { - nn := Number{ - base: base, - tpe: Integer, - atom: entire0(nb, sign), + if sign < 0 { + nb.Neg(nb) } - 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) + n = option.Some(IntOf0(nb, base)) } return } func parseInt10(str string) (n option.Option[Number]) { - next, sign := parseSign(str) + next, sign := parseSignN(str) return setInt(next, 10, sign) } -func parseIntN(str string) (n option.Option[Number]) { - next, sign := parseSignN(str) - next, base := parseBaseN(next) +func parseIntB(str string) (n option.Option[Number]) { + next, sign := parseSignB(str) + next, base := parseBaseB(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) - +func parseIntN(str string) (n option.Option[Number]) { + next, base := parseBaseN(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) + next, sign := parseSignN(next) + n = setInt(next, b, sign) } return } // Parse retourne un nombre à partir d’une chaîne de caractères. -func Parse(str string) option.Option[Number] { +func Parse(str string) (out option.Option[Number]) { switch str { case "+∞", "", "<+inf>": return option.Some(Inf()) @@ -447,33 +210,50 @@ func Parse(str string) option.Option[Number] { } 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): + case regIntB.MatchString(str): + return parseIntB(str) + case regIntN.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) + l := len(str) + i := strings.Index(str, ".") + dot := l - 1 - i + str = fmt.Sprintf("%s%s", str[:i], str[i+1:]) + if n, ok := Parse(str).Get(); ok { + out = option.Some(n.Div(pow(n.Base(), dot)).ToType(Decimal)) + } + case regExp10.MatchString(str): + i := strings.Index(strings.ToLower(str), "e") + if exponent, err := strconv.Atoi(str[i+1:]); err == nil { + if n, ok := Parse(str[:i]).Get(); ok { + out = option.Some(n.Mul(pow(10, exponent)).ToType(Scientific)) + } + } + case regExpN.MatchString(str): + i, j := strings.Index(str, "×"), strings.Index(str, "^") + 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 { if b { return One() @@ -481,6 +261,5 @@ func ParseBool(b bool) Number { return Zero() } -func ToBool(n Number) bool { - return !n.IsZero() && !n.IsNan() -} +// ToBool retourne vrai si le nombre n’est ni NaN ni 0. +func ToBool(n Number) bool { return n.Sign() != 0 } diff --git a/number/number.go b/number/number.go index fde7a9b..320b2bc 100644 --- a/number/number.go +++ b/number/number.go @@ -1,5 +1,13 @@ package number +import ( + "fmt" + "math/big" + "strings" + + "gitea.zaclys.com/bvaudour/gob/option" +) + // NumberType représente le type d’un nombre. type NumberType uint @@ -12,299 +20,585 @@ const ( // Number représente un nombre. type Number struct { - atom - tpe NumberType - base uint + number rat + sign int + 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), - } +func (n *Number) get() (r *big.Rat, ok bool) { + return n.number.Get() } -// 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), +func (n *Number) rat() (r *big.Rat, ok bool) { + if r, ok := n.get(); ok { + r = new(big.Rat).Set(r) + if n.Sign() < 0 { + r = r.Neg(r) + } } + + return } -// Zero retourne le nombre 0. -func Zero(base ...uint) Number { return Int(0, base...) } +func (n *Number) format() Number { + n.base, n.sign, n.tpe = formatBase(n.base), signOf(n.sign), min(n.tpe, Fraction) -// 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 + if nb, ok := n.get(); ok { + n.sign *= nb.Sign() + if n.tpe == Integer && !nb.IsInt() { + num, denom := nb.Num(), nb.Denom() + nb.SetInt(num.Quo(num, denom)) + } + nb.Abs(nb) + } else { n.tpe = Integer - } else if n.tpe == Integer && !n.isInt() { - n.setInt() } 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{ - 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, base: n.base, } - if len(format) > 0 && format[0] { - return out.format() + if nb, ok := n.get(); ok { + out.number = option.Some(new(big.Rat).Set(nb)) } + return out } -// Type retourne le type de nombre. -func (n Number) Type() NumberType { - return n.tpe -} +// Base retourne la base utilisée pour l’affichage du nombre. +func (n Number) Base() uint { return n.base } -// 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 - } +// Type retourne le type de nombre (entier, décimal, fraction ou scientifique). +func (n Number) Type() NumberType { return n.tpe } - return out.format(true) -} - -// Base retourne la base du nombre. -func (n Number) Base() uint { - return n.base -} +// Sign retourne : +// - 0 si n = 0 ou NaN +// - 1 si n > 0 +// - -1 si n < 0 +func (n Number) Sign() int { return n.sign } // ToBase convertit le nombre dans la base donnée. func (n Number) ToBase(base uint) Number { - out := n.clone(true) + out := n.Clone() out.base = formatBase(base) return out } -// Neg retourne -n. -func (n Number) Neg() Number { - return n.set(n.neg()) +// ToType convertit le nombre dans le type donné. La base est conservée, sauf si une base est fournie. +func (n Number) ToType(tpe NumberType, base ...uint) Number { + out := n.Clone() + out.tpe = tpe + out = out.format() + + if len(base) > 0 { + return out.ToBase(base[0]) + } + return out } -// Abs retourne |n|. -func (n Number) Abs() Number { - return n.set(n.abs()) -} +// IsDefined retourne vrai si le nombre est défini. +func (n Number) IsDefined() bool { return n.number.IsDefined() } // IsInt retourne vrai si le nombre est entier. 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. func (n Number) Num() Number { - out := Number{ - atom: n.num(), - base: n.base, - tpe: Integer, + if n.Sign() == 0 { + return IntOf(0, n.Base()) + } else if nb, ok := n.rat(); ok { + return IntOf0(nb.Num(), n.Base()) } - return out.format() + return IntOf(n.Sign(), n.Base()) } // Denom retourne le dénominateur. func (n Number) Denom() Number { - out := Number{ - atom: n.denom(), - base: n.base, - tpe: Integer, + if nb, ok := n.get(); ok { + return IntOf0(nb.Denom(), n.Base()) } - return out.format() + return IntOf(0, n.Base()) } -// Num retourne le numérateur et le dénominateur. -func (n Number) NumDenom() (Number, Number) { - return n.Num(), n.Denom() +// NumDenom retourne le numérateur et le dénominateur. +func (n Number) NumDenom() (Number, Number) { 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 : +// - 1 si n1 > n2 // - -1 si n1 < n2 -// - +1 si n1 > n2 -// - 0 sinon. +// - 0 si n1 = n2 +// - 2 ou -2 si les nombres ne sont pas comparables (ie. n1 = NaN ou n2 = NaN) 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 } + +// Ne retourne vrai si n1 ≠ n2. 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 } + +// Lt retourne vrai si n1 < n2. 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 } + +// Le retourne vrai si n1 ≤ n2. 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), + if r1, ok := n1.rat(); ok { + if r2, ok := n2.rat(); ok { + return n1.set(new(big.Rat).Add(r1, r2), max(n1.Type(), n2.Type())) + } + return n2.Clone() + } else if n2.IsDefined() || n1.Sign() == n2.Sign() { + return Undefined(n1.Sign()) } - return out.format() + return Nan() } -// 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() -} +// Sub retourne n1 − n2. +func (n1 Number) Sub(n2 Number) Number { return n1.Add(n2.Neg()) } // Inc retourne n + 1. -func (n Number) Inc() Number { - return n.Add(One()) -} +func (n Number) Inc() Number { return n.Add(One()) } // Dec retourne n − 1. -func (n Number) Dec() Number { - return n.Sub(One()) -} +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), + s := n1.Sign() * n2.Sign() + if nb1, ok := n1.get(); ok { + if nb2, ok := n2.get(); ok { + 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 { - out := Number{ - base: n1.base, - tpe: max(n1.tpe, n2.tpe), - atom: n1.div(n2.atom), + s := n1.Sign() * n2.Sign() + if nb1, ok := n1.get(); ok { + if nb2, ok := n2.get(); ok { + if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 { + return Nan() + } + + 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 out.format() + 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). func (n1 Number) Quo(n2 Number) Number { - out := Number{ - base: n1.base, - tpe: Integer, - atom: n1.quo(n2.atom), + if nb1, ok := n1.rat(); ok { + if nb2, ok := n2.rat(); ok { + if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 { + return Nan() + } + + 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 out.format() + 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. func (n1 Number) Rem(n2 Number) Number { - out := Number{ - base: n1.base, - tpe: max(n1.tpe, n2.tpe), - atom: n1.rem(n2.atom), - } + _, r := n1.QuoRem(n2) - return out.format() + return r } -// 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) +func (n Number) pow(p int64) (out Number) { + inv := p < 0 + p = abs(p) - return q.format(), r.format() + 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) + } + + if inv { + out = out.Inv() + } + return +} +func (n Number) optimize(precision Number) (out Number) { + out = n + if n.Denom().Gt(precision) { + out = n.Num().Mul(precision).Quo(n.Denom()).Div(precision).ToType(n.Type()) + } + + return +} +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 } -// Inv retourne 1 ÷ n. -func (n Number) Inv() Number { - t := n.tpe - if t == Integer { - t = Fraction +// Pow retourne n1 ^ n2. +func (n1 Number) Pow(n2 Number) Number { + if !n2.IsDefined() { + return Nan() } - out := Number{ - tpe: t, - base: n.base, - atom: n.inv(), + p2, s2 := n2.NumDenom() + p, pok := p2.ToInt64() + s, sok := s2.ToUint64() + if !pok || !sok { + return Nan() } - if n.tpe == Integer && out.IsInt() { - out.tpe = Integer + out := n1.pow(p) + if s > 1 { + out = out.sqrt(s) } - return out.format() + 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 { + 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) + } + + return n.Clone() +} + +// Lsh retourne n1 << n2. +func (n1 Number) Lsh(n2 Number) Number { + if s, ok := n2.ToUint64(); ok { + return n1.lsh(s) + } + + 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!. @@ -315,7 +609,7 @@ func (n Number) Fact() Number { case !n.IsInt() || n.IsNeg(): return Nan() default: - out := One(n.base) + out := One(n.Base()) for e := Two(); e.Le(n); e = e.Inc() { out.Mul(e) } @@ -323,104 +617,135 @@ func (n Number) Fact() Number { } } -// Lsh retourne n1 << n2. -func (n1 Number) Lsh(n2 Number) Number { - if n2.IsNeg() { - return n1.Rsh(n2.Abs()) +func (n Number) text() string { + if nb, ok := n.get(); ok { + return nb.Num().Text(int(n.Base())) } - - if n, ok := n2.toInt64(); ok && n >= 0 { - return n1.set(n1.lsh(uint(n)), true) - } - return Nan() + return "" } - -// Rsh retourne n1 << n2. -func (n1 Number) Rsh(n2 Number) Number { - if n2.IsNeg() { - return n1.Lsh(n2.Abs()) +func (n Number) undefinedString() string { + switch n.Sign() { + case -1: + return "-∞" + case +1: + return "+∞" + default: + return "NaN" } - - if n, ok := n2.toInt64(); ok && n >= 0 { - return n1.set(n1.rsh(uint(n)), true) - } - return Nan() } +func (n Number) intString() (out string) { + out = n.text() -// 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) + if n.Sign() < 0 { + out = fmt.Sprintf("-%s", out) } - return -1 -} - -// Pow retourne n1 ^ n2. -func (n1 Number) Pow(n2 Number) Number { - if !n2.IsDefined() { - return Nan() + if base := n.Base(); base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) } - 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() + return +} +func (n Number) fracString() string { + 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() + } + + p := int(FloatingPrecision) + if len(out) < p { + out = fmt.Sprintf("%s%s", strings.Repeat("0", p), out) + } + + dot := len(out) - p + out = fmt.Sprintf("%s.%s", out[:dot], out[dot:]) + + if !FixedPrecision { + l := len(out) - 1 + for out[l] == '0' { + out, l = out[:l], l-1 + } + if out == "." { + out = "0." } } - 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() - } + if sign < 0 { + out = fmt.Sprintf("-%s", out) + } else if q.IsZero() && sign > 0 { + out = fmt.Sprintf("+%s", out) } - 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() + 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))) + } + + if num.Lt(denom) { + exponent-- + num = num.Mul(nBase) + } + tmp = num.Div(denom) + } + + if sign < 0 { + tmp = tmp.Neg() + } + + signExponent := "" + if exponent > 0 { + signExponent = "+" + } + + out = tmp.decString() + if base == 10 { + return fmt.Sprintf("%sE%s%d", out, signExponent, exponent) + } + return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent) } -// Sqrt retourne la racine carrée de n. -func (n Number) Sqrt() Number { - return n.Sqrtn(Int(2)) +// 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() + } } diff --git a/number/operations.go b/number/operations.go index fb7da44..a30f415 100644 --- a/number/operations.go +++ b/number/operations.go @@ -11,6 +11,7 @@ type Op2To2Func func(Number, Number) (Number, Number) type ReduceFunc 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 { return n.ToBase(formatBase(base)) } @@ -24,36 +25,33 @@ func toType[N integer](n Number, t NumberType, base ...N) Number { return n } -func ToInteger[N integer](n Number, base ...N) Number { - return toType(n, Integer, base...) -} +// ToInteger convertit n en entier. +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...) -} +// ToDecimal convertit n en décimal. +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...) -} +// ToFraction convertit n en fraction. +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...) -} +// ToScientific convertit n en nombre scientifique. +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() } +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 IntOf(n.Len()) } +func Sqrt(n Number) Number { return n.Sqrt() } +func Square(n Number) Number { return n.Square() } // 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 Ne(n1, n2 Number) Number { return ParseBool(n1.Ne(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 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 Bit(n1, n2 Number) Number { return IntOf(n1.Bit(n2)) } func Pow(n1, n2 Number) Number { return n1.Pow(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) { return Reduce(func(n1, n2 Number) Number { if n2.Gt(n1) { @@ -114,6 +113,7 @@ func Max(numbers ...Number) (n Number) { })(numbers...) } +// Min retourne le nombre le plus petit de la liste. func Min(numbers ...Number) (n Number) { return Reduce(func(n1, n2 Number) Number { if n2.Lt(n1) { @@ -123,35 +123,100 @@ func Min(numbers ...Number) (n Number) { })(numbers...) } -func Sum(numbers ...Number) (n Number) { - return Reduce(Add)(numbers...) -} +// Sum retourne la somme des nombres. +func Sum(numbers ...Number) (n Number) { return Reduce(Add)(numbers...) } +// Mean retourne la moyenne des nombres. func Mean(numbers ...Number) (n Number) { l := len(numbers) if l == 0 { 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 { if !n.IsDefined() || n.Type() == Integer { return n } - p := Number{ - base: n.Base(), - tpe: n.Type(), - atom: pow(formatBase(base...), precision), + b := n.Base() + if len(base) > 0 { + b = formatBase(base...) } - p.format() + + p := pow(b, precision) 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 l’ordre de la liste des nombres. func Reverse(numbers ...Number) []Number { l := len(numbers) for i := 0; i < l>>1; i++ { @@ -162,6 +227,7 @@ func Reverse(numbers ...Number) []Number { return numbers } +// Sort trie les nombres par ordre croissant. func Sort(numbers ...Number) []Number { sort.Slice(numbers, func(i, j int) bool { return numbers[i].Lt(numbers[j]) @@ -170,6 +236,7 @@ func Sort(numbers ...Number) []Number { return numbers } +// SortDesc trie les nombres par ordre décroissant. func SortDesc(numbers ...Number) []Number { sort.Slice(numbers, func(i, j int) bool { return numbers[i].Gt(numbers[j]) diff --git a/number/util.go b/number/util.go index 219f2b2..127baf1 100644 --- a/number/util.go +++ b/number/util.go @@ -1,5 +1,11 @@ package number +import ( + "math/big" + + "gitea.zaclys.com/bvaudour/gob/option" +) + type integer interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 } @@ -8,6 +14,8 @@ type float interface { ~float32 | ~float64 } +type rat = option.Option[*big.Rat] + func signOf[N integer | float](n N) int { switch { 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 { return signOf(n1 - n2) }