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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user