'''
Numerical Computations and Control Flow**

1. Factorial Function:
   - Write a recursive function factorial(n) that computes the factorial of a non-negative integer n.
   - Include error handling to raise an exception if n  is negative or not an integer.

2. List of Primes:
   - Write a function generate_primes(m) that returns a list of all prime numbers less than or equal to m.

2. Numerical Integration
   - Write a function trapezoidal_rule(f, a, b, n) that computes the numerical integral of a function f over the interval
     [a, b] using the trapezoidal rule with n subintervals.

   - Use this print statement once you have implemented the trapezoidal rule
        print(f"n = {n}, Approximate Integral = {approx}, Error = {error}")
    
        where "approx" is the result from your function and "Error" is the difference between the analytical result
        and the approximated result.

    - Use func as the function to intergrate over.

        def func(x):
            return x**3 + x**2 + x + 5 

    
Submit Jypyter File in PDF format into Gradescope 
'''