Compare commits
	
		
			9 Commits
		
	
	
		
			c0c2ab8b4e
			...
			5809de419f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5809de419f | |||
| 7c748f0e31 | |||
| cd6b7982ee | |||
| e00886b1ed | |||
| 49904f9097 | |||
| 2d0d03b975 | |||
| 8cb048edb0 | |||
| cb3d8827fa | |||
| 4c83764332 | 
| @ -7,6 +7,7 @@ package expr | |||||||
| const ( | const ( | ||||||
| 	typeBoolean  = "boolean" | 	typeBoolean  = "boolean" | ||||||
| 	typeFloat    = "decimal" | 	typeFloat    = "decimal" | ||||||
|  | 	typeFraction = "fraction" | ||||||
| 	typeInt      = "integer" | 	typeInt      = "integer" | ||||||
| 	typeNumber   = "number" | 	typeNumber   = "number" | ||||||
| 	typeString   = "string" | 	typeString   = "string" | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								formatter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								formatter.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // formatter.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | type FmtOpt uint16 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	TTY FmtOpt = 1 << iota | ||||||
|  | 	MultiLine | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Formatter interface { | ||||||
|  | 	ToString(options FmtOpt) string | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								func-math.go
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								func-math.go
									
									
									
									
									
								
							| @ -10,16 +10,17 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) { | func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) { | ||||||
| 	if !(isNumber(paramValue) || isList(paramValue) || isIterator(paramValue)) { | 	if !(isNumber(paramValue) || isList(paramValue) || isFraction(paramValue)) { | ||||||
| 		err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue) | 		err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | ||||||
| 	var sumAsFloat = false | 	var sumAsFloat, sumAsFract bool | ||||||
| 	var floatSum float64 = 0.0 | 	var floatSum float64 = 0.0 | ||||||
| 	var intSum int64 = 0 | 	var intSum int64 = 0 | ||||||
|  | 	var fractSum *fraction | ||||||
| 	var v any | 	var v any | ||||||
| 
 | 
 | ||||||
| 	for v, err = it.Next(); err == nil; v, err = it.Next() { | 	for v, err = it.Next(); err == nil; v, err = it.Next() { | ||||||
| @ -36,19 +37,37 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | |||||||
| 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 			if array, ok := v.([]any); ok { | 			if array, ok := v.(*ListType); ok { | ||||||
| 				if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil { | 				if v, err = doAdd(ctx, name, NewFlatArrayIterator(*array)); err != nil { | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !sumAsFloat && isFloat(v) { | 		if !sumAsFloat { | ||||||
|  | 			if isFloat(v) { | ||||||
| 				sumAsFloat = true | 				sumAsFloat = true | ||||||
|  | 				if sumAsFract { | ||||||
|  | 					floatSum = fractSum.toFloat() | ||||||
|  | 				} else { | ||||||
| 					floatSum = float64(intSum) | 					floatSum = float64(intSum) | ||||||
| 				} | 				} | ||||||
|  | 			} else if !sumAsFract && isFraction(v) { | ||||||
|  | 				fractSum = newFraction(intSum, 1) | ||||||
|  | 				sumAsFract = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if sumAsFloat { | 		if sumAsFloat { | ||||||
| 			floatSum += numAsFloat(v) | 			floatSum += numAsFloat(v) | ||||||
|  | 		} else if sumAsFract { | ||||||
|  | 			var item *fraction | ||||||
|  | 			var ok bool | ||||||
|  | 			if item, ok = v.(*fraction); !ok { | ||||||
|  | 				iv, _ := v.(int64) | ||||||
|  | 				item = newFraction(iv, 1) | ||||||
|  | 			} | ||||||
|  | 			fractSum = sumFract(fractSum, item) | ||||||
| 		} else { | 		} else { | ||||||
| 			iv, _ := v.(int64) | 			iv, _ := v.(int64) | ||||||
| 			intSum += iv | 			intSum += iv | ||||||
| @ -58,6 +77,8 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | |||||||
| 		err = nil | 		err = nil | ||||||
| 		if sumAsFloat { | 		if sumAsFloat { | ||||||
| 			result = floatSum | 			result = floatSum | ||||||
|  | 		} else if sumAsFract { | ||||||
|  | 			result = fractSum | ||||||
| 		} else { | 		} else { | ||||||
| 			result = intSum | 			result = intSum | ||||||
| 		} | 		} | ||||||
| @ -71,28 +92,58 @@ func addFunc(ctx ExprContext, name string, args []any) (result any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) { | func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) { | ||||||
| 	var mulAsFloat = false | 	var mulAsFloat, mulAsFract bool | ||||||
| 	var floatProd float64 = 1.0 | 	var floatProd float64 = 1.0 | ||||||
| 	var intProd int64 = 1 | 	var intProd int64 = 1 | ||||||
|  | 	var fractProd *fraction | ||||||
| 	var v any | 	var v any | ||||||
| 
 | 
 | ||||||
| 	for v, err = it.Next(); err == nil; v, err = it.Next() { | 	for v, err = it.Next(); err == nil; v, err = it.Next() { | ||||||
|  | 		if subIter, ok := v.(Iterator); ok { | ||||||
|  | 			if v, err = doAdd(ctx, name, subIter); err != nil { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			if subIter.HasOperation(cleanName) { | ||||||
|  | 				if _, err = subIter.CallOperation(cleanName, nil); err != nil { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
| 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 		if array, ok := v.([]any); ok { | 			if array, ok := v.(*ListType); ok { | ||||||
| 			if v, err = doMul(ctx, name, NewFlatArrayIterator(array)); err != nil { | 				if v, err = doMul(ctx, name, NewFlatArrayIterator(*array)); err != nil { | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		if !mulAsFloat && isFloat(v) { | 		if !mulAsFloat { | ||||||
|  | 			if isFloat(v) { | ||||||
| 				mulAsFloat = true | 				mulAsFloat = true | ||||||
|  | 				if mulAsFract { | ||||||
|  | 					floatProd = fractProd.toFloat() | ||||||
|  | 				} else { | ||||||
| 					floatProd = float64(intProd) | 					floatProd = float64(intProd) | ||||||
| 				} | 				} | ||||||
|  | 			} else if !mulAsFract && isFraction(v) { | ||||||
|  | 				fractProd = newFraction(intProd, 1) | ||||||
|  | 				mulAsFract = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if mulAsFloat { | 		if mulAsFloat { | ||||||
| 			floatProd *= numAsFloat(v) | 			floatProd *= numAsFloat(v) | ||||||
|  | 		} else if mulAsFract { | ||||||
|  | 			var item *fraction | ||||||
|  | 			var ok bool | ||||||
|  | 			if item, ok = v.(*fraction); !ok { | ||||||
|  | 				iv, _ := v.(int64) | ||||||
|  | 				item = newFraction(iv, 1) | ||||||
|  | 			} | ||||||
|  | 			fractProd = mulFract(fractProd, item) | ||||||
| 		} else { | 		} else { | ||||||
| 			iv, _ := v.(int64) | 			iv, _ := v.(int64) | ||||||
| 			intProd *= iv | 			intProd *= iv | ||||||
| @ -102,6 +153,8 @@ func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) { | |||||||
| 		err = nil | 		err = nil | ||||||
| 		if mulAsFloat { | 		if mulAsFloat { | ||||||
| 			result = floatProd | 			result = floatProd | ||||||
|  | 		} else if mulAsFract { | ||||||
|  | 			result = fractProd | ||||||
| 		} else { | 		} else { | ||||||
| 			result = intProd | 			result = intProd | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -7,11 +7,11 @@ package expr | |||||||
| import "io" | import "io" | ||||||
| 
 | 
 | ||||||
| type FlatArrayIterator struct { | type FlatArrayIterator struct { | ||||||
| 	a     []any | 	a     ListType | ||||||
| 	index int | 	index int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewFlatArrayIterator(array []any) *FlatArrayIterator { | func NewFlatArrayIterator(array ListType) *FlatArrayIterator { | ||||||
| 	return &FlatArrayIterator{a: array, index: 0} | 	return &FlatArrayIterator{a: array, index: 0} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,44 @@ | |||||||
| // operand-list.go
 | // operand-list.go
 | ||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ListType []any | ||||||
|  | 
 | ||||||
|  | func (ls *ListType) ToString(opt FmtOpt) string { | ||||||
|  | 	var sb strings.Builder | ||||||
|  | 	sb.WriteByte('[') | ||||||
|  | 	if len(*ls) > 0 { | ||||||
|  | 		if opt&MultiLine != 0 { | ||||||
|  | 			sb.WriteString("\n  ") | ||||||
|  | 		} | ||||||
|  | 		for i, item := range []any(*ls) { | ||||||
|  | 			if i > 0 { | ||||||
|  | 				if opt&MultiLine != 0 { | ||||||
|  | 					sb.WriteString(",\n  ") | ||||||
|  | 				} else { | ||||||
|  | 					sb.WriteString(", ") | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if s, ok := item.(string); ok { | ||||||
|  | 				sb.WriteByte('"') | ||||||
|  | 				sb.WriteString(s) | ||||||
|  | 				sb.WriteByte('"') | ||||||
|  | 			} else { | ||||||
|  | 				sb.WriteString(fmt.Sprintf("%v", item)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if opt&MultiLine != 0 { | ||||||
|  | 			sb.WriteByte('\n') | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	sb.WriteByte(']') | ||||||
|  | 	return sb.String() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // -------- list term
 | // -------- list term
 | ||||||
| func newListTermA(args ...*term) *term { | func newListTermA(args ...*term) *term { | ||||||
| 	return newListTerm(args) | 	return newListTerm(args) | ||||||
| @ -23,7 +61,7 @@ func newListTerm(args []*term) *term { | |||||||
| // -------- list func
 | // -------- list func
 | ||||||
| func evalList(ctx ExprContext, self *term) (v any, err error) { | func evalList(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	list, _ := self.value().([]*term) | 	list, _ := self.value().([]*term) | ||||||
| 	items := make([]any, len(list)) | 	items := make(ListType, len(list)) | ||||||
| 	for i, tree := range list { | 	for i, tree := range list { | ||||||
| 		var param any | 		var param any | ||||||
| 		if param, err = tree.compute(ctx); err != nil { | 		if param, err = tree.compute(ctx); err != nil { | ||||||
| @ -32,35 +70,7 @@ func evalList(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 		items[i] = param | 		items[i] = param | ||||||
| 	} | 	} | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		v = items | 		v = &items | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // // -------- list term
 |  | ||||||
| // func newListTerm(args []*term) *term {
 |  | ||||||
| // 	return &term{
 |  | ||||||
| // 		tk:       *NewToken(0, 0, SymList, "[]"),
 |  | ||||||
| // 		parent:   nil,
 |  | ||||||
| // 		children: args,
 |  | ||||||
| // 		position: posLeaf,
 |  | ||||||
| // 		priority: priValue,
 |  | ||||||
| // 		evalFunc: evalList,
 |  | ||||||
| // 	}
 |  | ||||||
| // }
 |  | ||||||
| 
 |  | ||||||
| // // -------- list func
 |  | ||||||
| // func evalList(ctx ExprContext, self *term) (v any, err error) {
 |  | ||||||
| // 	items := make([]any, len(self.children))
 |  | ||||||
| // 	for i, tree := range self.children {
 |  | ||||||
| // 		var param any
 |  | ||||||
| // 		if param, err = tree.compute(ctx); err != nil {
 |  | ||||||
| // 			break
 |  | ||||||
| // 		}
 |  | ||||||
| // 		items[i] = param
 |  | ||||||
| // 	}
 |  | ||||||
| // 	if err == nil {
 |  | ||||||
| // 		v = items
 |  | ||||||
| // 	}
 |  | ||||||
| // 	return
 |  | ||||||
| // }
 |  | ||||||
|  | |||||||
							
								
								
									
										274
									
								
								operator-fraction.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								operator-fraction.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,274 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // operand-fraction.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type fraction struct { | ||||||
|  | 	num, den int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newFraction(num, den int64) *fraction { | ||||||
|  | 	if den < 0 { | ||||||
|  | 		den = -den | ||||||
|  | 		num = -num | ||||||
|  | 	} | ||||||
|  | 	return &fraction{num, den} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *fraction) toFloat() float64 { | ||||||
|  | 	return float64(f.num) / float64(f.den) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *fraction) String() string { | ||||||
|  | 	return f.ToString(0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *fraction) ToString(opt FmtOpt) string { | ||||||
|  | 	var sb strings.Builder | ||||||
|  | 	if opt&MultiLine == 0 { | ||||||
|  | 		sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den)) | ||||||
|  | 	} else { | ||||||
|  | 		var s, num string | ||||||
|  | 		if f.num < 0 && opt&TTY == 0 { | ||||||
|  | 			num = strconv.FormatInt(-f.num, 10) | ||||||
|  | 			s = "-" | ||||||
|  | 		} else { | ||||||
|  | 			num = strconv.FormatInt(f.num, 10) | ||||||
|  | 		} | ||||||
|  | 		den := strconv.FormatInt(f.den, 10) | ||||||
|  | 		size := max(len(num), len(den)) | ||||||
|  | 		if opt&TTY != 0 { | ||||||
|  | 			sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num))) | ||||||
|  | 		} else { | ||||||
|  | 			if len(s) > 0 { | ||||||
|  | 				sb.WriteString("  ") | ||||||
|  | 			} | ||||||
|  | 			sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num))) | ||||||
|  | 			sb.WriteByte('\n') | ||||||
|  | 			if len(s) > 0 { | ||||||
|  | 				sb.WriteString(s) | ||||||
|  | 				sb.WriteByte(' ') | ||||||
|  | 			} | ||||||
|  | 			sb.WriteString(strings.Repeat("-", size)) | ||||||
|  | 			sb.WriteByte('\n') | ||||||
|  | 			if len(s) > 0 { | ||||||
|  | 				sb.WriteString("  ") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return sb.String() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -------- fraction term
 | ||||||
|  | func newFractionTerm(tk *Token) *term { | ||||||
|  | 	return &term{ | ||||||
|  | 		tk:       *tk, | ||||||
|  | 		parent:   nil, | ||||||
|  | 		children: make([]*term, 0, 2), | ||||||
|  | 		position: posInfix, | ||||||
|  | 		priority: priFraction, | ||||||
|  | 		evalFunc: evalFraction, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -------- eval func
 | ||||||
|  | func evalFraction(ctx ExprContext, self *term) (v any, err error) { | ||||||
|  | 	var numValue, denValue any | ||||||
|  | 	var num, den int64 | ||||||
|  | 	var ok bool | ||||||
|  | 
 | ||||||
|  | 	if numValue, denValue, err = self.evalInfix(ctx); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if num, ok = numValue.(int64); !ok { | ||||||
|  | 		err = fmt.Errorf("numerator must be integer, got %T (%v)", numValue, numValue) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if den, ok = denValue.(int64); !ok { | ||||||
|  | 		err = fmt.Errorf("denominator must be integer, got %T (%v)", denValue, denValue) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if den == 0 { | ||||||
|  | 		err = errors.New("division by zero") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if den < 0 { | ||||||
|  | 		den = -den | ||||||
|  | 		num = -num | ||||||
|  | 	} | ||||||
|  | 	g := gcd(num, den) | ||||||
|  | 	num = num / g | ||||||
|  | 	den = den / g | ||||||
|  | 	if den == 1 { | ||||||
|  | 		v = num | ||||||
|  | 	} else { | ||||||
|  | 		v = &fraction{num, den} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func gcd(a, b int64) (g int64) { | ||||||
|  | 	if a < 0 { | ||||||
|  | 		a = -a | ||||||
|  | 	} | ||||||
|  | 	if b < 0 { | ||||||
|  | 		b = -b | ||||||
|  | 	} | ||||||
|  | 	if a < b { | ||||||
|  | 		a, b = b, a | ||||||
|  | 	} | ||||||
|  | 	r := a % b | ||||||
|  | 	for r > 0 { | ||||||
|  | 		a, b = b, r | ||||||
|  | 		r = a % b | ||||||
|  | 	} | ||||||
|  | 	g = b | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func lcm(a, b int64) (l int64) { | ||||||
|  | 	g := gcd(a, b) | ||||||
|  | 	l = a * b / g | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func sumFract(f1, f2 *fraction) (sum *fraction) { | ||||||
|  | 	m := lcm(f1.den, f2.den) | ||||||
|  | 	sum = &fraction{f1.num*(m/f1.den) + f2.num*(m/f2.den), m} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func mulFract(f1, f2 *fraction) (prod *fraction) { | ||||||
|  | 	prod = &fraction{f1.num * f2.num, f1.den * f2.den} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func anyToFract(v any) (f *fraction, err error) { | ||||||
|  | 	var ok bool | ||||||
|  | 	if f, ok = v.(*fraction); !ok { | ||||||
|  | 		if n, ok := v.(int64); ok { | ||||||
|  | 			f = intToFraction(n) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if f == nil { | ||||||
|  | 		err = errExpectedGot("fract", typeFraction, v) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func anyPairToFract(v1, v2 any) (f1, f2 *fraction, err error) { | ||||||
|  | 	if f1, err = anyToFract(v1); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if f2, err = anyToFract(v2); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func sumAnyFract(af1, af2 any) (sum any, err error) { | ||||||
|  | 	var f1, f2 *fraction | ||||||
|  | 	if f1, f2, err = anyPairToFract(af1, af2); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	f := sumFract(f1, f2) | ||||||
|  | 	if f.num == 0 { | ||||||
|  | 		sum = 0 | ||||||
|  | 	} else { | ||||||
|  | 		sum = simplifyFraction(f) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func subAnyFract(af1, af2 any) (sum any, err error) { | ||||||
|  | 	var f1, f2 *fraction | ||||||
|  | 	if f1, f2, err = anyPairToFract(af1, af2); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	f2.num = -f2.num | ||||||
|  | 	f := sumFract(f1, f2) | ||||||
|  | 	if f.num == 0 { | ||||||
|  | 		sum = 0 | ||||||
|  | 	} else { | ||||||
|  | 		sum = simplifyFraction(f) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func mulAnyFract(af1, af2 any) (prod any, err error) { | ||||||
|  | 	var f1, f2 *fraction | ||||||
|  | 	if f1, f2, err = anyPairToFract(af1, af2); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if f1.num == 0 || f2.num == 0 { | ||||||
|  | 		prod = 0 | ||||||
|  | 	} else { | ||||||
|  | 		f := &fraction{f1.num * f2.num, f1.den * f2.den} | ||||||
|  | 		prod = simplifyFraction(f) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func divAnyFract(af1, af2 any) (quot any, err error) { | ||||||
|  | 	var f1, f2 *fraction | ||||||
|  | 	if f1, f2, err = anyPairToFract(af1, af2); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if f2.num == 0 { | ||||||
|  | 		err = errors.New("division by zero") | ||||||
|  | 		return | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if f1.num == 0 || f2.den == 0 { | ||||||
|  | 		quot = 0 | ||||||
|  | 	} else { | ||||||
|  | 		f := &fraction{f1.num * f2.den, f1.den * f2.num} | ||||||
|  | 		quot = simplifyFraction(f) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func simplifyFraction(f *fraction) any { | ||||||
|  | 	return simplifyIntegers(f.num, f.den) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func simplifyIntegers(num, den int64) (v any) { | ||||||
|  | 	if den < 0 { | ||||||
|  | 		den = -den | ||||||
|  | 		num = -num | ||||||
|  | 	} | ||||||
|  | 	g := gcd(num, den) | ||||||
|  | 	num = num / g | ||||||
|  | 	den = den / g | ||||||
|  | 	if den == 1 { | ||||||
|  | 		v = num | ||||||
|  | 	} else { | ||||||
|  | 		v = &fraction{num, den} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func intToFraction(n int64) *fraction { | ||||||
|  | 	return &fraction{n, 1} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isFraction(v any) (ok bool) { | ||||||
|  | 	_, ok = v.(*fraction) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // init
 | ||||||
|  | func init() { | ||||||
|  | 	registerTermConstructor(SymVertBar, newFractionTerm) | ||||||
|  | } | ||||||
| @ -42,6 +42,8 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 			rightInt, _ := rightValue.(int64) | 			rightInt, _ := rightValue.(int64) | ||||||
| 			v = leftInt * rightInt | 			v = leftInt * rightInt | ||||||
| 		} | 		} | ||||||
|  | 	} else if isFraction(leftValue) || isFraction(rightValue) { | ||||||
|  | 		v, err = mulAnyFract(leftValue, rightValue) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleTypes(leftValue, rightValue) | 		err = self.errIncompatibleTypes(leftValue, rightValue) | ||||||
| 	} | 	} | ||||||
| @ -85,6 +87,8 @@ func evalDivide(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 				v = leftInt / rightInt | 				v = leftInt / rightInt | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} else if isFraction(leftValue) || isFraction(rightValue) { | ||||||
|  | 		v, err = divAnyFract(leftValue, rightValue) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleTypes(leftValue, rightValue) | 		err = self.errIncompatibleTypes(leftValue, rightValue) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -39,22 +39,24 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 			v = leftInt + rightInt | 			v = leftInt + rightInt | ||||||
| 		} | 		} | ||||||
| 	} else if isList(leftValue) || isList(rightValue) { | 	} else if isList(leftValue) || isList(rightValue) { | ||||||
| 		var leftList, rightList []any | 		var leftList, rightList *ListType | ||||||
| 		var ok bool | 		var ok bool | ||||||
| 		if leftList, ok = leftValue.([]any); !ok { | 		if leftList, ok = leftValue.(*ListType); !ok { | ||||||
| 			leftList = []any{leftValue} | 			leftList = &ListType{leftValue} | ||||||
| 		} | 		} | ||||||
| 		if rightList, ok = rightValue.([]any); !ok { | 		if rightList, ok = rightValue.(*ListType); !ok { | ||||||
| 			rightList = []any{rightValue} | 			rightList = &ListType{rightValue} | ||||||
| 		} | 		} | ||||||
| 		sumList := make([]any, 0, len(leftList)+len(rightList)) | 		sumList := make(ListType, 0, len(*leftList)+len(*rightList)) | ||||||
| 		for _, item := range leftList { | 		for _, item := range *leftList { | ||||||
| 			sumList = append(sumList, item) | 			sumList = append(sumList, item) | ||||||
| 		} | 		} | ||||||
| 		for _, item := range rightList { | 		for _, item := range *rightList { | ||||||
| 			sumList = append(sumList, item) | 			sumList = append(sumList, item) | ||||||
| 		} | 		} | ||||||
| 		v = sumList | 		v = &sumList | ||||||
|  | 	} else if isFraction(leftValue) || isFraction(rightValue) { | ||||||
|  | 		v, err = sumAnyFract(leftValue, rightValue) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleTypes(leftValue, rightValue) | 		err = self.errIncompatibleTypes(leftValue, rightValue) | ||||||
| 	} | 	} | ||||||
| @ -89,15 +91,17 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 			v = leftInt - rightInt | 			v = leftInt - rightInt | ||||||
| 		} | 		} | ||||||
| 	} else if isList(leftValue) && isList(rightValue) { | 	} else if isList(leftValue) && isList(rightValue) { | ||||||
| 		leftList, _ := leftValue.([]any) | 		leftList, _ := leftValue.(*ListType) | ||||||
| 		rightList, _ := rightValue.([]any) | 		rightList, _ := rightValue.(*ListType) | ||||||
| 		diffList := make([]any, 0, len(leftList)-len(rightList)) | 		diffList := make(ListType, 0, len(*leftList)-len(*rightList)) | ||||||
| 		for _, item := range leftList { | 		for _, item := range *leftList { | ||||||
| 			if slices.Index(rightList, item) < 0 { | 			if slices.Index(*rightList, item) < 0 { | ||||||
| 				diffList = append(diffList, item) | 				diffList = append(diffList, item) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		v = diffList | 		v = &diffList | ||||||
|  | 	} else if isFraction(leftValue) || isFraction(rightValue) { | ||||||
|  | 		v, err = subAnyFract(leftValue, rightValue) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleTypes(leftValue, rightValue) | 		err = self.errIncompatibleTypes(leftValue, rightValue) | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								term.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								term.go
									
									
									
									
									
								
							| @ -20,6 +20,7 @@ const ( | |||||||
| 	priRelational | 	priRelational | ||||||
| 	priSum | 	priSum | ||||||
| 	priProduct | 	priProduct | ||||||
|  | 	priFraction | ||||||
| 	priSelector | 	priSelector | ||||||
| 	priSign | 	priSign | ||||||
| 	priFact | 	priFact | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								utils.go
									
									
									
									
									
								
							| @ -25,7 +25,7 @@ func isFloat(v any) (ok bool) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func isList(v any) (ok bool) { | func isList(v any) (ok bool) { | ||||||
| 	_, ok = v.([]any) | 	_, ok = v.(*ListType) | ||||||
| 	return ok | 	return ok | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -55,9 +55,13 @@ func isIterator(v any) (ok bool) { | |||||||
| func numAsFloat(v any) (f float64) { | func numAsFloat(v any) (f float64) { | ||||||
| 	var ok bool | 	var ok bool | ||||||
| 	if f, ok = v.(float64); !ok { | 	if f, ok = v.(float64); !ok { | ||||||
|  | 		if fract, ok := v.(*fraction); ok { | ||||||
|  | 			f = fract.toFloat() | ||||||
|  | 		} else { | ||||||
| 			i, _ := v.(int64) | 			i, _ := v.(int64) | ||||||
| 			f = float64(i) | 			f = float64(i) | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user