#  Copyright 2020 Modelling, Simulation and Design Lab (MSDL) at
#  McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
#
#  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.

"""This file contains a set of mathematical equations as functions.

These equations can be used as an input for the :class:`pypdevsbbl.generic.math.Equation`
block for simplicity.

Note:
    The described functions were written with the idea of numerical values as
    inputs. While it is advised to refrain from using non-numerical arguments,
    it is allowed within *Python*. There are no guarantees on what might happen
    if you decide to use non-numeric input.
"""

def SUBTRACT(x, y):
    """Subtracts two values.

    Args:
        x (numeric):    The value to subtract from.
        y (numeric):    The value that subtracts.

    Returns:
        :math:`x - y`
    """
    return x - y


def DIVIDE(x, y):
    """Divides two values.

    Args:
        x (numeric):    The numerator.
        y (numeric):    The denominator.

    Returns:
        :math:`\\dfrac{x}{y}`
    """
    return x / y


def IDIVIDE(x, y):
    """Integer divides two values.

    Args:
        x (numeric):    The numerator.
        y (numeric):    The denominator.

    Returns:
        :math:`\\left\\lfloor\\dfrac{x}{y}\\right\\rfloor`
    """
    return x // y


def MODULO(x, y):
    """Modulo divides two values.

    Args:
        x (numeric):    The numerator.
        y (numeric):    The denominator.

    Returns:
        :math:`x \\textrm{ mod } y`
    """
    return x % y


def NEGATE(x):
    """Negates a value.

    Args:
        x (numeric):    The value to negate.

    Returns:
        :math:`- x`
    """
    return -x


def INVERT(x):
    """Inverts a value.

    Args:
        x (numeric):    The value to invert.

    Returns:
        :math:`\\dfrac{1}{x}`
    """
    return 1.0 / x


def POWER(x, y):
    """Raises x to the power of y.

    Args:
        x (numeric):    The base.
        y (numeric):    The power.

    Returns:
        :math:`x^y`
    """
    return x ** y


def AND(x, y):
    """Boolean AND gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ AND } y`
    """
    return x and y


def OR(x, y):
    """Boolean OR gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ OR } y`
    """
    return x or y


def BAND(x, y):
    """Bitwise AND gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ & } y`
    """
    return x & y


def BOR(x, y):
    """Bitwise OR gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ | } y`
    """
    return x | y


def NOT(x):
    """Boolean NOT gate of x.

    Args:
        x (numeric):    The value to invert.

    Returns:
        :math:`\\textrm{NOT } x`
    """
    return not x


def XOR(x, y):
    """The XOR gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ XOR } y`
    """
    return x ^ y


def NOR(x, y):
    """The NOR gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ NOR } y`
    """
    return not (x or y)


def XNOR(x, y):
    """The XNOR or XAND gate between x and y.

    Args:
        x (numeric):    The left hand side of the gate.
        y (numeric):    The right hand side of the gate.

    Returns:
        :math:`x \\textrm{ XNOR } y`
    """
    return not (x ^ y)


def LT(x, y):
    """Checks if x is less than y.

    Args:
        x (numeric):    The smaller value.
        y (numeric):    The larger value.

    Returns:
        :math:`x < y`
    """
    return x < y


def LE(x, y):
    """Checks if x is less than or equal to y.

    Args:
        x (numeric):    The smaller value.
        y (numeric):    The larger value.

    Returns:
        :math:`x \\leq y`
    """
    return x <= y

#  Copyright 2020 Modelling, Simulation and Design Lab (MSDL) at
#  McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
#
#  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.

LEQ = LE
"""Synonym of :meth:`LE`."""


def GT(x, y):
    """Checks if x is greater than y.

    Args:
        x (numeric):    The larger value.
        y (numeric):    The smaller value.

    Returns:
        :math:`x > y`
    """
    return x > y


def GE(x, y):
    """Checks if x is greater than or equal to y.

    Args:
        x (numeric):    The larger value.
        y (numeric):    The smaller value.

    Returns:
        :math:`x \\geq y`
    """
    return x >= y

GEQ = GE
"""Synonym of :meth:`GE`."""


def EQ(x, y):
    """Checks if x equals y.

    Args:
        x (numeric):    The left hand side.
        y (numeric):    The right hand side.

    Returns:
        :math:`x == y`
    """
    return x == y

EQUALS = EQ
"""Synonym of :meth:`EQ`."""


def IDENTICAL(x, y):
    """Checks if x is identical to y.

    Args:
        x (numeric):    The left hand side.
        y (numeric):    The right hand side.

    Returns:
        :math:`x \\textrm{ is } y`
    """
    return x is y

