/**
   * Multiplies the polynomial by another, taking the indices mod N. Does not change this polynomial
   * but returns the result as a new polynomial.<br>
   * Both polynomials must have the same number of coefficients.
   *
   * @param poly2 the polynomial to multiply by
   * @return a new polynomial
   */
  public BigIntPolynomial mult(BigIntPolynomial poly2) {
    int N = coeffs.length;
    if (poly2.coeffs.length != N) {
      throw new IllegalArgumentException("Number of coefficients must be the same");
    }

    BigIntPolynomial c = multRecursive(poly2);

    if (c.coeffs.length > N) {
      for (int k = N; k < c.coeffs.length; k++) {
        c.coeffs[k - N] = c.coeffs[k - N].add(c.coeffs[k]);
      }
      c.coeffs = Arrays.copyOf(c.coeffs, N);
    }
    return c;
  }
  /** Karazuba multiplication */
  private BigIntPolynomial multRecursive(BigIntPolynomial poly2) {
    BigInteger[] a = coeffs;
    BigInteger[] b = poly2.coeffs;

    int n = poly2.coeffs.length;
    if (n <= 1) {
      BigInteger[] c = Arrays.clone(coeffs);
      for (int i = 0; i < coeffs.length; i++) {
        c[i] = c[i].multiply(poly2.coeffs[0]);
      }
      return new BigIntPolynomial(c);
    } else {
      int n1 = n / 2;

      BigIntPolynomial a1 = new BigIntPolynomial(Arrays.copyOf(a, n1));
      BigIntPolynomial a2 = new BigIntPolynomial(Arrays.copyOfRange(a, n1, n));
      BigIntPolynomial b1 = new BigIntPolynomial(Arrays.copyOf(b, n1));
      BigIntPolynomial b2 = new BigIntPolynomial(Arrays.copyOfRange(b, n1, n));

      BigIntPolynomial A = (BigIntPolynomial) a1.clone();
      A.add(a2);
      BigIntPolynomial B = (BigIntPolynomial) b1.clone();
      B.add(b2);

      BigIntPolynomial c1 = a1.multRecursive(b1);
      BigIntPolynomial c2 = a2.multRecursive(b2);
      BigIntPolynomial c3 = A.multRecursive(B);
      c3.sub(c1);
      c3.sub(c2);

      BigIntPolynomial c = new BigIntPolynomial(2 * n - 1);
      for (int i = 0; i < c1.coeffs.length; i++) {
        c.coeffs[i] = c1.coeffs[i];
      }
      for (int i = 0; i < c3.coeffs.length; i++) {
        c.coeffs[n1 + i] = c.coeffs[n1 + i].add(c3.coeffs[i]);
      }
      for (int i = 0; i < c2.coeffs.length; i++) {
        c.coeffs[2 * n1 + i] = c.coeffs[2 * n1 + i].add(c2.coeffs[i]);
      }
      return c;
    }
  }