/*
 * $Id$
 * @author aakimov
 * @date   Jul 1, 2016 12:24:25 PM
 *
 * Copyright (C) 2016 Scytl Secure Electronic Voting SA
 *
 * All rights reserved.
 */
package com.scytl.math;

import static java.lang.Math.max;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.junit.Test;

/**
 * Generic abstract test of {@link MultiExponentiator} implementation.
 * 
 * @author aakimov
 */
public abstract class GenericMultiExponentiatorTest {
    private static final int COUNT = 100000;
    private static final int BOUND = 100;

    @Test
    public void testExponentiate() {
        Random random = new Random();
        for (int i = 0; i < COUNT; i++) {
            List<BigInteger> bases = new ArrayList<>(5);
            for (int j = 0; j < 5; j++) {
                bases.add(BigInteger.valueOf(random.nextInt(BOUND)));
            }
            List<BigInteger> exponents = new ArrayList<>(bases.size());
            int bitLength = 0;
            for (int j = 0; j < bases.size(); j++) {
                BigInteger exponent = BigInteger.valueOf(random.nextInt(BOUND));
                exponents.add(exponent);
                bitLength = max(bitLength, exponent.bitLength());
            }
            BigInteger modulus = BigInteger
                    .valueOf(random.nextInt(BOUND - 1) + 1);
            BigInteger expected = new SimpleMultiExponentiator(bases, modulus,
                    bitLength).exponentiate(exponents);
            BigInteger actual = newMultiExponentiator(bases, modulus, bitLength)
                    .exponentiate(exponents);
            if (!expected.equals(actual)) {
                String message = MessageFormat.format(
                        "Test with bases={0}, exponents={1}, modulus={2}, bitLength = {3}, failed, {4} is expected but {5} found.",
                        bases, exponents, modulus, bitLength, expected, actual);
                fail(message);
            }
        }
    }

    @Test
    public void testPerformNoBases() {
        List<BigInteger> bases = emptyList();
        List<BigInteger> exponents = emptyList();
        BigInteger modulus = BigInteger.TEN;
        assertEquals(BigInteger.ONE, newMultiExponentiator(bases, modulus, 1)
                .exponentiate(exponents));
    }

    @Test
    public void testPerformOneModulus() {
        List<BigInteger> bases = singletonList(BigInteger.valueOf(2));
        List<BigInteger> exponents = singletonList(BigInteger.valueOf(2));
        BigInteger modulus = BigInteger.ONE;
        assertEquals(BigInteger.ZERO, newMultiExponentiator(bases, modulus, 2)
                .exponentiate(exponents));
    }

    @Test
    public void testPerformZeroBase() {
        List<BigInteger> bases = singletonList(BigInteger.ZERO);
        List<BigInteger> exponents = singletonList(BigInteger.ONE);
        BigInteger modulus = BigInteger.TEN;
        assertEquals(BigInteger.ZERO, newMultiExponentiator(bases, modulus, 1)
                .exponentiate(exponents));
    }

    @Test
    public void testPerformZeroExponent() {
        List<BigInteger> bases = singletonList(BigInteger.TEN);
        List<BigInteger> exponents = singletonList(BigInteger.ZERO);
        BigInteger modulus = BigInteger.TEN;
        assertEquals(BigInteger.ONE, newMultiExponentiator(bases, modulus, 1)
                .exponentiate(exponents));
    }

    /**
     * Creates a {@link MultiExponentiator} instance to be tested.
     * 
     * @param bases
     *            the bases
     * @param exponents
     *            the exponents
     * @param modulus
     *            the modulus
     * @param maximumBitLengthOfExponent
     *            the maximum bit length of exponent
     * @return the instance
     */
    protected abstract MultiExponentiator newMultiExponentiator(
            List<BigInteger> bases, BigInteger modulus,
            int maximumBitLengthOfExponent);
}
