/**
   * Returns the polynomial obtained by adding the given polynomial p to this polynomial - DOES NOT
   * change this polynomial
   *
   * @param p Polynomial to be added
   * @return A new polynomial which is the sum of this polynomial and p.
   */
  public Polynomial add(Polynomial p) {

    // Returns other list if adding zero to it
    if (p.poly == null) return this;
    else if (this.poly == null) return p;

    Node list1 = this.poly;
    Node list2 = p.poly;
    Polynomial newList = new Polynomial();

    // Iterates through both lists
    while (list1 != null && list2 != null) {
      if (list1.term.degree == list2.term.degree) { // Checks for similar degrees between lists
        if (list1.term.coeff + list2.term.coeff
            == 0) { // Checks if the coefficients total 0, skips making a node
          list1 = list1.next;
          list2 = list2.next;
        } else { // If coeff total is not 0, makes a new node adding them
          float newCoeff = list1.term.coeff + list2.term.coeff;
          Node n = new Node(newCoeff, list1.term.degree, null);
          newList.addToEnd(n);
          list1 = list1.next;
          list2 = list2.next;
        }
      }

      // If currently evaluated nodes are not equal, then there are no like degrees between lists
      // Adds new node with the currently evaluated term's values and increments respective pointer
      else if (list1.term.degree > list2.term.degree) {
        Node n = new Node(list2.term.coeff, list2.term.degree, null);
        newList.addToEnd(n);
        list2 = list2.next;
      } else if (list2.term.degree > list1.term.degree) {
        Node n = new Node(list1.term.coeff, list1.term.degree, null);
        newList.addToEnd(n);
        list1 = list1.next;
      }
    }

    // Deals with uneven list: whatever list isn't finished parsing is simply added to end of
    // newList
    Node tmp = null;

    if (list1 != null) tmp = list1;
    else if (list2 != null) tmp = list2;

    while (tmp != null) {
      Node n = new Node(tmp.term.coeff, tmp.term.degree, null);
      newList.addToEnd(n);
      tmp = tmp.next;
    }

    return newList;
  }
  // Helper method to sort an unsorted linked list in ascending order. All like degrees will be next
  // to each other.
  private void sort() {

    Polynomial newList = new Polynomial();

    for (Node tmp1 = poly; tmp1 != null; tmp1 = tmp1.next) {
      Node n = new Node(tmp1.term.coeff, tmp1.term.degree, null);

      if (newList.poly == null) newList.poly = n;
      else {
        for (Node tmp2 = newList.poly; tmp2 != null; tmp2 = tmp2.next) {
          if (tmp2.next == null) {
            tmp2.next = n;
            break;
          } else if (tmp2.next.term.degree > n.term.degree) {
            n.next = tmp2.next;
            tmp2.next = n;
            break;
          }
        }
      }
    }
    poly = newList.poly;
  }
  /**
   * Returns the polynomial obtained by multiplying the given polynomial p with this polynomial -
   * DOES NOT change this polynomial
   *
   * @param p Polynomial with which this polynomial is to be multiplied
   * @return A new polynomial which is the product of this polynomial and p.
   */
  public Polynomial multiply(Polynomial p) {

    // If multiplying by zero, return the null list
    if (p.poly == null) return p;
    else if (this.poly == null) return this;

    Polynomial newList = new Polynomial();

    // Nested loop to multiply terms. Multiplies first term by every term in second list and so on.
    for (Node list1 = this.poly; list1 != null; list1 = list1.next) {
      for (Node list2 = p.poly; list2 != null; list2 = list2.next) {
        Node n =
            new Node(
                list1.term.coeff * list2.term.coeff, list1.term.degree + list2.term.degree, null);
        newList.addToEnd(n);
      }
    }

    // sorts newList in ascending order and then combines like degrees.
    newList.sort();
    newList.compress();

    return newList;
  }