@Test
  public void dblPrecomputedTableContainsExpectedGroupElements() {
    // Arrange:
    GroupElement g = ed25519.getB();
    GroupElement h = MathUtils.addGroupElements(g, g);

    // Act + Assert:
    for (int i = 0; i < 8; i++) {
      Assert.assertThat(
          MathUtils.toRepresentation(g, GroupElement.Representation.PRECOMP),
          IsEqual.equalTo(ed25519.getB().dblPrecmp[i]));
      g = MathUtils.addGroupElements(g, h);
    }
  }
  @Test
  public void precomputedTableContainsExpectedGroupElements() {
    // Arrange:
    GroupElement g = ed25519.getB();

    // Act + Assert:
    for (int i = 0; i < 32; i++) {
      GroupElement h = g;
      for (int j = 0; j < 8; j++) {
        Assert.assertThat(
            MathUtils.toRepresentation(h, GroupElement.Representation.PRECOMP),
            IsEqual.equalTo(ed25519.getB().precmp[i][j]));
        h = MathUtils.addGroupElements(h, g);
      }
      for (int k = 0; k < 8; k++) {
        g = MathUtils.addGroupElements(g, g);
      }
    }
  }
  @Test
  public void scalarMultiplyBasePointWithOneReturnsBasePoint() {
    // Arrange:
    final GroupElement basePoint = ed25519.getB();

    // Act:
    final GroupElement g = basePoint.scalarMultiply(curve.getField().ONE.toByteArray());

    // Assert:
    Assert.assertThat(basePoint, IsEqual.equalTo(g));
  }
  @Test
  public void scalarMultiplyBasePointWithZeroReturnsNeutralElement() {
    // Arrange:
    final GroupElement basePoint = ed25519.getB();

    // Act:
    final GroupElement g = basePoint.scalarMultiply(curve.getField().ZERO.toByteArray());

    // Assert:
    Assert.assertThat(curve.getZero(GroupElement.Representation.P3), IsEqual.equalTo(g));
  }
  /**
   * Test method for {@link GroupElement#scalarMultiply(byte[])}. Test values generated with Python
   * Ed25519 implementation.
   */
  @Test
  public void testScalarMultiplyByteArray() {
    // Little-endian
    byte[] zero =
        Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
    byte[] one =
        Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
    byte[] two =
        Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000");
    byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c");
    GroupElement A =
        new GroupElement(
            curve,
            Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66"));

    assertThat(
        "scalarMultiply(0) failed",
        ed25519.getB().scalarMultiply(zero),
        is(equalTo(curve.getZero(GroupElement.Representation.P3))));
    assertThat(
        "scalarMultiply(1) failed",
        ed25519.getB().scalarMultiply(one),
        is(equalTo(ed25519.getB())));
    assertThat(
        "scalarMultiply(2) failed",
        ed25519.getB().scalarMultiply(two),
        is(equalTo(ed25519.getB().dbl())));

    assertThat("scalarMultiply(a) failed", ed25519.getB().scalarMultiply(a), is(equalTo(A)));
  }
  // This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result.
  @Test
  public void scalarMultiplyBasePointReturnsExpectedResult() {
    for (int i = 0; i < 100; i++) {
      // Arrange:
      final GroupElement basePoint = ed25519.getB();
      final FieldElement f = MathUtils.getRandomFieldElement();

      // Act:
      final GroupElement g = basePoint.scalarMultiply(f.toByteArray());
      final GroupElement h = MathUtils.scalarMultiplyGroupElement(basePoint, f);

      // Assert:
      Assert.assertThat(g, IsEqual.equalTo(h));
    }
  }
  /** Test method for {@link GroupElement#toP2()}. */
  @Test
  public void testToP2() {
    GroupElement p3zero = curve.getZero(GroupElement.Representation.P3);
    GroupElement t = p3zero.toP2();
    assertThat(t.repr, is(GroupElement.Representation.P2));
    assertThat(t.X, is(p3zero.X));
    assertThat(t.Y, is(p3zero.Y));
    assertThat(t.Z, is(p3zero.Z));
    assertThat(t.T, is((FieldElement) null));

    GroupElement B = ed25519.getB();
    t = B.toP2();
    assertThat(t.repr, is(GroupElement.Representation.P2));
    assertThat(t.X, is(B.X));
    assertThat(t.Y, is(B.Y));
    assertThat(t.Z, is(B.Z));
    assertThat(t.T, is((FieldElement) null));
  }
  // This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result.
  @Test
  public void doubleScalarMultiplyVariableTimeReturnsExpectedResult() {
    for (int i = 0; i < 50; i++) {
      // Arrange:
      final GroupElement basePoint = ed25519.getB();
      final GroupElement g = MathUtils.getRandomGroupElement();
      g.precompute(false);
      final FieldElement f1 = MathUtils.getRandomFieldElement();
      final FieldElement f2 = MathUtils.getRandomFieldElement();

      // Act:
      final GroupElement h1 =
          basePoint.doubleScalarMultiplyVariableTime(g, f2.toByteArray(), f1.toByteArray());
      final GroupElement h2 = MathUtils.doubleScalarMultiplyGroupElements(basePoint, f1, g, f2);

      // Assert:
      Assert.assertThat(h1, IsEqual.equalTo(h2));
    }
  }
  @Test
  public void testDoubleScalarMultiplyVariableTime() {
    // Little-endian
    byte[] zero =
        Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
    byte[] one =
        Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
    byte[] two =
        Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000");
    byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c");
    GroupElement A =
        new GroupElement(
            curve,
            Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66"));
    GroupElement B = ed25519.getB();
    GroupElement geZero = curve.getZero(GroupElement.Representation.P3);
    geZero.precompute(false);

    // 0 * GE(0) + 0 * GE(0) = GE(0)
    assertThat(geZero.doubleScalarMultiplyVariableTime(geZero, zero, zero), is(equalTo(geZero)));
    // 0 * GE(0) + 0 * B = GE(0)
    assertThat(B.doubleScalarMultiplyVariableTime(geZero, zero, zero), is(equalTo(geZero)));
    // 1 * GE(0) + 0 * B = GE(0)
    assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, zero), is(equalTo(geZero)));
    // 1 * GE(0) + 1 * B = B
    assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, one), is(equalTo(B)));
    // 1 * B + 1 * B = 2 * B
    assertThat(B.doubleScalarMultiplyVariableTime(B, one, one), is(equalTo(B.dbl())));
    // 1 * B + 2 * B = 3 * B
    assertThat(
        B.doubleScalarMultiplyVariableTime(B, one, two),
        is(equalTo(B.dbl().toP3().add(B.toCached()))));
    // 2 * B + 2 * B = 4 * B
    assertThat(B.doubleScalarMultiplyVariableTime(B, two, two), is(equalTo(B.dbl().toP3().dbl())));

    // 0 * B + a * B = A
    assertThat(B.doubleScalarMultiplyVariableTime(B, zero, a), is(equalTo(A)));
    // a * B + 0 * B = A
    assertThat(B.doubleScalarMultiplyVariableTime(B, a, zero), is(equalTo(A)));
    // a * B + a * B = 2 * A
    assertThat(B.doubleScalarMultiplyVariableTime(B, a, a), is(equalTo(A.dbl())));
  }
 /** Test method for {@link GroupElement#select(int, int)}. */
 @Test
 public void testSelect() {
   GroupElement B = ed25519.getB();
   for (int i = 0; i < 32; i++) {
     // 16^i 0 B
     assertThat(
         i + ",0", B.select(i, 0), is(equalTo(GroupElement.precomp(curve, ONE, ONE, ZERO))));
     for (int j = 1; j < 8; j++) {
       // 16^i r_i B
       GroupElement t = B.select(i, j);
       assertThat(i + "," + j, t, is(equalTo(B.precmp[i][j - 1])));
       // -16^i r_i B
       t = B.select(i, -j);
       GroupElement neg =
           GroupElement.precomp(
               curve, B.precmp[i][j - 1].Y, B.precmp[i][j - 1].X, B.precmp[i][j - 1].Z.negate());
       assertThat(i + "," + -j, t, is(equalTo(neg)));
     }
   }
 }
 /** Test method for {@link GroupElement#dbl()}. */
 @Test
 public void testDbl() {
   GroupElement B = ed25519.getB();
   // 2 * B = B + B
   assertThat(B.dbl(), is(equalTo(B.add(B.toCached()))));
 }
 /** Test method for {@link GroupElement#precompute(boolean)}. */
 @Test
 public void testPrecompute() {
   GroupElement B = ed25519.getB();
   assertThat(B.precmp, is(equalTo(PrecomputationTestVectors.testPrecmp)));
   assertThat(B.dblPrecmp, is(equalTo(PrecomputationTestVectors.testDblPrecmp)));
 }