''' 
De nombreuses fonction existent déjà
Python permet aussi d'appeller des fonction nouvelles
D'abord nous allons voire les appels au sytème, 
puis des fonctions comme print que nous utilisions toujours 
print(VARIABLE)
''' 

A=!date
# ici A est à un type "IPython.utils.text.SList" c'est une liste
print(A)

"""
Explanation: The Return of the Functions
1) Rappels sur les fonctions


Quand a t'on besoin d'une fonction ? 
dB or not dB ?
Définir ou Utiliser ?


1.1) Quand à t'on besoin d'ecrire une fonction ?
Pas toujours, mais presque...

Appel d'une fonction du système
Cet exemple marche bien avec les systèmes baseés sur unix, qui ont une horloge
End of explanation
"""


# On peux ici se servir le la methode "list" qui va 
date=A.list[0]
# On peux ici se servir le la methode "split" qui va separer la chaine de charactères
#(jour,mois,date,heure,fuseau,annee)=date.split() # Dans certains sytèmes
(jour,date,mois,annee,heure,fuseau)=date.split() # Dans d'autres sytèmes
# Ici je travaille en python 3.5
print('En l\'an de grâces %s les étudiants de l\'ISEP on étudié les Fonctions, c\'était le %s du mois \'%s\'.' % (annee, date, mois))

# Comme on voit c'est pas franchement lisible en Français, donc on va traduire...
# On peux utiliser les types que sont les tableaux associatifs appelés "dicts" 
#pour associer un mois a son abreviation 

mois_Month = {'janvier': 'de janvier', 
              'fevrier': 'de février', 
              'mars': 'de mars', 
              'avril': 'd\'avril', 
              'mai':'de mai',
              'juin':'de juin',
              'juillet':'de juillet',
              'août':'d\'août',
              'septembre':'de septembre',
              'octobre':'d\' octobre',
              'novembre':'de novembre',
              'décembre':'de décembre'}
print("En l\'an de grâces {0} les étudiants on étudié les Fonctions, c\'était le {1} du mois {2}." .format(annee, date, mois_Month[mois]))

"""
Explanation: Utilisation des methodes associés aux listes (les methodes sont des fonctions)
End of explanation
"""


# Exemple d'appel d'une fonction dans une librairie 
from math import log10
# Ici je déclare les variables
signal_power = 50 #Watts
noise_power = 5 #Watts
# Là j'utilise une division entre les variables déclareés
ratio = signal_power / noise_power
# Nous allons utiliser une fonction logarithme
decibels = 10 * log10(ratio)
# voila la sortie
print(decibels)

# Cette operation peux être transformée en fonction
def dB(signal_power,noise_power):
    ratio = signal_power / noise_power
    decibels = 10 * log10(ratio)
    return decibels

#Warning: au moment de l'appeler if faut que le log10 soit défini
from math import log10
signal_power = 50
noise_power = 5
dB(signal_power,noise_power)

"""
Explanation: 1.2) Quand n'a t'on pas besoin d'ecrire une fonction ?
Pas besoin de définir de nouvelles fonctions si celles-ci existent déja.
Python dispose d'un grand nombre de librairies de fonctions
mais il se peux que l'on n'ai pas besoin de toutes les fonctions 
de cette librairie tout au long du programme....
Donc on a TOUJOURS besoin de connaître les fonctions
End of explanation
"""


# On va déclarer une fonction qui renvoie la valeur de la puissance entière d'un nombre
# Cette solution utilise le fait que les nombres peuvent être pairs ou impairs
def expo(a, n):
    if n == 0:
        return 1
    else:
        if n%2 == 0:
            # on utilise la puissance de 2 directerment pour les nombres pairs 
            return expo(a, n//2)**2
            # on utilise pour les impairs, la même methode mais appliquée 
        else:
            return a*(expo(a, (n-1)//2)**2)

# Par exemple...


print("Cette fonction peut s'avèrer utile pour ecrire une suite de nombres bien connue :")
print(expo(2,0),expo(2,1),expo(2,2),expo(2,3),expo(2,4))
print('...')
print(expo(2,11),'\n')


# On peux invoquer une puissance directement grace à son return 
print('Entrez le Nombre a que l\'on veux élever à la puissance n')
a=eval(input('a = '))
n=eval(input('n = '))
print("La puissance au {} de {} est egale à {} ".format(n,a,expo(a,n)))


# OK... pour les fortiches en math il existe déjà une fonction puissance
x=pow(4,3)
print(x)

"""
Explanation: 1.1) Quand doit t'on impérativement utiliser une fonction ?
les fonctions recursives (qui s'appellent elles mêmes)
Voici les 3 lois de la recursivité:

Une fonction recursive  contiens un cas de base 
Une fonction recursive  doit modifier son état pour se ramenner au cas de base 
Une fonction recursive  doit s'appeler soit même 

Par exemple:
$$ x_0 = 1  \,  \mathrm{et}  \,  x^n=x.x^{n-1}  \,  \mathrm{si}  \,  n>1 $$
End of explanation
"""


def jetter_les_des_jusquau_double_six():
    """
    Cette fonction importe toutes les fonction de la librairie "random"
    Elle continue de tourner jusqu'au moment ou un double 6 est tiré
    Bonne chance !
    """
    import random
    throw_1 = random.randint(1, 6)
    throw_2 = random.randint(1, 6)
    while not (throw_1 == 6 and throw_2 == 6):
        total = throw_1 + throw_2
        print(total)
        throw_1 = random.randint(1, 6)
        throw_2 = random.randint(1, 6)
    print('12 !!! Double Six thrown!')

jetter_les_des_jusquau_double_six()

"""
Explanation: Il existe des fonction primitives propres a python 3
En anglais Build-In Functions
   ...

On ne doit jamais redéfinir une fonction qui est déja dans le sytème d'ou l'intérêt de savoir ce que contiens une fonction
   ...

Ce qui suit est un exemple de fonction "dangeureuse"
End of explanation
"""


"""
Programme : estPalindrome(unMot)                  
Objectif : test de Palindrome         

Entrée : unMot, STR contenant une chaine de charactères

Sortie : True or False (Booléen) 

@auteur : VF                  
"""

def estPalindrome(unMot):
    # Declaration des Variables locales
    estPal = True 
    l = len(unMot)
    d = 0
    # On compare les lettres symétriques par rapport au milieu du mot de longueur l
    # qui ont pour indices [d] et [l-1-d], le parcours s'effectue tant que les lettres
    # comparées sont égales et que le milieu n'est pas atteint 
    while estPal and d < l/2:
        if unMot[d] == unMot[l-1-d]: d += 1
        else: estPal = False
    return estPal
####################################################
# Test de la fonction
mot = input("Donner un mot : ") 
# Boucle conditionnelle
if estPalindrome(mot):
    print(mot, "est un palindrome")
else: 
    print(mot, "n'est pas un palindrome")


"""
Explanation: 2) Reprise des exemples de l'introduction en fonctions


Palindrome
La racine carrée
Anneés Bissextiles


Nous alons reprendre les exemples du premier cours mais cette fois nous transformerons tout en fonctions. L'objectif est de montrer la rapidité d'execution, et la modularité que la programmation à l'aide des fonctions est seulle à permettre.
2.1) Le Palindrome
End of explanation
"""


def RacineCarree(unNombre_Carre,unNombre_Estimateur):
    # Declaration des Variables locales
    EPS = 0.0000000000001 # précision
    val = unNombre_Carre
    racCarreeVal = unNombre_Estimateur
    
    while racCarreeVal * racCarreeVal - val < -EPS or \
          racCarreeVal * racCarreeVal - val > EPS:
        # la valeur absolue de la différence est supérieure à un seuil 
          racCarreeVal = (racCarreeVal + val / racCarreeVal) / 2
    return racCarreeVal
####################################################
# Test de la fonction
unNombre_Carre = 42
unNombre_Carre=float(unNombre_Carre)
unNombre_Estimateur = input("Première estimation de la racine de {} : ".format(unNombre_Carre))
unNombre_Estimateur=float(unNombre_Estimateur)
print(RacineCarree(unNombre_Carre,unNombre_Estimateur))

42**(1/2)

from math import sqrt
sqrt(42)

"""
Explanation: 2.2) Le test d'Héron d'Alexandrie
End of explanation
"""


def Annees_Bissextiles(annee_debut,annee_fin):
    bissextile=[]
    for b in range(annee_debut,annee_fin+1): 
                     if ((b % 4 == 0) and (b % 100 != 0)) or (b % 400 == 0):
                         bissextile.append(b)
    return bissextile
####################################################
annees_a_28_fevrier=Annees_Bissextiles(1992,2021)
print(annees_a_28_fevrier)

"""
Explanation: 2.3) Les annees bisextiles
End of explanation
"""


