package org.bouncycastle.math.ec.test;

import java.math.BigInteger;
import java.security.SecureRandom;

import junit.framework.TestCase;

import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.CustomNamedCurves;

/**
 * Compares the performance of the the window NAF point multiplication against conventional point
 * multiplication.
 */
public class ECPointPerformanceTest extends TestCase
{
    public static final int PRE_ROUNDS = 100;
    public static final int NUM_ROUNDS = 1000;

    private static String[] COORD_NAMES = new String[]{ "AFFINE", "HOMOGENEOUS", "JACOBIAN", "JACOBIAN-CHUDNOVSKY",
        "JACOBIAN-MODIFIED", "LAMBDA-AFFINE", "LAMBDA-PROJECTIVE", "SKEWED" };

    private void randMult(String curveName) throws Exception
    {
        X9ECParameters spec = ECNamedCurveTable.getByName(curveName);
        if (spec != null)
        {
            randMult(curveName, spec);
        }

        spec = CustomNamedCurves.getByName(curveName);
        if (spec != null)
        {
            randMult(curveName + " (custom)", spec);
        }
    }

    private void randMult(String label, X9ECParameters spec) throws Exception
    {
        ECCurve C = spec.getCurve();
        ECPoint G = (ECPoint)spec.getG();
        BigInteger n = spec.getN();

        SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
        random.setSeed(System.currentTimeMillis());

        System.out.println(label);

        int[] coords = ECCurve.getAllCoordinateSystems();
        for (int i = 0; i < coords.length; ++i)
        {
            int coord = coords[i];
            if (C.supportsCoordinateSystem(coord))
            {
                ECCurve c = C;
                ECPoint g = G;
                if (c.getCoordinateSystem() != coord)
                {
                    c = C.configure().setCoordinateSystem(coord).create();
                    g = c.importPoint(G);
                }

                double avgDuration = randMult(random, c, g, n);
                String coordName = COORD_NAMES[coord];
                StringBuffer sb = new StringBuffer();
                sb.append("  ");
                sb.append(coordName);
                for (int j = coordName.length(); j < 30; ++j)
                {
                    sb.append(' ');
                }
                sb.append(": ");
                sb.append(avgDuration);
                sb.append("ms");
                System.out.println(sb.toString());
            }
        }

        System.out.println();
    }

    private double randMult(SecureRandom random, ECCurve c, ECPoint g, BigInteger n) throws Exception
    {
        BigInteger[] ks = new BigInteger[16];
        for (int i = 0; i < ks.length; ++i)
        {
            ks[i] = new BigInteger(n.bitLength() - 1, random);
        }

        int ki = 0;
        ECPoint p = g;
        for (int i = 1; i <= PRE_ROUNDS; i++)
        {
            BigInteger k = ks[ki];
            p = g.multiply(k);
            if (++ki == ks.length)
            {
                ki = 0;
                g = p;
            }
        }
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= NUM_ROUNDS; i++)
        {
            BigInteger k = ks[ki];
            p = g.multiply(k);
            if (++ki == ks.length)
            {
                ki = 0;
                g = p;
            }
        }
        long endTime = System.currentTimeMillis();

        return (double)(endTime - startTime) / NUM_ROUNDS;
    }

    public void testMultiply() throws Exception
    {
        // Enumeration e = SECNamedCurves.getNames();
        // while (e.hasMoreElements())
        // {
        // String name = (String)e.nextElement();
        // randMult(name);
        // }

        randMult("sect113r1");
        randMult("sect113r2");
        randMult("sect131r1");
        randMult("sect131r2");
        randMult("sect163k1");
        randMult("sect163r1");
        randMult("sect163r2");
        randMult("sect193r1");
        randMult("sect193r2");
        randMult("sect233k1");
        randMult("sect233r1");
        randMult("sect239k1");
        randMult("sect283k1");
        randMult("sect283r1");
        randMult("sect409k1");
        randMult("sect409r1");
        randMult("sect571k1");
        randMult("sect571r1");

        randMult("secp112r1");
        randMult("secp112r2");
        randMult("secp128r1");
        randMult("secp128r2");
        randMult("secp160k1");
        randMult("secp160r1");
        randMult("secp160r2");
        randMult("secp192k1");
        randMult("secp192r1");
        randMult("secp224k1");
        randMult("secp224r1");
        randMult("secp256k1");
        randMult("secp256r1");
        randMult("secp384r1");
        randMult("secp521r1");

        randMult("brainpoolp160r1");
        randMult("brainpoolp160t1");
        randMult("brainpoolp192r1");
        randMult("brainpoolp192t1");
        randMult("brainpoolp224r1");
        randMult("brainpoolp224t1");
        randMult("brainpoolp256r1");
        randMult("brainpoolp256t1");
        randMult("brainpoolp320r1");
        randMult("brainpoolp320t1");
        randMult("brainpoolp384r1");
        randMult("brainpoolp384t1");
        randMult("brainpoolp512r1");
        randMult("brainpoolp512t1");

        randMult("prime192v1");
        randMult("prime192v2");
        randMult("prime192v3");
        randMult("prime239v1");
        randMult("prime239v2");
        randMult("prime239v3");
        randMult("prime256v1");
        randMult("c2pnb163v1");
        randMult("c2pnb163v2");
        randMult("c2pnb163v3");
        randMult("c2pnb176w1");
        randMult("c2tnb191v1");
        randMult("c2tnb191v2");
        randMult("c2tnb191v3");
        randMult("c2pnb208w1");
        randMult("c2tnb239v1");
        randMult("c2tnb239v2");
        randMult("c2tnb239v3");
        randMult("c2pnb272w1");
        randMult("c2pnb304w1");
        randMult("c2tnb359v1");
        randMult("c2pnb368w1");
        randMult("c2tnb431r1");
    }

    // public static void main(String argv[]) throws Exception
    // {
    // ECMultiplyPerformanceTest test = new ECMultiplyPerformanceTest();
    // test.testMultiply();
    // }
}

@@ -595,17 +595,16 @@
 NDS = 10
-0
 ;%0A    pu
@@ -641,17 +641,16 @@
 DS = 100
-0
 ;%0A%0A    p

Revert number of test rounds to old values