math.go (4923B)
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 the two addends n1 and n2.
36 func (ns *Namespace) Add(n1, n2 any) (any, error) {
37 return _math.DoArithmetic(n1, n2, '+')
38 }
39
40 // Ceil returns the least integer value greater than or equal to n.
41 func (ns *Namespace) Ceil(n any) (float64, error) {
42 xf, err := cast.ToFloat64E(n)
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 n1 by n2.
51 func (ns *Namespace) Div(n1, n2 any) (any, error) {
52 return _math.DoArithmetic(n1, n2, '/')
53 }
54
55 // Floor returns the greatest integer value less than or equal to n.
56 func (ns *Namespace) Floor(n any) (float64, error) {
57 xf, err := cast.ToFloat64E(n)
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 the number n.
66 func (ns *Namespace) Log(n any) (float64, error) {
67 af, err := cast.ToFloat64E(n)
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 the two numbers n1 or n2.
76 func (ns *Namespace) Max(n1, n2 any) (float64, error) {
77 af, erra := cast.ToFloat64E(n1)
78 bf, errb := cast.ToFloat64E(n2)
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 n1 or n2.
88 func (ns *Namespace) Min(n1, n2 any) (float64, error) {
89 af, erra := cast.ToFloat64E(n1)
90 bf, errb := cast.ToFloat64E(n2)
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 n1 % n2.
100 func (ns *Namespace) Mod(n1, n2 any) (int64, error) {
101 ai, erra := cast.ToInt64E(n1)
102 bi, errb := cast.ToInt64E(n2)
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 n1 % n2. If n1 % n2 == 0, return true.
116 func (ns *Namespace) ModBool(n1, n2 any) (bool, error) {
117 res, err := ns.Mod(n1, n2)
118 if err != nil {
119 return false, err
120 }
121
122 return res == int64(0), nil
123 }
124
125 // Mul multiplies the two numbers n1 and n2.
126 func (ns *Namespace) Mul(n1, n2 any) (any, error) {
127 return _math.DoArithmetic(n1, n2, '*')
128 }
129
130 // Pow returns n1 raised to the power of n2.
131 func (ns *Namespace) Pow(n1, n2 any) (float64, error) {
132 af, erra := cast.ToFloat64E(n1)
133 bf, errb := cast.ToFloat64E(n2)
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 integer nearest to n, rounding half away from zero.
143 func (ns *Namespace) Round(n any) (float64, error) {
144 xf, err := cast.ToFloat64E(n)
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 the number n.
153 func (ns *Namespace) Sqrt(n any) (float64, error) {
154 af, err := cast.ToFloat64E(n)
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 n2 from n1.
163 func (ns *Namespace) Sub(n1, n2 any) (any, error) {
164 return _math.DoArithmetic(n1, n2, '-')
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 }