diff --git a/number/atom.go b/number/atom.go new file mode 100644 index 0000000..2b71510 --- /dev/null +++ b/number/atom.go @@ -0,0 +1,502 @@ +package number + +import ( + "math/big" + + "gitea.zaclys.com/bvaudour/gob/option" +) + +type atom struct { + number option.Option[*big.Rat] + sign int +} + +func undefined[S integer | float](sign S) atom { + out := atom{ + sign: signOf(sign), + } + + return out.format() +} + +func rational[S integer | float](rat *big.Rat, sign ...S) atom { + s := 1 + if len(sign) > 0 { + s = signOf(sign[0]) + } + + out := atom{ + number: option.Some(rat), + sign: s, + } + + return out.format() +} + +func frac0[S integer | float](num, denom *big.Int, sign ...S) atom { + if denom.IsInt64() && denom.Int64() == 0 { + s := num.Sign() + if len(sign) > 0 { + s *= signOf(sign[0]) + } + return undefined(s) + } + + rat := new(big.Rat).SetFrac(num, denom) + return rational(rat, sign...) +} + +func frac[N integer | float, S integer | float](num, denom N, sign ...S) atom { + if denom == 0 { + s := signOf(num) + if len(sign) > 0 { + s *= signOf(sign[0]) + } + return undefined(s) + } + + rat := new(big.Rat).SetFrac64(int64(num), int64(denom)) + return rational(rat, sign...) +} + +func entire0[S integer | float](n *big.Int, sign ...S) atom { + rat := new(big.Rat).SetInt(n) + + return rational(rat, sign...) +} + +func entire[N integer | float, S integer | float](n N, sign ...S) atom { + rat := new(big.Rat).SetInt64(int64(n)) + + return rational(rat, sign...) +} + +func decimal[N integer | float, S integer | float](n N, sign ...S) atom { + rat := new(big.Rat).SetFloat64(float64(n)) + + return rational(rat, sign...) +} + +func pow[B integer | float, P integer | float](base B, precision ...P) atom { + p := FloatingPrecision + if len(precision) > 0 { + p = uint64(precision[0]) + } + + a := entire(formatBase(base), 1) + return a.pow(p) +} + +func (a atom) get() (*big.Rat, bool) { + return a.number.Get() +} + +func (a *atom) format() atom { + if nb, ok := a.get(); ok { + a.sign *= nb.Sign() + nb.Abs(nb) + } + + a.sign = signOf(a.sign) + return *a +} + +func (a atom) clone(format ...bool) atom { + out := atom{ + sign: a.sign, + } + if nb, ok := a.get(); ok { + out.number = option.Some(new(big.Rat).Set(nb)) + } + + if len(format) > 0 && format[0] { + return out.format() + } + return out +} + +func (a atom) rat() (nb *big.Rat, ok bool) { + if nb, ok = a.get(); ok { + nb = new(big.Rat).Set(nb) + if a.sign < 0 { + nb.Neg(nb) + } else if a.sign == 0 { + nb.SetInt64(0) + } + } + + return +} + +func (a atom) Sign() int { + return a.sign +} + +func (a atom) IsNeg() bool { + return a.Sign() < 0 +} + +func (a atom) IsPos() bool { + return !a.IsNeg() +} + +func (a atom) IsZero() bool { + return a.Is(0) +} + +func (a atom) neg() atom { + out := a.clone() + out.sign = -out.sign + + return out.format() +} + +func (a atom) abs() atom { + out := a.clone() + if out.sign < 0 { + out.sign = -out.sign + } + + return out.format() +} + +func (a atom) IsDefined() bool { + return a.number.IsDefined() +} + +func (a atom) IsInf() bool { + return !a.IsDefined() && a.Sign() > 0 +} + +func (a atom) IsNegInf() bool { + return !a.IsDefined() && a.Sign() < 0 +} + +func (a atom) IsNan() bool { + return !a.IsDefined() && a.Sign() == 0 +} + +func (a atom) Is(i int64) bool { + if nb, ok := a.get(); ok { + return nb.IsInt() && nb.Num().Int64()*int64(a.Sign()) == i + } + + return false +} + +func (a atom) IsFloat(f float64) bool { + if nb, ok := a.get(); ok { + e, ok := nb.Float64() + return ok && e*float64(a.Sign()) == f + } + + return false +} + +func (a atom) isInt() bool { + if nb, ok := a.get(); ok { + return nb.IsInt() + } + + return false +} + +func (a atom) isInt64() bool { + if nb, ok := a.get(); ok { + return nb.IsInt() && nb.Num().IsInt64() + } + return false +} + +func (a atom) toInt64() (n int64, ok bool) { + var nb *big.Rat + if nb, ok = a.get(); ok { + if nb.IsInt() { + if num := nb.Num(); num.IsInt64() { + n = num.Int64() + } + } + } + + return +} + +func (a *atom) setInt() atom { + if nb, ok := a.get(); ok { + nb.SetInt(new(big.Int).Quo(nb.Num(), nb.Denom())) + } + + return a.format() +} + +func (a atom) num() atom { + if a.Sign() == 0 { + return entire(0, 0) + } else if nb, ok := a.get(); ok { + return entire0(nb.Num(), a.Sign()) + } + + return entire(a.Sign(), 1) +} + +func (a atom) denom() atom { + if nb, ok := a.get(); ok { + return entire0(nb.Denom(), 1) + } + + return entire(0, 0) +} + +func (a1 atom) cmp(a2 atom) int { + if a1.IsNan() { + if a2.IsNan() { + return 0 + } + return -2 + } else if a2.IsNan() { + return 2 + } else if nb1, ok := a1.rat(); ok { + if nb2, ok := a2.rat(); ok { + return nb1.Cmp(nb2) + } + return -a2.Sign() + } else if a2.IsDefined() { + return a1.Sign() + } + + return compare(a1.Sign(), a2.Sign()) +} + +func (a1 atom) add(a2 atom) atom { + if r1, ok := a1.rat(); ok { + if r2, ok := a2.rat(); ok { + return rational(new(big.Rat).Add(r1, r2), 1) + } + return a2.clone() + } else if a2.IsDefined() || a1.Sign() == a2.Sign() { + return undefined(a1.Sign()) + } + + return undefined(0) +} + +func (a1 atom) sub(a2 atom) atom { + return a1.add(a2.neg()) +} + +func (a atom) inc() atom { + return a.add(entire(1, 1)) +} + +func (a atom) dec() atom { + return a.sub(entire(1, 1)) +} + +func (a1 atom) mul(a2 atom) atom { + s := a1.Sign() * a2.Sign() + if nb1, ok := a1.get(); ok { + if nb2, ok := a2.get(); ok { + return rational(new(big.Rat).Mul(nb1, nb2), s) + } + } + + return undefined(s) +} + +func (a1 atom) div(a2 atom) atom { + if nb1, ok := a1.get(); ok { + if nb2, ok := a2.get(); ok { + if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 { + return undefined(0) + } + return rational(new(big.Rat).Quo(nb1, nb2), a1.Sign()*a2.Sign()) + } + return undefined(a1.Sign() * a2.Sign()) + } else if a2.IsDefined() { + return undefined(a1.Sign() * a2.Sign()) + } + + return undefined(0) +} + +func (a1 atom) quo(a2 atom) atom { + if nb1, ok := a1.get(); ok { + if nb2, ok := a2.get(); ok { + if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 { + return undefined(0) + } + n := new(big.Int).Mul(nb1.Num(), nb2.Num()) + d := new(big.Int).Mul(nb1.Denom(), nb2.Denom()) + return entire0(new(big.Int).Quo(n, d), a1.Sign()*a2.Sign()) + } + return undefined(a1.Sign() * a2.Sign()) + } else if a2.IsDefined() { + return undefined(a1.Sign() * a2.Sign()) + } + + return undefined(0) +} + +func (a1 atom) quoRem(a2 atom) (q, r atom) { + q = a1.quo(a2) + r = a1.sub(a2.mul(q)) + + return +} + +func (a1 atom) rem(a2 atom) atom { + _, r := a1.quoRem(a2) + return r +} + +func (a atom) inv() atom { + if nb, ok := a.get(); ok { + n, d := nb.Num(), nb.Denom() + return frac0(d, n, a.Sign()) + } + + if a.Sign() == 0 { + return undefined(0) + } + return entire(0, 0) +} + +func (a atom) pow(p uint64) atom { + switch { + case p == 0: + if a.IsNan() { + return undefined(0) + } + return entire(0, 0) + case p == 1: + return a.clone(true) + case p == 2: + return a.mul(a) + case p&1 == 0: + return a.mul(a).pow(p >> 1) + default: + return a.mul(a).pow(p >> 1).mul(a) + } +} + +func (a atom) optimize(precision atom) atom { + if a.isInt() || !a.IsDefined() { + return a + } + + n1, d1 := a.num().abs(), a.denom() + t := n1.mul(precision).quo(d1) + an := t.div(precision) + n2, d2 := an.num(), an.denom() + if n2.cmp(n1) < 0 || d2.cmp(d1) < 0 { + t.sign = a.sign + return t.format() + } + + return a +} + +func (a atom) heron(n uint64, tmp atom) atom { + n0 := entire(int64(n), 1) + n1 := n0.dec() + + return ((tmp.mul(n1)).add(a.div(tmp.pow(n - 1)))).div(n0) +} + +func (a atom) sqrtn(n uint64, base uint) atom { + switch { + case a.IsNan() || n < 2 || (a.IsNeg() && n&1 == 0): + return undefined(0) + case a.IsZero() || a.Is(1) || !a.IsDefined(): + return a.clone(true) + default: + precision := pow(base, FloatingPrecision) + pi := precision.inv() + out := a.heron(n, a).optimize(precision) + for { + t := a.heron(n, out).optimize(precision) + if out.cmp(t) == 0 { + break + } else if out.sub(t).abs().cmp(pi) < 0 { + out = t + break + } + } + + return out + } +} + +func (a atom) IsEven() bool { + if nb, ok := a.get(); ok { + return nb.IsInt() && nb.Num().Bit(0) == 0 + } + + return false +} + +func (a atom) IsOdd() bool { + if nb, ok := a.get(); ok { + return nb.IsInt() && nb.Num().Bit(0) != 0 + } + + return false +} + +func (a atom) lsh(n uint) atom { + if nb, ok := a.get(); ok { + if nb.IsInt() { + num, denom := new(big.Int).Lsh(nb.Num(), n), nb.Denom() + return frac0(num, denom, a.Sign()) + } + return a.mul(pow(2, n)) + } + + return undefined(a.Sign()) +} + +func (a atom) rsh(n uint) atom { + if nb, ok := a.get(); ok { + if nb.IsInt() { + num, denom := new(big.Int).Rsh(nb.Num(), n), nb.Denom() + return frac0(num, denom, a.Sign()) + } + return a.div(pow(2, n)) + } + + return undefined(a.Sign()) +} + +func (a atom) len(base uint) int { + if nb, ok := a.get(); ok { + if !nb.IsInt() { + return -1 + } + num := nb.Num() + if num.IsInt64() && num.Int64() == 0 { + return 1 + } + s := num.Text(int(base)) + return len(s) + } + + return -1 +} + +func (a atom) bit(n uint64, base uint) int { + if !a.isInt() { + return -1 + } + + out := a + if n > 0 { + out = a.quo(pow(base, n)) + } + + out = out.rem(entire(base, 1)) + if nb, ok := out.get(); ok { + return int(nb.Num().Int64()) + } + + return -1 +} diff --git a/number/const.go b/number/const.go index 3a1ee97..9ad31da 100644 --- a/number/const.go +++ b/number/const.go @@ -7,56 +7,42 @@ 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 de chiffres après la virgule est fixe. + FixedPrecision = false // Si vrai le nombre chiffre après la virgule est fixe. ) const ( - 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+\^` + 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 ( - 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) + 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)) - 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)) + 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 index 39bcfa7..9b7c44d 100644 --- a/number/convert.go +++ b/number/convert.go @@ -9,109 +9,160 @@ import ( "gitea.zaclys.com/bvaudour/gob/option" ) -// 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 undefinedString(sign int) string { + switch sign { + case -1: + return "-∞" + case +1: + return "+∞" + default: + return "NaN" } } -// Nan retourne NaN. -func Nan() Number { return Undefined(0) } +func integerString(n *big.Int, base uint, sign int) string { + out := n.Text(int(base)) -// 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 sign < 0 { + out = fmt.Sprintf("-%s", out) } - n := Number{ - number: option.Some(new(big.Rat).SetInt(i)), - sign: 1, - base: formatBase(base...), - tpe: Integer, + if base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) } - return n.format() + 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 fractionString(n, d *big.Int, base uint, sign int) string { + out := fmt.Sprintf("%s/%s", n.Text(int(base)), d.Text(int(base))) -func pow[N integer | float, M integer | float](b N, p ...M) Number { - if len(p) > 0 { - return IntOf(b).pow(int64(p[0])) - } - return IntOf(b).pow(int64(FloatingPrecision)) -} - -// Zero retourne le nombre 0. -func Zero(base ...uint) Number { return IntOf(0, base...) } - -// 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() + if sign < 0 { + out = fmt.Sprintf("-%s", out) } - n := Number{ - number: option.Some(new(big.Rat).Set(f)), - sign: 1, - base: formatBase(base...), - tpe: Decimal, + if base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) } - return n.format() + return out } -// 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 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) -// 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() + out := "0" + if num.cmp(denom) >= 0 { + if qn, ok := q.get(); ok { + out = qn.Num().Text(int(base)) + } else { + return "" + } } - n := Number{ - number: option.Some(new(big.Rat).SetFrac(num, denom)), - sign: 1, - base: formatBase(base...), - tpe: Fraction, + precision := int(FloatingPrecision) + if len(out) < precision { + out = fmt.Sprintf("%s%s", strings.Repeat("0", precision), out) } - return n.format() + 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 } -// 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 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) } -func parseBaseN(str string) (next string, base option.Option[uint]) { +// 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) { @@ -120,7 +171,7 @@ func parseBaseN(str string) (next string, base option.Option[uint]) { return } -func parseBaseB(str string) (next string, base uint) { +func parseBaseN(str string) (next string, base uint) { next = str[1:] switch str[0] { case 'B', 'b': @@ -134,7 +185,7 @@ func parseBaseB(str string) (next string, base uint) { return } -func parseSignN(str string) (next string, sign int) { +func parseSign(str string) (next string, sign int) { switch str[0] { case '-': next, sign = str[1:], -1 @@ -147,7 +198,7 @@ func parseSignN(str string) (next string, sign int) { return } -func parseSignB(str string) (next string, sign int) { +func parseSignN(str string) (next string, sign int) { next, sign = str[1:], 1 if str[0] == '1' { sign = -1 @@ -166,40 +217,226 @@ 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 { - if sign < 0 { - nb.Neg(nb) + nn := Number{ + base: base, + tpe: Integer, + atom: entire0(nb, sign), } - n = option.Some(IntOf0(nb, base)) + n = option.Some(nn.format()) } return } -func parseInt10(str string) (n option.Option[Number]) { - next, sign := parseSignN(str) +func parseInt(str string) (n option.Option[Number]) { + next, base := parseBase(str) - return setInt(next, 10, sign) -} - -func parseIntB(str string) (n option.Option[Number]) { - next, sign := parseSignB(str) - next, base := parseBaseB(next) - - return setInt(next, base, sign) -} - -func parseIntN(str string) (n option.Option[Number]) { - next, base := parseBaseN(str) if b, ok := base.Get(); ok { - next, sign := parseSignN(next) + 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) (out option.Option[Number]) { +func Parse(str string) option.Option[Number] { switch str { case "+∞", "", "<+inf>": return option.Some(Inf()) @@ -210,50 +447,33 @@ func Parse(str string) (out option.Option[Number]) { } switch { + case regInt.MatchString(str): + return parseInt(str) case regInt10.MatchString(str): return parseInt10(str) - case regIntB.MatchString(str): - return parseIntB(str) - case regIntN.MatchString(str): + case regInt2.MatchString(str), regInt8.MatchString(str), regInt16.MatchString(str): return parseIntN(str) - case regDec.MatchString(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 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 + return option.None[Number]() } -// ParseBool retourne 1 si vrai, et 0 sinon. func ParseBool(b bool) Number { if b { return One() @@ -261,5 +481,6 @@ func ParseBool(b bool) Number { return Zero() } -// ToBool retourne vrai si le nombre n’est ni NaN ni 0. -func ToBool(n Number) bool { return n.Sign() != 0 } +func ToBool(n Number) bool { + return !n.IsZero() && !n.IsNan() +} diff --git a/number/number.go b/number/number.go index 320b2bc..fde7a9b 100644 --- a/number/number.go +++ b/number/number.go @@ -1,13 +1,5 @@ package number -import ( - "fmt" - "math/big" - "strings" - - "gitea.zaclys.com/bvaudour/gob/option" -) - // NumberType représente le type d’un nombre. type NumberType uint @@ -20,585 +12,299 @@ const ( // Number représente un nombre. type Number struct { - number rat - sign int - tpe NumberType - base uint + atom + tpe NumberType + base uint } -func (n *Number) get() (r *big.Rat, ok bool) { - return n.number.Get() +// 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) 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) - } +// 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 + return out.format(format...) } -func (n *Number) format() Number { - n.base, n.sign, n.tpe = formatBase(n.base), signOf(n.sign), min(n.tpe, Fraction) +func (n *Number) format(formatAtom ...bool) Number { + if len(formatAtom) > 0 && formatAtom[0] { + n.atom.format() + } - 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.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) set(r *big.Rat, tpe NumberType, sign ...int) Number { - s := 1 - if len(sign) > 0 { - s = sign[0] - } +func (n Number) clone(format ...bool) Number { out := Number{ - 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, + atom: n.atom.clone(format...), tpe: n.tpe, base: n.base, } - if nb, ok := n.get(); ok { - out.number = option.Some(new(big.Rat).Set(nb)) + if len(format) > 0 && format[0] { + return out.format() } - return out } -// Base retourne la base utilisée pour l’affichage du nombre. -func (n Number) Base() uint { return n.base } +// Type retourne le type de nombre. +func (n Number) Type() NumberType { + return n.tpe +} -// Type retourne le type de nombre (entier, décimal, fraction ou scientifique). -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 + } -// Sign retourne : -// - 0 si n = 0 ou NaN -// - 1 si n > 0 -// - -1 si n < 0 -func (n Number) Sign() int { return n.sign } + 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() + out := n.clone(true) out.base = formatBase(base) return out } -// 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 -} - -// 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 { - 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 { - if n.Sign() == 0 { - return IntOf(0, n.Base()) - } else if nb, ok := n.rat(); ok { - return IntOf0(nb.Num(), n.Base()) - } - - return IntOf(n.Sign(), n.Base()) -} - -// Denom retourne le dénominateur. -func (n Number) Denom() Number { - if nb, ok := n.get(); ok { - return IntOf0(nb.Denom(), n.Base()) - } - - return IntOf(0, n.Base()) -} - -// 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 + return n.set(n.neg()) } // Abs retourne |n|. func (n Number) Abs() Number { - out := n.Clone() - out.sign = abs(out.sign) + return n.set(n.abs()) +} - return out +// 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 si n1 = n2 -// - 2 ou -2 si les nombres ne sont pas comparables (ie. n1 = NaN ou n2 = NaN) +// - +1 si n1 > n2 +// - 0 sinon. func (n1 Number) Cmp(n2 Number) int { - 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()) + return n1.cmp(n2.atom) } -// 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 { - 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()) + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.add(n2.atom), } - return Nan() + return out.format() } -// Sub retourne n1 − n2. -func (n1 Number) Sub(n2 Number) Number { return n1.Add(n2.Neg()) } +// 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()) } +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 { - 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) - } + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.mul(n2.atom), } - return Undefined(s) + return out.format() } -// Div retourne n1 / n2 (division exacte). +// Div retourne n1 ÷ n2 (division exacte). func (n1 Number) Div(n2 Number) Number { - 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) + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.div(n2.atom), } - return Nan() + return out.format() } -// 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 { - 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()) + out := Number{ + base: n1.base, + tpe: Integer, + atom: n1.quo(n2.atom), } - 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 + return out.format() } // Rem retourne n1 % n2. func (n1 Number) Rem(n2 Number) Number { - _, r := n1.QuoRem(n2) + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.rem(n2.atom), + } - return r + return out.format() } -func (n Number) pow(p int64) (out Number) { - inv := p < 0 - p = abs(p) +// 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) - 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 + return q.format(), r.format() } -// Pow retourne n1 ^ n2. -func (n1 Number) Pow(n2 Number) Number { - if !n2.IsDefined() { - return Nan() +// Inv retourne 1 ÷ n. +func (n Number) Inv() Number { + t := n.tpe + if t == Integer { + t = Fraction } - p2, s2 := n2.NumDenom() - p, pok := p2.ToInt64() - s, sok := s2.ToUint64() - if !pok || !sok { - return Nan() + out := Number{ + tpe: t, + base: n.base, + atom: n.inv(), } - out := n1.pow(p) - if s > 1 { - out = out.sqrt(s) + if n.tpe == Integer && out.IsInt() { + out.tpe = Integer } - 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 + return out.format() } // Fact retourne n!. @@ -609,7 +315,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) } @@ -617,135 +323,104 @@ func (n Number) Fact() Number { } } -func (n Number) text() string { - if nb, ok := n.get(); ok { - return nb.Num().Text(int(n.Base())) +// Lsh retourne n1 << n2. +func (n1 Number) Lsh(n2 Number) Number { + if n2.IsNeg() { + return n1.Rsh(n2.Abs()) } - return "" + + if n, ok := n2.toInt64(); ok && n >= 0 { + return n1.set(n1.lsh(uint(n)), true) + } + return Nan() } -func (n Number) undefinedString() string { - switch n.Sign() { - case -1: - return "-∞" - case +1: - return "+∞" - default: - 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() } -func (n Number) intString() (out string) { - out = n.text() - if n.Sign() < 0 { - out = fmt.Sprintf("-%s", out) - } - - if base := n.Base(); base != 10 { - out = fmt.Sprintf("(%d)%s", base, out) - } - - return +// 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) } -func (n Number) fracString() string { - num, denom := n.NumDenom() - return fmt.Sprintf("%s/%s", num.intString(), denom.intString()) +// 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 } -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() +// Pow retourne n1 ^ n2. +func (n1 Number) Pow(n2 Number) Number { + if !n2.IsDefined() { + return Nan() } - 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." + 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 sign < 0 { - out = fmt.Sprintf("-%s", out) - } else if q.IsZero() && sign > 0 { - out = fmt.Sprintf("+%s", out) + 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 base != 10 { - out = fmt.Sprintf("(%d)%s", base, out) - } - - return + return Nan() } -func (n Number) sciString() (out string) { - base, sign := n.Base(), n.Sign() - var exponent int - tmp := n - if !n.IsZero() { - num, denom := n.Abs().NumDenom() - nl, dl := num.Len(), denom.Len() - exponent = nl - dl - nBase := IntOf(base) - if exponent > 0 { - denom = denom.Mul(nBase.pow(int64(exponent))) - } else if exponent < 0 { - num = num.Mul(nBase.pow(int64(-exponent))) + +// Sqrtn retourne la racine n2ième de n1. +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 } - if num.Lt(denom) { - exponent-- - num = num.Mul(nBase) - } - tmp = num.Div(denom) + return out.format() } - 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) + return Nan() } -// 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() - } +// 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 index a30f415..fb7da44 100644 --- a/number/operations.go +++ b/number/operations.go @@ -11,7 +11,6 @@ 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)) } @@ -25,33 +24,36 @@ func toType[N integer](n Number, t NumberType, base ...N) Number { return n } -// ToInteger convertit n en entier. -func ToInteger[N integer](n Number, base ...N) Number { return toType(n, Integer, base...) } +func ToInteger[N integer](n Number, base ...N) Number { + return toType(n, Integer, base...) +} -// ToDecimal convertit n en décimal. -func ToDecimal[N integer](n Number, base ...N) Number { return toType(n, Decimal, base...) } +func ToDecimal[N integer](n Number, base ...N) Number { + return toType(n, Decimal, base...) +} -// ToFraction convertit n en fraction. -func ToFraction[N integer](n Number, base ...N) Number { return toType(n, Fraction, base...) } +func ToFraction[N integer](n Number, base ...N) Number { + return toType(n, Fraction, base...) +} -// ToScientific convertit n en nombre scientifique. -func ToScientific[N integer](n Number, base ...N) Number { return toType(n, Scientific, base...) } +func ToScientific[N integer](n Number, base ...N) Number { + return toType(n, Scientific, base...) +} // Fonctions de type f(n) → n -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() } +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 IntOf(n1.Cmp(n2)) } +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)) } @@ -66,7 +68,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 IntOf(n1.Bit(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) } @@ -103,7 +105,6 @@ 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) { @@ -113,7 +114,6 @@ 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,100 +123,35 @@ func Min(numbers ...Number) (n Number) { })(numbers...) } -// Sum retourne la somme des nombres. -func Sum(numbers ...Number) (n Number) { return Reduce(Add)(numbers...) } +func Sum(numbers ...Number) (n Number) { + return Reduce(Add)(numbers...) +} -// Mean retourne la moyenne des nombres. func Mean(numbers ...Number) (n Number) { l := len(numbers) if l == 0 { return Nan() } - return Sum(numbers...).Div(IntOf(l)) + return Sum(numbers...).Div(Int(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 } - b := n.Base() - if len(base) > 0 { - b = formatBase(base...) + p := Number{ + base: n.Base(), + tpe: n.Type(), + atom: pow(formatBase(base...), precision), } - - p := pow(b, precision) + p.format() num, denom := n.Num().Mul(p), n.Denom() - return num.Quo(denom).Div(p).ToType(n.Type()) + return num.Quo(denom).Div(p) } -// Reverse inverse l’ordre de la liste des nombres. func Reverse(numbers ...Number) []Number { l := len(numbers) for i := 0; i < l>>1; i++ { @@ -227,7 +162,6 @@ 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]) @@ -236,7 +170,6 @@ 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 127baf1..219f2b2 100644 --- a/number/util.go +++ b/number/util.go @@ -1,11 +1,5 @@ package number -import ( - "math/big" - - "gitea.zaclys.com/bvaudour/gob/option" -) - type integer interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 } @@ -14,8 +8,6 @@ type float interface { ~float32 | ~float64 } -type rat = option.Option[*big.Rat] - func signOf[N integer | float](n N) int { switch { case n < 0: @@ -27,14 +19,6 @@ 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) } diff --git a/option/result.go b/option/result.go index a6f62e1..ba63397 100644 --- a/option/result.go +++ b/option/result.go @@ -1,9 +1,5 @@ package option -import ( - "fmt" -) - // Result stocke un résultat : // - soit le résultat est valide, et une valeur est stockée, // - soit le résultat est invalide, et une erreur est stockée. @@ -45,11 +41,3 @@ func (r Result[T]) Err() (err error, ok bool) { func (r Result[T]) IsOk() bool { return r.ok } - -func (r Result[T]) String() string { - return fmt.Sprintf(`{ - value: %v, - error: %s, - ok: %v, - }`, r.v, r.err, r.ok) -} diff --git a/shell/console/atom/input.go b/shell/console/atom/input.go index c686177..fbf1d1b 100644 --- a/shell/console/atom/input.go +++ b/shell/console/atom/input.go @@ -56,8 +56,11 @@ func (in *input) restart() { n := in.readRune() next <- n - r, ok := n.Ok() - needClose := !ok || r == Lf || r == Cr || r == C_C || r == C_D + needClose := !n.IsOk() + if !needClose { + r, ok := n.Ok() + needClose = ok && (r == Lf || r == Cr || r == C_C || r == C_D) + } if needClose { close(next) @@ -314,21 +317,22 @@ func (in *input) nextChar() (key nkey) { return } + var s Sequence switch r { case Bs: - key = nk(keyS(A_Bs)) + s = A_Bs case 'O': - key = in.escO() + return in.escO() case '[': - key = in.escBracket() + return in.escBracket() case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': - key = nk(keyS(Sequence(r << 16))) - //default: - // return + s = Sequence(r << 16) + default: + return } in.clear() - return + return nk(keyS(s)) }