Skip to content

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

Iremos apresentar capturas de ecrã das três sessões do Gemini que foram utilizadas para resolver os três problemas colocados. Entraremos em pormenores consideráveis. Uma vez feito isto, não repetiremos o processo para as outras IA testadas. Estas funcionam de forma semelhante. Apenas apresentaremos os pormenores mais notáveis.

4.1. Introdução

Remetemos para a primeira captura de ecrã do Gemini apresentada anteriormente:

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

O Gemini é um produto do Google disponível no URL [https://gemini.google.com/]. Para visualizar um histórico das suas sessões de perguntas e respostas, como mostrado acima, é necessário criar uma conta. Além disso, tal como todas as outras IAs testadas, o Gemini limita o número de perguntas que pode fazer e o número de ficheiros que pode carregar. Quando este limite é atingido, a sessão termina e é-lhe oferecida a opção de a continuar mais tarde. Uma vez que é bastante frustrante parar a meio de uma sessão, subscrevi uma assinatura. Felizmente, o primeiro mês da assinatura do Gemini é gratuito. Fiz o mesmo com as outras IAs que tinham estes limites, nomeadamente o ChatGPT, o MistralAI e o ClaudeAI. Subscrevi uma assinatura de um mês, mas, nesses casos, o primeiro mês foi pago. Não encontrei quaisquer limites com o Grok. O DeepSeek não anuncia quaisquer limites, mas, por vezes, responde com [Servidor ocupado] e interrompe a sessão. Isso é, essencialmente, estabelecer limites sem o dizer.

A partir daqui, vou referir-me às sessões de perguntas e respostas simplesmente como «sessões». As IAs utilizam mais frequentemente os termos ingleses «chat» ou «conversation».

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

  • Em [1], a sua pergunta;
  • Em [2], o ícone que aciona a IA para calcular a resposta;
  • Em [3-4], pode anexar ficheiros;

4.2. Problema 1

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

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

O resto da resposta é o seguinte:

 
 
 
 

A resposta está correta. As outras cinco IAs também darão a resposta correta de forma semelhante.

4.3. Problema 2

4.3.1. Introdução

Aqui relembramos o problema inicial do curso [python3-flask-2020]. Trata-se de um texto entregue aos alunos numa aula prática.

 

A tabela acima permite-nos calcular o imposto no caso simplificado de um contribuinte que tem apenas o seu salário para declarar. Conforme indicado na nota (1), o imposto calculado desta forma é o imposto antes de três mecanismos:

  • O limite máximo do quociente familiar, que se aplica a rendimentos elevados;
  • O crédito fiscal e a redução fiscal que se aplicam aos contribuintes com rendimentos baixos;

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

 

Propomos escrever um programa para calcular a obrigação fiscal de um contribuinte para 2019, no caso simplificado de um contribuinte que tenha apenas o seu salário a declarar.

4.3.1.1. Cálculo do imposto bruto

O imposto bruto pode ser calculado da seguinte forma:

Primeiro, calcule o número de quotas do contribuinte:

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

O número de ações é, portanto:

  • nbParts=1+nbChildren*0,5+(nbChildren-2)*0,5 se o funcionário for solteiro;
  • nbParts=2+nbChildren*0,5+(nbChildren-2)*0,5 se for casado;
  • onde nbChildren é o número de filhos;
  • Calculamos o rendimento tributável R = 0,9 * S, em que S é o salário anual;
  • O quociente familiar QF é calculado como QF = R / nbParts;
  • Calculamos o imposto bruto I com base nos seguintes dados (2019):
9964
0
0
27 519
0,14
1.394,96
73.779
0,3
5.798
156 244
0,4
13 913,69
0
0,45
20163,45

Cada linha tem 3 campos: campo1, campo2, campo3. Para calcular o imposto I, procuramos a primeira linha em que QF <= campo1 e retiramos os valores dessa linha. Por exemplo, para um funcionário 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*númeroDeAções=[0,14*45000-1394,96*3]=2115. O imposto é arredondado por defeito para o euro mais próximo.

Se a condição QF <= campo1 for verdadeira na primeira linha, então o imposto é zero.

Se QF for tal que a condição QF <= campo1 nunca for satisfeita, então são utilizados os coeficientes da última linha. Aqui:

    0    0.45    20163.45

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

4.3.1.2. Limite do Quociente Familiar

 

Para determinar se o limite do quociente familiar (QF) se aplica, recalculamos o imposto bruto sem os filhos. Mais uma vez, para o 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 (os filhos já não são contabilizados)

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*número de ações = [0,14*45 000 – 1394,96*2] = 3510.

Benefício máximo relacionado com filhos: 1551 * 2 = 3102 euros

Imposto mínimo: 3.510 – 3.102 = 408 euros

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

Em geral, o imposto bruto é superior a (imposto1, imposto2), em que:

  • [imposto1]: é o imposto bruto calculado incluindo os filhos;
  • [imposto2]: é o imposto bruto calculado sem filhos e reduzido pelo crédito máximo (aqui 1 551 euros por meia quota) relacionado com os filhos;

4.3.1.3. Cálculo da redução fiscal

 

Ainda no caso do trabalhador casado com dois filhos e um salário anual S de 50 000 euros:

O imposto bruto (2.115 euros) da etapa anterior é inferior a 2.627 euros para um casal (1.595 euros para uma pessoa solteira): a redução fiscal aplica-se, portanto. É calculada da seguinte forma:

crédito fiscal = limiar (casal = 1.970 / solteiro = 1.196) – 0,75 * imposto bruto

desconto = 1.970 – 0,75 * 2.115 = 383,75, arredondado para 384 euros.

Novo imposto bruto = 2.115 – 384 = 1.731 euros

É necessário respeitar duas regras ao calcular o desconto (algumas ferramentas de IA têm tropeçado nesta questão):

  • O desconto não pode ser negativo;
  • O desconto não pode exceder o imposto já calculado;

4.3.1.4. Cálculo da redução do imposto

 

Abaixo de um determinado limiar, é aplicada uma redução de 20% ao 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 incorreto);

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

Mais uma vez, para o 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% no seu imposto: 1.731 * 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 1.384 euros. Na realidade, o contribuinte pode ter direito a outras deduções, nomeadamente por doações a organizações de interesse público ou geral.

4.3.1.6. Casos de rendimentos elevados

O nosso exemplo anterior aplica-se à maioria dos trabalhadores. No entanto, o cálculo do imposto difere para quem aufere rendimentos elevados.

4.3.1.6.1. Limite à redução de 10% sobre o rendimento anual

Na maioria dos casos, o rendimento tributável é calculado utilizando a fórmula: R = 0,9 × S, em que S é o salário anual. Isto é conhecido como a redução de 10%. Esta redução está limitada. Em 2019:

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

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

  • A redução de 10 % corresponde a 200 000 euros > 12 502 euros. Por conseguinte, o valor máximo é de 12 502 euros;

4.3.1.6.2. Limite do Quociente Familiar

Consideremos um caso em que se aplica o limite familiar descrito na secção |Limite do Quociente Familiar|. Tomemos o caso de um casal com três filhos e um rendimento anual de 100 000 euros. Repassemos novamente os passos 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 do quociente familiar estabelece que o benefício derivado dos filhos não pode exceder (1.551 × 4 meias quotas) = 6.204 euros. No entanto, neste caso, é I2 – I1 = 15.404 – 7.020 = 8.384 euros, o que é superior a 6.204 euros;
  • O imposto bruto é, portanto, recalculado como I3 = I2 - 6.204 = 15.404 - 6.204 = 9.200 euros;
  • Uma vez que I3 > I1, o imposto I3 será retido;

Este casal não receberá nem crédito fiscal nem redução, e o seu imposto final será de 9.200 euros.

4.3.1.7. Dados oficiais

O cálculo do imposto é complexo. Ao longo deste documento, serão realizados testes utilizando os seguintes exemplos. Os resultados provêm 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 um rendimento anual de 55 555 euros
Imposto = 2.815 euros
Taxa de imposto = 14%
Imposto = 2.814 euros
Taxa de imposto = 14%
Casal com 2 filhos e um rendimento anual de 50 000 euros
Imposto = 1 385 euros
Crédito fiscal = 384 euros
Redução = 346 euros
Taxa de imposto = 14%
Imposto = 1.384 euros
Desconto = 384 euros
Crédito = 347 euros
Taxa de imposto = 14%
Casal com 3 filhos e um rendimento anual de 50 000 euros
Imposto = 0 euros
Crédito fiscal = 720 euros
Redução = 0 euros
Taxa de imposto = 14%
Imposto = 0 euros
Desconto = 720 euros
Dedução = 0 euros
Taxa de imposto = 14%
Solteiro com 2 filhos e um rendimento anual de 100 000 euros
Imposto = 19 884 euros
Crédito fiscal = 0 euros
Deduçã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 um rendimento anual de 100 000 euros
Imposto = 16 782 euros
Crédito fiscal = 0 euros
Dedução = 0 euros
Taxa de imposto = 41%
Imposto = 16 782
Sobretaxa = 7.176 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41%
Casal com 3 filhos e um rendimento anual de 100 000 euros
Imposto = 9.200 euros
Crédito fiscal = 0 euros
Deduçã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 um rendimento anual de 100 000 euros
Imposto = 4.230 euros
Crédito fiscal = 0 euros
Dedução = 0 euros
Taxa de imposto = 14%
Imposto = 4.230
Desconto = 0 euros
Dedução = 0 euros
Taxa de imposto = 14%
Solteiro, sem filhos e rendimento anual de 100 000 euros
Imposto = 22.986 euros
Crédito fiscal = 0 euros
Dedução = 0 euros
Taxa de imposto = 41%
Imposto = 22 986 euros
Sobretaxa = 0 euros
Desconto = 0 euros
Dedução = 0 euros
Taxa de imposto = 41%
Casal com 2 filhos e um rendimento anual de 30 000 euros
Imposto = 0 euros
Crédito fiscal = 0 euros
Deduçã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 um rendimento anual de 200 000 euros
Imposto = 64 211 euros
Crédito fiscal = 0 euros
Deduçã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 um rendimento anual de 200 000 euros
Imposto = 42 843 euros
Crédito fiscal = 0 euros
Dedução = 0 euros
Taxa de imposto = 41%
Imposto = 42 842 euros
Sobretaxa = 17 283 euros
Desconto = 0 euros
Redução = 0 euros
Taxa de imposto = 41%

No exemplo acima, a «sobretaxa» refere-se ao montante adicional pago pelos contribuintes com rendimentos elevados devido a dois fatores:

  • O limite máximo de 10% de dedução do rendimento anual;
  • O limite máximo do abono de família;

Este indicador não pôde ser verificado porque o simulador da autoridade fiscal não o disponibiliza.

Podemos ver que o algoritmo do documento calcula sempre o montante correto do imposto, embora com uma margem de erro de 1 euro. Esta margem de erro resulta do arredondamento. Todos os montantes monetários são arredondados para o euro superior em alguns casos e para o euro inferior noutros. Como não estava familiarizado com as regras oficiais, os montantes monetários no algoritmo do documento foram arredondados:

  • Para o euro superior no caso de descontos e reduções;
  • Para baixo, para o euro mais próximo, no caso de sobretaxas e do imposto final;

Vamos pedir à IA para realizar este cálculo de impostos.

4.3.2. Configuração da sessão do Gemini

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

 
  • Em [1], o cálculo acima descrito foi incluído num PDF que foi fornecido à Gemini. A Gemini encontrará nesse documento as regras exatas para o cálculo simplificado do imposto de 2019 sobre os rendimentos de 2018;
  • Em [2], as nossas instruções;
  • Em [3], o comando para iniciar a 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/simulateur/calcul_impot/2019/simplifie/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. Torna-se rapidamente claro que a IA precisa de ser orientada de forma muito rigorosa se quisermos obter o que pretendemos. Foi devido a toda esta tentativa e erro que a sessão do Gemini acabou por ser encerrada por exceder os limites. Vamos examinar o resto destas instruções:

  • Linha 1: Especificamos que a conversa deve ser em francês. Esta instrução destina-se ao DeepSeek, que tendia a falar inglês;
  • Linha 3: o que queremos;
  • Linha 5: Dizemos à IA para usar o PDF que fornecemos;
  • Linhas 7–14: várias dicas úteis, especialmente para o Problema 3 sem o PDF. Várias IA tiveram dificuldades no cálculo dos impostos;
  • Linhas 15–44: os 11 testes unitários que queremos incluir no script gerado. Assim que o script for gerado, vamos executá-lo no PyCharm e verificar se todos os 11 testes são aprovados;
  • Linhas 46–53: sem estas instruções, as IAs gerariam testes unitários à procura de resultados exatos que falhariam;
  • Linhas 55–56: digo à IA para não ir à Internet. A solução mais simples é usar o PDF;
  • Linhas 58–59: A IA não seguiu esta instrução. Tive de a escrever explicitamente num prompt quando reparei que um teste tinha falhado;
  • Linhas 61–65: Especifico que tipo de script Python pretendo;
  • Linhas 67–69: Teria preferido um link para recuperar o script gerado, porque exibir o código no ecrã demora tempo. Acabou por se verificar que a maioria das IAs não consegue fazer isto. Os links fornecidos não funcionaram;
  • Linhas 71–72: Gostaria de saber o tempo que a IA demorou a responder à pergunta. Apenas o Gemini foi capaz de fornecer esta informação. As outras IAs ou não responderam a esta instrução ou forneceram números arbitrários, indicando que não a compreenderam;

4.3.3. Resposta do Gemini

A primeira resposta do Gemini é a seguinte:

 
  • Em [1-4], o Gemini fornece links para a parte do ficheiro PDF ou de texto que contém as instruções que está a utilizar num determinado momento;

O resto é o seguinte:

 
  • Em [1], o Gemini afirma que executou com sucesso todos os 11 testes unitários. A maioria das IAs fez esta afirmação tanto para o Problema 2 como para o Problema 3 e, frequentemente, quando o script gerado era carregado, este não funcionava. Esta afirmação deve, portanto, ser encarada com alguma cautela. No caso do Gemini, porém, isto revelar-se-á verdadeiro;
  • Em [2], um link que acaba por não funcionar;
  • Em [3], apenas o Gemini forneceu um tempo de execução realista;

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

 

Resposta do Gemini:

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

Carregamos este script no PyCharm e executamo-lo:

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

Quando o script é executado, surgem os seguintes 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 utilizado para gerar um tipo específico de texto;

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

 

A resposta do Gemini é então a seguinte:

 

Quando carregado no PyCharm, a execução do novo script gera exatamente o mesmo erro. Informamos isso ao Gemini, fornecendo novamente os registos de execução:

 

A resposta do Gemini é a seguinte:

 

Desta vez funciona. Todos os 11 testes unitários são aprovados. Dizemos ao Gemini:

 

Ao que ele responde:

 

O script gerado pelo Gemini seguiu as instruções fornecidas no ficheiro de texto [instructionsWithPDF.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 ---
#  All values are taken from the PDF document supplied.

#  Ceilings for the 10% allowance on annual income
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

#  2019 income tax scale
#  Structure: (Maximum bracket threshold, Tax rate, Amount to be deducted per share)
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}
]

#  Family allowance ceiling
GAIN_MAX_DEMI_PART = 1551.0  #  Maximum gain per additional half-share

#  Thresholds and amounts for the discount
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

#  Thresholds for the 20% tax reduction
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
    #  The allowance is capped between a minimum and a maximum
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

    #  The statement requires the abatement to be rounded up to the nearest euro
    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

    #  If the QF is greater than all the thresholds, the last slice is used
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

    #  Gross tax calculation formula
    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

    #  Tax calculation without children's shares
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

    #  Calculation of maximum child-related earnings
    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

    #  The tax used is the higher of the two calculations
    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
    #  The discount applies if the tax is below the application threshold
    if impot_avant_decote < seuil_application:
        #  Discount formula: threshold - 0.75 * gross tax
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

        #  Rounded up to the nearest euro
        decote = math.ceil(decote_calculee)

        #  The discount cannot be negative or greater than the tax due
        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]

    #  The threshold is increased for each half-percentage point for children
    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
    #  The reduction applies if taxable income is below the threshold
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

        #  Rounded up to the nearest euro
        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.
    """
    #  Step 1: Calculating taxable income
    revenu_imposable = calculer_revenu_imposable(revenus)

    #  Step 2: Calculating the number of shares
    nombre_parts = calculer_nombre_parts(adultes, enfants)

    #  Step 3: Gross tax calculation
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

    #  Step 4: Applying the QF cap
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

    #  Step 5: Applying the discount
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

    #  Step 6: Apply the 20% discount
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

    #  Step 7: Final tax rounding down to the nearest euro
    impot_final = math.floor(impot_avant_arrondi_final)

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


#  --- TESTS UNITAIRES ---
#  Validation of the script with the cases provided
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)

    #  Example of how to use the calculator for a specific case
    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 aprovados, considero-o «provavelmente correto». Não fiz mais nada ao meu próprio código além de verificar estes 11 testes.

4.4. Problema 3

O Problema 3 é idêntico ao Problema 2, exceto que já não fornecemos à IA o PDF contendo as regras de cálculo a seguir.

A pergunta inicial ao Gemini é a seguinte:

 

O ficheiro de instruções em [1] é quase 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 ao aluno que descubra online as regras para calcular o imposto de 2019 sobre o rendimento de 2018. Este é um exercício mais difícil do que o anterior;

Abaixo, apresento apenas partes da primeira resposta do Gemini:

 
 

O tempo estimado parece plausível. Esperamos há muito tempo pela resposta da Gemini.

Tal como antes, o Gemini forneceu um link para descarregar o script gerado, mas o link não funciona. Dizemos-lhe:

 

Resposta da Gemini:

 

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

 

Executamo-lo e… não funciona. Os registos de 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 à Gemini:

 

Resposta do Gemini:

 
 

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

 

Resposta do Gemini:

 
 
 

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

O código gerado pelo Gemini é o seguinte:

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

# ==============================================================================
#  CONSTANTES POUR L'IMPOSITION 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: If tax is already 0, no discount or reduction.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

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

    #  1. Determining eligibility for the 20% discount
    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. Determining eligibility for the discount
    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: The discount returned cannot exceed the tax it cancels out
        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. Apply the 20% discount if eligible
    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 verifiquei este código. Apenas verifiquei que passou com sucesso nos 11 testes.

Mas pode ser que alguém tenha curiosidade em saber o seu raciocínio, especialmente para casos específicos no cálculo de impostos. Vamos perguntar-lhe:

 

Este é um caso de rendimento elevado, com um possível limite de dedução de 10% e um possível limite do quociente familiar.

A resposta da 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. Este método de cálculo pode, de facto, ser encontrado online. Os dois métodos são equivalentes.

 
 
 

A explicação é extremamente clara. Pode ser apresentada tal como está aos alunos para explicar o método de cálculo dos impostos.

Vamos agora ver outro exemplo, desta vez com rendimentos baixos. Neste caso, poderá haver um crédito fiscal e uma redução:

 

A resposta da Gemini é a seguinte:

 
 
 
 
 

Aqui, vemos que o Gemini aplica uma regra que não consta no PDF. Provavelmente encontrou-a online, mas será que a fonte é fiável?

 

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

 
 
 

Portanto, os resultados do Gemini correspondem aos do simulador fiscal oficial. Mas utilizou uma regra que não consta no PDF. Onde está o erro? Perguntamos ao Gemini, anexando o PDF:

 

Resposta da Gemini:

 
 
 
 

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

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

A resposta da Gemini é a seguinte:

 

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

 

No entanto, veremos que o raciocínio do Gemini realmente conduz aos resultados acima. Vamos continuar:

 
 
 
 

Muito bem. Tomado nota. Vamos continuar:

 
 
 
 

Assim, o Gemini encontrou (imposto, desconto, redução) = (431, 325, 1296), enquanto o simulador que utilizei apresenta (431, 324, 1297). Portanto, o Gemini encontrou os resultados corretos com uma diferença de apenas 1 euro, mas não sabe disso. Nós dizemos-lhe:

 

O Gemini responde:

 
 

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

 

Resposta do Gemini:

 

Então, o Gemini não me forneceu um link para um PDF, mas gerou texto para que eu próprio pudesse criar o PDF. Embora seja complicado incluir capturas de ecrã do PDF aqui, estou a fazê-lo para mostrar aos leitores o aspeto generativo da IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Para ser sincero, não verifiquei se tudo o que está neste PDF é verdade. Seja como for, é um documento perfeito para um tutorial, gerado em apenas alguns segundos.

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

 
  • em [1], incluímos o PDF gerado pelo Gemini [The Problem According to Gemini.pdf];
  • em [2], o ficheiro [instructionsWithPDF2.txt] é idêntico às instruções em [instructionsWithPDF.txt], exceto que adicionámos um décimo segundo teste de unidade — precisamente aquele que revelou que o PDF inicial estava incorreto:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, foram necessárias várias iterações de idas e vindas até que o Gemini gerasse o script correto:

Pergunta 2

 

Pergunta 3

 

Tal como já foi feito várias vezes, quando o script gerado e carregado no PyCharm falha, fornecemos ao Gemini o ficheiro de texto que contém os registos de execução. O Gemini compreende-os muito bem.

Pergunta 4

 

Pergunta 5

 

Pergunta 6 e conclusão

Estamos agora confiantes na validade do PDF gerado pelo Gemini. As regras de cálculo nele apresentadas estão corretas.

Vamos agora fazer o mesmo com as outras cinco IAs, mas manteremos as nossas explicações muito breves, exceto no caso do ChatGPT, o atual líder em IA. O que nos interessa é saber se a IA resolve ou não os três problemas que lhe apresentamos. Na verdade, as interfaces de todas estas IAs são muito semelhantes, e procedi com elas da mesma forma que com o Gemini. Encorajamos os leitores a reproduzirem as conversas do Gemini com a IA da sua escolha.