class G2Point implements Group<G2Point> {

  public static G2Point fromBytesCompressed(Bytes bytes) {
    checkArgument(
        bytes.size() == 2 * fpPointSize,
        "Expected %s bytes but received %s",
        2 * fpPointSize,
        bytes.size());
    byte[] xImBytes = bytes.slice(0, fpPointSize).toArray();
    byte[] xReBytes = bytes.slice(fpPointSize, fpPointSize).toArray();

    boolean aIn = (xImBytes[0] & (byte) (1 << 5)) != 0;
    boolean bIn = (xImBytes[0] & (byte) (1 << 6)) != 0;
    boolean cIn = (xImBytes[0] & (byte) (1 << 7)) != 0;
    xImBytes[0] &= (byte) 31;

    if ((xReBytes[0] & (byte) 224) != 0) {
      throw new IllegalArgumentException("The input has non-zero a2, b2 or c2 flag on xRe");
    }

    if (!cIn) {
      throw new IllegalArgumentException("The serialized input does not have the C flag set.");
    }

    if (bIn) {
      if (!aIn && Bytes.wrap(xImBytes).isZero() && Bytes.wrap(xReBytes).isZero()) {
        // This is a correctly formed serialization of infinity
        return new G2Point();
      } else {
        // The input is malformed
        throw new IllegalArgumentException(
            "The serialized input has B flag set, but A flag is set, or X is non-zero.");
      }
    }

    // We must check that x < q (the curve modulus) for this serialization to be valid
    // We raise an exception (that should be caught) if this check fails: somebody might feed us
    // faulty input.
    BIG xImBig = BIG.fromBytes(xImBytes);
    BIG xReBig = BIG.fromBytes(xReBytes);
    BIG modulus = new BIG(ROM.Modulus);
    if (BIG.comp(modulus, xReBig) <= 0 || BIG.comp(modulus, xImBig) <= 0) {
      throw new IllegalArgumentException(
          "The deserialized X real or imaginary coordinate is too large.");
    }

    ECP2 point = new ECP2(new FP2(xReBig, xImBig));

    if (point.is_infinity()) {
      throw new IllegalArgumentException("X coordinate is not on the curve.");
    }

    if (!isInGroup(point)) {
      throw new IllegalArgumentException("The deserialized point is not in the G2 subgroup.");
    }

    // Did we get the right branch of the sqrt?
    if (aIn != Util.calculateYFlag(point.getY().getB())) {
      // We didn't: so choose the other branch of the sqrt.
      FP2 x = point.getX();
      FP2 yneg = point.getY();
      yneg.neg();
      point = new ECP2(x, yneg);
    }

    return new G2Point(point);
  }

  public  G2Point();
  public  G2Point(ECP2 point);

}