github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/math/math.go (about)

     1  // Copyright 2017 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 provides template functions for mathematical operations.
    15  package math
    16  
    17  import (
    18  	"errors"
    19  	"math"
    20  	"sync/atomic"
    21  
    22  	_math "github.com/gohugoio/hugo/common/math"
    23  
    24  	"github.com/spf13/cast"
    25  )
    26  
    27  // New returns a new instance of the math-namespaced template functions.
    28  func New() *Namespace {
    29  	return &Namespace{}
    30  }
    31  
    32  // Namespace provides template functions for the "math" namespace.
    33  type Namespace struct{}
    34  
    35  // Add adds two numbers.
    36  func (ns *Namespace) Add(a, b interface{}) (interface{}, error) {
    37  	return _math.DoArithmetic(a, b, '+')
    38  }
    39  
    40  // Ceil returns the least integer value greater than or equal to x.
    41  func (ns *Namespace) Ceil(x interface{}) (float64, error) {
    42  	xf, err := cast.ToFloat64E(x)
    43  	if err != nil {
    44  		return 0, errors.New("Ceil operator can't be used with non-float value")
    45  	}
    46  
    47  	return math.Ceil(xf), nil
    48  }
    49  
    50  // Div divides two numbers.
    51  func (ns *Namespace) Div(a, b interface{}) (interface{}, error) {
    52  	return _math.DoArithmetic(a, b, '/')
    53  }
    54  
    55  // Floor returns the greatest integer value less than or equal to x.
    56  func (ns *Namespace) Floor(x interface{}) (float64, error) {
    57  	xf, err := cast.ToFloat64E(x)
    58  	if err != nil {
    59  		return 0, errors.New("Floor operator can't be used with non-float value")
    60  	}
    61  
    62  	return math.Floor(xf), nil
    63  }
    64  
    65  // Log returns the natural logarithm of a number.
    66  func (ns *Namespace) Log(a interface{}) (float64, error) {
    67  	af, err := cast.ToFloat64E(a)
    68  	if err != nil {
    69  		return 0, errors.New("Log operator can't be used with non integer or float value")
    70  	}
    71  
    72  	return math.Log(af), nil
    73  }
    74  
    75  // Max returns the greater of two numbers.
    76  func (ns *Namespace) Max(a, b interface{}) (float64, error) {
    77  	af, erra := cast.ToFloat64E(a)
    78  	bf, errb := cast.ToFloat64E(b)
    79  
    80  	if erra != nil || errb != nil {
    81  		return 0, errors.New("Max operator can't be used with non-float value")
    82  	}
    83  
    84  	return math.Max(af, bf), nil
    85  }
    86  
    87  // Min returns the smaller of two numbers.
    88  func (ns *Namespace) Min(a, b interface{}) (float64, error) {
    89  	af, erra := cast.ToFloat64E(a)
    90  	bf, errb := cast.ToFloat64E(b)
    91  
    92  	if erra != nil || errb != nil {
    93  		return 0, errors.New("Min operator can't be used with non-float value")
    94  	}
    95  
    96  	return math.Min(af, bf), nil
    97  }
    98  
    99  // Mod returns a % b.
   100  func (ns *Namespace) Mod(a, b interface{}) (int64, error) {
   101  	ai, erra := cast.ToInt64E(a)
   102  	bi, errb := cast.ToInt64E(b)
   103  
   104  	if erra != nil || errb != nil {
   105  		return 0, errors.New("modulo operator can't be used with non integer value")
   106  	}
   107  
   108  	if bi == 0 {
   109  		return 0, errors.New("the number can't be divided by zero at modulo operation")
   110  	}
   111  
   112  	return ai % bi, nil
   113  }
   114  
   115  // ModBool returns the boolean of a % b.  If a % b == 0, return true.
   116  func (ns *Namespace) ModBool(a, b interface{}) (bool, error) {
   117  	res, err := ns.Mod(a, b)
   118  	if err != nil {
   119  		return false, err
   120  	}
   121  
   122  	return res == int64(0), nil
   123  }
   124  
   125  // Mul multiplies two numbers.
   126  func (ns *Namespace) Mul(a, b interface{}) (interface{}, error) {
   127  	return _math.DoArithmetic(a, b, '*')
   128  }
   129  
   130  // Pow returns a raised to the power of b.
   131  func (ns *Namespace) Pow(a, b interface{}) (float64, error) {
   132  	af, erra := cast.ToFloat64E(a)
   133  	bf, errb := cast.ToFloat64E(b)
   134  
   135  	if erra != nil || errb != nil {
   136  		return 0, errors.New("Pow operator can't be used with non-float value")
   137  	}
   138  
   139  	return math.Pow(af, bf), nil
   140  }
   141  
   142  // Round returns the nearest integer, rounding half away from zero.
   143  func (ns *Namespace) Round(x interface{}) (float64, error) {
   144  	xf, err := cast.ToFloat64E(x)
   145  	if err != nil {
   146  		return 0, errors.New("Round operator can't be used with non-float value")
   147  	}
   148  
   149  	return _round(xf), nil
   150  }
   151  
   152  // Sqrt returns the square root of a number.
   153  func (ns *Namespace) Sqrt(a interface{}) (float64, error) {
   154  	af, err := cast.ToFloat64E(a)
   155  	if err != nil {
   156  		return 0, errors.New("Sqrt operator can't be used with non integer or float value")
   157  	}
   158  
   159  	return math.Sqrt(af), nil
   160  }
   161  
   162  // Sub subtracts two numbers.
   163  func (ns *Namespace) Sub(a, b interface{}) (interface{}, error) {
   164  	return _math.DoArithmetic(a, b, '-')
   165  }
   166  
   167  var counter uint64
   168  
   169  // Counter increments and returns a global counter.
   170  // This was originally added to be used in tests where now.UnixNano did not
   171  // have the needed precision (especially on Windows).
   172  // Note that given the parallel nature of Hugo, you cannot use this to get sequences of numbers,
   173  // and the counter will reset on new builds.
   174  func (ns *Namespace) Counter() uint64 {
   175  	return atomic.AddUint64(&counter, uint64(1))
   176  }