import os
import filecmp
import unittest
import numpy as np
from openql import openql as ql
from utils import file_compare

curdir = os.path.dirname(os.path.realpath(__file__))
output_dir = os.path.join(curdir, 'test_output')

class Test_bugs(unittest.TestCase):
    @classmethod
    def setUp(self):
        ql.initialize()
        ql.set_option('output_dir', output_dir)
        ql.set_option('use_default_gates', 'yes')
        ql.set_option('log_level', 'LOG_WARNING')

    # @unittest.expectedFailure
    # @unittest.skip
    def test_typecast(self):
        sweep_points = [1,2]
        num_circuits = 1
        num_qubits = 2
        platf = ql.Platform("starmon", 'cc_light')
        p = ql.Program('test_bug', platf, num_qubits)
        p.set_sweep_points(sweep_points)
        k = ql.Kernel('kernel1', platf, num_qubits)

        qubit = 1

        k.identity(np.int(qubit))
        k.identity(np.int32(qubit))
        k.identity(np.int64(qubit))

        k.identity(np.uint(qubit))
        k.identity(np.uint32(qubit))
        k.identity(np.uint64(qubit))

        # add the kernel to the program
        p.add_kernel(k)


    # relates to https://github.com/QE-Lab/OpenQL/issues/171
    # various runs of compiles were generating different results or in the best
    # case strange errors. So multiple (NCOMPILES) runs of compile are executed
    # to make sure there is no error and output generated in all these runs is same
    # JvS: more likely, it also had to do with the classical register allocator
    # depending on stuff like Python's garbage collection to free a register.
    # The register numbers have to be hardcoded now for that reason.
    def test_stateful_behavior(self):
        ql.set_option('optimize', 'no')
        ql.set_option('scheduler', 'ALAP')

        platform = ql.Platform("myPlatform", 'cc_light')

        sweep_points = [1]
        nqubits = 3
        nregs = 3

        p = ql.Program("statelessProgram", platform, nqubits, nregs)
        p.set_sweep_points(sweep_points)
        k = ql.Kernel("aKernel", platform, nqubits, nregs)

        k.prepz(0)
        k.gate('rx180', [0])
        k.measure(0)

        rd = ql.CReg(0)
        rs1 = ql.CReg(1)
        rs2 = ql.CReg(2)

        k.classical(rs1, ql.Operation(3))
        k.classical(rs1, ql.Operation(4))
        k.classical(rd, ql.Operation(rs1, '+', rs2))

        p.add_kernel(k)

        NCOMPILES=50
        QISA_fn = os.path.join(output_dir, p.name+'_last.qasm')
        for i in range(NCOMPILES):
            p.compile()
            self.setUpClass()
            QISA_fn_i = os.path.join(output_dir, p.name+'_'+str(i)+'_last.qasm')
            os.rename(QISA_fn,QISA_fn_i)

        for i in range(NCOMPILES-1):
            QISA_fn_1 = os.path.join(output_dir, p.name+'_'+str(i)+'_last.qasm')
            QISA_fn_2 = os.path.join(output_dir, p.name+'_'+str(i+1)+'_last.qasm')
            self.assertTrue( file_compare(QISA_fn_1, QISA_fn_2))

# Unclear how this test works.
# When clear, enable it again.
# Now it fails, not clear how to repair, so it is disabled.
#    def test_empty_infinite_loop(self):
#        name = 'empty_infinite_loop'
#        in_fn = 'test_' + name + '.cq'
#        out_fn = 'test_output/' + name + '_out.cq'
#        gold_fn = 'golden/' + name + '_out.cq'
#        ql.initialize()
#        #ql.set_option('log_level', 'LOG_DEBUG')
#        ql.compile(in_fn)
#        self.assertTrue(file_compare(out_fn, gold_fn))

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

[(9, 'os.path.join', 'os.path.join', False, 'import os\n'), (8, 'os.path.realpath', 'os.path.realpath', False, 'import os\n'), (106, 'unittest.main', 'unittest.main', False, 'import unittest\n'), (14, 'openql.openql.initialize', 'ql.initialize', True, 'from openql import openql as ql\n'), (15, 'openql.openql.set_option', 'ql.set_option', True, 'from openql import openql as ql\n'), (16, 'openql.openql.set_option', 'ql.set_option', True, 'from openql import openql as ql\n'), (17, 'openql.openql.set_option', 'ql.set_option', True, 'from openql import openql as ql\n'), (25, 'openql.openql.Platform', 'ql.Platform', True, 'from openql import openql as ql\n'), (26, 'openql.openql.Program', 'ql.Program', True, 'from openql import openql as ql\n'), (28, 'openql.openql.Kernel', 'ql.Kernel', True, 'from openql import openql as ql\n'), (52, 'openql.openql.set_option', 'ql.set_option', True, 'from openql import openql as ql\n'), (53, 'openql.openql.set_option', 'ql.set_option', True, 'from openql import openql as ql\n'), (55, 'openql.openql.Platform', 'ql.Platform', True, 'from openql import openql as ql\n'), (61, 'openql.openql.Program', 'ql.Program', True, 'from openql import openql as ql\n'), (63, 'openql.openql.Kernel', 'ql.Kernel', True, 'from openql import openql as ql\n'), (69, 'openql.openql.CReg', 'ql.CReg', True, 'from openql import openql as ql\n'), (70, 'openql.openql.CReg', 'ql.CReg', True, 'from openql import openql as ql\n'), (71, 'openql.openql.CReg', 'ql.CReg', True, 'from openql import openql as ql\n'), (80, 'os.path.join', 'os.path.join', False, 'import os\n'), (32, 'numpy.int', 'np.int', True, 'import numpy as np\n'), (33, 'numpy.int32', 'np.int32', True, 'import numpy as np\n'), (34, 'numpy.int64', 'np.int64', True, 'import numpy as np\n'), (36, 'numpy.uint', 'np.uint', True, 'import numpy as np\n'), (37, 'numpy.uint32', 'np.uint32', True, 'import numpy as np\n'), (38, 'numpy.uint64', 'np.uint64', True, 'import numpy as np\n'), (73, 'openql.openql.Operation', 'ql.Operation', True, 'from openql import openql as ql\n'), (74, 'openql.openql.Operation', 'ql.Operation', True, 'from openql import openql as ql\n'), (75, 'openql.openql.Operation', 'ql.Operation', True, 'from openql import openql as ql\n'), (85, 'os.rename', 'os.rename', False, 'import os\n'), (90, 'utils.file_compare', 'file_compare', False, 'from utils import file_compare\n')]