add min and max to the obitools expression language

This commit is contained in:
Eric Coissac
2025-05-13 16:03:03 +02:00
parent f1b9ac4a13
commit f9324dd8f4
4 changed files with 210 additions and 12 deletions

View File

@ -268,7 +268,11 @@ func (marker *Marker) CheckTagLength() error {
reverse_length[len(tags.Reverse)]++
}
maxfl, _ := obiutils.MaxMap(forward_length)
maxfl, _, err := obiutils.MaxMap(forward_length)
if err != nil {
return err
}
if len(forward_length) > 1 {
others := make([]int, 0)
@ -280,7 +284,11 @@ func (marker *Marker) CheckTagLength() error {
return fmt.Errorf("forward tag length %d is not the same for all the PCRs : %v", maxfl, others)
}
maxrl, _ := obiutils.MaxMap(reverse_length)
maxrl, _, err := obiutils.MaxMap(reverse_length)
if err != nil {
return err
}
if len(reverse_length) > 1 {
others := make([]int, 0)

View File

@ -8,7 +8,7 @@ import (
// corresponds to the last commit, and not the one when the file will be
// commited
var _Commit = "e065e29"
var _Commit = "f1b9ac4"
var _Version = "Release 4.4.0"
// Version returns the version of the obitools package.

View File

@ -135,6 +135,12 @@ var OBILang = gval.NewLanguage(
length := obiutils.Len(args[0])
return (float64)(length), nil
}),
gval.Function("min", func(args ...interface{}) (interface{}, error) {
return obiutils.Min(args[0])
}),
gval.Function("max", func(args ...interface{}) (interface{}, error) {
return obiutils.Max(args[0])
}),
gval.Function("contains", func(args ...interface{}) (interface{}, error) {
if obiutils.IsAMap(args[0]) {
val := reflect.ValueOf(args[0]).MapIndex(reflect.ValueOf(args[1]))

View File

@ -1,7 +1,10 @@
package obiutils
import (
log "github.com/sirupsen/logrus"
"errors"
"fmt"
"reflect"
"golang.org/x/exp/constraints"
)
@ -31,19 +34,200 @@ func MinMaxSlice[T constraints.Ordered](vec []T) (min, max T) {
return
}
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T) {
if len(values) == 0 {
log.Panicf("empty map")
}
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T, error) {
var maxKey K
var maxValue T
if len(values) == 0 {
return maxKey, maxValue, errors.New("Empty map")
}
first := true
for k, v := range values {
if v > maxValue {
if v > maxValue || first {
maxValue = v
maxKey = k
first = false
}
}
return maxKey, maxValue
return maxKey, maxValue, nil
}
func MinMap[K comparable, T constraints.Ordered](values map[K]T) (K, T, error) {
var minKey K
var minValue T
if len(values) == 0 {
return minKey, minValue, errors.New("Empty map")
}
first := true
for k, v := range values {
if v < minValue || first {
minValue = v
minKey = k
first = false
}
}
return minKey, minValue, nil
}
// Min returns the smallest element in a slice/array or map,
// or the value itself if data is a single comparable value.
// Returns an error if the container is empty or the type is unsupported.
func Min(data interface{}) (interface{}, error) {
v := reflect.ValueOf(data)
switch v.Kind() {
case reflect.Slice, reflect.Array:
if v.Len() == 0 {
return nil, errors.New("empty slice or array")
}
return minFromIterable(v)
case reflect.Map:
if v.Len() == 0 {
return nil, errors.New("empty map")
}
return minFromMap(v)
default:
if !isOrderedKind(v.Kind()) {
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
}
// single comparable value → return it
return data, nil
}
}
// Max returns the largest element in a slice/array or map,
// or the value itself if data is a single comparable value.
// Returns an error if the container is empty or the type is unsupported.
func Max(data interface{}) (interface{}, error) {
v := reflect.ValueOf(data)
switch v.Kind() {
case reflect.Slice, reflect.Array:
if v.Len() == 0 {
return nil, errors.New("empty slice or array")
}
return maxFromIterable(v)
case reflect.Map:
if v.Len() == 0 {
return nil, errors.New("empty map")
}
return maxFromMap(v)
default:
if !isOrderedKind(v.Kind()) {
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
}
return data, nil
}
}
// maxFromIterable scans a slice/array to find the maximum.
func maxFromIterable(v reflect.Value) (interface{}, error) {
var best reflect.Value
for i := 0; i < v.Len(); i++ {
elem := v.Index(i)
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if i == 0 || greater(elem, best) {
best = elem
}
}
return best.Interface(), nil
}
// minFromIterable finds min in slice or array.
func minFromIterable(v reflect.Value) (interface{}, error) {
var minVal reflect.Value
for i := 0; i < v.Len(); i++ {
elem := v.Index(i)
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if i == 0 || less(elem, minVal) {
minVal = elem
}
}
return minVal.Interface(), nil
}
// maxFromMap scans map values to find the maximum.
func maxFromMap(v reflect.Value) (interface{}, error) {
var best reflect.Value
first := true
for _, key := range v.MapKeys() {
elem := v.MapIndex(key)
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if first || greater(elem, best) {
best = elem
first = false
}
}
return best.Interface(), nil
}
// minFromMap finds min among map values.
func minFromMap(v reflect.Value) (interface{}, error) {
var minVal reflect.Value
first := true
for _, key := range v.MapKeys() {
elem := v.MapIndex(key)
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if first || less(elem, minVal) {
minVal = elem
first = false
}
}
return minVal.Interface(), nil
}
// isOrderedKind reports whether k supports comparison ordering.
func isOrderedKind(k reflect.Kind) bool {
switch k {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
return true
default:
return false
}
}
// less returns true if a < b for supported kinds.
func less(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return a.Int() < b.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
default:
// should never happen if caller checked isOrderedKind
return false
}
}
// greater returns true if a > b for supported kinds.
func greater(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return a.Int() > b.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return a.Uint() > b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() > b.Float()
case reflect.String:
return a.String() > b.String()
default:
return false
}
}