Created on Thu Oct  5 16:44:23 2017 author: Christian Bender This Python library contains some useful functions to deal with prime numbers and whole numbers. Overview: isprimenumber sieveerN getprimenumbersN primefactorizationnumber greatestprimefactornumber smallestprimefactornumber getprimen getprimesbetweenpNumber1, pNumber2  isevennumber isoddnumber kgvnumber1, number2   least common multiple getdivisorsnumber     all divisors of 'number' inclusive 1, number isperfectnumbernumber NEWFUNCTIONS simplifyfractionnumerator, denominator factorial n  n! fib n  calculate the nth fibonacci term.  goldbachnumber   Goldbach's assumption input: positive integer 'number' returns true if 'number' is prime otherwise false.  isprime3 True  isprime10 False  isprime97 True  isprime9991 False  isprime1 Traceback most recent call last: ... AssertionError: 'number' must been an int and positive  isprimetest Traceback most recent call last: ... AssertionError: 'number' must been an int and positive precondition 0 and 1 are none primes. if 'number' divisible by 'divisor' then sets 'status' of false and break up the loop. precondition  input: positive integer 'N'  2 returns a list of prime numbers from 2 up to N. This function implements the algorithm called sieve of erathostenes.  sieveer8 2, 3, 5, 7  sieveer1 Traceback most recent call last: ... AssertionError: 'N' must been an int and  2  sieveertest Traceback most recent call last: ... AssertionError: 'N' must been an int and  2 precondition beginList: contains all natural numbers from 2 up to N actual sieve of erathostenes filters actual prime numbers. precondition  input: positive integer 'N'  2 returns a list of prime numbers from 2 up to N inclusive This function is more efficient as function 'sieveEr...'  getprimenumbers8 2, 3, 5, 7  getprimenumbers1 Traceback most recent call last: ... AssertionError: 'N' must been an int and  2  getprimenumberstest Traceback most recent call last: ... AssertionError: 'N' must been an int and  2 precondition iterates over all numbers between 2 up to N1 if a number is prime then appends to list 'ans' precondition  input: positive integer 'number' returns a list of the prime number factors of 'number'  primefactorization0 0  primefactorization8 2, 2, 2  primefactorization287 7, 41  primefactorization1 Traceback most recent call last: ... AssertionError: 'number' must been an int and  0  primefactorizationtest Traceback most recent call last: ... AssertionError: 'number' must been an int and  0 precondition potential prime number factors. if 'number' not prime then builds the prime factorization of 'number' precondition  input: positive integer 'number'  0 returns the greatest prime number factor of 'number'  greatestprimefactor0 0  greatestprimefactor8 2  greatestprimefactor287 41  greatestprimefactor1 Traceback most recent call last: ... AssertionError: 'number' must been an int and  0  greatestprimefactortest Traceback most recent call last: ... AssertionError: 'number' must been an int and  0 precondition prime factorization of 'number' precondition  input: integer 'number'  0 returns the smallest prime number factor of 'number'  smallestprimefactor0 0  smallestprimefactor8 2  smallestprimefactor287 7  smallestprimefactor1 Traceback most recent call last: ... AssertionError: 'number' must been an int and  0  smallestprimefactortest Traceback most recent call last: ... AssertionError: 'number' must been an int and  0 precondition prime factorization of 'number' precondition  input: integer 'number' returns true if 'number' is even, otherwise false.  iseven0 True  iseven8 True  iseven287 False  iseven1 False  iseventest Traceback most recent call last: ... AssertionError: 'number' must been an int precondition  input: integer 'number' returns true if 'number' is odd, otherwise false.  isodd0 False  isodd8 False  isodd287 True  isodd1 True  isoddtest Traceback most recent call last: ... AssertionError: 'number' must been an int precondition  Goldbach's assumption input: a even positive integer 'number'  2 returns a list of two prime numbers whose sum is equal to 'number'  goldbach8 3, 5  goldbach824 3, 821  goldbach0 Traceback most recent call last: ... AssertionError: 'number' must been an int, even and  2  goldbach1 Traceback most recent call last: ... AssertionError: 'number' must been an int, even and  2  goldbachtest Traceback most recent call last: ... AssertionError: 'number' must been an int, even and  2 precondition creates a list of prime numbers between 2 up to 'number' run variable for whileloops. exit variable. for break up the loops precondition  Least common multiple input: two positive integer 'number1' and 'number2' returns the least common multiple of 'number1' and 'number2'  kgv8,10 40  kgv824,67 55208  kgv1, 10 10  kgv0 Traceback most recent call last: ... TypeError: kgv missing 1 required positional argument: 'number2'  kgv10,1 Traceback most recent call last: ... AssertionError: 'number1' and 'number2' must been positive integer.  kgvtest,test2 Traceback most recent call last: ... AssertionError: 'number1' and 'number2' must been positive integer. precondition for kgV x,1 builds the prime factorization of 'number1' and 'number2' iterates through primeFac1 iterates through primeFac2 precondition  Gets the nth prime number. input: positive integer 'n'  0 returns the nth prime number, beginning at index 0  getprime0 2  getprime8 23  getprime824 6337  getprime1 Traceback most recent call last: ... AssertionError: 'number' must been a positive int  getprimetest Traceback most recent call last: ... AssertionError: 'number' must been a positive int precondition if ans not prime then runs to the next prime number. precondition  input: prime numbers 'pNumber1' and 'pNumber2' pNumber1  pNumber2 returns a list of all prime numbers between 'pNumber1' exclusive and 'pNumber2' exclusive  getprimesbetween3, 67 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61  getprimesbetween0 Traceback most recent call last: ... TypeError: getprimesbetween missing 1 required positional argument: 'pnumber2'  getprimesbetween0, 1 Traceback most recent call last: ... AssertionError: The arguments must been prime numbers and 'pNumber1'  'pNumber2'  getprimesbetween1, 3 Traceback most recent call last: ... AssertionError: 'number' must been an int and positive  getprimesbetweentest,test Traceback most recent call last: ... AssertionError: 'number' must been an int and positive precondition if number is not prime then fetch the next prime number. fetch the next prime number. precondition 'ans' contains not 'pNumber1' and 'pNumber2' !  input: positive integer 'n'  1 returns all divisors of n inclusive 1 and 'n'  getdivisors8 1, 2, 4, 8  getdivisors824 1, 2, 4, 8, 103, 206, 412, 824  getdivisors1 Traceback most recent call last: ... AssertionError: 'n' must been int and  1  getdivisorstest Traceback most recent call last: ... AssertionError: 'n' must been int and  1 precondition precondition  input: positive integer 'number'  1 returns true if 'number' is a perfect number otherwise false.  isperfectnumber28 True  isperfectnumber824 False  isperfectnumber1 Traceback most recent call last: ... AssertionError: 'number' must been an int and  1  isperfectnumbertest Traceback most recent call last: ... AssertionError: 'number' must been an int and  1 precondition precondition summed all divisors up to 'number' exclusive, hence :1  input: two integer 'numerator' and 'denominator' assumes: 'denominator' ! 0 returns: a tuple with simplify numerator and denominator.  simplifyfraction10, 20 1, 2  simplifyfraction10, 1 10, 1  simplifyfractiontest,test Traceback most recent call last: ... AssertionError: The arguments must been from type int and 'denominator' ! 0 precondition build the greatest common divisor of numerator and denominator. precondition  input: positive integer 'n' returns the factorial of 'n' n!  factorial0 1  factorial20 2432902008176640000  factorial1 Traceback most recent call last: ... AssertionError: 'n' must been a int and  0  factorialtest Traceback most recent call last: ... AssertionError: 'n' must been a int and  0 precondition  input: positive integer 'n' returns the nth fibonacci term , indexing by 0  fib0 1  fib5 8  fib20 10946  fib99 354224848179261915075  fib1 Traceback most recent call last: ... AssertionError: 'n' must been an int and  0  fibtest Traceback most recent call last: ... AssertionError: 'n' must been an int and  0 precondition
from math import sqrt

from maths.greatest_common_divisor import gcd_by_iterative


def is_prime(number: int) -> bool:
    """
    input: positive integer 'number'
    returns true if 'number' is prime otherwise false.

    >>> is_prime(3)
    True
    >>> is_prime(10)
    False
    >>> is_prime(97)
    True
    >>> is_prime(9991)
    False
    >>> is_prime(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and positive
    >>> is_prime("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and positive
    """

    # precondition
    assert isinstance(number, int) and (
        number >= 0
    ), "'number' must been an int and positive"

    status = True

    # 0 and 1 are none primes.
    if number <= 1:
        status = False

    for divisor in range(2, int(round(sqrt(number))) + 1):
        # if 'number' divisible by 'divisor' then sets 'status'
        # of false and break up the loop.
        if number % divisor == 0:
            status = False
            break

    # precondition
    assert isinstance(status, bool), "'status' must been from type bool"

    return status


# ------------------------------------------


def sieve_er(n):
    """
    input: positive integer 'N' > 2
    returns a list of prime numbers from 2 up to N.

    This function implements the algorithm called
    sieve of erathostenes.

    >>> sieve_er(8)
    [2, 3, 5, 7]
    >>> sieve_er(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'N' must been an int and > 2
    >>> sieve_er("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'N' must been an int and > 2
    """

    # precondition
    assert isinstance(n, int) and (n > 2), "'N' must been an int and > 2"

    # beginList: contains all natural numbers from 2 up to N
    begin_list = list(range(2, n + 1))

    ans = []  # this list will be returns.

    # actual sieve of erathostenes
    for i in range(len(begin_list)):
        for j in range(i + 1, len(begin_list)):
            if (begin_list[i] != 0) and (begin_list[j] % begin_list[i] == 0):
                begin_list[j] = 0

    # filters actual prime numbers.
    ans = [x for x in begin_list if x != 0]

    # precondition
    assert isinstance(ans, list), "'ans' must been from type list"

    return ans


# --------------------------------


def get_prime_numbers(n):
    """
    input: positive integer 'N' > 2
    returns a list of prime numbers from 2 up to N (inclusive)
    This function is more efficient as function 'sieveEr(...)'

    >>> get_prime_numbers(8)
    [2, 3, 5, 7]
    >>> get_prime_numbers(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'N' must been an int and > 2
    >>> get_prime_numbers("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'N' must been an int and > 2
    """

    # precondition
    assert isinstance(n, int) and (n > 2), "'N' must been an int and > 2"

    ans = []

    # iterates over all numbers between 2 up to N+1
    # if a number is prime then appends to list 'ans'
    for number in range(2, n + 1):
        if is_prime(number):
            ans.append(number)

    # precondition
    assert isinstance(ans, list), "'ans' must been from type list"

    return ans


# -----------------------------------------


def prime_factorization(number):
    """
    input: positive integer 'number'
    returns a list of the prime number factors of 'number'

    >>> prime_factorization(0)
    [0]
    >>> prime_factorization(8)
    [2, 2, 2]
    >>> prime_factorization(287)
    [7, 41]
    >>> prime_factorization(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    >>> prime_factorization("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    """

    # precondition
    assert isinstance(number, int) and number >= 0, "'number' must been an int and >= 0"

    ans = []  # this list will be returns of the function.

    # potential prime number factors.

    factor = 2

    quotient = number

    if number in {0, 1}:
        ans.append(number)

    # if 'number' not prime then builds the prime factorization of 'number'
    elif not is_prime(number):
        while quotient != 1:
            if is_prime(factor) and (quotient % factor == 0):
                ans.append(factor)
                quotient /= factor
            else:
                factor += 1

    else:
        ans.append(number)

    # precondition
    assert isinstance(ans, list), "'ans' must been from type list"

    return ans


# -----------------------------------------


def greatest_prime_factor(number):
    """
    input: positive integer 'number' >= 0
    returns the greatest prime number factor of 'number'

    >>> greatest_prime_factor(0)
    0
    >>> greatest_prime_factor(8)
    2
    >>> greatest_prime_factor(287)
    41
    >>> greatest_prime_factor(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    >>> greatest_prime_factor("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    """

    # precondition
    assert isinstance(number, int) and (
        number >= 0
    ), "'number' must been an int and >= 0"

    ans = 0

    # prime factorization of 'number'
    prime_factors = prime_factorization(number)

    ans = max(prime_factors)

    # precondition
    assert isinstance(ans, int), "'ans' must been from type int"

    return ans


# ----------------------------------------------


def smallest_prime_factor(number):
    """
    input: integer 'number' >= 0
    returns the smallest prime number factor of 'number'

    >>> smallest_prime_factor(0)
    0
    >>> smallest_prime_factor(8)
    2
    >>> smallest_prime_factor(287)
    7
    >>> smallest_prime_factor(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    >>> smallest_prime_factor("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 0
    """

    # precondition
    assert isinstance(number, int) and (
        number >= 0
    ), "'number' must been an int and >= 0"

    ans = 0

    # prime factorization of 'number'
    prime_factors = prime_factorization(number)

    ans = min(prime_factors)

    # precondition
    assert isinstance(ans, int), "'ans' must been from type int"

    return ans


# ----------------------


def is_even(number):
    """
    input: integer 'number'
    returns true if 'number' is even, otherwise false.

    >>> is_even(0)
    True
    >>> is_even(8)
    True
    >>> is_even(287)
    False
    >>> is_even(-1)
    False
    >>> is_even("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int
    """

    # precondition
    assert isinstance(number, int), "'number' must been an int"
    assert isinstance(number % 2 == 0, bool), "compare must been from type bool"

    return number % 2 == 0


# ------------------------


def is_odd(number):
    """
    input: integer 'number'
    returns true if 'number' is odd, otherwise false.

    >>> is_odd(0)
    False
    >>> is_odd(8)
    False
    >>> is_odd(287)
    True
    >>> is_odd(-1)
    True
    >>> is_odd("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int
    """

    # precondition
    assert isinstance(number, int), "'number' must been an int"
    assert isinstance(number % 2 != 0, bool), "compare must been from type bool"

    return number % 2 != 0


# ------------------------


def goldbach(number):
    """
    Goldbach's assumption
    input: a even positive integer 'number' > 2
    returns a list of two prime numbers whose sum is equal to 'number'

    >>> goldbach(8)
    [3, 5]
    >>> goldbach(824)
    [3, 821]
    >>> goldbach(0)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int, even and > 2
    >>> goldbach(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int, even and > 2
    >>> goldbach("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int, even and > 2
    """

    # precondition
    assert (
        isinstance(number, int) and (number > 2) and is_even(number)
    ), "'number' must been an int, even and > 2"

    ans = []  # this list will returned

    # creates a list of prime numbers between 2 up to 'number'
    prime_numbers = get_prime_numbers(number)
    len_pn = len(prime_numbers)

    # run variable for while-loops.
    i = 0
    j = None

    # exit variable. for break up the loops
    loop = True

    while i < len_pn and loop:
        j = i + 1

        while j < len_pn and loop:
            if prime_numbers[i] + prime_numbers[j] == number:
                loop = False
                ans.append(prime_numbers[i])
                ans.append(prime_numbers[j])

            j += 1

        i += 1

    # precondition
    assert (
        isinstance(ans, list)
        and (len(ans) == 2)
        and (ans[0] + ans[1] == number)
        and is_prime(ans[0])
        and is_prime(ans[1])
    ), "'ans' must contains two primes. And sum of elements must been eq 'number'"

    return ans


# ----------------------------------------------


def kg_v(number1, number2):
    """
    Least common multiple
    input: two positive integer 'number1' and 'number2'
    returns the least common multiple of 'number1' and 'number2'

    >>> kg_v(8,10)
    40
    >>> kg_v(824,67)
    55208
    >>> kg_v(1, 10)
    10
    >>> kg_v(0)
    Traceback (most recent call last):
        ...
    TypeError: kg_v() missing 1 required positional argument: 'number2'
    >>> kg_v(10,-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number1' and 'number2' must been positive integer.
    >>> kg_v("test","test2")
    Traceback (most recent call last):
        ...
    AssertionError: 'number1' and 'number2' must been positive integer.
    """

    # precondition
    assert (
        isinstance(number1, int)
        and isinstance(number2, int)
        and (number1 >= 1)
        and (number2 >= 1)
    ), "'number1' and 'number2' must been positive integer."

    ans = 1  # actual answer that will be return.

    # for kgV (x,1)
    if number1 > 1 and number2 > 1:
        # builds the prime factorization of 'number1' and 'number2'
        prime_fac_1 = prime_factorization(number1)
        prime_fac_2 = prime_factorization(number2)

    elif number1 == 1 or number2 == 1:
        prime_fac_1 = []
        prime_fac_2 = []
        ans = max(number1, number2)

    count1 = 0
    count2 = 0

    done = []  # captured numbers int both 'primeFac1' and 'primeFac2'

    # iterates through primeFac1
    for n in prime_fac_1:
        if n not in done:
            if n in prime_fac_2:
                count1 = prime_fac_1.count(n)
                count2 = prime_fac_2.count(n)

                for _ in range(max(count1, count2)):
                    ans *= n

            else:
                count1 = prime_fac_1.count(n)

                for _ in range(count1):
                    ans *= n

            done.append(n)

    # iterates through primeFac2
    for n in prime_fac_2:
        if n not in done:
            count2 = prime_fac_2.count(n)

            for _ in range(count2):
                ans *= n

            done.append(n)

    # precondition
    assert isinstance(ans, int) and (
        ans >= 0
    ), "'ans' must been from type int and positive"

    return ans


# ----------------------------------


def get_prime(n):
    """
    Gets the n-th prime number.
    input: positive integer 'n' >= 0
    returns the n-th prime number, beginning at index 0

    >>> get_prime(0)
    2
    >>> get_prime(8)
    23
    >>> get_prime(824)
    6337
    >>> get_prime(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been a positive int
    >>> get_prime("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been a positive int
    """

    # precondition
    assert isinstance(n, int) and (n >= 0), "'number' must been a positive int"

    index = 0
    ans = 2  # this variable holds the answer

    while index < n:
        index += 1

        ans += 1  # counts to the next number

        # if ans not prime then
        # runs to the next prime number.
        while not is_prime(ans):
            ans += 1

    # precondition
    assert isinstance(ans, int) and is_prime(
        ans
    ), "'ans' must been a prime number and from type int"

    return ans


# ---------------------------------------------------


def get_primes_between(p_number_1, p_number_2):
    """
    input: prime numbers 'pNumber1' and 'pNumber2'
            pNumber1 < pNumber2
    returns a list of all prime numbers between 'pNumber1' (exclusive)
            and 'pNumber2' (exclusive)

    >>> get_primes_between(3, 67)
    [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61]
    >>> get_primes_between(0)
    Traceback (most recent call last):
        ...
    TypeError: get_primes_between() missing 1 required positional argument: 'p_number_2'
    >>> get_primes_between(0, 1)
    Traceback (most recent call last):
        ...
    AssertionError: The arguments must been prime numbers and 'pNumber1' < 'pNumber2'
    >>> get_primes_between(-1, 3)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and positive
    >>> get_primes_between("test","test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and positive
    """

    # precondition
    assert (
        is_prime(p_number_1) and is_prime(p_number_2) and (p_number_1 < p_number_2)
    ), "The arguments must been prime numbers and 'pNumber1' < 'pNumber2'"

    number = p_number_1 + 1  # jump to the next number

    ans = []  # this list will be returns.

    # if number is not prime then
    # fetch the next prime number.
    while not is_prime(number):
        number += 1

    while number < p_number_2:
        ans.append(number)

        number += 1

        # fetch the next prime number.
        while not is_prime(number):
            number += 1

    # precondition
    assert (
        isinstance(ans, list)
        and ans[0] != p_number_1
        and ans[len(ans) - 1] != p_number_2
    ), "'ans' must been a list without the arguments"

    # 'ans' contains not 'pNumber1' and 'pNumber2' !
    return ans


# ----------------------------------------------------


def get_divisors(n):
    """
    input: positive integer 'n' >= 1
    returns all divisors of n (inclusive 1 and 'n')

    >>> get_divisors(8)
    [1, 2, 4, 8]
    >>> get_divisors(824)
    [1, 2, 4, 8, 103, 206, 412, 824]
    >>> get_divisors(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'n' must been int and >= 1
    >>> get_divisors("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'n' must been int and >= 1
    """

    # precondition
    assert isinstance(n, int) and (n >= 1), "'n' must been int and >= 1"

    ans = []  # will be returned.

    for divisor in range(1, n + 1):
        if n % divisor == 0:
            ans.append(divisor)

    # precondition
    assert ans[0] == 1 and ans[len(ans) - 1] == n, "Error in function getDivisiors(...)"

    return ans


# ----------------------------------------------------


def is_perfect_number(number):
    """
    input: positive integer 'number' > 1
    returns true if 'number' is a perfect number otherwise false.

    >>> is_perfect_number(28)
    True
    >>> is_perfect_number(824)
    False
    >>> is_perfect_number(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 1
    >>> is_perfect_number("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'number' must been an int and >= 1
    """

    # precondition
    assert isinstance(number, int) and (
        number > 1
    ), "'number' must been an int and >= 1"

    divisors = get_divisors(number)

    # precondition
    assert (
        isinstance(divisors, list)
        and (divisors[0] == 1)
        and (divisors[len(divisors) - 1] == number)
    ), "Error in help-function getDivisiors(...)"

    # summed all divisors up to 'number' (exclusive), hence [:-1]
    return sum(divisors[:-1]) == number


# ------------------------------------------------------------


def simplify_fraction(numerator, denominator):
    """
    input: two integer 'numerator' and 'denominator'
    assumes: 'denominator' != 0
    returns: a tuple with simplify numerator and denominator.

    >>> simplify_fraction(10, 20)
    (1, 2)
    >>> simplify_fraction(10, -1)
    (10, -1)
    >>> simplify_fraction("test","test")
    Traceback (most recent call last):
        ...
    AssertionError: The arguments must been from type int and 'denominator' != 0
    """

    # precondition
    assert (
        isinstance(numerator, int)
        and isinstance(denominator, int)
        and (denominator != 0)
    ), "The arguments must been from type int and 'denominator' != 0"

    # build the greatest common divisor of numerator and denominator.
    gcd_of_fraction = gcd_by_iterative(abs(numerator), abs(denominator))

    # precondition
    assert (
        isinstance(gcd_of_fraction, int)
        and (numerator % gcd_of_fraction == 0)
        and (denominator % gcd_of_fraction == 0)
    ), "Error in function gcd_by_iterative(...,...)"

    return (numerator // gcd_of_fraction, denominator // gcd_of_fraction)


# -----------------------------------------------------------------


def factorial(n):
    """
    input: positive integer 'n'
    returns the factorial of 'n' (n!)

    >>> factorial(0)
    1
    >>> factorial(20)
    2432902008176640000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    AssertionError: 'n' must been a int and >= 0
    >>> factorial("test")
    Traceback (most recent call last):
        ...
    AssertionError: 'n' must been a int and >= 0
    """

    # precondition
    assert isinstance(n, int) and (n >= 0), "'n' must been a int and >= 0"

    ans = 1  # this will be return.

    for factor in range(1, n + 1):
        ans *= factor

    return ans


# -------------------------------------------------------------------


def fib(n: int) -> int:
    """
    input: positive integer 'n'
    returns the n-th fibonacci term , indexing by 0

    >>> fib(0)
    1
    >>> fib(5)
    8
    >>> fib(20)
    10946
    >>> fib(99)
    354224848179261915075
    >>> fib(-1)
    Traceback (most recent call last):
    ...
    AssertionError: 'n' must been an int and >= 0
    >>> fib("test")
    Traceback (most recent call last):
    ...
    AssertionError: 'n' must been an int and >= 0
    """

    # precondition
    assert isinstance(n, int) and (n >= 0), "'n' must been an int and >= 0"

    tmp = 0
    fib1 = 1
    ans = 1  # this will be return

    for _ in range(n - 1):
        tmp = ans
        ans += fib1
        fib1 = tmp

    return ans


if __name__ == "__main__":
    import doctest

    doctest.testmod()
