Skip to content

4. Resolução dos três problemas com o Google Gemini

Vamos apresentar as capturas de ecrã das três sessões do Gemini que permitiram resolver os três problemas colocados. Entraremos em pormenores. Feito isto, não repetiremos o processo para os outros IA testados. Funcionam de forma semelhante. Apresentaremos apenas os pormenores mais relevantes.

4.1. Introdução

Recordamos a primeira captura de ecrã do Gemini apresentada anteriormente:

 
  • No [1], o URL do Gemini;
  • Em [2], a versão do Gemini utilizada;
  • Em [3-5], os três problemas colocados ao Gemini;

O Gemini é um produto da Google disponível no URL [https://gemini.google.com/]. Para ter um histórico das suas sessões de perguntas e respostas, como o apresentado acima, é necessário criar uma conta. Além disso, tal como todas as outras IA testadas, o Gemini limita o número de perguntas e o número de ficheiros descarregados. Quando este limite é atingido, a sessão é encerrada e é-lhe sugerido que a continue mais tarde. Como é bastante frustrante ter de parar a meio de uma sessão, subscrevi uma assinatura. Felizmente, o primeiro mês da subscrição do Gemini é gratuito. Fiz o mesmo com os outros IA que apresentavam estas limitações, nomeadamente o ChatGPT, o MistralAI e o ClaudeAI. Fiz uma subscrição de um mês, mas, neste caso, o primeiro mês era pago. Não encontrei quaisquer limites com o Grok. O DeepSeek não anuncia limites, mas, por vezes, responde [Server busy] e interrompe a sessão. É o mesmo que impor limites sem o dizer.

Daqui em diante, referir-me-ei à sessão de perguntas e respostas, abreviada simplesmente como «sessão». Os IA utilizam, na maioria das vezes, o termo inglês «chat» (conversa) ou «conversa».

A interface do Gemini para fazer uma pergunta é a seguinte:

  • Em [1], a sua pergunta;
  • Em [2], o ícone que inicia o IA para o cálculo da resposta;
  • Em [3-4], pode anexar ficheiros;

4.2. Problema 1

A sessão para o problema 1 é a seguinte:

 
  • No [1], a pergunta;
  • No [2], o início da resposta do Gemini;

A continuação da resposta é a seguinte:

 
 
 
 

A resposta está correta. Os outros cinco IA também darão a resposta correta de forma semelhante.

4.3. Problema 2

4.3.1. Introdução

Recordamos aqui o problema inicial da aula [python3-flask-2020]. Trata-se de um texto fornecido aos alunos em TD.

 

A tabela acima permite calcular o imposto no caso simplificado de um contribuinte que tenha apenas o seu salário para declarar. Tal como indicado na nota (1), o imposto assim calculado é o imposto antes de três mecanismos:

  • O limite máximo do quociente familiar, que se aplica aos rendimentos elevados;
  • A dedução e a redução de impostos que se aplicam aos rendimentos baixos;

Assim, o cálculo do imposto inclui as seguintes etapas [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

Propõe-se escrever um programa que permita calcular o imposto de um contribuinte em 2019, no caso simplificado de um contribuinte que tenha apenas o seu salário para declarar.

4.3.1.1. Cálculo do imposto bruto

O imposto bruto pode ser calculado da seguinte forma:

Primeiro, calcula-se o número de quotas do contribuinte:

  • Cada progenitor contribui com 1 quota;
  • Os dois primeiros filhos contribuem com 1/2 parte cada um;
  • Os filhos seguintes contribuem com uma parte cada um:

O número de quotas é, portanto:

  • nbParts=1+nbEnfants*0,5+(n.º de filhos-2)*0,5 se o trabalhador não for casado;
  • nbParts = 2 + nbEnfants * 0,5 + (nbEnfants - 2) * 0,5 se for casado;
  • onde nbEnfants é o número de filhos;
  • Calcula-se o rendimento tributável R = 0,9 * S, em que S é o salário anual;
  • Calcula-se o quociente familiar QF = R / nbParts;
  • Calcula-se o imposto bruto I com base nos seguintes dados (2019):
9964
0
0
27519
0,14
1394,96
73779
0,3
5798
156 244
0,4
13 913,69
0
0,45
20163,45

Cada linha tem 3 campos: champ1, champ2, champ3. Para calcular o imposto I, procura-se a primeira linha em que QF <= campo1 e utilizam-se os valores dessa linha. Por exemplo, para um trabalhador casado com dois filhos e um salário anual S de 50 000 euros:

Rendimento tributável: R = 0,9 * S = 45 000

Número de quotas: nbParts = 2 + 2 * 0,5 = 3

Quociente familiar: QF = 45 000 / 3 = 15 000

A primeira linha em que QF <= campo1 é a seguinte:

    27519    0.14    1394.96

O imposto I é, então, igual a 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*3] = 2115. O imposto é arredondado para o euro inferior.

Se a relação QF <= campo1 for verdadeira desde a 1.ª linha, então o imposto é nulo.

Se QF for tal que a relação QF <= campo1 nunca for verificada, então são utilizados os coeficientes da última linha. Neste caso:

    0    0.45    20163.45

o que dá o imposto bruto I = 0,45*R – 20 163,45*nbParts.

4.3.1.2. Limite máximo do quociente familiar

 

Para saber se o limite máximo do quociente familiar QF se aplica, refaz-se o cálculo do imposto bruto sem os filhos. Continuando com o exemplo do trabalhador casado com dois filhos e um salário anual S de 50 000 euros:

Rendimento tributável: R = 0,9 * S = 45 000

Número de quotas: nbParts=2 (já não se contabilizam os filhos)

Quociente familiar: QF = 45 000 / 2 = 22 500

A primeira linha em que QF <= campo1 é a seguinte:

    27519    0.14    1394.96

O imposto I é, então, igual a 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.

Ganho máximo relacionado com os filhos: 1551 * 2 = 3102 euros

Imposto mínimo: 3510 – 3102 = 408 euros

O imposto bruto com 2 quotas, já calculado no parágrafo anterior (2115 euros), é superior ao imposto mínimo (408 euros), pelo que o limite máximo familiar não se aplica neste caso.

De um modo geral, o imposto bruto é maior que (imposto1, imposto2), em que:

  • [impôt1]: é o imposto bruto calculado com os filhos;
  • [impôt2]: é o imposto bruto calculado sem os filhos e deduzido do benefício máximo (neste caso, 1551 euros por meia quota) relacionado com os filhos;

4.3.1.3. Cálculo da redução

 

Continuando com o exemplo do trabalhador casado, com dois filhos e um salário anual S de 50 000 euros:

O imposto bruto (2115 euros) resultante da etapa anterior é inferior a 2627 euros para um casal (1595 euros para um solteiro): aplica-se, portanto, a redução. Esta é obtida através do seguinte cálculo:

desconto = limiar (casal = 1 970 / solteiro = 1 196) – 0,75 * Imposto bruto

redução = 1970 – 0,75 * 2115 = 383,75, arredondado para 384 euros.

Novo imposto bruto = 2115 – 384 = 1731 euros

É necessário observar duas regras no cálculo da dedução (algumas ferramentas de IA tiveram dificuldades com esta questão):

  • A dedução não pode ser negativa;
  • A dedução não pode ser superior ao imposto já calculado;

4.3.1.4. Cálculo da redução de impostos

 

Abaixo de um determinado limiar, é aplicada uma redução de 20 % sobre o imposto bruto resultante dos cálculos anteriores. Em 2019, os limiares são os seguintes:

  • solteiro: 21 037 euros;
  • casal: 42 074 euros; (o valor de 37 968 utilizado no exemplo acima parece estar errado);

Este limiar é aumentado pelo valor: 3 797 * (número de meias-partes atribuídas pelos filhos).

Continuando com o exemplo do trabalhador casado com dois filhos e um salário anual S de 50 000 euros:

  • O seu rendimento tributável (45 000 euros) é inferior ao limiar (42 074 + 2 * 3 797) = 49 668 euros;
  • Tem, portanto, direito a uma redução de 20 % do seu imposto: 1731 * 0,2 = 346,2 euros, arredondado para 347 euros;
  • O imposto bruto do contribuinte passa a ser: 1 731 – 347 = 1 384 euros;

4.3.1.5. Cálculo do imposto líquido

O nosso cálculo termina aqui: o imposto líquido a pagar será de 1384 euros. Na realidade, o contribuinte pode beneficiar de outras deduções, nomeadamente por doações a organismos de interesse público ou geral.

4.3.1.6. Caso dos rendimentos elevados

O nosso exemplo anterior corresponde à maioria dos casos de assalariados. No entanto, o cálculo do imposto é diferente no caso dos rendimentos elevados.

4.3.1.6.1. Limite máximo da redução de 10 % sobre os rendimentos anuais

Na maioria dos casos, o rendimento tributável é obtido através da fórmula: R = 0,9 * S, em que S é o salário anual. A isto chama-se a redução de 10 %. Esta redução está sujeita a um limite máximo. Em 2019:

  • Não pode ser superior a 12 502 euros;
  • Não pode ser inferior a 437 euros;

Consideremos o caso de um trabalhador solteiro, sem filhos, com um salário anual de 200 000 euros:

  • A redução de 10 % é de 20 000 euros > 12 502 euros. É, portanto, reduzida para 12 502 euros;

4.3.1.6.2. Limite máximo do quociente familiar

Consideremos um caso em que se aplica o limite máximo familiar apresentado no parágrafo |Limite máximo do quociente familiar|. Tomemos o caso de um casal com três filhos e rendimentos anuais de 100 000 euros. Repassemos as etapas do cálculo:

  • A dedução de 10 % é de 100 000 euros < 12 502 euros. O rendimento tributável R é, portanto, 100 000 - 10 000 = 90 000 euros;
  • O casal tem nbParts = 2 + 0,5 × 2 + 1 = 4 quotas;
  • O seu quociente familiar é, portanto, QF = R / nbParts = 90 000 / 4 = 22 500 euros;
  • O seu imposto bruto I1 com filhos é I1 = 0,14 × 90 000 − 1 394,96 × 4 = 7 020 euros;
  • O seu imposto bruto I2 sem filhos:
    • QF = 90 000 / 2 = 45 000 euros;
    • I2 = 0,3 × 90 000 – 5 798 × 2 = 15 404 euros;
  • A regra do limite máximo do quociente familiar estipula que o benefício proporcionado pelos filhos não pode exceder (1551 × 4 meias-partes) = 6204 euros. No entanto, neste caso, é I2-I1 = 15404 – 7020 = 8384 euros, ou seja, superior a 6204 euros;
  • O imposto bruto é, portanto, recalculado como I3 = I2-6204 = 15 404 – 6 204 = 9 200 euros;
  • Como I3 > I1, será o imposto I3 que será mantido;

Este casal não terá qualquer abatimento nem redução e o seu imposto final será de 9200 euros.

4.3.1.7. Números oficiais

O cálculo do imposto é complexo. Ao longo do documento, os cálculos serão efetuados com os seguintes exemplos. Os resultados são os do simulador da administração fiscal |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

Contribuinte
Resultados oficiais
Resultados do algoritmo do documento
Casal com 2 filhos e rendimentos anuais de 55 555 euros
Imposto = 2815 euros
Taxa de imposto = 14 %
Imposto = 2814 euros
Taxa de imposto = 14 %
Casal com 2 filhos e rendimentos anuais de 50 000 euros
Imposto = 1385 euros
Abatimento = 384 euros
Redução = 346 euros
Taxa de imposto = 14 %
Imposto = 1 384 euros
Abatimento = 384 euros
Redução = 347 euros
Taxa de imposto = 14 %
Casal com 3 filhos e rendimentos anuais de 50 000 euros
Imposto = 0 euros
Desconto = 720 euros
Redução = 0 euros
Taxa de imposto = 14 %
Imposto = 0 euros
desconto = 720 euros
Redução = 0 euros
Taxa de imposto = 14 %
Solteiro, com 2 filhos e rendimentos anuais de 100 000 euros
Imposto = 19 884 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Imposto = 19 884 euros
Sobretaxa = 4 480 euros
desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Solteiro, com 3 filhos e rendimentos anuais de 100 000 euros
Imposto = 16 782 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Imposto = 16 782 euros
Sobretaxa = 7 176 euros
desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Casal com 3 filhos e rendimentos anuais de 100 000 euros
Imposto = 9 200 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 30 %
Imposto = 9 200 euros
Sobretaxa = 2 180 euros
desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 30 %
Casal com 5 filhos e rendimentos anuais de 100 000 euros
Imposto = 4 230 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 14 %
Imposto = 4 230 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 14 %
Solteiro, sem filhos e com rendimentos anuais de 100 000 euros
Imposto = 22 986 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Imposto = 22 986 euros
Sobretaxa = 0 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Casal com 2 filhos e rendimentos anuais de 30 000 euros
Imposto = 0 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 0 %
Imposto = 0 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 0 %
Solteiro, sem filhos e com rendimentos anuais de 200 000 euros
Imposto = 64 211 euros
Abatimento = 0 euros
Redução = 0 euros
Taxa de imposto = 45 %
Imposto = 64 210 euros
Sobretaxa = 7 498 euros
desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 45 %
Casal com 3 filhos e rendimentos anuais de 200 000 euros
Imposto = 42 843 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %
Imposto = 42 842 euros
Sobrevalorização = 17 283 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41 %

No exemplo acima, denomina-se «sobretaxa» o montante adicional que os rendimentos elevados pagam devido a dois fenómenos:

  • O limite máximo de 10 % na dedução sobre os rendimentos anuais;
  • O limite máximo do quociente familiar;

Este indicador não pôde ser verificado, uma vez que o simulador da administração fiscal não o fornece.

Verifica-se que o algoritmo do documento apresenta sempre um imposto correto, embora com uma margem de erro de 1 euro. Esta margem de erro resulta dos arredondamentos. Todos os montantes são arredondados, por vezes para o euro superior, por vezes para o euro inferior. Como eu não conhecia as regras oficiais, os montantes do algoritmo do documento foram arredondados:

  • Para o euro superior, no caso dos descontos e reduções;
  • Para o euro inferior, no caso das sobretaxas e do imposto final;

Vamos pedir ao IA para efetuar este cálculo do imposto.

4.3.2. Configuração da sessão Gemini

A questão colocada ao Gemini é acompanhada por dois ficheiros:

 
  • No [1], o cálculo que acabámos de detalhar foi inserido num PDF que é fornecido ao Gemini. O Gemini irá encontrar aí as regras exatas para o cálculo simplificado do imposto de 2019 sobre os rendimentos de 2018;
  • No [2], as nossas instruções;
  • No ficheiro [3], para executar o ficheiro IA;

As nossas instruções no ficheiro de texto [instructionsAvecPDF.txt] são as seguintes:

1 - Exprime-toi en français.

2 - Peux-tu générer un script Python permettant de calculer l'impôt payé par les familles en 2019 sur leurs revenus de 2018.

3 - Tu t'aideras du document PDF que j'ai joint et qui explique les calculs à faire.

4 - Tu dois faire attention aux points suivants :

- plafonnement du quotient familial. Il y a des seuils à vérifier.
- calcul de la décote dans certains cas. Il y a des seuils à vérifier.
- calcul de la réduction de 20% dans certains cas. Il y a des seuils à vérifier.
- plafonnement de l'abattement de 10% sur les revenus annuels dans certains cas.
- tu considèreras que tous les revenus sont à déclarer pour le déclarant 1 même si le couple est marié.

5 - Tu ajouteras au script généré des tests unitaires pour les cas suivants.

Dans ces tests on appelle :

adultes :nombre d'adultes du foyer fiscal
enfants : nombre d'enfants du foyer fiscal
revenus : revenus nets annuels avant impôt, ç-à-d avant le 1er calcul de l'abattement.
impot : l'impôt à payer
decote :la décote éventuelle du foyer fiscal
reduction : la réduction de 20% pour les faibles revenus

Voici les 11 tests à vérifier. Ils ont tous été vérifiés manuellement sur le simulateur officiel
du calcul de l'impôt 2019 [https://www3.impots.gouv.fr/simulador/calcul_impot/2019/simplifica/index.html].
Si tu utilises ce simulateur les revenus doivent être associés au seul déclarant 1 dans le cas d'un couple, le déclarant 2 étant alors ignoré. Lorsqu'on répartit les revenus
sur deux déclarants, on n'obtient pas le même résultat.

On utilise la syntaxe (adultes, enfants, revenus) -> (impot, decote, reduction) pour dire que le script reçoit les entrées
(adultes, enfants, revenus) et produit les résultats (impot, decote, reduction)

test1 : (2,2,55555) -> (2815, 0, 0)
test2 : (2, 2, 50000) -> (1385, 384, 346)
test3 : (2,3,50000) -> (0, 720, 0)
test4: (1,2,100000) -> (19884, 0, 0)
test5: (1,3,100000) -> (16782, 0, 0)
test6 : (2, 3, 100000) -> (9200, 0, 0)
test7 : (2, 5, 100000) -> (4230, 0, 0)
test8 : (1, 0, 100000) -> (22986, 0, 0)
test9 : (2, 2, 30000) -> (0, 0, 0)
test10 : (1,0,200000) -> (64211, 0, 0)
test11  : (2, 3, 200000) -> (42843, 0, 0)

6 - Il peut y avoir des problèmes d'arrondis. Tu procèderas comme suit
- l'impôt à payer sera arrondi à l'euro inférieur,
- la décote sera arrondie à l'euro supérieur,
- la réduction de 20 % sera arrondie à l'euro supérieur.
- l'abattement de 10% sera arrondi à l'euro supérieur

Fais tous les tests unitaires à l'euro près à cause de ces éventuelles erreurs d'arrondi.
Ne cherche pas à avoir les valeurs exactes ci-dessus mais ces valeurs à 1 euro près.

7 - Evite de chercher sur internet. Le PDF que je te donne est correct.
Ne donne ton résultat que lorsque tu as passé les 11 tests unitaires avec succès.

8 - si l'un des tests échoue et que tu es bloqué, affiche ton raisonnement pour ce test
afin que je puisse t'aider.

9- Mets des commentaires détaillés dans le script que tu génères.
Mets le barème progressif dans une liste ou dictionnaire puis utilise cette liste ou dictionnaire
Mets les nombres en dur (nombres magiques) que tu utilises dans des constantes
Utilise des fonctions pour séparer les étapes du calcul.
Ecris le tout en français

9 - n'affiche pas le code généré à l'écran. Donne-moi simplement un lien pour le récupérer.
Si les tests unitaires échouent, je te donnerai les logs de l'exécution du script pour que
tu voies tes erreurs.

10 - si c'est possible, indique le temps en minutes et secondes que tu as mis pour produire
le script demandé.

Estas instruções são o resultado de inúmeras perguntas feitas ao Gemini. Rapidamente, percebe-se que o IA tem de ser muito específico se quisermos obter o que pretendemos. Foi devido a todas estas tentativas e erros que a sessão do Gemini acabou por ser interrompida por ter excedido os limites. Analisemos o resto destas instruções:

  • Linha 1: solicita-se que a conversa seja em francês. Esta indicação destina-se ao DeepSeek, que tinha tendência para falar inglês;
  • Linha 3: o que pretendemos;
  • Linha 5: diz-se ao IA para utilizar o PDF que lhe foi fornecido;
  • Linhas 7-14: algumas dicas úteis, sobretudo para o problema 3 sem o PDF. Vários IA perderam-se no cálculo do imposto;
  • Linhas 15-44: os 11 testes unitários que queremos ver incluídos no script gerado. Quando o script for gerado, iremos executá-lo no PyCharm e veremos se os 11 testes são bem-sucedidos;
  • Linhas 46-53: sem estas instruções, o IA gerava testes unitários que procuravam resultados exatos e que falhavam;
  • Linhas 55-56: indico ao IA para não aceder à Internet. A solução mais simples é utilizar o PDF;
  • Linhas 58-59: esta instrução não foi seguida pelo IA. Fui obrigado a escrevê-la explicitamente numa pergunta, quando verifiquei que um teste tinha falhado;
  • Linhas 61-65: indico que tipo de script Python pretendo;
  • Linhas 67-69: teria preferido um link para obter o script gerado, pois a exibição do código no ecrã demora algum tempo. Verificou-se que a maioria dos IA não sabe fazer isso. Os links fornecidos não funcionavam;
  • Linhas 71-72: gostaria de ter sabido o tempo que o IA demorou a responder à pergunta. Apenas o Gemini me conseguiu fornecer essa informação. Os outros IA ou não respondiam a esta instrução, ou forneciam números aleatórios, o que demonstra que não compreendiam a instrução;

4.3.3. A resposta da Gemini

A primeira resposta da Gemini é a seguinte:

 
  • Em [1-4], o Gemini fornece ligações para a parte do PDF ou do ficheiro de texto das instruções que utiliza num determinado momento;

O que se segue é o seguinte:

 
  • No [1], o Gemini afirma que executou com sucesso os 11 testes unitários. A maioria dos IA afirmou o mesmo tanto no problema 2 como no problema 3 e, muitas vezes, quando se carregava o script gerado, este não funcionava. Por isso, é preciso ter cuidado com esta afirmação. No caso do Gemini, isso irá revelar-se verdadeiro;
  • No [2], um link que acabará por não funcionar;
  • No [3], apenas o Gemini apresentou um tempo de execução realista;

Portanto, o link [2] não funciona. Informamos o Gemini:

 

A resposta do Gemini:

 
  • Em [1], o script Python gerado pelo Gemini;

Carregamos este script em PyCharm e executamo-lo:

 
  • Em [1], [gemini1] é o script gerado pelo Gemini;

Ao executar o script, surgem erros de compilação:


"C:\Program Files\Python313\python.exe" "C:/Program Files/JetBrains/PyCharm 2025.2.0.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --path "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py" 
Testing started at 17:12 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2025.2.0.1\plugins\python-ce\helpers\pycharm\_jb_unittest_runner.py", line 38, in <module>
    sys.exit(main(argv=args, module=None, testRunner=unittestpy.TeamcityTestRunner,
             ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  buffer=not JB_DISABLE_BUFFERING))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 103, in __init__
    self.parseArgs(argv)
    ~~~~~~~~~~~~~~^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 142, in parseArgs
    self.createTests()
    ~~~~~~~~~~~~~~~~^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 153, in createTests
    self.test = self.testLoader.loadTestsFromNames(self.testNames,
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
                                                   self.module)
                                                   ^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\loader.py", line 207, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
              ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\loader.py", line 137, in loadTestsFromName
    module = __import__(module_name)
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py", line 28, in <module>
    [cite_start]
     ^^^^^^^^^^
NameError: name 'cite_start' is not defined

Process finished with exit code 1

Empty suite
  • Linha 30, o erro de compilação. [cite_start] é um marcador para gerar um determinado tipo de texto;

Colocamos os registos acima num ficheiro [logs gemini1.txt] e entregamo-lo ao Gemini:

 

A resposta do Gemini é então a seguinte:

 

Carregado no ficheiro PyCharm, a execução do novo script gera exatamente o mesmo erro. Informamos o Gemini, fornecendo-lhe novamente os registos da execução:

 

A resposta do Gemini é a seguinte:

 

Desta vez está tudo bem. Os 11 testes unitários foram bem-sucedidos. Informamos o Gemini:

 

Que responde:

 

O script gerado pelo Gemini seguiu as instruções fornecidas no ficheiro de texto [instructionsAvecPDF.txt]:

# -*- coding: utf-8 -*-
"""
Script pour le calcul de l'impôt 2019 sur les revenus de 2018 en France,
basé sur les règles simplifiées fournies dans le document PDF.
"""

import math
import unittest

# --- CONSTANTES DE CALCUL POUR L'IMPÔT 2019 ---
# Todos os valores são extraídos do documento PDF fornecido.

# Limites máximos para a dedução de 10% sobre os rendimentos anuais
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# Tabela do imposto sobre o rendimento de 2019
# Estrutura: (Limite máximo da faixa, Taxa de imposto, Montante a deduzir por quota)
BAREME_IMPOT_2019 = [
    {"seuil": 9964, "taux": 0.0, "deduction_par_part": 0},
    {"seuil": 27519, "taux": 0.14, "deduction_par_part": 1394.96},
    {"seuil": 73779, "taux": 0.30, "deduction_par_part": 5798},
    {"seuil": 156244, "taux": 0.41, "deduction_par_part": 13913.69},
    {"seuil": float('inf'), "taux": 0.45, "deduction_par_part": 20163.45}
]

# Limite máximo do quociente familiar
GAIN_MAX_DEMI_PART = 1551.0  # Ganho máximo por meia quota adicional

# Limiares e montantes para a redução
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# Limiares para a redução fiscal de 20%
SEUIL_BASE_REDUCTION_20 = {"celibataire": 21037.0, "couple": 42074.0}
MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART = 3797.0


def calculer_nombre_parts(adultes: int, enfants: int) -> float:
    """
    Calcule le nombre de parts fiscales du foyer.
    - Chaque parent amène 1 part.
    - Les deux premiers enfants amènent chacun 1/2 part.
    - Les enfants suivants amènent une part chacun.
    """
    if adultes not in [1, 2]:
        raise ValueError("Le nombre d'adultes doit être 1 ou 2.")

    parts = float(adultes)
    if enfants > 0:
        parts += 0.5 * min(enfants, 2)
    if enfants > 2:
        parts += 1.0 * (enfants - 2)

    return parts


def calculer_revenu_imposable(revenus: float) -> float:
    """
    Calcule le revenu net imposable après l'abattement de 10%.
    Cet abattement est plafonné.
    L'abattement est arrondi à l'euro supérieur.
    """
    abattement_calcule = revenus * 0.1
     # A dedução está limitada entre um valor mínimo e um valor máximo
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # A instrução pede que a dedução seja arredondada para o euro superior
    abattement_arrondi = math.ceil(abattement)

    return revenus - abattement_arrondi


def calculer_impot_brut_base(revenu_imposable: float, nombre_parts: float) -> float:
    """
    Calcule l'impôt brut selon le barème progressif.
    """
    if nombre_parts <= 0:
        return 0.0

    quotient_familial = revenu_imposable / nombre_parts

    tranche_applicable = None
    for tranche in BAREME_IMPOT_2019:
        if quotient_familial <= tranche["seuil"]:
            tranche_applicable = tranche
            break

     # Se o valor de QF for superior a todos os limiares, utiliza-se a última faixa
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # Fórmula de cálculo do imposto bruto
    impot_brut = (revenu_imposable * tranche_applicable["taux"]) - (
            tranche_applicable["deduction_par_part"] * nombre_parts)

    return max(0.0, impot_brut)


def appliquer_plafonnement_qf(impot_brut_avec_enfants: float, revenu_imposable: float, adultes: int,
                              enfants: int) -> float:
    """
    Applique le plafonnement du quotient familial si nécessaire.
    L'impôt final ne peut être inférieur à l'impôt calculé sans les enfants,
    diminué d'un gain maximal par demi-part.
    """
    if enfants == 0:
        return impot_brut_avec_enfants

     # Cálculo do imposto sem as quotas-partes dos filhos
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # Cálculo do ganho máximo relacionado com os filhos
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    demi_parts_enfants = (parts_avec_enfants - parts_sans_enfants) * 2
    gain_maximal = demi_parts_enfants * GAIN_MAX_DEMI_PART

    impot_minimal_plafone = impot_sans_enfants - gain_maximal

     # O imposto retido é o mais elevado dos dois cálculos
    return max(impot_brut_avec_enfants, impot_minimal_plafone)


def appliquer_decote(impot_avant_decote: float, adultes: int) -> tuple[float, float]:
    """
    Applique la décote si l'impôt est inférieur à certains seuils.
    La décote est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_application = SEUIL_APPLICATION_DECOTE[statut]
    seuil_calcul = SEUIL_CALCUL_DECOTE[statut]

    decote = 0.0
     # A redução aplica-se se o imposto for inferior ao limiar de aplicação
    if impot_avant_decote < seuil_application:
         # Fórmula do abatimento: limiar - 0,75 * Imposto bruto
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # Arredondamento para o euro superior
        decote = math.ceil(decote_calculee)

         # A redução não pode ser negativa nem superior ao imposto
        decote = max(0.0, min(decote, impot_avant_decote))

    impot_apres_decote = impot_avant_decote - decote
    return impot_apres_decote, decote


def appliquer_reduction_20_pourcent(impot_avant_reduction: float, revenu_imposable: float, adultes: int,
                                    enfants: int) -> tuple[float, float]:
    """
    Applique la réduction de 20% sous condition de revenus.
    La réduction est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_base = SEUIL_BASE_REDUCTION_20[statut]

     # O limiar é aumentado por cada meia-parte relacionada com os filhos
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    parts_adultes = calculer_nombre_parts(adultes, 0)
    demi_parts_enfants = (parts_avec_enfants - parts_adultes) * 2
    seuil_final = seuil_base + (demi_parts_enfants * MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART)

    reduction = 0.0
     # A redução aplica-se se o rendimento tributável for inferior ao limiar
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # Arredondado para o euro superior
        reduction = math.ceil(reduction_calculee)

    impot_apres_reduction = impot_avant_reduction - reduction
    return max(0.0, impot_apres_reduction), reduction


def calculer_impot(adultes: int, revenus: float, enfants: int) -> dict:
    """
    Orchestre le calcul complet de l'impôt net à payer.
    """
     # Etapa 1: Cálculo do rendimento tributável
    revenu_imposable = calculer_revenu_imposable(revenus)

     # Etapa 2: Cálculo do número de quotas
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # Etapa 3: Cálculo do imposto bruto
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # Etapa 4: Aplicação do limite máximo do QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # Etapa 5: Aplicação do abatimento
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # Etapa 6: Aplicação da redução de 20%
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # Passo 7: Arredondamento final do imposto para o euro inferior
    impot_final = math.floor(impot_avant_arrondi_final)

    return {
        "impot": impot_final,
        "decote": decote_calculee,
        "reduction": reduction_calculee
    }


# --- TESTS UNITAIRES ---
# Validação do script com os casos fornecidos
class TestCalculImpot2019(unittest.TestCase):

    def _verifier_resultat(self, adultes, enfants, revenus, impot_attendu, decote_attendue, reduction_attendue):
        """Fonction utilitaire pour exécuter un test et vérifier les résultats à 1 euro près."""
        resultat = calculer_impot(adultes=adultes, revenus=revenus, enfants=enfants)

        self.assertAlmostEqual(resultat["impot"], impot_attendu, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Impôt attendu {impot_attendu}, obtenu {resultat['impot']}")

        self.assertAlmostEqual(resultat["decote"], decote_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Décote attendue {decote_attendue}, obtenue {resultat['decote']}")

        self.assertAlmostEqual(resultat["reduction"], reduction_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Réduction attendue {reduction_attendue}, obtenue {resultat['reduction']}")

    def test_cas_1(self):
        # (2, 2, 55555) -> (2815, 0, 0)
        self._verifier_resultat(2, 2, 55555, 2815, 0, 0)

    def test_cas_2(self):
        # (2, 2, 50000) -> (1385, 384, 346)
        self._verifier_resultat(2, 2, 50000, 1385, 384, 346)

    def test_cas_3(self):
        # (2, 3, 50000) -> (0, 720, 0)
        self._verifier_resultat(2, 3, 50000, 0, 720, 0)

    def test_cas_4(self):
        # (1, 2, 100000) -> (19884, 0, 0)
        self._verifier_resultat(1, 2, 100000, 19884, 0, 0)

    def test_cas_5(self):
        # (1, 3, 100000) -> (16782, 0, 0)
        self._verifier_resultat(1, 3, 100000, 16782, 0, 0)

    def test_cas_6(self):
        # (2, 3, 100000) -> (9200, 0, 0)
        self._verifier_resultat(2, 3, 100000, 9200, 0, 0)

    def test_cas_7(self):
        # (2, 5, 100000) -> (4230, 0, 0)
        self._verifier_resultat(2, 5, 100000, 4230, 0, 0)

    def test_cas_8(self):
        # (1, 0, 100000) -> (22986, 0, 0)
        self._verifier_resultat(1, 0, 100000, 22986, 0, 0)

    def test_cas_9(self):
        # (2, 2, 30000) -> (0, 0, 0)
        self._verifier_resultat(2, 2, 30000, 0, 0, 0)

    def test_cas_10(self):
        # (1, 0, 200000) -> (64211, 0, 0)
        self._verifier_resultat(1, 0, 200000, 64211, 0, 0)

    def test_cas_11(self):
        # (2, 3, 200000) -> (42843, 0, 0)
        self._verifier_resultat(2, 3, 200000, 42843, 0, 0)


if __name__ == '__main__':
    print("Exécution des tests unitaires...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

     # Exemplo de utilização da calculadora para um caso específico
    print("\n--- Exemple de calcul ---")
    revenus_annuels = 50000
    nombre_adultes = 2
    nombre_enfants = 2

    resultat_calcul = calculer_impot(adultes=nombre_adultes, revenus=revenus_annuels, enfants=nombre_enfants)

    print(f"Pour un couple ({nombre_adultes} adultes) avec {nombre_enfants} enfants et {revenus_annuels}€ de revenus :")
    print(f"  - Impôt à payer : {resultat_calcul['impot']}€")
    print(f"  - Montant de la décote : {resultat_calcul['decote']}€")
    print(f"  - Montant de la réduction : {resultat_calcul['reduction']}€")

Não verifiquei este código. Uma vez que os 11 testes unitários foram bem-sucedidos, considero que está «provavelmente correto». Não fiz mais nada em relação ao meu próprio código além de verificar estes 11 testes.

4.4. O problema 3

O problema 3 é idêntico ao problema 2, com a diferença de que já não se fornece ao IA o PDF, que indicava as regras de cálculo a seguir.

A pergunta inicial ao Gemini é a seguinte:

 

O ficheiro de instruções em [1] é praticamente idêntico ao do problema 2, com as seguintes diferenças:


1 - Exprime-toi en français.

2 - Peux-tu générer un script Python permettant de calculer l'impôt payé par les familles en 2019 sur leurs revenus de 2018.

3 - Tu t'aideras des sources que tu trouveras sur internet. Dans ta réponse indique-moi ces sources.

4 - Tu dois faire attention aux points suivants :

  • No [3], pede-se-lhe que procure na Internet as regras de cálculo do imposto de 2019 sobre os rendimentos de 2018. Trata-se de um exercício mais difícil do que o anterior;

Apresento abaixo apenas partes da primeira resposta do Gemini:

 
 

O tempo estimado é plausível. Espera-se muito tempo pela resposta do Gemini.

Tal como anteriormente, o Gemini forneceu um link para descarregar o script gerado, link esse que não funciona. Informamo-lo disso:

 

A resposta da Gemini:

 

Carregamos o script em PyCharm com o nome [gemini2]:

 

Executamo-lo e… não funciona. Os registos da execução são os seguintes:


"C:\Program Files\Python313\python.exe" "C:/Program Files/JetBrains/PyCharm 2025.2.0.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --path "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py" 
Testing started at 17:23 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours


Failure
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 278, in test_cas_2
    self.assertAlmostEqual(impot, 1385, delta=1)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1691 != 1385 within 1 delta (306 difference)


Error
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 291, in test_cas_3
    impot, _, _ = calculer_impot_final(2, 3, 50000)
                  ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 187, in calculer_impot_final
    decote, impot_apres_decote = calculer_decote(impot_apres_plafonnement, adultes)
                                 ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 134, in calculer_decote
    decote = seuil_decote - (impot_avant_decote * COFFICIENT_DECOTE)
                                                  ^^^^^^^^^^^^^^^^^
NameError: name 'COFFICIENT_DECOTE' is not defined. Did you mean: 'COEFFICIENT_DECOTE'?


Error
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 316, in test_cas_9
    self._verifier_cas(2, 2, 30000, (0, 0, 0))
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 216, in _verifier_cas
    impot_calcule, decote_calculee, reduction_calculee = calculer_impot_final(adultes, enfants, revenus)
                                                         ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 187, in calculer_impot_final
    decote, impot_apres_decote = calculer_decote(impot_apres_plafonnement, adultes)
                                 ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 134, in calculer_decote
    decote = seuil_decote - (impot_avant_decote * COFFICIENT_DECOTE)
                                                  ^^^^^^^^^^^^^^^^^
NameError: name 'COFFICIENT_DECOTE' is not defined. Did you mean: 'COEFFICIENT_DECOTE'?



Ran 11 tests in 0.038s

FAILED (failures=1, errors=2)

Process finished with exit code 1
  • Linha 11, um teste falhou;
  • Linhas 25 e 42: o mesmo erro de compilação;

Colocamos estes registos num ficheiro de texto que enviamos ao Gemini:

 

A resposta do Gemini:

 
 

Carregamos o script em PyCharm e executamo-lo. Mais erros. Informamos o Gemini, anexando novamente os registos da execução:

 

A resposta da Gemini:

 
 
 

Carregamos este novo script no PyCharm e executamo-lo. Desta vez, os 11 testes unitários são bem-sucedidos:

O código gerado pelo Gemini é o seguinte:

# -*- coding: utf-8 -*-
import math
import unittest

# ==============================================================================
# CONSTANTES POUR OIMPOSITION DES REVENUS 2018 (PAYÉE EN 2019)
# ==============================================================================
BAREME_IMPOT_2019 = [
    {'seuil': 0, 'taux': 0.0},
    {'seuil': 9964, 'taux': 0.14},
    {'seuil': 27519, 'taux': 0.30},
    {'seuil': 73779, 'taux': 0.41},
    {'seuil': 156244, 'taux': 0.45},
]
ABATTEMENT_FORFAITAIRE_TAUX = 0.10
ABATTEMENT_FORFAITAIRE_MINIMUM = 437
ABATTEMENT_FORFAITAIRE_PLAFOND = 12502
PLAFONNEMENT_AVANTAGE_QF_DEMI_PART = 1551
SEUIL_DECOTE_CELIBATAIRE = 1196
SEUIL_DECOTE_COUPLE = 1970
COEFFICIENT_DECOTE = 0.75
TAUX_REDUCTION_20_POURCENT = 0.20
SEUIL_RFR_REDUCTION_CELIBATAIRE = 18985
SEUIL_RFR_REDUCTION_COUPLE = 37970
MAJORATION_RFR_REDUCTION_PAR_DEMI_PART = 3803


# ==============================================================================
# FONCTIONS DE CALCUL
# ==============================================================================

def calculer_nombre_parts(adultes: int, enfants: int) -> float:
    """Calcule le nombre de parts du foyer fiscal."""
    if adultes < 1: return 0
    parts = float(adultes)
    if enfants >= 1: parts += 0.5
    if enfants >= 2: parts += 0.5
    if enfants >= 3: parts += (enfants - 2) * 1.0
    return parts


def calculer_revenu_imposable(revenus_bruts: float) -> float:
    """Calcule le revenu net imposable après abattement de 10%."""
    if revenus_bruts == 0: return 0.0
    abattement = revenus_bruts * ABATTEMENT_FORFAITAIRE_TAUX
    if abattement < ABATTEMENT_FORFAITAIRE_MINIMUM: abattement = ABATTEMENT_FORFAITAIRE_MINIMUM
    if abattement > ABATTEMENT_FORFAITAIRE_PLAFOND: abattement = ABATTEMENT_FORFAITAIRE_PLAFOND
    abattement = math.ceil(abattement)
    revenu_imposable = revenus_bruts - abattement
    return revenu_imposable if revenu_imposable > 0 else 0


def calculer_impot_brut_par_tranche(revenu_imposable: float, nombre_parts: float) -> float:
    """Calcule l'impôt brut en appliquant le barème progressif."""
    if nombre_parts <= 0 or revenu_imposable <= 0: return 0.0
    quotient_familial = revenu_imposable / nombre_parts
    impot_par_part = 0.0
    for i in range(len(BAREME_IMPOT_2019)):
        tranche = BAREME_IMPOT_2019[i]
        seuil_actuel = tranche['seuil']
        taux = tranche['taux']
        if quotient_familial > seuil_actuel:
            base_imposable_tranche = 0
            if i < len(BAREME_IMPOT_2019) - 1:
                seuil_suivant = BAREME_IMPOT_2019[i + 1]['seuil']
                base_imposable_tranche = min(quotient_familial, seuil_suivant) - seuil_actuel
            else:
                base_imposable_tranche = quotient_familial - seuil_actuel
            impot_par_part += base_imposable_tranche * taux
    return impot_par_part * nombre_parts


def appliquer_plafonnement_qf(revenu_imposable: float, nombre_parts: float, adultes: int,
                              impot_brut_famille: float) -> float:
    """Applique le plafonnement des effets du quotient familial."""
    if nombre_parts <= adultes: return impot_brut_famille
    impot_base = calculer_impot_brut_par_tranche(revenu_imposable, float(adultes))
    demi_parts_enfants = (nombre_parts - adultes) * 2
    avantage_qf_max = PLAFONNEMENT_AVANTAGE_QF_DEMI_PART * demi_parts_enfants
    avantage_qf_reel = impot_base - impot_brut_famille
    if avantage_qf_reel > avantage_qf_max:
        return impot_base - avantage_qf_max
    return impot_brut_famille


def calculer_impot_final(adultes: int, enfants: int, revenus: float) -> (int, int, int):
    """Orchestre le calcul complet de l'impôt."""
    revenu_imposable = calculer_revenu_imposable(revenus)
    nombre_parts = calculer_nombre_parts(adultes, enfants)
    impot_brut = calculer_impot_brut_par_tranche(revenu_imposable, nombre_parts)
    impot_apres_plafonnement = appliquer_plafonnement_qf(revenu_imposable, nombre_parts, adultes, impot_brut)

     # CORRECTION: Se o imposto já for 0, não há desconto nem redução.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

     # --- LOGIQUE CORRIGÉE POUR O INTERACTION DÉCOTE / RÉDUCTION ---
    decote_calculee = 0.0
    reduction_calculee = 0.0
    impot_a_traiter = impot_apres_plafonnement

     # 1. Determinar a elegibilidade para o desconto de 20%
    est_eligible_reduction = False
    if adultes >= 2:
        seuil_rfr = SEUIL_RFR_REDUCTION_COUPLE + max(0, (nombre_parts - 2) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    else:
        seuil_rfr = SEUIL_RFR_REDUCTION_CELIBATAIRE + max(0, (
                nombre_parts - 1) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    if revenu_imposable < seuil_rfr:
        est_eligible_reduction = True

     # 2. Determinar a elegibilidade para o desconto
    seuil_decote_foyer = SEUIL_DECOTE_COUPLE if adultes >= 2 else SEUIL_DECOTE_CELIBATAIRE
    reduction_theorique = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT) if est_eligible_reduction else 0

    if (impot_a_traiter - reduction_theorique) < seuil_decote_foyer:
        decote_calculee = seuil_decote_foyer - (impot_a_traiter * COEFFICIENT_DECOTE)
        decote_calculee = math.ceil(decote_calculee)
         # CORRECTION: O abatimento devolvido não pode exceder o imposto que anula
        decote_calculee = min(decote_calculee, math.ceil(impot_a_traiter))
        impot_a_traiter -= decote_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

     # 3. Aplicar a redução de 20% se for elegível
    if est_eligible_reduction:
        reduction_calculee = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT)
        impot_a_traiter -= reduction_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

    impot_final = math.floor(impot_a_traiter)

    return impot_final, int(decote_calculee), int(reduction_calculee)


# ==============================================================================
# TESTS UNITAIRES
# ==============================================================================

class TestCalculImpot2019(unittest.TestCase):
    def _verifier_cas(self, adultes, enfants, revenus, attendu):
        impot_attendu, decote_attendue, reduction_attendue = attendu
        impot_calcule, decote_calculee, reduction_calculee = calculer_impot_final(adultes, enfants, revenus)
        self.assertAlmostEqual(impot_calcule, impot_attendu, delta=1, msg=f"Impôt ({impot_calcule} vs {impot_attendu})")
        self.assertAlmostEqual(decote_calculee, decote_attendue, delta=1,
                               msg=f"Décote ({decote_calculee} vs {decote_attendue})")
        self.assertAlmostEqual(reduction_calculee, reduction_attendue, delta=1,
                               msg=f"Réduction ({reduction_calculee} vs {reduction_attendue})")

    def test_cas_1(self): self._verifier_cas(2, 2, 55555, (2815, 0, 0))

    def test_cas_2(self): self._verifier_cas(2, 2, 50000, (1385, 384, 346))

    def test_cas_3(self): self._verifier_cas(2, 3, 50000, (0, 720, 0))

    def test_cas_4(self): self._verifier_cas(1, 2, 100000, (19884, 0, 0))

    def test_cas_5(self): self._verifier_cas(1, 3, 100000, (16782, 0, 0))

    def test_cas_6(self): self._verifier_cas(2, 3, 100000, (9200, 0, 0))

    def test_cas_7(self): self._verifier_cas(2, 5, 100000, (4230, 0, 0))

    def test_cas_8(self): self._verifier_cas(1, 0, 100000, (22986, 0, 0))

    def test_cas_9(self): self._verifier_cas(2, 2, 30000, (0, 0, 0))

    def test_cas_10(self): self._verifier_cas(1, 0, 200000, (64211, 0, 0))

    def test_cas_11(self): self._verifier_cas(2, 3, 200000, (42843, 0, 0))


if __name__ == '__main__':
    print("Lancement des tests unitaires pour le calcul de l'impôt 2019...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

Mais uma vez, não analisei este código. Limitei-me a verificar que passou com sucesso nos 11 testes.

Mas podemos ficar curiosos em saber o seu raciocínio, nomeadamente nos casos específicos do cálculo do imposto. Perguntamos-lhe:

 

Trata-se de um caso de rendimentos elevados, com um possível limite máximo de 10% na dedução e um possível limite máximo no quociente familiar.

A resposta do Gemini é a seguinte:

 
 
 
 
 

Estas duas últimas capturas de ecrã são interessantes. O Gemini utiliza um método de cálculo diferente do explicado no PDF. De facto, este método de cálculo pode ser encontrado na Internet. Os dois métodos são equivalentes.

 
 
 

A explicação destaca-se pela sua clareza. Poderia ser apresentada tal como está aos estudantes para explicar o método de cálculo do imposto.

Agora, vejamos outro exemplo, desta vez com rendimentos baixos. Neste caso, pode haver abatimento e redução:

 

A resposta do Gemini é a seguinte:

 
 
 
 
 

Aqui, verifica-se que o Gemini aplica uma regra que não consta do PDF. Provavelmente encontrou-a na Internet, mas será que a fonte é fiável?

 

Aqui, o Gemini continua a aplicar uma regra desconhecida (a regra especial acima referida).

 
 
 

Portanto, os resultados do Gemini coincidem com o resultado do simulador oficial de impostos. Mas ele utilizou uma regra que não consta no PDF. Onde está o erro? Perguntamos-lhe, anexando o PDF:

 

A resposta do Gemini:

 
 
 
 

Acho que a Gemini tem razão e que o meu PDF está errado. Para verificar isso, peço-lhe que faça um teste:

  • Em que o seu raciocínio daria os mesmos resultados que o simulador fiscal oficial;
  • Onde o raciocínio do PDF lhe daria resultados diferentes dos do simulador;
 

A resposta do Gemini é a seguinte:

 

Aqui, o Gemini está errado. Executei o simulador com este exemplo e obtive o seguinte resultado:

 

No entanto, vamos ver que o raciocínio do Gemini leva, de facto, aos resultados acima. Continuamos:

 
 
 
 

Muito bem. Tomamos nota. Continuamos:

 
 
 
 

Portanto, o Gemini calculou (imposto, desconto, redução) = (431, 325, 1296), enquanto o simulador que utilizei dá (431, 324, 1297). Assim, o Gemini obteve os resultados corretos com uma diferença de 1 euro, mas não tem conhecimento disso. Informamo-lo:

 

O Gemini responde:

 
 

Agora, questiona-se se o Gemini poderia gerar um PDF corrigido:

 

A resposta do Gemini:

 

O Gemini não me forneceu, portanto, um link para um PDF, mas gerou um texto para que eu próprio pudesse gerar esse PDF. Embora seja um pouco complicado apresentar aqui as capturas de ecrã do PDF, faço-o para que o leitor veja o lado generativo do IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Sinceramente, não verifiquei se tudo o que está escrito neste PDF é verdade. De qualquer forma, é um documento perfeito para um TD, gerado em poucos segundos.

No entanto, podemos pedir ao próprio Gemini para verificar se o seu PDF está correto. Iniciamos uma nova conversa:

 
  • no [1], colocámos o PDF gerado pelo Gemini [Le problème selon Gemini.pdf];
  • no [2], o [instructionsAvecPDF2.txt] é idêntico às instruções do [instructionsAvecPDF.txt], com a única diferença de que foi adicionado um décimo segundo teste unitário, precisamente aquele que revelou que o PDF inicial estava errado:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, foram necessárias várias idas e voltas até que o Gemini gerasse o script correto:

Pergunta 2

 

Pergunta 3

 

Como já foi feito várias vezes, quando o script gerado e carregado em PyCharm falha, fornecemos ao Gemini o ficheiro de texto com os registos da execução. O Gemini compreende-os muito bem.

Pergunta 4

 

Pergunta 5

 

Pergunta 6 e fim

Agora estamos certos da validade do PDF gerado pelo Gemini. As regras de cálculo aí indicadas estão corretas.

Vamos agora fazer o mesmo com os outros cinco IA, mas seremos muito sucintos nas nossas explicações, exceto no caso do ChatGPT, o IA líder do momento. O que nos interessa é saber se o IA resolve ou não os três problemas que lhe colocamos. Com efeito, as interfaces de todos estes IA são muito semelhantes e procedi com elas tal como fiz com o Gemini. O leitor é encorajado a repetir as conversas do Gemini com o IA da sua escolha.