diff --git a/engine/number.go b/engine/number.go index d6ec1a9a..f15d3a1b 100644 --- a/engine/number.go +++ b/engine/number.go @@ -46,6 +46,7 @@ var DefaultEvaluableFunctors = EvaluableFunctors{ `div`: IntFloorDiv, `max`: Max, `min`: Min, + `^`: IntegerPower, }, } @@ -901,6 +902,24 @@ func Min(x, y Number) (Number, error) { } } +// IntegerPower returns x raised to the power of y. +func IntegerPower(x, y Number) (Number, error) { + if x, ok := x.(Integer); ok { + if y, ok := y.(Integer); ok { + if x != 1 && y < -1 { + return nil, TypeErrorFloat(x) + } + + r, err := Power(x, y) + if err != nil { + return nil, err + } + return truncateFtoI(r.(Float)) + } + } + return Power(x, y) +} + // Comparison func eqF(x, y Float) bool { diff --git a/engine/number_test.go b/engine/number_test.go index 02e634f2..dfc814df 100644 --- a/engine/number_test.go +++ b/engine/number_test.go @@ -1532,6 +1532,78 @@ func TestMin(t *testing.T) { }) } +func TestIntegerPower(t *testing.T) { + t.Run("integer", func(t *testing.T) { + t.Run("integer", func(t *testing.T) { + r, err := IntegerPower(Integer(1), Integer(1)) + assert.NoError(t, err) + assert.Equal(t, Integer(1), r) + + t.Run("x is not equal to 1 and y is less than -1", func(t *testing.T) { + _, err := IntegerPower(Integer(2), Integer(-2)) + assert.Equal(t, TypeErrorFloat(Integer(2)), err) + }) + }) + + t.Run("float", func(t *testing.T) { + r, err := IntegerPower(Integer(1), Float(1)) + assert.NoError(t, err) + assert.Equal(t, Float(1), r) + }) + + t.Run("not a number", func(t *testing.T) { + _, err := IntegerPower(Integer(1), mockNumber{}) + assert.Equal(t, ErrUndefined, err) + }) + }) + + t.Run("float", func(t *testing.T) { + t.Run("integer", func(t *testing.T) { + r, err := IntegerPower(Float(1), Integer(1)) + assert.NoError(t, err) + assert.Equal(t, Float(1), r) + }) + + t.Run("float", func(t *testing.T) { + r, err := IntegerPower(Float(1), Float(1)) + assert.NoError(t, err) + assert.Equal(t, Float(1), r) + }) + + t.Run("not a number", func(t *testing.T) { + _, err := IntegerPower(Float(1), mockNumber{}) + assert.Equal(t, ErrUndefined, err) + }) + }) + + t.Run("not a number", func(t *testing.T) { + _, err := IntegerPower(mockNumber{}, Float(1)) + assert.Equal(t, ErrUndefined, err) + }) + + t.Run("overflow", func(t *testing.T) { + _, err := IntegerPower(Float(math.MaxFloat64), Float(2)) + assert.Equal(t, ErrFloatOverflow, err) + }) + + t.Run("underflow", func(t *testing.T) { + _, err := IntegerPower(Float(math.SmallestNonzeroFloat64), Float(2)) + assert.Equal(t, ErrUnderflow, err) + }) + + t.Run("undefined", func(t *testing.T) { + t.Run("vx is negative and vy is not an integer", func(t *testing.T) { + _, err := IntegerPower(Integer(-1), Float(1.1)) + assert.Equal(t, ErrUndefined, err) + }) + + t.Run("vx is zero and vy is negative", func(t *testing.T) { + _, err := IntegerPower(Integer(0), Integer(-1)) + assert.Equal(t, ErrUndefined, err) + }) + }) +} + type mockNumber struct { mock.Mock }