/**
   * Invert this field element and return the result. The inverse is found via Fermat's little
   * theorem: a^p congruent a mod p and therefore a^(p-2) congruent a^-1 mod p
   *
   * @return The inverse of this field element.
   */
  public Ed25519FieldElement invert() {
    Ed25519FieldElement f0, f1;

    // comments describe how exponent is created

    // 2 == 2 * 1
    f0 = this.square();

    // 9 == 9
    f1 = this.pow2to9();

    // 11 == 9 + 2
    f0 = f0.multiply(f1);

    // 2^252 - 2^2
    f1 = this.pow2to252sub4();

    // 2^255 - 2^5
    for (int i = 1; i < 4; ++i) {
      f1 = f1.square();
    }

    // 2^255 - 21
    return f1.multiply(f0);
  }
  /**
   * Calculates and returns one of the square roots of u / v.
   *
   * <pre>{@code
   * x = (u * v^3) * (u * v^7)^((p - 5) / 8) ==> x^2 = +-(u / v).
   * }</pre>
   *
   * Note that this means x can be sqrt(u / v), -sqrt(u / v), +i * sqrt(u / v), -i * sqrt(u / v).
   *
   * @param u The nominator of the fraction.
   * @param v The denominator of the fraction.
   * @return The square root of u / v.
   */
  public static Ed25519FieldElement sqrt(final Ed25519FieldElement u, final Ed25519FieldElement v) {
    Ed25519FieldElement x;
    final Ed25519FieldElement v3;

    // v3 = v^3
    v3 = v.square().multiply(v);

    // x = (v3^2) * v * u = u * v^7
    x = v3.square().multiply(v).multiply(u);

    //  x = (u * v^7)^((q - 5) / 8)
    x = x.pow2to252sub4().multiply(x); // 2^252 - 3

    // x = u * v^3 * (u * v^7)^((q - 5) / 8)
    x = v3.multiply(u).multiply(x);

    return x;
  }
  /**
   * Computes this field element to the power of (2^252 - 4) and returns the result. This is a
   * helper function for calculating the square root.
   *
   * @return This field element to the power of (2^252 - 4).
   */
  private Ed25519FieldElement pow2to252sub4() {
    Ed25519FieldElement f0, f1, f2;

    // 2 == 2 * 1
    f0 = this.square();

    // 9
    f1 = this.pow2to9();

    // 11 == 9 + 2
    f0 = f0.multiply(f1);

    // 22 == 2 * 11
    f0 = f0.square();

    // 31 == 22 + 9
    f0 = f1.multiply(f0);

    // 2^6 - 2^1
    f1 = f0.square();

    // 2^10 - 2^5
    for (int i = 1; i < 5; ++i) {
      f1 = f1.square();
    }

    // 2^10 - 2^0
    f0 = f1.multiply(f0);

    // 2^11 - 2^1
    f1 = f0.square();

    // 2^20 - 2^10
    for (int i = 1; i < 10; ++i) {
      f1 = f1.square();
    }

    // 2^20 - 2^0
    f1 = f1.multiply(f0);

    // 2^21 - 2^1
    f2 = f1.square();

    // 2^40 - 2^20
    for (int i = 1; i < 20; ++i) {
      f2 = f2.square();
    }

    // 2^40 - 2^0
    f1 = f2.multiply(f1);

    // 2^41 - 2^1
    f1 = f1.square();

    // 2^50 - 2^10
    for (int i = 1; i < 10; ++i) {
      f1 = f1.square();
    }

    // 2^50 - 2^0
    f0 = f1.multiply(f0);

    // 2^51 - 2^1
    f1 = f0.square();

    // 2^100 - 2^50
    for (int i = 1; i < 50; ++i) {
      f1 = f1.square();
    }

    // 2^100 - 2^0
    f1 = f1.multiply(f0);

    // 2^101 - 2^1
    f2 = f1.square();

    // 2^200 - 2^100
    for (int i = 1; i < 100; ++i) {
      f2 = f2.square();
    }

    // 2^200 - 2^0
    f1 = f2.multiply(f1);

    // 2^201 - 2^1
    f1 = f1.square();

    // 2^250 - 2^50
    for (int i = 1; i < 50; ++i) {
      f1 = f1.square();
    }

    // 2^250 - 2^0
    f0 = f1.multiply(f0);

    // 2^251 - 2^1
    f0 = f0.square();

    // 2^252 - 2^2
    return f0.square();
  }