/*
 * $Id$
 * @author aakimov
 * @date   Jun 30, 2016 7:41:03 PM
 *
 * Copyright (C) 2016 Scytl Secure Electronic Voting SA
 *
 * All rights reserved.
 */
package com.scytl.math;

import static com.scytl.math.BigIntegers.isNegative;
import static com.scytl.math.BigIntegers.isNonPositive;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

import java.math.BigInteger;
import java.util.List;

import javax.annotation.Nonnegative;

/**
 * Basic abstract implementation of {@link MultiExponentiator}.
 * 
 * @author aakimov
 */
abstract class AbstractMultiExponentiator implements MultiExponentiator {
    /**
     * The bases. The subclasses must not change the content of the array.
     */
    protected final BigInteger[] bases;
    private final BigInteger modulus;
    private final int maximumBitLengthOfExponent;

    /**
     * Constructor.
     * 
     * @param bases
     *            the bases
     * @param modulus
     *            the modulus, must be positive
     * @param maximumBitLengthOfExponent
     *            the maximum bit length of exponent, must be positive
     * @throws IllegalArgumentException
     *             some bases are negative, the modulus is not positive or the
     *             maximum bit length is not positive.
     */
    public AbstractMultiExponentiator(@Nonnegative List<BigInteger> bases,
            @Nonnegative BigInteger modulus, int maximumBitLengthOfExponent) {
        for (BigInteger base : bases) {
            if (isNegative(base)) {
                throw new IllegalArgumentException("Base is negative.");
            }
        }
        if (isNonPositive(modulus)) {
            throw new IllegalArgumentException("Modulus is not positive");
        }
        if (maximumBitLengthOfExponent <= 0) {
            throw new IllegalArgumentException(
                    "Maximum bit length of exponent is not positive");
        }
        this.bases = bases.toArray(new BigInteger[bases.size()]);
        this.modulus = modulus;
        this.maximumBitLengthOfExponent = maximumBitLengthOfExponent;
    }

    @Override
    public final List<BigInteger> bases() {
        return unmodifiableList(asList(bases));
    }

    @Override
    public final BigInteger exponentiate(List<BigInteger> exponents) {
        if (exponents.size() != bases.length) {
            throw new IllegalArgumentException(
                    "The number of exponents does not math the number of bases.");
        }
        for (BigInteger exponent : exponents) {
            if (isNegative(exponent)) {
                throw new IllegalArgumentException("Exponent is negative.");
            }
            if (exponent.bitLength() > maximumBitLengthOfExponent) {
                throw new IllegalArgumentException("Exponent is too long.");
            }
        }
        return doExponentiate(
                exponents.toArray(new BigInteger[exponents.size()]));
    }

    @Override
    public final int maximumBitLengthOfExponent() {
        return maximumBitLengthOfExponent;
    }

    @Override
    public final BigInteger modulus() {
        return modulus;
    }

    /**
     * Performs exponentiation for given exponents. All the exponents are
     * not-negative and the number of exponents matches the number of bases.
     * Subclasses can modify the content of the supplied array.
     * 
     * @param exponents
     *            the exponents
     * @return the result of exponentiation.
     */
    protected abstract BigInteger doExponentiate(BigInteger[] exponents);
}
