Polinômios esparsos podem ser representados eficientemente por meio de
listas encadeadas. Neste exercício-programa você escreverá uma
biblioteca que usa listas encadeadas para implementar o tipo abstrato de
dados "polinomio". A interface da sua biblioteca deve ser o arquivo
polinomio.h
, cujo conteúdo é o seguinte:
typedef struct termo *Polinomio; Polinomio cria_monomio(double coef, int exp); void soma(Polinomio p, Polinomio q); void subtrai(Polinomio p, Polinomio q); void multiplica(Polinomio p, Polinomio q); Polinomio deriva(Polinomio p); int grau(Polinomio p); double calcula(Polinomio p, double x); Polinomio duplica(Polinomio p); void imprime(Polinomio p); void libera(Polinomio p);
Note que o arquivo polinomio.h
especifica o que se pode fazer com
um Polinomio
(isto é, as operações sobre polinômios), mas não
informa como um Polinomio
é implementado. O tipo
Polinomio
é definido como um apontador para uma estrutura cuja
definição não é parte da interface da biblioteca. (A definição da estrutura
termo
não aparece no arquivo polinomio.h
.)
Abaixo descrevemos o que deve fazer cada uma das funções da biblioteca:
Polinomio cria_monomio(double coef, int exp);
coef
e cujo expoente é exp
e devolve o monômio
criado. Caso o parâmetro coef
seja igual a zero, esta
função cria um polinômio nulo (um polinômio que não possui nenhum
termo com coeficiente não nulo) e devolve o polinômio nulo criado.void soma(Polinomio p, Polinomio q);
p
e q
, e devolve em
p
a soma de p
e q
.void subtrai(Polinomio p, Polinomio q);
p
e q
, e devolve em
p
a diferença entre p
e q
, ou
seja, p-q
.void multiplica(Polinomio p, Polinomio q);
p
e q
, e devolve em
p
o produto de p
e q
.Polinomio deriva(Polinomio p);
p
e devolve como valor da função um
novo polinômio que é a derivada de p
.int grau(Polinomio p);
p
e devolve o grau do polinômio.double calcula(Polinomio p, double x);
p
e um real x
e devolve o
valor do polinômio p
em x
.Polinomio duplica(Polinomio p);
p
e devolve um novo polinômio que é
uma cópia de p
.void imprime(Polinomio p);
p
e o imprime.void libera (Polinomio p);
p
.
Todos os polinômios devolvidos por essas funções são alocados dinâmicamente e
devem ser liberados (mediante chamadas à funcão libera
) quando
não forem ser mais utilizados. Por exemplo: se uma variável v
referenciar um Polinomio
, é um erro deixar que v
saia de escopo sem antes executar libera(v)
.
As funções soma
, subtrai
e multiplica
destroem o Polinomio
originalmente referenciado pelo primeiro
parâmetro (o polinômio p
) e liberam a memória ocupada por aquele
polinômio. Caso seja necessário preservar uma cópia desse polinômio, tal cópia
deve ser obtida com uma chamada à função duplica
:
Polinomio p, q, p_salvo; p = ...; q = ...; p_salvo = duplica(p); soma(p, q); /* destrói o valor original de p */
É importante entender que um cliente da biblioteca precisa chamar
funções da biblioteca para obter polinômios. O cliente não tem como "fabricar"
polinômios diretamente. As funções cria_monomio
,
soma
, subtrai
, multiplica
,
deriva
e duplica
criam novos polinômios. No caso da
função duplica
, o Polinomio
criado é uma "cópia
funda" (deep copy) de um já existente.
Cada polinômio é armazenado em uma lista ligada com cabeça de
lista (para facilitar a manipulação). Cada célula da lista é do tipo
struct termo
e armazena os dados de um dos termos do
polinômio: o coeficiente e o expoente, além do apontador para o
próximo termo. Apenas termos com coeficiente não-nulo devem estar
na lista e, para que a manipulação seja mais eficiente, os termos
devem aparecer na lista em ordem crescente do expoente.
Exemplo 1: O polinômio p(x)=-x5+2x3+5x2-7 fica armazenado em uma lista de acordo com a seguinte figura.
|
|
|
|
|
onde prox
representa um apontador para a célula seguinte na
figura e a primeira célula é a cabeça da lista. Note a ausência dos
termos correspondentes a x4
e x
, que
neste polinômio têm coeficientes nulos.
Exemplo 2: O polinômio nulo p(x)=0 é representado por uma lista como a que aparece na figura abaixo.
|
Note que essa lista contém somente a cabeça de lista, pois o polinômio p(x)=0 não possui nehum termo com coeficiente não nulo.
A implementação da biblioteca (o arquivo polinomio.c
) deve
incluir as seguintes declarações:
#include <stdlib.h> #include "polinomio.h" typedef struct termo { double coef; int exp; struct termo *prox; } Termo; static Termo *lista_livre = NULL;
Sua implementação deve manter uma lista livre, com os Termo
s
alocados via malloc
mas disponíveis no momento. O apontador
lista_livre
aponta para o início dessa lista. Um novo
Termo
deve ser alocado através da função malloc
apenas quando a lista livre estiver vazia. A função
Polinomio cria_monomio(double coef, int exp)
devolve um Polinomio
constituído por duas células recém-retiradas
da lista livre (ou alocadas diretamente via malloc
): a cabeça de
lista (um Termo
dummy, que existe apenas por
conveniência) e o "verdadeiro Termo
" do monômio, termo este já
preenchido com coef
, exp
e NULL
. A
função void libera(Polinomio p)
insere na lista livre
todas as células do polinômio referenciado por p
.
Não deixe sua implementação vazar memória, ou seja, inclua na lista livre todas as células que não estiverem em um polinômio no momento.
Além da implementação das funções da biblioteca, você deverá entregar um programa principal que teste essas funções. O programa principal deve ler de um arquivo uma seqüência de polinômios e operações e mostrar o resultado da execução de cada uma destas operações sobre os polinômios dados.
As operações de soma, subtração, multiplicação, derivação, grau,
cópia e calcula valor são codificadas por '+', '-', '*', 'd', 'g', 'c'
e 'v' respectivamente. Cada linha do arquivo contem ou um polinômio,
dado por um inteiro k e uma seqüência de k pares de números (cada par
representando um termo), ou uma operação. No caso do polinômio,
suponha que os termos são dados em ordem crescente de expoente, para
facilitar. No caso da operação calcula, a letra 'v' vem seguida de um
valor real. Uma operação se refere sempre ao último polinômio
manipulado e, caso a operação seja binária, ao próximo polinômio no
arquivo. Tome cuidado com a manipulação do arquivo de entrada. Em
C
, vários problemas podem acontecer na leitura. Assim, numa
primeira etapa, implemente a leitura dos dados e a impressão de um
polinômio. Apenas depois que essa parte estiver funcionando bem, passe a
escrever e testar as demais funções de manipulação de polinômios. Teste
cada função separadamente, para não se atrapalhar.
Após a leitura de cada polinômio do arquivo de entrada, o seu programa deve imprimir o polinômio lido. Também após o processamento de cada operação, o seu programa deve imprimir o resultado da operação.
Abaixo mostramos um exemplo de arquivo de entrada.
3 1 2 -1 3 4 5 + 4 -1 0 2 2 1 3 3 4 g - 4 -1 0 2 2 1 3 3 4 * 1 1 1 d * 2 1 1 -1 2
A saída para tal exemplo poderia ser algo como:
(x^2-x^3+4x^5) + (-1+2x^2+x^3+3x^4) = -1+3x^2+3x^4+4x^5 O grau de -1+3x^2+3x^4+4x^5 e' 5. (-1+3x^2+3x^4+4x^5) - (-1+2x^2+x^3+3x^4) = x^2-x^3+4x^5 (x^2-x^3+4x^5) * (x) = x^3-x^4+4x^6 A derivada de x^3-x^4+4x^6 e' 3x^2-4x^3+24x^5. (3x^2-4x^3+24x^5) * (x-x^2) = 3x^3-7x^4+4x^5+24x^6-24x^7
Avisos
Este trabalho deve ser entregue até 22/09, através do sistema Paca/Moodle.ep1-<seu-número-USP.tar.gz>
ou
ep1-<seu-número-USP.zip>
.
Por exemplo: ep1-12345678.zip
..tar.gz
ou .zip
. (Exemplo:
ep1-12345678
.) Todos os arquivos desempacotados devem
estar dentro desse diretório.polinomio.h
com a interface da sua
biblioteca. O conteúdo desse arquivo deve ser o especificado
neste enunciado.polinomio.c
com a implementação da sua
biblioteca.cliente.c
com o programa cliente que
exercita as funções da sua biblioteca. (É nesse arquivo que
está a função main
.).h
ou .c
que você utilizar para funções auxiliares compiladas
separadamente. Por exemplo, se você tiver um módulo compilado
separadamente contendo apenas a função mallocX
,
então você deverá entregar os arquivos mallocx.h
e
mallocx.c
..h
ou .c
devem ter
um cabeçalho como o seguinte:
/**********************************************************/ /* Aluno: Fulano de Tal */ /* Número USP: 12345678 */ /* Exercício-Programa 1 -- Polinômios Esparsos */ /* MAC122 -- BMAC -- 2008 -- Professor: Reverbel */ /* Compilador: ... (DevC++ ou gcc) versão ... */ /**********************************************************/
gcc
,
com as opções "-Wall -ansi -pedantic -O2
".gcc
,
passe ao compilador (na linha de comando) as opções
"-Wall -ansi -pedantic -O2
".
Caso você use o DevC++, clique em "Ferramentas" (ou
"Tools") e "Opções do Compilador" (ou "Compiler
Options") e, na tela de opções do compilador, marque como
selecionada a opção "Adicione os seguintes comandos quando chamar o
compilador" (ou "Add the following commands when calling
compiler"). Na caixa de texto que aparece logo depois dessa opção,
digite "-Wall -ansi -O2
". (Não use "-pedantic
"
com o DevC++.)