Skip to content

Latest commit

 

History

History
1229 lines (1015 loc) · 34.4 KB

16.md

File metadata and controls

1229 lines (1015 loc) · 34.4 KB

Exercícios

Foi uma aula de exercícios de arquivos, fazer um programa que conta o número de palavras em um arquivo.

Trabalho 3 - Calculadora de polinômios

parte 1

Um polinômio de uma variável é uma equação que tem a seguinte forma:

y = a0 + a1 x + a2 x² + a3x³ + ... + anxn

A variável do polinômio é x, o grau do polinômio é o maior expoente de x (no exemplo acima, n), os coeficientes do polinômio são os valores a0 a an. Podemos representar um polinômio com um vetor contendo seus coeficientes. Em C, como não temos como saber o tamanho de um vetor, para termos suporte a polinômios de qualquer grau, necessitamos saber também o grau do polinômio.

No nosso caso, representaremos um polinômio como um vetor de double, contendo na posição 0 do vetor um valor inteiro que é o grau do polinômio (n) e nas posições 1 a n+1 os coeficiente, começando com a0. Vamos limitar o grau máximo dos polinômios que trabalharemos a 10, o que significa que qualquer dos nossos polinômios pode ser colocado em um vetor de tamanho 12. As posições não utilizadas de um vetor que armazena um pol;inômio podem conter qualquer valor (não devem sr acessadas). Um polinômio não deve ter grau maior que o necessário (o último valor válido do vetor não pode ser 0 a não ser que o polinômio tenha grau 0).

Implemente funções para:

  • calcular o valor de um polinômio, sabendo o valor de sua variável. A função deve receber o polinômio (um vetor de double, como descrito acima) e o valor de x, e retornar o valor de y.
  • somar dois polinômios. A função deve receber dois polinômios, e alterar o primeiro deles para que contenha a soma dos dois. Deve-se tomar cuidado com a possibilidade de o polinômio resultante ter grau inferior aos dos polinômios que são somados. A função deve retornar um bool com valor true caso a operação seja possível ou false caso o polinômio resultante não possa ser representado (tem grau maior que o máximo permitido ou mais monômios que o máximo permitido).
  • subtrair dois polinômios. A função deve receber dois polinômios, e alterar o primeiro deles para que contenha a subtração dos dois. Deve-se tomar cuidado com a possibilidade de o polinômio resultante ter grau inferior aos dos polinômios que são subtraídos. A função deve retornar um bool com valor true caso a operação seja possível ou false caso o polinômio resultante não possa ser representado (tem grau maior que o máximo permitido ou mais monômios que o máximo permitido).
  • multiplicar 2 polinômios. A função deve receber dois polinômios, e alterar o primeiro deles para que contenha a multiplicação dos dois. Deve-se tomar cuidado com a possibilidade de o polinômio resultante ter grau superior ou inferior aos dos polinômios que são multiplicados. A função deve retornar um bool com valor true caso a multiplicação seja possível ou false caso o polinômio resultante não possa ser representado (tem grau maior que o máximo permitido ou mais monômios que o máximo permitido).

As funções de soma, subtração e multiplicação não devem alterar o segundo polinômio recebido. A função de multiplicação não deve alterar o primeiro polinômio recebido caso retorne false.

Esqueceu como se opera polinômios? Dá uma olhada por exemplo aqui.

Quer um desafio? Faça a divisão.

parte 2

Altere a forma de armazenar um polinômio. Em vez de um vetor com todos os valores dos coeficientes, teremos um vetor com somente os coeficientes que não são zero. Será um vetor de monômios. Cada monômio é definido por dois números, um inteiro que identifiac seu expoente e um double que tem o vaor do coeficiente. Além desse vetor, o polinômio tem também um inteiro, contendo o número de monômios que o representam (o número de elementos no vetor).

Resumindo, um polinômio passa a ser um registro que contém um inteiro e um vetor de monômios; um monômio é um registro que contém um inteiro e um double.

A tabela abaixo apresenta alguns exemplos de polinômios e como seriam armazenados.

Polinômio n e0 v0 e1 v1 e2 v2 e3 v3 e4 v4 e5 v5 ...
1 +2 +1
2x³ 1 +3 +2
-3x² - 3 2 +2 -3 +0 -3
x⁴-3x²+x 3 +4 +1 +2 -3 +1 +1

Reimplemente as funções da parte 1 com a nova forma de representar polinômios.

Implemente um programa que é uma calculadora de polinômios, que deve funcionar como descrito a seguir. A calculadora tem um polinômio chamado acumulador, um valor x e um valor y, que estão sempre sendo mostrado. Eles são inicializados com 0. A calculadora fica em um laço, em que calcula o valor de y (é o valor do polinômio acumulador para o valor atual de x), mostra os valores do acumulador, x e y, pede uma operação para o usuário, executa essa operação, e, se a operação não for terminar o programa, volta a repetir o laço. As operações são:

  • alterar o valor de x (pede um valor para o usuário e o atribui a x);
  • altera o valor do polinômio (pede um polinômio ao usuário e altera o valor do acumulador);
  • soma polinômios (pede um polinômio ao usuário, soma-o ao acumulador, e esse passa a ser o novo valor do acumulador);
  • subtrai polinômios (como a soma);
  • multiplica polinômios (como a soma);
  • termina o programa.

Deve ter uma função para ler um polinômio; essa função (e aquelas descritas na parte I) devem ser usadas para implementar as operações da calculadora.

Possíveis soluções

Nosso monitor (o Marcos Visentini) nos brindou com uma impleentação para os programas pedidos no trabalho.

/*
 * Arquivo:   t3-pt-1-solucao.c
 * Descrição: O arquivo contém uma das possíveis soluções para a primeira parte do trabalho 3.
 * Autor:     Marcos Visentini
 * Contato:   mvisentini@inf.ufsm.br
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define TAM_MAX_VET 12
#define GRAU_MAX 10
#define GRAU_MIN 0

/*
 * Recebe dois vetores do tipo `double`, representando polinômios.
 * Copia o conteúdo do vetor fonte para o vetor destino.
 */
void copia_vet(double *vet_dest, double *vet_src)
{
    int vet_dest_tam = vet_src[0] + 2;

    for (int i = 0; i < vet_dest_tam; i++) {
        vet_dest[i] = vet_src[i];
    }
}

/*
 * Recebe um inteiro que representa o grau de um polinômio.
 * Retorna `true` caso o grau recebido seja válido (maior que 0 e menor ou
 * igual a 10); caso contrário, retorna `false`.
 */
bool grau_eh_valido(int grau)
{
    if (grau < GRAU_MIN) {
        printf("\nErro: o grau do polinômio deve ser no mínimo %d.\n", GRAU_MIN);
    } else if (grau > GRAU_MAX) {
        printf("\nErro: o grau do polinômio deve ser no máximo %d.\n", GRAU_MAX);
    } else {
        return true;
    }

    return false;
}

/*
 * Lê e retorna um grau válido informado pelo usuário.
 */
int le_grau(void)
{
    int grau;

    while (true) {
        printf("\nInforme o grau do polinômio a ser lido [0, 10]: ");
        scanf(" %d", &grau);

        if (grau_eh_valido(grau)) break;

        printf("Por favor, tente novamente.\n");
    }

    return grau;
}

/*
 * Recebe dois inteiros, representando o grau e o expoente de
 * um polinômio, respectivamente.
 * Lê e retorna um elemento válido para `x` elevado ao expoente informado.
 */
double le_elemento(int grau, int exp)
{
    double elem;

    while (true) {
        printf("\nDigite o coeficiente para x^%d: ", exp);
        scanf(" %lf", &elem);

        if (elem == 0 && grau != 0) {
            printf("\nErro: um polinômio de grau %d ", grau);
            printf("não pode ter 0 como coeficiente.");
            printf("\nPor favor, tente novamente.\n");
            continue;
        }

        break;
    }

    return elem;
}

/*
 * Recebe um vetor do tipo `double` que representa um polinômio e um inteiro
 * que identifica qual polinômio será manipulado na função.
 * Lê e armazena monômios válidos para o polinômio recebido.
 */
void le_polinomio(double *p, int id)
{
    printf("\n\nLeitura do %dº polinômio...\n", id);

    int grau = le_grau();

    p[0] = grau;

    for (int i = 1; i <= grau + 1; i++) {
        p[i] = le_elemento(grau, i - 1);
    }
}

/*
 * Recebe um valor do tipo `double` que representa o coeficiente de um
 * monômio e um inteiro que representa o expoente desse mesmo monômio.
 * Imprime o monômio.
 */
void imprime_monomio(double coef, int exp)
{
    if (coef == 0) return;
    
    printf("%.2lf", coef);

    if (exp != 0) {
        printf("x");

        if (exp != 1 && exp != -1) {
            printf("^%d", exp);
        }
    }
}

/*
 * Recebe um vetor do tipo `double`, representando um polinômio.
 * Imprime o polinômio.
 */
void imprime_polinomio(double *p)
{
    printf("(");

    if (p[0] == 0) {
        printf(")\n\n");
        return;
    }

    int tam_vet_p = p[0] + 2;

    for (int i = tam_vet_p - 1; i > 1; i--) {
        if (p[i] == 0) continue;

        imprime_monomio(p[i], i - 1);

        if (p[i - 1] != 0) printf(" + ");
    }
    
    imprime_monomio(p[1], 0);
    printf(")");
}

/*
 * Lê e retorna um valor do tipo `double` para `x` informado pelo usuário.
 */
double le_x(void)
{
    double x;

    printf("\nInforme um valor para X: ");
    scanf(" %lf", &x);

    return x;
}

/*
 * Recebe um vetor do tipo `double` que representa um polinômio e um
 * `double` que representa um valor para `x` a ser substituído no polinômio.
 * Calcula e retorna o valor do polinômio para o valor de `x` recebido.
 */
double calcula_polinomio(double *p, double x)
{
    int tam_vet_p = p[0] + 2;

    double y = 0;

    for (int i = 1; i < tam_vet_p; i++) {
        y += p[i] * pow(x, i - 1);
    }

    return y;
}

/*
 * Recebe dois vetores do tipo `double`.
 * Retorna o tamanho do maior vetor entre os recebidos.
 */
int tamanho_do_maior_vet(double *v1, double *v2)
{
    if (v1[0] >= v2[0]) {
        return v1[0] + 2;
    } else {
        return v2[0] + 2;
    }
}

/*
 * Recebe dois vetores do tipo `double`, representando polinômios.
 * Altera o primeiro polinômio recebido (`p1`) para que contenha
 * a soma dele com o segundo polinômio recebido (`p2`).
 * Retorna `true` caso seja possível realizar a operação; caso
 * contrário, `false`.
 */
bool soma_polinomios(double *p1, double *p2)
{
    if (!grau_eh_valido(p1[0]) || !grau_eh_valido(p2[0])) return false;

    int tam = tamanho_do_maior_vet(p1, p2);

    p1[0] = tam - 2;

    for (int i = 1; i < tam; i++) {
        p1[i] += p2[i];
    }

    return true;
}

/*
 * Recebe dois vetores do tipo `double`, representando polinômios.
 * Altera o primeiro polinômio recebido (`p1`) para que contenha
 * a subtração dele com o segundo polinômio recebido (`p2`).
 * Retorna `true` caso seja possível realizar a operação; caso
 * contrário, `false`.
 */
bool subtrai_polinomios(double *p1, double *p2)
{
    if (!grau_eh_valido(p1[0]) || !grau_eh_valido(p2[0])) return false;

    int tam = tamanho_do_maior_vet(p1, p2);

    p1[0] = tam - 2;

    for (int i = 1; i < tam; i++) {
        p1[i] -= p2[i];
    }

    return true;
}

/*
 * Recebe dois vetores do tipo `double`, representando polinômios.
 * Altera o primeiro polinômio recebido (`p1`) para que contenha
 * a multiplicação dele com o segundo polinômio recebido (`p2`).
 * Retorna `true` caso seja possível realizar a operação; caso
 * contrário, `false`.
 */
bool multiplica_polinomios(double *p1, double *p2)
{
    if (!grau_eh_valido(p1[0])) return false;
    if (!grau_eh_valido(p2[0])) return false;
    
    if (p1[0] + p2[0] > GRAU_MAX) {
        printf("\nErro: o grau do polinômio resultante não pode ultrapassar %d.\n", GRAU_MAX);
        return false;
    }

    int tam_vet_p1 = p1[0] + 2;
    int tam_vet_p2 = p2[0] + 2;

    double p_aux[TAM_MAX_VET] = {0};
    p_aux[0] = p1[0] + p2[0];
    
    for (int i = 1; i < tam_vet_p1; i++) {
        for (int j = 1; j < tam_vet_p2; j++) {
            int grau = i + j - 1;
            p_aux[grau] += p1[i] * p2[j];
        }
    }

    copia_vet(p1, p_aux);

    return true;
}

/*
 * Pede ao usuário se ele realmente deseja sair do programa.
 * Caso positivo, é retornado `true`; caso contrário, `false`.
 */
bool confirma_saida(void)
{
    bool quer_sair = false;

    while (true) {
        char opcao;

        printf("\nVocê deseja mesmo sair do programa? [s/n]\n");
        scanf("\n%c", &opcao);

        if (opcao == 's' || opcao == 'S') {
            printf("\nVocê saiu do programa.\n\n");
            quer_sair = true;
        } else if (opcao == 'n' || opcao == 'N') {
            printf("\nVocê não saiu do programa.\n");
            quer_sair = false;
        } else {
            printf("\nOpção inválida.");
            printf("\nPor favor, tente novamente.\n\n");
            continue;
        }

        break;
    }

    return quer_sair;
}

/*
 * Cria um polinômio "zerado", lê valores para ele, lê um valor para `x` e
 * calcula o valor do polinômio para o valor de `x`lido do usuário.
 * O resultado é impresso na tela.
 */
void opcao_calculo_polinomio(void)
{
    double p1[TAM_MAX_VET] = {0};

    le_polinomio(p1, 1);

    double x = le_x();
    double y = calcula_polinomio(p1, x);

    printf("\nY = %.2lf\n", y);
}

/*
 * Cria dois polinômios "zerados", lê valores para ambos e opera
 * a soma entre os dois. Se a soma for possível, o resultado é
 * impresso; senão, uma mensagem de erro é impressa.
 */
void opcao_soma(void)
{
    double p1[TAM_MAX_VET] = {0};
    double p2[TAM_MAX_VET] = {0};

    le_polinomio(p1, 1);
    le_polinomio(p2, 2);

    if (soma_polinomios(p1, p2)) {
        printf("\nResultado da soma: ");
        imprime_polinomio(p1);
    } else {
        printf("\nNão foi possível somar os polinômios.\n");
    }
}

/*
 * Cria dois polinômios "zerados", lê valores para ambos e opera
 * a subtração entre os dois. Se a subtração for possível, o resultado é
 * impresso; senão, uma mensagem de erro é impressa.
 */
void opcao_subtracao(void)
{
    double p1[TAM_MAX_VET] = {0};
    double p2[TAM_MAX_VET] = {0};

    le_polinomio(p1, 1);
    le_polinomio(p2, 2);

    if (subtrai_polinomios(p1, p2)) {
        printf("\nResultado da subtração: ");
        imprime_polinomio(p1);
    } else {
        printf("\nNão foi possível subtrair os polinômios.\n");
    }
}

/*
 * Cria dois polinômios "zerados", lê valores para ambos e opera
 * a multiplicação entre os dois. Se a multiplicação for possível, o resultado
 * é impresso; senão, uma mensagem de erro é impressa.
 */
void opcao_multiplicacao(void)
{
    double p1[TAM_MAX_VET] = {0};
    double p2[TAM_MAX_VET] = {0};

    le_polinomio(p1, 1);
    le_polinomio(p2, 2);

    if (multiplica_polinomios(p1, p2)) {
        printf("\nResultado da multiplicação: ");
        imprime_polinomio(p1);
    } else {
        printf("\nNão foi possível multiplicar os polinômios.\n");
    }
}

/*
 * Recebe um inteiro que representa a opção do menu lida do usuário.
 * Interpreta essa opção e realiza a operação necessária.
 * Se a opção for sair do jogo, é retornado `true`; senão, `false`.
 */
bool interpreta_opcao(int opcao)
{
    switch (opcao) {
        case 0:
            opcao_calculo_polinomio();
            break;
        case 1:
            opcao_soma();
            break;
        case 2:
            opcao_subtracao();
            break;
        case 3:
            opcao_multiplicacao();
            break;
        case 4:
            return confirma_saida();
        default:
            break;
    }

    return false;
}

/*
 * Imprime o menu de opções.
 */
void imprime_menu(void)
{
    printf("\n");
    printf("\n╔════════════════MENU════════════════╗");
    printf("\n║════╦═══════════════════════════════║");
    printf("\n║ ID ║            OPERAÇÃO           ║");
    printf("\n╠════╬═══════════════════════════════╣");
    printf("\n║ 0  ║ Calcular o valor do polinômio ║");
    printf("\n║ 1  ║ Somar polinômios              ║");
    printf("\n║ 2  ║ Subtrair polinômios           ║");
    printf("\n║ 3  ║ Multiplicar polinômios        ║");
    printf("\n║ 4  ║ Sair do programa              ║");
    printf("\n╚════╩═══════════════════════════════╝\n");
}

/*
 * Lê e retorna um inteiro que representa
 * a opção do menu escolhida pelo usuário.
 */
int le_opcao(void)
{
    int opcao;

    while (true) {
        printf("\nInforme o ID correspondente à operação desejada: ");
        scanf(" %d", &opcao);

        if (opcao < 0 || opcao > 4) {
            printf("\nO ID informado não corresponde a nenhuma operação válida.");
            printf("\nPor favor, tente novamente.\n");
            continue;
        }

        break;
    }

    return opcao;
}

int main(void)
{
   bool quer_sair;

    do {
        imprime_menu();
        int opcao = le_opcao();
        quer_sair = interpreta_opcao(opcao);
    } while (!quer_sair);

    return 0;
}
/*
 * Arquivo:   t3-pt-2-solucao.c
 * Descrição: O arquivo contém uma das possíveis soluções para a segunda parte do trabalho 3.
 * Autor:     Marcos Visentini
 * Contato:   mvisentini@inf.ufsm.br
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define NUM_MON_PADRAO 11  // Número máximo de monômios que podem ser representados.
#define GRAU_MAX       10  // Grau máximo admitido para um polinômio.

typedef struct Monomio {
    int exp;               // Exponente do monômio.
    double val;            // Valor do coeficiente do monômio.
} Monomio;

typedef struct Polinomio {
    int n_monomios;        // Número de monômios não nulos que compõem o polinômio.
    Monomio *vet;          // Vetor de monômios que representa um polinômio.
} Polinomio;

typedef struct Calculadora {
    int x;                 // Valor para substituir no polinômio.
    int y;                 // Resultado da substituição e cálculo de `x` para o polinômio acumulador.
    Polinomio acum;        // Polinômio acumulador.
} Calculadora;

/*
 * Recebe um tamanho.
 * Aloca espaço na memória para um vetor de monômios de tamanho informado.
 * Retorna um ponteiro para a região alocada.
 */
Monomio *aloca_vet_monomios(int tam)
{
    Monomio *vet_monomios = calloc(tam, sizeof (Monomio));

    if (vet_monomios == NULL) {
        printf("\nOcorreu algum erro durante a manipulação de memória.");
        printf("\nO programa será abortado.\n\n");
        exit(EXIT_FAILURE);
    }

    return vet_monomios;
}

/*
 * Recebe um vetor de monômios e um tamanho.
 * Realoca espaço na memória para o vetor recebido de novo tamanho.
 * Retorna um ponteiro para a região realocada.
 */
Monomio *realoca_vet_monomios(Monomio *vet_src, int tam_vet_dest)
{
    Monomio *vet_dest = realloc(vet_src, tam_vet_dest * sizeof (Monomio));

    if (vet_dest == NULL) {
        printf("\nOcorreu algum erro durante a manipulação de memória.");
        printf("\nO programa será abortado.\n\n");
        exit(EXIT_FAILURE);
    }

    return vet_dest;
}

/*
 * Recebe um vetor de monômios e o tamanho desse vetor.
 * Inicializa os campos dos monômios do vetor com 0.
 */
void inicializa_vet_monomios(Monomio *vet, int tam)
{
    for (int i = 0; i < tam; i++) {
        vet[i].exp = 0;
        vet[i].val = 0;
    }
}

/*
 * Recebe um tamanho.
 * Cria e retorna um vetor de monômios de tamanho informado.
 */
Monomio *cria_vet_monomios(int tam)
{
    Monomio *vet_monomios = aloca_vet_monomios(tam);
    inicializa_vet_monomios(vet_monomios, tam);
    return vet_monomios;
}

/*
 * Recebe um vetor de monômios.
 * Libera a memória alocada para o vetor de monômios.
 */
void destroi_vet_monomios(Monomio *vet)
{
    if (vet != NULL) free(vet);
}

/*
 * Recebe um polinômio e o número máximo de monômios admitidos por ele.
 * Inicializa os campos do polinômio recebido.
 */
void inicializa_polinomio(Polinomio *p, int max_n_monomios)
{
    p->n_monomios = 0;
    p->vet = cria_vet_monomios(max_n_monomios);
}

/*
 * Recebe um monômio.
 * Imprime o monômio.
 */
void imprime_monomio(Monomio m)
{
    if (m.val == 0) return;
    
    printf("%.2lf", m.val);

    if (m.exp != 0) {
        printf("x");

        if (m.exp != 1 && m.exp != -1) {
            printf("^%d", m.exp);
        }
    }
}

/*
 * Recebe um polinômio.
 * Imprime o polinômio.
 */
void imprime_polinomio(Polinomio p)
{
    printf("(");

    if (p.n_monomios == 0) {
        printf(")");
        return;
    }

    for (int i = 0; i < p.n_monomios - 1; i++) {
        imprime_monomio(p.vet[i]);
        printf(" + ");
    }
    
    imprime_monomio(p.vet[p.n_monomios - 1]);
    printf(")");
}

/*
 * Recebe uma calculadora e o número máximo de
 * monômios admitidos pelo polinômio acumulador.
 * Inicializa os campos da calculadora.
 */
void inicializa_calculadora(Calculadora *c, int max_n_monomios)
{
    c->x = 0;
    c->y = 0;
    inicializa_polinomio(&c->acum, max_n_monomios);
}

/*
 * Recebe uma calculadora.
 * Imprime os campos da calculadora.
 */
void imprime_calculadora(Calculadora c)
{
    printf("\n         X = %d", c.x);
    printf("\n         Y = %d", c.y);
    printf("\nAcumulador = ");
    imprime_polinomio(c.acum);
    printf("\n");
}

/*
 * Imprime o menu de opções.
 */
void imprime_menu(void)
{
    printf("\n");
    printf("\n╔════════════════MENU════════════════╗");
    printf("\n║════╦═══════════════════════════════║");
    printf("\n║ ID ║            OPERAÇÃO           ║");
    printf("\n╠════╬═══════════════════════════════╣");
    printf("\n║ 0  ║ Alterar o valor de X          ║");
    printf("\n║ 1  ║ Alterar o valor do acumulador ║");
    printf("\n║ 2  ║ Somar polinômios              ║");
    printf("\n║ 3  ║ Subtrair polinômios           ║");
    printf("\n║ 4  ║ Multiplicar polinômios        ║");
    printf("\n║ 5  ║ Sair do programa              ║");
    printf("\n╚════╩═══════════════════════════════╝\n");
}

/*
 * Lê e retorna um inteiro que representa
 * a opção do menu escolhida pelo usuário.
 */
int le_opcao(void)
{
    int opcao;

    while (true) {
        printf("\nInforme o ID correspondente à operação desejada: ");
        scanf(" %d", &opcao);

        if (opcao < 0 || opcao > 5) {
            printf("\nO ID informado não corresponde a nenhuma operação válida.");
            printf("\nPor favor, tente novamente.\n");
            continue;
        }

        break;
    }

    return opcao;
}

/*
 * Pede ao usuário se ele realmente deseja sair do programa.
 * Caso positivo, é retornado `true`; caso contrário, `false`.
 */
bool confirma_saida(void)
{
    bool quer_sair = false;

    while (true) {
        char opcao;

        printf("\nVocê deseja mesmo sair do programa? [s/n]\n");
        scanf("\n%c", &opcao);

        if (opcao == 's' || opcao == 'S') {
            printf("\nVocê saiu do programa.\n\n");
            quer_sair = true;
        } else if (opcao == 'n' || opcao == 'N') {
            printf("\nVocê não saiu do programa.\n");
            quer_sair = false;
        } else {
            printf("\nOpção inválida.");
            printf("\nPor favor, tente novamente.\n\n");
            continue;
        }

        break;
    }

    return quer_sair;
}

/*
 * Imprime uma mensagem informando que o polinômio acumulador está vazio.
 */
void polinomio_vazio_msg(void)
{
    printf("\nA operação solicitada não pôde ser executada");
    printf("\npois o polinômio acumulador se encontra vazio.\n");
}

/*
 * Recebe um polinômio.
 * Retorna um valor booleano que indica se o polinômio em questão está vazio.
 */
bool polinomio_ta_vazio(Polinomio p)
{
    return p.n_monomios == 0;
}

/*
 * Recebe dois monômios.
 * Realiza a troca do primeiro pelo segundo.
 */
void troca_monomios(Monomio *a, Monomio *b)
{
    Monomio aux = *a;
    *a = *b;
    *b = aux;
}

/*
 * Recebe um polinômio.
 * Ordena de forma decrescente (com base no expoente) os monômios do polinômio.
 */
void ordena_polinomio(Polinomio *p)
{
    for (int i = 0; i < p->n_monomios - 1; i++) {
        for (int j = i + 1; j < p->n_monomios; j++) {
            if (p->vet[i].exp < p->vet[j].exp) {
                troca_monomios(&p->vet[i], &p->vet[j]);
            } else if (p->vet[i].exp == p->vet[j].exp) {
                if (p->vet[i].val < p->vet[j].val) {
                    troca_monomios(&p->vet[i], &p->vet[j]);
                }
            }
        }
    }
}

/*
 * Lê e retorna um valor do tipo `double` para `x` informado pelo usuário.
 */
int le_x(void)
{
    int x;

    printf("\nInforme um valor para X: ");
    scanf(" %d", &x);

    return x;
}

/*
 * Lê e retorna o número de monômios de um polinômio informado pelo usuário.
 */
int le_n_monomios(void)
{
    int n_monomios;

    while (true) {
        printf("\nQuantos monômios o polinômio terá? [1, %d]\n", NUM_MON_PADRAO);
        scanf("\n%d", &n_monomios);

        if (n_monomios < 1) {
            printf("\nErro: o número de monômios deve ser no mínimo 1.\n");
        } else if (n_monomios > NUM_MON_PADRAO) {
            printf("\nErro: o número de monômios deve ser no máximo %d.\n", NUM_MON_PADRAO);
        } else {
            break;
        }

        printf("\nPor favor, tente novamente.\n");
    }

    return n_monomios;
}

/*
 * Lê e retorna o expoente de um monômio informado pelo usuário.
 */
int le_exp(int n)
{
    int exp;

    printf("\n\nInforme o expoente do %dº monômio: ", n);
    scanf(" %d", &exp);

    return exp;
}

/*
 * Lê e retorna o coeficiente de um monômio informado pelo usuário.
 */
int le_val(int n)
{
    int val;
    
    printf("\nInforme o valor do coeficiente do %dº monômio: ", n);
    scanf(" %d", &val);

    return val;
}

/*
 * Recebe um polinômio.
 * Lê e armazena monômios válidos para o polinômio recebido.
 */
void le_polinomio(Polinomio *p)
{
    int n_mon = le_n_monomios();

    for (int i = 0; i < n_mon; i++) {
        p->vet[i].exp = le_exp(i + 1);
        p->vet[i].val = le_val(i + 1);
    }

    p->n_monomios = n_mon;
}

/*
 * Recebe um polinômio.
 * Desloca todos os monômios que não possuem coeficiente 0 para
 * a esquerda, fazendo com que não haja espaços
 * vazios entre os monômios do vetor.
 */
void desloca_monomios_nao_nulos(Polinomio *p)
{
    int j = 0;

    for (int i = 0; i < p->n_monomios; i++) {
        if (p->vet[i].val != 0) {
            p->vet[j].exp = p->vet[i].exp;
            p->vet[j].val = p->vet[i].val;
            j++;
        }

    }

    p->n_monomios = j;
}

/*
 * Recebe um polinômio.
 * Reduz os monômios com mesmo expoente, somando seus coeficientes e zerando
 * o segundo monômio. Depois que a redução é realizada, é chamada a função de
 * deslocação para retirar os espaços vazios entre os monômios.
 */
void reduz_polinomio(Polinomio *p)
{
    for (int i = 0; i < p->n_monomios - 1; i++) {
        for (int j = i + 1; j < p->n_monomios; j++) {
            if (p->vet[i].val == 0) continue;

            if (p->vet[i].exp == p->vet[j].exp) {
                p->vet[i].val += p->vet[j].val;
                p->vet[j].val = 0;
                p->vet[j].exp = 0;
            }
        }
    }

    desloca_monomios_nao_nulos(p);
}

/*
 * Recebe dois polinômios.
 * Copia o conteúdo do polinômio fonte para o polinômio destino.
 */
void copia_polinomio(Polinomio *p_dest, Polinomio *p_src)
{
    for (int i = 0; i < p_dest->n_monomios; i++) {
        p_dest->vet[i].exp = p_src->vet[i].exp;
        p_dest->vet[i].val = p_src->vet[i].val;
    }
}

/*
 * Imprime uma mensagem detalhando a operação que está sendo feita.
 */
void operacao_msg(Polinomio p1, Polinomio p2, char *msg, char op)
{
    printf("\n\n%s: ", msg);
    imprime_polinomio(p1);
    printf(" %c ", op);
    imprime_polinomio(p2);
    printf("\n");
}

/*
 * Imprime uma mensagem informando que o número de monômios resultante
 * de uma operação é inválido e não podem ser representados no vetor de monômios.
 */
void n_monomios_invalido_msg(void)
{
    printf("\nO número de monômios resultante da operação sobre os dois polinômios");
    printf("\ninformados ultrapassa %d, ou seja, não podem ser representados.\n", NUM_MON_PADRAO);
}

/*
 * Recebe um polinômio.
 * Retorna um valor booleano que indica se o número de monômios do
 * polinômio recebido é passível de ser representado.
 */
bool n_monomios_resultante_eh_valido(Polinomio p)
{
    if (p.n_monomios > NUM_MON_PADRAO) {
        n_monomios_invalido_msg();
        return false;
    }

    return true;
}

/*
 * Realiza as operações de soma ou subtração entre os dois polinômios recebidos.
 * Se o número de monômios resultante for maior que o número de monômios
 * permitido em um polinômio (11), o programa exibe uma mensagem de erro,
 * o resultado não é copiado para o acumulador e o programa retorna `false`;
 * senão, copia o resultado para o acumulador e retorna `true`.
 */
bool opera_soma_ou_subtracao_de_polinomios(Polinomio *acum, Polinomio *p)
{
    int p_aux_n_monomios = acum->n_monomios + p->n_monomios;

    Polinomio p_aux;
    inicializa_polinomio(&p_aux, p_aux_n_monomios);
    p_aux.n_monomios = p_aux_n_monomios;

    for (int i = 0; i < acum->n_monomios; i++) {
        p_aux.vet[i].exp = acum->vet[i].exp;
        p_aux.vet[i].val = acum->vet[i].val;
    }

    for (int i = 0; i < p->n_monomios; i++) {
        p_aux.vet[i + acum->n_monomios].exp = p->vet[i].exp;
        p_aux.vet[i + acum->n_monomios].val = p->vet[i].val;
    }

    reduz_polinomio(&p_aux);

    if (!n_monomios_resultante_eh_valido(p_aux)) return false;

    acum->vet = realoca_vet_monomios(acum->vet, p_aux.n_monomios);
    acum->n_monomios = p_aux.n_monomios;

    copia_polinomio(acum, &p_aux);
    destroi_vet_monomios(p_aux.vet);

    return true;
}

/*
 * Realiza a operação de multiplicação entre os dois polinômios recebidos.
 * Se o número de monômios resultante for maior que o número de monômios
 * permitido em um polinômio (11), o programa exibe uma mensagem de erro,
 * o resultado não é copiado para o acumulador e o programa retorna `false`;
 * senão, copia o resultado para o acumulador e retorna `true`.
 */
bool opera_multiplicacao_de_polinomios(Polinomio *acum, Polinomio *p)
{
    Polinomio p_aux;
    int p_aux_n_monomios = acum->n_monomios * p->n_monomios;
    inicializa_polinomio(&p_aux, p_aux_n_monomios);
    p_aux.n_monomios = p_aux_n_monomios;

    int k = 0;

    for (int i = 0; i < acum->n_monomios; i++) {
        for (int j = 0; j < p->n_monomios; j++) {
            int grau = acum->vet[i].exp + p->vet[j].exp;
            p_aux.vet[k].exp = grau;
            p_aux.vet[k].val = acum->vet[i].val * p->vet[j].val;
            k++;
        }
    }

    reduz_polinomio(&p_aux);

    if (!n_monomios_resultante_eh_valido(p_aux)) return false;

    acum->vet = realoca_vet_monomios(acum->vet, p_aux.n_monomios);
    acum->n_monomios = p_aux.n_monomios;

    copia_polinomio(acum, &p_aux);
    destroi_vet_monomios(p_aux.vet);

    return true;
}

/*
 * Recebe um polinômio.
 * Inverte o sinal do coeficiente de cada monômio do polinômio recebido.
 */
void nega_polinomio(Polinomio *p)
{
    for (int i = 0; i < p->n_monomios; i++) {
        if (p->vet[i].val == 0) continue;

        p->vet[i].val = -p->vet[i].val;
    }
}

/*
 * Recebe o polinômio acumulador da calculadora e um caractere
 * que representa qual operação deve ser feita.
 * Com base no caractere, é realizada a operação.
 */
void operacao(Polinomio *acum, char op)
{
    if (polinomio_ta_vazio(*acum)) {
        polinomio_vazio_msg();
        return;
    }

    Polinomio p;
    inicializa_polinomio(&p, NUM_MON_PADRAO);
    le_polinomio(&p);
    ordena_polinomio(&p);

    switch (op) {
        case '+':
        case '-': {
            char *msg = (op == '+') ? "SOMANDO" : "SUBTRAINDO";
            if (op == '-') nega_polinomio(&p);
            operacao_msg(*acum, p, msg, '+');
            if (!opera_soma_ou_subtracao_de_polinomios(acum, &p)) {
                printf("\nNão foi possível realizar a");
                printf("\noperação entre os dois polinômios.\n");
            }
            break;
        }
        case '*':
            operacao_msg(*acum, p, "MULTIPLICANDO", '*');
            if (!opera_multiplicacao_de_polinomios(acum, &p)) {
                printf("\nNão foi possível realizar a operação de");
                printf("\nmultiplicação entre os dois polinômios.\n");
            }
            break;
        default:
            printf("\nErro: operação desconhecida.\n");
            printf("\nPor favor, tente novamente.\n");
            break;
    }

    destroi_vet_monomios(p.vet);
}

/*
 * Recebe uma calculadora.
 * Calcula e armazena na calculadora o valor do polinômio acumulador
 * para o valor atual de `x` contido na calculadora.
 */
void calcula_y(Calculadora *c)
{
    int y = 0;

    for (int i = 0; i < c->acum.n_monomios; i++) {
        double val = c->acum.vet[i].val;
        int exp = c->acum.vet[i].exp;

        y += val * pow(c->x, exp);
    }

    c->y = y;
}

/*
 * Recebe uma calculadora
 * Lê e interpreta uma opção do menu lida do usuário e realiza a operação necessária.
 * Se a opção for sair do jogo, é retornado `true`; senão, `false`.
 */
bool operacoes(Calculadora *c)
{
    int opcao = le_opcao();

    switch (opcao) {
        case 0:
            c->x = le_x();
            break;
        case 1:
            le_polinomio(&c->acum);
            break;
        case 2: {
            operacao(&c->acum, '+');
            break;
        }
        case 3: {
            operacao(&c->acum, '-');
            break;
        }
        case 4:
            operacao(&c->acum, '*');
            break;
        case 5:
            return confirma_saida();
        default:
            break;
    }

    ordena_polinomio(&c->acum);
    return false;
}

int main(void)
{
    Calculadora calc;
    inicializa_calculadora(&calc, NUM_MON_PADRAO);

    bool quer_sair;

    do {
        calcula_y(&calc);
        imprime_menu();
        imprime_calculadora(calc);
        quer_sair = operacoes(&calc);
    } while (!quer_sair);

    destroi_vet_monomios(calc.acum.vet);

    return 0;
}