#!/usr/bin/env python3
import numpy as np
import timeit
import sys


sys.setrecursionlimit(10**6)


def fact_rec(n):
    # y = fact_rec(n) berechnet die Fakultät von n als
    # fact_rec(n) = n * fact_rec(n - 1) mit fact_rec(0) = 1
    # Fehler, falls n <0 oder nicht ganzzahlig
    if n < 0 or np.trunc(n) != n:
        raise Exception('The factorial is defined only for positive integers')
    if n <= 1:
        return 1
    else:
        return n*fact_rec(n-1)


def fact_for(n):
    # y = fact_rec(n) berechnet die Fakultät von n als
    # fact_rec(n) = n * fact_rec(n - 1) mit fact_rec(0) = 1
    # Fehler, falls n <0 oder nicht ganzzahlig
    if n < 0 or np.trunc(n) != n:
        raise Exception('The factorial is defined only for positive integers')
    if n <= 1:
        return 1
    else:
        fak = 1
        for num in range(n):
            fak *= n
            n -= 1
    return fak


if __name__ == "__main__":
    t1 = timeit.repeat(
        "fact_rec(500)",
        "from __main__ import fact_rec",
        number=100)

    t2 = timeit.repeat(
        "fact_for(500)",
        "from __main__ import fact_for",
        number=100)

    print("fact_for ist um den Faktor: %s schneller als fact_rec" % str(
        np.average(t1) / np.average(t2)))

    # FRAGE: Welche der beiden Funktionen ist schneller und um was für einen Faktor?
    # fact_for ist um den Faktor 8 schneller als fact_rec bei n = 500

    # FRAGE: Weshalb?
    # Rekursion an sich braucht mehr ressourcen in Python(Java, C),
    # weil man einen Stack benötigt.
    # Das heisst, dass neben den Berechnungen, gibt es noch
    # Stack operationen bei der Rekursion, was wiederum erklärt warum
    # es einbisschen langsamer ist

    # FRAGE: Gibt es in Python eine obere Grenze für die Fakultät von n
    # FRAGE: als ganze Zahl (integer)?
    # Aus stackoverflow:
    # "In Python 3, this question doesn't apply. The plain int type is unbounded."
    # Das bedeutet eine Grenze gibt es nicht, aber je nach Zahl kann es
    # viel länger brauchen

    # FRAGE: als reelle Zahl (float)?
    # Ja, es gibt eine Grenze die liegt bei sys.float_info.max