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 new file mode 100644 index 0000000..9ad31da --- /dev/null +++ b/number/const.go @@ -0,0 +1,48 @@ +package number + +import ( + "fmt" + "regexp" +) + +var ( + FloatingPrecision uint64 = 10 // Nombre de chiffres après la virgule pour un nombre décimal ou un nombre scientifique + FixedPrecision = false // Si vrai le nombre chiffre après la virgule est fixe. +) + +const ( + regSign = `\+|-` + regBSign = `0|1` + regBase = `\(\d+\)` + regBase2 = `B|b` + regBase8 = `O|o` + regBase16 = `X|x` + regNb = `[0-9a-zA-Z]` + regNb2 = `0|1` + regNb8 = `[0-7]` + regNb10 = `\d` + regNb16 = `[0-9a-fA-F]` + regExp = `×\d+\^` + regExp10 = `E|e` +) + +var ( + regInt = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+`, regBase, regSign, regNb)) + regInt2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase2, regNb2)) + regInt8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase8, regNb8)) + regInt10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+`, regSign, regNb10)) + regInt16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)(%s)+`, regBSign, regBase16, regNb16)) + + regDec = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.)`, regBase, regSign, regNb, regNb, regNb)) + regDec2 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase2, regNb2, regNb2, regNb2)) + regDec8 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase8, regNb8, regNb8, regNb8)) + regDec10 = regexp.MustCompile(fmt.Sprintf(`(%s)?(%s*\.%s+|%s+\.)`, regSign, regNb10, regNb10, regNb10)) + regDec16 = regexp.MustCompile(fmt.Sprintf(`(%s)(%s)((%s)*\.(%s)+|(%s)+\.)`, regBSign, regBase16, regNb16, regNb16, regNb16)) + + regFrac = regexp.MustCompile(fmt.Sprintf(`%s(%s)?%s+/(%s)?%s+`, regBase, regSign, regNb, regSign, regNb)) + regFrac10 = regexp.MustCompile(fmt.Sprintf(`(%s)?%s+/(%s)?%s+`, regSign, regNb10, regSign, regNb10)) + + regSci = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)%s(%s)?(%s)+`, regBase, regSign, regNb, regNb, regNb, regExp, regSign, regNb10)) + regSci10Simple = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp, regSign, regNb10)) + regSci10 = regexp.MustCompile(fmt.Sprintf(`%s(%s)?(%s*\.%s+|%s+\.?)`, regSign, regNb10, regNb10, regNb10, regExp10, regSign, regNb10)) +) diff --git a/number/convert.go b/number/convert.go new file mode 100644 index 0000000..9b7c44d --- /dev/null +++ b/number/convert.go @@ -0,0 +1,486 @@ +package number + +import ( + "fmt" + "math/big" + "strconv" + "strings" + + "gitea.zaclys.com/bvaudour/gob/option" +) + +func undefinedString(sign int) string { + switch sign { + case -1: + return "-∞" + case +1: + return "+∞" + default: + return "NaN" + } +} + +func integerString(n *big.Int, base uint, sign int) string { + out := n.Text(int(base)) + + if sign < 0 { + out = fmt.Sprintf("-%s", out) + } + + if base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) + } + + return out +} + +func fractionString(n, d *big.Int, base uint, sign int) string { + out := fmt.Sprintf("%s/%s", n.Text(int(base)), d.Text(int(base))) + + if sign < 0 { + out = fmt.Sprintf("-%s", out) + } + + if base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) + } + + return out +} + +func decimalString(n Number) string { + base := n.Base() + num, denom := n.num(), n.denom() + p := pow(base, FloatingPrecision) + num = num.mul(p) + q := num.quo(denom) + + out := "0" + if num.cmp(denom) >= 0 { + if qn, ok := q.get(); ok { + out = qn.Num().Text(int(base)) + } else { + return "" + } + } + + precision := int(FloatingPrecision) + if len(out) < precision { + out = fmt.Sprintf("%s%s", strings.Repeat("0", precision), out) + } + + deltaPrecision := len(out) - precision + out = fmt.Sprintf("%s.%s", out[:deltaPrecision], out[deltaPrecision:]) + + if !FixedPrecision { + l := len(out) - 1 + for out[l] == '0' { + out, l = out[:l], l-1 + } + if out == "." { + out = "0." + } + } + + if n.IsNeg() { + out = fmt.Sprintf("-%s", out) + } else if q.IsZero() && !n.IsZero() { + out = fmt.Sprintf("+%s", out) + } + + if base != 10 { + out = fmt.Sprintf("(%d)%s", base, out) + } + + return out +} + +func scientificStsring(n Number) string { + base := n.Base() + var exponent int + tmp := n + if n.IsZero() { + num, denom := n.Abs().NumDenom() + nl, dl := num.Len(), denom.Len() + exponent := nl - dl + if exponent > 0 { + denom = denom.Mul(Number{ + base: base, + tpe: Integer, + atom: pow(base, exponent), + }) + } else if exponent < 0 { + num = num.Mul(Number{ + base: base, + tpe: Integer, + atom: pow(base, -exponent), + }) + } + + if num.Lt(denom) { + exponent-- + num.Mul(Int(base, base)) + } + tmp = num.Div(denom) + } + + out := decimalString(tmp) + + signExponent := "" + if exponent > 0 { + signExponent = "+" + } else if exponent < 0 { + signExponent = "-" + } + + if base == 10 { + return fmt.Sprintf("%sE%s%d", out, signExponent, exponent) + } + return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent) +} + +// String retourne la représentation du nombre sous forme de chaîne de caractères. +func (n Number) String() string { + if nb, ok := n.get(); ok { + switch n.Type() { + case Integer: + return integerString(nb.Num(), n.Base(), n.Sign()) + case Fraction: + return fractionString(nb.Num(), nb.Denom(), n.base, n.Sign()) + case Scientific: + return scientificStsring(n) + default: + return decimalString(n) + } + } + + return undefinedString(n.Sign()) +} + +func parseBase(str string) (next string, base option.Option[uint]) { + begin := strings.Index(str, "(") + end := strings.Index(str, ")") + if begin > end || begin < 0 || end < 0 { + return + } + + next = str[end+1:] + if b, err := strconv.ParseUint(str[begin+1:end], 10, 64); err == nil && isBaseValid(b) { + base = option.Some(uint(b)) + } + return +} + +func parseBaseN(str string) (next string, base uint) { + next = str[1:] + switch str[0] { + case 'B', 'b': + base = 2 + case 'O', 'o': + base = 8 + default: + base = 16 + } + + return +} + +func parseSign(str string) (next string, sign int) { + switch str[0] { + case '-': + next, sign = str[1:], -1 + case '+': + next, sign = str[1:], 1 + default: + next, sign = str, 1 + } + + return +} + +func parseSignN(str string) (next string, sign int) { + next, sign = str[1:], 1 + if str[0] == '1' { + sign = -1 + } + + return +} + +func parseNumber(str string, base uint) (n option.Option[*big.Int]) { + if e, ok := new(big.Int).SetString(str, int(base)); ok { + n = option.Some(e) + } + + return +} + +func setInt(str string, base uint, sign int) (n option.Option[Number]) { + if nb, ok := parseNumber(str, base).Get(); ok { + nn := Number{ + base: base, + tpe: Integer, + atom: entire0(nb, sign), + } + n = option.Some(nn.format()) + } + + return +} + +func parseInt(str string) (n option.Option[Number]) { + next, base := parseBase(str) + + if b, ok := base.Get(); ok { + next, sign := parseSign(next) + n = setInt(next, b, sign) + } + + return +} + +func parseInt10(str string) (n option.Option[Number]) { + next, sign := parseSign(str) + + return setInt(next, 10, sign) +} + +func parseIntN(str string) (n option.Option[Number]) { + next, sign := parseSignN(str) + next, base := parseBaseN(next) + + return setInt(next, base, sign) +} + +func setFrac(str string, base uint, sign int) (n option.Option[Number]) { + i := strings.Index(str, "/") + if i < 0 { + return + } + + sNum, sDenom := str[:i], str[i:] + if num, ok := parseNumber(sNum, base).Get(); ok { + if denom, ok := parseNumber(sDenom, base).Get(); ok { + nn := Number{ + base: base, + tpe: Fraction, + atom: frac0(num, denom, sign), + } + n = option.Some(nn.format()) + } + } + + return +} + +func parseFrac(str string) (n option.Option[Number]) { + next, base := parseBase(str) + + if b, ok := base.Get(); ok { + next, sign := parseSign(next) + n = setFrac(next, b, sign) + } + + return +} + +func parseFrac10(str string) (n option.Option[Number]) { + next, sign := parseSign(str) + + return setFrac(next, 10, sign) +} + +func parseDot(str string) (next string, dot int) { + i := strings.Index(str, ".") + if i >= 0 { + next = str[:i] + str[:i] + dot = len(next) - i + } + + return +} + +func setDec(str string, base uint, sign int) (n option.Option[Number]) { + next, dot := parseDot(str) + + if num, ok := parseNumber(next, base).Get(); ok { + denom := pow(base, dot) + nn := Number{ + base: base, + tpe: Decimal, + atom: entire0(num, sign).div(denom), + } + n = option.Some(nn.format()) + } + + return +} + +func parseDec(str string) (n option.Option[Number]) { + next, base := parseBase(str) + + if b, ok := base.Get(); ok { + next, sign := parseSign(next) + n = setDec(next, b, sign) + } + + return +} + +func parseDec10(str string) (n option.Option[Number]) { + next, sign := parseSign(str) + + return setDec(next, 10, sign) +} + +func parseDecN(str string) (n option.Option[Number]) { + next, sign := parseSignN(str) + next, base := parseBaseN(next) + + return setDec(next, base, sign) +} + +func parseExp(str string) (next string, baseExponent option.Option[int], exponent option.Option[int]) { + begin := strings.Index(str, "x") + end := strings.Index(str, "^") + if begin > end || begin < 0 || end < 0 { + return + } + + sBase, sExponent := str[begin+1:end], str[end+1:] + + if b, err := strconv.Atoi(sBase); err == nil && isBaseValid(b) { + if e, err := strconv.Atoi(sExponent); err == nil { + next, baseExponent, exponent = str[:begin], option.Some(b), option.Some(e) + } + } + + return +} + +func parseExp10(str string) (next string, exponent option.Option[int]) { + i := strings.Index(strings.ToLower(str), "e") + if i < 0 { + return + } + + sExponent := str[i+1:] + + if e, err := strconv.Atoi(sExponent); err == nil { + next, exponent = str[:i], option.Some(e) + } + + return +} + +func setSci(str string, base uint, sign, baseExponent, exponent int) (n option.Option[Number]) { + next, dot := parseDot(str) + + if num, ok := parseNumber(next, base).Get(); ok { + denom := pow(base, dot) + nn := Number{ + base: base, + tpe: Scientific, + atom: entire0(num, sign).div(denom), + } + if exponent > 0 { + nn.atom = nn.atom.mul(pow(baseExponent, exponent)) + } else if exponent < 0 { + nn.atom = nn.atom.div(pow(baseExponent, -exponent)) + } + + n = option.Some(nn.format()) + } + + return +} + +func parseSci(str string) (n option.Option[Number]) { + next, base := parseBase(str) + + if b, ok := base.Get(); ok { + next, sign := parseSign(next) + next, be, e := parseExp(next) + if baseExponent, ok := be.Get(); ok { + if exponent, ok := e.Get(); ok { + n = setSci(next, b, sign, baseExponent, exponent) + } + } + } + + return +} + +func parseSci10(str string) (n option.Option[Number]) { + next, sign := parseSign(str) + next, be, e := parseExp(next) + + if baseExponent, ok := be.Get(); ok { + if exponent, ok := e.Get(); ok { + n = setSci(next, 10, sign, baseExponent, exponent) + } + } + + return +} + +func parseSci10Simple(str string) (n option.Option[Number]) { + next, sign := parseSign(str) + next, e := parseExp10(next) + + if exponent, ok := e.Get(); ok { + n = setSci(next, 10, sign, 10, exponent) + } + + return +} + +// Parse retourne un nombre à partir d’une chaîne de caractères. +func Parse(str string) option.Option[Number] { + switch str { + case "+∞", "", "<+inf>": + return option.Some(Inf()) + case "-∞", "<-inf>": + return option.Some(NegInf()) + case "NaN", "": + return option.Some(Nan()) + } + + switch { + case regInt.MatchString(str): + return parseInt(str) + case regInt10.MatchString(str): + return parseInt10(str) + case regInt2.MatchString(str), regInt8.MatchString(str), regInt16.MatchString(str): + return parseIntN(str) + case regFrac.MatchString(str): + return parseFrac(str) + case regFrac10.MatchString(str): + return parseFrac10(str) + case regDec.MatchString(str): + return parseInt(str) + case regDec10.MatchString(str): + return parseInt10(str) + case regDec2.MatchString(str), regDec8.MatchString(str), regDec16.MatchString(str): + return parseIntN(str) + case regSci.MatchString(str): + return parseSci(str) + case regSci10.MatchString(str): + return parseSci10(str) + case regSci10Simple.MatchString(str): + return parseSci10Simple(str) + } + + return option.None[Number]() +} + +func ParseBool(b bool) Number { + if b { + return One() + } + return Zero() +} + +func ToBool(n Number) bool { + return !n.IsZero() && !n.IsNan() +} diff --git a/number/number.go b/number/number.go new file mode 100644 index 0000000..fde7a9b --- /dev/null +++ b/number/number.go @@ -0,0 +1,426 @@ +package number + +// NumberType représente le type d’un nombre. +type NumberType uint + +const ( + Integer NumberType = iota + Scientific + Decimal + Fraction +) + +// Number représente un nombre. +type Number struct { + atom + tpe NumberType + base uint +} + +// Undefined retourne un nombre indéfini, signé : +// - si sign < 0, retourne -∞ +// - si sign > 0, retourne +∞ +// - sinon, retourne NaN +func Undefined(sign int) Number { + return Number{ + base: 10, + tpe: Integer, + atom: undefined(sign), + } +} + +// Nan retourne NaN. +func Nan() Number { return Undefined(0) } + +// Inf retourne +∞. +func Inf() Number { return Undefined(1) } + +// NegInf retourne -∞. +func NegInf() Number { return Undefined(-1) } + +// Int retourne un nombre à partir d’un entier, et éventuellement une base. +func Int[N integer](n N, base ...uint) Number { + return Number{ + base: formatBase(base...), + tpe: Integer, + atom: entire(n, 1), + } +} + +// Zero retourne le nombre 0. +func Zero(base ...uint) Number { return Int(0, base...) } + +// One retourne le nombre 1. +func One(base ...uint) Number { return Int(1, base...) } + +// Two retourne le nombre 2. +func Two(base ...uint) Number { return Int(2, base...) } + +// Float retourne un nombre à partir d’un flottant, et éventuellement, une base. +func Float[N ~float32 | ~float64](n N, base ...uint) Number { + return Number{ + base: formatBase(base...), + tpe: Decimal, + atom: decimal(n, 1), + } +} + +// Frac retourne un nombre à partir du numérateur, du dénominateur, et éventuellement, une base. +func Frac[N integer](num N, denom N, base ...uint) Number { + return Number{ + base: formatBase(base...), + tpe: Fraction, + atom: frac(num, denom, 1), + } +} + +func (n Number) set(a atom, format ...bool) Number { + out := Number{ + atom: a, + tpe: n.tpe, + base: n.base, + } + + return out.format(format...) +} + +func (n *Number) format(formatAtom ...bool) Number { + if len(formatAtom) > 0 && formatAtom[0] { + n.atom.format() + } + + n.base = formatBase(n.base) + + if !n.IsDefined() { + n.base = 10 + n.tpe = Integer + } else if n.tpe == Integer && !n.isInt() { + n.setInt() + } + + return *n +} + +func (n Number) clone(format ...bool) Number { + out := Number{ + atom: n.atom.clone(format...), + tpe: n.tpe, + base: n.base, + } + + if len(format) > 0 && format[0] { + return out.format() + } + return out +} + +// Type retourne le type de nombre. +func (n Number) Type() NumberType { + return n.tpe +} + +// ToType convertit le nombre au type donné. +func (n Number) ToType(t NumberType) Number { + out := n.clone() + switch t { + case Integer, Decimal, Fraction, Scientific: + out.tpe = t + default: + out.tpe = Decimal + } + + return out.format(true) +} + +// Base retourne la base du nombre. +func (n Number) Base() uint { + return n.base +} + +// ToBase convertit le nombre dans la base donnée. +func (n Number) ToBase(base uint) Number { + out := n.clone(true) + out.base = formatBase(base) + + return out +} + +// Neg retourne -n. +func (n Number) Neg() Number { + return n.set(n.neg()) +} + +// Abs retourne |n|. +func (n Number) Abs() Number { + return n.set(n.abs()) +} + +// IsInt retourne vrai si le nombre est entier. +func (n Number) IsInt() bool { + return n.isInt() +} + +// Num retourne le numérateur. +func (n Number) Num() Number { + out := Number{ + atom: n.num(), + base: n.base, + tpe: Integer, + } + + return out.format() +} + +// Denom retourne le dénominateur. +func (n Number) Denom() Number { + out := Number{ + atom: n.denom(), + base: n.base, + tpe: Integer, + } + + return out.format() +} + +// Num retourne le numérateur et le dénominateur. +func (n Number) NumDenom() (Number, Number) { + return n.Num(), n.Denom() +} + +// Cmp retourne : +// - -1 si n1 < n2 +// - +1 si n1 > n2 +// - 0 sinon. +func (n1 Number) Cmp(n2 Number) int { + return n1.cmp(n2.atom) +} + +func (n1 Number) Eq(n2 Number) bool { return n1.Cmp(n2) == 0 } +func (n1 Number) Ne(n2 Number) bool { return n1.Cmp(n2) != 0 } +func (n1 Number) Gt(n2 Number) bool { return n1.Cmp(n2) > 0 } +func (n1 Number) Lt(n2 Number) bool { return n1.Cmp(n2) < 0 } +func (n1 Number) Ge(n2 Number) bool { return n1.Cmp(n2) >= 0 } +func (n1 Number) Le(n2 Number) bool { return n1.Cmp(n2) <= 0 } + +// Add retourne n1 + n2. +func (n1 Number) Add(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.add(n2.atom), + } + + return out.format() +} + +// Sub retourne n1 - n2. +func (n1 Number) Sub(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.sub(n2.atom), + } + + return out.format() +} + +// Inc retourne n + 1. +func (n Number) Inc() Number { + return n.Add(One()) +} + +// Dec retourne n − 1. +func (n Number) Dec() Number { + return n.Sub(One()) +} + +// Mul retourne n1 × n2. +func (n1 Number) Mul(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.mul(n2.atom), + } + + return out.format() +} + +// Div retourne n1 ÷ n2 (division exacte). +func (n1 Number) Div(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.div(n2.atom), + } + + return out.format() +} + +// Quo retourne n1 ÷ n2 (division entière). +func (n1 Number) Quo(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: Integer, + atom: n1.quo(n2.atom), + } + + return out.format() +} + +// Rem retourne n1 % n2. +func (n1 Number) Rem(n2 Number) Number { + out := Number{ + base: n1.base, + tpe: max(n1.tpe, n2.tpe), + atom: n1.rem(n2.atom), + } + + return out.format() +} + +// QuoRem retourne la division entière et le reste. +func (n1 Number) QuoRem(n2 Number) (q, r Number) { + q.base, r.base = n1.base, n1.base + q.tpe, r.tpe = Integer, max(n1.tpe, n2.tpe) + q.atom, r.atom = n1.quoRem(n2.atom) + + return q.format(), r.format() +} + +// Inv retourne 1 ÷ n. +func (n Number) Inv() Number { + t := n.tpe + if t == Integer { + t = Fraction + } + + out := Number{ + tpe: t, + base: n.base, + atom: n.inv(), + } + + if n.tpe == Integer && out.IsInt() { + out.tpe = Integer + } + + return out.format() +} + +// Fact retourne n!. +func (n Number) Fact() Number { + switch { + case n.IsInf(): + return Inf() + case !n.IsInt() || n.IsNeg(): + return Nan() + default: + out := One(n.base) + for e := Two(); e.Le(n); e = e.Inc() { + out.Mul(e) + } + return out + } +} + +// Lsh retourne n1 << n2. +func (n1 Number) Lsh(n2 Number) Number { + if n2.IsNeg() { + return n1.Rsh(n2.Abs()) + } + + if n, ok := n2.toInt64(); ok && n >= 0 { + return n1.set(n1.lsh(uint(n)), true) + } + return Nan() +} + +// Rsh retourne n1 << n2. +func (n1 Number) Rsh(n2 Number) Number { + if n2.IsNeg() { + return n1.Lsh(n2.Abs()) + } + + if n, ok := n2.toInt64(); ok && n >= 0 { + return n1.set(n1.rsh(uint(n)), true) + } + return Nan() +} + +// Len retourne le nombre de chiffre d’un nombre entier (ou -1 s’il n’est pas entier) +func (n Number) Len() int { + return n.len(n.base) +} + +// Bit retourne le chiffre à la position n2 (en partant de 0) +// d’un nombre entier (ou -1 si n1 ou n2 n’est pas entier). +func (n1 Number) Bit(n2 Number) int { + if n, ok := n2.toInt64(); ok && n >= 0 { + return n1.bit(uint64(n), n1.base) + } + + return -1 +} + +// Pow retourne n1 ^ n2. +func (n1 Number) Pow(n2 Number) Number { + if !n2.IsDefined() { + return Nan() + } + + p, s := n2.NumDenom() + if !p.isInt64() || !s.isInt64() { + n2 = n2.set(n2.optimize(pow(n2.base, FloatingPrecision))) + p, s = n2.NumDenom() + if !p.isInt64() || !s.isInt64() { + return Nan() + } + } + + if p0, ok := p.toInt64(); ok { + if s0, ok := s.toInt64(); ok && s0 > 0 { + inv := p0 < 0 + if inv { + p0 = -p0 + } + out := Number{ + tpe: n1.tpe, + base: n1.base, + atom: n1.pow(uint64(p0)).sqrtn(uint64(s0), n1.base), + } + if inv { + out.atom = out.atom.inv() + } + if n1.tpe == Integer && !out.isInt() { + out.tpe = Decimal + } + + return out.format() + } + } + + return Nan() +} + +// Sqrtn retourne la racine n2ième de n1. +func (n1 Number) Sqrtn(n2 Number) Number { + if n, ok := n2.toInt64(); ok && n > 0 { + out := Number{ + tpe: n1.tpe, + base: n1.base, + atom: n1.sqrtn(uint64(n), n1.base), + } + if n1.tpe == Integer && !out.isInt() { + out.tpe = Decimal + } + + return out.format() + } + + return Nan() +} + +// Sqrt retourne la racine carrée de n. +func (n Number) Sqrt() Number { + return n.Sqrtn(Int(2)) +} diff --git a/number/operations.go b/number/operations.go new file mode 100644 index 0000000..fb7da44 --- /dev/null +++ b/number/operations.go @@ -0,0 +1,179 @@ +package number + +import ( + "sort" +) + +type Op1Func func(Number) Number +type Op2Func func(Number, Number) Number +type Op1To2Func func(Number) (Number, Number) +type Op2To2Func func(Number, Number) (Number, Number) +type ReduceFunc func(...Number) Number +type MapFunc func(...Number) []Number + +func ToBase[N integer](n Number, base N) Number { + return n.ToBase(formatBase(base)) +} + +func toType[N integer](n Number, t NumberType, base ...N) Number { + n = n.ToType(t) + if len(base) > 0 { + n = ToBase(n, base[0]) + } + + return n +} + +func ToInteger[N integer](n Number, base ...N) Number { + return toType(n, Integer, base...) +} + +func ToDecimal[N integer](n Number, base ...N) Number { + return toType(n, Decimal, base...) +} + +func ToFraction[N integer](n Number, base ...N) Number { + return toType(n, Fraction, base...) +} + +func ToScientific[N integer](n Number, base ...N) Number { + return toType(n, Scientific, base...) +} + +// Fonctions de type f(n) → n +func Neg(n Number) Number { return n.Neg() } +func Abs(n Number) Number { return n.Abs() } +func Num(n Number) Number { return n.Num() } +func Denom(n Number) Number { return n.Denom() } +func Inc(n Number) Number { return n.Inc() } +func Dec(n Number) Number { return n.Dec() } +func Inv(n Number) Number { return n.Inv() } +func Fact(n Number) Number { return n.Fact() } +func Len(n Number) Number { return Int(n.Len()) } +func Sqrt(n Number) Number { return n.Sqrt() } + +// Fonctions de type f(n, n) → n +func Cmp(n1, n2 Number) Number { return Int(n1.Cmp(n2)) } +func Eq(n1, n2 Number) Number { return ParseBool(n1.Eq(n2)) } +func Ne(n1, n2 Number) Number { return ParseBool(n1.Ne(n2)) } +func Gt(n1, n2 Number) Number { return ParseBool(n1.Gt(n2)) } +func Lt(n1, n2 Number) Number { return ParseBool(n1.Lt(n2)) } +func Ge(n1, n2 Number) Number { return ParseBool(n1.Ge(n2)) } +func Le(n1, n2 Number) Number { return ParseBool(n1.Le(n2)) } +func Add(n1, n2 Number) Number { return n1.Add(n2) } +func Sub(n1, n2 Number) Number { return n1.Sub(n2) } +func Mul(n1, n2 Number) Number { return n1.Mul(n2) } +func Div(n1, n2 Number) Number { return n1.Div(n2) } +func Quo(n1, n2 Number) Number { return n1.Quo(n2) } +func Rem(n1, n2 Number) Number { return n1.Rem(n2) } +func Lsh(n1, n2 Number) Number { return n1.Lsh(n2) } +func Rsh(n1, n2 Number) Number { return n1.Rsh(n2) } +func Bit(n1, n2 Number) Number { return Int(n1.Bit(n2)) } +func Pow(n1, n2 Number) Number { return n1.Pow(n2) } +func Sqrtn(n1, n2 Number) Number { return n1.Sqrtn(n2) } + +// Fonctions de type f(n) → (n, n) +func NumDenom(n Number) (Number, Number) { return n.NumDenom() } + +// Fonctions de type f(n, n) → (n, n) +func QuoRem(n1, n2 Number) (Number, Number) { return n1.QuoRem(n2) } + +// Autre +func Map(callback Op1Func) MapFunc { + return func(numbers ...Number) []Number { + out := make([]Number, len(numbers)) + for i, n := range numbers { + out[i] = callback(n) + } + + return out + } +} + +func Reduce(callback Op2Func) ReduceFunc { + return func(numbers ...Number) Number { + var acc Number + for i, n := range numbers { + if i == 0 { + acc = n + } else { + acc = callback(acc, n) + } + } + + return acc + } +} + +func Max(numbers ...Number) (n Number) { + return Reduce(func(n1, n2 Number) Number { + if n2.Gt(n1) { + return n2 + } + return n1 + })(numbers...) +} + +func Min(numbers ...Number) (n Number) { + return Reduce(func(n1, n2 Number) Number { + if n2.Lt(n1) { + return n2 + } + return n1 + })(numbers...) +} + +func Sum(numbers ...Number) (n Number) { + return Reduce(Add)(numbers...) +} + +func Mean(numbers ...Number) (n Number) { + l := len(numbers) + if l == 0 { + return Nan() + } + + return Sum(numbers...).Div(Int(l)) +} + +func Round(n Number, precision uint64, base ...uint) Number { + if !n.IsDefined() || n.Type() == Integer { + return n + } + + p := Number{ + base: n.Base(), + tpe: n.Type(), + atom: pow(formatBase(base...), precision), + } + p.format() + num, denom := n.Num().Mul(p), n.Denom() + + return num.Quo(denom).Div(p) +} + +func Reverse(numbers ...Number) []Number { + l := len(numbers) + for i := 0; i < l>>1; i++ { + j := l - 1 - i + numbers[i], numbers[j] = numbers[j], numbers[i] + } + + return numbers +} + +func Sort(numbers ...Number) []Number { + sort.Slice(numbers, func(i, j int) bool { + return numbers[i].Lt(numbers[j]) + }) + + return numbers +} + +func SortDesc(numbers ...Number) []Number { + sort.Slice(numbers, func(i, j int) bool { + return numbers[i].Gt(numbers[j]) + }) + + return numbers +} diff --git a/number/util.go b/number/util.go new file mode 100644 index 0000000..219f2b2 --- /dev/null +++ b/number/util.go @@ -0,0 +1,36 @@ +package number + +type integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +type float interface { + ~float32 | ~float64 +} + +func signOf[N integer | float](n N) int { + switch { + case n < 0: + return -1 + case n > 0: + return 1 + default: + return 0 + } +} + +func compare[N integer | float](n1, n2 N) int { + return signOf(n1 - n2) +} + +func isBaseValid[N integer | float](base N) bool { + return base >= 0 && base <= 36 +} + +func formatBase[N integer | float](base ...N) uint { + if len(base) > 0 && isBaseValid(base[0]) { + return uint(base[0]) + } + + return 10 +}