#!/usr/bin/env python3

# Pyromaths
# Un programme en Python qui permet de créer des fiches d'exercices types de
# mathématiques niveau collège ainsi que leur corrigé en LaTeX.
# Copyright (C) 2006 -- Jérôme Ortais (jerome.ortais@pyromaths.org)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#

from random import randint, shuffle
from pyromaths.ex import LegacyExercise

from pyromaths.outils.Affichage import decimaux
from pyromaths.outils.Arithmetique import pgcd, factoriseTex, carrerise

premiers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
            67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139,
            149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
            227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
            307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
            389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
            467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
            571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647,
            653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
            751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
            853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
            947, 953, 967, 971, 977, 983, 991, 997]  # # Liste de longueur 168


def _Arithmetique():
    """Exercice de décomposition de nombres en facteurs premiers, puis de
    recherche du PGCD et du PPCM, et d'applications aux fractions"""

    # ## Question 1
    exo = ["\\exercice", '\\begin{enumerate}', u"\\item Donner la décomposition" +
           u" en facteurs premiers des nombres suivants, et préciser quand il" +
           u" s\'agit d\'un nombre premier :\\par"]
    cor = ["\\exercice*", '\\begin{enumerate}', u"\\item Donner la décomposition"
           + u" en facteurs premiers des nombres suivants, et préciser quand il"
           + u" s\'agit d\'un nombre premier :\\par"]

    prime = premiers[randint(10, 167)]

    fauxpgcd = randint(1, 101)
    fauxpgcdfactor = factoriseTex(fauxpgcd)[0]

    complementaires = [randint(6, 50), randint(6, 50)]
    pgcdcompl = pgcd(complementaires[0], complementaires[1])
    pgcdcomplfact = factoriseTex(pgcdcompl)[0]

    if pgcdcompl != 1:
        facteurs = pgcdcomplfact + fauxpgcdfactor
        facteurs.sort()
    else:
        facteurs = fauxpgcdfactor

    autresnombres = [randint(51, 999), randint(51, 999)]

    listenombres = [prime, complementaires[0] * fauxpgcd, complementaires[1] *
                    fauxpgcd, ] + autresnombres
    melange = [prime, complementaires[0] * fauxpgcd, complementaires[1] *
               fauxpgcd, ] + autresnombres
    shuffle(melange)

    exo.append(decimaux(melange[0]) + " ; " + decimaux(melange[1]) + " ; " +
               decimaux(melange[2]) + " ; " + decimaux(melange[3]) + " ; " +
               decimaux(melange[4]) + " ;")

    cor.append("\\begin{multicols}{2}")

    for i in range(5):
        cor += factoriseTex(melange[i])[1]

    cor.append("\\end{multicols}")

    # ## Question 2
    exo.append(u'\\item En déduire le PGCD et le PPCM des nombres ' +
               decimaux(listenombres[1]) + " et " + decimaux(listenombres[2]) +
               ".")
    cor.append(u'\\item En déduire le PGCD et le PPCM des nombres ' +
               decimaux(listenombres[1]) + " et " + decimaux(listenombres[2]) +
               ".\\par")

    cor.append(u"D'après la question 1), on sait que les nombres " +
               decimaux(listenombres[1]) + " et " + decimaux(listenombres[2]) +
               " ont comme facteurs premiers communs : ")

    for j in range(len(facteurs)):
        if j == 0:
            temp = "$"
        if j != len(facteurs) - 1:
            temp += decimaux(facteurs[j]) + " , "
        else:
            temp += decimaux(facteurs[j]) + "$.\\par"
    cor.append(temp)

    cor.append(u"On en déduit que le PGCD des nombres " +
               decimaux(listenombres[1]) + " et " + decimaux(listenombres[2]) +
               " est : ")
    temp = "$"
    if len(facteurs) > 1:
        for j in range(len(facteurs)):
            if j != len(facteurs) - 1:
                temp += decimaux(facteurs[j]) + " \\times "
            else:
                temp += decimaux(facteurs[j]) + " = "

    temp += decimaux(fauxpgcd * pgcdcompl) + ".$\\par"
    cor.append(temp)

    vraippcm = (listenombres[1] * listenombres[2]) // (fauxpgcd * pgcdcompl)

    if (listenombres[1] % listenombres[2] == 0):
        cor.append(decimaux(listenombres[1]) + u" est un multiple de " +
                   decimaux(listenombres[2]) + u", donc leur PPCM est directement "
                   + decimaux(listenombres[1]) + ".")
    elif (listenombres[2] % listenombres[1] == 0):
        cor.append(decimaux(listenombres[2]) + u" est un multiple de " +
                   decimaux(listenombres[1]) + u", donc leur PPCM est directement " +
                   decimaux(listenombres[2]) + ".")
    else:
        cor.append(u"Il existe plusieurs méthodes pour calculer le PPCM de " +
                   decimaux(listenombres[1]) + " et de " + decimaux(listenombres[2]) +
                   ".\\par")
        cor.append(u"En voici deux :")
        cor.append("\\begin{enumerate}")

        cor.append(u"\\item On peut simplement utiliser la formule :")
        cor.append(u"$a \\times b = PGCD(a;~b) \\times PPCM(a;~b)$.\\par")
        cor.append(u"Donc : $PPCM(" + decimaux(listenombres[1]) + ";~" +
                   decimaux(listenombres[2]) + ") = " + "\\dfrac{" +
                   decimaux(listenombres[1]) + "\\times" + decimaux(listenombres[2]) + "}{"
                   + decimaux(fauxpgcd * pgcdcompl) + "} = " + decimaux(vraippcm) +
                   ".$")

        cor.append(u"\\item On peut aussi multiplier un nombre par les \"facteurs "
                   + u"complémentaires\" de l'autre.\n" + u"Ces \"facteurs " +
                   u"complémentaires\" sont les facteurs qui complètent le PGCD pour " +
                   u"former le nombre.\\par")

        temp = u"Comme $PGCD(" + decimaux(listenombres[1]) + ";~" + \
               decimaux(listenombres[2]) + ") = " + decimaux(fauxpgcd * pgcdcompl)

        if len(facteurs) > 1:
            temp += " = "
            for j in range(len(facteurs)):
                if j != len(facteurs) - 1:
                    temp += decimaux(facteurs[j]) + " \\times "
                else:
                    temp += decimaux(facteurs[j])

        factornb1 = factoriseTex(listenombres[1])[0]

        if len(factornb1) > 1:
            textcompl = u"$, alors les \"facteurs complémentaires\" de $"
        else:
            textcompl = u"$, alors le \"facteur complémentaire\" de $"

        temp += textcompl + decimaux(listenombres[1]) + " = "

        for j in range(len(factornb1)):
            if j != len(factornb1) - 1:
                temp += decimaux(factornb1[j]) + " \\times "
            else:
                temp += decimaux(factornb1[j])

        factcompl = factoriseTex(listenombres[1] // (fauxpgcd * pgcdcompl))[0]

        if len(factcompl) == 1:
            temp += u"$ est : "
        else:
            temp += u"$ sont : "
        for j in range(len(factcompl)):
            if j != len(factcompl) - 1:
                temp += decimaux(factcompl[j]) + " , "
            else:
                temp += decimaux(factcompl[j]) + ".\n"
        temp += u"On en déduit que $PPCM(" + decimaux(listenombres[1]) + ";~" + \
                decimaux(listenombres[2]) + ") = " + decimaux(listenombres[2]) + \
                " \\times "

        for j in range(len(factcompl)):
            if j != len(factcompl) - 1:
                temp += decimaux(factcompl[j]) + " \\times "
            else:
                temp += decimaux(factcompl[j]) + " = "
        temp += decimaux(vraippcm) + ".$"
        cor.append(temp)
        cor.append("\\end{enumerate}")

    # ## Question 3

    exo.append(u"\\item Quel est le plus petit nombre par lequel il faut " +
               u"multiplier " + decimaux(autresnombres[0]) +
               u" pour obtenir un carré parfait ?")

    cor.append(u" \\item Pour obtenir un carré parfait, il faut que sa " +
               u"décomposition en facteurs premiers ne contienne que des facteurs "
               + u"apparaissant un nombre pair de fois. D'après la question 1, " +
               u"la décomposition en facteurs premiers de "
               + decimaux(autresnombres[0]))

    decompautre = factoriseTex(autresnombres[0])[1]

    if len(decompautre) == 1:
        cor.append(u" est lui-même, car c'est un nombre premier.")
    else:
        cor.append(" est : \\par\n$" + decimaux(autresnombres[0]) + " = " +
                   decompautre[-2][5:-2] + ".$\\par")

    cor.append(u"Il faut donc encore multiplier ce nombre par ")

    carre = carrerise(autresnombres[0])
    factsup = factoriseTex(carre)[0]

    if len(factsup) == 1:
        cor.append(" le facteur ")
    else:
        cor.append(" les facteurs ")

    for j in range(len(factsup)):
        if (j != len(factsup) - 1) and (j != len(factsup) - 2):
            cor.append(decimaux(factsup[j]) + " , ")
        elif (j == len(factsup) - 2):
            cor.append(decimaux(factsup[j]) + " et ")
        else:
            cor.append(decimaux(factsup[j]) + ".\\par")

    cor.append(u"Le nombre cherché est par conséquent " + decimaux(carre) +
               u" et le carré parfait obtenu est " + decimaux(carre *
                                                              autresnombres[0]) + ".")

    # ## Question 4
    exo.append(u"\\item Rendre la fraction $\\dfrac{" + decimaux(listenombres[1])
               + "}{" + decimaux(listenombres[2]) + u"}$ irréductible.")

    cor.append(u"\\item Le moyen le plus rapide de simplifier cette fraction est"
               + u"de diviser le numérateur et le dénominateur par leur PGCD." +
               u" D'après la question 2),  PGCD(" + decimaux(listenombres[1]) + ";~"
               + decimaux(listenombres[2]) + ") = "
               + decimaux(fauxpgcd * pgcdcompl) + ", donc on obtient :\\par")
    cor.append(u"$\dfrac{" + decimaux(listenombres[1]) + "{\\scriptstyle \\div " +
               decimaux(fauxpgcd * pgcdcompl) + "}}{" + decimaux(listenombres[2]) +
               "{\\scriptstyle \\div " + decimaux(fauxpgcd * pgcdcompl) +
               "}} = \dfrac{" + decimaux(listenombres[1] // (fauxpgcd * pgcdcompl)) +
               "}{" + decimaux(listenombres[2] // (fauxpgcd * pgcdcompl)) + "}.$")

    # ## Question 5

    num = [randint(6, 50), randint(6, 50)]
    exo.append(u"\\item Calculer $\\dfrac{" + decimaux(num[0]) + "}{" +
               decimaux(listenombres[1]) + "} + \\dfrac{" + decimaux(num[1]) + "}{" +
               decimaux(listenombres[2]) + "}$.")

    mult1 = vraippcm // listenombres[1]
    mult2 = vraippcm // listenombres[2]

    num1 = mult1 * num[0]
    num2 = mult2 * num[1]

    simplfin = pgcd(num1 + num2, vraippcm)

    if simplfin != 1:
        simpl = "{\\scriptstyle \\div " + decimaux(simplfin) + "}"
        result = " = \\dfrac{" + decimaux((num1 + num2) // simplfin) + "}{" + \
                 decimaux((vraippcm) // simplfin) + "}"
    else:
        simpl = ""
        result = ""

    cor.append(u"\\item Il faut mettre les fractions au même dénominateur. Grâce"
               + u"à la question 2), nous avons déjà un dénominateur commun : " +
               u"le PPCM des nombres " + decimaux(listenombres[1]) + " et " +
               decimaux(listenombres[2]) + u", qui est par définition le plus petit"
               + u"multiple commun de ces deux nombres.\\par")
    cor.append(u"$\\dfrac{" + decimaux(num[0]) + "{\\scriptstyle \\times " +
               decimaux(mult1) + "}}{" + decimaux(listenombres[1]) +
               "{\\scriptstyle \\times " + decimaux(mult1) + "}} + \\dfrac{" +
               decimaux(num[1]) + "{\\scriptstyle \\times " + decimaux(mult2) + "}}{"
               + decimaux(listenombres[2]) + "{\\scriptstyle \\times " +
               decimaux(mult2) + "}} = \\dfrac{" + decimaux(num1) + "}{" +
               decimaux(vraippcm) + "} + \\dfrac{" + decimaux(num2) + "}{" +
               decimaux(vraippcm) + "} = \\dfrac{" + decimaux(num1 + num2) + simpl +
               "}{" + decimaux(vraippcm) + simpl + "}" + result + ".$")

    exo.append("\\end{enumerate}")
    cor.append("\\end{enumerate}")

    return (exo, cor)

class Arithmetique(LegacyExercise):
    """Arithmétique"""

    tags = ["Troisième"]
    function = _Arithmetique
