/* Solution proposee par Ning Hu, legerement modifiee */

#include <stdio.h>
#include "polynome.h"

//comment in the following line when building the polynome library
//it will remove main in compilation or at compile tile use -D IS_LIBRARY
//-----------------------------------------------------------------
//#define IS_LIBRARY

//----------------
#ifndef IS_LIBRARY
//---------------

//------------------
//main driver method
//------------------
main()
{
  node *head,*p,*q;
  poly *p1,*p2,*p3;
  int choix,i;
  float constant;

  //initialization
  head = NULL;
  

  while(1)
  {
    //demander le choix
    printf("***************************************\n");
    printf("Qu'est que vous voulez?\n");
    printf("1) cree un polynome\n");
    printf("2) mettre les polynomes a l'ecran\n");
    printf("3) recalculer tous les polynomes\n");
    printf("4) ajouter deux polynomes ensemble\n");
    printf("5) multiplier un polynome par une constante\n");
    printf("6) multiplier deux polynomes ensemble\n");
    printf("7) evaluer le polynome\n");
    printf("0) quitter\n");
    printf("***************************************\n");
 
    //performer le action
    scanf("%i",&choix);
    switch(choix)
    {

    case 0://detruire touts les polynomes et quitter
      for(p = head,q = head; 
	  p != NULL; 
	  q = p, p = p->next, free(q) );
      exit(0);	

    case 1://cree un polynome
      head = ajouter_a_list(head,cree());
      break;

    case 2://mettre les polynomes a l'ecran
      printf("les polynomes:\n");
      montre_plusiers(head);
      break;

    case 3: //calculer tous les polynomes
      for(p = head; p != NULL; p = p->next )
	p->poly = calculer(p->poly);
      break;

    case 4: //ajouter deux polynomes ensemble

      //mettre touts les polynomes a le ecran
      montre_plusiers(head);

      p1 = choisir_poly(head);
      p2 = choisir_poly(head);
      p3 = ajouter(p1,p2); //ajouter

      //montre le resultat
      printf("La result: ");
      montre(p3);
      
      //ajouter le resultat a la liste
      head = ajouter_a_list(head,p3);
      break;

    case 5: //multiplier un polynome par une constante

      //mettre tous les polynomes a l'ecran
      montre_plusiers(head);

      p1 = choisir_poly(head);

      printf("donner un const svp:");
      scanf("%f",&constant);

      //faire la multiplication avec une autre function
      multiplier_const(p1,constant);

      //montre le result
      printf("La result: ");
      montre(p1);
      break;

    case 6: //multiplier deux polynomes ensemble

      //mettre touts les polynomes a l'ecran
      montre_plusiers(head);

      p1 = choisir_poly(head);
      p2 = choisir_poly(head);
      p3 = multiplier(p1,p2);  //multiplier
      
      //montre le result
      printf("La result: ");
      montre(p3);

      //ajouter le result a la liste
      head = ajouter_a_list(head,p3);
      break;

    case 7: //evaluer le polynome
      
      montre_plusiers(head);
      
      p1 = choisir_poly(head);

      printf("donner un const a evaluer svp:");
      scanf("%f",&constant);

      constant = (float)evaluer_a_horner(p1,constant);

      //montrer le resultat
      printf("La result est: %f\n",constant);
    }
  }
 
}

//----
#endif
//----

//---------------------------------------------------
//desc   = this method chooses a polynome from a list
//input  = link list (node pointer)
//output = poly struct
//---------------------------------------------------
poly* choisir_poly(node *head)
{
  int choix,i;
  node *p;
  poly *p1;

  printf("choisir un polynome svp:");
  scanf("%i",&choix);
  
  //chercher le polynome
  for(p = head,i = 0; 
      p != NULL, i < choix; 
      p = p->next, i++ )
  {
    p1 = p->poly;
  }

  if(i != choix)
  {
    printf("mauvais choix\n");
    return 0;
  }
  else return p1;
}

//---------------------------------------------------
//desc   = this method adds a polynome to a list
//input  = link list (node struct pointer) and polynomial
//output = link list (node struct pointer)
//---------------------------------------------------
node*  ajouter_a_list(node* head, poly* p1)
{
  node *p,*q;
  
  //ajouter le resultat a la liste
  p = (node*)malloc(sizeof(node));

  if(head == NULL)
  {
    head = p;
    head->next = NULL;
    head->poly = p1;
  }
  else //must add at end of list
  {
    p->next = NULL;
    p->poly = p1;

    q = head;
    while(q->next != NULL)
      q = q->next;

    q->next = p;
  }

  return head;
}

//---------------------------------------------------
//desc   = this method prints a list of polys to screen
//input  = link list(node struct pointer)
//output = null
//---------------------------------------------------
void  montre_plusiers(node* head)
{
  node *p;
  int i;

  //mettre tous les polynomes a l'ecran
  for(p = head, i = 1; 
      p != NULL; 
      p = p->next,i++ )
  {
    printf("%i) ",i);
    montre(p->poly);
  }
}



//---------------------------------------------------
//desc   = this method creates a poly nomial structure
//input  = null
//output = poly struct
//---------------------------------------------------
poly* cree(void)
{
  poly* nouveau;
  int i;
  float coeff;

  nouveau = (poly*)malloc(sizeof(poly));

  printf("la degree est:");
  scanf("%i",&(nouveau->degree));  
  
  nouveau->coefficient = (float*)malloc(sizeof(float)*(nouveau->degree+1));  

  for(i = nouveau->degree; i >= 0; i--)
  {
    printf("entre le coefficient de la degree %i:-->",i);
    scanf("%f",&coeff);
    nouveau->coefficient[i] = coeff;
  }

  return nouveau;
}

//-----------------------------------------------------
//desc   = this method prints a polynomial to the screen
//input  = pointer to a poly struct
//output = null
//-----------------------------------------------------
void montre(poly* p)
{
  int i;

  printf("--> "); 

  for(i = p->degree; i > 0; i--)
  {
    if(p->coefficient[i] != 0)
      printf("%.2f(X^%i) + ",p->coefficient[i],i);
  }

  printf("%.2f\n",p->coefficient[0]);

}

//-----------------------------------------------------
//desc   = this method recalculates the degree of a polynomial
//input  = pointer to a poly struct
//output = pointer to a new poly struct if changes are made
//-----------------------------------------------------
poly* calculer(poly* p)
{
  int i,save;
  poly* q;

  save = p->degree;

  for(i = p->degree; i > 0; i--)
  {
    if(p->coefficient[i] == 0) //highest degree coeff = 0, decrement degree
      (p->degree)--;
  }

  //recreate poly if degree has changed
  if(save == p->degree)
    return p;

  q = (poly*)malloc(sizeof(poly));
  q->degree = p->degree;

  q->coefficient = (float*)malloc(sizeof(float)*(q->degree+1));

  for(i = p->degree ; i >= 0; i--)
    q->coefficient[i] = p->coefficient[i];
  
  printf("Ce polynome a change de degree de %i a %i\n",save,p->degree);
  montre(p);  
  
  free(p);
  return q;
}

//-----------------------------------------------------
//desc   = this method adds two polynomials together
//input  = two pointers to poly structs
//output = pointer to a new poly struct
//-----------------------------------------------------
poly* ajouter(poly* p1,poly* p2)
{
  poly *nouveau;
  int i;

  //error check
  if( (p1 == NULL) || (p2 == NULL))
  {
    perror("Null pointer encountered in ajouter\n");
    return NULL;
  }

  nouveau = (poly*)malloc(sizeof(poly));

  //degree est la max des deux polys
  if(p1->degree > p2->degree)
    nouveau->degree = p1->degree;
  else
    nouveau->degree = p2->degree;

  nouveau->coefficient = (float*)malloc(sizeof(float)*(nouveau->degree+1));

  //initialier a zero
  for(i = nouveau->degree; i >= 0; i--)
    nouveau->coefficient[i] = 0;
  

  //add p1 to nouveau
  for(i = p1->degree; i >= 0; i--)
    nouveau->coefficient[i] += p1->coefficient[i];

  //add p2 to nouveau
  for(i = p2->degree; i >= 0; i--)
    nouveau->coefficient[i] += p2->coefficient[i];

  return nouveau;
}

//-----------------------------------------------------
//desc   = this method multiplies two polynomials together
//input  = pointers to two poly structs 
//output = pointer to a new poly struct
//-----------------------------------------------------
poly* multiplier(poly* p1,poly* p2)
{
  poly *nouveau;
  int i,j,degree;
  double result;

  //error check
  if( (p1 == NULL) || (p2 == NULL))
  {
    perror("Null pointer encountered in multiply of two polys\n");
    return NULL;
  }

  nouveau = (poly*)malloc(sizeof(poly));

  //degree est la sum des deux polys
  nouveau->degree = p1->degree + p2->degree;

  nouveau->coefficient = (float*)malloc(sizeof(float)*(nouveau->degree+1));

  //initialier a zero
  for(i = nouveau->degree; i >= 0; i--)
  {
    nouveau->coefficient[i] = 0;
  }


  for(i = 0; i <= p1->degree; i++)//loop through 1st polynome
  {
    for(j = 0; j <= p2->degree; j++ )//loop through 2nd polynome
    {
      degree = i + j; //new degree for coefficient

      //calculate coefficient
      result = (p1->coefficient[i] * p2->coefficient[j]);
  
      nouveau->coefficient[degree] += result;
    }  
  }

  return nouveau;
}

//-----------------------------------------------------
//desc   = this method multiplies a polynomial by a const
//input  = a pointer to a poly struct and a const double
//output = pointer to the same poly struct
//-----------------------------------------------------
poly* multiplier_const(poly *p,double n)
{
  int i;
  
  for(i = p->degree; i >= 0; i--)
    p->coefficient[i] *= n;

  return p;
}

//-----------------------------------------------------
//desc   = this method evaluates a polynome using horner's rule
//input  = a pointer to a poly struct and a const double
//output = double value
//-----------------------------------------------------
double evaluer_a_horner(poly *p,double n)
{
  //variables
  double sum;
  int i;

  //action
  sum = (p->coefficient[p->degree]);

  for(i = p->degree; i > 0 ;i--)
  {
    sum = (sum * n) + p->coefficient[i-1];
  }

  return sum;
}


//-----------------------------------------------------
//desc   = this method makes a copy of a polynomial
//input  = a pointer to a poly struct
//output = a pointer to a new poly struct. the original is unaffected
//-----------------------------------------------------
poly*  copy(poly* ptr_oldPoly)
{
  poly *ptr_newPoly;
  int i;

  ptr_newPoly = (poly*)malloc(sizeof(poly));

  ptr_newPoly->coefficient=(float*)malloc(sizeof(float)*(ptr_oldPoly->degree+1));
  ptr_newPoly->degree = ptr_oldPoly->degree;

  for(i = 0; i <= ptr_oldPoly->degree; i++)
    ptr_newPoly->coefficient[i] = ptr_oldPoly->coefficient[i];

  return ptr_newPoly;
}


//-----------------------------------------------------
//desc   = this method destroys a polynomial object
//input  = a pointer to a poly struct
//output = nothing.
//-----------------------------------------------------
void  destroy(poly *p) 
{
  //just in case
  if(p == NULL) 
    return;

  free(p->coefficient);
  free(p);
}