427 lines
7.9 KiB
Go
427 lines
7.9 KiB
Go
|
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))
|
|||
|
}
|