yaql/yaql/standard_library/math.py

707 lines
15 KiB
Python

# Copyright (c) 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
The Math module describes implemented math operations on numbers.
"""
import random
from yaql.language import specs
from yaql.language import yaqltypes
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_+')
def binary_plus(left, right):
""":yaql:operator +
Returns the sum of left and right operands.
:signature: left + right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType right: number
:returnType: number
.. code::
yaql> 3 + 2
5
"""
return left + right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_-')
def binary_minus(left, right):
""":yaql:operator -
Returns the difference between left and right.
:signature: left - right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType right: number
:returnType: number
.. code::
yaql> 3 - 2
1
"""
return left - right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_*')
def multiplication(left, right):
""":yaql:operator *
Returns left multiplied by right.
:signature: left * right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType right: number
:returnType: number
.. code::
yaql> 3 * 2.5
7.5
"""
return left * right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_/')
def division(left, right):
""":yaql:operator /
Returns left divided by right.
:signature: left / right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType right: number
:returnType: number
.. code::
yaql> 3 / 2
1
yaql> 3.0 / 2
1.5
"""
if isinstance(left, int) and isinstance(right, int):
return left // right
return left / right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_mod')
def modulo(left, right):
""":yaql:operator mod
Returns left modulo right.
:signature: left mod right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType right: number
:returnType: number
.. code::
yaql> 3 mod 2
1
"""
return left % right
@specs.parameter('op', yaqltypes.Number())
@specs.name('#unary_operator_+')
def unary_plus(op):
""":yaql:operator unary +
Returns +op.
:signature: +op
:arg op: operand
:argType op: number
:returnType: number
.. code::
yaql> +2
2
"""
return +op
@specs.parameter('op', yaqltypes.Number())
@specs.name('#unary_operator_-')
def unary_minus(op):
""":yaql:operator unary -
Returns -op.
:signature: -op
:arg op: operand
:argType op: number
:returnType: number
.. code::
yaql> -2
-2
"""
return -op
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_>')
def gt(left, right):
""":yaql:operator >
Returns true if left is strictly greater than right, false otherwise.
:signature: left > right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType left: number
:returnType: boolean
.. code::
yaql> 3 > 2
true
"""
return left > right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_>=')
def gte(left, right):
""":yaql:operator >=
Returns true if left is greater or equal to right, false otherwise.
:signature: left >= right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType left: number
:returnType: boolean
.. code::
yaql> 3 >= 3
true
"""
return left >= right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_<')
def lt(left, right):
""":yaql:operator <
Returns true if left is strictly less than right, false otherwise.
:signature: left < right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType left: number
:returnType: boolean
.. code::
yaql> 3 < 2
false
"""
return left < right
@specs.parameter('left', yaqltypes.Number())
@specs.parameter('right', yaqltypes.Number())
@specs.name('#operator_<=')
def lte(left, right):
""":yaql:operator <=
Returns true if left is less or equal to right, false otherwise.
:signature: left <= right
:arg left: left operand
:argType left: number
:arg right: right operand
:argType left: number
:returnType: boolean
.. code::
yaql> 3 <= 3
true
"""
return left <= right
@specs.parameter('op', yaqltypes.Number())
def abs_(op):
""":yaql:abs
Returns the absolute value of a number.
:signature: abs(op)
:arg op: input value
:argType op: number
:returnType: number
.. code::
yaql> abs(-2)
2
"""
return abs(op)
def int_(value):
""":yaql:int
Returns an integer built from number, string or null value.
:signature: int(value)
:arg value: input value
:argType value: number, string or null
:returnType: integer
.. code::
yaql> int("2")
2
yaql> int(12.999)
12
yaql> int(null)
0
"""
if value is None:
return 0
return int(value)
def float_(value):
""":yaql:float
Returns a floating number built from number, string or null value.
:signature: float(value)
:arg value: input value
:argType value: number, string or null
:returnType: float
.. code::
yaql> float("2.2")
2.2
yaql> float(12)
12.0
yaql> float(null)
0.0
"""
if value is None:
return 0.0
return float(value)
def random_():
""":yaql:random
Returns the next random floating number from [0.0, 1.0).
:signature: random()
:returnType: float
.. code::
yaql> random()
0.6039529924951869
"""
return random.random()
def random__(from_, to_):
""":yaql:random
Returns the next random integer from [a, b].
:signature: random(from, to)
:arg from: left value for generating random number
:argType from: integer
:arg to: right value for generating random number
:argType to: integer
:returnType: integer
.. code::
yaql> random(1, 2)
2
yaql> random(1, 2)
1
"""
return random.randint(from_, to_)
@specs.parameter('left', int)
@specs.parameter('right', int)
def bitwise_and(left, right):
""":yaql:bitwiseAnd
Returns applied "bitwise and" to left and right integers.
Each bit of the output is 1 if the corresponding bit of left AND right
is 1, otherwise 0.
:signature: bitwiseAnd(left, right)
:arg left: left value
:argType left: integer
:arg right: right value
:argType right: integer
:returnType: integer
.. code::
yaql> bitwiseAnd(6, 12)
4
"""
return left & right
@specs.parameter('left', int)
@specs.parameter('right', int)
def bitwise_or(left, right):
""":yaql:bitwiseOr
Returns applied "bitwise or" to left and right numbers.
Each bit of the output is 1 if the corresponding bit of left OR right
is 1, otherwise 0.
:signature: bitwiseOr(left, right)
:arg left: left value
:argType left: integer
:arg right: right value
:argType right: integer
:returnType: integer
.. code::
yaql> bitwiseOr(6, 12)
14
"""
return left | right
@specs.parameter('left', int)
@specs.parameter('right', int)
def bitwise_xor(left, right):
""":yaql:bitwiseXor
Returns applied "bitwise exclusive or" to left and right numbers.
Each bit of the output is equal to the sum of corresponding left and right
bits mod 2.
:signature: bitwiseXor(left, right)
:arg left: left value
:argType left: integer
:arg right: right value
:argType right: integer
:returnType: integer
.. code::
yaql> bitwiseXor(6, 12)
10
"""
return left ^ right
@specs.parameter('arg', int)
def bitwise_not(arg):
""":yaql:bitwiseNot
Returns an integer where each bit is a reversed corresponding bit of arg.
:signature: bitwiseNot(arg)
:arg arg: input value
:argType arg: integer
:returnType: integer
.. code::
yaql> bitwiseNot(6)
-7
"""
return ~arg
@specs.parameter('value', int)
@specs.parameter('bits_number', int)
def shift_bits_right(value, bits_number):
""":yaql:shiftBitsRight
Shifts the bits of value right by the number of bits bitsNumber.
:signature: shiftBitsRight(value, bitsNumber)
:arg value: given value
:argType value: integer
:arg bitsNumber: number of bits
:argType right: integer
:returnType: integer
.. code::
yaql> shiftBitsRight(8, 2)
2
"""
return value >> bits_number
@specs.parameter('value', int)
@specs.parameter('bits_number', int)
def shift_bits_left(value, bits_number):
""":yaql:shiftBitsLeft
Shifts the bits of value left by the number of bits bitsNumber.
:signature: shiftBitsLeft(value, bitsNumber)
:arg value: given value
:argType value: integer
:arg bitsNumber: number of bits
:argType right: integer
:returnType: integer
.. code::
yaql> shiftBitsLeft(8, 2)
32
"""
return value << bits_number
@specs.parameter('a', nullable=True)
@specs.parameter('b', nullable=True)
@specs.inject('operator', yaqltypes.Delegate('#operator_>'))
def max_(a, b, operator):
""":yaql:max
Returns max from a and b.
:signature: max(a, b)
:arg a: input value
:argType a: number
:arg b: input value
:argType b: number
:returnType: number
.. code::
yaql> max(8, 2)
8
"""
if operator(b, a):
return b
return a
@specs.inject('operator', yaqltypes.Delegate('#operator_>'))
def min_(a, b, operator):
""":yaql:min
Returns min from a and b.
:signature: min(a, b)
:arg a: input value
:argType a: number
:arg b: input value
:argType b: number
:returnType: number
.. code::
yaql> min(8, 2)
2
"""
if operator(b, a):
return a
return b
@specs.parameter('a', yaqltypes.Number())
@specs.parameter('b', yaqltypes.Number())
@specs.parameter('c', yaqltypes.Number(nullable=True))
def pow_(a, b, c=None):
""":yaql:pow
Returns a to the power b modulo c.
:signature: pow(a, b, c => null)
:arg a: input value
:argType a: number
:arg b: power
:argType b: number
:arg c: modulo. null by default, which means no modulo is done after power.
:argType c: integer
:returnType: number
.. code::
yaql> pow(3, 2)
9
yaql> pow(3, 2, 5)
4
"""
return pow(a, b, c)
@specs.parameter('num', yaqltypes.Number())
def sign(num):
""":yaql:sign
Returns 1 if num > 0; 0 if num = 0; -1 if num < 0.
:signature: sign(num)
:arg num: input value
:argType num: number
:returnType: integer (-1, 0 or 1)
.. code::
yaql> sign(2)
1
"""
if num > 0:
return 1
elif num < 0:
return -1
return 0
@specs.parameter('number', yaqltypes.Number())
@specs.parameter('ndigits', int)
def round_(number, ndigits=0):
""":yaql:round
Returns a floating number rounded to ndigits after the decimal point.
:signature: round(number, ndigits => 0)
:arg number: input value
:argType number: number
:arg ndigits: with how many digits after decimal point to round.
0 by default
:argType ndigits: integer
:returnType: number
.. code::
yaql> round(12.52)
13
yaql> round(12.52, 1)
12.5
"""
return round(number, ndigits)
def is_integer(value):
""":yaql:isInteger
Returns true if value is an integer number, otherwise false.
:signature: isInteger(value)
:arg value: input value
:argType value: any
:returnType: boolean
.. code::
yaql> isInteger(12.0)
false
yaql> isInteger(12)
true
"""
return isinstance(value, int) and not isinstance(value, bool)
def is_number(value):
""":yaql:isNumber
Returns true if value is an integer or floating number, otherwise false.
:signature: isNumber(value)
:arg value: input value
:argType value: any
:returnType: boolean
.. code::
yaql> isNumber(12.0)
true
yaql> isNumber(12)
true
"""
return isinstance(value, (int, float)) and not isinstance(value, bool)
def register(context):
context.register_function(binary_plus)
context.register_function(binary_minus)
context.register_function(multiplication)
context.register_function(division)
context.register_function(modulo)
context.register_function(unary_plus)
context.register_function(unary_minus)
context.register_function(abs_)
context.register_function(gt)
context.register_function(gte)
context.register_function(lt)
context.register_function(lte)
context.register_function(int_)
context.register_function(float_)
context.register_function(random_)
context.register_function(random__)
context.register_function(bitwise_and)
context.register_function(bitwise_or)
context.register_function(bitwise_not)
context.register_function(bitwise_xor)
context.register_function(shift_bits_left)
context.register_function(shift_bits_right)
context.register_function(max_)
context.register_function(min_)
context.register_function(pow_)
context.register_function(sign)
context.register_function(round_)
context.register_function(is_integer)
context.register_function(is_number)