/**
 * @file test_mpin_sign.c
 * @author Mike Scott
 * @brief Test and benchmark pairing functions
 *
 * LICENSE
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "amcl.h" /* Make sure and select a pairing-friendly curve in here! */

#define MIN_TIME 10.0
#define MIN_ITERS 10

int main()
{
    csprng RNG;
    BIG q,s,r,x,y,a,b,m;
    ECP P,G;
    FP2 wx,wy,f;
    FP4 c,cp,cpm1,cpm2,cr;
    ECP2 Q,W;
    FP12 g,w;
    unsigned long ran;

    int i,iterations;
    clock_t start;
    double elapsed;
    char pr[10];

    printf("Bechmark test PAIR - ");

#if CHOICE==BN254
    printf("BN254 Curve\n");
#endif
#if CHOICE==BN454
    printf("BN454 Curve\n");
#endif
#if CHOICE==BN646
    printf("BN646 Curve\n");
#endif

#if CHOICE==BN254_CX
    printf("BN254_CX Curve\n");
#endif
#if CHOICE==BN254_T
    printf("BN254_T Curve\n");
#endif
#if CHOICE==BN254_T2
    printf("BN254_T2 Curve\n");
#endif
#if CHOICE==BLS455
    printf("BLS455 Curve\n");
#endif
#if CHOICE==BLS383
    printf("BLS383 Curve\n");
#endif

#if CHUNK==16
    printf("16-bit Build\n\n");
#endif
#if CHUNK==32
    printf("32-bit Build\n\n");
#endif
#if CHUNK==64
    printf("64-bit Build\n\n");
#endif

    time((time_t *)&ran);
    pr[0]=ran;
    pr[1]=ran>>8;
    pr[2]=ran>>16;
    pr[3]=ran>>24;
    for (i=4; i<10; i++) pr[i]=i;

    RAND_seed(&RNG,10,pr);

    BIG_rcopy(x,CURVE_Gx);

    BIG_rcopy(y,CURVE_Gy);
    ECP_set(&G,x,y);


    BIG_rcopy(r,CURVE_Order);
    BIG_randomnum(s,r,&RNG);
    ECP_copy(&P,&G);
    PAIR_G1mul(&P,r);

    if (!ECP_isinf(&P))
    {
        printf("FAILURE - rG!=O\n");
        exit(EXIT_FAILURE);
    }

    iterations=0;
    start=clock();
    do
    {
        ECP_copy(&P,&G);
        PAIR_G1mul(&P,s);

        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("G1 mul              - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);


    BIG_rcopy(wx.a,CURVE_Pxa);
    FP_nres(wx.a);
    BIG_rcopy(wx.b,CURVE_Pxb);
    FP_nres(wx.b);
    BIG_rcopy(wy.a,CURVE_Pya);
    FP_nres(wy.a);
    BIG_rcopy(wy.b,CURVE_Pyb);
    FP_nres(wy.b);
    ECP2_set(&W,&wx,&wy);

    ECP2_copy(&Q,&W);
    ECP2_mul(&Q,r);

    if (!ECP2_isinf(&Q))
    {
        printf("FAILURE - rQ!=O\n");
        exit(EXIT_FAILURE);
    }

    iterations=0;
    start=clock();
    do
    {
        ECP2_copy(&Q,&W);
        PAIR_G2mul(&Q,s);

        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("G2 mul              - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);

    PAIR_ate(&w,&Q,&P);
    PAIR_fexp(&w);

    FP12_copy(&g,&w);

    PAIR_GTpow(&g,r);

    if (!FP12_isunity(&g))
    {
        printf("FAILURE - g^r!=1\n");
        exit(EXIT_FAILURE);
    }

    iterations=0;
    start=clock();
    do
    {
        FP12_copy(&g,&w);
        PAIR_GTpow(&g,s);

        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("GT pow              - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);

    BIG_rcopy(a,CURVE_Fra);
    BIG_rcopy(b,CURVE_Frb);
    FP2_from_BIGs(&f,a,b);

    BIG_rcopy(q,Modulus);

    BIG_copy(m,q);
    BIG_mod(m,r);

    BIG_copy(a,s);
    BIG_mod(a,m);

    BIG_copy(b,s);
    BIG_sdiv(b,m);

    FP12_copy(&g,&w);
    FP12_trace(&c,&g);

    FP12_frob(&g,&f);
    FP12_trace(&cp,&g);

    FP12_conj(&w,&w);
    FP12_mul(&g,&w);

    FP12_trace(&cpm1,&g);
    FP12_mul(&g,&w);
    FP12_trace(&cpm2,&g);

    iterations=0;
    start=clock();
    do
    {
        FP4_xtr_pow2(&cr,&cp,&c,&cpm1,&cpm2,a,b);
        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("GT pow (compressed) - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);

    iterations=0;
    start=clock();
    do
    {
        PAIR_ate(&w,&Q,&P);
        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("PAIRing ATE         - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);

    iterations=0;
    start=clock();
    do
    {
        FP12_copy(&g,&w);
        PAIR_fexp(&g);
        iterations++;
        elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
    }
    while (elapsed<MIN_TIME || iterations<MIN_ITERS);
    elapsed=1000.0*elapsed/iterations;
    printf("PAIRing FEXP        - %8d iterations  ",iterations);
    printf(" %8.2lf ms per iteration\n",elapsed);

    ECP_copy(&P,&G);
    ECP2_copy(&Q,&W);

    PAIR_G1mul(&P,s);
    PAIR_ate(&g,&Q,&P);
    PAIR_fexp(&g);

    ECP_copy(&P,&G);

    PAIR_G2mul(&Q,s);
    PAIR_ate(&w,&Q,&P);
    PAIR_fexp(&w);

    if (!FP12_equals(&g,&w))
    {
        printf("FAILURE - e(sQ,p)!=e(Q,sP) \n");
        exit(EXIT_FAILURE);
    }

    ECP2_copy(&Q,&W);
    PAIR_ate(&g,&Q,&P);
    PAIR_fexp(&g);

    PAIR_GTpow(&g,s);

    if (!FP12_equals(&g,&w))
    {
        printf("FAILURE - e(sQ,p)!=e(Q,P)^s \n");
        exit(EXIT_FAILURE);
    }

    printf("\nSUCCESS BENCHMARK TEST OF PAIR FUNCTIONS PASSED\n\n");
    exit(EXIT_SUCCESS);
}
