Пример #1
0
def factorial(argument: int) -> int:
    """Returns the product of all non-negative integers less than or equal to some non-negative integer.

    Args:
        argument (int): Non-negative integer used as input to factorial function

    Returns:
        int: Product of all non-negative integers less than or equal to 'argument'

    Raises:
        InputError: If 'argument' is not a non-negative integer
    """
    # Ensure that input is an integer.
    if int(argument) != argument:
        raise exceptions.InputError(
            argument, "Input to factorial must be a positive integer.")
    argument = int(argument)
    # Ensure that input is non-negative.
    if argument < 0:
        raise exceptions.InputError(
            argument, "Input to factorial must be a positive integer.")

    # Calculate factorial using repeated multiplication.
    result = 1
    for i in range(1, argument + 1):
        result *= i
    return result
Пример #2
0
def decimal_to_binary_fraction(value: float) -> str:
    """Returns a string that contains the binary equivalent of a decimal fraction.

    Args:
        value (float): Value in decimal
        
    Returns:
        str: Value in binary

    Raises:
        InputError: If input is not a valid positive number.
    """
    if common.is_negative(value):
        raise exceptions.InputError(value, "decimal_to_binary_fraction does not accept negative inputs.")
        
    bits = list()
    while value != 0 and (len(bits) <= 15):
        new_value = float(value * 2)
        number_to_store = int(new_value)
        value = new_value % 1
        bits.append(number_to_store)
    binary_string = "0."
    for x in bits:
        binary_string = binary_string + str(x)
    return binary_string
Пример #3
0
def decimal_to_binary_integer(value: int) -> str:
    """Returns a string that contains the binary equivalent of a decimal integer.

    Args:
        value (int): Value in decimal
        
    Returns:
        str: Value in binary

    Raises:
        InputError: If input is not a valid positive number.
    """
    if common.is_negative(value):
        raise exceptions.InputError(value, "decimal_to_binary_integer does not accept negative inputs.")
        
    bits = list()
    if value == 0:
        return ""
    while value != 0:
        number_to_store = value % 2
        value = value // 2
        bits.append(number_to_store)
    binary_string = ""
    for x in reversed(bits):
        binary_string = binary_string + str(x)
    return binary_string
Пример #4
0
def ln_taylor(argument: float, term_count: int = 50) -> float:
    """Returns an approximation of the value of the natural logarithm of some argument using the Maclaurin series expansion of ln(1-x).

    Maclaurin series used only converges when 'argument' is a real value between 0 and 2.

    Args:
        argument (float): Argument to natural logarithm being approximated
        term_count (int): Number of terms used in approximation of natural logarithm

    Returns:
        float: Approximation of natural logarithm of 'argument'

    Raises:
        InputError: If 'argument' is not within interval of convergence for Taylor series used.
    """
    # Ensure that series will converge for argument provided.
    if argument <= 0 or argument >= 2:
        raise exceptions.InputError(
            argument,
            "This expansion of natural logarithms only converges for arguments between 0 and 2."
        )

    # Perform calculation.
    x = 1 - argument
    result = 0
    numerator = 1
    for denominator in range(1, term_count):
        numerator *= x
        result -= numerator / denominator

    return result
Пример #5
0
def pow(base: float, exponent: float, root_used: int = int(1E10)) -> float:
    """Returns an approximation of some power with base 'base' and exponent 'exponent'.

    Function implementation does not accept powers with negative bases and real exponents.

    Args:
        base (float): Base of power being approximated
        exponent (float): Exponent of power being approximated
        root_used (float): Root used to approximate real part of exponent. Larger roots yield greater accuracy but risk overflow problems for large bases.

    Returns:
        float: Approximation of power with base 'base' and exponent 'exponent'

    Raises:
        InputError: If 'base' is negative and 'exponent' is not an integer.
    """
    # Get integer and decimal parts of exponent.
    integer_part_of_exponent = int(exponent)
    fractional_part_of_exponent = exponent - integer_part_of_exponent
    # Ensure that base is not negative if exponent is not a integer.
    if common.is_negative(base) and fractional_part_of_exponent != 0:
        raise exceptions.InputError(None,
                                    "Negative base with fractional exponent.")

    # Calculate integer part using exponentiation by squaring
    result = pow_int(float(base), integer_part_of_exponent)

    # If fractional part remains, approximate it using Newton's method for the denominator and exponentiation by
    # squaring for the numerator
    if fractional_part_of_exponent != 0:
        result *= pow_int(radical(float(base), root_used),
                          int(root_used * fractional_part_of_exponent))

    return result
Пример #6
0
def binary_to_decimal_fraction(value: str) -> float:
    """Returns a float that contains the decimal equivalent of a binary fraction.

    Args:
        value (str): Value in binary
        
    Returns:
        float: Value in decimal

    Raises:
        InputError: If input is not a valid positive binary number.
    """
    if value[0] == '-':
        raise exceptions.InputError(value, "binary_to_decimal_fraction does not accept negative inputs.")
    if not is_binary(value):
        raise exceptions.InputError(value, "Not in binary.")
        
    the_number = str(value)[2:]
    position = -1
    value = 0
    for char in the_number:
        value += int(char) * exponents.pow(2,position)
        position -= 1
    return value
Пример #7
0
def binary_to_decimal_integer(value: str) -> int:
    """Returns an integer that contains the decimal equivalent of a binary integer.

    Args:
        value (str): Value in binary
        
    Returns:
        int: Value in decimal

    Raises:
        InputError: If input is not a valid positive binary number.
    """
    if value[0] == '-':
        raise exceptions.InputError(value, "binary_to_decimal_integer does not accept negative inputs.")
    if not is_binary(value):
        raise exceptions.InputError(value, "Not in binary.")
        
    the_number = str(value)
    position = 0
    value = 0
    for char in reversed(the_number):
        value += int(char) * exponents.pow(2,position)
        position += 1
    return value
Пример #8
0
def log(argument: float, base: float = 10) -> float:
    """Returns an approximation of the value of some logarithm with base 'base' and argument 'argument'.

    Args:
        argument (float): Argument to logarithm being approximated
        base (float): Base of logarithm being approximated

    Returns:
        float: Approximation of value of logarithm of with base 'base' and argument 'argument'

    Raises:
        InputError: If 'argument' and/or 'base' are outside of domain of logarithm.
    """
    # Ensure that base is not equal to 1.
    if base == 1:
        raise exceptions.InputError(base,
                                    "Logarithm with base of 1 is undefined.")
    # Ensure that base is positive.
    if not common.is_positive(base):
        raise exceptions.InputError(
            base, "Logarithm with non-positive base is undefined.")

    # Calculate logarithm with arbitrary base using conversion of base property of logarithms.
    return ln(argument) / ln(base)
Пример #9
0
def inverse(argument: float) -> float:
    """Returns inverse of input.

    Args:
        argument (float): Non-zero input to inverse function

    Returns:
        float: Inverse of 'argument'.

    Raises:
        InputError: If 'argument' is equal to 0
    """
    # Ensure that input is not equal to 0.
    if argument == 0:
        raise exceptions.InputError(argument, "Inverse of 0 is undefined.")

    # Calculate inverse of argument.
    return 1 / argument
Пример #10
0
def is_odd(argument: int) -> bool:
    """Returns true is input is odd and false otherwise.

    Args:
        argument (int): Value to be checked

    Returns:
        bool: True if 'argument' is odd and false otherwise

    Raises:
        InputError: If 'argument' is not an integer
    """
    # Ensure that argument is an integer.
    if not isinstance(argument, int):
        raise exceptions.InputError(argument,
                                    "Parity of non-integers is undefined.")

    return argument & 1
Пример #11
0
def ln(argument: float) -> float:
    """Returns an approximation of the value of the natural logarithm of some argument.

    Args:
        argument (float): Argument to natural logarithm being approximated

    Returns:
        float: Approximation of natural logarithm of 'argument'

    Raises:
        InputError: If 'argument' is not within domain of natural logarithm (set of positive real numbers).
    """
    # Ensure that argument is within domain of natural logarithm.
    if not common.is_positive(argument):
        raise exceptions.InputError(argument, "Not in domain of log function.")

    # We use frexp to fetch mantissa and exponent of argument in a platform agnostic way.
    mantissa, exponent = math.frexp(argument)

    # It follows from the properties of logarithms that ln(m*2^p)=ln(m)+p*ln(2).
    ln_2 = 0.6931471805599453
    ln_mantissa = ln_taylor(mantissa)
    return ln_mantissa + exponent * ln_2
Пример #12
0
def pow_int(base: float, exponent: int) -> float:
    """Returns value of 'base' to the power of 'exponent' using exponentiation by squaring where the latter is an integer.

    Args:
        base (float): Base of power to be returned
        exponent (int): Exponent of power to be returned

    Returns:
        float: Value of 'base' to the power of 'exponent'

    Raises:
        InputError: If 'exponent' is not an integer
    """
    # Ensure that exponent is an integer.
    if not isinstance(exponent, int):
        raise exceptions.InputError(
            exponent,
            "Exponentiation by squaring does not work with non-integer exponents."
        )

    # If exponent is negative, use the property x^y = (1/x)^-y.
    if common.is_negative(exponent):
        base = common.inverse(base)
        exponent *= -1

    # Iterate over bits in exponent
    result = 1
    while common.is_positive(exponent):
        # If exponent is odd, we can use the fact that x^y = x(x^2)^((n-1)/2)
        if common.is_odd(exponent):
            result *= base
            # Normally, we'd subtract 1 from y here but we don't actually need to since it will be lost in the coming bit-shift.
        # Now that y is even, we can use the fact that x^y = (x^2)^(n/2) when y is even
        base *= base
        exponent >>= 1
    return result
Пример #13
0
def radical(radicand: float, index: int, delta: float = 1E-10) -> float:
    """Returns an approximation of the real value of some radical using Newton's method for finding roots of functions if such a value exists.

    Value returned is positive if a positive radical exists and negative otherwise. If no real root exists, an exception is raised.

    Args:
        radicand (float): Radicand of radical to be determined
        index (int): Index of radical to be determined
        delta (float): Error tolerance

    Returns:
        float: Approximation of radical defined by arguments

    Raises:
        InputError: If no real root exists for provided arguments
    """
    # Ensure that real value of root exists.
    if common.is_negative(radicand) and common.is_even(index):
        raise exceptions.InputError(None, "Not in domain of radical function.")

    # Approximate radicand using Newton's method.
    approx = 1.0
    old_mantissa, old_exp = math.frexp(approx)
    while True:
        # Incrementally improve approximation.
        power = pow_int(approx, index)
        approx = (index - 1 + radicand / power) * (approx / index)

        # Return when approximation stops improving
        crrt_mantissa, crrt_exp = math.frexp(approx)
        diff = abs(old_mantissa - crrt_mantissa)
        # We use 1E-15 here because a double precision floating point mantissa can hold no more than 52 bits of precision.
        if crrt_exp == old_exp and diff < 1E-15:
            return approx
        old_mantissa = crrt_mantissa
        old_exp = crrt_exp