github.com/neohugo/neohugo@v0.123.8/common/math/math.go (about)

     1  // Copyright 2018 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package math
    15  
    16  import (
    17  	"errors"
    18  	"reflect"
    19  )
    20  
    21  // DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
    22  // determine the type of the two terms.
    23  func DoArithmetic(a, b any, op rune) (any, error) {
    24  	av := reflect.ValueOf(a)
    25  	bv := reflect.ValueOf(b)
    26  	var ai, bi int64
    27  	var af, bf float64
    28  	var au, bu uint64
    29  	switch av.Kind() {
    30  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    31  		ai = av.Int()
    32  		switch bv.Kind() {
    33  		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    34  			bi = bv.Int()
    35  		case reflect.Float32, reflect.Float64:
    36  			af = float64(ai) // may overflow
    37  			ai = 0
    38  			bf = bv.Float()
    39  		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    40  			bu = bv.Uint()
    41  			if ai >= 0 {
    42  				au = uint64(ai)
    43  				ai = 0
    44  			} else {
    45  				bi = int64(bu) // may overflow
    46  				bu = 0
    47  			}
    48  		default:
    49  			return nil, errors.New("can't apply the operator to the values")
    50  		}
    51  	case reflect.Float32, reflect.Float64:
    52  		af = av.Float()
    53  		switch bv.Kind() {
    54  		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    55  			bf = float64(bv.Int()) // may overflow
    56  		case reflect.Float32, reflect.Float64:
    57  			bf = bv.Float()
    58  		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    59  			bf = float64(bv.Uint()) // may overflow
    60  		default:
    61  			return nil, errors.New("can't apply the operator to the values")
    62  		}
    63  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    64  		au = av.Uint()
    65  		switch bv.Kind() {
    66  		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    67  			bi = bv.Int()
    68  			if bi >= 0 {
    69  				bu = uint64(bi)
    70  				bi = 0
    71  			} else {
    72  				ai = int64(au) // may overflow
    73  				au = 0
    74  			}
    75  		case reflect.Float32, reflect.Float64:
    76  			af = float64(au) // may overflow
    77  			au = 0
    78  			bf = bv.Float()
    79  		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    80  			bu = bv.Uint()
    81  		default:
    82  			return nil, errors.New("can't apply the operator to the values")
    83  		}
    84  	case reflect.String:
    85  		as := av.String()
    86  		if bv.Kind() == reflect.String && op == '+' {
    87  			bs := bv.String()
    88  			return as + bs, nil
    89  		}
    90  		return nil, errors.New("can't apply the operator to the values")
    91  	default:
    92  		return nil, errors.New("can't apply the operator to the values")
    93  	}
    94  
    95  	switch op {
    96  	case '+':
    97  		if ai != 0 || bi != 0 {
    98  			return ai + bi, nil
    99  		} else if af != 0 || bf != 0 {
   100  			return af + bf, nil
   101  		} else if au != 0 || bu != 0 {
   102  			return au + bu, nil
   103  		}
   104  		return 0, nil
   105  	case '-':
   106  		if ai != 0 || bi != 0 {
   107  			return ai - bi, nil
   108  		} else if af != 0 || bf != 0 {
   109  			return af - bf, nil
   110  		} else if au != 0 || bu != 0 {
   111  			return au - bu, nil
   112  		}
   113  		return 0, nil
   114  	case '*':
   115  		if ai != 0 || bi != 0 {
   116  			return ai * bi, nil
   117  		} else if af != 0 || bf != 0 {
   118  			return af * bf, nil
   119  		} else if au != 0 || bu != 0 {
   120  			return au * bu, nil
   121  		}
   122  		return 0, nil
   123  	case '/':
   124  		if bi != 0 {
   125  			return ai / bi, nil
   126  		} else if bf != 0 {
   127  			return af / bf, nil
   128  		} else if bu != 0 {
   129  			return au / bu, nil
   130  		}
   131  		return nil, errors.New("can't divide the value by 0")
   132  	default:
   133  		return nil, errors.New("there is no such an operation")
   134  	}
   135  }