chore: add grug-math
This commit is contained in:
parent
b1cc2af8d8
commit
4181cd5fed
189
grug/math/math.go
Normal file
189
grug/math/math.go
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
// grug math is an unbounded precision math library for integers. It is not designed to be performant in any means. But as an example of how one works.
|
||||||
|
package math
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type Number []rune
|
||||||
|
|
||||||
|
func NewNumber() *Number {
|
||||||
|
return &Number{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Number) FromString(s string) *Number {
|
||||||
|
for _, a := range s {
|
||||||
|
if !(a >= '0' && a <= '9') {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var n Number = []rune(s)
|
||||||
|
|
||||||
|
i:=0
|
||||||
|
for range n[:len(n)-1] {
|
||||||
|
if n[i] == 0 || n[i] == '0' {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = n[i:]
|
||||||
|
|
||||||
|
return &n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Number) FromInt(i int) *Number {
|
||||||
|
s := strconv.Itoa(i)
|
||||||
|
var n Number = []rune(s)
|
||||||
|
return &n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Number) String() string {
|
||||||
|
if n == nil || len(*n) == 0 {
|
||||||
|
return "NaN"
|
||||||
|
}
|
||||||
|
return string(*n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Number) Add(a *Number) *Number {
|
||||||
|
if n == nil || a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lenN, lenA := len(*n), len(*a)
|
||||||
|
sum := make(Number, max(lenN, lenA)+1)
|
||||||
|
|
||||||
|
for i := range sum[:len(sum)-1] {
|
||||||
|
ii := len(sum) - i - 1
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case lenN == lenA:
|
||||||
|
j := (*n)[lenN-i-1]
|
||||||
|
k := (*a)[lenA-i-1]
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = add(j, k, sum[ii])
|
||||||
|
|
||||||
|
case lenN > lenA:
|
||||||
|
j := (*n)[lenN-i-1]
|
||||||
|
k := '0'
|
||||||
|
if i < lenA {
|
||||||
|
k = (*a)[lenA-i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = add(j, k, sum[ii])
|
||||||
|
|
||||||
|
case lenN < lenA:
|
||||||
|
j := '0'
|
||||||
|
if i < lenN {
|
||||||
|
j = (*n)[lenN-i-1]
|
||||||
|
}
|
||||||
|
k := (*a)[lenA-i-1]
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = add(j, k, sum[ii])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim the extra 0 if present
|
||||||
|
if sum[0] == 0 || sum[0] == '0' {
|
||||||
|
sum = sum[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sum
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (n *Number) Sub(s *Number) *Number {
|
||||||
|
if n == nil || s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lenN, lenA := len(*n), len(*s)
|
||||||
|
sum := make(Number, max(lenN, lenA)+1)
|
||||||
|
|
||||||
|
for i := range sum[:len(sum)-1] {
|
||||||
|
ii := len(sum) - i - 1
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case lenN == lenA:
|
||||||
|
j := (*n)[lenN-i-1]
|
||||||
|
k := (*s)[lenA-i-1]
|
||||||
|
c := '0'
|
||||||
|
if i+1 < lenN {
|
||||||
|
c = (*n)[lenN-i-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = sub(j, k, c)
|
||||||
|
|
||||||
|
case lenN > lenA:
|
||||||
|
j := (*n)[lenN-i-1]
|
||||||
|
k := '0'
|
||||||
|
if i < lenA {
|
||||||
|
k = (*s)[lenA-i-1]
|
||||||
|
}
|
||||||
|
c := '0'
|
||||||
|
if i+1 < lenN {
|
||||||
|
c = (*n)[lenN-i-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = sub(j, k, c)
|
||||||
|
if i+1 < lenN {
|
||||||
|
(*n)[lenN-i-2] =sum[ii-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
case lenN < lenA:
|
||||||
|
j := '0'
|
||||||
|
if i < lenN {
|
||||||
|
j = (*n)[lenN-i-1]
|
||||||
|
}
|
||||||
|
k := (*s)[lenA-i-1]
|
||||||
|
c := '0'
|
||||||
|
if i+1 < lenN {
|
||||||
|
c = (*n)[lenN-i-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
sum[ii-1], sum[ii] = sub(j, k, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim the extra 0 if present
|
||||||
|
i:=0
|
||||||
|
for range sum[:len(sum)-1] {
|
||||||
|
if sum[i] == 0 || sum[i] == '0' {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum = sum[i:]
|
||||||
|
|
||||||
|
|
||||||
|
return &sum
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func friends(r rune) (int32, int32) {
|
||||||
|
return 1, 10 - int32(r-'0')
|
||||||
|
}
|
||||||
|
func add(a, b, c rune) (rune, rune) {
|
||||||
|
up, dn := friends(b)
|
||||||
|
if c == 0 {
|
||||||
|
c = '0'
|
||||||
|
}
|
||||||
|
a = a + c - '0'
|
||||||
|
|
||||||
|
if a-dn < '0' {
|
||||||
|
return '0', a + b - '0'
|
||||||
|
}
|
||||||
|
return c + up, a - dn
|
||||||
|
}
|
||||||
|
|
||||||
|
func sub(a, b, c rune) (rune, rune) {
|
||||||
|
dn, up := friends(b)
|
||||||
|
if c == 0 {
|
||||||
|
c = '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
if a+up > '9' {
|
||||||
|
return c, a - b + '0'
|
||||||
|
}
|
||||||
|
return c - dn, a + up
|
||||||
|
}
|
74
grug/math/math_test.go
Normal file
74
grug/math/math_test.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package math_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
"go.sour.is/pkg/grug/math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumber(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
n := math.NewNumber().FromInt(100)
|
||||||
|
is.Equal(n.String(), "100")
|
||||||
|
|
||||||
|
n = n.FromString("00001")
|
||||||
|
is.Equal(n.String(), "1")
|
||||||
|
|
||||||
|
n = n.FromString("1x0")
|
||||||
|
is.True(n==nil)
|
||||||
|
is.Equal(n.String(), "NaN")
|
||||||
|
|
||||||
|
n = n.FromString("200")
|
||||||
|
is.True(n!=nil)
|
||||||
|
is.Equal(n.String(), "200")
|
||||||
|
|
||||||
|
n = (&math.Number{}).FromString("300")
|
||||||
|
is.True(n!=nil)
|
||||||
|
is.Equal(n.String(), "300")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdd(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
n := math.NewNumber().FromString("100")
|
||||||
|
|
||||||
|
n = n.Add(nil)
|
||||||
|
is.Equal(n.String(), "NaN")
|
||||||
|
|
||||||
|
n = n.FromInt(100)
|
||||||
|
n = n.Add(n.FromString("900"))
|
||||||
|
is.Equal(n.String(), "1000")
|
||||||
|
|
||||||
|
n = n.Add(math.NewNumber())
|
||||||
|
is.Equal(n.String(), "1000")
|
||||||
|
|
||||||
|
n = n.Add(n.FromString("10"))
|
||||||
|
is.Equal(n.String(), "1010")
|
||||||
|
|
||||||
|
n = n.Add(n.FromString("10000"))
|
||||||
|
is.Equal(n.String(), "11010")
|
||||||
|
|
||||||
|
n = n.Add(n.FromString("9000"))
|
||||||
|
is.Equal(n.String(), "20010")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSub(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
n := math.NewNumber()
|
||||||
|
|
||||||
|
// n = n.FromString("100")
|
||||||
|
// n = n.Sub(n.FromInt(100))
|
||||||
|
// is.Equal(n.String(), "0")
|
||||||
|
|
||||||
|
// n = n.FromString("200")
|
||||||
|
// n = n.Sub(n.FromInt(100))
|
||||||
|
// is.Equal(n.String(), "100")
|
||||||
|
|
||||||
|
n = n.FromString("100")
|
||||||
|
n = n.Sub(n.FromInt(50))
|
||||||
|
is.Equal(n.String(), "50")
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user