#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//Estrutura que representa os elementos da lista
typedef struct _no{
    int coeficiente;
    int *grau;
    struct _no *prox;
}*no;
//Funcao para verificar se a lista esta vazia
int vazia(no cabeca){
    if (cabeca->prox==NULL)
        return 1;
    else
        return 0;
}
//Cria uma lista vazia
no lista_vazia(void){
    //Cria um sentinela para facilitar os algoritmos de inserção e remoção
    no sentinela=(no)malloc(sizeof(struct _no));
    //Prox de sentinela aponta para nada
    sentinela->prox=NULL;
    return sentinela;
}

//Funcao para retornar a primeira posicao valida da lista
no inicio(no cabeca){
    return cabeca->prox;
}

//Funcao para retornar o endereco do proximo elemento da lista
no avancar(no lista){
    return lista->prox;
}

//Percorre a lista
no percorrer(no cabeca,int posicao){
    int i;
    //chama avança na lista a cada iteração
    for (i=0;i<posicao;i++){
        cabeca=avancar(cabeca);
    }
    return cabeca;
}
//Funcao para remover
int remover_apos(no _no){
    no aux=_no->prox;
    //Se a posicao solicitada for nula a funcao retorna 0
    if (aux==NULL)
        return 0;
    //_no->prox aponta para o proximo de aux    
    _no->prox=aux->prox;
    //Libera aux
    free(aux);
    return 1;
}

//Funcao para inserir apos um no da lista
void inserir_apos(no lista,int coeficiente,int *vetor,int tamanho_vetor){
    int i;
    //Aloca espaco para um novo no
    no novo=(no)malloc(sizeof(struct _no));
    //Aloca espaco para o vetor de inteiros dentro do no
    novo->grau=(int*)malloc(sizeof(int)*tamanho_vetor);
    
    //Prox de novo aponta para o proximo da no atual
    novo->prox=lista->prox;
    //Novo recebe coeficiente
    novo->coeficiente=coeficiente;
    //Grau vai receber o vetor de inteiros
    for(i=0;i<tamanho_vetor;i++)
        novo->grau[i]=vetor[i];
    //Lista atual aponta para o novo no
    lista->prox=novo;
}
//Funcao para derivar um no da lista
void derivar(int var,no cabeca){
    no anterior=cabeca;
    //Enquanto o no nao apontar para nulo
    while ((cabeca=cabeca->prox)!=NULL){
        //Mutiplica o coeficiente pelo grau da variavel escolhida
        cabeca->coeficiente=cabeca->coeficiente*cabeca->grau[var-1];
        //Decrementa o grau da variavel
        cabeca->grau[var-1]--;
        //Se o coeficiente for igual a 0, retira o no da lista
        if(cabeca->coeficiente==0){
            //Vai para a posicao anterior da lista
            cabeca=anterior;
            //Remove a posicao atual
            remover_apos(anterior);
        }
        else
            anterior=anterior->prox;
    }
}
void multiplicar(int coeficiente,int *vetor, int num_var,no cabeca){
    int i;
    //Enquanto o no nao apontar para nulo
    while ((cabeca=cabeca->prox)!=NULL){
        //Mutiplica o coeficiente pelo coeficiente lido do arquivo
        cabeca->coeficiente=cabeca->coeficiente*coeficiente;
        for (i=0;i<num_var;i++)
            //Soma o grau das variaveis com  a do arquivo recebido
            cabeca->grau[i]+=vetor[i];
    }
}
void somar_subtrair(int coeficiente,int *vetor,int num_var,no cabeca,char opcao){
    int igual=0;
    int i,alocou=0;
    while (cabeca->prox!=NULL){
        cabeca=cabeca->prox;
        for (i=0;i<num_var;i++){
            if (vetor[i]==cabeca->grau[i])
                igual++;
        }
        if (igual==num_var){
            switch (opcao){
                case '+':
                    cabeca->coeficiente+=coeficiente;
                    break;
                case '-':
                    cabeca->coeficiente-=coeficiente;
            }
            alocou=1;
        }
    }
    if (!alocou){
        switch (opcao){
            case '+':
                inserir_apos(cabeca,coeficiente,vetor,num_var);
                break;
            case '-':
                inserir_apos(cabeca,-coeficiente,vetor,num_var);
        }
    }
}        
void simplificar(no cabeca,int num_var){
    int i,igual=0;;
    no base=cabeca;
    //Enquanto o no nao apontar para nulo
    while ((base=base->prox)!=NULL){
        no anterior=base;
        cabeca=base;
        while ((cabeca=cabeca->prox)!=NULL){
            for (i=0;i<num_var;i++)
                if (cabeca->grau[i]==base->grau[i])
                    igual++;
            if (igual==num_var){
                base->coeficiente+=cabeca->coeficiente;
                //Vai para a posicao anterior da lista
                cabeca=anterior;
                //Remove a posicao atual
                remover_apos(anterior);
            }
            else
                anterior=anterior->prox;
        }
    }
}
  
//Funcao principal
int main(int argc,char **argv){
    if (argc!=2){
        printf("Digite na linha de comando o nome do arquivo a ser lido\n");
        exit(0);
    }
    
    char string[20];
    no cabeca=lista_vazia();
    no temp=cabeca;
    FILE *entrada,*saida;
    int num_var,*vetor,coeficiente,i,j,ordem;
    
    entrada=fopen(argv[1],"r");
    saida=fopen("pol.out","w");
    
    //Le do arquivo o numero de variaveis e o coeficiente como uma string
    fscanf(entrada,"%d\n%s",&num_var,string);
    //Aloca posicoes para um vetor
    vetor=(int*)malloc(sizeof(int)*num_var);
    //Le os coeficientes e o grau das variáveis
    while(strcmp(string,"d")){
        coeficiente=atoi(string);
        for (i=0;i<num_var;i++) 
            fscanf(entrada,"%d",&vetor[i]);
        //Insere no no apos o temp
        inserir_apos(temp,coeficiente,vetor,num_var);
        //Avanca com temp
        temp=avancar(temp);
        fscanf(entrada,"%s",string);
    }
    //Imprime no arquivo o valor do numero de variaveis
    fprintf(saida,"%d\n",num_var);
    
    //Iteracao para escrever na tela o polinomio recebido como dado de entrada
    temp=inicio(cabeca);
    printf("\nPolinomio:");
    while(temp!=NULL){
        printf("%d",temp->coeficiente);
        for(j=0;j<num_var;j++){
            if (temp->grau[j]!=0)
                printf("X[%d]^%d ",j+1,temp->grau[j]);
        }
        printf(" + ");
        temp=avancar(temp);
    }
    printf("\n");
        
    i=0;
    //Iteração para ler do arquivo a ordem de derivação e fazer a derivação
    while((fscanf(entrada,"%d\n",&ordem)) != EOF){
        i++;
        //Deriva
        derivar(ordem,cabeca);
        printf("\n%da. derivada em X%d:",i,ordem);
        //Vai para o inicio da lista
        temp=inicio(cabeca);
        //Iteração para escrever na tela os passos de derivação
        while(temp!=NULL){
            printf("%d",temp->coeficiente);
            for(j=0;j<num_var;j++){
                if (temp->grau[j]!=0)
                    printf("X[%d]^%d ",j+1,temp->grau[j]);
            }
            printf(" + ");
            temp=avancar(temp);
        }
   }
   printf("\n");
   //Vai para o inicio da lista
   temp=inicio(cabeca);
   //Escreve no arquivo a saida
   while(temp!=NULL){
        //Escreve no arquivo o coeficiente
		fprintf(saida,"%d\n",temp->coeficiente);
		//Escreve no arquivo o grau das variaveis
		for(j=0;j<num_var;j++){
			fprintf(saida,"%d ",temp->grau[j]);
		}
		fprintf(saida,"\n");
		//Vai para o proximo no da lista
		temp=avancar(temp);
   }
   //Se o resultado final der 0 imprime na tela e no arquivo
   if (vazia(cabeca)){
       printf("0");
       fprintf(saida,"0");
   }
   //Fecha os arquivos
   fclose(entrada);
   fclose(saida);
   
   return 0;
}
