/*-----------------------------------------------------------------------------
Filename   : polynomial.c
Name       : Tristan Miller
SID#       : 123456789
Description: program which adds and evaluates polynomials
-----------------------------------------------------------------------------*/

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

#define MAX_DEGREE 30

int get_degree(void);
void get_coefficients(double p[], int degree);
int max(int x, int y);
int add_polynomial(const double p[], int p_degree,
		   const double q[], int q_degree, double r[]);
void print_polynomial(const double p[], int degree);
double eval_polynomial(const double p[], int degree, double x);

/*-----------------------------------------------------------------------------
Name   : get_degree()
Purpose: prompts user for a polynomial degree from 0 to MAX_DEGREE
Returns: the degree entered
-----------------------------------------------------------------------------*/
int get_degree(void) {
  int degree = -1;

  do {
    if (scanf("%d", &degree) == EOF) {
      printf("Error -- unexpected end of input\n");
      exit(EXIT_FAILURE);
    }
    if (degree < 0 || degree > MAX_DEGREE)
      printf("Degree must be in [0, %d].  Try again: ", MAX_DEGREE);
  } while (degree < 0 || degree > MAX_DEGREE);

  return degree;
}


/*-----------------------------------------------------------------------------
Name   : get_coefficients()
Purpose: prompts user for coefficients of a polynomial of degree specified
         by the second argument
Returns: the coefficients of the polynomial, via p[]
-----------------------------------------------------------------------------*/
void get_coefficients(double p[], int degree) {
  double a;
  int max_degree = degree;

  do {
    if (scanf("%lf", &a) == EOF) {
      printf("Error -- unexpected end of input\n");
      exit(EXIT_FAILURE);
    }
    if (max_degree && degree == max_degree && a == 0.0)
      printf("Coefficient %d cannot be 0. Try again: ", degree++);
    else
      p[degree] = a;
  } while (degree--);
}


/*-----------------------------------------------------------------------------
Name   : max()
Returns: returns the maximum of its two int arguments
-----------------------------------------------------------------------------*/
int max(int x, int y) {
  if (x > y)
    return x;
  else
    return y;
}


/*-----------------------------------------------------------------------------
Name   : add_polynomial()
Purpose: adds two unevaluated polynomials together
Args   : p[], p_degree -- coefficients of a polynomial addend and its degree
         q[], q_degree -- coefficients of a polynomial addend and its degree
         r[]           -- coefficients of resulting polynomial
Returns: degree of polynomial expressed in r[]
Pre    : the array r must be at least as big as max(p_degree+1, q_degree+1)
-----------------------------------------------------------------------------*/
int add_polynomial(const double p[], int p_degree,
		   const double q[], int q_degree, double r[]) {
  int r_degree = p_degree;

  /* Can simplify things if we guarantee that the largest polynomial is p */
  if (p_degree < q_degree)
    return add_polynomial(q, q_degree, p, p_degree, r);

  /* Copy p to r */
  do {
    r[p_degree] = p[p_degree];
  } while (p_degree--);

  /* Add q to r */
  do {
    r[q_degree] += q[q_degree];
  } while (q_degree--);

  /* Find the first nonzero coefficient in r */
  while (r_degree && r[r_degree] == 0.0)
    r_degree--;

  return r_degree;
}


/*-----------------------------------------------------------------------------
Name   : print_polynomial()
Purpose: prints a polynomial in human-readable form
Args   : p[], p_degree -- coefficients of a polynomial and its degree
Returns: nothing
-----------------------------------------------------------------------------*/
void print_polynomial(const double p[], int degree) {
  /* The leading coefficient does not need a + sign */
  printf("%.3f", p[degree]);
  if (degree > 0)
    printf("x");
  if (degree > 1)
      printf("^%d", degree);

  /* General case */
  while (degree--) {
    if (p[degree] == 0.0)
      continue;
    printf("%+.3f", p[degree]);
    if (degree > 0)
      printf("x");
    if (degree > 1)
      printf("^%d", degree);
  }
}

/*-----------------------------------------------------------------------------
Name   : eval_polynomial()
Returns: value of a polynomial at a certain point
Args   : p[], p_degree -- coefficients of a polynomial and its degree
         x -- point at which to evaluate polynomial
-----------------------------------------------------------------------------*/
double eval_polynomial(const double p[], int degree, double x) {
  double result = p[degree];

  while (--degree >= 0)
    result = p[degree] + result * x;

  return result;
}


/*=============================================================================
                                Main program
=============================================================================*/
int main(void) {
  int p_degree, q_degree, r_degree;
  double p[MAX_DEGREE + 1], q[MAX_DEGREE + 1], r[MAX_DEGREE + 1], x;

  printf("Enter degree of p(x): ");
  p_degree = get_degree();

  printf("Enter degree of q(x): ");
  q_degree = get_degree();

  printf("Enter coefficients for p(x): ");
  get_coefficients(p, p_degree);

  printf("Enter coefficients for q(x): ");
  get_coefficients(q, q_degree);

  r_degree = add_polynomial(p, p_degree, q, q_degree, r);
  printf("p(x) + q(x) = ");
  print_polynomial(r, r_degree);

  printf("\nEnter a value for x: ");
  while (scanf("%lf", &x) != EOF) {
    printf("p(x) + q(x) = %.3f\n", eval_polynomial(r, r_degree, x));
    printf("Enter a value for x: ");
  }

  return EXIT_SUCCESS;
}

