/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.proofs.maurer.function;

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

import org.junit.BeforeClass;
import org.junit.Test;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;

/**
 * Tests of PhiFunction class.
 */
public class PhiFunctionTest {

    private static int _numInputs = 1;

    private static int _numOutputs = 1;

    private static int[][][] _computationRules;

    private static List<ZpGroupElement> _baseElements;

    private static ZpSubgroup _group;

    private static PhiFunction<BigInteger> _phiFunction;

    @BeforeClass
    public static void setUp() throws GeneralCryptoLibException {

        _computationRules = new int[][][] {{{1, 1 } } };

        BigInteger ZpSubgroupP = new BigInteger("23");
        BigInteger ZpSubgroupQ = new BigInteger("11");
        BigInteger ZpSubgroupG = new BigInteger("2");

        _group =
            new ZpSubgroup(ZpSubgroupG,
                ZpSubgroupP, ZpSubgroupQ);

        _baseElements = new ArrayList<ZpGroupElement>();
        _baseElements.add(_group.getGenerator());

        _phiFunction =
            new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
                _baseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNullGroupWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        ZpSubgroup nullGroup = null;

        new PhiFunction<BigInteger>(nullGroup, _numInputs, _numOutputs,
            _baseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNegativeNumInputsWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int negativeNumInputs = -1;

        new PhiFunction<BigInteger>(_group, negativeNumInputs,
            _numOutputs, _baseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNegativeNumOutputsWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int negativeNumOutputs = -1;

        new PhiFunction<BigInteger>(_group, _numInputs,
            negativeNumOutputs, _baseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNullBaseElementsWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        List<ZpGroupElement> nullBaseElements = null;

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            nullBaseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenEmptyBaseElementsWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        List<ZpGroupElement> emptyBaseElements =
            new ArrayList<ZpGroupElement>();

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            emptyBaseElements, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenBaseElementsWhichAreNotGroupMembersWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        List<ZpGroupElement> baseElementsNotGroupMembers =
            new ArrayList<ZpGroupElement>();

        BigInteger q = _group.getQ();
        BigInteger p = q.multiply(BigInteger.TEN);
        BigInteger value = p.subtract(BigInteger.ONE);

        ZpGroupElement baseElement =
            new ZpGroupElement(value, _group);
        baseElementsNotGroupMembers.add(baseElement);

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            baseElementsNotGroupMembers, _computationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNullComputationalRulesWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] nullComputationRules = null;

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, nullComputationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenZeroLengthComputationalRulesWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] zeroLengthComputationRules = new int[0][0][0];

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, zeroLengthComputationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenRulesWithListOfWrongSizeWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] wrongLengthComputationRules =
            new int[][][] { {{1, 1 } }, {{1, 1 } }, {{1, 1 } }, {{1, 1 } } };

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, wrongLengthComputationRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenRulesFirstIndexValueNegativeWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] badRules =
            _computationRules = new int[][][] {{{-1, 1 } } };

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, badRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenRulesFirstIndexValueLargerThenNumBasesWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] badRules =
            _computationRules = new int[][][] {{{2, 1 } } };

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, badRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenRulesSecondIndexValueNegativeWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] badRules =
            _computationRules = new int[][][] {{{1, -1 } } };

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, badRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenRulesSecondIndexValueLargerThenNumBasesWhenCreatePhiFunctionThenException()
            throws GeneralCryptoLibException {

        int[][][] badRules =
            _computationRules = new int[][][] {{{1, 2 } } };

        new PhiFunction<BigInteger>(_group, _numInputs, _numOutputs,
            _baseElements, badRules);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenNullInputWhenCalculatePhiThenException()
            throws GeneralCryptoLibException {

        List<Exponent> nullInputs = null;

        _phiFunction.calculatePhi(nullInputs);
    }

    @Test(expected = GeneralCryptoLibException.class)
    public void givenInputWithSizeNotExpectedNumOfInputsWhenCalculatePhiThenException()
            throws GeneralCryptoLibException {

        List<Exponent> notRSizeInput = new ArrayList<Exponent>();

        _phiFunction.calculatePhi(notRSizeInput);
    }
}
