"""@file mathlib.py
@brief Math functions library
@author Libor Novotny (xnovot2j), Tomas Drdla (xdrdla04), Martin Hemza (xhemza05), Petr Sima (xsimap00)
"""

class calc_math:

    @staticmethod
    def add(x, y):
        """Addition
        @param x addend
        @param y addend
        @return sum of arguments
        """

        return x + y
    
    @staticmethod
    def array_sum(array):
        """Sum of array items
        @param array array of numbers
        @return sum of array
        """
        
        result = 0
        
        for x in array:
            result += x

        return result
    
    @staticmethod
    def sub(x, y):
        """Substraction
        @param x minuend
        @param y subtrahend
        @return difference
        """

        return x - y
    
    @staticmethod
    def abs_val(x):
        """Absolute value
        @param number
        @return absolute value of argument
        """

        return (x if x >= 0 else -x)

    @staticmethod
    def mul(x, y):
        """Multiplication
        @param x multiplicant
        @param y multiplicator
        @return product
        """

        return x * y
    
    @staticmethod
    def div(dividend, divisor, rnd = 10):
        """Division
        @param dividend
        @param divisor
        @param rnd number of decimal places (deafult 2, max 100), -1 for division with the remainder
        @return  quotient for round > -1, [quotient,remainder] for round -1
        """

        if divisor == 0:
            raise ZeroDivisionError('Division by zero!')

        if rnd < -1:
            raise ValueError('Round argument cannot be lower than -1!')

        quotient = dividend / divisor

        if rnd == -1:
            return [int(quotient), dividend - int(quotient) * divisor] 
        else:
            return round(quotient, rnd)
    
    @staticmethod
    def factor(x):
        """Factorial
        @param x non-negative integer
        @return returns product of all positive integers less than or equal to argument
        """

        x = int(x)

        if x < 0:
            raise ValueError("Factorial argument has to be a non-negative integer")
        elif x > 20:
            raise OverflowError("Factorial argument cannot be larger than 20")

        if x == 0 or x == 1:
            return 1

        result = 1

        for n in range(2, x + 1):
            result *= n

        return result

    @staticmethod
    def exp(base, exponent):
        """Exponentation
        @param base
        @param exponent non-negative integer
        @return product of exponentation
        """

        base = int(base)

        if exponent < 0:
            raise ValueError("Exponent has to be a non-negative integer")

        if exponent == 0:
            return 1

        result = base

        while exponent > 1:
            result *= base
            exponent -= 1

        return result

    @staticmethod
    def rad(radicand, index):
        """Radical
        @param radicand
        @param index
        @return root
        """

        index = int(index)

        if index <= 0:
            raise ValueError("Index has to be a positive integer")

        if calc_math.div(index, 2, -1)[1] == 0 and radicand < 0:
            raise ValueError("Radicand cannot be less than 0 for even indexes")

        result = pow(calc_math.abs_val(radicand), 1/index)

        # convert result to int if result is a whole number
        if result - int(result) == 0:
            result = int(result)

        return (result if radicand >= 0 else -result)

    @staticmethod
    def log(argument, base):
        """Logarithm
        @param argument
        @param base
        @return exponent
        """

        if argument < base:
            return 0

        return calc_math.log(argument / base, base) + 1
