Compare commits
	
		
			No commits in common. "5809de419f19cc98824f480e0b43f21be63739e8" and "c0c2ab8b4ea1aa1c6d43c8112b9775f487087b6f" have entirely different histories.
		
	
	
		
			5809de419f
			...
			c0c2ab8b4e
		
	
		
| @ -5,10 +5,9 @@ | |||||||
| package expr | 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
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								formatter.go
									
									
									
									
									
								
							| @ -1,16 +0,0 @@ | |||||||
| // 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										87
									
								
								func-math.go
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								func-math.go
									
									
									
									
									
								
							| @ -10,17 +10,16 @@ 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) || isFraction(paramValue)) { | 	if !(isNumber(paramValue) || isList(paramValue) || isIterator(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, sumAsFract bool | 	var sumAsFloat = false | ||||||
| 	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() { | ||||||
| @ -37,37 +36,19 @@ 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.(*ListType); ok { | 			if array, ok := v.([]any); 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 { | 		if !sumAsFloat && isFloat(v) { | ||||||
| 			if isFloat(v) { | 			sumAsFloat = true | ||||||
| 				sumAsFloat = true | 			floatSum = float64(intSum) | ||||||
| 				if sumAsFract { |  | ||||||
| 					floatSum = fractSum.toFloat() |  | ||||||
| 				} else { |  | ||||||
| 					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 | ||||||
| @ -77,8 +58,6 @@ 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 | ||||||
| 		} | 		} | ||||||
| @ -92,58 +71,28 @@ 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, mulAsFract bool | 	var mulAsFloat = false | ||||||
| 	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 err = checkNumberParamExpected(name, v, it.Index()); err != nil { | ||||||
| 			if v, err = doAdd(ctx, name, subIter); err != nil { | 			break | ||||||
| 				break | 		} | ||||||
| 			} |  | ||||||
| 			if subIter.HasOperation(cleanName) { |  | ||||||
| 				if _, err = subIter.CallOperation(cleanName, nil); err != nil { |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			if array, ok := v.(*ListType); ok { | 		if array, ok := v.([]any); 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 { | 		if !mulAsFloat && isFloat(v) { | ||||||
| 			if isFloat(v) { | 			mulAsFloat = true | ||||||
| 				mulAsFloat = true | 			floatProd = float64(intProd) | ||||||
| 				if mulAsFract { |  | ||||||
| 					floatProd = fractProd.toFloat() |  | ||||||
| 				} else { |  | ||||||
| 					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 | ||||||
| @ -153,8 +102,6 @@ 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     ListType | 	a     []any | ||||||
| 	index int | 	index int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewFlatArrayIterator(array ListType) *FlatArrayIterator { | func NewFlatArrayIterator(array []any) *FlatArrayIterator { | ||||||
| 	return &FlatArrayIterator{a: array, index: 0} | 	return &FlatArrayIterator{a: array, index: 0} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,44 +4,6 @@ | |||||||
| // 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) | ||||||
| @ -61,7 +23,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(ListType, len(list)) | 	items := make([]any, 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 { | ||||||
| @ -70,7 +32,35 @@ 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
 | ||||||
|  | // }
 | ||||||
|  | |||||||
| @ -1,274 +0,0 @@ | |||||||
| // 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,8 +42,6 @@ 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) | ||||||
| 	} | 	} | ||||||
| @ -87,8 +85,6 @@ 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,24 +39,22 @@ 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 *ListType | 		var leftList, rightList []any | ||||||
| 		var ok bool | 		var ok bool | ||||||
| 		if leftList, ok = leftValue.(*ListType); !ok { | 		if leftList, ok = leftValue.([]any); !ok { | ||||||
| 			leftList = &ListType{leftValue} | 			leftList = []any{leftValue} | ||||||
| 		} | 		} | ||||||
| 		if rightList, ok = rightValue.(*ListType); !ok { | 		if rightList, ok = rightValue.([]any); !ok { | ||||||
| 			rightList = &ListType{rightValue} | 			rightList = []any{rightValue} | ||||||
| 		} | 		} | ||||||
| 		sumList := make(ListType, 0, len(*leftList)+len(*rightList)) | 		sumList := make([]any, 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) | ||||||
| 	} | 	} | ||||||
| @ -91,17 +89,15 @@ 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.(*ListType) | 		leftList, _ := leftValue.([]any) | ||||||
| 		rightList, _ := rightValue.(*ListType) | 		rightList, _ := rightValue.([]any) | ||||||
| 		diffList := make(ListType, 0, len(*leftList)-len(*rightList)) | 		diffList := make([]any, 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,7 +20,6 @@ const ( | |||||||
| 	priRelational | 	priRelational | ||||||
| 	priSum | 	priSum | ||||||
| 	priProduct | 	priProduct | ||||||
| 	priFraction |  | ||||||
| 	priSelector | 	priSelector | ||||||
| 	priSign | 	priSign | ||||||
| 	priFact | 	priFact | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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.(*ListType) | 	_, ok = v.([]any) | ||||||
| 	return ok | 	return ok | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -55,12 +55,8 @@ 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 { | 		i, _ := v.(int64) | ||||||
| 			f = fract.toFloat() | 		f = float64(i) | ||||||
| 		} else { |  | ||||||
| 			i, _ := v.(int64) |  | ||||||
| 			f = float64(i) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user