gob/number/atom.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
}