

######################################## Correction hw 3


##################################Exercice 1 ###############################################
########################################################################################

from math import *

def for_decreasing_by_three(n): 
    ## we write n=3p + r with r=0,1,2
    p=floor(n/3)
    output=str(n)
    for i in range(1,p):
        output=output+", "+str(n-(i*3)) 
    output=output+ ", Finish."    
    print (output)
    
    
for_decreasing_by_three(10)    
for_decreasing_by_three(11)

for_decreasing_by_three(12)        

for_decreasing_by_three(3)    


def while_decreasing_by_three(n):
    value=n
    output=str(n)
    
    while (value>=3):
        value-=3
        output=output+", "+str(value)
    output=output+ ", Finish."
    print(output)
    
    
while_decreasing_by_three(20)
while_decreasing_by_three(21)
while_decreasing_by_three(22)


def subrec_decreasing_by_three(n):
    if (n<=2):
        return str(n)+", Finish."
    else:
        return str(n)+", "+subrec_decreasing_by_three(n-3)  
        
              
def rec_decreasing_by_three(n):
    print(subrec_decreasing_by_three(n))


rec_decreasing_by_three(40)
rec_decreasing_by_three(41)
rec_decreasing_by_three(42)


##################################################Exercice 2 ##############################


def for_factorial(n):
    if (n<0):
        print("Error")
        return -1
    elif (n==0):
        return 1
    else:
        value=1
        for i in range(1,n+1):
            value*=i
        return value
        
print(for_factorial(10))            


def multiplication_table(n): 
    for i in range(1,n+1):
        ## we print row i
        output=str(i)
        for j in range(2, n+1):
            output= output + ", " +str(i*j)
        print(output)    
                
multiplication_table(10)





################################### Exercice 3 #######################
## define the function f
def f(x):
    return exp(- x*x)
    
    
### question 4
def trapezoid(a,b,n):
    value=0
    if (n<0):
        print("Error")
        return -1    
    delta=(b-a)/n
    for i in range(0,n):
        xi=a + i *delta
        xip=xi+delta
        value+= delta* (f(xi)+f(xip))/2
    return value    

########question 5 to treat this question, we need the maximal value of the second derivative. 

def second_derivative(x):
    return (4 * x * x - 2)* f(x)

def maximum_second_derivative():
    return max(abs(second_derivative(0)), abs(second_derivative(sqrt(3/2))))


def n_epsilon(a,b, epsilon):
    if (epsilon<=0):
        print("Error")
        return -1
    M=maximum_second_derivative()
    return ceil(sqrt(pow(abs(b-a),3)*5/12*M /epsilon))
    
     
### question 6
def trapezoid_integral(a,b,epsilon):
    if (epsilon<=0):
        print("Error")
        return -1
    ## compute the value of N_epsilon
    N=n_epsilon(a,b,epsilon)
    ## return the integral approximation
    return trapezoid(a,b, N)





    
