gob/number/number.go

427 lines
7.9 KiB
Go
Raw Normal View History

2024-02-17 18:13:24 +00:00
package number
// NumberType représente le type dun 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 dun 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 dun 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 dun nombre entier (ou -1 sil nest pas entier)
func (n Number) Len() int {
return n.len(n.base)
}
// Bit retourne le chiffre à la position n2 (en partant de 0)
// dun nombre entier (ou -1 si n1 ou n2 nest 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))
}