chore: add grug-math

This commit is contained in:
xuu committed 2024-01-15 11:27:32 -07:00
1 parent b1cc2af8d8
commit 4181cd5fed
2 files changed
+263

No files matched your search

+189
View 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
View 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")
}