503 lines
8.6 KiB
Go
503 lines
8.6 KiB
Go
|
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
|
||
|
}
|