library BN256 {
    struct G1Point {
        uint x;
        uint y;
    }

    struct G2Point {
        uint[2] x;
        uint[2] y;
    }

    function P1() internal pure returns (G1Point memory) {
        return G1Point(1, 2);
    }

    function P2() internal pure returns (G2Point memory) {
        return G2Point(
            [11559732032986387107991004021392285783925812861821192530917403151452391805634,
            10857046999023057135944570762232829481370756359578518086990519993285655852781],

            [4082367875863433681332203403145435568316851327593401208105741076214120093531,
            8495653923123431417604973247489272438418190587263600148770280649306958101930]
        );
    }

    function pointAdd(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) {
        uint[4] memory input;
        input[0] = p1.x;
        input[1] = p1.y;
        input[2] = p2.x;
        input[3] = p2.y;
        assembly {
            if iszero(call(sub(gas, 2000), 0x6, 0, input, 0x80, r, 0x40)) {
                revert(0, 0)
            }
        }
    }

    function scalarMul(G1Point memory p, uint s) internal returns (G1Point memory r) {
        uint[3] memory input;
        input[0] = p.x;
        input[1] = p.y;
        input[2] = s;
        assembly {
            if iszero(call(sub(gas, 2000), 0x7, 0, input, 0x60, r, 0x40)) {
                revert(0, 0)
            }
        }
    }

    function negate(G1Point memory p) internal pure returns (G1Point memory) {
        if (p.x == 0 && p.y == 0) {
            return p;
        }
        uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
        return G1Point(p.x, q - p.y % q);
    }

    function hashToG1(bytes memory data) internal returns (G1Point memory) {
        uint256 h = uint256(keccak256(data));
        return scalarMul(P1(), h);
    }

    function G2Equal(G2Point memory p1, G2Point memory p2) internal pure returns (bool) {
        return p1.x[0] == p2.x[0] && p1.x[1] == p2.x[1] && p1.y[0] == p2.y[0] && p1.y[1] == p2.y[1];
    }

    // @return the result of computing the pairing check
    // check passes if e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
    // E.g. pairing([p1, p1.negate()], [p2, p2]) should return true.
    function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) {
        require(p1.length == p2.length);
        uint elements = p1.length;
        uint inputSize = elements * 6;
        uint[] memory input = new uint[](inputSize);

        for (uint i = 0; i < elements; i++)
        {
            input[i * 6 + 0] = p1[i].x;
            input[i * 6 + 1] = p1[i].y;
            input[i * 6 + 2] = p2[i].x[0];
            input[i * 6 + 3] = p2[i].x[1];
            input[i * 6 + 4] = p2[i].y[0];
            input[i * 6 + 5] = p2[i].y[1];
        }

        uint[1] memory out;
        bool success;
        assembly {
            success := call(
                sub(gas, 2000),
                0x8,
                0,
                add(input, 0x20),
                mul(inputSize, 0x20),
                out, 0x20
            )
        }
        return success && (out[0] != 0);
    }
}
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) {
    require(p1.length == p2.length);
    uint elements = p1.length;
    uint inputSize = elements * 6;
    uint[] memory input = new uint[](inputSize);

    for (uint i = 0; i < elements; i++)
    {
        input[i * 6 + 0] = p1[i].x;
        input[i * 6 + 1] = p1[i].y;
        input[i * 6 + 2] = p2[i].x[0];
        input[i * 6 + 3] = p2[i].x[1];
        input[i * 6 + 4] = p2[i].y[0];
        input[i * 6 + 5] = p2[i].y[1];
    }

    uint[1] memory out;
    bool success;
    assembly {
        success := call(
            sub(gas, 2000),
            0x8,
            0,
            add(input, 0x20),
            mul(inputSize, 0x20),
            out, 0x20
        )
    }
    return success && (out[0] != 0);
}

// @return the result of computing the pairing check
// check passes if e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
// E.g. pairing([p1, p1.negate()], [p2, p2]) should return true.