#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Polynom {
  private:
    vector<double> a;

    explicit Polynom(int n) : a(n + 1, 0) {}
    void normalize() {
      while (a.size() > 0 && a.back() == 0)
        a.pop_back();
    }

  public:
    Polynom() : a(0) {}
    Polynom(const vector<double>& a_) : a(a_) {
      normalize();
    }

    int grad() const { return a.size() - 1; }
    Polynom deriv() const {
      if (grad() <= 0)
        return Polynom{};
    
      Polynom p{grad() - 1};
      for (unsigned int i = 1; i < a.size(); i++)
        p.a[i - 1] = i * a[i];
    
      return p;
    }

    friend Polynom operator+(Polynom p, Polynom q) {
      int m = p.grad(), n = q.grad();
    
      Polynom r{max(m, n)};
      for (int i = 0; i < max(m, n) + 1; i++) {
        if (i < m + 1)
          r.a[i] += p.a[i];
        if (i < n + 1)
          r.a[i] += q.a[i];
      }
    
      r.normalize();
    
      return r;
    }
    
    Polynom operator-() const {
      Polynom r{*this};
    
      for (unsigned int i = 0; i < r.a.size(); i++)
        r.a[i] = -r.a[i];
    
      return r;
    }
    friend Polynom operator-(Polynom p, Polynom q) { return p + (-q); }
    
    friend Polynom operator*(Polynom p, Polynom q) {
      int m = p.grad(), n = q.grad();
    
      if (m < 0 || n < 0)
        return Polynom{};
    
      Polynom r{m + n};
      for (int k = 0; k <= m + n; k++)
        for (int i = max(0, k - n); i <= min(k, m); i++)
          r.a[k] += p.a[i] * q.a[k - i];
    
      return r;
    }

    friend istream& operator>>(istream& stream, Polynom &p) {
      int n;
      stream >> n;
    
      p = Polynom{max(-1, n)};
      for (unsigned int i = 0; i < p.a.size(); i++)
        stream >> p.a[i];
      p.normalize();
    
      return stream;
    }
    friend ostream& operator<<(ostream& stream, const Polynom& p) {
      bool at_begin = true;
      for (unsigned int i = 0; i < p.a.size(); i++) {
        if (p.a[i] == 0)
          continue;
    
        if (!at_begin)
          stream << " + ";
        else
          at_begin = false;
    
        if (p.a[i] != 1 || i == 0)
          stream << p.a[i];
        if (p.a[i] != 1 && i != 0)
          stream << "*";
        if (i != 0)
          stream << "x";
        if (i > 1)
          stream << "^" << i;
      }
    
      if (at_begin)
        stream << "0";
    
      return stream;
    }
};

int main() {
  Polynom p, q;

  cout << "p q: ";
  cin >> p >> q;

  // cout << "p   = " << p << endl;
  // cout << "-p  = " << -p << endl;
  // cout << "q   = " << q << endl;
  // cout << "p+q = " << p+q << endl;
  // cout << "p-q = " << p-q << endl;
  // cout << "p*q = " << p*q << endl;

  cout << "(pq)' = " << (p * q).deriv() << endl;
  cout << "p'q + pq' = " << (p.deriv() * q + p * q.deriv()) << endl;

  cout << "grad(p) = " << p.grad() << endl;
  cout << "grad(q) = " << q.grad() << endl;
  cout << "grad((pq)') = " << (p * q).deriv().grad() << endl;
  cout << "grad((pq)' - (p'q + pq')) = " << ((p * q).deriv() - (p.deriv() * q + p * q.deriv())).grad() << endl;

  return 0;
}
