# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test library of quantum circuits."""

import unittest
from ddt import ddt, data, unpack
import numpy as np

from qiskit.test.base import QiskitTestCase
from qiskit.circuit.library import FourierChecking
from qiskit.circuit.exceptions import CircuitError
from qiskit.quantum_info import Operator


@ddt
class TestFourierCheckingLibrary(QiskitTestCase):
    """Test the Fourier Checking circuit."""

    def assertFourierCheckingIsCorrect(self, f_truth_table, g_truth_table, fc_circuit):
        """Assert that the Fourier Checking circuit produces the correct matrix."""

        simulated = Operator(fc_circuit)

        num_qubits = int(np.log2(len(f_truth_table)))

        # create Hadamard matrix, re-use approach from TestMCMT
        h_i = 1 / np.sqrt(2) * np.array([[1, 1], [1, -1]])
        h_tot = np.array([1])
        for _ in range(num_qubits):
            h_tot = np.kron(h_tot, h_i)

        f_mat = np.diag(f_truth_table)
        g_mat = np.diag(g_truth_table)

        expected = np.linalg.multi_dot([h_tot, g_mat, h_tot, f_mat, h_tot])
        expected = Operator(expected)

        self.assertTrue(expected.equiv(simulated))

    @data(([1, -1, -1, -1], [1, 1, -1, -1]), ([1, 1, 1, 1], [1, 1, 1, 1]))
    @unpack
    def test_fourier_checking(self, f_truth_table, g_truth_table):
        """Test if the Fourier Checking circuit produces the correct matrix."""
        fc_circuit = FourierChecking(f_truth_table, g_truth_table)
        self.assertFourierCheckingIsCorrect(f_truth_table, g_truth_table, fc_circuit)

    @data(([1, -1, -1, -1], [1, 1, -1]), ([1], [-1]), ([1, -1, -1, -1, 1], [1, 1, -1, -1, 1]))
    @unpack
    def test_invalid_input_raises(self, f_truth_table, g_truth_table):
        """Test that invalid input truth tables raise an error."""
        with self.assertRaises(CircuitError):
            FourierChecking(f_truth_table, g_truth_table)


if __name__ == "__main__":
    unittest.main()
