summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2017-10-14 13:51:01 -0400
committerDave Henderson <dhenderson@gmail.com>2017-10-30 22:18:37 -0400
commit90ca86ec9c3db804ac32ef942fa04a0aa8c133e7 (patch)
tree5295f93799d77f8fc0eb5ed3199fd41b858b0d6e
parent4e626fdfc870c212c73e5b9494f63888e570f8ac (diff)
Adding math functions
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
-rw-r--r--docs/content/functions/math.md167
-rw-r--r--funcs.go1
-rw-r--r--funcs/math.go72
-rw-r--r--funcs/math_test.go61
-rw-r--r--math/math.go19
-rw-r--r--math/math_test.go12
-rw-r--r--test/integration/math.bats39
7 files changed, 371 insertions, 0 deletions
diff --git a/docs/content/functions/math.md b/docs/content/functions/math.md
new file mode 100644
index 00000000..7b9d01f5
--- /dev/null
+++ b/docs/content/functions/math.md
@@ -0,0 +1,167 @@
+---
+title: math functions
+menu:
+ main:
+ parent: functions
+---
+
+A set of basic math functions to be able to perform simple arithmetic operations with `gomplate`.
+
+### Supported input
+
+_**Note:** currently, `gomplate` supports only integer arithmetic. All functions
+return 64-bit integers (`int64` type). Floating point support will be added in
+later releases._
+
+In general, any input will be converted to the correct input type by the various
+functions in this package. For integer-based functions, floating-point inputs will
+be truncated (not rounded).
+
+In addition to regular base-10 numbers, integers can be
+[specified](https://golang.org/ref/spec#Integer_literals) as octal (prefix with
+`0`) or hexadecimal (prefix with `0x`).
+
+Decimal/floating-point numbers can be [specified](https://golang.org/ref/spec#Floating-point_literals)
+with optional exponents.
+
+Some examples demonstrating this:
+
+```console
+$ NUM=50 gomplate -i '{{ div (getenv "NUM") 10 }}'
+5
+$ gomplate -i '{{ add "0x2" "02" "2.0" "2e0" }}'
+8
+$ gomplate -i '{{ add 2.5 2.5 }}' # decimals are truncated!
+4
+```
+
+## `math.Add`
+
+**Alias:** `add`
+
+Adds all given operators.
+
+### Usage
+```go
+math.Add n...
+```
+```go
+x | math.Add.Add n...
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Add 1 2 3 4 }}
+10
+```
+
+## `math.Sub`
+
+**Alias:** `sub`
+
+Subtract the second from the first of the given operators.
+
+### Usage
+```go
+math.Sub a b
+```
+```go
+b | math.Sub a
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Sub 3 1 }}'
+2
+```
+
+## `math.Mul`
+
+**Alias:** `mul`
+
+Multiply all given operators together.
+
+### Usage
+```go
+math.Mul n...
+```
+```go
+x | math.Mul n...
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Mul 8 8 2 }}'
+128
+```
+
+## `math.Div`
+
+**Alias:** `div`
+
+Divide the first number by the second. Division by zero is disallowed.
+
+### Usage
+```go
+math.Div a b
+```
+```go
+b | math.Div a
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Div 8 2 }}'
+4
+```
+
+## `math.Rem`
+
+**Alias:** `rem`
+
+Return the remainder from an integer division operation.
+
+### Usage
+```go
+math.Rem a b
+```
+```go
+b | math.Rem b
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Rem 5 3 }}'
+2
+$ gomplate -i '{{ math.Rem -5 3 }}'
+-2
+```
+
+## `math.Pow`
+
+**Alias:** `pow`
+
+Calculate an exponent - _b<sup>n</sup>_. This wraps Go's [`math.Pow`](https://golang.org/pkg/math/#Pow).
+
+### Usage
+```go
+math.Pow b n
+```
+```go
+n | math.Pow b
+```
+
+### Example
+
+```console
+$ gomplate -i '{{ math.Pow 10 2 }}'
+100
+$ gomplate -i '{{ math.Pow 2 32 }}'
+4294967296
+```
+
diff --git a/funcs.go b/funcs.go
index 28bfc907..00967511 100644
--- a/funcs.go
+++ b/funcs.go
@@ -19,5 +19,6 @@ func initFuncs(d *data.Data) template.FuncMap {
funcs.AddEnvFuncs(f)
funcs.AddConvFuncs(f)
funcs.AddTimeFuncs(f)
+ funcs.AddMathFuncs(f)
return f
}
diff --git a/funcs/math.go b/funcs/math.go
new file mode 100644
index 00000000..e26e42dc
--- /dev/null
+++ b/funcs/math.go
@@ -0,0 +1,72 @@
+package funcs
+
+import (
+ "fmt"
+ gmath "math"
+ "sync"
+
+ "github.com/hairyhenderson/gomplate/conv"
+
+ "github.com/hairyhenderson/gomplate/math"
+)
+
+var (
+ mathNS *MathFuncs
+ mathNSInit sync.Once
+)
+
+// MathNS - the math namespace
+func MathNS() *MathFuncs {
+ mathNSInit.Do(func() { mathNS = &MathFuncs{} })
+ return mathNS
+}
+
+// AddMathFuncs -
+func AddMathFuncs(f map[string]interface{}) {
+ f["math"] = MathNS
+
+ f["add"] = MathNS().Add
+ f["sub"] = MathNS().Sub
+ f["mul"] = MathNS().Mul
+ f["div"] = MathNS().Div
+ f["rem"] = MathNS().Rem
+ f["pow"] = MathNS().Pow
+}
+
+// MathFuncs -
+type MathFuncs struct{}
+
+// Add -
+func (f *MathFuncs) Add(n ...interface{}) int64 {
+ return math.AddInt(conv.ToInt64s(n...)...)
+}
+
+// Mul -
+func (f *MathFuncs) Mul(n ...interface{}) int64 {
+ return math.MulInt(conv.ToInt64s(n...)...)
+}
+
+// Sub -
+func (f *MathFuncs) Sub(a, b interface{}) int64 {
+ return conv.ToInt64(a) - conv.ToInt64(b)
+}
+
+// Div -
+func (f *MathFuncs) Div(a, b interface{}) (int64, error) {
+ divisor := conv.ToInt64(a)
+ dividend := conv.ToInt64(b)
+ if dividend == 0 {
+ return 0, fmt.Errorf("Error: division by 0")
+ }
+ return divisor / dividend, nil
+}
+
+// Rem -
+func (f *MathFuncs) Rem(a, b interface{}) int64 {
+ return conv.ToInt64(a) % conv.ToInt64(b)
+}
+
+// Pow -
+func (f *MathFuncs) Pow(a, b interface{}) int64 {
+ return conv.ToInt64(gmath.Pow(conv.ToFloat64(a), conv.ToFloat64(b)))
+}
diff --git a/funcs/math_test.go b/funcs/math_test.go
new file mode 100644
index 00000000..cb0a0922
--- /dev/null
+++ b/funcs/math_test.go
@@ -0,0 +1,61 @@
+package funcs
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAdd(t *testing.T) {
+ m := MathNS()
+ assert.Equal(t, int64(12), m.Add(1, 1, 2, 3, 5))
+ assert.Equal(t, int64(2), m.Add(1, 1))
+ assert.Equal(t, int64(1), m.Add(1))
+ assert.Equal(t, int64(0), m.Add(-5, 5))
+}
+
+func TestMul(t *testing.T) {
+ m := MathNS()
+ assert.Equal(t, int64(30), m.Mul(1, 1, 2, 3, 5))
+ assert.Equal(t, int64(1), m.Mul(1, 1))
+ assert.Equal(t, int64(1), m.Mul(1))
+ assert.Equal(t, int64(-25), m.Mul("-5", 5))
+ assert.Equal(t, int64(28), m.Mul(14, "2"))
+}
+
+func TestSub(t *testing.T) {
+ m := MathNS()
+ assert.Equal(t, int64(0), m.Sub(1, 1))
+ assert.Equal(t, int64(-10), m.Sub(-5, 5))
+ assert.Equal(t, int64(-41), m.Sub(true, "42"))
+}
+
+func mustDiv(a, b interface{}) int64 {
+ m := MathNS()
+ r, err := m.Div(a, b)
+ if err != nil {
+ return -1
+ }
+ return r
+}
+
+func TestDiv(t *testing.T) {
+ m := MathNS()
+ _, err := m.Div(1, 0)
+ assert.Error(t, err)
+ assert.Equal(t, int64(1), mustDiv(1, 1))
+ assert.Equal(t, int64(-1), mustDiv(-5, 5))
+ assert.Equal(t, int64(0), mustDiv(true, "42"))
+}
+
+func TestRem(t *testing.T) {
+ m := MathNS()
+ assert.Equal(t, int64(0), m.Rem(1, 1))
+ assert.Equal(t, int64(2), m.Rem(5, 3.0))
+ // assert.Equal(t, int64(1), m.Mod(true, "42"))
+}
+
+func TestPow(t *testing.T) {
+ m := MathNS()
+ assert.Equal(t, int64(4), m.Pow(2, "2"))
+}
diff --git a/math/math.go b/math/math.go
new file mode 100644
index 00000000..9eaef051
--- /dev/null
+++ b/math/math.go
@@ -0,0 +1,19 @@
+package math
+
+// AddInt -
+func AddInt(n ...int64) int64 {
+ x := int64(0)
+ for _, i := range n {
+ x += i
+ }
+ return x
+}
+
+// MulInt -
+func MulInt(n ...int64) int64 {
+ var x int64 = 1
+ for _, i := range n {
+ x *= i
+ }
+ return x
+}
diff --git a/math/math_test.go b/math/math_test.go
new file mode 100644
index 00000000..9c4da13a
--- /dev/null
+++ b/math/math_test.go
@@ -0,0 +1,12 @@
+package math
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMath(t *testing.T) {
+ assert.Equal(t, int64(10), AddInt(1, 2, 3, 4))
+ assert.Equal(t, int64(12), MulInt(3, 4, 1))
+}
diff --git a/test/integration/math.bats b/test/integration/math.bats
new file mode 100644
index 00000000..0756485d
--- /dev/null
+++ b/test/integration/math.bats
@@ -0,0 +1,39 @@
+#!/usr/bin/env bats
+
+load helper
+
+@test "'math.Add'" {
+ gomplate -i '{{ math.Add 1 2 3 4 }} {{ add -5 5 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "10 0" ]]
+}
+
+@test "'math.Sub'" {
+ gomplate -i '{{ math.Sub 10 5 }} {{ sub -5 5 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "5 -10" ]]
+}
+
+@test "'math.Mul'" {
+ gomplate -i '{{ math.Mul 1 2 3 4 }} {{ mul -5 5 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "24 -25" ]]
+}
+
+@test "'math.Div'" {
+ gomplate -i '{{ math.Div 5 3 }} {{ div -5 5 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "1 -1" ]]
+}
+
+@test "'math.Rem'" {
+ gomplate -i '{{ math.Rem 5 3 }} {{ rem 2 2 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "2 0" ]]
+}
+
+@test "'math.Pow'" {
+ gomplate -i '{{ math.Pow 8 4 }} {{ pow 2 2 }}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "4096 4" ]]
+} \ No newline at end of file