@Test(expected = IllegalArgumentException.class)
  public void toCachedThrowsIfGroupElementHasP1P1Representation() {
    // Arrange:
    final GroupElement g =
        MathUtils.toRepresentation(
            MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1);

    // Assert:
    g.toCached();
  }
  @Test
  public void addingNeutralGroupElementDoesNotChangeGroupElement() {
    final GroupElement neutral =
        GroupElement.p3(
            curve,
            curve.getField().ZERO,
            curve.getField().ONE,
            curve.getField().ONE,
            curve.getField().ZERO);
    for (int i = 0; i < 1000; i++) {
      // Arrange:
      final GroupElement g = MathUtils.getRandomGroupElement();

      // Act:
      final GroupElement h1 = g.add(neutral.toCached());
      final GroupElement h2 = neutral.add(g.toCached());

      // Assert:
      Assert.assertThat(g, IsEqual.equalTo(h1));
      Assert.assertThat(g, IsEqual.equalTo(h2));
    }
  }
  @Test
  public void subReturnsExpectedResult() {
    for (int i = 0; i < 1000; i++) {
      // Arrange:
      final GroupElement g1 = MathUtils.getRandomGroupElement();
      final GroupElement g2 = MathUtils.getRandomGroupElement();

      // Act:
      final GroupElement h1 = g1.sub(g2.toCached());
      final GroupElement h2 = MathUtils.addGroupElements(g1, MathUtils.negateGroupElement(g2));

      // Assert:
      Assert.assertThat(h2, IsEqual.equalTo(h1));
    }
  }
  @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
  public void toCachedReturnsExpectedResultIfGroupElementHasP3Representation() {
    for (int i = 0; i < 10; i++) {
      // Arrange:
      final GroupElement g = MathUtils.getRandomGroupElement();

      // Act:
      final GroupElement h1 = g.toCached();
      final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.CACHED);

      // Assert:
      Assert.assertThat(h1, IsEqual.equalTo(h2));
      Assert.assertThat(
          h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED));
      Assert.assertThat(h1, IsEqual.equalTo(g));
      Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getY().add(g.getX())));
      Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().subtract(g.getX())));
      Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ()));
      Assert.assertThat(h1.getT(), IsEqual.equalTo(g.getT().multiply(curve.get2D())));
    }
  }
  @Test
  public void toCachedReturnsExpectedResultIfGroupElementHasCachedRepresentation() {
    for (int i = 0; i < 10; i++) {
      // Arrange:
      final GroupElement g =
          MathUtils.toRepresentation(
              MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED);

      // Act:
      final GroupElement h = g.toCached();

      // Assert:
      Assert.assertThat(h, IsEqual.equalTo(g));
      Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED));
      Assert.assertThat(h, IsEqual.equalTo(g));
      Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX()));
      Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY()));
      Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ()));
      Assert.assertThat(h.getT(), IsEqual.equalTo(g.getT()));
    }
  }
 /** 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()))));
 }